hedgequantx 2.6.43 → 2.6.45

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.43",
3
+ "version": "2.6.45",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -246,6 +246,13 @@ const launchAlgo = async (service, account, contract, config) => {
246
246
  propfirm: account.propfirm || 'Unknown',
247
247
  platform: connectionType,
248
248
  pnl: 0,
249
+ // R Trader style P&L breakdown
250
+ openPnl: 0, // Unrealized P&L (current position)
251
+ closedPnl: 0, // Realized P&L (closed trades today)
252
+ // Position info (like R Trader)
253
+ position: 0, // Current position qty (+ long, - short)
254
+ entryPrice: 0, // Average entry price
255
+ lastPrice: 0, // Last market price
249
256
  trades: 0,
250
257
  wins: 0,
251
258
  losses: 0,
@@ -372,21 +379,39 @@ const launchAlgo = async (service, account, contract, config) => {
372
379
  // Only update for our account
373
380
  if (pnlData.accountId !== rithmicAccountId) return;
374
381
 
375
- // Calculate P&L exactly like R Trader: openPositionPnl + closedPositionPnl
376
- const openPnl = parseFloat(pnlData.openPositionPnl || 0);
382
+ // ACCOUNT_PNL_UPDATE (451) provides account-level totals
383
+ // closedPositionPnl = realized P&L from all closed trades today
377
384
  const closedPnl = parseFloat(pnlData.closedPositionPnl || 0);
378
385
 
379
- // R Trader "Today's P&L" = unrealized + realized
380
- stats.pnl = openPnl + closedPnl;
386
+ // Update closed P&L (realized) from account-level data
387
+ stats.closedPnl = closedPnl;
388
+
389
+ // Total P&L = openPnl (from positionUpdate) + closedPnl (from pnlUpdate)
390
+ // This matches R Trader's "Today's P&L" calculation
391
+ stats.pnl = (stats.openPnl || 0) + (stats.closedPnl || 0);
381
392
  });
382
393
 
383
- // Also listen to position updates for real-time position tracking
394
+ // Listen to position updates for real-time position tracking (like R Trader)
395
+ // INSTRUMENT_PNL_UPDATE (450) provides per-instrument P&L in real-time
384
396
  service.on('positionUpdate', (pos) => {
385
397
  if (!pos || pos.accountId !== rithmicAccountId) return;
386
398
  if (pos.symbol !== symbolName && !pos.symbol?.includes(symbolName.replace(/[A-Z]\d+$/, ''))) return;
387
399
 
388
- // Update current position from WebSocket
400
+ // Update position info (like R Trader Positions panel)
401
+ stats.position = pos.quantity || 0;
402
+ stats.entryPrice = pos.averagePrice || 0;
389
403
  currentPosition = pos.quantity || 0;
404
+
405
+ // CRITICAL: Update Open P&L from instrument-level data (real-time, same as R Trader)
406
+ // pos.openPnl comes from INSTRUMENT_PNL_UPDATE (450) - this is the unrealized P&L
407
+ if (pos.openPnl !== undefined && pos.openPnl !== null) {
408
+ stats.openPnl = pos.openPnl;
409
+ }
410
+ // Update day P&L if available
411
+ if (pos.dayPnl !== undefined && pos.dayPnl !== null) {
412
+ // Total P&L = openPnl + closedPnl (same formula as R Trader)
413
+ stats.pnl = (stats.openPnl || 0) + (stats.closedPnl || 0);
414
+ }
390
415
  });
391
416
 
392
417
  // Initialize AI Strategy Supervisor - agents observe, learn & optimize
@@ -624,6 +649,9 @@ const launchAlgo = async (service, account, contract, config) => {
624
649
  timestamp: tick.timestamp || Date.now()
625
650
  };
626
651
 
652
+ // Update last price for UI (like R Trader)
653
+ stats.lastPrice = tickData.price;
654
+
627
655
  // Feed tick to AI supervisor (agents observe same data as strategy)
