hedgequantx 2.6.132 → 2.6.134

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.6.132",
3
+ "version": "2.6.134",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -1683,10 +1683,15 @@ const launchMultiSymbolRithmic = async (service, account, contracts, config) =>
1683
1683
  algoLogger.algoOperational(ui, 'RITHMIC');
1684
1684
  });
1685
1685
 
1686
+ // Smart logs state tracking
1687
+ let lastHeartbeat = Date.now();
1688
+ let tps = 0;
1689
+
1686
1690
  marketFeed.on('tick', (tick) => {
1687
1691
  if (!running) return;
1688
1692
 
1689
1693
  tickCount++;
1694
+ tps++;
1690
1695
  stats.latency = tick.latency || 0;
1691
1696
 
1692
1697
  // Route tick to correct strategy based on symbol
@@ -1743,6 +1748,48 @@ const launchMultiSymbolRithmic = async (service, account, contracts, config) =>
1743
1748
  }
1744
1749
  }
1745
1750
 
1751
+ // ═══════════════════════════════════════════════════════════════════════════
1752
+ // SMART LOGS - Same as single-symbol mode
1753
+ // ═══════════════════════════════════════════════════════════════════════════
1754
+ const now = Date.now();
1755
+ if (now - lastHeartbeat > 1000) {
1756
+ // Get model values from first active symbol's strategy
1757
+ const firstSymbol = Object.keys(strategies)[0];
1758
+ const firstStrategy = strategies[firstSymbol];
1759
+ const modelValues = firstStrategy?.getModelValues?.() || firstStrategy?.getModelValues?.(firstSymbol) || null;
1760
+
1761
+ if (modelValues && modelValues.ofi !== undefined) {
1762
+ const ofi = modelValues.ofi || 0;
1763
+ const delta = modelValues.delta || 0;
1764
+ const zscore = modelValues.zscore || 0;
1765
+ const mom = modelValues.momentum || 0;
1766
+
1767
+ // Check if any symbol has an open position
1768
+ const totalPosition = Object.values(stats.symbolStats).reduce((sum, s) => sum + Math.abs(s.position || 0), 0);
1769
+
1770
+ if (totalPosition === 0) {
1771
+ // Not in position - show market analysis (varied messages)
1772
+ const smartLogs = require('./smart-logs');
1773
+ const stateLog = smartLogs.getMarketStateLog(ofi, zscore, mom, delta);
1774
+ if (stateLog.details) {
1775
+ ui.addLog('analysis', `${stateLog.message} - ${stateLog.details}`);
1776
+ } else {
1777
+ ui.addLog('info', stateLog.message);
1778
+ }
1779
+ }
1780
+ // When IN POSITION: Don't spam logs every second
1781
+ } else {
1782
+ // Waiting for data - log every 5 seconds only
1783
+ if (now - lastHeartbeat > 5000) {
1784
+ const smartLogs = require('./smart-logs');
1785
+ const scanLog = smartLogs.getScanningLog(true);
1786
+ ui.addLog('info', `${scanLog.message} ${tps} ticks/s`);
1787
+ }
1788
+ }
1789
+ lastHeartbeat = now;
1790
+ tps = 0;
1791
+ }
1792
+
1746
1793
  ui.render(stats);
1747
1794
  });
1748
1795
 
