hedgequantx 1.5.3 → 1.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "1.5.3",
3
+ "version": "1.5.4",
4
4
  "description": "Prop Futures Algo Trading CLI - Connect to Topstep, Alpha Futures, and other prop firms",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -205,50 +205,45 @@ const copyTradingMenu = async () => {
205
205
  const selectSymbol = async (service, label) => {
206
206
  log.debug('selectSymbol called', { label, hasGetContracts: typeof service.getContracts === 'function' });
207
207
  try {
208
- // Check if service has getContracts method
209
- if (typeof service.getContracts !== 'function') {
210
- log.warn('Service does not have getContracts method, using searchContracts fallback');
211
- // Fallback: use searchContracts or return predefined list
212
- if (typeof service.searchContracts === 'function') {
213
- const contracts = await service.searchContracts('');
214
- log.debug('searchContracts result', { count: contracts?.length });
215
- if (contracts && contracts.length > 0) {
216
- const choices = contracts.map(c => ({ name: c.name || c.symbol, value: c }));
217
- choices.push({ name: chalk.yellow('< Cancel'), value: null });
218
-
219
- const { symbol } = await inquirer.prompt([{
220
- type: 'list',
221
- name: 'symbol',
222
- message: `${label} Symbol:`,
223
- choices,
224
- pageSize: 15
225
- }]);
226
- return symbol;
227
- }
208
+ let contracts = [];
209
+
210
+ // Try getContracts first
211
+ if (typeof service.getContracts === 'function') {
212
+ const result = await service.getContracts();
213
+ log.debug('getContracts result', { success: result?.success, count: result?.contracts?.length });
214
+ if (result.success && result.contracts?.length > 0) {
215
+ contracts = result.contracts;
228
216
  }
229
- log.error('No contract fetching method available');
230
- return null;
231
217
  }
232
218
 
233
- const result = await service.getContracts();
234
- log.debug('getContracts result', { success: result?.success, count: result?.contracts?.length });
235
- if (!result.success) return null;
236
-
237
- const choices = [];
238
- const cats = {};
239
-
240
- for (const c of result.contracts) {
241
- const cat = c.group || 'Other';
242
- if (!cats[cat]) cats[cat] = [];
243
- cats[cat].push(c);
219
+ // Fallback to searchContracts if no contracts yet
220
+ if (contracts.length === 0 && typeof service.searchContracts === 'function') {
221
+ log.debug('Trying searchContracts fallback');
222
+ // For Rithmic, searchContracts returns array directly
223
+ const searchResult = await service.searchContracts('ES');
224
+ log.debug('searchContracts result', { result: searchResult });
225
+
226
+ if (Array.isArray(searchResult)) {
227
+ contracts = searchResult;
228
+ } else if (searchResult?.contracts) {
229
+ contracts = searchResult.contracts;
230
+ }
244
231
  }
245
232
 
246
- for (const [cat, list] of Object.entries(cats)) {
247
- choices.push(new inquirer.Separator(chalk.gray(`--- ${cat} ---`)));
248
- for (const c of list.slice(0, 8)) {
249
- choices.push({ name: c.name || c.symbol, value: c });
250
- }
233
+ // If still no contracts, show error
234
+ if (!contracts || contracts.length === 0) {
235
+ log.error('No contracts available');
236
+ console.log(chalk.red(' No contracts available for this service'));
237
+ return null;
251
238
  }
239
+
240
+ log.debug('Contracts loaded', { count: contracts.length });
241
+
242
+ // Build choices - simple list without categories
243
+ const choices = contracts.map(c => ({
244
+ name: c.name || c.symbol,
245
+ value: c
246
+ }));
252
247
  choices.push(new inquirer.Separator());
253
248
  choices.push({ name: chalk.yellow('< Cancel'), value: null });
254
249
 
@@ -443,6 +443,35 @@ class ProjectXService {
443
443
 
444
444
  // ==================== CONTRACTS ====================
445
445
 
446
+ /**
447
+ * Get popular contracts for trading
448
+ */
449
+ async getContracts() {
450
+ try {
451
+ // Search for popular futures symbols
452
+ const symbols = ['ES', 'NQ', 'MES', 'MNQ', 'CL', 'GC', 'RTY', 'YM'];
453
+ const allContracts = [];
454
+
455
+ for (const sym of symbols) {
456
+ const response = await this._request(
457
+ this.propfirm.gatewayApi, '/api/Contract/search', 'POST',
458
+ { searchText: sym, live: false }
459
+ );
460
+ if (response.statusCode === 200) {
461
+ const contracts = response.data.contracts || response.data || [];
462
+ // Take first contract for each symbol (front month)
463
+ if (contracts.length > 0) {
464
+ allContracts.push(contracts[0]);
465
+ }
466
+ }
467
+ }
468
+
469
+ return { success: true, contracts: allContracts };
470
+ } catch (error) {
471
+ return { success: false, contracts: [], error: error.message };
472
+ }
473
+ }
474
+
446
475
  async searchContracts(searchText) {
447
476
  try {
448
477
  const response = await this._request(
@@ -31,21 +31,19 @@ const COLORS = {
31
31
 
32
32
  class Logger {
33
33
  constructor() {
34
- this.enabled = process.env.HQX_DEBUG === '1';
34
+ this.consoleEnabled = process.env.HQX_DEBUG === '1';
35
35
  this.level = LEVELS.DEBUG;
36
36
  this.logFile = path.join(os.homedir(), '.hedgequantx', 'debug.log');
37
37
 
38
- // Always write to file when debug is enabled
39
- if (this.enabled) {
40
- const dir = path.dirname(this.logFile);
41
- if (!fs.existsSync(dir)) {
42
- fs.mkdirSync(dir, { recursive: true });
43
- }
44
- // Clear log file on start
45
- fs.writeFileSync(this.logFile, `=== HQX Debug Log Started ${new Date().toISOString()} ===\n`);
46
- fs.appendFileSync(this.logFile, `Platform: ${process.platform}, Node: ${process.version}\n`);
47
- fs.appendFileSync(this.logFile, `CWD: ${process.cwd()}\n\n`);
38
+ // Always write to file (logs are always saved)
39
+ const dir = path.dirname(this.logFile);
40
+ if (!fs.existsSync(dir)) {
41
+ fs.mkdirSync(dir, { recursive: true });
48
42
  }
43
+ // Clear log file on start
44
+ fs.writeFileSync(this.logFile, `=== HQX Log Started ${new Date().toISOString()} ===\n`);
45
+ fs.appendFileSync(this.logFile, `Platform: ${process.platform}, Node: ${process.version}\n`);
46
+ fs.appendFileSync(this.logFile, `CWD: ${process.cwd()}\n\n`);
49
47
  }
50
48
 
51
49
  _format(level, module, message, data) {
@@ -55,20 +53,22 @@ class Logger {
55
53
  }
56
54
 
57
55
  _log(level, levelName, module, message, data) {
58
- if (!this.enabled || level > this.level) return;
56
+ if (level > this.level) return;
59
57
 
60
58
  const formatted = this._format(levelName, module, message, data);
61
- const color = COLORS[levelName] || COLORS.RESET;
62
59
 
63
- // Always write to file first (survives crashes)
60
+ // Always write to file (survives crashes)
64
61
  try {
65
62
  fs.appendFileSync(this.logFile, formatted + '\n');
66
63
  } catch (e) {
67
64
  // Ignore file write errors
68
65
  }
69
66
 
70
- // Console output (stderr to not interfere with CLI UI)
71
- console.error(`${color}${formatted}${COLORS.RESET}`);
67
+ // Console output only if HQX_DEBUG=1
68
+ if (this.consoleEnabled) {
69
+ const color = COLORS[levelName] || COLORS.RESET;
70
+ console.error(`${color}${formatted}${COLORS.RESET}`);
71
+ }
72
72
  }
73
73
 
74
74
  error(module, message, data) {