hedgequantx 2.6.42 → 2.6.44

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.42",
3
+ "version": "2.6.44",
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,20 +379,24 @@ 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
382
+ // R Trader style P&L breakdown
376
383
  const openPnl = parseFloat(pnlData.openPositionPnl || 0);
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 stats (like R Trader display)
387
+ stats.openPnl = openPnl; // Unrealized
388
+ stats.closedPnl = closedPnl; // Realized
389
+ stats.pnl = openPnl + closedPnl; // Total (same as R Trader "Today's P&L")
381
390
  });
382
391
 
383
- // Also listen to position updates for real-time position tracking
392
+ // Listen to position updates for real-time position tracking (like R Trader)
384
393
  service.on('positionUpdate', (pos) => {
385
394
  if (!pos || pos.accountId !== rithmicAccountId) return;
386
395
  if (pos.symbol !== symbolName && !pos.symbol?.includes(symbolName.replace(/[A-Z]\d+$/, ''))) return;
387
396
 
388
- // Update current position from WebSocket
397
+ // Update position info (like R Trader Positions panel)
398
+ stats.position = pos.quantity || 0;
399
+ stats.entryPrice = pos.averagePrice || 0;
389
400
  currentPosition = pos.quantity || 0;
390
401
  });
391
402
 
@@ -624,6 +635,9 @@ const launchAlgo = async (service, account, contract, config) => {
624
635
  timestamp: tick.timestamp || Date.now()
625
636
  };
626
637
 
638
+ // Update last price for UI (like R Trader)
639
+ stats.lastPrice = tickData.price;
640
+
627
641
  // Feed tick to AI supervisor (agents observe same data as strategy)
628
642
  if (stats.aiSupervision) {
629
643
  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
  }
@@ -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
- console.log(`[FILL] Received: ${orderTag} | ${fillInfo.transactionType === 1 ? 'BUY' : 'SELL'} ${actualFillQty}x @ ${fillPrice} | latency=${roundTripLatency}ms`);
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
- console.log(`[ORDER STATUS] ${orderTag} | status=${fillInfo.status} text=${fillInfo.text || 'N/A'}`);
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
- // DEBUG: Log order details
139
- console.log(`[ORDER] Sending: ${orderTag} | ${orderData.side === 0 ? 'BUY' : 'SELL'} ${orderData.size}x ${orderData.symbol} | acct=${orderData.accountId} | route=${tradeRoute}`);
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
- console.log(`[ORDER] Sent to Rithmic: ${orderTag} | buffer=${buffer.length} bytes`);
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);