hedgequantx 2.6.41 → 2.6.42

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.41",
3
+ "version": "2.6.42",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -362,6 +362,33 @@ const launchAlgo = async (service, account, contract, config) => {
362
362
  });
363
363
  }
364
364
 
365
+ // ═══════════════════════════════════════════════════════════════════════════
366
+ // REAL-TIME P&L VIA WEBSOCKET (like R Trader)
367
+ // Listen to pnlUpdate events from Rithmic PNL_PLANT WebSocket
368
+ // ═══════════════════════════════════════════════════════════════════════════
369
+ const rithmicAccountId = account.rithmicAccountId || account.accountId;
370
+
371
+ service.on('pnlUpdate', (pnlData) => {
372
+ // Only update for our account
373
+ if (pnlData.accountId !== rithmicAccountId) return;
374
+
375
+ // Calculate P&L exactly like R Trader: openPositionPnl + closedPositionPnl
376
+ const openPnl = parseFloat(pnlData.openPositionPnl || 0);
377
+ const closedPnl = parseFloat(pnlData.closedPositionPnl || 0);
378
+
379
+ // R Trader "Today's P&L" = unrealized + realized
380
+ stats.pnl = openPnl + closedPnl;
381
+ });
382
+
383
+ // Also listen to position updates for real-time position tracking
384
+ service.on('positionUpdate', (pos) => {
385
+ if (!pos || pos.accountId !== rithmicAccountId) return;
386
+ if (pos.symbol !== symbolName && !pos.symbol?.includes(symbolName.replace(/[A-Z]\d+$/, ''))) return;
387
+
388
+ // Update current position from WebSocket
389
+ currentPosition = pos.quantity || 0;
390
+ });
391
+
365
392
  // Initialize AI Strategy Supervisor - agents observe, learn & optimize
366
393
  // Only if user enabled AI in config
367
394
  let aiAgentCount = 0;
@@ -716,16 +743,16 @@ const launchAlgo = async (service, account, contract, config) => {
716
743
  algoLogger.error(ui, 'CONNECTION ERROR', e.message.substring(0, 50));
717
744
  }
718
745
 
719
- // Poll account P&L and sync with real trades from API
746
+ // Poll account P&L and sync with real trades from API (fallback, WebSocket is primary)
720
747
  const pollPnL = async () => {
721
748
  try {
722
- // Get account P&L
749
+ // Get account P&L (fallback if WebSocket not updating)
723
750
  const accountResult = await service.getTradingAccounts();
724
751
  if (accountResult.success && accountResult.accounts) {
725
752
  const acc = accountResult.accounts.find(a => a.accountId === account.accountId);
726
- if (acc && acc.profitAndLoss !== undefined) {
727
- if (startingPnL === null) startingPnL = acc.profitAndLoss;
728
- stats.pnl = acc.profitAndLoss - startingPnL;
753
+ if (acc && acc.profitAndLoss !== undefined && acc.profitAndLoss !== null) {
754
+ // Use day P&L directly (same as R Trader)
755
+ stats.pnl = acc.profitAndLoss;
729
756
  }
730
757
  }
731
758
 
@@ -828,8 +855,9 @@ const launchAlgo = async (service, account, contract, config) => {
828
855
  };
829
856
 
830
857
  // Start polling and UI refresh
858
+ // UI refreshes every 250ms, P&L polling every 10s (WebSocket is primary for P&L)
831
859
  const refreshInterval = setInterval(() => { if (running) ui.render(stats); }, 250);
832
- const pnlInterval = setInterval(() => { if (running) pollPnL(); }, 2000);
860
+ const pnlInterval = setInterval(() => { if (running) pollPnL(); }, 10000);
833
861
  pollPnL(); // Initial poll
834
862
 
835
863
  // Keyboard handler
@@ -115,15 +115,22 @@ const getTradingAccounts = async (service) => {
115
115
  const closedPnL = pnlData.closedPositionPnl ? parseFloat(pnlData.closedPositionPnl) : null;
116
116
  const dayPnL = pnlData.dayPnl ? parseFloat(pnlData.dayPnl) : null;
117
117
 
118
+ // Calculate P&L like R Trader: openPositionPnl + closedPositionPnl
119
+ // This matches what R Trader shows as "Today's P&L"
120
+ const totalPnL = (openPnL !== null || closedPnL !== null)
121
+ ? (openPnL || 0) + (closedPnL || 0)
122
+ : null;
123
+
118
124
  return {
119
125
  accountId: hashAccountId(acc.accountId),
120
126
  rithmicAccountId: acc.accountId,
121
127
  accountName: acc.accountId, // Never expose real name - only account ID
122
128
  name: acc.accountId, // Never expose real name - only account ID
123
129
  balance: accountBalance,
124
- profitAndLoss: dayPnL !== null ? dayPnL : (openPnL !== null || closedPnL !== null ? (openPnL || 0) + (closedPnL || 0) : null),
130
+ profitAndLoss: totalPnL, // Same as R Trader: open + closed
125
131
  openPnL: openPnL,
126
- todayPnL: closedPnL,
132
+ closedPnL: closedPnL,
133
+ dayPnL: dayPnL, // Keep for reference
127
134
  status: 0,
128
135
  platform: 'Rithmic',
129
136
  propfirm: service.propfirm.name,