hedgequantx 2.6.88 → 2.6.89

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.88",
3
+ "version": "2.6.89",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -399,84 +399,68 @@ const launchAlgo = async (service, account, contract, config) => {
399
399
  }
400
400
 
401
401
  // ═══════════════════════════════════════════════════════════════════════════
402
- // REAL-TIME P&L VIA WEBSOCKET (like R Trader)
403
- // Listen to pnlUpdate events from Rithmic PNL_PLANT WebSocket
402
+ // REAL-TIME P&L VIA WEBSOCKET
403
+ // - Rithmic: Uses EventEmitter (service.on) for real-time updates
404
+ // - ProjectX: Uses HTTP polling (handled in pollPnL function below)
404
405
  // ═══════════════════════════════════════════════════════════════════════════
405
406
  const rithmicAccountId = account.rithmicAccountId || account.accountId;
407
+ const serviceHasEvents = typeof service.on === 'function';
406
408
 
407
- service.on('pnlUpdate', (pnlData) => {
408
- // Only update for our account
409
- if (pnlData.accountId !== rithmicAccountId) return;
410
-
411
- // ═══════════════════════════════════════════════════════════════════════════
412
- // ACCOUNT_PNL_UPDATE (451) - All R Trader account-level metrics
413
- // ═══════════════════════════════════════════════════════════════════════════
414
-
415
- // Closed P&L (realized) - from closed trades today
416
- if (pnlData.closedPositionPnl !== undefined) {
417
- stats.closedPnl = parseFloat(pnlData.closedPositionPnl);
418
- }
419
-
420
- // Account Balance
421
- if (pnlData.accountBalance !== undefined) {
422
- stats.balance = parseFloat(pnlData.accountBalance);
423
- }
424
-
425
- // Buying Power (Available)
426
- if (pnlData.availableBuyingPower !== undefined) {
427
- stats.buyingPower = parseFloat(pnlData.availableBuyingPower);
428
- }
429
-
430
- // Margin Balance
431
- if (pnlData.marginBalance !== undefined) {
432
- stats.margin = parseFloat(pnlData.marginBalance);
433
- }
434
-
435
- // Net Liquidation Value (balance + openPnl) - same as R Trader
436
- if (pnlData.netLiquidation !== undefined) {
437
- stats.netLiquidation = parseFloat(pnlData.netLiquidation);
438
- } else if (stats.balance !== null) {
439
- // Calculate if not provided directly
440
- stats.netLiquidation = stats.balance + (stats.openPnl || 0);
441
- }
442
-
443
- // Total P&L = openPnl (from positionUpdate) + closedPnl (from pnlUpdate)
444
- // This matches R Trader's "Today's P&L" calculation
445
- stats.pnl = (stats.openPnl || 0) + (stats.closedPnl || 0);
446
- });
447
-
448
- // Listen to position updates for real-time position tracking (like R Trader)
449
- // INSTRUMENT_PNL_UPDATE (450) provides per-instrument P&L in real-time
450
- service.on('positionUpdate', (pos) => {
451
- if (!pos || pos.accountId !== rithmicAccountId) return;
452
- if (pos.symbol !== symbolName && !pos.symbol?.includes(symbolName.replace(/[A-Z]\d+$/, ''))) return;
453
-
454
- // Update position info (like R Trader Positions panel)
455
- const wasFlat = stats.position === 0;
456
- stats.position = pos.quantity || 0;
457
- stats.entryPrice = pos.averagePrice || 0;
458
- currentPosition = pos.quantity || 0;
459
-
460
- // Track entry time when opening a new position
461
- if (wasFlat && stats.position !== 0) {
462
- stats.entryTime = Date.now();
463
- } else if (stats.position === 0) {
464
- stats.entryTime = null;
465
- }
466
-
467
- // CRITICAL: Update Open P&L from instrument-level data (real-time, same as R Trader)
468
- // pos.openPnl comes from INSTRUMENT_PNL_UPDATE (450) - this is the unrealized P&L
469
- if (pos.openPnl !== undefined && pos.openPnl !== null) {
470
- stats.openPnl = pos.openPnl;
471
- // Recalculate total P&L whenever Open P&L changes
472
- // Total P&L = openPnl + closedPnl (same formula as R Trader)
409
+ if (serviceHasEvents) {
410
+ // RITHMIC ONLY: Real-time P&L via WebSocket
411
+ service.on('pnlUpdate', (pnlData) => {
412
+ // Only update for our account
413
+ if (pnlData.accountId !== rithmicAccountId) return;
414
+
415
+ // ACCOUNT_PNL_UPDATE (451) - All R Trader account-level metrics
416
+ if (pnlData.closedPositionPnl !== undefined) {
417
+ stats.closedPnl = parseFloat(pnlData.closedPositionPnl);
418
+ }
419
+ if (pnlData.accountBalance !== undefined) {
420
+ stats.balance = parseFloat(pnlData.accountBalance);
421
+ }
422
+ if (pnlData.availableBuyingPower !== undefined) {
423
+ stats.buyingPower = parseFloat(pnlData.availableBuyingPower);
424
+ }
425
+ if (pnlData.marginBalance !== undefined) {
426
+ stats.margin = parseFloat(pnlData.marginBalance);
427
+ }
428
+ if (pnlData.netLiquidation !== undefined) {
429
+ stats.netLiquidation = parseFloat(pnlData.netLiquidation);
430
+ } else if (stats.balance !== null) {
431
+ stats.netLiquidation = stats.balance + (stats.openPnl || 0);
432
+ }
433
+
434
+ // Total P&L = openPnl + closedPnl (same as R Trader)
473
435
  stats.pnl = (stats.openPnl || 0) + (stats.closedPnl || 0);
474
- // Also update Net Liquidation (balance + openPnl)
475
- if (stats.balance !== null) {
476
- stats.netLiquidation = stats.balance + stats.openPnl;
436
+ });
437
+
438
+ // RITHMIC ONLY: Real-time position updates
439
+ service.on('positionUpdate', (pos) => {
440
+ if (!pos || pos.accountId !== rithmicAccountId) return;
441
+ if (pos.symbol !== symbolName && !pos.symbol?.includes(symbolName.replace(/[A-Z]\d+$/, ''))) return;
442
+
443
+ const wasFlat = stats.position === 0;
444
+ stats.position = pos.quantity || 0;
445
+ stats.entryPrice = pos.averagePrice || 0;
446
+ currentPosition = pos.quantity || 0;
447
+
448
+ if (wasFlat && stats.position !== 0) {
449
+ stats.entryTime = Date.now();
450
+ } else if (stats.position === 0) {
451
+ stats.entryTime = null;
477
452
  }
478
- }
479
- });
453
+
454
+ // Update Open P&L from INSTRUMENT_PNL_UPDATE (450)
455
+ if (pos.openPnl !== undefined && pos.openPnl !== null) {
456
+ stats.openPnl = pos.openPnl;
457
+ stats.pnl = (stats.openPnl || 0) + (stats.closedPnl || 0);
458
+ if (stats.balance !== null) {
459
+ stats.netLiquidation = stats.balance + stats.openPnl;
460
+ }
461
+ }
462
+ });
463
+ }
480
464
 
481
465
  // Initialize AI Strategy Supervisor - agents observe, learn & optimize
482
466
  // Only if user enabled AI in config