hedgequantx 2.6.57 → 2.6.59

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.57",
3
+ "version": "2.6.59",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -359,6 +359,7 @@ class CopyEngine {
359
359
 
360
360
  if (successCount === this.followers.length) {
361
361
  this.stats.trades++;
362
+ this.stats.sessionPnl += pnl; // Track session P&L
362
363
  if (pnl >= 0) this.stats.wins++;
363
364
  else this.stats.losses++;
364
365
  }
@@ -867,6 +868,7 @@ const launchCopyTrading = async (config) => {
867
868
  target: dailyTarget,
868
869
  risk: maxRisk,
869
870
  pnl: 0,
871
+ sessionPnl: 0, // P&L from THIS session only (HQX trades)
870
872
  trades: 0,
871
873
  wins: 0,
872
874
  losses: 0,
@@ -263,6 +263,7 @@ const launchAlgo = async (service, account, contract, config) => {
263
263
  trades: 0,
264
264
  wins: 0,
265
265
  losses: 0,
266
+ sessionPnl: 0, // P&L from THIS session only (HQX trades)
266
267
  latency: 0,
267
268
  connected: false,
268
269
  startTime: Date.now(),
@@ -314,67 +315,53 @@ const launchAlgo = async (service, account, contract, config) => {
314
315
 
315
316
  positionManager.start();
316
317
 
317
- // Listen for position manager events
318
+ // Listen for position manager events - CLEAN LOGS (no verbose order status)
318
319
  positionManager.on('entryFilled', ({ orderTag, position, fillLatencyMs }) => {
319
320
  stats.entryLatencies.push(fillLatencyMs);
320
321
  stats.avgFillLatency = stats.entryLatencies.reduce((a, b) => a + b, 0) / stats.entryLatencies.length;
321
- algoLogger.info(ui, 'FAST FILL', `${fillLatencyMs}ms | avg=${stats.avgFillLatency.toFixed(1)}ms`);
322
+ const side = position.side === 0 ? 'LONG' : 'SHORT';
323
+ algoLogger.info(ui, 'FILLED', `${side} ${position.size}x ${symbolName} @ ${position.entryPrice} | ${fillLatencyMs}ms`);
322
324
  });
323
325
 
324
326
  positionManager.on('exitFilled', ({ orderTag, exitPrice, pnlTicks, holdDurationMs }) => {
327
+ const holdSec = (holdDurationMs / 1000).toFixed(1);
325
328
  // Calculate PnL in dollars only if tickValue is available from API
326
329
  if (pnlTicks !== null && tickValue !== null) {
327
330
  const pnlDollars = pnlTicks * tickValue;
331
+ stats.sessionPnl += pnlDollars; // Track session P&L
328
332
  if (pnlDollars >= 0) {
329
333
  stats.wins++;
330
- algoLogger.targetHit(ui, symbolName, exitPrice, pnlDollars);
334
+ algoLogger.info(ui, 'WIN', `+$${pnlDollars.toFixed(2)} @ ${exitPrice} | ${holdSec}s`);
331
335
  } else {
332
336
  stats.losses++;
333
- algoLogger.stopHit(ui, symbolName, exitPrice, Math.abs(pnlDollars));
337
+ algoLogger.info(ui, 'LOSS', `-$${Math.abs(pnlDollars).toFixed(2)} @ ${exitPrice} | ${holdSec}s`);
334
338
  }
335
339
  } else {
336
340
  // Log with ticks only if tickValue unavailable
337
341
  if (pnlTicks !== null && pnlTicks >= 0) {
338
342
  stats.wins++;
339
- algoLogger.info(ui, 'TARGET', `+${pnlTicks} ticks`);
343
+ algoLogger.info(ui, 'WIN', `+${pnlTicks} ticks | ${holdSec}s`);
340
344
  } else if (pnlTicks !== null) {
341
345
  stats.losses++;
342
- algoLogger.info(ui, 'STOP', `${pnlTicks} ticks`);
346
+ algoLogger.info(ui, 'LOSS', `${pnlTicks} ticks | ${holdSec}s`);
343
347
  }
344
348
  }
345
349
  stats.trades++;
346
350
  currentPosition = 0;
347
351
  stats.position = 0; // Reset UI position display
348
352
  pendingOrder = false;
349
- algoLogger.info(ui, 'HOLD TIME', `${(holdDurationMs / 1000).toFixed(1)}s`);
350
353
  });
351
354
 
352
355
  positionManager.on('holdComplete', ({ orderTag, position }) => {
353
- algoLogger.info(ui, 'HOLD COMPLETE', `${FAST_SCALPING.MIN_HOLD_MS / 1000}s minimum reached`);
356
+ algoLogger.info(ui, 'READY', `Hold complete - monitoring exit`);
354
357
  });
355
358
 
356
359
  positionManager.on('exitOrderFired', ({ orderTag, exitReason, latencyMs }) => {
357
- algoLogger.info(ui, 'EXIT FIRED', `${exitReason.reason} | ${latencyMs.toFixed(1)}ms`);
360
+ // Don't log here - exitFilled will log the result
358
361
  });
359
362
 
360
- // DEBUG: Listen for raw order notifications from Rithmic
361
- service.on('orderAccepted', (data) => {
362
- algoLogger.info(ui, 'ORDER ACCEPTED', `tag=${data.orderTag} basket=${data.basketId}`);
363
- });
364
-
365
- service.on('orderNotification', (data) => {
366
- const status = data.status || 'unknown';
367
- const fillQty = data.fillQuantity || data.totalFillQuantity || 0;
368
- if (fillQty > 0) {
369
- algoLogger.info(ui, 'ORDER FILL', `tag=${data.orderTag} qty=${fillQty} @ ${data.avgFillPrice}`);
370
- } else {
371
- algoLogger.info(ui, 'ORDER STATUS', `tag=${data.orderTag} status=${status}`);
372
- }
373
- });
374
-
375
- service.on('orderFilled', (data) => {
376
- algoLogger.info(ui, 'FILL CONFIRMED', `${data.transactionType === 1 ? 'BUY' : 'SELL'} ${data.fillQuantity}x @ ${data.avgFillPrice}`);
377
- });
363
+ // NOTE: Removed verbose DEBUG logs (ORDER ACCEPTED, ORDER STATUS, ORDER FILL, FILL CONFIRMED)
364
+ // Entry/Exit fills are logged by positionManager events (entryFilled, exitFilled)
378
365
  }
379
366
 
380
367
  // ═══════════════════════════════════════════════════════════════════════════
@@ -537,11 +524,11 @@ const launchAlgo = async (service, account, contract, config) => {
537
524
  }
538
525
 
539
526
  const riskPct = Math.round((riskAmount / maxRisk) * 100);
540
- algoLogger.positionSized(ui, contracts, kelly, riskAmount, riskPct);
541
527
 
542
528
  // Place order via API
543
529
  pendingOrder = true;
544
530
  const orderSide = direction === 'long' ? 0 : 1; // 0=Buy, 1=Sell
531
+ const sideStr = direction === 'long' ? 'LONG' : 'SHORT';
545
532
 
546
533
  try {
547
534
  // ═══════════════════════════════════════════════════════════════
@@ -557,49 +544,29 @@ const launchAlgo = async (service, account, contract, config) => {
557
544
  };
558
545
 
559
546
  // CRITICAL: Use rithmicAccountId (original) not accountId (hash) for Rithmic orders
560
- // The accountId field is hashed for display, but Rithmic API needs the original
561
547
  if (account.rithmicAccountId) {
562
548
  orderData.accountId = account.rithmicAccountId;
563
549
  }
564
550
 
565
- // Log order details before sending
566
- algoLogger.info(ui, 'ORDER DATA', `acct=${orderData.accountId} sym=${orderData.symbol} side=${orderData.side} qty=${orderData.size}`);
551
+ // Log entry attempt (single line)
552
+ algoLogger.info(ui, 'ENTRY', `${sideStr} ${contracts}x ${symbolName} | risk: $${riskAmount} (${riskPct}%)`);
567
553
 
568
554
  // Fire-and-forget entry (no await on fill)
569
555
  const entryResult = service.fastEntry(orderData);
570
556
 
571
- // Log the order tag for tracking
572
- algoLogger.info(ui, 'ORDER TAG', `${entryResult.orderTag} | success=${entryResult.success}`);
573
-
574
557
  if (entryResult.success) {
575
558
  // Register with position manager for lifecycle tracking
576
- // Pass contract info from API (NOT hardcoded)
577
- const contractInfo = {
578
- tickSize,
579
- tickValue,
580
- contractId,
581
- };
559
+ const contractInfo = { tickSize, tickValue, contractId };
582
560
  positionManager.registerEntry(entryResult, orderData, contractInfo);
583
561
 
584
562
  currentPosition = direction === 'long' ? contracts : -contracts;
585
- const sideStr = direction === 'long' ? 'BUY' : 'SELL';
586
-
587
- // Log with latency
588
- const latencyColor = entryResult.latencyMs < FAST_SCALPING.LATENCY_TARGET_MS
589
- ? chalk.green
590
- : entryResult.latencyMs < FAST_SCALPING.LATENCY_WARN_MS
591
- ? chalk.yellow
592
- : chalk.red;
593
563
 
564
+ // Update avg entry latency
594
565
  stats.avgEntryLatency = stats.entryLatencies.length > 0
595
566
  ? (stats.avgEntryLatency * stats.entryLatencies.length + entryResult.latencyMs) / (stats.entryLatencies.length + 1)
596
567
  : entryResult.latencyMs;
597
568
 
598
- algoLogger.info(ui, 'FAST ENTRY', `${sideStr} ${contracts}x ${symbolName} | ${latencyColor(entryResult.latencyMs.toFixed(2) + 'ms')}`);
599
- algoLogger.info(ui, 'HOLD START', `Min ${FAST_SCALPING.MIN_HOLD_MS / 1000}s before exit`);
600
-
601
- // Note: NO bracket orders in fast path
602
- // PositionManager handles exit logic after 10s hold
569
+ // Note: Fill confirmation logged by positionManager.on('entryFilled')
603
570
 
604
571
  } else {
605
572
  algoLogger.orderRejected(ui, symbolName, entryResult.error || 'Fast entry failed');
@@ -468,12 +468,13 @@ const renderSessionSummary = (stats, stopReason) => {
468
468
 
469
469
  console.log(chalk.cyan(GM));
470
470
 
471
- // Row 4: P&L | Target
472
- const pnl = stats.pnl || 0;
471
+ // Row 4: Session P&L | Target
472
+ // Use sessionPnl (trades from this HQX session) if available, fallback to pnl
473
+ const pnl = stats.sessionPnl !== undefined ? stats.sessionPnl : (stats.pnl || 0);
473
474
  const pnlStr = `${pnl >= 0 ? '+' : ''}$${Math.abs(pnl).toFixed(2)}`;
474
475
  const pnlColor = pnl >= 0 ? chalk.green : chalk.red;
475
476
  const targetStr = `$${(stats.target || 0).toFixed(2)}`;
476
- row('P&L', pnlStr, pnlColor, 'TARGET', targetStr, chalk.cyan);
477
+ row('Session P&L', pnlStr, pnlColor, 'TARGET', targetStr, chalk.cyan);
477
478
 
478
479
  // Bottom border
479
480
  console.log(chalk.cyan(BOX.BOT + BOX.H.repeat(W) + BOX.BR));