hedgequantx 2.9.152 → 2.9.154
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/dist/lib/m/hqx-2b.js
CHANGED
|
@@ -1 +1,883 @@
|
|
|
1
|
-
function _0x4f16(_0x1214b8,_0x18e896){_0x1214b8=_0x1214b8-0x8d;const _0x20c70d=_0x20c7();let _0x4f1627=_0x20c70d[_0x1214b8];return _0x4f1627;}const _0x27887c=_0x4f16;(function(_0x45217f,_0x399198){const _0xccb945=_0x4f16,_0xd39802=_0x45217f();while(!![]){try{const _0xf82280=parseInt(_0xccb945(0xeb))/0x1+parseInt(_0xccb945(0xef))/0x2*(-parseInt(_0xccb945(0xfa))/0x3)+-parseInt(_0xccb945(0xe9))/0x4+parseInt(_0xccb945(0x117))/0x5*(-parseInt(_0xccb945(0x10c))/0x6)+parseInt(_0xccb945(0x128))/0x7+-parseInt(_0xccb945(0x105))/0x8*(parseInt(_0xccb945(0xa5))/0x9)+-parseInt(_0xccb945(0xdc))/0xa*(-parseInt(_0xccb945(0x98))/0xb);if(_0xf82280===_0x399198)break;else _0xd39802['push'](_0xd39802['shift']());}catch(_0x18a46e){_0xd39802['push'](_0xd39802['shift']());}}}(_0x20c7,0x963e3));const EventEmitter=require('events'),{v4:uuidv4}=require(_0x27887c(0xe6)),OrderSide={'BID':0x0,'ASK':0x1},SignalStrength={'WEAK':0x1,'MODERATE':0x2,'STRONG':0x3,'VERY_STRONG':0x4},SweepType={'HIGH_SWEEP':'high','LOW_SWEEP':'low'},ZoneType={'RESISTANCE':_0x27887c(0xad),'SUPPORT':'support'},DEFAULT_CONFIG={'tickSize':0.25,'tickValue':0x5,'swing':{'lookbackBars':0x1,'minStrength':0x1,'confirmationBars':0x1},'zone':{'clusterToleranceTicks':0x6,'minTouches':0x1,'maxZoneAgeBars':0xc8,'maxZoneDistanceTicks':0x3c,'cooldownBars':0x5},'sweep':{'minPenetrationTicks':0.25,'maxPenetrationTicks':0xc,'maxDurationBars':0x3,'minQualityScore':0.15,'minVolumeRatio':0.3,'minBodyRatio':0.05},'execution':{'stopTicks':0xa,'targetTicks':0x28,'breakevenTicks':0x4,'trailTriggerTicks':0x8,'trailDistanceTicks':0x4,'cooldownMs':0x3a98,'minHoldTimeMs':0x2710,'slippageTicks':0x1,'commissionPerSide':0x2},'session':{'enabled':![],'startHour':0x9,'startMinute':0x1e,'endHour':0x10,'endMinute':0x0,'timezone':'America/New_York'}};class SwingPoint{constructor(_0x345d70,_0x4a2bd1,_0x860d21,_0x504a63,_0x213231=0x1){const _0x5aa1ff=_0x27887c;this['type']=_0x345d70,this[_0x5aa1ff(0x10d)]=_0x4a2bd1,this['barIndex']=_0x860d21,this[_0x5aa1ff(0x12b)]=_0x504a63,this[_0x5aa1ff(0x11d)]=_0x213231;}}class LiquidityZone{constructor(_0x1f7f52,_0x4fe0aa,_0x5df5d6,_0x48bd58,_0x3ba692){const _0x5b3c17=_0x27887c;this['id']=uuidv4(),this[_0x5b3c17(0xf3)]=_0x1f7f52,this[_0x5b3c17(0x12a)]=_0x4fe0aa,this['priceLow']=_0x5df5d6,this['createdAt']=_0x48bd58,this[_0x5b3c17(0xd3)]=_0x3ba692,this[_0x5b3c17(0x8d)]=0x1,this['swept']=![],this[_0x5b3c17(0x10a)]=null,this['lastUsedBarIndex']=-0x3e7,this['qualityScore']=0.5;}[_0x27887c(0xb1)](_0x20fde3,_0x3d911a,_0x6b01fd){const _0xa0a354=_0x27887c,_0x269b78=_0x3d911a*_0x6b01fd;return _0x20fde3>=this[_0xa0a354(0xb8)]-_0x269b78&&_0x20fde3<=this['priceHigh']+_0x269b78;}['getLevel'](){return(this['priceHigh']+this['priceLow'])/0x2;}}class SweepEvent{constructor(_0x478487,_0x5065fa,_0x55ae12,_0x3e6539,_0x127e66){const _0x5c0a02=_0x27887c;this['sweepType']=_0x478487,this[_0x5c0a02(0x119)]=_0x5065fa,this[_0x5c0a02(0xd1)]=_0x55ae12,this['extremeBarIndex']=_0x3e6539,this[_0x5c0a02(0xd7)]=_0x127e66,this[_0x5c0a02(0x10b)]=null,this['isValid']=![],this['qualityScore']=0x0,this['penetrationTicks']=0x0,this['durationBars']=0x0,this['volumeRatio']=0x1;}}class HQX2BLiquiditySweep extends EventEmitter{constructor(_0x8a6fb7={}){const _0x2a76a8=_0x27887c;super(),this[_0x2a76a8(0xb0)]=this['_mergeConfig'](DEFAULT_CONFIG,_0x8a6fb7),this[_0x2a76a8(0xe1)]=this[_0x2a76a8(0xb0)]['tickSize'],this['tickValue']=this[_0x2a76a8(0xb0)][_0x2a76a8(0x113)],this[_0x2a76a8(0x132)]=new Map(),this['swingPoints']=new Map(),this[_0x2a76a8(0x11a)]=new Map(),this[_0x2a76a8(0x92)]=new Map(),this['currentBar']=new Map(),this[_0x2a76a8(0xc2)]=0xea60,this[_0x2a76a8(0x11b)]=0x0,this['startTime']=Date['now'](),this['stats']={'signals':0x0,'trades':0x0,'wins':0x0,'losses':0x0,'pnl':0x0},this['recentTrades']=[];}['_mergeConfig'](_0x13babd,_0x17286a){const _0x25825b=_0x27887c,_0x1fe3c7={..._0x13babd};for(const _0x1d4310 in _0x17286a){typeof _0x17286a[_0x1d4310]===_0x25825b(0xa7)&&!Array['isArray'](_0x17286a[_0x1d4310])?_0x1fe3c7[_0x1d4310]={..._0x13babd[_0x1d4310],..._0x17286a[_0x1d4310]}:_0x1fe3c7[_0x1d4310]=_0x17286a[_0x1d4310];}return _0x1fe3c7;}[_0x27887c(0xc7)](_0x29bc7e){const _0x5ed4bf=_0x27887c;if(!this[_0x5ed4bf(0xb0)][_0x5ed4bf(0xfc)][_0x5ed4bf(0xc5)])return!![];const _0x5816bf=new Date(_0x29bc7e),_0x4a8d0a=this[_0x5ed4bf(0x118)](_0x5816bf)?-0x4:-0x5,_0x2af2af=_0x5816bf['getUTCHours'](),_0x1dad00=_0x5816bf[_0x5ed4bf(0xa3)](),_0x3ec5f8=(_0x2af2af+_0x4a8d0a+0x18)%0x18,{startHour:_0x4cb439,startMinute:_0x5196c6,endHour:_0x2b9ab3,endMinute:_0x5eae24}=this['config'][_0x5ed4bf(0xfc)],_0x5f1ab9=_0x3ec5f8*0x3c+_0x1dad00,_0x352012=_0x4cb439*0x3c+_0x5196c6,_0x283058=_0x2b9ab3*0x3c+_0x5eae24;return _0x5f1ab9>=_0x352012&&_0x5f1ab9<_0x283058;}['isDST'](_0x36d92e){const _0x1f604c=_0x27887c,_0x19a6ac=new Date(_0x36d92e['getFullYear'](),0x0,0x1),_0x2e5199=new Date(_0x36d92e[_0x1f604c(0x9d)](),0x6,0x1),_0x152251=Math['max'](_0x19a6ac[_0x1f604c(0xcc)](),_0x2e5199['getTimezoneOffset']());return _0x36d92e['getTimezoneOffset']()<_0x152251;}[_0x27887c(0x100)](_0x44dffd,_0x2efb71=0.25,_0x13e6c2=0x5){const _0x5e050f=_0x27887c;this[_0x5e050f(0xe1)]=_0x2efb71,this['tickValue']=_0x13e6c2,this[_0x5e050f(0xb0)][_0x5e050f(0xe1)]=_0x2efb71,this['config'][_0x5e050f(0x113)]=_0x13e6c2,this[_0x5e050f(0x132)]['set'](_0x44dffd,[]),this['swingPoints']['set'](_0x44dffd,[]),this['liquidityZones']['set'](_0x44dffd,[]),this['activeSweeps']['set'](_0x44dffd,[]),this[_0x5e050f(0xf9)]['delete'](_0x44dffd),this['emit']('log',{'type':'info','message':'[HQX-2B]\x20Initialized\x20for\x20'+_0x44dffd+_0x5e050f(0x12e)+_0x2efb71+_0x5e050f(0x12d)+_0x13e6c2+',\x20TF=1min'}),this['emit']('log',{'type':_0x5e050f(0xcf),'message':_0x5e050f(0xf8)+this[_0x5e050f(0xb0)]['execution']['stopTicks']+'t,\x20Target='+this['config'][_0x5e050f(0x10f)][_0x5e050f(0x108)]+_0x5e050f(0xa0)+this['config']['execution'][_0x5e050f(0x111)]+_0x5e050f(0x93)+this['config']['execution']['trailTriggerTicks']+'/'+this['config']['execution'][_0x5e050f(0xaa)]});}['processTick'](_0x268f26){const _0x54d7dd=_0x27887c,{contractId:_0x128755,price:_0x5290e6,volume:_0x202369,timestamp:_0x5926bd}=_0x268f26,_0x45ae8b=_0x5926bd||Date[_0x54d7dd(0x11c)](),_0x1c6f62=_0x202369||0x1;if(!this[_0x54d7dd(0xc7)](_0x45ae8b))return null;if(!this['_tickCount'])this[_0x54d7dd(0x8f)]=0x0;this[_0x54d7dd(0x8f)]++;let _0x5e9b42=this[_0x54d7dd(0xf9)]['get'](_0x128755);const _0x4ed502=Math['floor'](_0x45ae8b/this['barIntervalMs'])*this['barIntervalMs'];if(!_0x5e9b42||_0x5e9b42[_0x54d7dd(0x99)]!==_0x4ed502){if(_0x5e9b42){const _0x52321d={'timestamp':_0x5e9b42[_0x54d7dd(0x99)],'open':_0x5e9b42['open'],'high':_0x5e9b42[_0x54d7dd(0xb9)],'low':_0x5e9b42[_0x54d7dd(0x91)],'close':_0x5e9b42[_0x54d7dd(0xde)],'volume':_0x5e9b42[_0x54d7dd(0xee)]},_0x537d5a=this[_0x54d7dd(0x132)][_0x54d7dd(0x123)](_0x128755)||[];this[_0x54d7dd(0xa9)](_0x54d7dd(0xaf),{'type':_0x54d7dd(0x9b),'message':_0x54d7dd(0x103)+(_0x537d5a[_0x54d7dd(0xf4)]+0x1)+_0x54d7dd(0xcb)+_0x52321d['close']['toFixed'](0x2)+_0x54d7dd(0xd0)+_0x52321d[_0x54d7dd(0xa6)][_0x54d7dd(0x97)](0x2)+'\x20H:'+_0x52321d['high'][_0x54d7dd(0x97)](0x2)+'\x20L:'+_0x52321d['low'][_0x54d7dd(0x97)](0x2)+'\x20Vol:'+_0x52321d['volume']});const _0x5e5bf2=this['processBar'](_0x128755,_0x52321d);return this[_0x54d7dd(0xf9)][_0x54d7dd(0x124)](_0x128755,{'startTime':_0x4ed502,'open':_0x5290e6,'high':_0x5290e6,'low':_0x5290e6,'close':_0x5290e6,'volume':_0x1c6f62}),_0x5e5bf2;}else return this['currentBar'][_0x54d7dd(0x124)](_0x128755,{'startTime':_0x4ed502,'open':_0x5290e6,'high':_0x5290e6,'low':_0x5290e6,'close':_0x5290e6,'volume':_0x1c6f62}),null;}else return _0x5e9b42['high']=Math['max'](_0x5e9b42[_0x54d7dd(0xb9)],_0x5290e6),_0x5e9b42['low']=Math['min'](_0x5e9b42[_0x54d7dd(0x91)],_0x5290e6),_0x5e9b42['close']=_0x5290e6,_0x5e9b42[_0x54d7dd(0xee)]+=_0x1c6f62,null;}['onTick'](_0x427df8){return this['processTick'](_0x427df8);}[_0x27887c(0xfd)](_0x389c8b){const _0x3fcce5=_0x27887c;return this['processTick']({'contractId':_0x389c8b['contractId']||_0x389c8b[_0x3fcce5(0x12c)],'price':_0x389c8b['price'],'volume':_0x389c8b['size']||_0x389c8b[_0x3fcce5(0xee)]||0x1,'timestamp':_0x389c8b['timestamp']||Date[_0x3fcce5(0x11c)]()});}[_0x27887c(0xc8)](_0x3376f5,_0x277a60){const _0x2458d8=_0x27887c;let _0x242747=this[_0x2458d8(0x132)]['get'](_0x3376f5);!_0x242747&&(this[_0x2458d8(0x100)](_0x3376f5),_0x242747=this[_0x2458d8(0x132)]['get'](_0x3376f5));_0x242747[_0x2458d8(0xbf)](_0x277a60);if(_0x242747['length']>0x1f4)_0x242747['shift']();const _0x533ba7=_0x242747[_0x2458d8(0xf4)]-0x1;if(_0x242747[_0x2458d8(0xf4)]<this[_0x2458d8(0xb0)][_0x2458d8(0x9e)]['lookbackBars']+0x2)return null;const _0x267e5c=this['swingPoints']['get'](_0x3376f5)['length'];this[_0x2458d8(0xc0)](_0x3376f5,_0x242747,_0x533ba7);const _0x353cbd=this['swingPoints']['get'](_0x3376f5);if(_0x353cbd['length']>_0x267e5c){const _0x1efe36=_0x353cbd[_0x353cbd['length']-0x1];this['emit'](_0x2458d8(0xaf),{'type':_0x2458d8(0x9b),'message':_0x2458d8(0xf7)+_0x1efe36['type'][_0x2458d8(0xe8)]()+'\x20@\x20'+_0x1efe36['price'][_0x2458d8(0x97)](0x2)+_0x2458d8(0xe4)+_0x353cbd['length']});}const _0x50d152=this['_detectSweep'](_0x3376f5,_0x242747,_0x533ba7);_0x50d152&&this['emit'](_0x2458d8(0xaf),{'type':_0x2458d8(0x9b),'message':'[2B]\x20SWEEP\x20'+_0x50d152[_0x2458d8(0xdb)]+'\x20|\x20Valid:\x20'+_0x50d152['isValid']+_0x2458d8(0x116)+_0x50d152[_0x2458d8(0x104)]['toFixed'](0x1)+_0x2458d8(0x121)+(_0x50d152['qualityScore']*0x64)['toFixed'](0x0)+'%'});const _0x22029e=this[_0x2458d8(0x11a)]['get'](_0x3376f5)['length'];this['_updateZones'](_0x3376f5,_0x533ba7);const _0x4d7b87=this[_0x2458d8(0x11a)][_0x2458d8(0x123)](_0x3376f5);if(_0x4d7b87['length']>_0x22029e){const _0x5ec6ae=_0x4d7b87[_0x4d7b87[_0x2458d8(0xf4)]-0x1];this['emit']('log',{'type':'debug','message':_0x2458d8(0x109)+_0x5ec6ae[_0x2458d8(0xf3)][_0x2458d8(0xe8)]()+'\x20@\x20'+_0x5ec6ae[_0x2458d8(0xe5)]()['toFixed'](0x2)+'\x20|\x20Total:\x20'+_0x4d7b87[_0x2458d8(0xf4)]});}if(_0x50d152&&_0x50d152['isValid']){this[_0x2458d8(0xa9)](_0x2458d8(0xaf),{'type':_0x2458d8(0x9b),'message':_0x2458d8(0xf6)+_0x50d152['sweepType']+'\x20sweep...'});const _0x184bb4=this['_generateSignal'](_0x3376f5,_0x277a60,_0x533ba7,_0x50d152);return!_0x184bb4&&this[_0x2458d8(0xa9)]('log',{'type':'debug','message':'[2B]\x20_generateSignal\x20returned\x20null\x20(likely\x20cooldown)'}),_0x184bb4;}return null;}['_detectSwings'](_0x116c6a,_0x97c483,_0x2937bc){const _0x3e993d=_0x27887c,_0x507691=this[_0x3e993d(0xb0)]['swing'][_0x3e993d(0xea)],_0x26827d=this['config'][_0x3e993d(0x9e)][_0x3e993d(0xda)];if(_0x2937bc<_0x507691*0x2)return;const _0x4be4ad=this[_0x3e993d(0x107)]['get'](_0x116c6a),_0x31cdb3=_0x2937bc-_0x507691,_0x203a86=_0x97c483[_0x31cdb3];_0x2937bc%0x5===0x0&&this[_0x3e993d(0xa9)]('log',{'type':'debug','message':_0x3e993d(0xfb)+_0x31cdb3+'\x20|\x20H:'+_0x203a86[_0x3e993d(0xb9)][_0x3e993d(0x97)](0x2)+'\x20L:'+_0x203a86['low'][_0x3e993d(0x97)](0x2)+_0x3e993d(0xca)+_0x4be4ad[_0x3e993d(0xf4)]});let _0x3d0c6a=!![],_0x1d1f31=0x0;for(let _0x5e8a8e=_0x31cdb3-_0x507691;_0x5e8a8e<=_0x31cdb3+_0x507691;_0x5e8a8e++){if(_0x5e8a8e===_0x31cdb3||_0x5e8a8e<0x0||_0x5e8a8e>=_0x97c483['length'])continue;if(_0x97c483[_0x5e8a8e][_0x3e993d(0xb9)]>=_0x203a86['high']){_0x3d0c6a=![];break;}_0x1d1f31++;}if(_0x3d0c6a&&_0x1d1f31>=_0x26827d){const _0x40f09f=_0x4be4ad[_0x3e993d(0xec)](_0xf1e8c0=>_0xf1e8c0[_0x3e993d(0xd3)]===_0x31cdb3&&_0xf1e8c0['type']==='high');!_0x40f09f&&(_0x4be4ad['push'](new SwingPoint('high',_0x203a86['high'],_0x31cdb3,_0x203a86['timestamp'],_0x1d1f31)),this['emit'](_0x3e993d(0xaf),{'type':_0x3e993d(0x9b),'message':_0x3e993d(0xe0)+_0x203a86[_0x3e993d(0xb9)]['toFixed'](0x2)+_0x3e993d(0xb5)+_0x31cdb3+_0x3e993d(0xd9)+_0x1d1f31}));}let _0x5a4b70=!![],_0x1a5a7c=0x0;for(let _0x2e1007=_0x31cdb3-_0x507691;_0x2e1007<=_0x31cdb3+_0x507691;_0x2e1007++){if(_0x2e1007===_0x31cdb3||_0x2e1007<0x0||_0x2e1007>=_0x97c483['length'])continue;if(_0x97c483[_0x2e1007]['low']<=_0x203a86[_0x3e993d(0x91)]){_0x5a4b70=![];break;}_0x1a5a7c++;}if(_0x5a4b70&&_0x1a5a7c>=_0x26827d){const _0x511e00=_0x4be4ad['find'](_0x2bb600=>_0x2bb600[_0x3e993d(0xd3)]===_0x31cdb3&&_0x2bb600[_0x3e993d(0xf3)]===_0x3e993d(0x91));!_0x511e00&&(_0x4be4ad[_0x3e993d(0xbf)](new SwingPoint('low',_0x203a86[_0x3e993d(0x91)],_0x31cdb3,_0x203a86['timestamp'],_0x1a5a7c)),this[_0x3e993d(0xa9)](_0x3e993d(0xaf),{'type':_0x3e993d(0x9b),'message':'[SWING]\x20NEW\x20LOW\x20@\x20'+_0x203a86['low']['toFixed'](0x2)+'\x20|\x20Bar\x20'+_0x31cdb3+'\x20|\x20Strength:\x20'+_0x1a5a7c}));}const _0x2cba84=this['config']['zone']['maxZoneAgeBars'];while(_0x4be4ad['length']>0x0&&_0x4be4ad[0x0][_0x3e993d(0xd3)]<_0x2937bc-_0x2cba84){_0x4be4ad['shift']();}}['_updateZones'](_0x5c8589,_0x4b876e){const _0x491e8d=_0x27887c,_0x46d749=this['swingPoints']['get'](_0x5c8589),_0x4c1653=this['liquidityZones']['get'](_0x5c8589),_0x5c08ca=this[_0x491e8d(0xb0)]['zone']['clusterToleranceTicks']*this['tickSize'],_0x2c67ea=this['config'][_0x491e8d(0x119)]['maxZoneAgeBars'];for(let _0xbf8bdb=_0x4c1653[_0x491e8d(0xf4)]-0x1;_0xbf8bdb>=0x0;_0xbf8bdb--){_0x4b876e-_0x4c1653[_0xbf8bdb]['barIndex']>_0x2c67ea&&_0x4c1653['splice'](_0xbf8bdb,0x1);}for(const _0x129708 of _0x46d749){let _0x12ce3b=null;for(const _0x1a7d4f of _0x4c1653){if(_0x1a7d4f['containsPrice'](_0x129708[_0x491e8d(0x10d)],this['config'][_0x491e8d(0x119)][_0x491e8d(0x129)],this[_0x491e8d(0xe1)])){_0x12ce3b=_0x1a7d4f;break;}}if(_0x12ce3b){_0x12ce3b['touches']++;if(_0x129708[_0x491e8d(0x10d)]>_0x12ce3b['priceHigh'])_0x12ce3b['priceHigh']=_0x129708[_0x491e8d(0x10d)];if(_0x129708[_0x491e8d(0x10d)]<_0x12ce3b['priceLow'])_0x12ce3b['priceLow']=_0x129708['price'];_0x12ce3b[_0x491e8d(0x101)]=Math['min'](0x1,0.3+_0x12ce3b[_0x491e8d(0x8d)]*0.15);}else{const _0x303546=_0x129708[_0x491e8d(0xf3)]==='high'?ZoneType['RESISTANCE']:ZoneType[_0x491e8d(0xb4)],_0x52abc1=new LiquidityZone(_0x303546,_0x129708['price']+_0x5c08ca/0x2,_0x129708['price']-_0x5c08ca/0x2,_0x129708[_0x491e8d(0x12b)],_0x129708['barIndex']);_0x52abc1[_0x491e8d(0x101)]=0.3+_0x129708['strength']*0.1,_0x4c1653['push'](_0x52abc1);}}}[_0x27887c(0x94)](_0x125b04,_0x35b9e3,_0xa84ee){const _0x2e1d2c=_0x27887c,_0x110c29=this[_0x2e1d2c(0x11a)]['get'](_0x125b04),_0x5505a0=_0x35b9e3[_0xa84ee],_0x229b67=_0x5505a0[_0x2e1d2c(0xde)],_0x151177=this[_0x2e1d2c(0xb0)]['sweep'],_0x17811b=this[_0x2e1d2c(0xb0)][_0x2e1d2c(0x119)];for(const _0x5472e9 of _0x110c29){if(_0x5472e9['lastUsedBarIndex']>=0x0&&_0xa84ee-_0x5472e9[_0x2e1d2c(0x96)]<_0x17811b['cooldownBars'])continue;const _0xfc11ab=_0x5472e9['getLevel'](),_0x3cc64d=Math[_0x2e1d2c(0xab)](_0x229b67-_0xfc11ab)/this['tickSize'];if(_0x3cc64d>_0x17811b['maxZoneDistanceTicks'])continue;const _0x433e31=Math[_0x2e1d2c(0xd4)](0x0,_0xa84ee-_0x151177['maxDurationBars']*0x2);for(let _0x47444b=_0x433e31;_0x47444b<_0xa84ee;_0x47444b++){const _0x1b7170=_0x35b9e3[_0x47444b];if(_0x5472e9[_0x2e1d2c(0xf3)]===ZoneType[_0x2e1d2c(0x131)]){const _0x1f156c=(_0x1b7170['high']-_0x5472e9[_0x2e1d2c(0x12a)])/this['tickSize'];if(_0x1f156c>=_0x151177['minPenetrationTicks']&&_0x1f156c<=_0x151177['maxPenetrationTicks']){const _0x313bf0=Math['min'](_0x5472e9['priceHigh'],_0x5472e9['priceHigh']+_0x1f156c*this[_0x2e1d2c(0xe1)]);if(_0x229b67<_0x313bf0+this['tickSize']*0x2){const _0x1e6f34=_0x1b7170[_0x2e1d2c(0xb9)]-_0x1b7170[_0x2e1d2c(0x91)],_0xbeaa9c=Math['abs'](_0x1b7170['close']-_0x1b7170[_0x2e1d2c(0xa6)]),_0x495064=_0x1e6f34>0x0?_0xbeaa9c/_0x1e6f34:0x0;if(_0x495064>=_0x151177['minBodyRatio']){const _0x5b0821=this['_getVolumeRatio'](_0x35b9e3,_0x47444b,0x14);if(_0x5b0821>=_0x151177['minVolumeRatio']){const _0x71b24a=new SweepEvent(SweepType[_0x2e1d2c(0x9f)],_0x5472e9,_0x47444b,_0x47444b,_0x1b7170['high']);_0x71b24a['exitBarIndex']=_0xa84ee,_0x71b24a[_0x2e1d2c(0x104)]=_0x1f156c,_0x71b24a['durationBars']=_0xa84ee-_0x47444b,_0x71b24a['volumeRatio']=_0x5b0821,_0x71b24a[_0x2e1d2c(0x101)]=this['_scoreSweep'](_0x71b24a,_0x495064),_0x71b24a['isValid']=_0x71b24a['qualityScore']>=_0x151177[_0x2e1d2c(0x11f)];if(_0x71b24a[_0x2e1d2c(0xd2)])return _0x71b24a;}}}}}if(_0x5472e9['type']===ZoneType[_0x2e1d2c(0xb4)]){const _0x31da05=(_0x5472e9['priceLow']-_0x1b7170['low'])/this[_0x2e1d2c(0xe1)];if(_0x31da05>=_0x151177['minPenetrationTicks']&&_0x31da05<=_0x151177[_0x2e1d2c(0xf2)]){const _0x1b1400=Math['max'](_0x5472e9['priceLow'],_0x5472e9['priceLow']-_0x31da05*this['tickSize']);if(_0x229b67>_0x1b1400-this[_0x2e1d2c(0xe1)]*0x2){const _0x5b786d=_0x1b7170['high']-_0x1b7170['low'],_0xcd0082=Math[_0x2e1d2c(0xab)](_0x1b7170['close']-_0x1b7170[_0x2e1d2c(0xa6)]),_0x33003e=_0x5b786d>0x0?_0xcd0082/_0x5b786d:0x0;if(_0x33003e>=_0x151177[_0x2e1d2c(0xba)]){const _0x39ad41=this[_0x2e1d2c(0xc4)](_0x35b9e3,_0x47444b,0x14);if(_0x39ad41>=_0x151177['minVolumeRatio']){const _0x16fae7=new SweepEvent(SweepType['LOW_SWEEP'],_0x5472e9,_0x47444b,_0x47444b,_0x1b7170[_0x2e1d2c(0x91)]);_0x16fae7['exitBarIndex']=_0xa84ee,_0x16fae7[_0x2e1d2c(0x104)]=_0x31da05,_0x16fae7['durationBars']=_0xa84ee-_0x47444b,_0x16fae7[_0x2e1d2c(0xcd)]=_0x39ad41,_0x16fae7[_0x2e1d2c(0x101)]=this['_scoreSweep'](_0x16fae7,_0x33003e),_0x16fae7[_0x2e1d2c(0xd2)]=_0x16fae7['qualityScore']>=_0x151177[_0x2e1d2c(0x11f)];if(_0x16fae7[_0x2e1d2c(0xd2)])return _0x16fae7;}}}}}}}return null;}['_getVolumeRatio'](_0x5dc92f,_0xa23d17,_0x364cf7){const _0x572e9e=_0x27887c,_0x193fae=Math['max'](0x0,_0xa23d17-_0x364cf7),_0x3995ba=_0x5dc92f[_0x572e9e(0x95)](_0x193fae,_0xa23d17);if(_0x3995ba[_0x572e9e(0xf4)]===0x0)return 0x1;const _0x43a488=_0x3995ba[_0x572e9e(0x112)](_0x2f6c9c=>_0x2f6c9c[_0x572e9e(0xee)])[_0x572e9e(0xf0)]((_0x25bf41,_0x449574)=>_0x25bf41-_0x449574),_0x123100=Math['floor'](_0x43a488[_0x572e9e(0xf4)]/0x2),_0x4db1d4=_0x43a488[_0x123100]||0x1;return _0x5dc92f[_0xa23d17][_0x572e9e(0xee)]/_0x4db1d4;}['_scoreSweep'](_0xf08f8b,_0x458814){const _0x3cea1b=_0x27887c;let _0x4a2524=0x0;const _0x1fd2ac=0x4,_0x2087e2=Math['abs'](_0xf08f8b[_0x3cea1b(0x104)]-_0x1fd2ac);return _0x4a2524+=Math['max'](0x0,0.3-_0x2087e2*0.03),_0x4a2524+=Math[_0x3cea1b(0xd4)](0x0,0.25-_0xf08f8b[_0x3cea1b(0x114)]*0.05),_0x4a2524+=Math['min'](0.25,_0xf08f8b['volumeRatio']*0.1),_0x4a2524+=Math['min'](0.2,_0x458814*0.4),Math['min'](0x1,_0x4a2524);}[_0x27887c(0x110)](_0x2bed02,_0x50662b,_0x21ec97,_0x58fb7f){const _0x357fd4=_0x27887c,_0x13e5a6=Date[_0x357fd4(0x11c)]()-this['lastSignalTime'];if(_0x13e5a6<this['config']['execution']['cooldownMs'])return this['emit'](_0x357fd4(0xaf),{'type':_0x357fd4(0x9b),'message':_0x357fd4(0xd6)+(_0x13e5a6/0x3e8)['toFixed'](0x1)+_0x357fd4(0xa8)+this[_0x357fd4(0xb0)][_0x357fd4(0x10f)][_0x357fd4(0xf5)]/0x3e8+'s)'}),null;const _0x28c107=this['config'][_0x357fd4(0x10f)],_0x12f271=_0x50662b[_0x357fd4(0xde)],_0x76865f=_0x58fb7f['sweepType']===SweepType[_0x357fd4(0x9f)]?_0x357fd4(0x120):_0x357fd4(0xe3);let _0x2369d6,_0x194ca1,_0x10dc37,_0x24c42e;_0x76865f===_0x357fd4(0xe3)?(_0x2369d6=_0x12f271-_0x28c107[_0x357fd4(0xce)]*this[_0x357fd4(0xe1)],_0x194ca1=_0x12f271+_0x28c107['targetTicks']*this[_0x357fd4(0xe1)],_0x10dc37=_0x12f271+_0x28c107[_0x357fd4(0x111)]*this[_0x357fd4(0xe1)],_0x24c42e=_0x12f271+_0x28c107['trailTriggerTicks']*this['tickSize']):(_0x2369d6=_0x12f271+_0x28c107['stopTicks']*this['tickSize'],_0x194ca1=_0x12f271-_0x28c107[_0x357fd4(0x108)]*this['tickSize'],_0x10dc37=_0x12f271-_0x28c107[_0x357fd4(0x111)]*this[_0x357fd4(0xe1)],_0x24c42e=_0x12f271-_0x28c107[_0x357fd4(0xa2)]*this['tickSize']);const _0x441b18=_0x28c107[_0x357fd4(0x108)]/_0x28c107['stopTicks'],_0x8406c7=Math[_0x357fd4(0x130)](0x1,_0x58fb7f[_0x357fd4(0x101)]*0.5+_0x58fb7f[_0x357fd4(0x119)]['qualityScore']*0.3+(_0x58fb7f[_0x357fd4(0xcd)]>1.5?0.2:_0x58fb7f[_0x357fd4(0xcd)]*0.1));let _0x53410d=SignalStrength[_0x357fd4(0xbc)];if(_0x8406c7>=0.8)_0x53410d=SignalStrength[_0x357fd4(0xed)];else{if(_0x8406c7>=0.65)_0x53410d=SignalStrength[_0x357fd4(0x8e)];else{if(_0x8406c7<0.5)_0x53410d=SignalStrength[_0x357fd4(0xc9)];}}const _0x12be08=0.5+(_0x8406c7-0.5)*0.4,_0x41f2f6=_0x12be08*Math[_0x357fd4(0xab)](_0x194ca1-_0x12f271)-(0x1-_0x12be08)*Math['abs'](_0x12f271-_0x2369d6);_0x58fb7f['zone'][_0x357fd4(0x96)]=_0x21ec97,_0x58fb7f[_0x357fd4(0x119)][_0x357fd4(0xe2)]=!![],_0x58fb7f[_0x357fd4(0x119)][_0x357fd4(0x10a)]=new Date(_0x50662b['timestamp']),this['lastSignalTime']=Date['now'](),this['stats']['signals']++;const _0x1d26cb={'id':uuidv4(),'timestamp':Date['now'](),'symbol':_0x2bed02[_0x357fd4(0x106)]('.')[0x0]||_0x2bed02,'contractId':_0x2bed02,'side':_0x76865f===_0x357fd4(0xe3)?OrderSide['BID']:OrderSide['ASK'],'direction':_0x76865f,'strategy':'HQX_2B_LIQUIDITY_SWEEP','strength':_0x53410d,'edge':_0x41f2f6,'confidence':_0x8406c7,'entry':_0x12f271,'entryPrice':_0x12f271,'stopLoss':_0x2369d6,'takeProfit':_0x194ca1,'riskReward':_0x441b18,'stopTicks':_0x28c107[_0x357fd4(0xce)],'targetTicks':_0x28c107['targetTicks'],'breakevenTicks':_0x28c107['breakevenTicks'],'trailTriggerTicks':_0x28c107['trailTriggerTicks'],'trailDistanceTicks':_0x28c107[_0x357fd4(0xaa)],'beLevel':_0x10dc37,'trailTrigger':_0x24c42e,'sweepType':_0x58fb7f[_0x357fd4(0xdb)],'penetrationTicks':_0x58fb7f['penetrationTicks'],'sweepDurationBars':_0x58fb7f[_0x357fd4(0x114)],'sweepQuality':_0x58fb7f[_0x357fd4(0x101)],'volumeRatio':_0x58fb7f[_0x357fd4(0xcd)],'zoneType':_0x58fb7f['zone'][_0x357fd4(0xf3)],'zoneLevel':_0x58fb7f['zone'][_0x357fd4(0xe5)](),'zoneTouches':_0x58fb7f['zone'][_0x357fd4(0x8d)],'zoneQuality':_0x58fb7f[_0x357fd4(0x119)]['qualityScore'],'expires':Date['now']()+0xea60};return this['emit']('signal',{'side':_0x76865f==='long'?_0x357fd4(0xa4):_0x357fd4(0x115),'action':'open','reason':'2B\x20'+_0x58fb7f['sweepType']+'\x20|\x20Pen:'+_0x58fb7f['penetrationTicks'][_0x357fd4(0x97)](0x1)+_0x357fd4(0xf1)+_0x58fb7f[_0x357fd4(0xcd)]['toFixed'](0x1)+'x\x20|\x20Q:'+(_0x58fb7f[_0x357fd4(0x101)]*0x64)['toFixed'](0x0)+'%',..._0x1d26cb}),this[_0x357fd4(0xa9)]('log',{'type':_0x357fd4(0xcf),'message':_0x357fd4(0x125)+_0x76865f[_0x357fd4(0xe8)]()+_0x357fd4(0xd5)+_0x12f271['toFixed'](0x2)+'\x20|\x20'+_0x58fb7f[_0x357fd4(0xdb)]+'\x20|\x20Pen:'+_0x58fb7f['penetrationTicks']['toFixed'](0x1)+'t\x20Vol:'+_0x58fb7f['volumeRatio']['toFixed'](0x1)+'x\x20|\x20Conf:'+(_0x8406c7*0x64)['toFixed'](0x0)+'%'}),_0x1d26cb;}[_0x27887c(0x11e)](_0xbd28aa,_0x43e014){const _0x4b891f=_0x27887c,_0x429b51=this['barHistory']['get'](_0xbd28aa)||[],_0x73cc52=this[_0x4b891f(0x11a)][_0x4b891f(0x123)](_0xbd28aa)||[],_0xd8d658=this[_0x4b891f(0x107)]['get'](_0xbd28aa)||[];if(_0x429b51['length']<0x3)return{'ready':![],'message':'Collecting\x20data...\x20'+_0x429b51['length']+_0x4b891f(0x102)};const _0x4ab000=_0x73cc52[_0x4b891f(0x112)](_0x325c3c=>({'zone':_0x325c3c,'distance':Math[_0x4b891f(0xab)](_0x43e014-_0x325c3c['getLevel']())}))['sort']((_0x337385,_0x263687)=>_0x337385['distance']-_0x263687[_0x4b891f(0x122)]),_0x4694ab=_0x4ab000[_0x4b891f(0xec)](_0x57e926=>_0x57e926[_0x4b891f(0x119)][_0x4b891f(0xf3)]===ZoneType[_0x4b891f(0x131)]),_0x5a9dbe=_0x4ab000['find'](_0x145263=>_0x145263['zone']['type']===ZoneType[_0x4b891f(0xb4)]);return{'ready':!![],'barsProcessed':_0x429b51['length'],'swingsDetected':_0xd8d658[_0x4b891f(0xf4)],'activeZones':_0x73cc52['length'],'nearestResistance':_0x4694ab?_0x4694ab['zone'][_0x4b891f(0xe5)]():null,'nearestSupport':_0x5a9dbe?_0x5a9dbe['zone']['getLevel']():null,'stopTicks':this['config'][_0x4b891f(0x10f)][_0x4b891f(0xce)],'targetTicks':this[_0x4b891f(0xb0)]['execution']['targetTicks'],'strategy':_0x4b891f(0xdf)};}['recordTradeResult'](_0x5ef5ef){const _0x1ef522=_0x27887c;this[_0x1ef522(0x10e)][_0x1ef522(0xbf)]({'netPnl':_0x5ef5ef,'timestamp':Date['now']()});if(this['recentTrades']['length']>0x64)this[_0x1ef522(0x10e)]['shift']();_0x5ef5ef>0x0?this['stats'][_0x1ef522(0x9a)]++:this['stats']['losses']++,this[_0x1ef522(0xb2)][_0x1ef522(0xbe)]++,this['stats'][_0x1ef522(0xd8)]+=_0x5ef5ef,this['emit']('log',{'type':'debug','message':_0x1ef522(0xbb)+(_0x5ef5ef>0x0?_0x1ef522(0xb7):_0x1ef522(0xb6))+'\x20$'+_0x5ef5ef[_0x1ef522(0x97)](0x2)});}[_0x27887c(0xae)](_0x4baedd){const _0x3efb2e=_0x27887c;return this['barHistory'][_0x3efb2e(0x123)](_0x4baedd)||[];}['getStats'](){return this['stats'];}[_0x27887c(0x12f)](_0x391695){const _0x5ec6be=_0x27887c,_0xef2216=this[_0x5ec6be(0x132)]['get'](_0x391695)||[],_0x45eb5d=this['liquidityZones'][_0x5ec6be(0x123)](_0x391695)||[],_0xfaacf4=this[_0x5ec6be(0x107)]['get'](_0x391695)||[],_0x1e2b60=this[_0x5ec6be(0xf9)][_0x5ec6be(0x123)](_0x391695),_0x54e049=_0xef2216[_0xef2216[_0x5ec6be(0xf4)]-0x1],_0x411627=_0x54e049?Date[_0x5ec6be(0x11c)]()-_0x54e049[_0x5ec6be(0x12b)]:null,_0x311292=_0x45eb5d['filter'](_0xb1659f=>_0xb1659f['type']===ZoneType[_0x5ec6be(0x131)])['length'],_0xc79214=_0x45eb5d[_0x5ec6be(0xa1)](_0x547d92=>_0x547d92['type']===ZoneType['SUPPORT'])[_0x5ec6be(0xf4)];return{'healthy':_0xef2216[_0x5ec6be(0xf4)]>=0x5,'barsTotal':_0xef2216['length'],'barsLast5Min':_0xef2216[_0x5ec6be(0xa1)](_0x5c7700=>Date['now']()-_0x5c7700[_0x5ec6be(0x12b)]<0x5*0x3c*0x3e8)['length'],'swingsTotal':_0xfaacf4['length'],'zonesResistance':_0x311292,'zonesSupport':_0xc79214,'zonesTotal':_0x45eb5d['length'],'currentBarTicks':_0x1e2b60?_0x1e2b60['tickCount']:0x0,'isAggregating':_0x1e2b60&&_0x1e2b60['tickCount']>0x0,'timeSinceLastBarMs':_0x411627,'lastSignalTime':this['lastSignalTime'],'signalCooldownMs':this[_0x5ec6be(0xb0)]['execution']['cooldownMs'],'uptime':Date[_0x5ec6be(0x11c)]()-(this[_0x5ec6be(0x99)]||Date[_0x5ec6be(0x11c)]())};}['reset'](_0x576d73){const _0x3fd7c6=_0x27887c;this['barHistory'][_0x3fd7c6(0x124)](_0x576d73,[]),this['swingPoints'][_0x3fd7c6(0x124)](_0x576d73,[]),this['liquidityZones'][_0x3fd7c6(0x124)](_0x576d73,[]),this['activeSweeps']['set'](_0x576d73,[]),this[_0x3fd7c6(0xf9)][_0x3fd7c6(0xbd)](_0x576d73),this['emit'](_0x3fd7c6(0xaf),{'type':'info','message':'[HQX-2B]\x20Reset\x20state\x20for\x20'+_0x576d73});}['preloadBars'](_0x52e0ac,_0x22b56e){const _0x2399ba=_0x27887c;if(!_0x22b56e||_0x22b56e[_0x2399ba(0xf4)]===0x0){this['emit']('log',{'type':'debug','message':'[HQX-2B]\x20No\x20historical\x20bars\x20to\x20preload'});return;}!this[_0x2399ba(0x132)][_0x2399ba(0xb3)](_0x52e0ac)&&this[_0x2399ba(0x100)](_0x52e0ac);const _0x417a2f=[..._0x22b56e]['sort']((_0x24a18c,_0x379497)=>_0x24a18c[_0x2399ba(0x12b)]-_0x379497[_0x2399ba(0x12b)]);this['emit'](_0x2399ba(0xaf),{'type':'info','message':_0x2399ba(0xc1)+_0x417a2f['length']+'\x20historical\x20bars...'});let _0x5decb5=0x0;for(const _0x1be23a of _0x417a2f){const _0x3f343f=this[_0x2399ba(0xc8)](_0x52e0ac,_0x1be23a);if(_0x3f343f)_0x5decb5++;}const _0x4b60bd=this['barHistory']['get'](_0x52e0ac)||[],_0x260b10=this[_0x2399ba(0x107)][_0x2399ba(0x123)](_0x52e0ac)||[],_0x3d59f5=this[_0x2399ba(0x11a)][_0x2399ba(0x123)](_0x52e0ac)||[];this[_0x2399ba(0xa9)]('log',{'type':_0x2399ba(0xcf),'message':'[HQX-2B]\x20Preload\x20complete:\x20'+_0x4b60bd['length']+_0x2399ba(0xc6)+_0x260b10['length']+_0x2399ba(0xfe)+_0x3d59f5[_0x2399ba(0xf4)]+'\x20zones'}),_0x5decb5>0x0&&this['emit']('log',{'type':_0x2399ba(0x9b),'message':'[HQX-2B]\x20'+_0x5decb5+_0x2399ba(0xc3)}),this[_0x2399ba(0x11b)]=0x0;}['createSyntheticZones'](_0x5e51de,_0x2d726a,_0x21e875=0x14){const _0x242dfc=_0x27887c;!this[_0x242dfc(0x11a)]['has'](_0x5e51de)&&this[_0x242dfc(0x100)](_0x5e51de);const _0x57ace3=this['liquidityZones'][_0x242dfc(0x123)](_0x5e51de),_0x1f78dc=this[_0x242dfc(0xe1)],_0x46c2a3=_0x21e875*_0x1f78dc,_0x59259e=_0x2d726a+_0x46c2a3,_0x551737=new LiquidityZone(ZoneType[_0x242dfc(0x131)],_0x59259e,this['barHistory'][_0x242dfc(0x123)](_0x5e51de)?.[_0x242dfc(0xf4)]||0x0,Date[_0x242dfc(0x11c)]());_0x551737[_0x242dfc(0x9c)](_0x59259e,this[_0x242dfc(0x132)]['get'](_0x5e51de)?.[_0x242dfc(0xf4)]||0x0),_0x57ace3[_0x242dfc(0xbf)](_0x551737);const _0x5e6e85=_0x2d726a-_0x46c2a3,_0x473336=new LiquidityZone(ZoneType['SUPPORT'],_0x5e6e85,this[_0x242dfc(0x132)][_0x242dfc(0x123)](_0x5e51de)?.['length']||0x0,Date[_0x242dfc(0x11c)]());return _0x473336['addTouch'](_0x5e6e85,this['barHistory'][_0x242dfc(0x123)](_0x5e51de)?.[_0x242dfc(0xf4)]||0x0),_0x57ace3[_0x242dfc(0xbf)](_0x473336),this['emit'](_0x242dfc(0xaf),{'type':_0x242dfc(0xcf),'message':'[HQX-2B]\x20Synthetic\x20zones:\x20R\x20@\x20'+_0x59259e['toFixed'](0x2)+'\x20|\x20S\x20@\x20'+_0x5e6e85['toFixed'](0x2)}),{'resistance':_0x59259e,'support':_0x5e6e85};}}class HQX2BStrategy extends EventEmitter{constructor(_0x4039e8={}){const _0x35bf81=_0x27887c;super(),this['config']=_0x4039e8,this['strategy']=new HQX2BLiquiditySweep(_0x4039e8),this[_0x35bf81(0xe7)]['on'](_0x35bf81(0x127),_0x591e47=>this[_0x35bf81(0xa9)]('signal',_0x591e47)),this['strategy']['on'](_0x35bf81(0xaf),_0x20b1b0=>this['emit']('log',_0x20b1b0));}['processTick'](_0x4e2eee){const _0x4f23ba=_0x27887c;return this[_0x4f23ba(0xe7)]['processTick'](_0x4e2eee);}[_0x27887c(0x126)](_0x256012){const _0x3785ba=_0x27887c;return this[_0x3785ba(0xe7)]['onTick'](_0x256012);}['onTrade'](_0x36eb70){const _0x368669=_0x27887c;return this[_0x368669(0xe7)]['onTrade'](_0x36eb70);}['processBar'](_0x4a4faa,_0x3b6b02){const _0x5d3972=_0x27887c;return this['strategy'][_0x5d3972(0xc8)](_0x4a4faa,_0x3b6b02);}[_0x27887c(0x100)](_0x23ac59,_0x39dc52,_0x3c584d){const _0x5c5d0c=_0x27887c;return this[_0x5c5d0c(0xe7)]['initialize'](_0x23ac59,_0x39dc52,_0x3c584d);}['getAnalysisState'](_0xa0cbdc,_0x5e5547){return this['strategy']['getAnalysisState'](_0xa0cbdc,_0x5e5547);}['recordTradeResult'](_0x1f4254){const _0x3a4189=_0x27887c;return this[_0x3a4189(0xe7)][_0x3a4189(0xdd)](_0x1f4254);}[_0x27887c(0x90)](_0xf75bd3){const _0x37e94d=_0x27887c;return this['strategy'][_0x37e94d(0x90)](_0xf75bd3);}[_0x27887c(0xac)](){const _0x292324=_0x27887c;return this[_0x292324(0xe7)]['getStats']();}['getBarHistory'](_0x683ba0){return this['strategy']['getBarHistory'](_0x683ba0);}['preloadBars'](_0x2d1067,_0x2a89d4){const _0x423cf9=_0x27887c;return this[_0x423cf9(0xe7)]['preloadBars'](_0x2d1067,_0x2a89d4);}[_0x27887c(0x12f)](_0x4e7665){const _0x221df8=_0x27887c;return this[_0x221df8(0xe7)]['getHealthStatus'](_0x4e7665);}['generateSignal'](_0x2c30f8){return null;}}function _0x20c7(){const _0x17f9f4=['processBar','WEAK','\x20|\x20Total\x20swings:\x20','\x20closed\x20@\x20','getTimezoneOffset','volumeRatio','stopTicks','info','\x20|\x20O:','entryBarIndex','isValid','barIndex','max','\x20@\x20','[2B]\x20Signal\x20blocked\x20by\x20cooldown\x20(','extremePrice','pnl','\x20|\x20Strength:\x20','minStrength','sweepType','40FhmyGQ','recordTradeResult','close','HQX-2B\x20Liquidity\x20Sweep\x20(Optimized)','[SWING]\x20NEW\x20HIGH\x20@\x20','tickSize','swept','long','\x20|\x20Total:\x20','getLevel','uuid','strategy','toUpperCase','3086764doIOOt','lookbackBars','430075KZBiXa','find','VERY_STRONG','volume','396254FnLiYL','sort','t\x20|\x20Vol:','maxPenetrationTicks','type','length','cooldownMs','[2B]\x20Calling\x20_generateSignal\x20for\x20','[2B]\x20NEW\x20SWING\x20','[HQX-2B]\x20Params:\x20Stop=','currentBar','9HbTvBY','[SWING]\x20Checking\x20pivot\x20at\x20bar\x20','session','onTrade','\x20swings,\x20','exports','initialize','qualityScore','/3\x20bars','[BAR]\x20#','penetrationTicks','6900912rcWGyt','split','swingPoints','targetTicks','[2B]\x20NEW\x20ZONE\x20','sweptAt','exitBarIndex','4242BumatB','price','recentTrades','execution','_generateSignal','breakevenTicks','map','tickValue','durationBars','sell','\x20|\x20Pen:\x20','5925AvUXWR','isDST','zone','liquidityZones','lastSignalTime','now','strength','getAnalysisState','minQualityScore','short','t\x20|\x20Q:\x20','distance','get','set','[HQX-2B]\x20SIGNAL:\x20','onTick','signal','2376143oBUWNX','clusterToleranceTicks','priceHigh','timestamp','symbol',',\x20value=',':\x20tick=','getHealthStatus','min','RESISTANCE','barHistory','touches','STRONG','_tickCount','reset','low','activeSweeps','t,\x20Trail=','_detectSweep','slice','lastUsedBarIndex','toFixed','8008968cNLIvc','startTime','wins','debug','addTouch','getFullYear','swing','HIGH_SWEEP','t,\x20BE=','filter','trailTriggerTicks','getUTCMinutes','buy','9gKetra','open','object','s\x20<\x20','emit','trailDistanceTicks','abs','getStats','resistance','getBarHistory','log','config','containsPrice','stats','has','SUPPORT','\x20|\x20Bar\x20','LOSS','WIN','priceLow','high','minBodyRatio','[HQX-2B]\x20Trade\x20result:\x20','MODERATE','delete','trades','push','_detectSwings','[HQX-2B]\x20Preloading\x20','barIntervalMs','\x20historical\x20signals\x20detected\x20(ignored)','_getVolumeRatio','enabled','\x20bars,\x20','isWithinSession'];_0x20c7=function(){return _0x17f9f4;};return _0x20c7();}module[_0x27887c(0xff)]={'HQX2BLiquiditySweep':HQX2BLiquiditySweep,'HQX2BStrategy':HQX2BStrategy,'M2':HQX2BStrategy,'S2':HQX2BLiquiditySweep,'OrderSide':OrderSide,'SignalStrength':SignalStrength,'SweepType':SweepType,'ZoneType':ZoneType,'DEFAULT_CONFIG':DEFAULT_CONFIG};
|
|
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
|
+
// hqx-2b/config.js
|
|
7
|
+
var require_config = __commonJS({
|
|
8
|
+
"hqx-2b/config.js"(exports2, module2) {
|
|
9
|
+
var SweepType2 = { HIGH_SWEEP: "high", LOW_SWEEP: "low" };
|
|
10
|
+
var ZoneType2 = { RESISTANCE: "resistance", SUPPORT: "support" };
|
|
11
|
+
var DEFAULT_CONFIG2 = {
|
|
12
|
+
// Instrument
|
|
13
|
+
tickSize: 0.25,
|
|
14
|
+
tickValue: 5,
|
|
15
|
+
// Swing Detection (BACKTEST VALIDATED)
|
|
16
|
+
swing: {
|
|
17
|
+
lookbackBars: 3,
|
|
18
|
+
// 3 bars each side for swing confirmation
|
|
19
|
+
minStrength: 2,
|
|
20
|
+
// Minimum strength required
|
|
21
|
+
confirmationBars: 1
|
|
22
|
+
// 1 bar confirmation
|
|
23
|
+
},
|
|
24
|
+
// Zone Detection (BACKTEST VALIDATED)
|
|
25
|
+
zone: {
|
|
26
|
+
clusterToleranceTicks: 4,
|
|
27
|
+
// 4 ticks tolerance for clustering
|
|
28
|
+
minTouches: 2,
|
|
29
|
+
// Minimum 2 touches for valid zone
|
|
30
|
+
maxZoneAgeBars: 200,
|
|
31
|
+
// Zone valid for 200 bars
|
|
32
|
+
maxZoneDistanceTicks: 40,
|
|
33
|
+
// Max distance to consider zone
|
|
34
|
+
cooldownBars: 10
|
|
35
|
+
// 10 bars cooldown after zone used
|
|
36
|
+
},
|
|
37
|
+
// Sweep Detection (BACKTEST VALIDATED)
|
|
38
|
+
sweep: {
|
|
39
|
+
minPenetrationTicks: 0.5,
|
|
40
|
+
// Minimum 0.5 tick penetration
|
|
41
|
+
maxPenetrationTicks: 8,
|
|
42
|
+
// Maximum 8 ticks penetration
|
|
43
|
+
maxDurationBars: 5,
|
|
44
|
+
// Sweep must complete within 5 bars
|
|
45
|
+
minQualityScore: 0.25,
|
|
46
|
+
// Minimum quality score 25%
|
|
47
|
+
minVolumeRatio: 0.5,
|
|
48
|
+
// Volume must be 50% of average
|
|
49
|
+
minBodyRatio: 0.1
|
|
50
|
+
// Candle body must be 10% of range
|
|
51
|
+
},
|
|
52
|
+
// Execution (BACKTEST VALIDATED - 4:1 R:R)
|
|
53
|
+
execution: {
|
|
54
|
+
stopTicks: 10,
|
|
55
|
+
// $50 stop
|
|
56
|
+
targetTicks: 40,
|
|
57
|
+
// $200 target (4:1 R:R)
|
|
58
|
+
breakevenTicks: 4,
|
|
59
|
+
// Move to BE at +4 ticks
|
|
60
|
+
trailTriggerTicks: 8,
|
|
61
|
+
// Activate trailing at +8 ticks
|
|
62
|
+
trailDistanceTicks: 4,
|
|
63
|
+
// Trail by 4 ticks
|
|
64
|
+
cooldownMs: 15e3,
|
|
65
|
+
// 15 seconds between signals
|
|
66
|
+
minHoldTimeMs: 1e4,
|
|
67
|
+
// 10 seconds min hold
|
|
68
|
+
slippageTicks: 1,
|
|
69
|
+
commissionPerSide: 2
|
|
70
|
+
// $4 round-trip
|
|
71
|
+
},
|
|
72
|
+
// Session filter (DISABLED - 24/7 trading)
|
|
73
|
+
session: {
|
|
74
|
+
enabled: false,
|
|
75
|
+
// Trade 24/7
|
|
76
|
+
startHour: 9,
|
|
77
|
+
// 9:30 AM EST (ignored when disabled)
|
|
78
|
+
startMinute: 30,
|
|
79
|
+
endHour: 16,
|
|
80
|
+
// 4:00 PM EST (ignored when disabled)
|
|
81
|
+
endMinute: 0,
|
|
82
|
+
timezone: "America/New_York"
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
module2.exports = { DEFAULT_CONFIG: DEFAULT_CONFIG2, SweepType: SweepType2, ZoneType: ZoneType2 };
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// common/types.js
|
|
90
|
+
var require_types = __commonJS({
|
|
91
|
+
"common/types.js"(exports2, module2) {
|
|
92
|
+
var OrderSide2 = { BID: 0, ASK: 1 };
|
|
93
|
+
var SignalStrength2 = { WEAK: 1, MODERATE: 2, STRONG: 3, VERY_STRONG: 4 };
|
|
94
|
+
module2.exports = { OrderSide: OrderSide2, SignalStrength: SignalStrength2 };
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// hqx-2b/signal.js
|
|
99
|
+
var require_signal = __commonJS({
|
|
100
|
+
"hqx-2b/signal.js"(exports2, module2) {
|
|
101
|
+
var { v4: uuidv4 } = require("uuid");
|
|
102
|
+
var { OrderSide: OrderSide2, SignalStrength: SignalStrength2 } = require_types();
|
|
103
|
+
var { SweepType: SweepType2 } = require_config();
|
|
104
|
+
function generateSignal(params) {
|
|
105
|
+
const {
|
|
106
|
+
contractId,
|
|
107
|
+
currentBar,
|
|
108
|
+
currentIndex,
|
|
109
|
+
sweep,
|
|
110
|
+
config,
|
|
111
|
+
tickSize
|
|
112
|
+
} = params;
|
|
113
|
+
const exec = config.execution;
|
|
114
|
+
const currentPrice = currentBar.close;
|
|
115
|
+
const direction = sweep.sweepType === SweepType2.HIGH_SWEEP ? "short" : "long";
|
|
116
|
+
let stopLoss, takeProfit, beLevel, trailTrigger;
|
|
117
|
+
if (direction === "long") {
|
|
118
|
+
stopLoss = currentPrice - exec.stopTicks * tickSize;
|
|
119
|
+
takeProfit = currentPrice + exec.targetTicks * tickSize;
|
|
120
|
+
beLevel = currentPrice + exec.breakevenTicks * tickSize;
|
|
121
|
+
trailTrigger = currentPrice + exec.trailTriggerTicks * tickSize;
|
|
122
|
+
} else {
|
|
123
|
+
stopLoss = currentPrice + exec.stopTicks * tickSize;
|
|
124
|
+
takeProfit = currentPrice - exec.targetTicks * tickSize;
|
|
125
|
+
beLevel = currentPrice - exec.breakevenTicks * tickSize;
|
|
126
|
+
trailTrigger = currentPrice - exec.trailTriggerTicks * tickSize;
|
|
127
|
+
}
|
|
128
|
+
const riskReward = exec.targetTicks / exec.stopTicks;
|
|
129
|
+
const confidence = Math.min(
|
|
130
|
+
1,
|
|
131
|
+
sweep.qualityScore * 0.5 + sweep.zone.qualityScore * 0.3 + (sweep.volumeRatio > 1.5 ? 0.2 : sweep.volumeRatio * 0.1)
|
|
132
|
+
);
|
|
133
|
+
let strength = SignalStrength2.MODERATE;
|
|
134
|
+
if (confidence >= 0.8) strength = SignalStrength2.VERY_STRONG;
|
|
135
|
+
else if (confidence >= 0.65) strength = SignalStrength2.STRONG;
|
|
136
|
+
else if (confidence < 0.5) strength = SignalStrength2.WEAK;
|
|
137
|
+
const winProb = 0.5 + (confidence - 0.5) * 0.4;
|
|
138
|
+
const edge = winProb * Math.abs(takeProfit - currentPrice) - (1 - winProb) * Math.abs(currentPrice - stopLoss);
|
|
139
|
+
sweep.zone.lastUsedBarIndex = currentIndex;
|
|
140
|
+
sweep.zone.swept = true;
|
|
141
|
+
sweep.zone.sweptAt = new Date(currentBar.timestamp);
|
|
142
|
+
return {
|
|
143
|
+
id: uuidv4(),
|
|
144
|
+
timestamp: Date.now(),
|
|
145
|
+
symbol: contractId.split(".")[0] || contractId,
|
|
146
|
+
contractId,
|
|
147
|
+
side: direction === "long" ? OrderSide2.BID : OrderSide2.ASK,
|
|
148
|
+
direction,
|
|
149
|
+
strategy: "HQX_2B_LIQUIDITY_SWEEP",
|
|
150
|
+
strength,
|
|
151
|
+
edge,
|
|
152
|
+
confidence,
|
|
153
|
+
entry: currentPrice,
|
|
154
|
+
entryPrice: currentPrice,
|
|
155
|
+
stopLoss,
|
|
156
|
+
takeProfit,
|
|
157
|
+
riskReward,
|
|
158
|
+
stopTicks: exec.stopTicks,
|
|
159
|
+
targetTicks: exec.targetTicks,
|
|
160
|
+
breakevenTicks: exec.breakevenTicks,
|
|
161
|
+
trailTriggerTicks: exec.trailTriggerTicks,
|
|
162
|
+
trailDistanceTicks: exec.trailDistanceTicks,
|
|
163
|
+
beLevel,
|
|
164
|
+
trailTrigger,
|
|
165
|
+
// Sweep details
|
|
166
|
+
sweepType: sweep.sweepType,
|
|
167
|
+
penetrationTicks: sweep.penetrationTicks,
|
|
168
|
+
sweepDurationBars: sweep.durationBars,
|
|
169
|
+
sweepQuality: sweep.qualityScore,
|
|
170
|
+
volumeRatio: sweep.volumeRatio,
|
|
171
|
+
// Zone details
|
|
172
|
+
zoneType: sweep.zone.type,
|
|
173
|
+
zoneLevel: sweep.zone.getLevel(),
|
|
174
|
+
zoneTouches: sweep.zone.touches,
|
|
175
|
+
zoneQuality: sweep.zone.qualityScore,
|
|
176
|
+
expires: Date.now() + 6e4
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
module2.exports = { generateSignal };
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// hqx-2b/detection/swings.js
|
|
184
|
+
var require_swings = __commonJS({
|
|
185
|
+
"hqx-2b/detection/swings.js"(exports2, module2) {
|
|
186
|
+
var SwingPoint = class {
|
|
187
|
+
constructor(type, price, barIndex, timestamp, strength = 1) {
|
|
188
|
+
this.type = type;
|
|
189
|
+
this.price = price;
|
|
190
|
+
this.barIndex = barIndex;
|
|
191
|
+
this.timestamp = timestamp;
|
|
192
|
+
this.strength = strength;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
function detectSwings(bars, currentIndex, existingSwings, config, maxAge) {
|
|
196
|
+
const { lookbackBars, minStrength } = config;
|
|
197
|
+
const swings = [...existingSwings];
|
|
198
|
+
if (currentIndex < lookbackBars * 2) return swings;
|
|
199
|
+
const pivotIndex = currentIndex - lookbackBars;
|
|
200
|
+
const pivotBar = bars[pivotIndex];
|
|
201
|
+
let isSwingHigh = true;
|
|
202
|
+
let highStrength = 0;
|
|
203
|
+
for (let i = pivotIndex - lookbackBars; i <= pivotIndex + lookbackBars; i++) {
|
|
204
|
+
if (i === pivotIndex || i < 0 || i >= bars.length) continue;
|
|
205
|
+
if (bars[i].high >= pivotBar.high) {
|
|
206
|
+
isSwingHigh = false;
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
highStrength++;
|
|
210
|
+
}
|
|
211
|
+
if (isSwingHigh && highStrength >= minStrength) {
|
|
212
|
+
const existing = swings.find((s) => s.barIndex === pivotIndex && s.type === "high");
|
|
213
|
+
if (!existing) {
|
|
214
|
+
swings.push(new SwingPoint("high", pivotBar.high, pivotIndex, pivotBar.timestamp, highStrength));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
let isSwingLow = true;
|
|
218
|
+
let lowStrength = 0;
|
|
219
|
+
for (let i = pivotIndex - lookbackBars; i <= pivotIndex + lookbackBars; i++) {
|
|
220
|
+
if (i === pivotIndex || i < 0 || i >= bars.length) continue;
|
|
221
|
+
if (bars[i].low <= pivotBar.low) {
|
|
222
|
+
isSwingLow = false;
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
lowStrength++;
|
|
226
|
+
}
|
|
227
|
+
if (isSwingLow && lowStrength >= minStrength) {
|
|
228
|
+
const existing = swings.find((s) => s.barIndex === pivotIndex && s.type === "low");
|
|
229
|
+
if (!existing) {
|
|
230
|
+
swings.push(new SwingPoint("low", pivotBar.low, pivotIndex, pivotBar.timestamp, lowStrength));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
while (swings.length > 0 && swings[0].barIndex < currentIndex - maxAge) {
|
|
234
|
+
swings.shift();
|
|
235
|
+
}
|
|
236
|
+
return swings;
|
|
237
|
+
}
|
|
238
|
+
module2.exports = { SwingPoint, detectSwings };
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// hqx-2b/detection/zones.js
|
|
243
|
+
var require_zones = __commonJS({
|
|
244
|
+
"hqx-2b/detection/zones.js"(exports2, module2) {
|
|
245
|
+
var { v4: uuidv4 } = require("uuid");
|
|
246
|
+
var { ZoneType: ZoneType2 } = require_config();
|
|
247
|
+
var LiquidityZone = class {
|
|
248
|
+
constructor(type, priceHigh, priceLow, createdAt, barIndex) {
|
|
249
|
+
this.id = uuidv4();
|
|
250
|
+
this.type = type;
|
|
251
|
+
this.priceHigh = priceHigh;
|
|
252
|
+
this.priceLow = priceLow;
|
|
253
|
+
this.createdAt = createdAt;
|
|
254
|
+
this.barIndex = barIndex;
|
|
255
|
+
this.touches = 1;
|
|
256
|
+
this.swept = false;
|
|
257
|
+
this.sweptAt = null;
|
|
258
|
+
this.lastUsedBarIndex = -999;
|
|
259
|
+
this.qualityScore = 0.5;
|
|
260
|
+
}
|
|
261
|
+
containsPrice(price, toleranceTicks, tickSize) {
|
|
262
|
+
const tolerance = toleranceTicks * tickSize;
|
|
263
|
+
return price >= this.priceLow - tolerance && price <= this.priceHigh + tolerance;
|
|
264
|
+
}
|
|
265
|
+
getLevel() {
|
|
266
|
+
return (this.priceHigh + this.priceLow) / 2;
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
function updateZones(swings, existingZones, currentIndex, config, tickSize) {
|
|
270
|
+
const { clusterToleranceTicks, maxZoneAgeBars } = config;
|
|
271
|
+
const zones = [...existingZones];
|
|
272
|
+
const tolerance = clusterToleranceTicks * tickSize;
|
|
273
|
+
for (let i = zones.length - 1; i >= 0; i--) {
|
|
274
|
+
if (currentIndex - zones[i].barIndex > maxZoneAgeBars) {
|
|
275
|
+
zones.splice(i, 1);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
for (const swing of swings) {
|
|
279
|
+
let foundZone = null;
|
|
280
|
+
for (const zone of zones) {
|
|
281
|
+
if (zone.containsPrice(swing.price, clusterToleranceTicks, tickSize)) {
|
|
282
|
+
foundZone = zone;
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (foundZone) {
|
|
287
|
+
foundZone.touches++;
|
|
288
|
+
if (swing.price > foundZone.priceHigh) foundZone.priceHigh = swing.price;
|
|
289
|
+
if (swing.price < foundZone.priceLow) foundZone.priceLow = swing.price;
|
|
290
|
+
foundZone.qualityScore = Math.min(1, 0.3 + foundZone.touches * 0.15);
|
|
291
|
+
} else {
|
|
292
|
+
const zoneType = swing.type === "high" ? ZoneType2.RESISTANCE : ZoneType2.SUPPORT;
|
|
293
|
+
const newZone = new LiquidityZone(
|
|
294
|
+
zoneType,
|
|
295
|
+
swing.price + tolerance / 2,
|
|
296
|
+
swing.price - tolerance / 2,
|
|
297
|
+
swing.timestamp,
|
|
298
|
+
swing.barIndex
|
|
299
|
+
);
|
|
300
|
+
newZone.qualityScore = 0.3 + swing.strength * 0.1;
|
|
301
|
+
zones.push(newZone);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return zones;
|
|
305
|
+
}
|
|
306
|
+
module2.exports = { LiquidityZone, updateZones };
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// hqx-2b/detection/sweeps.js
|
|
311
|
+
var require_sweeps = __commonJS({
|
|
312
|
+
"hqx-2b/detection/sweeps.js"(exports2, module2) {
|
|
313
|
+
var { SweepType: SweepType2, ZoneType: ZoneType2 } = require_config();
|
|
314
|
+
var SweepEvent = class {
|
|
315
|
+
constructor(sweepType, zone, entryBarIndex, extremeBarIndex, extremePrice) {
|
|
316
|
+
this.sweepType = sweepType;
|
|
317
|
+
this.zone = zone;
|
|
318
|
+
this.entryBarIndex = entryBarIndex;
|
|
319
|
+
this.extremeBarIndex = extremeBarIndex;
|
|
320
|
+
this.extremePrice = extremePrice;
|
|
321
|
+
this.exitBarIndex = null;
|
|
322
|
+
this.isValid = false;
|
|
323
|
+
this.qualityScore = 0;
|
|
324
|
+
this.penetrationTicks = 0;
|
|
325
|
+
this.durationBars = 0;
|
|
326
|
+
this.volumeRatio = 1;
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
function getVolumeRatio(bars, index, lookback) {
|
|
330
|
+
const start = Math.max(0, index - lookback);
|
|
331
|
+
const recentBars = bars.slice(start, index);
|
|
332
|
+
if (recentBars.length === 0) return 1;
|
|
333
|
+
const volumes = recentBars.map((b) => b.volume).sort((a, b) => a - b);
|
|
334
|
+
const medianIdx = Math.floor(volumes.length / 2);
|
|
335
|
+
const medianVolume = volumes[medianIdx] || 1;
|
|
336
|
+
return bars[index].volume / medianVolume;
|
|
337
|
+
}
|
|
338
|
+
function scoreSweep(sweep, bodyRatio) {
|
|
339
|
+
let score = 0;
|
|
340
|
+
const optimalPen = 4;
|
|
341
|
+
const penDiff = Math.abs(sweep.penetrationTicks - optimalPen);
|
|
342
|
+
score += Math.max(0, 0.3 - penDiff * 0.03);
|
|
343
|
+
score += Math.max(0, 0.25 - sweep.durationBars * 0.05);
|
|
344
|
+
score += Math.min(0.25, sweep.volumeRatio * 0.1);
|
|
345
|
+
score += Math.min(0.2, bodyRatio * 0.4);
|
|
346
|
+
return Math.min(1, score);
|
|
347
|
+
}
|
|
348
|
+
function detectSweep(zones, bars, currentIndex, sweepConfig, zoneConfig, tickSize) {
|
|
349
|
+
const currentBar = bars[currentIndex];
|
|
350
|
+
const currentPrice = currentBar.close;
|
|
351
|
+
const cfg = sweepConfig;
|
|
352
|
+
for (const zone of zones) {
|
|
353
|
+
if (zone.lastUsedBarIndex >= 0 && currentIndex - zone.lastUsedBarIndex < zoneConfig.cooldownBars) {
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
const zoneLevel = zone.getLevel();
|
|
357
|
+
const distanceTicks = Math.abs(currentPrice - zoneLevel) / tickSize;
|
|
358
|
+
if (distanceTicks > zoneConfig.maxZoneDistanceTicks) continue;
|
|
359
|
+
const lookbackStart = Math.max(0, currentIndex - cfg.maxDurationBars * 2);
|
|
360
|
+
for (let i = lookbackStart; i < currentIndex; i++) {
|
|
361
|
+
const bar = bars[i];
|
|
362
|
+
if (zone.type === ZoneType2.RESISTANCE) {
|
|
363
|
+
const penetration = (bar.high - zone.priceHigh) / tickSize;
|
|
364
|
+
if (penetration >= cfg.minPenetrationTicks && penetration <= cfg.maxPenetrationTicks) {
|
|
365
|
+
if (currentPrice < zone.priceHigh) {
|
|
366
|
+
const barRange = bar.high - bar.low;
|
|
367
|
+
const bodySize = Math.abs(bar.close - bar.open);
|
|
368
|
+
const bodyRatio = barRange > 0 ? bodySize / barRange : 0;
|
|
369
|
+
if (bodyRatio >= cfg.minBodyRatio) {
|
|
370
|
+
const volumeRatio = getVolumeRatio(bars, i, 20);
|
|
371
|
+
if (volumeRatio >= cfg.minVolumeRatio) {
|
|
372
|
+
const sweep = new SweepEvent(
|
|
373
|
+
SweepType2.HIGH_SWEEP,
|
|
374
|
+
zone,
|
|
375
|
+
i,
|
|
376
|
+
i,
|
|
377
|
+
bar.high
|
|
378
|
+
);
|
|
379
|
+
sweep.exitBarIndex = currentIndex;
|
|
380
|
+
sweep.penetrationTicks = penetration;
|
|
381
|
+
sweep.durationBars = currentIndex - i;
|
|
382
|
+
sweep.volumeRatio = volumeRatio;
|
|
383
|
+
sweep.qualityScore = scoreSweep(sweep, bodyRatio);
|
|
384
|
+
sweep.isValid = sweep.qualityScore >= cfg.minQualityScore;
|
|
385
|
+
if (sweep.isValid) {
|
|
386
|
+
return sweep;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (zone.type === ZoneType2.SUPPORT) {
|
|
394
|
+
const penetration = (zone.priceLow - bar.low) / tickSize;
|
|
395
|
+
if (penetration >= cfg.minPenetrationTicks && penetration <= cfg.maxPenetrationTicks) {
|
|
396
|
+
if (currentPrice > zone.priceLow) {
|
|
397
|
+
const barRange = bar.high - bar.low;
|
|
398
|
+
const bodySize = Math.abs(bar.close - bar.open);
|
|
399
|
+
const bodyRatio = barRange > 0 ? bodySize / barRange : 0;
|
|
400
|
+
if (bodyRatio >= cfg.minBodyRatio) {
|
|
401
|
+
const volumeRatio = getVolumeRatio(bars, i, 20);
|
|
402
|
+
if (volumeRatio >= cfg.minVolumeRatio) {
|
|
403
|
+
const sweep = new SweepEvent(
|
|
404
|
+
SweepType2.LOW_SWEEP,
|
|
405
|
+
zone,
|
|
406
|
+
i,
|
|
407
|
+
i,
|
|
408
|
+
bar.low
|
|
409
|
+
);
|
|
410
|
+
sweep.exitBarIndex = currentIndex;
|
|
411
|
+
sweep.penetrationTicks = penetration;
|
|
412
|
+
sweep.durationBars = currentIndex - i;
|
|
413
|
+
sweep.volumeRatio = volumeRatio;
|
|
414
|
+
sweep.qualityScore = scoreSweep(sweep, bodyRatio);
|
|
415
|
+
sweep.isValid = sweep.qualityScore >= cfg.minQualityScore;
|
|
416
|
+
if (sweep.isValid) {
|
|
417
|
+
return sweep;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
module2.exports = { SweepEvent, detectSweep, getVolumeRatio, scoreSweep };
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// hqx-2b/detection/index.js
|
|
433
|
+
var require_detection = __commonJS({
|
|
434
|
+
"hqx-2b/detection/index.js"(exports2, module2) {
|
|
435
|
+
var { SwingPoint, detectSwings } = require_swings();
|
|
436
|
+
var { LiquidityZone, updateZones } = require_zones();
|
|
437
|
+
var { SweepEvent, detectSweep } = require_sweeps();
|
|
438
|
+
module2.exports = {
|
|
439
|
+
SwingPoint,
|
|
440
|
+
detectSwings,
|
|
441
|
+
LiquidityZone,
|
|
442
|
+
updateZones,
|
|
443
|
+
SweepEvent,
|
|
444
|
+
detectSweep
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
// hqx-2b/core.js
|
|
450
|
+
var require_core = __commonJS({
|
|
451
|
+
"hqx-2b/core.js"(exports2, module2) {
|
|
452
|
+
var EventEmitter2 = require("events");
|
|
453
|
+
var { DEFAULT_CONFIG: DEFAULT_CONFIG2, ZoneType: ZoneType2 } = require_config();
|
|
454
|
+
var { generateSignal } = require_signal();
|
|
455
|
+
var { detectSwings, updateZones, detectSweep } = require_detection();
|
|
456
|
+
function mergeConfig(defaults, custom) {
|
|
457
|
+
const result = { ...defaults };
|
|
458
|
+
for (const key in custom) {
|
|
459
|
+
if (typeof custom[key] === "object" && !Array.isArray(custom[key]) && custom[key] !== null) {
|
|
460
|
+
result[key] = { ...defaults[key], ...custom[key] };
|
|
461
|
+
} else {
|
|
462
|
+
result[key] = custom[key];
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return result;
|
|
466
|
+
}
|
|
467
|
+
var HQX2BLiquiditySweep2 = class extends EventEmitter2 {
|
|
468
|
+
constructor(config = {}) {
|
|
469
|
+
super();
|
|
470
|
+
this.config = mergeConfig(DEFAULT_CONFIG2, config);
|
|
471
|
+
this.tickSize = this.config.tickSize;
|
|
472
|
+
this.tickValue = this.config.tickValue;
|
|
473
|
+
this.barHistory = /* @__PURE__ */ new Map();
|
|
474
|
+
this.swingPoints = /* @__PURE__ */ new Map();
|
|
475
|
+
this.liquidityZones = /* @__PURE__ */ new Map();
|
|
476
|
+
this.currentBar = /* @__PURE__ */ new Map();
|
|
477
|
+
this.barIntervalMs = 6e4;
|
|
478
|
+
this.lastSignalTime = 0;
|
|
479
|
+
this.startTime = Date.now();
|
|
480
|
+
this.stats = { signals: 0, trades: 0, wins: 0, losses: 0, pnl: 0 };
|
|
481
|
+
this.recentTrades = [];
|
|
482
|
+
}
|
|
483
|
+
initialize(contractId, tickSize = 0.25, tickValue = 5) {
|
|
484
|
+
this.tickSize = tickSize;
|
|
485
|
+
this.tickValue = tickValue;
|
|
486
|
+
this.config.tickSize = tickSize;
|
|
487
|
+
this.config.tickValue = tickValue;
|
|
488
|
+
this.barHistory.set(contractId, []);
|
|
489
|
+
this.swingPoints.set(contractId, []);
|
|
490
|
+
this.liquidityZones.set(contractId, []);
|
|
491
|
+
this.currentBar.delete(contractId);
|
|
492
|
+
this.emit("log", {
|
|
493
|
+
type: "info",
|
|
494
|
+
message: `[HQX-2B] Initialized for ${contractId}: tick=${tickSize}, value=${tickValue}, TF=1min`
|
|
495
|
+
});
|
|
496
|
+
this.emit("log", {
|
|
497
|
+
type: "info",
|
|
498
|
+
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}`
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Check if current time is within trading session (9:30-16:00 EST)
|
|
503
|
+
*/
|
|
504
|
+
isWithinSession(timestamp) {
|
|
505
|
+
if (!this.config.session.enabled) return true;
|
|
506
|
+
const date = new Date(timestamp);
|
|
507
|
+
const estOffset = this.isDST(date) ? -4 : -5;
|
|
508
|
+
const utcHours = date.getUTCHours();
|
|
509
|
+
const utcMinutes = date.getUTCMinutes();
|
|
510
|
+
const estHours = (utcHours + estOffset + 24) % 24;
|
|
511
|
+
const { startHour, startMinute, endHour, endMinute } = this.config.session;
|
|
512
|
+
const currentMins = estHours * 60 + utcMinutes;
|
|
513
|
+
const startMins = startHour * 60 + startMinute;
|
|
514
|
+
const endMins = endHour * 60 + endMinute;
|
|
515
|
+
return currentMins >= startMins && currentMins < endMins;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Check if date is in US Daylight Saving Time
|
|
519
|
+
*/
|
|
520
|
+
isDST(date) {
|
|
521
|
+
const jan = new Date(date.getFullYear(), 0, 1);
|
|
522
|
+
const jul = new Date(date.getFullYear(), 6, 1);
|
|
523
|
+
const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
|
|
524
|
+
return date.getTimezoneOffset() < stdOffset;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Process incoming tick and aggregate into 1-minute bars
|
|
528
|
+
* Only calls processBar() when a bar closes (every 60 seconds)
|
|
529
|
+
*/
|
|
530
|
+
processTick(tick) {
|
|
531
|
+
const { contractId, price, volume, timestamp } = tick;
|
|
532
|
+
const ts = timestamp || Date.now();
|
|
533
|
+
const vol = volume || 1;
|
|
534
|
+
if (!this.isWithinSession(ts)) {
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
let bar = this.currentBar.get(contractId);
|
|
538
|
+
const barStartTime = Math.floor(ts / this.barIntervalMs) * this.barIntervalMs;
|
|
539
|
+
if (!bar || bar.startTime !== barStartTime) {
|
|
540
|
+
if (bar) {
|
|
541
|
+
const closedBar = {
|
|
542
|
+
timestamp: bar.startTime,
|
|
543
|
+
open: bar.open,
|
|
544
|
+
high: bar.high,
|
|
545
|
+
low: bar.low,
|
|
546
|
+
close: bar.close,
|
|
547
|
+
volume: bar.volume
|
|
548
|
+
};
|
|
549
|
+
const signal = this.processBar(contractId, closedBar);
|
|
550
|
+
this.currentBar.set(contractId, {
|
|
551
|
+
startTime: barStartTime,
|
|
552
|
+
open: price,
|
|
553
|
+
high: price,
|
|
554
|
+
low: price,
|
|
555
|
+
close: price,
|
|
556
|
+
volume: vol
|
|
557
|
+
});
|
|
558
|
+
return signal;
|
|
559
|
+
} else {
|
|
560
|
+
this.currentBar.set(contractId, {
|
|
561
|
+
startTime: barStartTime,
|
|
562
|
+
open: price,
|
|
563
|
+
high: price,
|
|
564
|
+
low: price,
|
|
565
|
+
close: price,
|
|
566
|
+
volume: vol
|
|
567
|
+
});
|
|
568
|
+
return null;
|
|
569
|
+
}
|
|
570
|
+
} else {
|
|
571
|
+
bar.high = Math.max(bar.high, price);
|
|
572
|
+
bar.low = Math.min(bar.low, price);
|
|
573
|
+
bar.close = price;
|
|
574
|
+
bar.volume += vol;
|
|
575
|
+
return null;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
onTick(tick) {
|
|
579
|
+
return this.processTick(tick);
|
|
580
|
+
}
|
|
581
|
+
onTrade(trade) {
|
|
582
|
+
return this.processTick({
|
|
583
|
+
contractId: trade.contractId || trade.symbol,
|
|
584
|
+
price: trade.price,
|
|
585
|
+
volume: trade.size || trade.volume || 1,
|
|
586
|
+
timestamp: trade.timestamp || Date.now()
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
processBar(contractId, bar) {
|
|
590
|
+
let bars = this.barHistory.get(contractId);
|
|
591
|
+
if (!bars) {
|
|
592
|
+
this.initialize(contractId);
|
|
593
|
+
bars = this.barHistory.get(contractId);
|
|
594
|
+
}
|
|
595
|
+
bars.push(bar);
|
|
596
|
+
if (bars.length > 500) bars.shift();
|
|
597
|
+
const currentIndex = bars.length - 1;
|
|
598
|
+
if (bars.length < this.config.swing.lookbackBars * 3) {
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
const swings = this.swingPoints.get(contractId);
|
|
602
|
+
const prevSwingCount = swings.length;
|
|
603
|
+
const updatedSwings = detectSwings(
|
|
604
|
+
bars,
|
|
605
|
+
currentIndex,
|
|
606
|
+
swings,
|
|
607
|
+
this.config.swing,
|
|
608
|
+
this.config.zone.maxZoneAgeBars
|
|
609
|
+
);
|
|
610
|
+
this.swingPoints.set(contractId, updatedSwings);
|
|
611
|
+
if (updatedSwings.length > prevSwingCount) {
|
|
612
|
+
const newSwing = updatedSwings[updatedSwings.length - 1];
|
|
613
|
+
this.emit("log", {
|
|
614
|
+
type: "debug",
|
|
615
|
+
message: `[2B] NEW SWING ${newSwing.type.toUpperCase()} @ ${newSwing.price.toFixed(2)} | Total: ${updatedSwings.length}`
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
const zones = this.liquidityZones.get(contractId);
|
|
619
|
+
const prevZoneCount = zones.length;
|
|
620
|
+
const updatedZones = updateZones(
|
|
621
|
+
updatedSwings,
|
|
622
|
+
zones,
|
|
623
|
+
currentIndex,
|
|
624
|
+
this.config.zone,
|
|
625
|
+
this.tickSize
|
|
626
|
+
);
|
|
627
|
+
this.liquidityZones.set(contractId, updatedZones);
|
|
628
|
+
if (updatedZones.length > prevZoneCount) {
|
|
629
|
+
const newZone = updatedZones[updatedZones.length - 1];
|
|
630
|
+
this.emit("log", {
|
|
631
|
+
type: "debug",
|
|
632
|
+
message: `[2B] NEW ZONE ${newZone.type.toUpperCase()} @ ${newZone.getLevel().toFixed(2)} | Total: ${updatedZones.length}`
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
const sweep = detectSweep(
|
|
636
|
+
updatedZones,
|
|
637
|
+
bars,
|
|
638
|
+
currentIndex,
|
|
639
|
+
this.config.sweep,
|
|
640
|
+
this.config.zone,
|
|
641
|
+
this.tickSize
|
|
642
|
+
);
|
|
643
|
+
if (sweep) {
|
|
644
|
+
this.emit("log", {
|
|
645
|
+
type: "debug",
|
|
646
|
+
message: `[2B] SWEEP ${sweep.sweepType} | Valid: ${sweep.isValid} | Pen: ${sweep.penetrationTicks.toFixed(1)}t | Q: ${(sweep.qualityScore * 100).toFixed(0)}%`
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
if (sweep && sweep.isValid) {
|
|
650
|
+
if (Date.now() - this.lastSignalTime < this.config.execution.cooldownMs) {
|
|
651
|
+
this.emit("log", {
|
|
652
|
+
type: "debug",
|
|
653
|
+
message: `[2B] COOLDOWN - waiting ${Math.ceil((this.config.execution.cooldownMs - (Date.now() - this.lastSignalTime)) / 1e3)}s`
|
|
654
|
+
});
|
|
655
|
+
return null;
|
|
656
|
+
}
|
|
657
|
+
this.emit("log", {
|
|
658
|
+
type: "debug",
|
|
659
|
+
message: `[2B] GENERATING SIGNAL from ${sweep.sweepType} sweep...`
|
|
660
|
+
});
|
|
661
|
+
const signal = generateSignal({
|
|
662
|
+
contractId,
|
|
663
|
+
currentBar: bar,
|
|
664
|
+
currentIndex,
|
|
665
|
+
sweep,
|
|
666
|
+
config: this.config,
|
|
667
|
+
tickSize: this.tickSize
|
|
668
|
+
});
|
|
669
|
+
if (signal) {
|
|
670
|
+
this.lastSignalTime = Date.now();
|
|
671
|
+
this.stats.signals++;
|
|
672
|
+
this.emit("signal", {
|
|
673
|
+
side: signal.direction === "long" ? "buy" : "sell",
|
|
674
|
+
action: "open",
|
|
675
|
+
reason: `2B ${sweep.sweepType} | Pen:${sweep.penetrationTicks.toFixed(1)}t | Vol:${sweep.volumeRatio.toFixed(1)}x | Q:${(sweep.qualityScore * 100).toFixed(0)}%`,
|
|
676
|
+
...signal
|
|
677
|
+
});
|
|
678
|
+
this.emit("log", {
|
|
679
|
+
type: "info",
|
|
680
|
+
message: `[HQX-2B] SIGNAL: ${signal.direction.toUpperCase()} @ ${bar.close.toFixed(2)} | ${sweep.sweepType} | Pen:${sweep.penetrationTicks.toFixed(1)}t Vol:${sweep.volumeRatio.toFixed(1)}x | Conf:${(signal.confidence * 100).toFixed(0)}%`
|
|
681
|
+
});
|
|
682
|
+
return signal;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
return null;
|
|
686
|
+
}
|
|
687
|
+
getAnalysisState(contractId, currentPrice) {
|
|
688
|
+
const bars = this.barHistory.get(contractId) || [];
|
|
689
|
+
const zones = this.liquidityZones.get(contractId) || [];
|
|
690
|
+
const swings = this.swingPoints.get(contractId) || [];
|
|
691
|
+
if (bars.length < 5) {
|
|
692
|
+
return { ready: false, message: `Collecting data... ${bars.length}/5 bars` };
|
|
693
|
+
}
|
|
694
|
+
const sortedZones = zones.map((z) => ({ zone: z, distance: Math.abs(currentPrice - z.getLevel()) })).sort((a, b) => a.distance - b.distance);
|
|
695
|
+
const nearestResistance = sortedZones.find((z) => z.zone.type === ZoneType2.RESISTANCE);
|
|
696
|
+
const nearestSupport = sortedZones.find((z) => z.zone.type === ZoneType2.SUPPORT);
|
|
697
|
+
return {
|
|
698
|
+
ready: true,
|
|
699
|
+
barsProcessed: bars.length,
|
|
700
|
+
swingsDetected: swings.length,
|
|
701
|
+
activeZones: zones.length,
|
|
702
|
+
nearestResistance: nearestResistance ? nearestResistance.zone.getLevel() : null,
|
|
703
|
+
nearestSupport: nearestSupport ? nearestSupport.zone.getLevel() : null,
|
|
704
|
+
stopTicks: this.config.execution.stopTicks,
|
|
705
|
+
targetTicks: this.config.execution.targetTicks,
|
|
706
|
+
strategy: "HQX-2B Liquidity Sweep (Optimized)"
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
recordTradeResult(pnl) {
|
|
710
|
+
this.recentTrades.push({ netPnl: pnl, timestamp: Date.now() });
|
|
711
|
+
if (this.recentTrades.length > 100) this.recentTrades.shift();
|
|
712
|
+
if (pnl > 0) {
|
|
713
|
+
this.stats.wins++;
|
|
714
|
+
} else {
|
|
715
|
+
this.stats.losses++;
|
|
716
|
+
}
|
|
717
|
+
this.stats.trades++;
|
|
718
|
+
this.stats.pnl += pnl;
|
|
719
|
+
this.emit("log", {
|
|
720
|
+
type: "debug",
|
|
721
|
+
message: `[HQX-2B] Trade result: ${pnl > 0 ? "WIN" : "LOSS"} $${pnl.toFixed(2)}`
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
getBarHistory(contractId) {
|
|
725
|
+
return this.barHistory.get(contractId) || [];
|
|
726
|
+
}
|
|
727
|
+
getStats() {
|
|
728
|
+
return this.stats;
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Get detailed health status to confirm strategy is working
|
|
732
|
+
* @param {string} contractId - Contract ID
|
|
733
|
+
* @returns {Object} Health status with diagnostic info
|
|
734
|
+
*/
|
|
735
|
+
getHealthStatus(contractId) {
|
|
736
|
+
const bars = this.barHistory.get(contractId) || [];
|
|
737
|
+
const zones = this.liquidityZones.get(contractId) || [];
|
|
738
|
+
const swings = this.swingPoints.get(contractId) || [];
|
|
739
|
+
const currentBar = this.currentBar.get(contractId);
|
|
740
|
+
const lastBar = bars[bars.length - 1];
|
|
741
|
+
const timeSinceLastBar = lastBar ? Date.now() - lastBar.timestamp : null;
|
|
742
|
+
const resistanceZones = zones.filter((z) => z.type === ZoneType2.RESISTANCE).length;
|
|
743
|
+
const supportZones = zones.filter((z) => z.type === ZoneType2.SUPPORT).length;
|
|
744
|
+
const isAggregating = currentBar && currentBar.tickCount > 0;
|
|
745
|
+
return {
|
|
746
|
+
healthy: bars.length >= 5,
|
|
747
|
+
barsTotal: bars.length,
|
|
748
|
+
barsLast5Min: bars.filter((b) => Date.now() - b.timestamp < 5 * 60 * 1e3).length,
|
|
749
|
+
swingsTotal: swings.length,
|
|
750
|
+
zonesResistance: resistanceZones,
|
|
751
|
+
zonesSupport: supportZones,
|
|
752
|
+
zonesTotal: zones.length,
|
|
753
|
+
currentBarTicks: currentBar ? currentBar.tickCount : 0,
|
|
754
|
+
isAggregating,
|
|
755
|
+
timeSinceLastBarMs: timeSinceLastBar,
|
|
756
|
+
lastSignalTime: this.lastSignalTime,
|
|
757
|
+
signalCooldownMs: this.config.execution.cooldownMs,
|
|
758
|
+
uptime: Date.now() - (this.startTime || Date.now())
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
reset(contractId) {
|
|
762
|
+
this.barHistory.set(contractId, []);
|
|
763
|
+
this.swingPoints.set(contractId, []);
|
|
764
|
+
this.liquidityZones.set(contractId, []);
|
|
765
|
+
this.currentBar.delete(contractId);
|
|
766
|
+
this.emit("log", {
|
|
767
|
+
type: "info",
|
|
768
|
+
message: `[HQX-2B] Reset state for ${contractId}`
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Preload historical bars to warm up the strategy
|
|
773
|
+
* @param {string} contractId - Contract ID
|
|
774
|
+
* @param {Array} bars - Array of bars {timestamp, open, high, low, close, volume}
|
|
775
|
+
*/
|
|
776
|
+
preloadBars(contractId, bars) {
|
|
777
|
+
if (!bars || bars.length === 0) {
|
|
778
|
+
this.emit("log", {
|
|
779
|
+
type: "debug",
|
|
780
|
+
message: `[HQX-2B] No historical bars to preload`
|
|
781
|
+
});
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
if (!this.barHistory.has(contractId)) {
|
|
785
|
+
this.initialize(contractId);
|
|
786
|
+
}
|
|
787
|
+
const sortedBars = [...bars].sort((a, b) => a.timestamp - b.timestamp);
|
|
788
|
+
this.emit("log", {
|
|
789
|
+
type: "info",
|
|
790
|
+
message: `[HQX-2B] Preloading ${sortedBars.length} historical bars...`
|
|
791
|
+
});
|
|
792
|
+
let signalCount = 0;
|
|
793
|
+
for (const bar of sortedBars) {
|
|
794
|
+
const signal = this.processBar(contractId, bar);
|
|
795
|
+
if (signal) signalCount++;
|
|
796
|
+
}
|
|
797
|
+
const history = this.barHistory.get(contractId) || [];
|
|
798
|
+
const swings = this.swingPoints.get(contractId) || [];
|
|
799
|
+
const zones = this.liquidityZones.get(contractId) || [];
|
|
800
|
+
this.emit("log", {
|
|
801
|
+
type: "info",
|
|
802
|
+
message: `[HQX-2B] Preload complete: ${history.length} bars, ${swings.length} swings, ${zones.length} zones`
|
|
803
|
+
});
|
|
804
|
+
if (signalCount > 0) {
|
|
805
|
+
this.emit("log", {
|
|
806
|
+
type: "debug",
|
|
807
|
+
message: `[HQX-2B] ${signalCount} historical signals detected (ignored)`
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
this.lastSignalTime = 0;
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
module2.exports = { HQX2BLiquiditySweep: HQX2BLiquiditySweep2 };
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
// hqx-2b/index.js
|
|
818
|
+
var EventEmitter = require("events");
|
|
819
|
+
var { HQX2BLiquiditySweep } = require_core();
|
|
820
|
+
var { OrderSide, SignalStrength } = require_types();
|
|
821
|
+
var { SweepType, ZoneType, DEFAULT_CONFIG } = require_config();
|
|
822
|
+
var HQX2BStrategy = class extends EventEmitter {
|
|
823
|
+
constructor(config = {}) {
|
|
824
|
+
super();
|
|
825
|
+
this.config = config;
|
|
826
|
+
this.strategy = new HQX2BLiquiditySweep(config);
|
|
827
|
+
this.strategy.on("signal", (sig) => this.emit("signal", sig));
|
|
828
|
+
this.strategy.on("log", (log) => this.emit("log", log));
|
|
829
|
+
}
|
|
830
|
+
// Interface methods (compatible with M1)
|
|
831
|
+
processTick(tick) {
|
|
832
|
+
return this.strategy.processTick(tick);
|
|
833
|
+
}
|
|
834
|
+
onTick(tick) {
|
|
835
|
+
return this.strategy.onTick(tick);
|
|
836
|
+
}
|
|
837
|
+
onTrade(trade) {
|
|
838
|
+
return this.strategy.onTrade(trade);
|
|
839
|
+
}
|
|
840
|
+
processBar(contractId, bar) {
|
|
841
|
+
return this.strategy.processBar(contractId, bar);
|
|
842
|
+
}
|
|
843
|
+
initialize(contractId, tickSize, tickValue) {
|
|
844
|
+
return this.strategy.initialize(contractId, tickSize, tickValue);
|
|
845
|
+
}
|
|
846
|
+
getAnalysisState(contractId, price) {
|
|
847
|
+
return this.strategy.getAnalysisState(contractId, price);
|
|
848
|
+
}
|
|
849
|
+
recordTradeResult(pnl) {
|
|
850
|
+
return this.strategy.recordTradeResult(pnl);
|
|
851
|
+
}
|
|
852
|
+
reset(contractId) {
|
|
853
|
+
return this.strategy.reset(contractId);
|
|
854
|
+
}
|
|
855
|
+
getStats() {
|
|
856
|
+
return this.strategy.getStats();
|
|
857
|
+
}
|
|
858
|
+
getBarHistory(contractId) {
|
|
859
|
+
return this.strategy.getBarHistory(contractId);
|
|
860
|
+
}
|
|
861
|
+
preloadBars(contractId, bars) {
|
|
862
|
+
return this.strategy.preloadBars(contractId, bars);
|
|
863
|
+
}
|
|
864
|
+
getHealthStatus(contractId) {
|
|
865
|
+
return this.strategy.getHealthStatus(contractId);
|
|
866
|
+
}
|
|
867
|
+
generateSignal(params) {
|
|
868
|
+
return null;
|
|
869
|
+
}
|
|
870
|
+
// Signals come from processBar
|
|
871
|
+
};
|
|
872
|
+
module.exports = {
|
|
873
|
+
HQX2BLiquiditySweep,
|
|
874
|
+
HQX2BStrategy,
|
|
875
|
+
// Aliases
|
|
876
|
+
M2: HQX2BStrategy,
|
|
877
|
+
S2: HQX2BLiquiditySweep,
|
|
878
|
+
OrderSide,
|
|
879
|
+
SignalStrength,
|
|
880
|
+
SweepType,
|
|
881
|
+
ZoneType,
|
|
882
|
+
DEFAULT_CONFIG
|
|
883
|
+
};
|