hedgequantx 2.6.130 → 2.6.131
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
|
@@ -1477,6 +1477,7 @@ const launchMultiSymbolRithmic = async (service, account, contracts, config) =>
|
|
|
1477
1477
|
wins: 0,
|
|
1478
1478
|
losses: 0,
|
|
1479
1479
|
pnl: 0,
|
|
1480
|
+
openPnl: 0, // Per-symbol unrealized P&L
|
|
1480
1481
|
tickCount: 0,
|
|
1481
1482
|
};
|
|
1482
1483
|
});
|
|
@@ -1504,6 +1505,7 @@ const launchMultiSymbolRithmic = async (service, account, contracts, config) =>
|
|
|
1504
1505
|
const positionManagers = {};
|
|
1505
1506
|
const strategies = {};
|
|
1506
1507
|
const pendingOrders = {}; // Track pending orders per symbol
|
|
1508
|
+
const disabledSymbols = new Set(); // Symbols disabled after SL hit
|
|
1507
1509
|
|
|
1508
1510
|
contracts.forEach(contract => {
|
|
1509
1511
|
const symbolName = contract.name || contract.symbol;
|
|
@@ -1577,6 +1579,11 @@ const launchMultiSymbolRithmic = async (service, account, contracts, config) =>
|
|
|
1577
1579
|
stats.losses++;
|
|
1578
1580
|
stats.symbolStats[symbolName].losses++;
|
|
1579
1581
|
ui.addLog('loss', `[${symbolName}] -$${Math.abs(pnlDollars).toFixed(2)} @ ${exitPrice} | ${holdSec}s`);
|
|
1582
|
+
|
|
1583
|
+
// Disable symbol after loss (SL hit) - other symbols continue
|
|
1584
|
+
disabledSymbols.add(symbolName);
|
|
1585
|
+
ui.addLog('warning', `[${symbolName}] DISABLED - SL hit, other symbols continue`);
|
|
1586
|
+
stats.activeSymbols = contracts.length - disabledSymbols.size;
|
|
1580
1587
|
}
|
|
1581
1588
|
} else if (pnlTicks !== null) {
|
|
1582
1589
|
// Log with ticks only
|
|
@@ -1590,9 +1597,17 @@ const launchMultiSymbolRithmic = async (service, account, contracts, config) =>
|
|
|
1590
1597
|
stats.losses++;
|
|
1591
1598
|
stats.symbolStats[symbolName].losses++;
|
|
1592
1599
|
ui.addLog('loss', `[${symbolName}] ${pnlTicks} ticks | ${holdSec}s`);
|
|
1600
|
+
|
|
1601
|
+
// Disable symbol after loss (SL hit) - other symbols continue
|
|
1602
|
+
disabledSymbols.add(symbolName);
|
|
1603
|
+
ui.addLog('warning', `[${symbolName}] DISABLED - SL hit, other symbols continue`);
|
|
1604
|
+
stats.activeSymbols = contracts.length - disabledSymbols.size;
|
|
1593
1605
|
}
|
|
1594
1606
|
}
|
|
1595
1607
|
stats.symbolStats[symbolName].position = 0;
|
|
1608
|
+
stats.symbolStats[symbolName].openPnl = 0; // Reset open P&L when position closed
|
|
1609
|
+
// Recalculate total Open P&L
|
|
1610
|
+
stats.openPnl = Object.values(stats.symbolStats).reduce((sum, s) => sum + (s.openPnl || 0), 0);
|
|
1596
1611
|
pendingOrders[symbolName] = false;
|
|
1597
1612
|
ui.render(stats);
|
|
1598
1613
|
});
|
|
@@ -1610,6 +1625,7 @@ const launchMultiSymbolRithmic = async (service, account, contracts, config) =>
|
|
|
1610
1625
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1611
1626
|
strategy.on('signal', async (signal) => {
|
|
1612
1627
|
if (!running) return;
|
|
1628
|
+
if (disabledSymbols.has(symbolName)) return; // Skip disabled symbols (SL hit)
|
|
1613
1629
|
if (pendingOrders[symbolName]) return;
|
|
1614
1630
|
if (!pm.canEnter(symbolName)) return;
|
|
1615
1631
|
|
|
@@ -1773,6 +1789,32 @@ const launchMultiSymbolRithmic = async (service, account, contracts, config) =>
|
|
|
1773
1789
|
algoLogger.error(ui, 'CONNECTION ERROR', e.message);
|
|
1774
1790
|
}
|
|
1775
1791
|
|
|
1792
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1793
|
+
// TARGET/RISK CHECK - Stop algo when limits reached
|
|
1794
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1795
|
+
const checkTargetRisk = () => {
|
|
1796
|
+
if (!running) return;
|
|
1797
|
+
|
|
1798
|
+
const totalPnl = (stats.openPnl || 0) + (stats.closedPnl || 0);
|
|
1799
|
+
|
|
1800
|
+
// Daily target reached - STOP with profit
|
|
1801
|
+
if (totalPnl >= dailyTarget) {
|
|
1802
|
+
stopReason = 'target';
|
|
1803
|
+
running = false;
|
|
1804
|
+
algoLogger.info(ui, 'TARGET REACHED', `+$${totalPnl.toFixed(2)} >= $${dailyTarget}`);
|
|
1805
|
+
ui.addLog('success', `████ DAILY TARGET REACHED: +$${totalPnl.toFixed(2)} ████`);
|
|
1806
|
+
emergencyStopAll(); // Close all positions
|
|
1807
|
+
}
|
|
1808
|
+
// Max risk reached - STOP to protect capital
|
|
1809
|
+
else if (totalPnl <= -maxRisk) {
|
|
1810
|
+
stopReason = 'risk';
|
|
1811
|
+
running = false;
|
|
1812
|
+
algoLogger.info(ui, 'MAX RISK HIT', `-$${Math.abs(totalPnl).toFixed(2)} <= -$${maxRisk}`);
|
|
1813
|
+
ui.addLog('error', `████ MAX RISK REACHED: -$${Math.abs(totalPnl).toFixed(2)} ████`);
|
|
1814
|
+
emergencyStopAll(); // Close all positions
|
|
1815
|
+
}
|
|
1816
|
+
};
|
|
1817
|
+
|
|
1776
1818
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1777
1819
|
// REAL-TIME P&L VIA WEBSOCKET (same as launchAlgo)
|
|
1778
1820
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -1800,10 +1842,14 @@ const launchMultiSymbolRithmic = async (service, account, contracts, config) =>
|
|
|
1800
1842
|
}
|
|
1801
1843
|
|
|
1802
1844
|
stats.pnl = (stats.openPnl || 0) + (stats.closedPnl || 0);
|
|
1845
|
+
|
|
1846
|
+
// Check target/risk on every P&L update
|
|
1847
|
+
checkTargetRisk();
|
|
1848
|
+
|
|
1803
1849
|
ui.render(stats);
|
|
1804
1850
|
});
|
|
1805
1851
|
|
|
1806
|
-
// Position-level updates (for Open P&L)
|
|
1852
|
+
// Position-level updates (for Open P&L per symbol)
|
|
1807
1853
|
service.on('positionUpdate', (pos) => {
|
|
1808
1854
|
if (!pos || pos.accountId !== rithmicAccountId) return;
|
|
1809
1855
|
|
|
@@ -1813,14 +1859,20 @@ const launchMultiSymbolRithmic = async (service, account, contracts, config) =>
|
|
|
1813
1859
|
if (posSymbol === baseSymbol || posSymbol === symbolName || symbolName.startsWith(posSymbol)) {
|
|
1814
1860
|
stats.symbolStats[symbolName].position = pos.quantity || 0;
|
|
1815
1861
|
|
|
1816
|
-
// Update Open P&L
|
|
1862
|
+
// Update Open P&L for this symbol
|
|
1817
1863
|
if (pos.openPnl !== undefined && pos.openPnl !== null) {
|
|
1818
|
-
stats.openPnl = pos.openPnl;
|
|
1864
|
+
stats.symbolStats[symbolName].openPnl = pos.openPnl;
|
|
1865
|
+
|
|
1866
|
+
// Calculate total Open P&L from all symbols
|
|
1867
|
+
stats.openPnl = Object.values(stats.symbolStats).reduce((sum, s) => sum + (s.openPnl || 0), 0);
|
|
1819
1868
|
stats.pnl = (stats.openPnl || 0) + (stats.closedPnl || 0);
|
|
1820
1869
|
|
|
1821
1870
|
if (stats.balance !== null) {
|
|
1822
1871
|
stats.netLiquidation = stats.balance + stats.openPnl;
|
|
1823
1872
|
}
|
|
1873
|
+
|
|
1874
|
+
// Check target/risk on Open P&L changes too
|
|
1875
|
+
checkTargetRisk();
|
|
1824
1876
|
}
|
|
1825
1877
|
break;
|
|
1826
1878
|
}
|