hedgequantx 2.9.81 → 2.9.84

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.
@@ -65,10 +65,16 @@ var require_config = __commonJS({
65
65
  commissionPerSide: 2
66
66
  // $4 round-trip
67
67
  },
68
- // Session (Futures Market Hours - Sunday 18:00 to Friday 17:00 EST)
68
+ // Session filter (US Regular Hours only - matches backtest)
69
69
  session: {
70
- enabled: false,
71
- // Trade anytime markets are open
70
+ enabled: true,
71
+ // MUST be enabled to match backtest results
72
+ startHour: 9,
73
+ // 9:30 AM EST
74
+ startMinute: 30,
75
+ endHour: 16,
76
+ // 4:00 PM EST
77
+ endMinute: 0,
72
78
  timezone: "America/New_York"
73
79
  }
74
80
  };
@@ -463,6 +469,8 @@ var require_core = __commonJS({
463
469
  this.barHistory = /* @__PURE__ */ new Map();
464
470
  this.swingPoints = /* @__PURE__ */ new Map();
465
471
  this.liquidityZones = /* @__PURE__ */ new Map();
472
+ this.currentBar = /* @__PURE__ */ new Map();
473
+ this.barIntervalMs = 6e4;
466
474
  this.lastSignalTime = 0;
467
475
  this.stats = { signals: 0, trades: 0, wins: 0, losses: 0, pnl: 0 };
468
476
  this.recentTrades = [];
@@ -475,26 +483,92 @@ var require_core = __commonJS({
475
483
  this.barHistory.set(contractId, []);
476
484
  this.swingPoints.set(contractId, []);
477
485
  this.liquidityZones.set(contractId, []);
486
+ this.currentBar.delete(contractId);
478
487
  this.emit("log", {
479
488
  type: "info",
480
- message: `[HQX-2B] Initialized for ${contractId}: tick=${tickSize}, value=${tickValue}`
489
+ message: `[HQX-2B] Initialized for ${contractId}: tick=${tickSize}, value=${tickValue}, TF=1min`
481
490
  });
482
491
  this.emit("log", {
483
492
  type: "info",
484
493
  message: `[HQX-2B] Params: Stop=${this.config.execution.stopTicks}t, Target=${this.config.execution.targetTicks}t, BE=${this.config.execution.breakevenTicks}t, Trail=${this.config.execution.trailTriggerTicks}/${this.config.execution.trailDistanceTicks}`
485
494
  });
486
495
  }
496
+ /**
497
+ * Check if current time is within trading session (9:30-16:00 EST)
498
+ */
499
+ isWithinSession(timestamp) {
500
+ if (!this.config.session.enabled) return true;
501
+ const date = new Date(timestamp);
502
+ const estOffset = this.isDST(date) ? -4 : -5;
503
+ const utcHours = date.getUTCHours();
504
+ const utcMinutes = date.getUTCMinutes();
505
+ const estHours = (utcHours + estOffset + 24) % 24;
506
+ const { startHour, startMinute, endHour, endMinute } = this.config.session;
507
+ const currentMins = estHours * 60 + utcMinutes;
508
+ const startMins = startHour * 60 + startMinute;
509
+ const endMins = endHour * 60 + endMinute;
510
+ return currentMins >= startMins && currentMins < endMins;
511
+ }
512
+ /**
513
+ * Check if date is in US Daylight Saving Time
514
+ */
515
+ isDST(date) {
516
+ const jan = new Date(date.getFullYear(), 0, 1);
517
+ const jul = new Date(date.getFullYear(), 6, 1);
518
+ const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
519
+ return date.getTimezoneOffset() < stdOffset;
520
+ }
521
+ /**
522
+ * Process incoming tick and aggregate into 1-minute bars
523
+ * Only calls processBar() when a bar closes (every 60 seconds)
524
+ */
487
525
  processTick(tick) {
488
526
  const { contractId, price, volume, timestamp } = tick;
489
- const bar = {
490
- timestamp: timestamp || Date.now(),
491
- open: price,
492
- high: price,
493
- low: price,
494
- close: price,
495
- volume: volume || 1
496
- };
497
- return this.processBar(contractId, bar);
527
+ const ts = timestamp || Date.now();
528
+ const vol = volume || 1;
529
+ if (!this.isWithinSession(ts)) {
530
+ return null;
531
+ }
532
+ let bar = this.currentBar.get(contractId);
533
+ const barStartTime = Math.floor(ts / this.barIntervalMs) * this.barIntervalMs;
534
+ if (!bar || bar.startTime !== barStartTime) {
535
+ if (bar) {
536
+ const closedBar = {
537
+ timestamp: bar.startTime,
538
+ open: bar.open,
539
+ high: bar.high,
540
+ low: bar.low,
541
+ close: bar.close,
542
+ volume: bar.volume
543
+ };
544
+ const signal = this.processBar(contractId, closedBar);
545
+ this.currentBar.set(contractId, {
546
+ startTime: barStartTime,
547
+ open: price,
548
+ high: price,
549
+ low: price,
550
+ close: price,
551
+ volume: vol
552
+ });
553
+ return signal;
554
+ } else {
555
+ this.currentBar.set(contractId, {
556
+ startTime: barStartTime,
557
+ open: price,
558
+ high: price,
559
+ low: price,
560
+ close: price,
561
+ volume: vol
562
+ });
563
+ return null;
564
+ }
565
+ } else {
566
+ bar.high = Math.max(bar.high, price);
567
+ bar.low = Math.min(bar.low, price);
568
+ bar.close = price;
569
+ bar.volume += vol;
570
+ return null;
571
+ }
498
572
  }
499
573
  onTick(tick) {
500
574
  return this.processTick(tick);
@@ -622,6 +696,7 @@ var require_core = __commonJS({
622
696
  this.barHistory.set(contractId, []);
623
697
  this.swingPoints.set(contractId, []);
624
698
  this.liquidityZones.set(contractId, []);
699
+ this.currentBar.delete(contractId);
625
700
  this.emit("log", {
626
701
  type: "info",
627
702
  message: `[HQX-2B] Reset state for ${contractId}`
@@ -1 +1,705 @@
1
- const _0x371eb1=_0x4b27;(function(_0x507643,_0x538df8){const _0x563eca=_0x4b27,_0x3dfc41=_0x507643();while(!![]){try{const _0x42b617=-parseInt(_0x563eca(0x190))/0x1*(-parseInt(_0x563eca(0x156))/0x2)+parseInt(_0x563eca(0x183))/0x3+parseInt(_0x563eca(0x180))/0x4+-parseInt(_0x563eca(0x158))/0x5+parseInt(_0x563eca(0x15e))/0x6*(-parseInt(_0x563eca(0x189))/0x7)+parseInt(_0x563eca(0x137))/0x8+parseInt(_0x563eca(0x13f))/0x9*(-parseInt(_0x563eca(0x181))/0xa);if(_0x42b617===_0x538df8)break;else _0x3dfc41['push'](_0x3dfc41['shift']());}catch(_0xd4d426){_0x3dfc41['push'](_0x3dfc41['shift']());}}}(_0x411a,0x53323));const EventEmitter=require('events'),{v4:uuidv4}=require('uuid'),OrderSide={'BID':0x0,'ASK':0x1},SignalStrength={'WEAK':0x1,'MODERATE':0x2,'STRONG':0x3,'VERY_STRONG':0x4};function _0x411a(){const _0x27a23d=['2475448YZZsWq','kalman','price','kalmanStates','%\x20Kyle:','confidenceBonus','HQX_ULTRA_SCALPING_6MODELS','tickSize','694269rFwgpb','info','exports','BID','profitLockPct','split','ASK','stopMultiplier','trades','length','\x20VPIN:','onTick','\x20Regime:','stats','close','processTick','signal','getAnalysisState','lossStreak','barHistory','_generateSignal','lastSignalTime','getStats','470192mXQQiX','_detectVolatilityRegime','2511350svvQNF','estimate','[HQX-UltraScalping]\x20Initialized\x20for\x20','zscoreThreshold','weights','WEAK','204vdZvxj','now','sell','_computeVPIN','targetMultiplier','_applyKalmanFilter','kalmanProcessNoise',':\x20tick=','zscoreExitThreshold','signals',',\x20streak:\x20','pow','strategy','losses','winStreak','zscoreEntryThreshold','wins','symbol','_calculateATR','baseTargetTicks','filter','ofiLookback','generateSignal','abs','round','buy','initialize','push','reset','toFixed','composite','set','getModelValues','vpinToxicThreshold','1089760NgASxb','110shtkue','shift','2012835ylCsNd',',\x20value=','get','\x20@\x20','emit','Collecting\x20data...\x20','6468ehZsMS','getBarHistory','onTrade','_computeOrderFlowImbalance','high','reduce','breakevenTicks','2KorAWa','6\x20(Z-Score,\x20VPIN,\x20Kyle,\x20Kalman,\x20Vol,\x20OFI)','volumeBuffer','volume','minConfidence','pnl','log','recordTradeResult','long','kyleLambda','processBar','_computeKyleLambda','side','min','kalmanMeasurementNoise','priceBuffer','ofi','errorCovariance','max','short',',\x20VPIN=','slice','zscore','_computeZScore','atrHistory','volatility','low','baseStopTicks'];_0x411a=function(){return _0x27a23d;};return _0x411a();}class HQXUltraScalping extends EventEmitter{constructor(_0x36c01b={}){const _0x500d51=_0x4b27;super(),this[_0x500d51(0x13e)]=_0x36c01b[_0x500d51(0x13e)]||0.25,this['tickValue']=_0x36c01b['tickValue']||0x5,this[_0x500d51(0x16d)]=1.5,this['zscoreExitThreshold']=0.5,this['vpinWindow']=0x32,this['vpinToxicThreshold']=0.7,this[_0x500d51(0x164)]=0.01,this[_0x500d51(0x129)]=0.1,this['volatilityLookback']=0x64,this[_0x500d51(0x173)]=0x14,this['baseStopTicks']=0x8,this[_0x500d51(0x171)]=0x10,this[_0x500d51(0x18f)]=0x4,this['profitLockPct']=0.5,this['minConfidence']=0.55,this[_0x500d51(0x15c)]={'zscore':0.3,'ofi':0.2,'vpin':0.15,'kalman':0.15,'kyleLambda':0.1,'volatility':0.1},this['barHistory']=new Map(),this['priceBuffer']=new Map(),this['volumeBuffer']=new Map(),this[_0x500d51(0x13a)]=new Map(),this['atrHistory']=new Map(),this['recentTrades']=[],this['winStreak']=0x0,this['lossStreak']=0x0,this[_0x500d51(0x154)]=0x0,this['cooldownMs']=0x7530,this['minHoldTimeMs']=0x2710,this['stats']={'signals':0x0,'trades':0x0,'wins':0x0,'losses':0x0,'pnl':0x0};}[_0x371eb1(0x178)](_0x868a52,_0x4a1bf5=0.25,_0x27a4c2=0x5){const _0x4ddb38=_0x371eb1;this[_0x4ddb38(0x13e)]=_0x4a1bf5,this['tickValue']=_0x27a4c2,this[_0x4ddb38(0x152)][_0x4ddb38(0x17d)](_0x868a52,[]),this[_0x4ddb38(0x12a)]['set'](_0x868a52,[]),this[_0x4ddb38(0x192)]['set'](_0x868a52,[]),this[_0x4ddb38(0x133)]['set'](_0x868a52,[]),this[_0x4ddb38(0x13a)][_0x4ddb38(0x17d)](_0x868a52,{'estimate':0x0,'errorCovariance':0x1}),this['emit']('log',{'type':_0x4ddb38(0x140),'message':_0x4ddb38(0x15a)+_0x868a52+_0x4ddb38(0x165)+_0x4a1bf5+_0x4ddb38(0x184)+_0x27a4c2}),this[_0x4ddb38(0x187)](_0x4ddb38(0x121),{'type':_0x4ddb38(0x140),'message':'[HQX-UltraScalping]\x206\x20Models:\x20Z-Score(30%),\x20OFI(20%),\x20VPIN(15%),\x20Kalman(15%),\x20Kyle(10%),\x20Vol(10%)'});}[_0x371eb1(0x14e)](_0x16706f){const _0x4cbeda=_0x371eb1,{contractId:_0x27ef24,price:_0x53c926,volume:_0x388677,side:_0x3dff0d,timestamp:_0x1fd07f}=_0x16706f,_0x4bee87={'timestamp':_0x1fd07f||Date[_0x4cbeda(0x15f)](),'open':_0x53c926,'high':_0x53c926,'low':_0x53c926,'close':_0x53c926,'volume':_0x388677||0x1};return this[_0x4cbeda(0x125)](_0x27ef24,_0x4bee87);}['onTick'](_0x1328b5){const _0x5386f7=_0x371eb1;return this[_0x5386f7(0x14e)](_0x1328b5);}['onTrade'](_0xb54c4c){const _0x4d0ae5=_0x371eb1;return this[_0x4d0ae5(0x14e)]({'contractId':_0xb54c4c['contractId']||_0xb54c4c[_0x4d0ae5(0x16f)],'price':_0xb54c4c[_0x4d0ae5(0x139)],'volume':_0xb54c4c['size']||_0xb54c4c['volume']||0x1,'side':_0xb54c4c[_0x4d0ae5(0x127)],'timestamp':_0xb54c4c['timestamp']||Date[_0x4d0ae5(0x15f)]()});}[_0x371eb1(0x125)](_0x16055a,_0x283841){const _0x48a98e=_0x371eb1;let _0x4f34c0=this[_0x48a98e(0x152)]['get'](_0x16055a);!_0x4f34c0&&(this['initialize'](_0x16055a),_0x4f34c0=this[_0x48a98e(0x152)]['get'](_0x16055a));_0x4f34c0['push'](_0x283841);if(_0x4f34c0[_0x48a98e(0x148)]>0x1f4)_0x4f34c0[_0x48a98e(0x182)]();const _0x1254f0=this['priceBuffer']['get'](_0x16055a);_0x1254f0['push'](_0x283841['close']);if(_0x1254f0[_0x48a98e(0x148)]>0xc8)_0x1254f0[_0x48a98e(0x182)]();const _0x53a2b7=this['volumeBuffer']['get'](_0x16055a),_0x4eb2a6=_0x283841['high']-_0x283841[_0x48a98e(0x135)];let _0x473033=_0x283841[_0x48a98e(0x193)]*0.5,_0x13bb19=_0x283841['volume']*0.5;if(_0x4eb2a6>0x0){const _0x1c3017=(_0x283841['close']-_0x283841[_0x48a98e(0x135)])/_0x4eb2a6;_0x473033=_0x283841[_0x48a98e(0x193)]*_0x1c3017,_0x13bb19=_0x283841[_0x48a98e(0x193)]*(0x1-_0x1c3017);}_0x53a2b7['push']({'buy':_0x473033,'sell':_0x13bb19});if(_0x53a2b7['length']>0x64)_0x53a2b7[_0x48a98e(0x182)]();if(_0x4f34c0[_0x48a98e(0x148)]<0x32)return null;const _0x3dfcad=this['_computeZScore'](_0x1254f0),_0x1837a2=this[_0x48a98e(0x161)](_0x53a2b7),_0x74821=this[_0x48a98e(0x126)](_0x4f34c0),_0x25c8da=this[_0x48a98e(0x163)](_0x16055a,_0x283841['close']),{regime:_0x91e763,params:_0x24948f}=this[_0x48a98e(0x157)](_0x16055a,_0x4f34c0),_0x26a2b9=this[_0x48a98e(0x18c)](_0x4f34c0);return this['_generateSignal'](_0x16055a,_0x283841['close'],_0x3dfcad,_0x1837a2,_0x74821,_0x25c8da,_0x91e763,_0x24948f,_0x26a2b9,_0x4f34c0);}['_computeZScore'](_0x33dfb0,_0x2cf611=0x32){const _0x134efb=_0x371eb1;if(_0x33dfb0['length']<_0x2cf611)return 0x0;const _0x460593=_0x33dfb0[_0x134efb(0x130)](-_0x2cf611),_0x285e3f=_0x460593['reduce']((_0xd9a79b,_0x5d0d53)=>_0xd9a79b+_0x5d0d53,0x0)/_0x2cf611,_0x2cf47a=_0x460593[_0x134efb(0x18e)]((_0x442e16,_0x5b5e94)=>_0x442e16+Math[_0x134efb(0x169)](_0x5b5e94-_0x285e3f,0x2),0x0)/_0x2cf611,_0x42da2b=Math['sqrt'](_0x2cf47a);if(_0x42da2b<0.0001)return 0x0;const _0x4d38c3=_0x33dfb0[_0x33dfb0['length']-0x1];return(_0x4d38c3-_0x285e3f)/_0x42da2b;}[_0x371eb1(0x161)](_0x3b06d8){const _0x2eeedf=_0x371eb1;if(_0x3b06d8['length']<this['vpinWindow'])return 0.5;const _0x23f368=_0x3b06d8['slice'](-this['vpinWindow']);let _0x4031d1=0x0,_0x38689b=0x0;for(const _0x19a355 of _0x23f368){_0x4031d1+=_0x19a355[_0x2eeedf(0x177)],_0x38689b+=_0x19a355[_0x2eeedf(0x160)];}const _0x113023=_0x4031d1+_0x38689b;if(_0x113023<0x1)return 0.5;return Math['abs'](_0x4031d1-_0x38689b)/_0x113023;}['_computeKyleLambda'](_0xf4971c){const _0x813121=_0x371eb1;if(_0xf4971c['length']<0x14)return 0x0;const _0x33bf14=_0xf4971c['slice'](-0x14),_0x57317b=[],_0x34e3fd=[];for(let _0x56111a=0x1;_0x56111a<_0x33bf14[_0x813121(0x148)];_0x56111a++){_0x57317b['push'](_0x33bf14[_0x56111a][_0x813121(0x14d)]-_0x33bf14[_0x56111a-0x1]['close']),_0x34e3fd['push'](_0x33bf14[_0x56111a][_0x813121(0x193)]);}const _0x3d3b37=_0x57317b['reduce']((_0x738715,_0x3216f2)=>_0x738715+_0x3216f2,0x0)/_0x57317b[_0x813121(0x148)],_0x144ac9=_0x34e3fd[_0x813121(0x18e)]((_0x29a9b9,_0x2e41f7)=>_0x29a9b9+_0x2e41f7,0x0)/_0x34e3fd[_0x813121(0x148)];let _0x4b566e=0x0,_0x374f8d=0x0;for(let _0xa01cf4=0x0;_0xa01cf4<_0x57317b['length'];_0xa01cf4++){_0x4b566e+=(_0x57317b[_0xa01cf4]-_0x3d3b37)*(_0x34e3fd[_0xa01cf4]-_0x144ac9),_0x374f8d+=Math['pow'](_0x34e3fd[_0xa01cf4]-_0x144ac9,0x2);}_0x4b566e/=_0x57317b['length'],_0x374f8d/=_0x57317b[_0x813121(0x148)];if(_0x374f8d<0.0001)return 0x0;return Math['abs'](_0x4b566e/_0x374f8d);}[_0x371eb1(0x163)](_0x57d582,_0x3462ce){const _0x24cba8=_0x371eb1;let _0x486837=this[_0x24cba8(0x13a)]['get'](_0x57d582);if(!_0x486837)return _0x486837={'estimate':_0x3462ce,'errorCovariance':0x1},this['kalmanStates'][_0x24cba8(0x17d)](_0x57d582,_0x486837),_0x3462ce;const _0x2dd1da=_0x486837['estimate'],_0xc39734=_0x486837[_0x24cba8(0x12c)]+this['kalmanProcessNoise'],_0x56a97c=_0xc39734/(_0xc39734+this['kalmanMeasurementNoise']),_0x5a7c33=_0x2dd1da+_0x56a97c*(_0x3462ce-_0x2dd1da),_0x52d3e0=(0x1-_0x56a97c)*_0xc39734;return _0x486837[_0x24cba8(0x159)]=_0x5a7c33,_0x486837['errorCovariance']=_0x52d3e0,_0x5a7c33;}[_0x371eb1(0x157)](_0xc85bad,_0x8b120a){const _0x408ec8=_0x371eb1,_0x5aa785=this['_calculateATR'](_0x8b120a),_0x553dc3=_0x5aa785/this[_0x408ec8(0x13e)];let _0x5e843d=this[_0x408ec8(0x133)][_0x408ec8(0x185)](_0xc85bad);!_0x5e843d&&(_0x5e843d=[],this['atrHistory']['set'](_0xc85bad,_0x5e843d));_0x5e843d[_0x408ec8(0x179)](_0x5aa785);if(_0x5e843d[_0x408ec8(0x148)]>0x1f4)_0x5e843d[_0x408ec8(0x182)]();let _0x57b341=0.5;_0x5e843d['length']>=0x14&&(_0x57b341=_0x5e843d[_0x408ec8(0x172)](_0x5260f5=>_0x5260f5<=_0x5aa785)['length']/_0x5e843d['length']);let _0x2e01b2,_0x347a0e;if(_0x57b341<0.25)_0x2e01b2='low',_0x347a0e={'stopMultiplier':0.8,'targetMultiplier':0.9,'zscoreThreshold':1.2,'confidenceBonus':0.05};else _0x57b341<0.75?(_0x2e01b2='normal',_0x347a0e={'stopMultiplier':0x1,'targetMultiplier':0x1,'zscoreThreshold':1.5,'confidenceBonus':0x0}):(_0x2e01b2=_0x408ec8(0x18d),_0x347a0e={'stopMultiplier':1.3,'targetMultiplier':1.2,'zscoreThreshold':0x2,'confidenceBonus':-0.05});return{'regime':_0x2e01b2,'params':_0x347a0e};}[_0x371eb1(0x170)](_0x2d06a1,_0x169650=0xe){const _0x50f133=_0x371eb1;if(_0x2d06a1['length']<_0x169650+0x1)return 2.5;const _0x3d94b5=[];for(let _0x28cbae=_0x2d06a1['length']-_0x169650;_0x28cbae<_0x2d06a1['length'];_0x28cbae++){const _0x3038c5=_0x2d06a1[_0x28cbae],_0x4eb12b=_0x2d06a1[_0x28cbae-0x1]['close'],_0x1b7ea2=Math[_0x50f133(0x12d)](_0x3038c5[_0x50f133(0x18d)]-_0x3038c5[_0x50f133(0x135)],Math[_0x50f133(0x175)](_0x3038c5[_0x50f133(0x18d)]-_0x4eb12b),Math['abs'](_0x3038c5['low']-_0x4eb12b));_0x3d94b5[_0x50f133(0x179)](_0x1b7ea2);}return _0x3d94b5['reduce']((_0x39b5fd,_0xd20ab0)=>_0x39b5fd+_0xd20ab0,0x0)/_0x3d94b5['length'];}['_computeOrderFlowImbalance'](_0x582a89){const _0x4945c9=_0x371eb1;if(_0x582a89[_0x4945c9(0x148)]<this[_0x4945c9(0x173)])return 0x0;const _0x48d888=_0x582a89['slice'](-this['ofiLookback']);let _0x55edd9=0x0,_0x260a92=0x0;for(const _0x176c62 of _0x48d888){const _0x5b2d5a=_0x176c62['high']-_0x176c62[_0x4945c9(0x135)];if(_0x5b2d5a>0x0){const _0x54db25=(_0x176c62[_0x4945c9(0x14d)]-_0x176c62[_0x4945c9(0x135)])/_0x5b2d5a;_0x55edd9+=_0x54db25*_0x176c62[_0x4945c9(0x193)],_0x260a92+=(0x1-_0x54db25)*_0x176c62['volume'];}}const _0x5492ab=_0x55edd9+_0x260a92;if(_0x5492ab<0x1)return 0x0;return(_0x55edd9-_0x260a92)/_0x5492ab;}[_0x371eb1(0x153)](_0x317a32,_0x51668a,_0x1ee39d,_0x3b8899,_0x229d9c,_0x26fa8a,_0x22a87a,_0x38beeb,_0xd56043,_0x518297){const _0x184de5=_0x371eb1,_0x15d504=Math[_0x184de5(0x175)](_0x1ee39d);if(_0x15d504<_0x38beeb['zscoreThreshold'])return null;if(_0x3b8899>this[_0x184de5(0x17f)])return null;let _0x5759b5;if(_0x1ee39d<-_0x38beeb[_0x184de5(0x15b)])_0x5759b5='long';else{if(_0x1ee39d>_0x38beeb['zscoreThreshold'])_0x5759b5='short';else return null;}const _0x4a1fa1=_0x5759b5===_0x184de5(0x123)&&_0xd56043>0.1||_0x5759b5===_0x184de5(0x12e)&&_0xd56043<-0.1,_0x21e280=_0x51668a-_0x26fa8a,_0x493653=_0x5759b5===_0x184de5(0x123)&&_0x21e280<0x0||_0x5759b5===_0x184de5(0x12e)&&_0x21e280>0x0,_0x141172={'zscore':Math['min'](0x1,_0x15d504/0x4),'vpin':0x1-_0x3b8899,'kyleLambda':_0x229d9c>0.001?0.5:0.8,'kalman':_0x493653?0.8:0.4,'volatility':_0x22a87a==='normal'?0.8:_0x22a87a===_0x184de5(0x135)?0.7:0.6,'ofi':_0x4a1fa1?0.9:0.5,'composite':0x0};_0x141172[_0x184de5(0x17c)]=_0x141172['zscore']*this['weights'][_0x184de5(0x131)]+_0x141172['vpin']*this[_0x184de5(0x15c)]['vpin']+_0x141172['kyleLambda']*this['weights'][_0x184de5(0x124)]+_0x141172[_0x184de5(0x138)]*this['weights']['kalman']+_0x141172[_0x184de5(0x134)]*this[_0x184de5(0x15c)]['volatility']+_0x141172[_0x184de5(0x12b)]*this[_0x184de5(0x15c)]['ofi'];const _0x3b6c87=Math[_0x184de5(0x128)](0x1,_0x141172['composite']+_0x38beeb[_0x184de5(0x13c)]);if(_0x3b6c87<this[_0x184de5(0x11f)])return null;if(Date[_0x184de5(0x15f)]()-this[_0x184de5(0x154)]<this['cooldownMs'])return null;const _0x31f489=Math['round'](this[_0x184de5(0x136)]*_0x38beeb[_0x184de5(0x146)]),_0x2a44b6=Math[_0x184de5(0x176)](this[_0x184de5(0x171)]*_0x38beeb[_0x184de5(0x162)]),_0x422654=Math[_0x184de5(0x12d)](0x6,Math[_0x184de5(0x128)](0xc,_0x31f489)),_0x2353e7=Math['max'](_0x422654*1.5,Math[_0x184de5(0x128)](0x18,_0x2a44b6));let _0x1dcea3,_0x2cce06,_0x43e504,_0x495e95;_0x5759b5===_0x184de5(0x123)?(_0x1dcea3=_0x51668a-_0x422654*this[_0x184de5(0x13e)],_0x2cce06=_0x51668a+_0x2353e7*this['tickSize'],_0x43e504=_0x51668a+this['breakevenTicks']*this['tickSize'],_0x495e95=_0x51668a+_0x2353e7*this[_0x184de5(0x143)]*this[_0x184de5(0x13e)]):(_0x1dcea3=_0x51668a+_0x422654*this[_0x184de5(0x13e)],_0x2cce06=_0x51668a-_0x2353e7*this['tickSize'],_0x43e504=_0x51668a-this['breakevenTicks']*this[_0x184de5(0x13e)],_0x495e95=_0x51668a-_0x2353e7*this[_0x184de5(0x143)]*this['tickSize']);const _0x105cdf=_0x2353e7/_0x422654,_0x131f8c=Math[_0x184de5(0x176)](_0x2353e7*0.5),_0xe9a67d=Math[_0x184de5(0x176)](_0x422654*0.4);let _0x1d1787=SignalStrength['MODERATE'];if(_0x3b6c87>=0.85)_0x1d1787=SignalStrength['VERY_STRONG'];else{if(_0x3b6c87>=0.75)_0x1d1787=SignalStrength['STRONG'];else{if(_0x3b6c87<0.6)_0x1d1787=SignalStrength[_0x184de5(0x15d)];}}const _0x17213a=0.5+(_0x3b6c87-0.5)*0.4,_0x4e77a2=_0x17213a*Math[_0x184de5(0x175)](_0x2cce06-_0x51668a)-(0x1-_0x17213a)*Math['abs'](_0x51668a-_0x1dcea3);this['lastSignalTime']=Date['now'](),this['stats'][_0x184de5(0x167)]++;const _0x37f751={'id':uuidv4(),'timestamp':Date['now'](),'symbol':_0x317a32[_0x184de5(0x144)]('.')[0x0]||_0x317a32,'contractId':_0x317a32,'side':_0x5759b5===_0x184de5(0x123)?OrderSide[_0x184de5(0x142)]:OrderSide[_0x184de5(0x145)],'direction':_0x5759b5,'strategy':_0x184de5(0x13d),'strength':_0x1d1787,'edge':_0x4e77a2,'confidence':_0x3b6c87,'entry':_0x51668a,'entryPrice':_0x51668a,'stopLoss':_0x1dcea3,'takeProfit':_0x2cce06,'riskReward':_0x105cdf,'stopTicks':_0x422654,'targetTicks':_0x2353e7,'trailTriggerTicks':_0x131f8c,'trailDistanceTicks':_0xe9a67d,'beBreakeven':_0x43e504,'profitLockLevel':_0x495e95,'zScore':_0x1ee39d,'zScoreExit':this['zscoreExitThreshold'],'vpinValue':_0x3b8899,'kyleLambda':_0x229d9c,'kalmanEstimate':_0x26fa8a,'volatilityRegime':_0x22a87a,'ofiValue':_0xd56043,'models':_0x141172,'orderFlowConfirmed':_0x4a1fa1,'kalmanConfirmed':_0x493653,'expires':Date['now']()+0xea60};return this['emit'](_0x184de5(0x14f),{'side':_0x5759b5==='long'?'buy':'sell','action':'open','reason':'Z='+_0x1ee39d['toFixed'](0x2)+_0x184de5(0x12f)+(_0x3b8899*0x64)[_0x184de5(0x17b)](0x0)+'%,\x20OFI='+(_0xd56043*0x64)[_0x184de5(0x17b)](0x0)+'%,\x20cf='+(_0x3b6c87*0x64)[_0x184de5(0x17b)](0x0)+'%',..._0x37f751}),this['emit']('log',{'type':_0x184de5(0x140),'message':'[HQX]\x20SIGNAL:\x20'+_0x5759b5['toUpperCase']()+_0x184de5(0x186)+_0x51668a[_0x184de5(0x17b)](0x2)+'\x20|\x20Z:'+_0x1ee39d[_0x184de5(0x17b)](0x2)+_0x184de5(0x149)+(_0x3b8899*0x64)[_0x184de5(0x17b)](0x0)+'%\x20OFI:'+(_0xd56043*0x64)['toFixed'](0x0)+_0x184de5(0x13b)+_0x229d9c['toFixed'](0x5)+_0x184de5(0x14b)+_0x22a87a+'\x20|\x20Conf:'+(_0x3b6c87*0x64)['toFixed'](0x0)+'%'}),_0x37f751;}['shouldExitByZScore'](_0x18198d){const _0xe0018e=_0x371eb1,_0x9c72d7=this[_0xe0018e(0x12a)][_0xe0018e(0x185)](_0x18198d);if(!_0x9c72d7||_0x9c72d7[_0xe0018e(0x148)]<0x32)return![];const _0x287ecd=this[_0xe0018e(0x132)](_0x9c72d7);return Math[_0xe0018e(0x175)](_0x287ecd)<this[_0xe0018e(0x166)];}[_0x371eb1(0x17e)](_0x42ff12){const _0x453183=_0x371eb1,_0x4bc437=this[_0x453183(0x12a)][_0x453183(0x185)](_0x42ff12),_0x1d439f=this[_0x453183(0x192)][_0x453183(0x185)](_0x42ff12),_0xb50dc9=this['barHistory'][_0x453183(0x185)](_0x42ff12);if(!_0x4bc437||!_0x1d439f||!_0xb50dc9||_0xb50dc9['length']<0x32)return null;const _0x260c32=this['_computeZScore'](_0x4bc437),_0x276e0e=this[_0x453183(0x161)](_0x1d439f),_0x183dd3=this['_computeKyleLambda'](_0xb50dc9),_0x3df13c=this['_computeOrderFlowImbalance'](_0xb50dc9);return{'zscore':Math['min'](0x1,Math[_0x453183(0x175)](_0x260c32)/0x4),'vpin':0x1-_0x276e0e,'kyleLambda':_0x183dd3>0.001?0.5:0.8,'kalman':0.7,'volatility':0.7,'ofi':Math['abs'](_0x3df13c)>0.1?0.8:0.5,'composite':0.7,'raw':{'zscore':_0x260c32,'vpin':_0x276e0e,'kyleLambda':_0x183dd3,'ofi':_0x3df13c}};}[_0x371eb1(0x150)](_0x104d11,_0xe08a26){const _0x2a256e=_0x371eb1,_0xce6de4=this['barHistory'][_0x2a256e(0x185)](_0x104d11)||[];if(_0xce6de4['length']<0x32)return{'ready':![],'message':_0x2a256e(0x188)+_0xce6de4['length']+'/50\x20bars'};const _0x2b1fec=this[_0x2a256e(0x12a)][_0x2a256e(0x185)](_0x104d11)||[],_0x277c7d=this[_0x2a256e(0x192)]['get'](_0x104d11)||[],_0x529871=this[_0x2a256e(0x132)](_0x2b1fec),_0x34b9fc=this['_computeVPIN'](_0x277c7d),_0x2eab93=this['_computeOrderFlowImbalance'](_0xce6de4),_0x4e8e17=this[_0x2a256e(0x126)](_0xce6de4),{regime:_0x42fdf3,params:_0x14e667}=this[_0x2a256e(0x157)](_0x104d11,_0xce6de4);return{'ready':!![],'zScore':_0x529871,'vpin':_0x34b9fc,'ofi':_0x2eab93,'kyleLambda':_0x4e8e17,'regime':_0x42fdf3,'stopTicks':Math[_0x2a256e(0x176)](this[_0x2a256e(0x136)]*_0x14e667['stopMultiplier']),'targetTicks':Math[_0x2a256e(0x176)](this['baseTargetTicks']*_0x14e667['targetMultiplier']),'threshold':_0x14e667['zscoreThreshold'],'barsProcessed':_0xce6de4[_0x2a256e(0x148)],'models':_0x2a256e(0x191)};}[_0x371eb1(0x122)](_0x12a30f){const _0x9df8dc=_0x371eb1;this['recentTrades'][_0x9df8dc(0x179)]({'netPnl':_0x12a30f,'timestamp':Date['now']()});if(this['recentTrades'][_0x9df8dc(0x148)]>0x64)this['recentTrades']['shift']();_0x12a30f>0x0?(this['winStreak']++,this['lossStreak']=0x0,this[_0x9df8dc(0x14c)][_0x9df8dc(0x16e)]++):(this[_0x9df8dc(0x151)]++,this['winStreak']=0x0,this['stats'][_0x9df8dc(0x16b)]++),this[_0x9df8dc(0x14c)][_0x9df8dc(0x147)]++,this[_0x9df8dc(0x14c)][_0x9df8dc(0x120)]+=_0x12a30f,this[_0x9df8dc(0x187)]('log',{'type':'debug','message':'[HQX]\x20Trade\x20result:\x20'+(_0x12a30f>0x0?'WIN':'LOSS')+'\x20$'+_0x12a30f['toFixed'](0x2)+_0x9df8dc(0x168)+(_0x12a30f>0x0?this[_0x9df8dc(0x16c)]:-this['lossStreak'])});}[_0x371eb1(0x18a)](_0x48cb29){return this['barHistory']['get'](_0x48cb29)||[];}[_0x371eb1(0x155)](){return this['stats'];}[_0x371eb1(0x17a)](_0x2db903){const _0x29b967=_0x371eb1;this['barHistory']['set'](_0x2db903,[]),this[_0x29b967(0x12a)]['set'](_0x2db903,[]),this[_0x29b967(0x192)][_0x29b967(0x17d)](_0x2db903,[]),this[_0x29b967(0x133)]['set'](_0x2db903,[]),this['kalmanStates'][_0x29b967(0x17d)](_0x2db903,{'estimate':0x0,'errorCovariance':0x1}),this['emit'](_0x29b967(0x121),{'type':'info','message':'[HQX-UltraScalping]\x20Reset\x20state\x20for\x20'+_0x2db903});}}function _0x4b27(_0x5c3c89,_0x39f9b9){_0x5c3c89=_0x5c3c89-0x11f;const _0x411ae3=_0x411a();let _0x4b2782=_0x411ae3[_0x5c3c89];return _0x4b2782;}class UltraScalpingStrategy extends EventEmitter{constructor(_0x35d97c={}){const _0x594019=_0x371eb1;super(),this['config']=_0x35d97c,this[_0x594019(0x16a)]=new HQXUltraScalping(_0x35d97c),this['strategy']['on']('signal',_0x2742d2=>this['emit']('signal',_0x2742d2)),this['strategy']['on'](_0x594019(0x121),_0x3d29fb=>this['emit']('log',_0x3d29fb));}[_0x371eb1(0x14e)](_0x3b7fbe){return this['strategy']['processTick'](_0x3b7fbe);}[_0x371eb1(0x14a)](_0x56cfa0){return this['strategy']['onTick'](_0x56cfa0);}[_0x371eb1(0x18b)](_0xa016de){const _0x2f46cc=_0x371eb1;return this[_0x2f46cc(0x16a)][_0x2f46cc(0x18b)](_0xa016de);}['processBar'](_0x57a8c0,_0x277b59){const _0x126dbd=_0x371eb1;return this['strategy'][_0x126dbd(0x125)](_0x57a8c0,_0x277b59);}['initialize'](_0x23d70e,_0x18cd66,_0x2a4f00){const _0x2e7df3=_0x371eb1;return this[_0x2e7df3(0x16a)][_0x2e7df3(0x178)](_0x23d70e,_0x18cd66,_0x2a4f00);}['getAnalysisState'](_0x370971,_0x8ee12){return this['strategy']['getAnalysisState'](_0x370971,_0x8ee12);}[_0x371eb1(0x122)](_0x403f97){const _0x35f503=_0x371eb1;return this[_0x35f503(0x16a)][_0x35f503(0x122)](_0x403f97);}['reset'](_0x2adeff){const _0x3e03fc=_0x371eb1;return this['strategy'][_0x3e03fc(0x17a)](_0x2adeff);}['getStats'](){const _0x4c050e=_0x371eb1;return this[_0x4c050e(0x16a)][_0x4c050e(0x155)]();}[_0x371eb1(0x18a)](_0x3bc119){return this['strategy']['getBarHistory'](_0x3bc119);}['getModelValues'](_0x2e2674){const _0x30bb59=_0x371eb1;return this['strategy'][_0x30bb59(0x17e)](_0x2e2674);}['shouldExitByZScore'](_0x451471){const _0x2796fe=_0x371eb1;return this[_0x2796fe(0x16a)]['shouldExitByZScore'](_0x451471);}[_0x371eb1(0x174)](_0x32f693){return null;}}module[_0x371eb1(0x141)]={'HQXUltraScalping':HQXUltraScalping,'UltraScalpingStrategy':UltraScalpingStrategy,'M1':UltraScalpingStrategy,'S1':HQXUltraScalping,'OrderSide':OrderSide,'SignalStrength':SignalStrength};
1
+ var __getOwnPropNames = Object.getOwnPropertyNames;
2
+ var __commonJS = (cb, mod) => function __require() {
3
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
4
+ };
5
+
6
+ // ultra-scalping/config.js
7
+ var require_config = __commonJS({
8
+ "ultra-scalping/config.js"(exports2, module2) {
9
+ var DEFAULT_CONFIG = {
10
+ // Model Parameters
11
+ zscoreEntryThreshold: 1.5,
12
+ // Live trading threshold (backtest: 2.5)
13
+ zscoreExitThreshold: 0.5,
14
+ vpinWindow: 50,
15
+ vpinToxicThreshold: 0.7,
16
+ // Skip if VPIN > 0.7
17
+ volatilityLookback: 100,
18
+ ofiLookback: 20,
19
+ // Trade Parameters
20
+ baseStopTicks: 8,
21
+ // $40
22
+ baseTargetTicks: 16,
23
+ // $80
24
+ breakevenTicks: 4,
25
+ // Move to BE at +4 ticks
26
+ profitLockPct: 0.5,
27
+ // Lock 50% of profit
28
+ minConfidence: 0.55,
29
+ // Minimum composite confidence
30
+ cooldownMs: 3e4,
31
+ // 30 seconds between signals
32
+ minHoldTimeMs: 1e4,
33
+ // Minimum 10 seconds hold
34
+ // Model Weights (from Python backtest)
35
+ weights: {
36
+ zscore: 0.3,
37
+ // 30%
38
+ ofi: 0.2,
39
+ // 20%
40
+ vpin: 0.15,
41
+ // 15%
42
+ kalman: 0.15,
43
+ // 15%
44
+ kyleLambda: 0.1,
45
+ // 10%
46
+ volatility: 0.1
47
+ // 10%
48
+ },
49
+ // Session (Futures Market Hours - Sunday 18:00 to Friday 17:00 EST)
50
+ session: {
51
+ enabled: false,
52
+ // Trade anytime markets are open
53
+ timezone: "America/New_York"
54
+ }
55
+ };
56
+ module2.exports = { DEFAULT_CONFIG };
57
+ }
58
+ });
59
+
60
+ // common/types.js
61
+ var require_types = __commonJS({
62
+ "common/types.js"(exports2, module2) {
63
+ var OrderSide2 = { BID: 0, ASK: 1 };
64
+ var SignalStrength2 = { WEAK: 1, MODERATE: 2, STRONG: 3, VERY_STRONG: 4 };
65
+ module2.exports = { OrderSide: OrderSide2, SignalStrength: SignalStrength2 };
66
+ }
67
+ });
68
+
69
+ // ultra-scalping/signal.js
70
+ var require_signal = __commonJS({
71
+ "ultra-scalping/signal.js"(exports2, module2) {
72
+ var { v4: uuidv4 } = require("uuid");
73
+ var { OrderSide: OrderSide2, SignalStrength: SignalStrength2 } = require_types();
74
+ function generateSignal(params) {
75
+ const {
76
+ contractId,
77
+ currentPrice,
78
+ zscore,
79
+ vpin,
80
+ kyleLambda,
81
+ kalmanEstimate,
82
+ regime,
83
+ volParams,
84
+ ofi,
85
+ config,
86
+ tickSize
87
+ } = params;
88
+ const absZscore = Math.abs(zscore);
89
+ if (absZscore < volParams.zscoreThreshold) {
90
+ return null;
91
+ }
92
+ if (vpin > config.vpinToxicThreshold) {
93
+ return null;
94
+ }
95
+ let direction;
96
+ if (zscore < -volParams.zscoreThreshold) {
97
+ direction = "long";
98
+ } else if (zscore > volParams.zscoreThreshold) {
99
+ direction = "short";
100
+ } else {
101
+ return null;
102
+ }
103
+ const ofiConfirms = direction === "long" && ofi > 0.1 || direction === "short" && ofi < -0.1;
104
+ const kalmanDiff = currentPrice - kalmanEstimate;
105
+ const kalmanConfirms = direction === "long" && kalmanDiff < 0 || direction === "short" && kalmanDiff > 0;
106
+ const scores = {
107
+ zscore: Math.min(1, absZscore / 4),
108
+ // Normalize to 0-1
109
+ vpin: 1 - vpin,
110
+ // Lower VPIN = better
111
+ kyleLambda: kyleLambda > 1e-3 ? 0.5 : 0.8,
112
+ // Moderate lambda is good
113
+ kalman: kalmanConfirms ? 0.8 : 0.4,
114
+ volatility: regime === "normal" ? 0.8 : regime === "low" ? 0.7 : 0.6,
115
+ ofi: ofiConfirms ? 0.9 : 0.5,
116
+ composite: 0
117
+ // Calculated below
118
+ };
119
+ scores.composite = scores.zscore * config.weights.zscore + // 30%
120
+ scores.vpin * config.weights.vpin + // 15%
121
+ scores.kyleLambda * config.weights.kyleLambda + // 10%
122
+ scores.kalman * config.weights.kalman + // 15%
123
+ scores.volatility * config.weights.volatility + // 10%
124
+ scores.ofi * config.weights.ofi;
125
+ const confidence = Math.min(1, scores.composite + volParams.confidenceBonus);
126
+ if (confidence < config.minConfidence) {
127
+ return null;
128
+ }
129
+ const stopTicks = Math.round(config.baseStopTicks * volParams.stopMultiplier);
130
+ const targetTicks = Math.round(config.baseTargetTicks * volParams.targetMultiplier);
131
+ const actualStopTicks = Math.max(6, Math.min(12, stopTicks));
132
+ const actualTargetTicks = Math.max(actualStopTicks * 1.5, Math.min(24, targetTicks));
133
+ let stopLoss, takeProfit, beBreakeven, profitLockLevel;
134
+ if (direction === "long") {
135
+ stopLoss = currentPrice - actualStopTicks * tickSize;
136
+ takeProfit = currentPrice + actualTargetTicks * tickSize;
137
+ beBreakeven = currentPrice + config.breakevenTicks * tickSize;
138
+ profitLockLevel = currentPrice + actualTargetTicks * config.profitLockPct * tickSize;
139
+ } else {
140
+ stopLoss = currentPrice + actualStopTicks * tickSize;
141
+ takeProfit = currentPrice - actualTargetTicks * tickSize;
142
+ beBreakeven = currentPrice - config.breakevenTicks * tickSize;
143
+ profitLockLevel = currentPrice - actualTargetTicks * config.profitLockPct * tickSize;
144
+ }
145
+ const riskReward = actualTargetTicks / actualStopTicks;
146
+ const trailTriggerTicks = Math.round(actualTargetTicks * 0.5);
147
+ const trailDistanceTicks = Math.round(actualStopTicks * 0.4);
148
+ let strength = SignalStrength2.MODERATE;
149
+ if (confidence >= 0.85) strength = SignalStrength2.VERY_STRONG;
150
+ else if (confidence >= 0.75) strength = SignalStrength2.STRONG;
151
+ else if (confidence < 0.6) strength = SignalStrength2.WEAK;
152
+ const winProb = 0.5 + (confidence - 0.5) * 0.4;
153
+ const edge = winProb * Math.abs(takeProfit - currentPrice) - (1 - winProb) * Math.abs(currentPrice - stopLoss);
154
+ return {
155
+ id: uuidv4(),
156
+ timestamp: Date.now(),
157
+ symbol: contractId.split(".")[0] || contractId,
158
+ contractId,
159
+ side: direction === "long" ? OrderSide2.BID : OrderSide2.ASK,
160
+ direction,
161
+ strategy: "HQX_ULTRA_SCALPING_6MODELS",
162
+ strength,
163
+ edge,
164
+ confidence,
165
+ entry: currentPrice,
166
+ entryPrice: currentPrice,
167
+ stopLoss,
168
+ takeProfit,
169
+ riskReward,
170
+ stopTicks: actualStopTicks,
171
+ targetTicks: actualTargetTicks,
172
+ trailTriggerTicks,
173
+ trailDistanceTicks,
174
+ beBreakeven,
175
+ profitLockLevel,
176
+ // Model values for debugging/monitoring
177
+ zScore: zscore,
178
+ zScoreExit: config.zscoreExitThreshold,
179
+ vpinValue: vpin,
180
+ kyleLambda,
181
+ kalmanEstimate,
182
+ volatilityRegime: regime,
183
+ ofiValue: ofi,
184
+ models: scores,
185
+ // Order flow confirmation flag
186
+ orderFlowConfirmed: ofiConfirms,
187
+ kalmanConfirmed: kalmanConfirms,
188
+ expires: Date.now() + 6e4
189
+ };
190
+ }
191
+ module2.exports = { generateSignal };
192
+ }
193
+ });
194
+
195
+ // ultra-scalping/models/zscore.js
196
+ var require_zscore = __commonJS({
197
+ "ultra-scalping/models/zscore.js"(exports2, module2) {
198
+ function computeZScore(prices, window = 50) {
199
+ if (prices.length < window) return 0;
200
+ const recentPrices = prices.slice(-window);
201
+ const mean = recentPrices.reduce((a, b) => a + b, 0) / window;
202
+ const variance = recentPrices.reduce((sum, p) => sum + Math.pow(p - mean, 2), 0) / window;
203
+ const std = Math.sqrt(variance);
204
+ if (std < 1e-4) return 0;
205
+ const currentPrice = prices[prices.length - 1];
206
+ return (currentPrice - mean) / std;
207
+ }
208
+ module2.exports = { computeZScore };
209
+ }
210
+ });
211
+
212
+ // ultra-scalping/models/vpin.js
213
+ var require_vpin = __commonJS({
214
+ "ultra-scalping/models/vpin.js"(exports2, module2) {
215
+ function computeVPIN(volumes, vpinWindow = 50) {
216
+ if (volumes.length < vpinWindow) return 0.5;
217
+ const recentVolumes = volumes.slice(-vpinWindow);
218
+ let totalBuy = 0;
219
+ let totalSell = 0;
220
+ for (const v of recentVolumes) {
221
+ totalBuy += v.buy;
222
+ totalSell += v.sell;
223
+ }
224
+ const totalVolume = totalBuy + totalSell;
225
+ if (totalVolume < 1) return 0.5;
226
+ return Math.abs(totalBuy - totalSell) / totalVolume;
227
+ }
228
+ module2.exports = { computeVPIN };
229
+ }
230
+ });
231
+
232
+ // ultra-scalping/models/kyle.js
233
+ var require_kyle = __commonJS({
234
+ "ultra-scalping/models/kyle.js"(exports2, module2) {
235
+ function computeKyleLambda(bars) {
236
+ if (bars.length < 20) return 0;
237
+ const recentBars = bars.slice(-20);
238
+ const priceChanges = [];
239
+ const volumes = [];
240
+ for (let i = 1; i < recentBars.length; i++) {
241
+ priceChanges.push(recentBars[i].close - recentBars[i - 1].close);
242
+ volumes.push(recentBars[i].volume);
243
+ }
244
+ const meanPrice = priceChanges.reduce((a, b) => a + b, 0) / priceChanges.length;
245
+ const meanVol = volumes.reduce((a, b) => a + b, 0) / volumes.length;
246
+ let covariance = 0;
247
+ let varianceVol = 0;
248
+ for (let i = 0; i < priceChanges.length; i++) {
249
+ covariance += (priceChanges[i] - meanPrice) * (volumes[i] - meanVol);
250
+ varianceVol += Math.pow(volumes[i] - meanVol, 2);
251
+ }
252
+ covariance /= priceChanges.length;
253
+ varianceVol /= priceChanges.length;
254
+ if (varianceVol < 1e-4) return 0;
255
+ return Math.abs(covariance / varianceVol);
256
+ }
257
+ module2.exports = { computeKyleLambda };
258
+ }
259
+ });
260
+
261
+ // ultra-scalping/models/kalman.js
262
+ var require_kalman = __commonJS({
263
+ "ultra-scalping/models/kalman.js"(exports2, module2) {
264
+ var KALMAN_PROCESS_NOISE = 0.01;
265
+ var KALMAN_MEASUREMENT_NOISE = 0.1;
266
+ function applyKalmanFilter(state, measurement) {
267
+ if (!state || state.estimate === 0) {
268
+ return {
269
+ estimate: measurement,
270
+ errorCovariance: 1,
271
+ newEstimate: measurement
272
+ };
273
+ }
274
+ const predictedEstimate = state.estimate;
275
+ const predictedCovariance = state.errorCovariance + KALMAN_PROCESS_NOISE;
276
+ const kalmanGain = predictedCovariance / (predictedCovariance + KALMAN_MEASUREMENT_NOISE);
277
+ const newEstimate = predictedEstimate + kalmanGain * (measurement - predictedEstimate);
278
+ const newCovariance = (1 - kalmanGain) * predictedCovariance;
279
+ return {
280
+ estimate: newEstimate,
281
+ errorCovariance: newCovariance,
282
+ newEstimate
283
+ };
284
+ }
285
+ function createKalmanState() {
286
+ return {
287
+ estimate: 0,
288
+ errorCovariance: 1
289
+ };
290
+ }
291
+ module2.exports = {
292
+ applyKalmanFilter,
293
+ createKalmanState,
294
+ KALMAN_PROCESS_NOISE,
295
+ KALMAN_MEASUREMENT_NOISE
296
+ };
297
+ }
298
+ });
299
+
300
+ // ultra-scalping/models/volatility.js
301
+ var require_volatility = __commonJS({
302
+ "ultra-scalping/models/volatility.js"(exports2, module2) {
303
+ function calculateATR(bars, period = 14) {
304
+ if (bars.length < period + 1) return 2.5;
305
+ const trValues = [];
306
+ for (let i = bars.length - period; i < bars.length; i++) {
307
+ const bar = bars[i];
308
+ const prevClose = bars[i - 1].close;
309
+ const tr = Math.max(
310
+ bar.high - bar.low,
311
+ Math.abs(bar.high - prevClose),
312
+ Math.abs(bar.low - prevClose)
313
+ );
314
+ trValues.push(tr);
315
+ }
316
+ return trValues.reduce((a, b) => a + b, 0) / trValues.length;
317
+ }
318
+ function detectVolatilityRegime(atr, atrHistory, tickSize) {
319
+ let atrPercentile = 0.5;
320
+ if (atrHistory.length >= 20) {
321
+ atrPercentile = atrHistory.filter((a) => a <= atr).length / atrHistory.length;
322
+ }
323
+ let regime, params;
324
+ if (atrPercentile < 0.25) {
325
+ regime = "low";
326
+ params = {
327
+ stopMultiplier: 0.8,
328
+ targetMultiplier: 0.9,
329
+ zscoreThreshold: 1.2,
330
+ confidenceBonus: 0.05
331
+ };
332
+ } else if (atrPercentile < 0.75) {
333
+ regime = "normal";
334
+ params = {
335
+ stopMultiplier: 1,
336
+ targetMultiplier: 1,
337
+ zscoreThreshold: 1.5,
338
+ confidenceBonus: 0
339
+ };
340
+ } else {
341
+ regime = "high";
342
+ params = {
343
+ stopMultiplier: 1.3,
344
+ targetMultiplier: 1.2,
345
+ zscoreThreshold: 2,
346
+ confidenceBonus: -0.05
347
+ };
348
+ }
349
+ return { regime, params, atrPercentile };
350
+ }
351
+ module2.exports = { calculateATR, detectVolatilityRegime };
352
+ }
353
+ });
354
+
355
+ // ultra-scalping/models/ofi.js
356
+ var require_ofi = __commonJS({
357
+ "ultra-scalping/models/ofi.js"(exports2, module2) {
358
+ function computeOrderFlowImbalance(bars, lookback = 20) {
359
+ if (bars.length < lookback) return 0;
360
+ const recentBars = bars.slice(-lookback);
361
+ let totalBuyPressure = 0;
362
+ let totalSellPressure = 0;
363
+ for (const bar of recentBars) {
364
+ const barRange = bar.high - bar.low;
365
+ if (barRange > 0) {
366
+ const closePosition = (bar.close - bar.low) / barRange;
367
+ totalBuyPressure += closePosition * bar.volume;
368
+ totalSellPressure += (1 - closePosition) * bar.volume;
369
+ }
370
+ }
371
+ const totalPressure = totalBuyPressure + totalSellPressure;
372
+ if (totalPressure < 1) return 0;
373
+ return (totalBuyPressure - totalSellPressure) / totalPressure;
374
+ }
375
+ module2.exports = { computeOrderFlowImbalance };
376
+ }
377
+ });
378
+
379
+ // ultra-scalping/models/index.js
380
+ var require_models = __commonJS({
381
+ "ultra-scalping/models/index.js"(exports2, module2) {
382
+ var { computeZScore } = require_zscore();
383
+ var { computeVPIN } = require_vpin();
384
+ var { computeKyleLambda } = require_kyle();
385
+ var { applyKalmanFilter, createKalmanState } = require_kalman();
386
+ var { calculateATR, detectVolatilityRegime } = require_volatility();
387
+ var { computeOrderFlowImbalance } = require_ofi();
388
+ module2.exports = {
389
+ computeZScore,
390
+ computeVPIN,
391
+ computeKyleLambda,
392
+ applyKalmanFilter,
393
+ createKalmanState,
394
+ calculateATR,
395
+ detectVolatilityRegime,
396
+ computeOrderFlowImbalance
397
+ };
398
+ }
399
+ });
400
+
401
+ // ultra-scalping/core.js
402
+ var require_core = __commonJS({
403
+ "ultra-scalping/core.js"(exports2, module2) {
404
+ var EventEmitter2 = require("events");
405
+ var { DEFAULT_CONFIG } = require_config();
406
+ var { generateSignal } = require_signal();
407
+ var {
408
+ computeZScore,
409
+ computeVPIN,
410
+ computeKyleLambda,
411
+ applyKalmanFilter,
412
+ createKalmanState,
413
+ calculateATR,
414
+ detectVolatilityRegime,
415
+ computeOrderFlowImbalance
416
+ } = require_models();
417
+ var HQXUltraScalping2 = class extends EventEmitter2 {
418
+ constructor(config = {}) {
419
+ super();
420
+ this.tickSize = config.tickSize || 0.25;
421
+ this.tickValue = config.tickValue || 5;
422
+ this.config = { ...DEFAULT_CONFIG, ...config };
423
+ this.barHistory = /* @__PURE__ */ new Map();
424
+ this.priceBuffer = /* @__PURE__ */ new Map();
425
+ this.volumeBuffer = /* @__PURE__ */ new Map();
426
+ this.kalmanStates = /* @__PURE__ */ new Map();
427
+ this.atrHistory = /* @__PURE__ */ new Map();
428
+ this.recentTrades = [];
429
+ this.winStreak = 0;
430
+ this.lossStreak = 0;
431
+ this.lastSignalTime = 0;
432
+ this.stats = { signals: 0, trades: 0, wins: 0, losses: 0, pnl: 0 };
433
+ }
434
+ initialize(contractId, tickSize = 0.25, tickValue = 5) {
435
+ this.tickSize = tickSize;
436
+ this.tickValue = tickValue;
437
+ this.barHistory.set(contractId, []);
438
+ this.priceBuffer.set(contractId, []);
439
+ this.volumeBuffer.set(contractId, []);
440
+ this.atrHistory.set(contractId, []);
441
+ this.kalmanStates.set(contractId, createKalmanState());
442
+ this.emit("log", {
443
+ type: "info",
444
+ message: `[HQX-UltraScalping] Initialized for ${contractId}: tick=${tickSize}, value=${tickValue}`
445
+ });
446
+ this.emit("log", {
447
+ type: "info",
448
+ message: `[HQX-UltraScalping] 6 Models: Z-Score(30%), OFI(20%), VPIN(15%), Kalman(15%), Kyle(10%), Vol(10%)`
449
+ });
450
+ }
451
+ processTick(tick) {
452
+ const { contractId, price, volume, timestamp } = tick;
453
+ const bar = {
454
+ timestamp: timestamp || Date.now(),
455
+ open: price,
456
+ high: price,
457
+ low: price,
458
+ close: price,
459
+ volume: volume || 1
460
+ };
461
+ return this.processBar(contractId, bar);
462
+ }
463
+ onTick(tick) {
464
+ return this.processTick(tick);
465
+ }
466
+ onTrade(trade) {
467
+ return this.processTick({
468
+ contractId: trade.contractId || trade.symbol,
469
+ price: trade.price,
470
+ volume: trade.size || trade.volume || 1,
471
+ side: trade.side,
472
+ timestamp: trade.timestamp || Date.now()
473
+ });
474
+ }
475
+ processBar(contractId, bar) {
476
+ let bars = this.barHistory.get(contractId);
477
+ if (!bars) {
478
+ this.initialize(contractId);
479
+ bars = this.barHistory.get(contractId);
480
+ }
481
+ bars.push(bar);
482
+ if (bars.length > 500) bars.shift();
483
+ const prices = this.priceBuffer.get(contractId);
484
+ prices.push(bar.close);
485
+ if (prices.length > 200) prices.shift();
486
+ const volumes = this.volumeBuffer.get(contractId);
487
+ const barRange = bar.high - bar.low;
488
+ let buyVol = bar.volume * 0.5;
489
+ let sellVol = bar.volume * 0.5;
490
+ if (barRange > 0) {
491
+ const closePosition = (bar.close - bar.low) / barRange;
492
+ buyVol = bar.volume * closePosition;
493
+ sellVol = bar.volume * (1 - closePosition);
494
+ }
495
+ volumes.push({ buy: buyVol, sell: sellVol });
496
+ if (volumes.length > 100) volumes.shift();
497
+ if (bars.length < 50) return null;
498
+ const zscore = computeZScore(prices);
499
+ const vpin = computeVPIN(volumes, this.config.vpinWindow);
500
+ const kyleLambda = computeKyleLambda(bars);
501
+ const kalmanState = this.kalmanStates.get(contractId);
502
+ const kalmanResult = applyKalmanFilter(kalmanState, bar.close);
503
+ this.kalmanStates.set(contractId, {
504
+ estimate: kalmanResult.estimate,
505
+ errorCovariance: kalmanResult.errorCovariance
506
+ });
507
+ const kalmanEstimate = kalmanResult.newEstimate;
508
+ const atr = calculateATR(bars);
509
+ const atrHist = this.atrHistory.get(contractId);
510
+ atrHist.push(atr);
511
+ if (atrHist.length > 500) atrHist.shift();
512
+ const { regime, params: volParams } = detectVolatilityRegime(atr, atrHist, this.tickSize);
513
+ const ofi = computeOrderFlowImbalance(bars, this.config.ofiLookback);
514
+ if (Date.now() - this.lastSignalTime < this.config.cooldownMs) {
515
+ return null;
516
+ }
517
+ const signal = generateSignal({
518
+ contractId,
519
+ currentPrice: bar.close,
520
+ zscore,
521
+ vpin,
522
+ kyleLambda,
523
+ kalmanEstimate,
524
+ regime,
525
+ volParams,
526
+ ofi,
527
+ config: this.config,
528
+ tickSize: this.tickSize
529
+ });
530
+ if (signal) {
531
+ this.lastSignalTime = Date.now();
532
+ this.stats.signals++;
533
+ this.emit("signal", {
534
+ side: signal.direction === "long" ? "buy" : "sell",
535
+ action: "open",
536
+ reason: `Z=${zscore.toFixed(2)}, VPIN=${(vpin * 100).toFixed(0)}%, OFI=${(ofi * 100).toFixed(0)}%, cf=${(signal.confidence * 100).toFixed(0)}%`,
537
+ ...signal
538
+ });
539
+ this.emit("log", {
540
+ type: "info",
541
+ message: `[HQX] SIGNAL: ${signal.direction.toUpperCase()} @ ${bar.close.toFixed(2)} | Z:${zscore.toFixed(2)} VPIN:${(vpin * 100).toFixed(0)}% OFI:${(ofi * 100).toFixed(0)}% Kyle:${kyleLambda.toFixed(5)} Regime:${regime} | Conf:${(signal.confidence * 100).toFixed(0)}%`
542
+ });
543
+ }
544
+ return signal;
545
+ }
546
+ shouldExitByZScore(contractId) {
547
+ const prices = this.priceBuffer.get(contractId);
548
+ if (!prices || prices.length < 50) return false;
549
+ const zscore = computeZScore(prices);
550
+ return Math.abs(zscore) < this.config.zscoreExitThreshold;
551
+ }
552
+ getModelValues(contractId) {
553
+ const prices = this.priceBuffer.get(contractId);
554
+ const volumes = this.volumeBuffer.get(contractId);
555
+ const bars = this.barHistory.get(contractId);
556
+ if (!prices || !volumes || !bars || bars.length < 50) {
557
+ return null;
558
+ }
559
+ const zscore = computeZScore(prices);
560
+ const vpin = computeVPIN(volumes, this.config.vpinWindow);
561
+ const kyleLambda = computeKyleLambda(bars);
562
+ const ofi = computeOrderFlowImbalance(bars, this.config.ofiLookback);
563
+ return {
564
+ zscore: Math.min(1, Math.abs(zscore) / 4),
565
+ vpin: 1 - vpin,
566
+ kyleLambda: kyleLambda > 1e-3 ? 0.5 : 0.8,
567
+ kalman: 0.7,
568
+ volatility: 0.7,
569
+ ofi: Math.abs(ofi) > 0.1 ? 0.8 : 0.5,
570
+ composite: 0.7,
571
+ raw: { zscore, vpin, kyleLambda, ofi }
572
+ };
573
+ }
574
+ getAnalysisState(contractId, currentPrice) {
575
+ const bars = this.barHistory.get(contractId) || [];
576
+ if (bars.length < 50) {
577
+ return { ready: false, message: `Collecting data... ${bars.length}/50 bars` };
578
+ }
579
+ const prices = this.priceBuffer.get(contractId) || [];
580
+ const volumes = this.volumeBuffer.get(contractId) || [];
581
+ const atrHist = this.atrHistory.get(contractId) || [];
582
+ const zscore = computeZScore(prices);
583
+ const vpin = computeVPIN(volumes, this.config.vpinWindow);
584
+ const ofi = computeOrderFlowImbalance(bars, this.config.ofiLookback);
585
+ const kyleLambda = computeKyleLambda(bars);
586
+ const atr = calculateATR(bars);
587
+ const { regime, params } = detectVolatilityRegime(atr, atrHist, this.tickSize);
588
+ return {
589
+ ready: true,
590
+ zScore: zscore,
591
+ vpin,
592
+ ofi,
593
+ kyleLambda,
594
+ regime,
595
+ stopTicks: Math.round(this.config.baseStopTicks * params.stopMultiplier),
596
+ targetTicks: Math.round(this.config.baseTargetTicks * params.targetMultiplier),
597
+ threshold: params.zscoreThreshold,
598
+ barsProcessed: bars.length,
599
+ models: "6 (Z-Score, VPIN, Kyle, Kalman, Vol, OFI)"
600
+ };
601
+ }
602
+ recordTradeResult(pnl) {
603
+ this.recentTrades.push({ netPnl: pnl, timestamp: Date.now() });
604
+ if (this.recentTrades.length > 100) this.recentTrades.shift();
605
+ if (pnl > 0) {
606
+ this.winStreak++;
607
+ this.lossStreak = 0;
608
+ this.stats.wins++;
609
+ } else {
610
+ this.lossStreak++;
611
+ this.winStreak = 0;
612
+ this.stats.losses++;
613
+ }
614
+ this.stats.trades++;
615
+ this.stats.pnl += pnl;
616
+ this.emit("log", {
617
+ type: "debug",
618
+ message: `[HQX] Trade result: ${pnl > 0 ? "WIN" : "LOSS"} $${pnl.toFixed(2)}, streak: ${pnl > 0 ? this.winStreak : -this.lossStreak}`
619
+ });
620
+ }
621
+ getBarHistory(contractId) {
622
+ return this.barHistory.get(contractId) || [];
623
+ }
624
+ getStats() {
625
+ return this.stats;
626
+ }
627
+ reset(contractId) {
628
+ this.barHistory.set(contractId, []);
629
+ this.priceBuffer.set(contractId, []);
630
+ this.volumeBuffer.set(contractId, []);
631
+ this.atrHistory.set(contractId, []);
632
+ this.kalmanStates.set(contractId, createKalmanState());
633
+ this.emit("log", {
634
+ type: "info",
635
+ message: `[HQX-UltraScalping] Reset state for ${contractId}`
636
+ });
637
+ }
638
+ };
639
+ module2.exports = { HQXUltraScalping: HQXUltraScalping2 };
640
+ }
641
+ });
642
+
643
+ // ultra-scalping/index.js
644
+ var EventEmitter = require("events");
645
+ var { HQXUltraScalping } = require_core();
646
+ var { OrderSide, SignalStrength } = require_types();
647
+ var UltraScalpingStrategy = class extends EventEmitter {
648
+ constructor(config = {}) {
649
+ super();
650
+ this.config = config;
651
+ this.strategy = new HQXUltraScalping(config);
652
+ this.strategy.on("signal", (sig) => this.emit("signal", sig));
653
+ this.strategy.on("log", (log) => this.emit("log", log));
654
+ }
655
+ // Interface methods (compatible with M1)
656
+ processTick(tick) {
657
+ return this.strategy.processTick(tick);
658
+ }
659
+ onTick(tick) {
660
+ return this.strategy.onTick(tick);
661
+ }
662
+ onTrade(trade) {
663
+ return this.strategy.onTrade(trade);
664
+ }
665
+ processBar(contractId, bar) {
666
+ return this.strategy.processBar(contractId, bar);
667
+ }
668
+ initialize(contractId, tickSize, tickValue) {
669
+ return this.strategy.initialize(contractId, tickSize, tickValue);
670
+ }
671
+ getAnalysisState(contractId, price) {
672
+ return this.strategy.getAnalysisState(contractId, price);
673
+ }
674
+ recordTradeResult(pnl) {
675
+ return this.strategy.recordTradeResult(pnl);
676
+ }
677
+ reset(contractId) {
678
+ return this.strategy.reset(contractId);
679
+ }
680
+ getStats() {
681
+ return this.strategy.getStats();
682
+ }
683
+ getBarHistory(contractId) {
684
+ return this.strategy.getBarHistory(contractId);
685
+ }
686
+ getModelValues(contractId) {
687
+ return this.strategy.getModelValues(contractId);
688
+ }
689
+ shouldExitByZScore(contractId) {
690
+ return this.strategy.shouldExitByZScore(contractId);
691
+ }
692
+ generateSignal(params) {
693
+ return null;
694
+ }
695
+ // Signals come from processBar
696
+ };
697
+ module.exports = {
698
+ HQXUltraScalping,
699
+ UltraScalpingStrategy,
700
+ // Aliases for backward compatibility
701
+ M1: UltraScalpingStrategy,
702
+ S1: HQXUltraScalping,
703
+ OrderSide,
704
+ SignalStrength
705
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "2.9.81",
3
+ "version": "2.9.84",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -51,10 +51,46 @@ const ORDER_SIDE = {
51
51
 
52
52
  // All symbols/contracts come from Rithmic API (TICKER_PLANT)
53
53
 
54
+ // Contract descriptions for display (API only returns short codes)
55
+ const CONTRACT_DESCRIPTIONS = {
56
+ // Equity Index Futures
57
+ ES: 'E-mini S&P 500', MES: 'Micro E-mini S&P 500',
58
+ NQ: 'E-mini Nasdaq 100', MNQ: 'Micro E-mini Nasdaq',
59
+ RTY: 'E-mini Russell 2000', M2K: 'Micro E-mini Russell',
60
+ YM: 'E-mini Dow $5', MYM: 'Micro E-mini Dow',
61
+ EMD: 'E-mini S&P MidCap', NKD: 'Nikkei 225',
62
+ // Metals
63
+ GC: 'Gold', MGC: 'Micro Gold', '1OZ': 'Micro Gold (1oz)',
64
+ SI: 'Silver', SIL: 'Micro Silver', HG: 'Copper', MHG: 'Micro Copper',
65
+ PL: 'Platinum', PA: 'Palladium',
66
+ // Energy
67
+ CL: 'Crude Oil WTI', MCL: 'Micro Crude Oil', NG: 'Natural Gas',
68
+ BZ: 'Brent Crude', RB: 'RBOB Gasoline', HO: 'Heating Oil',
69
+ // Currencies
70
+ '6E': 'Euro FX', M6E: 'Micro Euro', '6B': 'British Pound', M6B: 'Micro GBP',
71
+ '6A': 'Australian $', M6A: 'Micro AUD', '6J': 'Japanese Yen',
72
+ '6C': 'Canadian $', '6S': 'Swiss Franc', '6N': 'New Zealand $',
73
+ '6M': 'Mexican Peso', E7: 'E-mini Euro',
74
+ // Crypto
75
+ BTC: 'Bitcoin', MBT: 'Micro Bitcoin', ETH: 'Ether', MET: 'Micro Ether',
76
+ // Treasuries
77
+ ZB: '30Y T-Bond', ZN: '10Y T-Note', ZF: '5Y T-Note', ZT: '2Y T-Note',
78
+ ZQ: '30-Day Fed Funds', TN: 'Ultra 10Y',
79
+ // Grains
80
+ ZC: 'Corn', ZS: 'Soybeans', ZW: 'Wheat', ZM: 'Soybean Meal',
81
+ ZL: 'Soybean Oil', ZO: 'Oats',
82
+ // Livestock
83
+ LE: 'Live Cattle', HE: 'Lean Hogs', GF: 'Feeder Cattle',
84
+ };
85
+
86
+ const getContractDescription = (baseSymbol) => CONTRACT_DESCRIPTIONS[baseSymbol] || baseSymbol;
87
+
54
88
  module.exports = {
55
89
  ACCOUNT_STATUS,
56
90
  ACCOUNT_TYPE,
57
91
  ORDER_STATUS,
58
92
  ORDER_TYPE,
59
- ORDER_SIDE
93
+ ORDER_SIDE,
94
+ CONTRACT_DESCRIPTIONS,
95
+ getContractDescription,
60
96
  };
@@ -17,6 +17,8 @@ const {
17
17
  ORDER_STATUS,
18
18
  ORDER_TYPE,
19
19
  ORDER_SIDE,
20
+ CONTRACT_DESCRIPTIONS,
21
+ getContractDescription,
20
22
  } = require('./constants');
21
23
 
22
24
  const {
@@ -43,6 +45,8 @@ module.exports = {
43
45
  ORDER_STATUS,
44
46
  ORDER_TYPE,
45
47
  ORDER_SIDE,
48
+ CONTRACT_DESCRIPTIONS,
49
+ getContractDescription,
46
50
 
47
51
  // Settings
48
52
  TIMEOUTS,
@@ -9,6 +9,7 @@ const ora = require('ora');
9
9
 
10
10
  const { connections } = require('../../services');
11
11
  const { prompts } = require('../../utils');
12
+ const { getContractDescription } = require('../../config');
12
13
  const { checkMarketHours } = require('../../services/rithmic/market');
13
14
  const { getActiveAgentCount, getSupervisionConfig, getActiveAgents } = require('../ai-agents');
14
15
  const { launchCopyTrading } = require('./copy-executor');
@@ -383,10 +384,14 @@ const selectSymbol = async (service) => {
383
384
 
384
385
  spinner.succeed(`Found ${contracts.length} contracts`);
385
386
 
386
- const options = contracts.map(c => ({
387
- label: `${c.symbol} - ${c.name} (${c.exchange})`,
388
- value: c
389
- }));
387
+ const options = contracts.map(c => {
388
+ const desc = getContractDescription(c.baseSymbol || c.name);
389
+ const isMicro = desc.toLowerCase().includes('micro');
390
+ const label = isMicro
391
+ ? `${c.symbol} - ${chalk.cyan(desc)} (${c.exchange})`
392
+ : `${c.symbol} - ${desc} (${c.exchange})`;
393
+ return { label, value: c };
394
+ });
390
395
  options.push({ label: chalk.gray('< Back'), value: 'back' });
391
396
 
392
397
  const selected = await prompts.selectOption(chalk.yellow('Select Symbol:'), options);
@@ -12,6 +12,7 @@ const ora = require('ora');
12
12
  const { getLogoWidth, centerText, displayBanner , clearScreen } = require('../../ui');
13
13
  const { prompts } = require('../../utils');
14
14
  const { connections } = require('../../services');
15
+ const { getContractDescription } = require('../../config');
15
16
  const { getActiveProvider, getActiveAgents } = require('../ai-agents');
16
17
  const cliproxy = require('../../services/cliproxy');
17
18
  const { runPreflightCheck, formatPreflightResults, getPreflightSummary } = require('../../services/ai-supervision');
@@ -146,7 +147,14 @@ const selectSymbol = async (service) => {
146
147
 
147
148
  spinner.succeed(`Found ${result.contracts.length} contracts`);
148
149
 
149
- const options = result.contracts.map(c => ({ label: `${c.symbol} - ${c.name} (${c.exchange})`, value: c }));
150
+ const options = result.contracts.map(c => {
151
+ const desc = getContractDescription(c.baseSymbol || c.name);
152
+ const isMicro = desc.toLowerCase().includes('micro');
153
+ const label = isMicro
154
+ ? `${c.symbol} - ${chalk.cyan(desc)} (${c.exchange})`
155
+ : `${c.symbol} - ${desc} (${c.exchange})`;
156
+ return { label, value: c };
157
+ });
150
158
  options.push({ label: chalk.gray('< Back'), value: 'back' });
151
159
 
152
160
  const selected = await prompts.selectOption(chalk.yellow('Select Symbol:'), options);
@@ -8,6 +8,7 @@ const ora = require('ora');
8
8
 
9
9
  const { connections } = require('../../services');
10
10
  const { prompts } = require('../../utils');
11
+ const { getContractDescription } = require('../../config');
11
12
  const { checkMarketHours } = require('../../services/rithmic/market');
12
13
  const { executeAlgo } = require('./algo-executor');
13
14
  const { getActiveAgentCount, getSupervisionConfig, getActiveAgents } = require('../ai-agents');
@@ -281,11 +282,15 @@ const selectSymbol = async (service, account) => {
281
282
 
282
283
  spinner.succeed(`Found ${contracts.length} contracts`);
283
284
 
284
- // Display sorted contracts from API: symbol - name (exchange)
285
- const options = contracts.map(c => ({
286
- label: `${c.symbol} - ${c.name} (${c.exchange})`,
287
- value: c
288
- }));
285
+ // Display sorted contracts with full description
286
+ const options = contracts.map(c => {
287
+ const desc = getContractDescription(c.baseSymbol || c.name);
288
+ const isMicro = desc.toLowerCase().includes('micro');
289
+ const label = isMicro
290
+ ? `${c.symbol} - ${chalk.cyan(desc)} (${c.exchange})`
291
+ : `${c.symbol} - ${desc} (${c.exchange})`;
292
+ return { label, value: c };
293
+ });
289
294
 
290
295
  options.push({ label: chalk.gray('< Back'), value: 'back' });
291
296