hedgequantx 1.8.26 → 1.8.27

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.8.26",
3
+ "version": "1.8.27",
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": {
@@ -8,7 +8,7 @@ const readline = require('readline');
8
8
 
9
9
  const { connections } = require('../../services');
10
10
  const { HQXServerService } = require('../../services/hqx-server');
11
- const { AlgoUI } = require('./ui');
11
+ const { AlgoUI, renderSessionSummary } = require('./ui');
12
12
  const { logger, prompts } = require('../../utils');
13
13
  const { checkMarketHours } = require('../../services/projectx/market');
14
14
 
@@ -192,6 +192,19 @@ const selectSymbol = async (service, label) => {
192
192
  return null;
193
193
  }
194
194
 
195
+ // Sort: Popular indices first (ES, NQ, YM, RTY, MES, MNQ)
196
+ const prioritySymbols = ['ES', 'NQ', 'YM', 'RTY', 'MES', 'MNQ', 'MYM', 'M2K', 'NKD', 'CL', 'GC'];
197
+ contracts.sort((a, b) => {
198
+ const aSymbol = (a.symbol || a.name || '').toUpperCase();
199
+ const bSymbol = (b.symbol || b.name || '').toUpperCase();
200
+ const aIdx = prioritySymbols.findIndex(p => aSymbol.startsWith(p));
201
+ const bIdx = prioritySymbols.findIndex(p => bSymbol.startsWith(p));
202
+ if (aIdx !== -1 && bIdx !== -1) return aIdx - bIdx;
203
+ if (aIdx !== -1) return -1;
204
+ if (bIdx !== -1) return 1;
205
+ return (a.name || '').localeCompare(b.name || '');
206
+ });
207
+
195
208
  spinner.succeed(`Found ${contracts.length} contracts`);
196
209
 
197
210
  const options = contracts.map(c => ({ label: c.name || c.symbol, value: c }));
@@ -335,18 +348,7 @@ const launchCopyTrading = async (config) => {
335
348
  ui.cleanup();
336
349
 
337
350
  // Summary
338
- console.clear();
339
- console.log();
340
- console.log(chalk.cyan('╔════════════════════════════════════════════════════════════════╗'));
341
- console.log(chalk.cyan('║') + chalk.white(' Copy Trading Summary ') + chalk.cyan('║'));
342
- console.log(chalk.cyan('╠════════════════════════════════════════════════════════════════╣'));
343
- console.log(chalk.cyan('║') + chalk.white(` Stop Reason: ${(stopReason || 'unknown').padEnd(47)}`) + chalk.cyan('║'));
344
- console.log(chalk.cyan('║') + chalk.white(` Trades: ${String(stats.trades).padEnd(10)} Wins: ${String(stats.wins).padEnd(10)} Losses: ${String(stats.losses).padEnd(10)}`) + chalk.cyan('║'));
345
- const pnlColor = stats.pnl >= 0 ? chalk.green : chalk.red;
346
- const pnlStr = `${stats.pnl >= 0 ? '+' : ''}$${stats.pnl.toFixed(2)}`;
347
- console.log(chalk.cyan('║') + chalk.white(' P&L: ') + pnlColor(pnlStr.padEnd(55)) + chalk.cyan('║'));
348
- console.log(chalk.cyan('╚════════════════════════════════════════════════════════════════╝'));
349
- console.log();
351
+ renderSessionSummary(stats, stopReason);
350
352
 
351
353
  await prompts.waitForEnter();
352
354
  };
@@ -8,7 +8,7 @@ const readline = require('readline');
8
8
 
9
9
  const { connections } = require('../../services');
10
10
  const { HQXServerService } = require('../../services/hqx-server');
11
- const { AlgoUI } = require('./ui');
11
+ const { AlgoUI, renderSessionSummary } = require('./ui');
12
12
  const { prompts } = require('../../utils');
13
13
  const { checkMarketHours } = require('../../services/projectx/market');
14
14
 
@@ -89,12 +89,25 @@ const selectSymbol = async (service, account) => {
89
89
  name: c.description || c.name || c.symbol
90
90
  }));
91
91
 
