hedgequantx 2.9.147 → 2.9.149

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": "2.9.147",
3
+ "version": "2.9.149",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -326,14 +326,11 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
326
326
  marketFeed.on('disconnected', () => { stats.connected = false; ui.addLog('error', 'Market disconnected'); });
327
327
 
328
328
  try {
329
- const rithmicCredentials = service.getRithmicCredentials?.();
330
- if (!rithmicCredentials) {
331
- throw new Error('Rithmic credentials not available');
332
- }
333
- // Disconnect service's TICKER_PLANT to avoid conflict (Rithmic allows only 1 TICKER connection per user)
334
- if (service.disconnectTicker) {
335
- await service.disconnectTicker();
336
- }
329
+ // Try sync (RithmicService) then async (BrokerClient)
330
+ let rithmicCredentials = service.getRithmicCredentials?.();
331
+ if (!rithmicCredentials && service.getRithmicCredentialsAsync) rithmicCredentials = await service.getRithmicCredentialsAsync();
332
+ if (!rithmicCredentials) throw new Error('Rithmic credentials not available - try "hqx login"');
333
+ if (service.disconnectTicker) await service.disconnectTicker(); // Avoid TICKER conflict
337
334
  await marketFeed.connect(rithmicCredentials);
338
335
  await marketFeed.subscribe(symbolCode, contract.exchange || 'CME');
339
336
 
@@ -188,9 +188,13 @@ const launchCopyTrading = async (config) => {
188
188
 
189
189
  // Connect to market data (Rithmic TICKER_PLANT)
190
190
  try {
191
- const rithmicCredentials = leadService.getRithmicCredentials?.();
191
+ // Try sync first (RithmicService), then async (RithmicBrokerClient)
192
+ let rithmicCredentials = leadService.getRithmicCredentials?.();
193
+ if (!rithmicCredentials && leadService.getRithmicCredentialsAsync) {
194
+ rithmicCredentials = await leadService.getRithmicCredentialsAsync();
195
+ }
192
196
  if (!rithmicCredentials) {
193
- throw new Error('Rithmic credentials not available');
197
+ throw new Error('Rithmic credentials not available - try "hqx login"');
194
198
  }
195
199
  await marketFeed.connect(rithmicCredentials);
196
200
  await marketFeed.subscribe(symbolName, contract.exchange || 'CME');
@@ -267,8 +267,12 @@ const executeMultiSymbol = async ({ service, account, contracts, config, strateg
267
267
 
268
268
  // Connect and subscribe to all symbols
269
269
  try {
270
- const rithmicCredentials = service.getRithmicCredentials?.();
271
- if (!rithmicCredentials) throw new Error('Rithmic credentials not available');
270
+ // Try sync first (RithmicService), then async (RithmicBrokerClient)
271
+ let rithmicCredentials = service.getRithmicCredentials?.();
272
+ if (!rithmicCredentials && service.getRithmicCredentialsAsync) {
273
+ rithmicCredentials = await service.getRithmicCredentialsAsync();
274
+ }
275
+ if (!rithmicCredentials) throw new Error('Rithmic credentials not available - try "hqx login"');
272
276
 
273
277
  if (service.disconnectTicker) await service.disconnectTicker();
274
278
  await marketFeed.connect(rithmicCredentials);
@@ -302,6 +302,9 @@ const oneAccountMenu = async (service) => {
302
302
  const selectMultipleSymbols = async (service, account) => {
303
303
  const spinner = ora({ text: 'Loading symbols...', color: 'yellow' }).start();
304
304
 
305
+ // Debug: log service type
306
+ const serviceType = service.constructor?.name || 'Unknown';
307
+
305
308
  // Ensure we have a logged-in service
306
309
  if (!service.loginInfo && service.credentials) {
307
310
  spinner.text = 'Reconnecting to broker...';
@@ -312,19 +315,24 @@ const selectMultipleSymbols = async (service, account) => {
312
315
  }
313
316
  }
314
317
 
315
- const contractsResult = await service.getContracts();
318
+ let contractsResult;
319
+ try {
320
+ contractsResult = await service.getContracts();
321
+ } catch (err) {
322
+ spinner.fail(`getContracts exception: ${err.message}`);
323
+ console.log(chalk.gray(` Service: ${serviceType} | Tip: Try "hqx login"`));
324
+ return null;
325
+ }
316
326
  if (!contractsResult.success || !contractsResult.contracts?.length) {
317
327
  spinner.fail(`Failed to load contracts: ${contractsResult.error || 'No contracts'}`);
328
+ console.log(chalk.gray(` Service: ${serviceType} | Tip: Try "hqx login"`));
318
329
  return null;
319
330
  }
320
331
 
321
332
  const contracts = sortContracts(contractsResult.contracts);
322
333
  spinner.succeed(`Found ${contracts.length} contracts`);
323
-
324
- console.log();
325
- console.log(chalk.cyan(' Select up to 5 symbols (one at a time)'));
326
- console.log(chalk.gray(' Select "Done" when finished'));
327
- console.log();
334
+ console.log(chalk.cyan('\n Select up to 5 symbols (one at a time)'));
335
+ console.log(chalk.gray(' Select "Done" when finished\n'));
328
336
 
329
337
  const selectedContracts = [];
330
338
  const maxSymbols = 5;
@@ -356,30 +364,17 @@ const selectMultipleSymbols = async (service, account) => {
356
364
  : `Select Symbol ${selectedContracts.length + 1}/${maxSymbols} (${remaining} remaining):`;
357
365
 
358
366
  const selection = await prompts.selectOption(chalk.yellow(promptText), options);
359
-
360
- if (selection === 'back' || selection === null) {
361
- return null;
362
- }
363
- if (selection === 'done') {
364
- break;
365
- }
366
-
367
+ if (selection === 'back' || selection === null) return null;
368
+ if (selection === 'done') break;
367
369
  selectedContracts.push(selection);
368
370
  console.log(chalk.green(` ✓ Added: ${selection.symbol}`));
369
371
  }
370
-
371
- if (selectedContracts.length === 0) {
372
- return null;
373
- }
372
+ if (selectedContracts.length === 0) return null;
374
373
 
375
374
  // Display summary
375
+ console.log(chalk.cyan(`\n Selected ${selectedContracts.length} symbol(s):`));
376
+ for (const c of selectedContracts) console.log(chalk.white(` - ${c.symbol} (${c.baseSymbol || c.name})`));
376
377
  console.log();
377
- console.log(chalk.cyan(` Selected ${selectedContracts.length} symbol(s):`));
378
- for (const c of selectedContracts) {
379
- console.log(chalk.white(` - ${c.symbol} (${c.baseSymbol || c.name})`));
380
- }
381
- console.log();
382
-
383
378
  return selectedContracts;
384
379
  };
385
380
 
@@ -389,6 +384,9 @@ const selectMultipleSymbols = async (service, account) => {
389
384
  const selectSymbol = async (service, account) => {
390
385
  const spinner = ora({ text: 'Loading symbols...', color: 'yellow' }).start();
391
386
 
387
+ // Debug: log service type
388
+ const serviceType = service.constructor?.name || 'Unknown';
389
+
392
390
  // Ensure we have a logged-in service (for direct RithmicService, not BrokerClient)
393
391
  if (!service.loginInfo && service.credentials && typeof service.login === 'function') {
394
392
  spinner.text = 'Reconnecting to broker...';
@@ -399,12 +397,17 @@ const selectSymbol = async (service, account) => {
399
397
  }
400
398
  }
401
399
 
402
- const contractsResult = await service.getContracts();
403
-
400
+ let contractsResult;
401
+ try {
402
+ contractsResult = await service.getContracts();
403
+ } catch (err) {
404
+ spinner.fail(`getContracts exception: ${err.message}`);
405
+ console.log(chalk.gray(` Service: ${serviceType} | Tip: Try "hqx login"`));
406
+ return null;
407
+ }
404
408
  if (!contractsResult.success || !contractsResult.contracts?.length) {
405
- const errorMsg = contractsResult.error || 'No contracts available';
406
- spinner.fail(`Failed to load contracts: ${errorMsg}`);
407
- console.log(chalk.gray(' Tip: Try reconnecting with "hqx login"'));
409
+ spinner.fail(`Failed to load contracts: ${contractsResult.error || 'No contracts available'}`);
410
+ console.log(chalk.gray(` Service: ${serviceType} | Tip: Try "hqx login"`));
408
411
  return null;
409
412
  }
410
413
 
@@ -211,7 +211,13 @@ class RithmicBrokerClient extends EventEmitter {
211
211
  */
212
212
  async getContracts() {
213
213
  const result = await this._request('getContracts', { propfirmKey: this.propfirmKey });
214
- if (result.error) return { success: false, contracts: [], error: result.error };
214
+ if (result.error) {
215
+ // Debug: show exactly what error came from daemon
216
+ if (process.env.HQX_DEBUG === '1') {
217
+ console.log('[BrokerClient] getContracts error:', result.error, 'propfirmKey:', this.propfirmKey);
218
+ }
219
+ return { success: false, contracts: [], error: result.error };
220
+ }
215
221
  return result.payload || { success: true, contracts: [] };
216
222
  }
217
223
 
@@ -299,13 +299,27 @@ class RithmicBrokerDaemon {
299
299
 
300
300
  async _handleGetContracts(payload, requestId) {
301
301
  const conn = this.connections.get(payload.propfirmKey);
302
- if (!conn?.service) return { error: 'Not connected to broker', requestId };
302
+ if (!conn?.service) {
303
+ log('WARN', 'getContracts: Not connected', { propfirm: payload.propfirmKey, hasConn: !!conn });
304
+ return { error: 'Not connected to broker', requestId };
305
+ }
306
+
307
+ // Log service state for debugging
308
+ const hasCredentials = !!conn.service.credentials;
309
+ const hasTickerConn = !!conn.service.tickerConn;
310
+ log('DEBUG', 'getContracts request', { propfirm: payload.propfirmKey, hasCredentials, hasTickerConn });
303
311
 
304
312
  try {
305
313
  const result = await conn.service.getContracts();
314
+ log('DEBUG', 'getContracts result', {
315
+ propfirm: payload.propfirmKey,
316
+ success: result.success,
317
+ count: result.contracts?.length || 0,
318
+ error: result.error
319
+ });
306
320
  return { type: 'contracts', payload: result, requestId };
307
321
  } catch (err) {
308
- log('ERROR', 'getContracts failed', { propfirm: payload.propfirmKey, error: err.message });
322
+ log('ERROR', 'getContracts exception', { propfirm: payload.propfirmKey, error: err.message, stack: err.stack?.split('\n')[1] });
309
323
  return { type: 'contracts', payload: { success: false, error: err.message, contracts: [] }, requestId };
310
324
  }
311
325
  }