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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "2.6.130",
3
+ "version": "2.6.131",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -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 from position
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
  }