92
+ // Sort: Popular indices first (ES, NQ, YM, RTY, MES, MNQ)
93
+ const prioritySymbols = ['ES', 'NQ', 'YM', 'RTY', 'MES', 'MNQ', 'MYM', 'M2K', 'NKD', 'CL', 'GC'];
94
+ contracts.sort((a, b) => {
95
+ const aSymbol = (a.symbol || '').toUpperCase();
96
+ const bSymbol = (b.symbol || '').toUpperCase();
97
+ const aIdx = prioritySymbols.findIndex(p => aSymbol.startsWith(p));
98
+ const bIdx = prioritySymbols.findIndex(p => bSymbol.startsWith(p));
99
+ if (aIdx !== -1 && bIdx !== -1) return aIdx - bIdx;
100
+ if (aIdx !== -1) return -1;
101
+ if (bIdx !== -1) return 1;
102
+ return (a.name || '').localeCompare(b.name || '');
103
+ });
104
+
92
105
  spinner.succeed(`Found ${contracts.length} contracts`);
93
106
 
94
107
  const options = contracts.map(c => ({ label: c.name, value: c }));
95
108
  options.push({ label: '< Back', value: 'back' });
96
109
 
97
- const contract = await prompts.selectOption('Select Symbol:', options);
110
+ const contract = await prompts.selectOption(chalk.yellow('Select Symbol:'), options);
98
111
  return contract === 'back' ? null : contract;
99
112
  };
100
113
 
@@ -242,18 +255,7 @@ const launchAlgo = async (service, account, contract, config) => {
242
255
  ui.cleanup();
243
256
 
244
257
  // Summary
245
- console.clear();
246
- console.log();
247
- console.log(chalk.cyan('╔════════════════════════════════════════════════════════════════╗'));
248
- console.log(chalk.cyan('║') + chalk.white(' Session Summary ') + chalk.cyan('║'));
249
- console.log(chalk.cyan('╠════════════════════════════════════════════════════════════════╣'));
250
- console.log(chalk.cyan('║') + chalk.white(` Stop Reason: ${(stopReason || 'unknown').padEnd(47)}`) + chalk.cyan('║'));
251
- console.log(chalk.cyan('║') + chalk.white(` Trades: ${String(stats.trades).padEnd(10)} Wins: ${String(stats.wins).padEnd(10)} Losses: ${String(stats.losses).padEnd(10)}`) + chalk.cyan('║'));
252
- const pnlColor = stats.pnl >= 0 ? chalk.green : chalk.red;
253
- const pnlStr = `${stats.pnl >= 0 ? '+' : ''}$${stats.pnl.toFixed(2)}`;
254
- console.log(chalk.cyan('║') + chalk.white(' P&L: ') + pnlColor(pnlStr.padEnd(55)) + chalk.cyan('║'));
255
- console.log(chalk.cyan('╚════════════════════════════════════════════════════════════════╝'));
256
- console.log();
258
+ renderSessionSummary(stats, stopReason);
257
259
 
258
260
  await prompts.waitForEnter();
259
261
  };
@@ -350,4 +350,79 @@ const checkMarketStatus = () => {
350
350
  return { isOpen: true, message: 'Market OPEN' };
351
351
  };
352
352
 
