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 +1 -1
- package/src/pages/algo/one-account.js +47 -0
- package/src/pages/algo/ui.js +52 -37
package/package.json
CHANGED
|
@@ -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
|
|
package/src/pages/algo/ui.js
CHANGED
|
@@ -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
|
-
//
|
|
579
|
-
const colSymbol =
|
|
580
|
-
const colTrades =
|
|
581
|
-
const colWR =
|
|
582
|
-
const colWins =
|
|
583
|
-
const colLosses =
|
|
584
|
-
const colPnL =
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
const
|
|
589
|
-
const
|
|
590
|
-
const
|
|
591
|
-
const
|
|
592
|
-
const
|
|
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 = (
|
|
613
|
-
const cellTrades = String(symStats.trades || 0)
|
|
614
|
-
const cellWR = winRate
|
|
615
|
-
const cellWins = String(symStats.wins || 0)
|
|
616
|
-
const cellLosses = String(symStats.losses || 0)
|
|
617
|
-
const cellPnL = pnlStr
|
|
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 = '
|
|
638
|
-
const totalCellTrades = String(stats.trades || 0)
|
|
639
|
-
const totalCellWR = totalWinRate
|
|
640
|
-
const totalCellWins = String(stats.wins || 0)
|
|
641
|
-
const totalCellLosses = String(stats.losses || 0)
|
|
642
|
-
const totalCellPnL = totalPnlStr
|
|
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.
|
|
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
|
|
659
|
-
const
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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));
|