628
656
  if (stats.aiSupervision) {
629
657
  StrategySupervisor.feedTick(tickData);
@@ -182,35 +182,57 @@ class AlgoUI {
182
182
 
183
183
  this._line(chalk.cyan(GM));
184
184
 
185
- // Row 2: Qty | P&L
186
- const r2c1 = buildCell('QTY', (stats.qty || '1').toString(), chalk.cyan, colL);
185
+ // Row 2: Position | P&L (like R Trader)
186
+ const posQty = stats.position || 0;
187
+ const posStr = posQty === 0 ? 'FLAT' : (posQty > 0 ? `+${posQty} LONG` : `${posQty} SHORT`);
188
+ const posColor = posQty === 0 ? chalk.gray : (posQty > 0 ? chalk.green : chalk.red);
189
+ const r2c1 = buildCell('POSITION', posStr, posColor, colL);
187
190
  const r2c2 = buildCell('P&L', pnlStr, pnlColor, colR);
188
191
  row(r2c1.padded, r2c2.padded);
189
192
 
190
193
  this._line(chalk.cyan(GM));
191
194
 
192
- // Row 3: Target | Risk
195
+ // Row 3: Open P&L | Closed P&L (R Trader style breakdown)
196
+ // Data from Rithmic PNL_PLANT WebSocket - show '--' if null (no data from API)
197
+ const openPnl = stats.openPnl;
198
+ const closedPnl = stats.closedPnl;
199
+ const openPnlStr = openPnl === null || openPnl === undefined
200
+ ? '--'
201
+ : (openPnl >= 0 ? `+$${openPnl.toFixed(2)}` : `-$${Math.abs(openPnl).toFixed(2)}`);
202
+ const closedPnlStr = closedPnl === null || closedPnl === undefined
203
+ ? '--'
204
+ : (closedPnl >= 0 ? `+$${closedPnl.toFixed(2)}` : `-$${Math.abs(closedPnl).toFixed(2)}`);
205
+ const openPnlColor = (openPnl || 0) >= 0 ? chalk.green : chalk.red;
206
+ const closedPnlColor = (closedPnl || 0) >= 0 ? chalk.green : chalk.red;
207
+ const r3c1 = buildCell('OPEN P&L', openPnlStr, openPnlColor, colL);
208
+ const r3c2 = buildCell('CLOSED P&L', closedPnlStr, closedPnlColor, colR);
209
+ row(r3c1.padded, r3c2.padded);
210
+
211
+ this._line(chalk.cyan(GM));
212
+
213
+ // Row 4: Target | Risk
193
214
  const targetStr = stats.target !== null && stats.target !== undefined ? '$' + stats.target.toFixed(2) : '--';
194
215
  const riskStr = stats.risk !== null && stats.risk !== undefined ? '$' + stats.risk.toFixed(2) : '--';
195
- const r3c1 = buildCell('TARGET', targetStr, chalk.green, colL);
196
- const r3c2 = buildCell('RISK', riskStr, chalk.red, colR);
197
- row(r3c1.padded, r3c2.padded);
216
+ const r4c1 = buildCell('TARGET', targetStr, chalk.green, colL);
217
+ const r4c2 = buildCell('RISK', riskStr, chalk.red, colR);
218
+ row(r4c1.padded, r4c2.padded);
198
219
 
199
220
  this._line(chalk.cyan(GM));
200
221
 
201
- // Row 4: Trades | Latency (API response time) - UPPERCASE BOLD
202
- const r4c1t = ` ${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)}`;
203
- const r4c1p = ` TRADES: ${stats.trades || 0} W/L: ${stats.wins || 0}/${stats.losses || 0}`;
204
- const r4c2 = buildCell('LATENCY', `${stats.latency || 0}MS`, latencyColor, colR);
205
- row(r4c1t + pad(colL - r4c1p.length), r4c2.padded);
222
+ // Row 5: Trades W/L | Entry Price (if in position)
223
+ const r5c1t = ` ${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)}`;
224
+ const r5c1p = ` TRADES: ${stats.trades || 0} W/L: ${stats.wins || 0}/${stats.losses || 0}`;
225
+ const entryStr = stats.entryPrice > 0 ? stats.entryPrice.toFixed(2) : '--';
226
+ const r5c2 = buildCell('ENTRY', entryStr, chalk.yellow, colR);
227
+ row(r5c1t + pad(colL - r5c1p.length), r5c2.padded);
206
228
 
207
229
  this._line(chalk.cyan(GM));
208
230
 
209
- // Row 5: Connection | Propfirm
231
+ // Row 6: Connection | Propfirm
210
232
  const connection = stats.platform || 'ProjectX';
211
- const r5c1 = buildCell('CONNECTION', connection, chalk.cyan, colL);
212
- const r5c2 = buildCell('PROPFIRM', stats.propfirm || 'N/A', chalk.cyan, colR);
213
- row(r5c1.padded, r5c2.padded);
233
+ const r6c1 = buildCell('CONNECTION', connection, chalk.cyan, colL);
234
+ const r6c2 = buildCell('PROPFIRM', stats.propfirm || 'N/A', chalk.cyan, colR);
235
+ row(r6c1.padded, r6c2.padded);
214
236
 
215
237
  this._line(chalk.cyan(GB));
216
238
  }