353
- module.exports = { AlgoUI, checkMarketStatus, LOG_COLORS, LOG_ICONS, stripAnsi, center, fitToWidth };
353
+ /**
354
+ * Render Session Summary - Same style as dashboard
355
+ */
356
+ const renderSessionSummary = (stats, stopReason) => {
357
+ const W = 96; // Same width as dashboard
358
+ const colL = Math.floor(W / 2) - 1;
359
+ const colR = W - colL - 1;
360
+ const version = require('../../../package.json').version;
361
+
362
+ console.clear();
363
+ console.log();
364
+
365
+ // Top border
366
+ console.log(chalk.cyan(BOX.TOP + BOX.H.repeat(W) + BOX.TR));
367
+
368
+ // Logo
369
+ console.log(chalk.cyan(BOX.V) + chalk.cyan(' ██╗ ██╗███████╗██████╗ ██████╗ ███████╗ ██████╗ ██╗ ██╗ █████╗ ███╗ ██╗████████╗') + chalk.yellow('██╗ ██╗') + ' ' + chalk.cyan(BOX.V));
370
+ console.log(chalk.cyan(BOX.V) + chalk.cyan(' ██║ ██║██╔════╝██╔══██╗██╔════╝ ██╔════╝██╔═══██╗██║ ██║██╔══██╗████╗ ██║╚══██╔══╝') + chalk.yellow('╚██╗██╔╝') + ' ' + chalk.cyan(BOX.V));
371
+ console.log(chalk.cyan(BOX.V) + chalk.cyan(' ███████║█████╗ ██║ ██║██║ ███╗█████╗ ██║ ██║██║ ██║███████║██╔██╗ ██║ ██║ ') + chalk.yellow(' ╚███╔╝ ') + ' ' + chalk.cyan(BOX.V));
372
+ console.log(chalk.cyan(BOX.V) + chalk.cyan(' ██╔══██║██╔══╝ ██║ ██║██║ ██║██╔══╝ ██║▄▄ ██║██║ ██║██╔══██║██║╚██╗██║ ██║ ') + chalk.yellow(' ██╔██╗ ') + ' ' + chalk.cyan(BOX.V));
373
+ console.log(chalk.cyan(BOX.V) + chalk.cyan(' ██║ ██║███████╗██████╔╝╚██████╔╝███████╗╚██████╔╝╚██████╔╝██║ ██║██║ ╚████║ ██║ ') + chalk.yellow('██╔╝ ██╗') + ' ' + chalk.cyan(BOX.V));
374
+ console.log(chalk.cyan(BOX.V) + chalk.cyan(' ╚═╝ ╚═╝╚══════╝╚═════╝ ╚═════╝ ╚══════╝ ╚══▀▀═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ') + chalk.yellow('╚═╝ ╚═╝') + ' ' + chalk.cyan(BOX.V));
375
+
376
+ // Separator + title
377
+ console.log(chalk.cyan(BOX.ML + BOX.H.repeat(W) + BOX.MR));
378
+ console.log(chalk.cyan(BOX.V) + chalk.white(center(`Prop Futures Algo Trading v${version}`, W)) + chalk.cyan(BOX.V));
379
+ console.log(chalk.cyan(BOX.ML + BOX.H.repeat(W) + BOX.MR));
380
+ console.log(chalk.cyan(BOX.V) + chalk.yellow(center('Session Summary', W)) + chalk.cyan(BOX.V));
381
+
382
+ // Grid separators
383
+ const GT = BOX.ML + BOX.H.repeat(colL) + BOX.TM + BOX.H.repeat(colR) + BOX.MR;
384
+ const GM = BOX.ML + BOX.H.repeat(colL) + BOX.MM + BOX.H.repeat(colR) + BOX.MR;
385
+
386
+ const row = (label1, value1, color1, label2, value2, color2) => {
387
+ const c1 = ` ${label1}: ${color1(value1)}`;
388
+ const c2 = ` ${label2}: ${color2(value2)}`;
389
+ const p1 = ` ${label1}: ${value1}`;
390
+ const p2 = ` ${label2}: ${value2}`;
391
+ const padded1 = c1 + ' '.repeat(Math.max(0, colL - p1.length));
392
+ const padded2 = c2 + ' '.repeat(Math.max(0, colR - p2.length));
393
+ console.log(chalk.cyan(BOX.V) + padded1 + chalk.cyan(BOX.VS) + padded2 + chalk.cyan(BOX.V));
394
+ };
395
+
396
+ console.log(chalk.cyan(GT));
397
+
398
+ // Row 1: Stop Reason | Duration
399
+ const duration = stats.duration || '--';
400
+ const reasonColor = stopReason === 'target' ? chalk.green : stopReason === 'risk' ? chalk.red : chalk.yellow;
401
+ row('Stop Reason', (stopReason || 'manual').toUpperCase(), reasonColor, 'Duration', duration, chalk.white);
402
+
403
+ console.log(chalk.cyan(GM));
404
+
405
+ // Row 2: Trades | Win Rate
406
+ const winRate = stats.trades > 0 ? ((stats.wins / stats.trades) * 100).toFixed(1) + '%' : '0%';
407
+ row('Trades', String(stats.trades || 0), chalk.white, 'Win Rate', winRate, stats.wins >= stats.losses ? chalk.green : chalk.red);
408
+
409
+ console.log(chalk.cyan(GM));
410
+
411
+ // Row 3: Wins | Losses
412
+ row('Wins', String(stats.wins || 0), chalk.green, 'Losses', String(stats.losses || 0), chalk.red);
413
+
414
+ console.log(chalk.cyan(GM));
415
+
416
+ // Row 4: P&L | Target
417
+ const pnl = stats.pnl || 0;
418
+ const pnlStr = `${pnl >= 0 ? '+' : ''}$${Math.abs(pnl).toFixed(2)}`;
419
+ const pnlColor = pnl >= 0 ? chalk.green : chalk.red;
420
+ const targetStr = `$${(stats.target || 0).toFixed(2)}`;
421
+ row('P&L', pnlStr, pnlColor, 'Target', targetStr, chalk.cyan);
422
+
423
+ // Bottom border
424
+ console.log(chalk.cyan(BOX.BOT + BOX.H.repeat(W) + BOX.BR));
425
+ console.log();
426
+ };
427
+
428
+ module.exports = { AlgoUI, checkMarketStatus, renderSessionSummary, LOG_COLORS, LOG_ICONS, stripAnsi, center, fitToWidth };