@@ -549,11 +549,18 @@ const renderSessionSummary = (stats, stopReason) => {
549
549
 
550
550
  /**
551
551
  * Render Multi-Symbol Session Summary - Same style as single-symbol
552
+ * All columns centered
552
553
  */
553
554
  const renderMultiSymbolSummary = (stats, stopReason, symbolStats) => {
554
555
  const W = 96;
555
556
  const version = require('../../../package.json').version;
556
557
 
558
+ // Helper: center text in column width
559
+ const centerCol = (text, width) => {
560
+ const pad = Math.floor((width - text.length) / 2);
561
+ return ' '.repeat(pad) + text + ' '.repeat(width - pad - text.length);
562
+ };
563
+
557
564
  console.clear();
558
565
  console.log();
559
566
 
@@ -575,22 +582,21 @@ const renderMultiSymbolSummary = (stats, stopReason, symbolStats) => {
575
582
  console.log(chalk.cyan(BOX.V) + chalk.yellow.bold(center('MULTI-SYMBOL SESSION SUMMARY', W)) + chalk.cyan(BOX.V));
576
583
  console.log(chalk.cyan(BOX.ML + BOX.H.repeat(W) + BOX.MR));
577
584
 
578
- // Per-symbol stats header
579
- const colSymbol = 12;
580
- const colTrades = 10;
581
- const colWR = 10;
582
- const colWins = 8;
583
- const colLosses = 8;
584
- const colPnL = 14;
585
- const remaining = W - colSymbol - colTrades - colWR - colWins - colLosses - colPnL - 5; // 5 separators
586
-
587
- // Header row
588
- const headerSymbol = ' SYMBOL'.padEnd(colSymbol);
589
- const headerTrades = 'TRADES'.padEnd(colTrades);
590
- const headerWR = 'WIN RATE'.padEnd(colWR);
591
- const headerWins = 'WINS'.padEnd(colWins);
592
- const headerLosses = 'LOSSES'.padEnd(colLosses);
593
- const headerPnL = 'P&L'.padEnd(colPnL + remaining);
585
+ // Column widths (total = 96 - 5 separators = 91)
586
+ const colSymbol = 14;
587
+ const colTrades = 12;
588
+ const colWR = 14;
589
+ const colWins = 12;
590
+ const colLosses = 12;
591
+ const colPnL = W - colSymbol - colTrades - colWR - colWins - colLosses - 5; // remaining
592
+
593
+ // Header row - centered
594
+ const headerSymbol = centerCol('SYMBOL', colSymbol);
595
+ const headerTrades = centerCol('TRADES', colTrades);
596
+ const headerWR = centerCol('WIN RATE', colWR);
597
+ const headerWins = centerCol('WINS', colWins);
598
+ const headerLosses = centerCol('LOSSES', colLosses);
599
+ const headerPnL = centerCol('P&L', colPnL);
594
600
 
595
601
  console.log(chalk.cyan(BOX.V) + chalk.bold.white(headerSymbol) + chalk.cyan(BOX.VS) +
596
602
  chalk.bold.white(headerTrades) + chalk.cyan(BOX.VS) +
@@ -601,7 +607,7 @@ const renderMultiSymbolSummary = (stats, stopReason, symbolStats) => {
601
607
 
602
608
  console.log(chalk.cyan(BOX.ML + BOX.H.repeat(W) + BOX.MR));
603
609
 
604
- // Per-symbol rows
610
+ // Per-symbol rows - centered
605
611
  for (const [symbol, symStats] of Object.entries(symbolStats)) {
606
612
  const winRate = symStats.trades > 0 ? ((symStats.wins / symStats.trades) * 100).toFixed(0) + '%' : '0%';
607
613
  const pnl = symStats.pnl || 0;
@@ -609,12 +615,12 @@ const renderMultiSymbolSummary = (stats, stopReason, symbolStats) => {
609
615
  const pnlColor = pnl >= 0 ? chalk.green : chalk.red;
610
616
  const wrColor = symStats.wins >= symStats.losses ? chalk.green : chalk.red;
611
617
 
612
- const cellSymbol = (' ' + symbol).padEnd(colSymbol);
613
- const cellTrades = String(symStats.trades || 0).padEnd(colTrades);
614
- const cellWR = winRate.padEnd(colWR);
615
- const cellWins = String(symStats.wins || 0).padEnd(colWins);
616
- const cellLosses = String(symStats.losses || 0).padEnd(colLosses);
617
- const cellPnL = pnlStr.padEnd(colPnL + remaining);
618
+ const cellSymbol = centerCol(symbol, colSymbol);
619
+ const cellTrades = centerCol(String(symStats.trades || 0), colTrades);
620
+ const cellWR = centerCol(winRate, colWR);
621
+ const cellWins = centerCol(String(symStats.wins || 0), colWins);
622
+ const cellLosses = centerCol(String(symStats.losses || 0), colLosses);
623
+ const cellPnL = centerCol(pnlStr, colPnL);
618
624
 
619
625
  console.log(chalk.cyan(BOX.V) + chalk.yellow(cellSymbol) + chalk.cyan(BOX.VS) +
620
626
  chalk.white(cellTrades) + chalk.cyan(BOX.VS) +
@@ -627,21 +633,21 @@ const renderMultiSymbolSummary = (stats, stopReason, symbolStats) => {
627
633
  // Separator before totals
628
634
  console.log(chalk.cyan(BOX.ML + BOX.H.repeat(W) + BOX.MR));
629
635
 
630
- // Total row
636
+ // Total row - centered
631
637
  const totalWinRate = stats.trades > 0 ? ((stats.wins / stats.trades) * 100).toFixed(0) + '%' : '0%';
632
638
  const totalPnl = stats.sessionPnl || 0;
633
639
  const totalPnlStr = (totalPnl >= 0 ? '+$' : '-$') + Math.abs(totalPnl).toFixed(2);
634
640
  const totalPnlColor = totalPnl >= 0 ? chalk.green : chalk.red;
635
641
  const totalWrColor = stats.wins >= stats.losses ? chalk.green : chalk.red;
636
642
 
637
- const totalCellSymbol = ' TOTAL'.padEnd(colSymbol);
638
- const totalCellTrades = String(stats.trades || 0).padEnd(colTrades);
639
- const totalCellWR = totalWinRate.padEnd(colWR);
640
- const totalCellWins = String(stats.wins || 0).padEnd(colWins);
641
- const totalCellLosses = String(stats.losses || 0).padEnd(colLosses);
642
- const totalCellPnL = totalPnlStr.padEnd(colPnL + remaining);
643
+ const totalCellSymbol = centerCol('TOTAL', colSymbol);
644
+ const totalCellTrades = centerCol(String(stats.trades || 0), colTrades);
645
+ const totalCellWR = centerCol(totalWinRate, colWR);
646
+ const totalCellWins = centerCol(String(stats.wins || 0), colWins);
647
+ const totalCellLosses = centerCol(String(stats.losses || 0), colLosses);
648
+ const totalCellPnL = centerCol(totalPnlStr, colPnL);
643
649
 
644
- console.log(chalk.cyan(BOX.V) + chalk.bold.white(totalCellSymbol) + chalk.cyan(BOX.VS) +
650
+ console.log(chalk.cyan(BOX.V) + chalk.bold.cyan(totalCellSymbol) + chalk.cyan(BOX.VS) +
645
651
  chalk.bold.white(totalCellTrades) + chalk.cyan(BOX.VS) +
646
652
  totalWrColor.bold(totalCellWR) + chalk.cyan(BOX.VS) +
647
653
  chalk.bold.green(totalCellWins) + chalk.cyan(BOX.VS) +
@@ -651,15 +657,24 @@ const renderMultiSymbolSummary = (stats, stopReason, symbolStats) => {
651
657
  // Separator
652
658
  console.log(chalk.cyan(BOX.ML + BOX.H.repeat(W) + BOX.MR));
653
659
 
654
- // Stop Reason & Duration row
660
+ // Stop Reason & Duration row - centered
655
661
  const duration = stats.duration || '--';
656
662
  const reasonColor = stopReason === 'target' ? chalk.green : stopReason === 'risk' ? chalk.red : chalk.yellow;
657
663
  const reasonStr = (stopReason || 'manual').toUpperCase();
658
- const infoText = ` STOP: ${reasonStr} | DURATION: ${duration} | TARGET: $${(stats.target || 0).toFixed(2)} | RISK: $${(stats.risk || 0).toFixed(2)}`;
659
- const infoPlain = ` STOP: ${reasonStr} | DURATION: ${duration} | TARGET: $${(stats.target || 0).toFixed(2)} | RISK: $${(stats.risk || 0).toFixed(2)}`;
660
- const infoColored = ` ${chalk.bold('STOP')}: ${reasonColor.bold(reasonStr)} | ${chalk.bold('DURATION')}: ${chalk.white(duration)} | ${chalk.bold('TARGET')}: ${chalk.cyan('$' + (stats.target || 0).toFixed(2))} | ${chalk.bold('RISK')}: ${chalk.red('$' + (stats.risk || 0).toFixed(2))}`;
661
-
662
- console.log(chalk.cyan(BOX.V) + infoColored + ' '.repeat(Math.max(0, W - infoPlain.length)) + chalk.cyan(BOX.V));
664
+ const infoPlain = `STOP: ${reasonStr} | DURATION: ${duration} | TARGET: $${(stats.target || 0).toFixed(2)} | RISK: $${(stats.risk || 0).toFixed(2)}`;
665
+ const infoPadded = center(infoPlain, W);
666
+
667
+ // Build colored version with same centering
668
+ const padLeft = Math.floor((W - infoPlain.length) / 2);
669
+ const padRight = W - infoPlain.length - padLeft;
670
+ const infoColored = ' '.repeat(padLeft) +
671
+ chalk.bold('STOP') + ': ' + reasonColor.bold(reasonStr) + ' | ' +
672
+ chalk.bold('DURATION') + ': ' + chalk.white(duration) + ' | ' +
673
+ chalk.bold('TARGET') + ': ' + chalk.cyan('$' + (stats.target || 0).toFixed(2)) + ' | ' +
674
+ chalk.bold('RISK') + ': ' + chalk.red('$' + (stats.risk || 0).toFixed(2)) +
675
+ ' '.repeat(padRight);
676
+
677
+ console.log(chalk.cyan(BOX.V) + infoColored + chalk.cyan(BOX.V));
663
678
 
664
679
  // Bottom border
665
680
  console.log(chalk.cyan(BOX.BOT + BOX.H.repeat(W) + BOX.BR));