hedgequantx 2.6.44 → 2.6.46
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
|
@@ -245,14 +245,21 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
245
245
|
risk: maxRisk,
|
|
246
246
|
propfirm: account.propfirm || 'Unknown',
|
|
247
247
|
platform: connectionType,
|
|
248
|
-
|
|
249
|
-
// R
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
//
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
248
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
249
|
+
// R TRADER METRICS - All from Rithmic API (ACCOUNT_PNL_UPDATE 451)
|
|
250
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
251
|
+
pnl: null, // Today's P&L (openPnl + closedPnl)
|
|
252
|
+
openPnl: null, // Unrealized P&L (current position) - from INSTRUMENT_PNL_UPDATE 450
|
|
253
|
+
closedPnl: null, // Realized P&L (closed trades today)
|
|
254
|
+
balance: null, // Account Balance
|
|
255
|
+
buyingPower: null, // Available Buying Power
|
|
256
|
+
margin: null, // Margin Balance
|
|
257
|
+
netLiquidation: null, // Net Liquidation Value (balance + openPnl)
|
|
258
|
+
// Position info (like R Trader Positions panel) - from INSTRUMENT_PNL_UPDATE 450
|
|
259
|
+
position: 0, // Current position qty (+ long, - short)
|
|
260
|
+
entryPrice: 0, // Average entry price
|
|
261
|
+
lastPrice: 0, // Last market price (from ticker)
|
|
262
|
+
// Trading stats
|
|
256
263
|
trades: 0,
|
|
257
264
|
wins: 0,
|
|
258
265
|
losses: 0,
|
|
@@ -379,17 +386,45 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
379
386
|
// Only update for our account
|
|
380
387
|
if (pnlData.accountId !== rithmicAccountId) return;
|
|
381
388
|
|
|
382
|
-
//
|
|
383
|
-
|
|
384
|
-
|
|
389
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
390
|
+
// ACCOUNT_PNL_UPDATE (451) - All R Trader account-level metrics
|
|
391
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
385
392
|
|
|
386
|
-
//
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
393
|
+
// Closed P&L (realized) - from closed trades today
|
|
394
|
+
if (pnlData.closedPositionPnl !== undefined) {
|
|
395
|
+
stats.closedPnl = parseFloat(pnlData.closedPositionPnl);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Account Balance
|
|
399
|
+
if (pnlData.accountBalance !== undefined) {
|
|
400
|
+
stats.balance = parseFloat(pnlData.accountBalance);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Buying Power (Available)
|
|
404
|
+
if (pnlData.availableBuyingPower !== undefined) {
|
|
405
|
+
stats.buyingPower = parseFloat(pnlData.availableBuyingPower);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Margin Balance
|
|
409
|
+
if (pnlData.marginBalance !== undefined) {
|
|
410
|
+
stats.margin = parseFloat(pnlData.marginBalance);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Net Liquidation Value (balance + openPnl) - same as R Trader
|
|
414
|
+
if (pnlData.netLiquidation !== undefined) {
|
|
415
|
+
stats.netLiquidation = parseFloat(pnlData.netLiquidation);
|
|
416
|
+
} else if (stats.balance !== null) {
|
|
417
|
+
// Calculate if not provided directly
|
|
418
|
+
stats.netLiquidation = stats.balance + (stats.openPnl || 0);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Total P&L = openPnl (from positionUpdate) + closedPnl (from pnlUpdate)
|
|
422
|
+
// This matches R Trader's "Today's P&L" calculation
|
|
423
|
+
stats.pnl = (stats.openPnl || 0) + (stats.closedPnl || 0);
|
|
390
424
|
});
|
|
391
425
|
|
|
392
426
|
// Listen to position updates for real-time position tracking (like R Trader)
|
|
427
|
+
// INSTRUMENT_PNL_UPDATE (450) provides per-instrument P&L in real-time
|
|
393
428
|
service.on('positionUpdate', (pos) => {
|
|
394
429
|
if (!pos || pos.accountId !== rithmicAccountId) return;
|
|
395
430
|
if (pos.symbol !== symbolName && !pos.symbol?.includes(symbolName.replace(/[A-Z]\d+$/, ''))) return;
|
|
@@ -398,6 +433,17 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
398
433
|
stats.position = pos.quantity || 0;
|
|
399
434
|
stats.entryPrice = pos.averagePrice || 0;
|
|
400
435
|
currentPosition = pos.quantity || 0;
|
|
436
|
+
|
|
437
|
+
// CRITICAL: Update Open P&L from instrument-level data (real-time, same as R Trader)
|
|
438
|
+
// pos.openPnl comes from INSTRUMENT_PNL_UPDATE (450) - this is the unrealized P&L
|
|
439
|
+
if (pos.openPnl !== undefined && pos.openPnl !== null) {
|
|
440
|
+
stats.openPnl = pos.openPnl;
|
|
441
|
+
}
|
|
442
|
+
// Update day P&L if available
|
|
443
|
+
if (pos.dayPnl !== undefined && pos.dayPnl !== null) {
|
|
444
|
+
// Total P&L = openPnl + closedPnl (same formula as R Trader)
|
|
445
|
+
stats.pnl = (stats.openPnl || 0) + (stats.closedPnl || 0);
|
|
446
|
+
}
|
|
401
447
|
});
|
|
402
448
|
|
|
403
449
|
// Initialize AI Strategy Supervisor - agents observe, learn & optimize
|
package/src/pages/algo/ui.js
CHANGED
|
@@ -210,29 +210,55 @@ class AlgoUI {
|
|
|
210
210
|
|
|
211
211
|
this._line(chalk.cyan(GM));
|
|
212
212
|
|
|
213
|
-
// Row 4:
|
|
213
|
+
// Row 4: Balance | Net Liquidation (R Trader style)
|
|
214
|
+
const balanceStr = stats.balance !== null && stats.balance !== undefined
|
|
215
|
+
? '$' + stats.balance.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
|
216
|
+
: '--';
|
|
217
|
+
const netLiqStr = stats.netLiquidation !== null && stats.netLiquidation !== undefined
|
|
218
|
+
? '$' + stats.netLiquidation.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
|
219
|
+
: '--';
|
|
220
|
+
const r4c1 = buildCell('BALANCE', balanceStr, chalk.white, colL);
|
|
221
|
+
const r4c2 = buildCell('NET LIQ', netLiqStr, chalk.cyan, colR);
|
|
222
|
+
row(r4c1.padded, r4c2.padded);
|
|
223
|
+
|
|
224
|
+
this._line(chalk.cyan(GM));
|
|
225
|
+
|
|
226
|
+
// Row 5: Buying Power | Margin (R Trader style)
|
|
227
|
+
const bpStr = stats.buyingPower !== null && stats.buyingPower !== undefined
|
|
228
|
+
? '$' + stats.buyingPower.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
|
229
|
+
: '--';
|
|
230
|
+
const marginStr = stats.margin !== null && stats.margin !== undefined
|
|
231
|
+
? '$' + stats.margin.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
|
232
|
+
: '--';
|
|
233
|
+
const r5c1 = buildCell('BUYING PWR', bpStr, chalk.green, colL);
|
|
234
|
+
const r5c2 = buildCell('MARGIN', marginStr, chalk.yellow, colR);
|
|
235
|
+
row(r5c1.padded, r5c2.padded);
|
|
236
|
+
|
|
237
|
+
this._line(chalk.cyan(GM));
|
|
238
|
+
|
|
239
|
+
// Row 6: Target | Risk
|
|
214
240
|
const targetStr = stats.target !== null && stats.target !== undefined ? '$' + stats.target.toFixed(2) : '--';
|
|
215
241
|
const riskStr = stats.risk !== null && stats.risk !== undefined ? '$' + stats.risk.toFixed(2) : '--';
|
|
216
|
-
const
|
|
217
|
-
const
|
|
218
|
-
row(
|
|
242
|
+
const r6c1 = buildCell('TARGET', targetStr, chalk.green, colL);
|
|
243
|
+
const r6c2 = buildCell('RISK', riskStr, chalk.red, colR);
|
|
244
|
+
row(r6c1.padded, r6c2.padded);
|
|
219
245
|
|
|
220
246
|
this._line(chalk.cyan(GM));
|
|
221
247
|
|
|
222
|
-
// Row
|
|
223
|
-
const
|
|
224
|
-
const
|
|
248
|
+
// Row 7: Trades W/L | Entry Price (if in position)
|
|
249
|
+
const r7c1t = ` ${chalk.bold('TRADES')}: ${chalk.cyan.bold(stats.trades || 0)} ${chalk.bold('W/L')}: ${chalk.green.bold(stats.wins || 0)}/${chalk.red.bold(stats.losses || 0)}`;
|
|
250
|
+
const r7c1p = ` TRADES: ${stats.trades || 0} W/L: ${stats.wins || 0}/${stats.losses || 0}`;
|
|
225
251
|
const entryStr = stats.entryPrice > 0 ? stats.entryPrice.toFixed(2) : '--';
|
|
226
|
-
const
|
|
227
|
-
row(
|
|
252
|
+
const r7c2 = buildCell('ENTRY', entryStr, chalk.yellow, colR);
|
|
253
|
+
row(r7c1t + pad(colL - r7c1p.length), r7c2.padded);
|
|
228
254
|
|
|
229
255
|
this._line(chalk.cyan(GM));
|
|
230
256
|
|
|
231
|
-
// Row
|
|
257
|
+
// Row 8: Connection | Propfirm
|
|
232
258
|
const connection = stats.platform || 'ProjectX';
|
|
233
|
-
const
|
|
234
|
-
const
|
|
235
|
-
row(
|
|
259
|
+
const r8c1 = buildCell('CONNECTION', connection, chalk.cyan, colL);
|
|
260
|
+
const r8c2 = buildCell('PROPFIRM', stats.propfirm || 'N/A', chalk.cyan, colR);
|
|
261
|
+
row(r8c1.padded, r8c2.padded);
|
|
236
262
|
|
|
237
263
|
this._line(chalk.cyan(GB));
|
|
238
264
|
}
|
|
@@ -109,28 +109,46 @@ const getTradingAccounts = async (service) => {
|
|
|
109
109
|
debug(`Account ${acc.accountId} pnlData:`, JSON.stringify(pnlData));
|
|
110
110
|
debug(` accountPnL map size:`, service.accountPnL.size);
|
|
111
111
|
|
|
112
|
-
// REAL DATA FROM RITHMIC ONLY - NO DEFAULTS
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
const
|
|
116
|
-
const
|
|
112
|
+
// REAL DATA FROM RITHMIC ONLY - NO DEFAULTS (RULES.md compliant)
|
|
113
|
+
// All values are null if not available from API
|
|
114
|
+
const accountBalance = pnlData.accountBalance !== undefined ? parseFloat(pnlData.accountBalance) : null;
|
|
115
|
+
const openPnL = pnlData.openPositionPnl !== undefined ? parseFloat(pnlData.openPositionPnl) : null;
|
|
116
|
+
const closedPnL = pnlData.closedPositionPnl !== undefined ? parseFloat(pnlData.closedPositionPnl) : null;
|
|
117
|
+
const dayPnL = pnlData.dayPnl !== undefined ? parseFloat(pnlData.dayPnl) : null;
|
|
118
|
+
|
|
119
|
+
// R Trader additional metrics (from ACCOUNT_PNL_UPDATE 451)
|
|
120
|
+
const buyingPower = pnlData.availableBuyingPower !== undefined ? parseFloat(pnlData.availableBuyingPower) : null;
|
|
121
|
+
const margin = pnlData.marginBalance !== undefined ? parseFloat(pnlData.marginBalance) : null;
|
|
122
|
+
const cashOnHand = pnlData.cashOnHand !== undefined ? parseFloat(pnlData.cashOnHand) : null;
|
|
117
123
|
|
|
118
124
|
// Calculate P&L like R Trader: openPositionPnl + closedPositionPnl
|
|
119
125
|
// This matches what R Trader shows as "Today's P&L"
|
|
120
126
|
const totalPnL = (openPnL !== null || closedPnL !== null)
|
|
121
127
|
? (openPnL || 0) + (closedPnL || 0)
|
|
122
128
|
: null;
|
|
129
|
+
|
|
130
|
+
// Net Liquidation Value = Account Balance + Open P&L (same as R Trader)
|
|
131
|
+
const netLiquidation = (accountBalance !== null || openPnL !== null)
|
|
132
|
+
? (accountBalance || 0) + (openPnL || 0)
|
|
133
|
+
: null;
|
|
123
134
|
|
|
124
135
|
return {
|
|
125
136
|
accountId: hashAccountId(acc.accountId),
|
|
126
137
|
rithmicAccountId: acc.accountId,
|
|
127
138
|
accountName: acc.accountId, // Never expose real name - only account ID
|
|
128
139
|
name: acc.accountId, // Never expose real name - only account ID
|
|
140
|
+
// Core metrics (same as R Trader)
|
|
129
141
|
balance: accountBalance,
|
|
130
142
|
profitAndLoss: totalPnL, // Same as R Trader: open + closed
|
|
131
143
|
openPnL: openPnL,
|
|
132
144
|
closedPnL: closedPnL,
|
|
133
|
-
dayPnL: dayPnL,
|
|
145
|
+
dayPnL: dayPnL,
|
|
146
|
+
// R Trader additional metrics
|
|
147
|
+
buyingPower: buyingPower,
|
|
148
|
+
margin: margin,
|
|
149
|
+
cashOnHand: cashOnHand,
|
|
150
|
+
netLiquidation: netLiquidation,
|
|
151
|
+
// Meta
|
|
134
152
|
status: 0,
|
|
135
153
|
platform: 'Rithmic',
|
|
136
154
|
propfirm: service.propfirm.name,
|
|
@@ -332,17 +332,23 @@ const handleAccountPnLUpdate = (service, data) => {
|
|
|
332
332
|
debug('Decoded Account PNL:', JSON.stringify(pnl));
|
|
333
333
|
|
|
334
334
|
if (pnl.accountId) {
|
|
335
|
+
// Store ALL R Trader metrics from ACCOUNT_PNL_UPDATE (451)
|
|
335
336
|
const pnlData = {
|
|
337
|
+
// Core P&L
|
|
336
338
|
accountBalance: parseFloat(pnl.accountBalance || 0),
|
|
337
|
-
cashOnHand: parseFloat(pnl.cashOnHand || 0),
|
|
338
|
-
marginBalance: parseFloat(pnl.marginBalance || 0),
|
|
339
339
|
openPositionPnl: parseFloat(pnl.openPositionPnl || 0),
|
|
340
340
|
closedPositionPnl: parseFloat(pnl.closedPositionPnl || 0),
|
|
341
341
|
dayPnl: parseFloat(pnl.dayPnl || 0),
|
|
342
|
+
// R Trader additional metrics
|
|
343
|
+
availableBuyingPower: parseFloat(pnl.availableBuyingPower || 0),
|
|
344
|
+
marginBalance: parseFloat(pnl.marginBalance || 0),
|
|
345
|
+
cashOnHand: parseFloat(pnl.cashOnHand || 0),
|
|
346
|
+
// Net Liquidation = Account Balance + Open P&L (same as R Trader)
|
|
347
|
+
netLiquidation: parseFloat(pnl.accountBalance || 0) + parseFloat(pnl.openPositionPnl || 0),
|
|
342
348
|
};
|
|
343
349
|
debug('Storing PNL for account:', pnl.accountId, pnlData);
|
|
344
350
|
service.accountPnL.set(pnl.accountId, pnlData);
|
|
345
|
-
service.emit('pnlUpdate', pnl);
|
|
351
|
+
service.emit('pnlUpdate', { accountId: pnl.accountId, ...pnlData });
|
|
346
352
|
} else {
|
|
347
353
|
debug('No accountId in PNL response');
|
|
348
354
|
}
|