hedgequantx 2.6.41 → 2.6.43
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
|
@@ -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
|
-
|
|
728
|
-
stats.pnl = acc.profitAndLoss
|
|
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(); },
|
|
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:
|
|
130
|
+
profitAndLoss: totalPnL, // Same as R Trader: open + closed
|
|
125
131
|
openPnL: openPnL,
|
|
126
|
-
|
|
132
|
+
closedPnL: closedPnL,
|
|
133
|
+
dayPnL: dayPnL, // Keep for reference
|
|
127
134
|
status: 0,
|
|
128
135
|
platform: 'Rithmic',
|
|
129
136
|
propfirm: service.propfirm.name,
|
|
@@ -529,20 +529,14 @@ const handleOrderNotification = (service, data) => {
|
|
|
529
529
|
const actualFillQty = fillInfo.totalFillQuantity || fillInfo.fillQuantity || notif.quantity || 0;
|
|
530
530
|
const fillPrice = fillInfo.avgFillPrice || fillInfo.lastFillPrice || 0;
|
|
531
531
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
debug('ORDER FILLED:', {
|
|
535
|
-
orderTag,
|
|
536
|
-
side: fillInfo.transactionType === 1 ? 'BUY' : 'SELL',
|
|
537
|
-
qty: actualFillQty,
|
|
538
|
-
avgPrice: fillPrice,
|
|
539
|
-
latencyMs: roundTripLatency,
|
|
540
|
-
});
|
|
532
|
+
// Debug only - UI handles display via orderFilled event
|
|
533
|
+
debug('FILL Received:', orderTag, fillInfo.transactionType === 1 ? 'BUY' : 'SELL', actualFillQty, '@', fillPrice, 'latency:', roundTripLatency, 'ms');
|
|
541
534
|
|
|
542
535
|
// Clone for fill event (async handlers may need to keep the data)
|
|
543
536
|
service.emit('orderFilled', FillInfoPool.clone(fillInfo));
|
|
544
537
|
} else {
|
|
545
|
-
|
|
538
|
+
// Debug only - UI handles display via orderNotification event
|
|
539
|
+
debug('ORDER STATUS:', orderTag, 'status:', fillInfo.status, 'text:', fillInfo.text || 'N/A');
|
|
546
540
|
}
|
|
547
541
|
} catch (e) {
|
|
548
542
|
debug('Error decoding order notification:', e.message);
|
|
@@ -135,8 +135,8 @@ const fastEntry = (service, orderData) => {
|
|
|
135
135
|
const orderWithRoute = { ...orderData, tradeRoute };
|
|
136
136
|
const order = OrderPool.fill(orderTag, effectiveLoginInfo, orderWithRoute);
|
|
137
137
|
|
|
138
|
-
//
|
|
139
|
-
|
|
138
|
+
// Debug only - UI handles display via events
|
|
139
|
+
debug('ORDER Sending:', orderTag, orderData.side === 0 ? 'BUY' : 'SELL', orderData.size, 'x', orderData.symbol, 'acct:', orderData.accountId, 'route:', tradeRoute);
|
|
140
140
|
|
|
141
141
|
// OPTIMIZED: Use fastEncode with cached type
|
|
142
142
|
const buffer = proto.fastEncode('RequestNewOrder', order);
|
|
@@ -150,7 +150,7 @@ const fastEntry = (service, orderData) => {
|
|
|
150
150
|
service.orderConn.fastSend(buffer);
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
|
|
153
|
+
debug('ORDER Sent to Rithmic:', orderTag, 'buffer:', buffer.length, 'bytes');
|
|
154
154
|
|
|
155
155
|
// Track for round-trip latency measurement
|
|
156
156
|
LatencyTracker.recordEntry(orderTag, entryTime);
|