hedgequantx 2.6.59 → 2.6.61

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.59",
3
+ "version": "2.6.61",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -143,11 +143,15 @@ const FAST_SCALPING = {
143
143
  MAX_HOLD_MS: 60000, // 60 seconds failsafe (force exit if stuck)
144
144
 
145
145
  // Exit targets (in ticks) - defaults, override per symbol
146
- TARGET_TICKS: 16, // Take profit target
147
- STOP_TICKS: 20, // Stop loss
146
+ TARGET_TICKS: 16, // Take profit target (+$80 on NQ)
147
+ STOP_TICKS: 20, // Stop loss (-$100 on NQ)
148
+
149
+ // Breakeven (BE) - move SL to entry price after profit threshold
150
+ BREAKEVEN_ACTIVATION_TICKS: 6, // Activate BE after +6 ticks profit (+$30 on NQ)
151
+ BREAKEVEN_OFFSET_TICKS: 1, // BE at entry + 1 tick (small profit lock)
148
152
 
149
153
  // Trailing stop (activates after MIN_HOLD + profit threshold)
150
- TRAILING_ACTIVATION_TICKS: 8, // Start trailing after +8 ticks profit
154
+ TRAILING_ACTIVATION_TICKS: 8, // Start trailing after +8 ticks profit (+$40 on NQ)
151
155
  TRAILING_DISTANCE_TICKS: 4, // Trail 4 ticks behind high/low
152
156
 
153
157
  // Position monitoring
@@ -877,12 +877,14 @@ const launchCopyTrading = async (config) => {
877
877
  platform: lead.type === 'rithmic' ? 'Rithmic' : 'ProjectX',
878
878
  startTime: Date.now(),
879
879
  aiSupervision: false,
880
- aiMode: null
880
+ aiMode: null,
881
+ agentCount: 0 // Number of AI agents active
881
882
  };
882
883
 
883
884
  // Initialize AI Supervisor - only if user enabled it
884
885
  if (enableAI) {
885
886
  const aiAgents = aiService.getAgents();
887
+ stats.agentCount = aiAgents.length;
886
888
  if (aiAgents.length > 0) {
887
889
  const supervisorResult = StrategySupervisor.initialize(null, aiAgents, lead.service, lead.account.accountId);
888
890
  stats.aiSupervision = supervisorResult.success;
@@ -269,6 +269,7 @@ const launchAlgo = async (service, account, contract, config) => {
269
269
  startTime: Date.now(),
270
270
  aiSupervision: false,
271
271
  aiMode: null,
272
+ agentCount: 0, // Number of AI agents active
272
273
  // Fast path stats
273
274
  fastPath: useFastPath,
274
275
  avgEntryLatency: 0,
@@ -356,6 +357,10 @@ const launchAlgo = async (service, account, contract, config) => {
356
357
  algoLogger.info(ui, 'READY', `Hold complete - monitoring exit`);
357
358
  });
358
359
 
360
+ positionManager.on('breakevenActivated', ({ orderTag, position, breakevenPrice, pnlTicks }) => {
361
+ algoLogger.info(ui, 'BE', `Breakeven activated @ ${breakevenPrice} | +${pnlTicks} ticks`);
362
+ });
363
+
359
364
  positionManager.on('exitOrderFired', ({ orderTag, exitReason, latencyMs }) => {
360
365
  // Don't log here - exitFilled will log the result
361
366
  });
@@ -450,6 +455,7 @@ const launchAlgo = async (service, account, contract, config) => {
450
455
  if (config.enableAI) {
451
456
  const aiAgents = aiService.getAgents();
452
457
  aiAgentCount = aiAgents.length;
458
+ stats.agentCount = aiAgentCount;
453
459
  if (aiAgents.length > 0) {
454
460
  const supervisorResult = StrategySupervisor.initialize(strategy, aiAgents, service, account.accountId);
455
461
  stats.aiSupervision = supervisorResult.success;
@@ -239,6 +239,18 @@ class AlgoUI {
239
239
  const r4c2 = buildCell('PROPFIRM', stats.propfirm || 'N/A', chalk.cyan, colR);
240
240
  row(r4c1t + pad(colL - r4c1p.length), r4c2.padded);
241
241
 
242
+ this._line(chalk.cyan(GM));
243
+
244
+ // Row 5: Connection | Agents
245
+ const connectionType = stats.platform || stats.connection || 'N/A';
246
+ const connectionColor = connectionType.toLowerCase().includes('rithmic') ? chalk.green : chalk.yellow;
247
+ const agentCount = stats.agentCount || 0;
248
+ const agentStr = agentCount > 0 ? `${agentCount} agent${agentCount > 1 ? 's' : ''} active` : 'None';
249
+ const agentColor = agentCount > 0 ? chalk.green : chalk.gray;
250
+ const r5c1 = buildCell('CONNECTION', connectionType, connectionColor, colL);
251
+ const r5c2 = buildCell('AGENTS', agentStr, agentColor, colR);
252
+ row(r5c1.padded, r5c2.padded);
253
+
242
254
  this._line(chalk.cyan(GB));
243
255
  }
244
256
 
@@ -58,6 +58,8 @@ const WEIGHTS = {
58
58
  * @property {number} lowWaterMark - Lowest price since entry (for trailing)
59
59
  * @property {string} status - 'pending' | 'active' | 'holding' | 'exiting' | 'closed'
60
60
  * @property {boolean} holdComplete - True after MIN_HOLD_MS elapsed
61
+ * @property {boolean} breakevenActive - True after BE threshold reached
62
+ * @property {number|null} breakevenPrice - Price at which BE stop is set
61
63
  * @property {Object|null} exitReason - Why position was exited
62
64
  * @property {number} tickSize - Tick size from API
63
65
  * @property {number} tickValue - Tick value from API
@@ -214,6 +216,8 @@ class PositionManager extends EventEmitter {
214
216
  lowWaterMark: null,
215
217
  status: 'pending', // Waiting for fill confirmation
216
218
  holdComplete: false,
219
+ breakevenActive: false, // BE not yet activated
220
+ breakevenPrice: null, // Will be set when BE activates
217
221
  exitReason: null,
218
222
  latencyMs,
219
223
  // Contract info from API (NOT hardcoded)
@@ -483,18 +487,64 @@ class PositionManager extends EventEmitter {
483
487
  return { type: 'target', reason: 'Target reached', pnlTicks };
484
488
  }
485
489
 
486
- // 2. STOP HIT - Always exit at stop
487
- if (pnlTicks <= -stopTicks) {
490
+ // 2. BREAKEVEN CHECK - If BE is active, use BE price as stop
491
+ if (position.breakevenActive && position.breakevenPrice !== null) {
492
+ const tickSize = this._getTickSize(position);
493
+ if (tickSize) {
494
+ // Check if price hit breakeven stop
495
+ if (position.side === 0) { // Long
496
+ if (currentPrice <= position.breakevenPrice) {
497
+ return { type: 'breakeven', reason: 'Breakeven stop hit', pnlTicks };
498
+ }
499
+ } else { // Short
500
+ if (currentPrice >= position.breakevenPrice) {
501
+ return { type: 'breakeven', reason: 'Breakeven stop hit', pnlTicks };
502
+ }
503
+ }
504
+ }
505
+ }
506
+
507
+ // 3. STOP HIT - Only if BE not active (original stop)
508
+ if (!position.breakevenActive && pnlTicks <= -stopTicks) {
488
509
  return { type: 'stop', reason: 'Stop loss hit', pnlTicks };
489
510
  }
490
511
 
491
- // 3. VPIN DANGER - Informed traders detected (from strategy)
512
+ // 4. ACTIVATE BREAKEVEN - Move stop to entry after profit threshold
513
+ if (!position.breakevenActive && pnlTicks >= FAST_SCALPING.BREAKEVEN_ACTIVATION_TICKS) {
514
+ const tickSize = this._getTickSize(position);
515
+ if (tickSize && position.entryPrice) {
516
+ // Set BE price at entry + offset (small profit lock)
517
+ const offset = FAST_SCALPING.BREAKEVEN_OFFSET_TICKS * tickSize;
518
+ if (position.side === 0) { // Long
519
+ position.breakevenPrice = position.entryPrice + offset;
520
+ } else { // Short
521
+ position.breakevenPrice = position.entryPrice - offset;
522
+ }
523
+ position.breakevenActive = true;
524
+
525
+ log.info('BREAKEVEN ACTIVATED', {
526
+ symbol: position.symbol,
527
+ entryPrice: position.entryPrice,
528
+ bePrice: position.breakevenPrice,
529
+ pnlTicks,
530
+ });
531
+
532
+ this.emit('breakevenActivated', {
533
+ orderTag: position.orderTag,
534
+ position,
535
+ breakevenPrice: position.breakevenPrice,
536
+ pnlTicks,
537
+ });
538
+ }
539
+ }
540
+
541
+ // 5. VPIN DANGER - Informed traders detected (from strategy)
492
542
  const vpin = this._getVPIN(position);
493
543
  if (vpin !== null && vpin > MOMENTUM.VPIN_DANGER) {
494
544
  return { type: 'vpin', reason: `VPIN spike ${(vpin * 100).toFixed(0)}% - informed traders`, pnlTicks, vpin };
495
545
  }
496
546
 
497
- // 4. TRAILING STOP (only if in profit above threshold)
547
+ // 6. TRAILING STOP (only if in profit above threshold)
498
548
  if (pnlTicks >= FAST_SCALPING.TRAILING_ACTIVATION_TICKS) {
499
549
  const trailingPnl = this._calculateTrailingPnl(position, currentPrice);
500
550
  if (trailingPnl !== null && trailingPnl <= -FAST_SCALPING.TRAILING_DISTANCE_TICKS) {
@@ -502,7 +552,7 @@ class PositionManager extends EventEmitter {
502
552
  }
503
553
  }
504
554
 
505
- // 5. MOMENTUM-BASED EXIT (using strategy's math models)
555
+ // 7. MOMENTUM-BASED EXIT (using strategy's math models)
506
556
  const momentum = this._calculateMomentum(position);
507
557
 
508
558
  if (momentum !== null) {