hedgequantx 2.9.93 → 2.9.95
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 +1 -845
- package/dist/lib/m/ultra-scalping.js +1 -705
- package/package.json +1 -1
- package/src/lib/m/hqx-2b.js +1 -1
- package/src/pages/algo/algo-executor.js +1 -2
package/dist/lib/m/hqx-2b.js
CHANGED
|
@@ -1,845 +1 @@
|
|
|
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 (HYPER AGGRESSIVE)
|
|
16
|
-
swing: {
|
|
17
|
-
lookbackBars: 1,
|
|
18
|
-
// Minimum lookback - detect swings faster
|
|
19
|
-
minStrength: 1,
|
|
20
|
-
// Any swing counts
|
|
21
|
-
confirmationBars: 1
|
|
22
|
-
// Immediate confirmation
|
|
23
|
-
},
|
|
24
|
-
// Zone Detection (HYPER AGGRESSIVE)
|
|
25
|
-
zone: {
|
|
26
|
-
clusterToleranceTicks: 8,
|
|
27
|
-
// Wider tolerance for zone clustering
|
|
28
|
-
minTouches: 1,
|
|
29
|
-
// Single-touch zones OK
|
|
30
|
-
maxZoneAgeBars: 500,
|
|
31
|
-
// Keep zones longer
|
|
32
|
-
maxZoneDistanceTicks: 80,
|
|
33
|
-
// Look for zones further away
|
|
34
|
-
cooldownBars: 3
|
|
35
|
-
// Quick zone reuse (was 10)
|
|
36
|
-
},
|
|
37
|
-
// Sweep Detection (HYPER AGGRESSIVE)
|
|
38
|
-
sweep: {
|
|
39
|
-
minPenetrationTicks: 0.5,
|
|
40
|
-
// Even tiny penetration counts
|
|
41
|
-
maxPenetrationTicks: 20,
|
|
42
|
-
// Allow deeper sweeps
|
|
43
|
-
maxDurationBars: 10,
|
|
44
|
-
// Allow slower sweeps
|
|
45
|
-
minQualityScore: 0.2,
|
|
46
|
-
// Lower quality threshold (was 0.40)
|
|
47
|
-
minVolumeRatio: 0.5,
|
|
48
|
-
// Lower volume requirement (was 0.8)
|
|
49
|
-
minBodyRatio: 0.1
|
|
50
|
-
// Lower body ratio (was 0.2)
|
|
51
|
-
},
|
|
52
|
-
// Execution (OPTIMIZED 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 (was 30)
|
|
66
|
-
minHoldTimeMs: 5e3,
|
|
67
|
-
// 5 seconds min hold (was 10)
|
|
68
|
-
slippageTicks: 1,
|
|
69
|
-
commissionPerSide: 2
|
|
70
|
-
// $4 round-trip
|
|
71
|
-
},
|
|
72
|
-
// Session filter (US Regular Hours only - matches backtest)
|
|
73
|
-
session: {
|
|
74
|
-
enabled: true,
|
|
75
|
-
// MUST be enabled to match backtest results
|
|
76
|
-
startHour: 9,
|
|
77
|
-
// 9:30 AM EST
|
|
78
|
-
startMinute: 30,
|
|
79
|
-
endHour: 16,
|
|
80
|
-
// 4:00 PM EST
|
|
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.stats = { signals: 0, trades: 0, wins: 0, losses: 0, pnl: 0 };
|
|
480
|
-
this.recentTrades = [];
|
|
481
|
-
}
|
|
482
|
-
initialize(contractId, tickSize = 0.25, tickValue = 5) {
|
|
483
|
-
this.tickSize = tickSize;
|
|
484
|
-
this.tickValue = tickValue;
|
|
485
|
-
this.config.tickSize = tickSize;
|
|
486
|
-
this.config.tickValue = tickValue;
|
|
487
|
-
this.barHistory.set(contractId, []);
|
|
488
|
-
this.swingPoints.set(contractId, []);
|
|
489
|
-
this.liquidityZones.set(contractId, []);
|
|
490
|
-
this.currentBar.delete(contractId);
|
|
491
|
-
this.emit("log", {
|
|
492
|
-
type: "info",
|
|
493
|
-
message: `[HQX-2B] Initialized for ${contractId}: tick=${tickSize}, value=${tickValue}, TF=1min`
|
|
494
|
-
});
|
|
495
|
-
this.emit("log", {
|
|
496
|
-
type: "info",
|
|
497
|
-
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}`
|
|
498
|
-
});
|
|
499
|
-
}
|
|
500
|
-
/**
|
|
501
|
-
* Check if current time is within trading session (9:30-16:00 EST)
|
|
502
|
-
*/
|
|
503
|
-
isWithinSession(timestamp) {
|
|
504
|
-
if (!this.config.session.enabled) return true;
|
|
505
|
-
const date = new Date(timestamp);
|
|
506
|
-
const estOffset = this.isDST(date) ? -4 : -5;
|
|
507
|
-
const utcHours = date.getUTCHours();
|
|
508
|
-
const utcMinutes = date.getUTCMinutes();
|
|
509
|
-
const estHours = (utcHours + estOffset + 24) % 24;
|
|
510
|
-
const { startHour, startMinute, endHour, endMinute } = this.config.session;
|
|
511
|
-
const currentMins = estHours * 60 + utcMinutes;
|
|
512
|
-
const startMins = startHour * 60 + startMinute;
|
|
513
|
-
const endMins = endHour * 60 + endMinute;
|
|
514
|
-
return currentMins >= startMins && currentMins < endMins;
|
|
515
|
-
}
|
|
516
|
-
/**
|
|
517
|
-
* Check if date is in US Daylight Saving Time
|
|
518
|
-
*/
|
|
519
|
-
isDST(date) {
|
|
520
|
-
const jan = new Date(date.getFullYear(), 0, 1);
|
|
521
|
-
const jul = new Date(date.getFullYear(), 6, 1);
|
|
522
|
-
const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
|
|
523
|
-
return date.getTimezoneOffset() < stdOffset;
|
|
524
|
-
}
|
|
525
|
-
/**
|
|
526
|
-
* Process incoming tick and aggregate into 1-minute bars
|
|
527
|
-
* Only calls processBar() when a bar closes (every 60 seconds)
|
|
528
|
-
*/
|
|
529
|
-
processTick(tick) {
|
|
530
|
-
const { contractId, price, volume, timestamp } = tick;
|
|
531
|
-
const ts = timestamp || Date.now();
|
|
532
|
-
const vol = volume || 1;
|
|
533
|
-
if (!this.isWithinSession(ts)) {
|
|
534
|
-
return null;
|
|
535
|
-
}
|
|
536
|
-
let bar = this.currentBar.get(contractId);
|
|
537
|
-
const barStartTime = Math.floor(ts / this.barIntervalMs) * this.barIntervalMs;
|
|
538
|
-
if (!bar || bar.startTime !== barStartTime) {
|
|
539
|
-
if (bar) {
|
|
540
|
-
const closedBar = {
|
|
541
|
-
timestamp: bar.startTime,
|
|
542
|
-
open: bar.open,
|
|
543
|
-
high: bar.high,
|
|
544
|
-
low: bar.low,
|
|
545
|
-
close: bar.close,
|
|
546
|
-
volume: bar.volume
|
|
547
|
-
};
|
|
548
|
-
const signal = this.processBar(contractId, closedBar);
|
|
549
|
-
this.currentBar.set(contractId, {
|
|
550
|
-
startTime: barStartTime,
|
|
551
|
-
open: price,
|
|
552
|
-
high: price,
|
|
553
|
-
low: price,
|
|
554
|
-
close: price,
|
|
555
|
-
volume: vol
|
|
556
|
-
});
|
|
557
|
-
return signal;
|
|
558
|
-
} else {
|
|
559
|
-
this.currentBar.set(contractId, {
|
|
560
|
-
startTime: barStartTime,
|
|
561
|
-
open: price,
|
|
562
|
-
high: price,
|
|
563
|
-
low: price,
|
|
564
|
-
close: price,
|
|
565
|
-
volume: vol
|
|
566
|
-
});
|
|
567
|
-
return null;
|
|
568
|
-
}
|
|
569
|
-
} else {
|
|
570
|
-
bar.high = Math.max(bar.high, price);
|
|
571
|
-
bar.low = Math.min(bar.low, price);
|
|
572
|
-
bar.close = price;
|
|
573
|
-
bar.volume += vol;
|
|
574
|
-
return null;
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
onTick(tick) {
|
|
578
|
-
return this.processTick(tick);
|
|
579
|
-
}
|
|
580
|
-
onTrade(trade) {
|
|
581
|
-
return this.processTick({
|
|
582
|
-
contractId: trade.contractId || trade.symbol,
|
|
583
|
-
price: trade.price,
|
|
584
|
-
volume: trade.size || trade.volume || 1,
|
|
585
|
-
timestamp: trade.timestamp || Date.now()
|
|
586
|
-
});
|
|
587
|
-
}
|
|
588
|
-
processBar(contractId, bar) {
|
|
589
|
-
let bars = this.barHistory.get(contractId);
|
|
590
|
-
if (!bars) {
|
|
591
|
-
this.initialize(contractId);
|
|
592
|
-
bars = this.barHistory.get(contractId);
|
|
593
|
-
}
|
|
594
|
-
bars.push(bar);
|
|
595
|
-
if (bars.length > 500) bars.shift();
|
|
596
|
-
const currentIndex = bars.length - 1;
|
|
597
|
-
if (bars.length < this.config.swing.lookbackBars * 3) {
|
|
598
|
-
return null;
|
|
599
|
-
}
|
|
600
|
-
const swings = this.swingPoints.get(contractId);
|
|
601
|
-
const prevSwingCount = swings.length;
|
|
602
|
-
const updatedSwings = detectSwings(
|
|
603
|
-
bars,
|
|
604
|
-
currentIndex,
|
|
605
|
-
swings,
|
|
606
|
-
this.config.swing,
|
|
607
|
-
this.config.zone.maxZoneAgeBars
|
|
608
|
-
);
|
|
609
|
-
this.swingPoints.set(contractId, updatedSwings);
|
|
610
|
-
if (updatedSwings.length > prevSwingCount) {
|
|
611
|
-
const newSwing = updatedSwings[updatedSwings.length - 1];
|
|
612
|
-
this.emit("log", {
|
|
613
|
-
type: "debug",
|
|
614
|
-
message: `[2B] NEW SWING ${newSwing.type.toUpperCase()} @ ${newSwing.price.toFixed(2)} | Total: ${updatedSwings.length}`
|
|
615
|
-
});
|
|
616
|
-
}
|
|
617
|
-
const zones = this.liquidityZones.get(contractId);
|
|
618
|
-
const prevZoneCount = zones.length;
|
|
619
|
-
const updatedZones = updateZones(
|
|
620
|
-
updatedSwings,
|
|
621
|
-
zones,
|
|
622
|
-
currentIndex,
|
|
623
|
-
this.config.zone,
|
|
624
|
-
this.tickSize
|
|
625
|
-
);
|
|
626
|
-
this.liquidityZones.set(contractId, updatedZones);
|
|
627
|
-
if (updatedZones.length > prevZoneCount) {
|
|
628
|
-
const newZone = updatedZones[updatedZones.length - 1];
|
|
629
|
-
this.emit("log", {
|
|
630
|
-
type: "debug",
|
|
631
|
-
message: `[2B] NEW ZONE ${newZone.type.toUpperCase()} @ ${newZone.getLevel().toFixed(2)} | Total: ${updatedZones.length}`
|
|
632
|
-
});
|
|
633
|
-
}
|
|
634
|
-
const sweep = detectSweep(
|
|
635
|
-
updatedZones,
|
|
636
|
-
bars,
|
|
637
|
-
currentIndex,
|
|
638
|
-
this.config.sweep,
|
|
639
|
-
this.config.zone,
|
|
640
|
-
this.tickSize
|
|
641
|
-
);
|
|
642
|
-
if (sweep) {
|
|
643
|
-
this.emit("log", {
|
|
644
|
-
type: "debug",
|
|
645
|
-
message: `[2B] SWEEP ${sweep.sweepType} | Valid: ${sweep.isValid} | Pen: ${sweep.penetrationTicks.toFixed(1)}t | Q: ${(sweep.qualityScore * 100).toFixed(0)}%`
|
|
646
|
-
});
|
|
647
|
-
}
|
|
648
|
-
if (sweep && sweep.isValid) {
|
|
649
|
-
if (Date.now() - this.lastSignalTime < this.config.execution.cooldownMs) {
|
|
650
|
-
this.emit("log", {
|
|
651
|
-
type: "debug",
|
|
652
|
-
message: `[2B] COOLDOWN - waiting ${Math.ceil((this.config.execution.cooldownMs - (Date.now() - this.lastSignalTime)) / 1e3)}s`
|
|
653
|
-
});
|
|
654
|
-
return null;
|
|
655
|
-
}
|
|
656
|
-
this.emit("log", {
|
|
657
|
-
type: "debug",
|
|
658
|
-
message: `[2B] GENERATING SIGNAL from ${sweep.sweepType} sweep...`
|
|
659
|
-
});
|
|
660
|
-
const signal = generateSignal({
|
|
661
|
-
contractId,
|
|
662
|
-
currentBar: bar,
|
|
663
|
-
currentIndex,
|
|
664
|
-
sweep,
|
|
665
|
-
config: this.config,
|
|
666
|
-
tickSize: this.tickSize
|
|
667
|
-
});
|
|
668
|
-
if (signal) {
|
|
669
|
-
this.lastSignalTime = Date.now();
|
|
670
|
-
this.stats.signals++;
|
|
671
|
-
this.emit("signal", {
|
|
672
|
-
side: signal.direction === "long" ? "buy" : "sell",
|
|
673
|
-
action: "open",
|
|
674
|
-
reason: `2B ${sweep.sweepType} | Pen:${sweep.penetrationTicks.toFixed(1)}t | Vol:${sweep.volumeRatio.toFixed(1)}x | Q:${(sweep.qualityScore * 100).toFixed(0)}%`,
|
|
675
|
-
...signal
|
|
676
|
-
});
|
|
677
|
-
this.emit("log", {
|
|
678
|
-
type: "info",
|
|
679
|
-
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)}%`
|
|
680
|
-
});
|
|
681
|
-
return signal;
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
return null;
|
|
685
|
-
}
|
|
686
|
-
getAnalysisState(contractId, currentPrice) {
|
|
687
|
-
const bars = this.barHistory.get(contractId) || [];
|
|
688
|
-
const zones = this.liquidityZones.get(contractId) || [];
|
|
689
|
-
const swings = this.swingPoints.get(contractId) || [];
|
|
690
|
-
if (bars.length < 5) {
|
|
691
|
-
return { ready: false, message: `Collecting data... ${bars.length}/5 bars` };
|
|
692
|
-
}
|
|
693
|
-
const sortedZones = zones.map((z) => ({ zone: z, distance: Math.abs(currentPrice - z.getLevel()) })).sort((a, b) => a.distance - b.distance);
|
|
694
|
-
const nearestResistance = sortedZones.find((z) => z.zone.type === ZoneType2.RESISTANCE);
|
|
695
|
-
const nearestSupport = sortedZones.find((z) => z.zone.type === ZoneType2.SUPPORT);
|
|
696
|
-
return {
|
|
697
|
-
ready: true,
|
|
698
|
-
barsProcessed: bars.length,
|
|
699
|
-
swingsDetected: swings.length,
|
|
700
|
-
activeZones: zones.length,
|
|
701
|
-
nearestResistance: nearestResistance ? nearestResistance.zone.getLevel() : null,
|
|
702
|
-
nearestSupport: nearestSupport ? nearestSupport.zone.getLevel() : null,
|
|
703
|
-
stopTicks: this.config.execution.stopTicks,
|
|
704
|
-
targetTicks: this.config.execution.targetTicks,
|
|
705
|
-
strategy: "HQX-2B Liquidity Sweep (Optimized)"
|
|
706
|
-
};
|
|
707
|
-
}
|
|
708
|
-
recordTradeResult(pnl) {
|
|
709
|
-
this.recentTrades.push({ netPnl: pnl, timestamp: Date.now() });
|
|
710
|
-
if (this.recentTrades.length > 100) this.recentTrades.shift();
|
|
711
|
-
if (pnl > 0) {
|
|
712
|
-
this.stats.wins++;
|
|
713
|
-
} else {
|
|
714
|
-
this.stats.losses++;
|
|
715
|
-
}
|
|
716
|
-
this.stats.trades++;
|
|
717
|
-
this.stats.pnl += pnl;
|
|
718
|
-
this.emit("log", {
|
|
719
|
-
type: "debug",
|
|
720
|
-
message: `[HQX-2B] Trade result: ${pnl > 0 ? "WIN" : "LOSS"} $${pnl.toFixed(2)}`
|
|
721
|
-
});
|
|
722
|
-
}
|
|
723
|
-
getBarHistory(contractId) {
|
|
724
|
-
return this.barHistory.get(contractId) || [];
|
|
725
|
-
}
|
|
726
|
-
getStats() {
|
|
727
|
-
return this.stats;
|
|
728
|
-
}
|
|
729
|
-
reset(contractId) {
|
|
730
|
-
this.barHistory.set(contractId, []);
|
|
731
|
-
this.swingPoints.set(contractId, []);
|
|
732
|
-
this.liquidityZones.set(contractId, []);
|
|
733
|
-
this.currentBar.delete(contractId);
|
|
734
|
-
this.emit("log", {
|
|
735
|
-
type: "info",
|
|
736
|
-
message: `[HQX-2B] Reset state for ${contractId}`
|
|
737
|
-
});
|
|
738
|
-
}
|
|
739
|
-
/**
|
|
740
|
-
* Preload historical bars to warm up the strategy
|
|
741
|
-
* @param {string} contractId - Contract ID
|
|
742
|
-
* @param {Array} bars - Array of bars {timestamp, open, high, low, close, volume}
|
|
743
|
-
*/
|
|
744
|
-
preloadBars(contractId, bars) {
|
|
745
|
-
if (!bars || bars.length === 0) {
|
|
746
|
-
this.emit("log", {
|
|
747
|
-
type: "debug",
|
|
748
|
-
message: `[HQX-2B] No historical bars to preload`
|
|
749
|
-
});
|
|
750
|
-
return;
|
|
751
|
-
}
|
|
752
|
-
if (!this.barHistory.has(contractId)) {
|
|
753
|
-
this.initialize(contractId);
|
|
754
|
-
}
|
|
755
|
-
const sortedBars = [...bars].sort((a, b) => a.timestamp - b.timestamp);
|
|
756
|
-
this.emit("log", {
|
|
757
|
-
type: "info",
|
|
758
|
-
message: `[HQX-2B] Preloading ${sortedBars.length} historical bars...`
|
|
759
|
-
});
|
|
760
|
-
let signalCount = 0;
|
|
761
|
-
for (const bar of sortedBars) {
|
|
762
|
-
const signal = this.processBar(contractId, bar);
|
|
763
|
-
if (signal) signalCount++;
|
|
764
|
-
}
|
|
765
|
-
const history = this.barHistory.get(contractId) || [];
|
|
766
|
-
const swings = this.swingPoints.get(contractId) || [];
|
|
767
|
-
const zones = this.liquidityZones.get(contractId) || [];
|
|
768
|
-
this.emit("log", {
|
|
769
|
-
type: "info",
|
|
770
|
-
message: `[HQX-2B] Preload complete: ${history.length} bars, ${swings.length} swings, ${zones.length} zones`
|
|
771
|
-
});
|
|
772
|
-
if (signalCount > 0) {
|
|
773
|
-
this.emit("log", {
|
|
774
|
-
type: "debug",
|
|
775
|
-
message: `[HQX-2B] ${signalCount} historical signals detected (ignored)`
|
|
776
|
-
});
|
|
777
|
-
}
|
|
778
|
-
this.lastSignalTime = 0;
|
|
779
|
-
}
|
|
780
|
-
};
|
|
781
|
-
module2.exports = { HQX2BLiquiditySweep: HQX2BLiquiditySweep2 };
|
|
782
|
-
}
|
|
783
|
-
});
|
|
784
|
-
|
|
785
|
-
// hqx-2b/index.js
|
|
786
|
-
var EventEmitter = require("events");
|
|
787
|
-
var { HQX2BLiquiditySweep } = require_core();
|
|
788
|
-
var { OrderSide, SignalStrength } = require_types();
|
|
789
|
-
var { SweepType, ZoneType, DEFAULT_CONFIG } = require_config();
|
|
790
|
-
var HQX2BStrategy = class extends EventEmitter {
|
|
791
|
-
constructor(config = {}) {
|
|
792
|
-
super();
|
|
793
|
-
this.config = config;
|
|
794
|
-
this.strategy = new HQX2BLiquiditySweep(config);
|
|
795
|
-
this.strategy.on("signal", (sig) => this.emit("signal", sig));
|
|
796
|
-
this.strategy.on("log", (log) => this.emit("log", log));
|
|
797
|
-
}
|
|
798
|
-
// Interface methods (compatible with M1)
|
|
799
|
-
processTick(tick) {
|
|
800
|
-
return this.strategy.processTick(tick);
|
|
801
|
-
}
|
|
802
|
-
onTick(tick) {
|
|
803
|
-
return this.strategy.onTick(tick);
|
|
804
|
-
}
|
|
805
|
-
onTrade(trade) {
|
|
806
|
-
return this.strategy.onTrade(trade);
|
|
807
|
-
}
|
|
808
|
-
processBar(contractId, bar) {
|
|
809
|
-
return this.strategy.processBar(contractId, bar);
|
|
810
|
-
}
|
|
811
|
-
initialize(contractId, tickSize, tickValue) {
|
|
812
|
-
return this.strategy.initialize(contractId, tickSize, tickValue);
|
|
813
|
-
}
|
|
814
|
-
getAnalysisState(contractId, price) {
|
|
815
|
-
return this.strategy.getAnalysisState(contractId, price);
|
|
816
|
-
}
|
|
817
|
-
recordTradeResult(pnl) {
|
|
818
|
-
return this.strategy.recordTradeResult(pnl);
|
|
819
|
-
}
|
|
820
|
-
reset(contractId) {
|
|
821
|
-
return this.strategy.reset(contractId);
|
|
822
|
-
}
|
|
823
|
-
getStats() {
|
|
824
|
-
return this.strategy.getStats();
|
|
825
|
-
}
|
|
826
|
-
getBarHistory(contractId) {
|
|
827
|
-
return this.strategy.getBarHistory(contractId);
|
|
828
|
-
}
|
|
829
|
-
generateSignal(params) {
|
|
830
|
-
return null;
|
|
831
|
-
}
|
|
832
|
-
// Signals come from processBar
|
|
833
|
-
};
|
|
834
|
-
module.exports = {
|
|
835
|
-
HQX2BLiquiditySweep,
|
|
836
|
-
HQX2BStrategy,
|
|
837
|
-
// Aliases
|
|
838
|
-
M2: HQX2BStrategy,
|
|
839
|
-
S2: HQX2BLiquiditySweep,
|
|
840
|
-
OrderSide,
|
|
841
|
-
SignalStrength,
|
|
842
|
-
SweepType,
|
|
843
|
-
ZoneType,
|
|
844
|
-
DEFAULT_CONFIG
|
|
845
|
-
};
|
|
1
|
+
const _0x183474=_0xa0d7;function _0xa0d7(_0x603741,_0x487671){_0x603741=_0x603741-0xb7;const _0x1fe0b1=_0x1fe0();let _0xa0d7ea=_0x1fe0b1[_0x603741];return _0xa0d7ea;}function _0x1fe0(){const _0x5362e2=['isDST','startTime','isWithinSession','RESISTANCE','_detectSweep','cooldownBars','811560xmeNET','push','maxDurationBars','exitBarIndex','session','open','_updateZones',',\x20TF=1min','isValid','Collecting\x20data...\x20','execution','onTrade','liquidityZones','now','[HQX-2B]\x20Params:\x20Stop=','info','breakevenTicks','pnl','maxZoneDistanceTicks','losses','targetTicks','28448910SsjWLs','strength','extremePrice','t\x20|\x20Vol:','\x20|\x20Pen:','sweepType','priceHigh','4000653PuitKT','sell','map','symbol','MODERATE','117235CAEGgI','debug','volumeRatio','find','stats','entryBarIndex','high','tickValue','[HQX-2B]\x20Preload\x20complete:\x20','low','short','log','min','1tQzsIO','split','tickSize','swing','object','priceLow','_generateSignal','maxPenetrationTicks','SUPPORT','getLevel','_mergeConfig','onTick','signal','processTick','zone','currentBar','stopTicks','enabled','clusterToleranceTicks','buy','sweptAt','distance','lastSignalTime','initialize','createdAt','get','[2B]\x20NEW\x20SWING\x20','_getVolumeRatio','lookbackBars','activeSweeps','HIGH_SWEEP','max','\x20|\x20Total:\x20','trades','LOSS','preloadBars','recordTradeResult','\x20|\x20Pen:\x20','touches','\x20bars,\x20','\x20@\x20','_detectSwings','t\x20|\x20Q:\x20','delete','[HQX-2B]\x20','timestamp','7MmcEyw','config','getBarHistory','7781496IBGcLi','set','trailTriggerTicks','\x20historical\x20signals\x20detected\x20(ignored)','minPenetrationTicks','type','toFixed','swingPoints','generateSignal','WEAK','[HQX-2B]\x20Initialized\x20for\x20','volume','price','floor','strategy','lastUsedBarIndex','[2B]\x20SWEEP\x20','durationBars','toUpperCase','_scoreSweep','qualityScore','\x20zones',',\x20value=','getFullYear','penetrationTicks','VERY_STRONG','resistance','processBar','getAnalysisState','has','long','BID','\x20|\x20Valid:\x20','barIndex','1213746dXecYN','barHistory','support','ASK','recentTrades','getTimezoneOffset','length','200HwALzf','shift','1488962xYcTRT','abs','reset','LOW_SWEEP','emit','close'];_0x1fe0=function(){return _0x5362e2;};return _0x1fe0();}(function(_0x1f217b,_0x27b1fa){const _0x31f12a=_0xa0d7,_0x5304b0=_0x1f217b();while(!![]){try{const _0x1d1bbf=parseInt(_0x31f12a(0xca))/0x1*(parseInt(_0x31f12a(0x126))/0x2)+-parseInt(_0x31f12a(0x11d))/0x3+-parseInt(_0x31f12a(0x124))/0x4*(parseInt(_0x31f12a(0xbd))/0x5)+parseInt(_0x31f12a(0x132))/0x6+parseInt(_0x31f12a(0xf8))/0x7*(-parseInt(_0x31f12a(0xfb))/0x8)+-parseInt(_0x31f12a(0xb8))/0x9+parseInt(_0x31f12a(0x147))/0xa;if(_0x1d1bbf===_0x27b1fa)break;else _0x5304b0['push'](_0x5304b0['shift']());}catch(_0x3ae918){_0x5304b0['push'](_0x5304b0['shift']());}}}(_0x1fe0,0xb2580));const EventEmitter=require('events'),{v4:uuidv4}=require('uuid'),OrderSide={'BID':0x0,'ASK':0x1},SignalStrength={'WEAK':0x1,'MODERATE':0x2,'STRONG':0x3,'VERY_STRONG':0x4},SweepType={'HIGH_SWEEP':_0x183474(0xc3),'LOW_SWEEP':_0x183474(0xc6)},ZoneType={'RESISTANCE':_0x183474(0x115),'SUPPORT':_0x183474(0x11f)},DEFAULT_CONFIG={'tickSize':0.25,'tickValue':0x5,'swing':{'lookbackBars':0x1,'minStrength':0x1,'confirmationBars':0x1},'zone':{'clusterToleranceTicks':0x8,'minTouches':0x1,'maxZoneAgeBars':0x1f4,'maxZoneDistanceTicks':0x50,'cooldownBars':0x3},'sweep':{'minPenetrationTicks':0.5,'maxPenetrationTicks':0x14,'maxDurationBars':0xa,'minQualityScore':0.2,'minVolumeRatio':0.5,'minBodyRatio':0.1},'execution':{'stopTicks':0xa,'targetTicks':0x28,'breakevenTicks':0x4,'trailTriggerTicks':0x8,'trailDistanceTicks':0x4,'cooldownMs':0x3a98,'minHoldTimeMs':0x1388,'slippageTicks':0x1,'commissionPerSide':0x2},'session':{'enabled':!![],'startHour':0x9,'startMinute':0x1e,'endHour':0x10,'endMinute':0x0,'timezone':'America/New_York'}};class SwingPoint{constructor(_0x1103eb,_0xc92303,_0x5f5708,_0x2c1e3c,_0x14682b=0x1){this['type']=_0x1103eb,this['price']=_0xc92303,this['barIndex']=_0x5f5708,this['timestamp']=_0x2c1e3c,this['strength']=_0x14682b;}}class LiquidityZone{constructor(_0xdfc396,_0x17e55e,_0x1ecdc7,_0x15ae41,_0x44cfe7){const _0x571717=_0x183474;this['id']=uuidv4(),this[_0x571717(0x100)]=_0xdfc396,this['priceHigh']=_0x17e55e,this['priceLow']=_0x1ecdc7,this[_0x571717(0xe2)]=_0x15ae41,this[_0x571717(0x11c)]=_0x44cfe7,this[_0x571717(0xf0)]=0x1,this['swept']=![],this['sweptAt']=null,this[_0x571717(0x10a)]=-0x3e7,this['qualityScore']=0.5;}['containsPrice'](_0x54c83b,_0x1bb25c,_0x46950d){const _0x395a18=_0x183474,_0x51b03d=_0x1bb25c*_0x46950d;return _0x54c83b>=this[_0x395a18(0xcf)]-_0x51b03d&&_0x54c83b<=this[_0x395a18(0xb7)]+_0x51b03d;}['getLevel'](){const _0x14d7d1=_0x183474;return(this[_0x14d7d1(0xb7)]+this['priceLow'])/0x2;}}class SweepEvent{constructor(_0x1b137b,_0x18b8ae,_0x4e4a8c,_0x4b741b,_0x138da8){const _0xa4cd4e=_0x183474;this[_0xa4cd4e(0x14c)]=_0x1b137b,this['zone']=_0x18b8ae,this[_0xa4cd4e(0xc2)]=_0x4e4a8c,this['extremeBarIndex']=_0x4b741b,this[_0xa4cd4e(0x149)]=_0x138da8,this['exitBarIndex']=null,this[_0xa4cd4e(0x13a)]=![],this['qualityScore']=0x0,this['penetrationTicks']=0x0,this['durationBars']=0x0,this['volumeRatio']=0x1;}}class HQX2BLiquiditySweep extends EventEmitter{constructor(_0x2c3245={}){const _0x49ac6a=_0x183474;super(),this['config']=this[_0x49ac6a(0xd4)](DEFAULT_CONFIG,_0x2c3245),this[_0x49ac6a(0xcc)]=this[_0x49ac6a(0xf9)][_0x49ac6a(0xcc)],this[_0x49ac6a(0xc4)]=this['config']['tickValue'],this['barHistory']=new Map(),this['swingPoints']=new Map(),this['liquidityZones']=new Map(),this[_0x49ac6a(0xe7)]=new Map(),this[_0x49ac6a(0xd9)]=new Map(),this['barIntervalMs']=0xea60,this[_0x49ac6a(0xe0)]=0x0,this['stats']={'signals':0x0,'trades':0x0,'wins':0x0,'losses':0x0,'pnl':0x0},this[_0x49ac6a(0x121)]=[];}['_mergeConfig'](_0x1c2002,_0x5e145a){const _0x2d9f6f=_0x183474,_0x40b63d={..._0x1c2002};for(const _0x28c5e4 in _0x5e145a){typeof _0x5e145a[_0x28c5e4]===_0x2d9f6f(0xce)&&!Array['isArray'](_0x5e145a[_0x28c5e4])?_0x40b63d[_0x28c5e4]={..._0x1c2002[_0x28c5e4],..._0x5e145a[_0x28c5e4]}:_0x40b63d[_0x28c5e4]=_0x5e145a[_0x28c5e4];}return _0x40b63d;}[_0x183474(0x12e)](_0x33e3cf){const _0x1cf77b=_0x183474;if(!this[_0x1cf77b(0xf9)]['session'][_0x1cf77b(0xdb)])return!![];const _0x54cd77=new Date(_0x33e3cf),_0x967318=this[_0x1cf77b(0x12c)](_0x54cd77)?-0x4:-0x5,_0x5909dd=_0x54cd77['getUTCHours'](),_0x33ed02=_0x54cd77['getUTCMinutes'](),_0x3d7ea0=(_0x5909dd+_0x967318+0x18)%0x18,{startHour:_0x1af91a,startMinute:_0x37f7e0,endHour:_0x37b0b2,endMinute:_0x42aeb8}=this['config'][_0x1cf77b(0x136)],_0x1bdffd=_0x3d7ea0*0x3c+_0x33ed02,_0xd28341=_0x1af91a*0x3c+_0x37f7e0,_0x18990e=_0x37b0b2*0x3c+_0x42aeb8;return _0x1bdffd>=_0xd28341&&_0x1bdffd<_0x18990e;}[_0x183474(0x12c)](_0x3b61fa){const _0x4e7f02=_0x183474,_0x498628=new Date(_0x3b61fa['getFullYear'](),0x0,0x1),_0x429d98=new Date(_0x3b61fa[_0x4e7f02(0x112)](),0x6,0x1),_0x1d1f0f=Math['max'](_0x498628['getTimezoneOffset'](),_0x429d98[_0x4e7f02(0x122)]());return _0x3b61fa[_0x4e7f02(0x122)]()<_0x1d1f0f;}[_0x183474(0xe1)](_0x2adcff,_0x2b81de=0.25,_0x2e9d01=0x5){const _0x45660b=_0x183474;this['tickSize']=_0x2b81de,this['tickValue']=_0x2e9d01,this['config'][_0x45660b(0xcc)]=_0x2b81de,this['config']['tickValue']=_0x2e9d01,this['barHistory']['set'](_0x2adcff,[]),this[_0x45660b(0x102)][_0x45660b(0xfc)](_0x2adcff,[]),this['liquidityZones'][_0x45660b(0xfc)](_0x2adcff,[]),this[_0x45660b(0xe7)]['set'](_0x2adcff,[]),this['currentBar'][_0x45660b(0xf5)](_0x2adcff),this['emit'](_0x45660b(0xc8),{'type':'info','message':_0x45660b(0x105)+_0x2adcff+':\x20tick='+_0x2b81de+_0x45660b(0x111)+_0x2e9d01+_0x45660b(0x139)}),this[_0x45660b(0x12a)](_0x45660b(0xc8),{'type':_0x45660b(0x141),'message':_0x45660b(0x140)+this['config']['execution'][_0x45660b(0xda)]+'t,\x20Target='+this[_0x45660b(0xf9)][_0x45660b(0x13c)][_0x45660b(0x146)]+'t,\x20BE='+this[_0x45660b(0xf9)]['execution'][_0x45660b(0x142)]+'t,\x20Trail='+this[_0x45660b(0xf9)]['execution'][_0x45660b(0xfd)]+'/'+this[_0x45660b(0xf9)]['execution']['trailDistanceTicks']});}['processTick'](_0xbe9b39){const _0x1f5b3e=_0x183474,{contractId:_0x4bb910,price:_0x4f2757,volume:_0x161cad,timestamp:_0x9eaa3a}=_0xbe9b39,_0x5b010b=_0x9eaa3a||Date[_0x1f5b3e(0x13f)](),_0x3358b6=_0x161cad||0x1;if(!this[_0x1f5b3e(0x12e)](_0x5b010b))return null;let _0x352786=this['currentBar'][_0x1f5b3e(0xe3)](_0x4bb910);const _0x1e8dc5=Math['floor'](_0x5b010b/this['barIntervalMs'])*this['barIntervalMs'];if(!_0x352786||_0x352786['startTime']!==_0x1e8dc5){if(_0x352786){const _0x5a6c07={'timestamp':_0x352786[_0x1f5b3e(0x12d)],'open':_0x352786[_0x1f5b3e(0x137)],'high':_0x352786[_0x1f5b3e(0xc3)],'low':_0x352786[_0x1f5b3e(0xc6)],'close':_0x352786[_0x1f5b3e(0x12b)],'volume':_0x352786['volume']},_0x2b885f=this['processBar'](_0x4bb910,_0x5a6c07);return this[_0x1f5b3e(0xd9)]['set'](_0x4bb910,{'startTime':_0x1e8dc5,'open':_0x4f2757,'high':_0x4f2757,'low':_0x4f2757,'close':_0x4f2757,'volume':_0x3358b6}),_0x2b885f;}else return this['currentBar'][_0x1f5b3e(0xfc)](_0x4bb910,{'startTime':_0x1e8dc5,'open':_0x4f2757,'high':_0x4f2757,'low':_0x4f2757,'close':_0x4f2757,'volume':_0x3358b6}),null;}else return _0x352786[_0x1f5b3e(0xc3)]=Math['max'](_0x352786['high'],_0x4f2757),_0x352786['low']=Math['min'](_0x352786['low'],_0x4f2757),_0x352786['close']=_0x4f2757,_0x352786['volume']+=_0x3358b6,null;}[_0x183474(0xd5)](_0x22080b){return this['processTick'](_0x22080b);}[_0x183474(0x13d)](_0x177772){const _0x18557b=_0x183474;return this['processTick']({'contractId':_0x177772['contractId']||_0x177772[_0x18557b(0xbb)],'price':_0x177772[_0x18557b(0x107)],'volume':_0x177772['size']||_0x177772['volume']||0x1,'timestamp':_0x177772[_0x18557b(0xf7)]||Date['now']()});}['processBar'](_0x3977b3,_0x4a411c){const _0x227711=_0x183474;let _0x25519b=this[_0x227711(0x11e)][_0x227711(0xe3)](_0x3977b3);!_0x25519b&&(this[_0x227711(0xe1)](_0x3977b3),_0x25519b=this[_0x227711(0x11e)][_0x227711(0xe3)](_0x3977b3));_0x25519b['push'](_0x4a411c);if(_0x25519b[_0x227711(0x123)]>0x1f4)_0x25519b[_0x227711(0x125)]();const _0x5bba1c=_0x25519b['length']-0x1;if(_0x25519b['length']<this['config'][_0x227711(0xcd)][_0x227711(0xe6)]*0x3)return null;const _0x420fbf=this['swingPoints']['get'](_0x3977b3)['length'];this[_0x227711(0xf3)](_0x3977b3,_0x25519b,_0x5bba1c);const _0x931204=this[_0x227711(0x102)][_0x227711(0xe3)](_0x3977b3);if(_0x931204['length']>_0x420fbf){const _0x144706=_0x931204[_0x931204['length']-0x1];this[_0x227711(0x12a)]('log',{'type':'debug','message':_0x227711(0xe4)+_0x144706[_0x227711(0x100)][_0x227711(0x10d)]()+'\x20@\x20'+_0x144706[_0x227711(0x107)][_0x227711(0x101)](0x2)+'\x20|\x20Total:\x20'+_0x931204[_0x227711(0x123)]});}const _0x3d72c8=this['liquidityZones'][_0x227711(0xe3)](_0x3977b3)[_0x227711(0x123)];this[_0x227711(0x138)](_0x3977b3,_0x5bba1c);const _0x48b8ba=this['liquidityZones']['get'](_0x3977b3);if(_0x48b8ba['length']>_0x3d72c8){const _0x9a4f93=_0x48b8ba[_0x48b8ba['length']-0x1];this['emit']('log',{'type':_0x227711(0xbe),'message':'[2B]\x20NEW\x20ZONE\x20'+_0x9a4f93['type']['toUpperCase']()+_0x227711(0xf2)+_0x9a4f93[_0x227711(0xd3)]()[_0x227711(0x101)](0x2)+_0x227711(0xea)+_0x48b8ba['length']});}const _0x34f389=this[_0x227711(0x130)](_0x3977b3,_0x25519b,_0x5bba1c);_0x34f389&&this['emit']('log',{'type':'debug','message':_0x227711(0x10b)+_0x34f389[_0x227711(0x14c)]+_0x227711(0x11b)+_0x34f389[_0x227711(0x13a)]+_0x227711(0xef)+_0x34f389['penetrationTicks']['toFixed'](0x1)+_0x227711(0xf4)+(_0x34f389[_0x227711(0x10f)]*0x64)[_0x227711(0x101)](0x0)+'%'});if(_0x34f389&&_0x34f389['isValid'])return this['_generateSignal'](_0x3977b3,_0x4a411c,_0x5bba1c,_0x34f389);return null;}[_0x183474(0xf3)](_0x4a995b,_0x1bc89c,_0x4833cc){const _0x3e5900=_0x183474,_0x43f547=this['config']['swing'][_0x3e5900(0xe6)],_0x43aebd=this[_0x3e5900(0xf9)][_0x3e5900(0xcd)]['minStrength'];if(_0x4833cc<_0x43f547*0x2)return;const _0x67c516=this['swingPoints']['get'](_0x4a995b),_0xb2cc55=_0x4833cc-_0x43f547,_0x2f6c0f=_0x1bc89c[_0xb2cc55];let _0x5e720f=!![],_0x381755=0x0;for(let _0x54a70c=_0xb2cc55-_0x43f547;_0x54a70c<=_0xb2cc55+_0x43f547;_0x54a70c++){if(_0x54a70c===_0xb2cc55||_0x54a70c<0x0||_0x54a70c>=_0x1bc89c[_0x3e5900(0x123)])continue;if(_0x1bc89c[_0x54a70c]['high']>=_0x2f6c0f[_0x3e5900(0xc3)]){_0x5e720f=![];break;}_0x381755++;}if(_0x5e720f&&_0x381755>=_0x43aebd){const _0x583636=_0x67c516[_0x3e5900(0xc0)](_0x147398=>_0x147398['barIndex']===_0xb2cc55&&_0x147398['type']===_0x3e5900(0xc3));!_0x583636&&_0x67c516['push'](new SwingPoint(_0x3e5900(0xc3),_0x2f6c0f['high'],_0xb2cc55,_0x2f6c0f[_0x3e5900(0xf7)],_0x381755));}let _0x4883ca=!![],_0x42e1d0=0x0;for(let _0x224db5=_0xb2cc55-_0x43f547;_0x224db5<=_0xb2cc55+_0x43f547;_0x224db5++){if(_0x224db5===_0xb2cc55||_0x224db5<0x0||_0x224db5>=_0x1bc89c['length'])continue;if(_0x1bc89c[_0x224db5]['low']<=_0x2f6c0f['low']){_0x4883ca=![];break;}_0x42e1d0++;}if(_0x4883ca&&_0x42e1d0>=_0x43aebd){const _0x2cc678=_0x67c516[_0x3e5900(0xc0)](_0x58ffa9=>_0x58ffa9['barIndex']===_0xb2cc55&&_0x58ffa9['type']===_0x3e5900(0xc6));!_0x2cc678&&_0x67c516['push'](new SwingPoint('low',_0x2f6c0f['low'],_0xb2cc55,_0x2f6c0f['timestamp'],_0x42e1d0));}const _0x28e4b8=this[_0x3e5900(0xf9)]['zone']['maxZoneAgeBars'];while(_0x67c516[_0x3e5900(0x123)]>0x0&&_0x67c516[0x0][_0x3e5900(0x11c)]<_0x4833cc-_0x28e4b8){_0x67c516[_0x3e5900(0x125)]();}}[_0x183474(0x138)](_0x38c13c,_0x3b5476){const _0x194197=_0x183474,_0x2f63f7=this['swingPoints']['get'](_0x38c13c),_0x584679=this['liquidityZones']['get'](_0x38c13c),_0x54d398=this['config']['zone'][_0x194197(0xdc)]*this[_0x194197(0xcc)],_0x50a32a=this[_0x194197(0xf9)][_0x194197(0xd8)]['maxZoneAgeBars'];for(let _0x2b9967=_0x584679[_0x194197(0x123)]-0x1;_0x2b9967>=0x0;_0x2b9967--){_0x3b5476-_0x584679[_0x2b9967]['barIndex']>_0x50a32a&&_0x584679['splice'](_0x2b9967,0x1);}for(const _0x20b283 of _0x2f63f7){let _0x4a01ed=null;for(const _0x57e4e8 of _0x584679){if(_0x57e4e8['containsPrice'](_0x20b283[_0x194197(0x107)],this[_0x194197(0xf9)]['zone']['clusterToleranceTicks'],this['tickSize'])){_0x4a01ed=_0x57e4e8;break;}}if(_0x4a01ed){_0x4a01ed['touches']++;if(_0x20b283[_0x194197(0x107)]>_0x4a01ed['priceHigh'])_0x4a01ed[_0x194197(0xb7)]=_0x20b283[_0x194197(0x107)];if(_0x20b283['price']<_0x4a01ed['priceLow'])_0x4a01ed[_0x194197(0xcf)]=_0x20b283['price'];_0x4a01ed['qualityScore']=Math['min'](0x1,0.3+_0x4a01ed['touches']*0.15);}else{const _0x57e59b=_0x20b283[_0x194197(0x100)]==='high'?ZoneType[_0x194197(0x12f)]:ZoneType[_0x194197(0xd2)],_0x55a5ef=new LiquidityZone(_0x57e59b,_0x20b283[_0x194197(0x107)]+_0x54d398/0x2,_0x20b283['price']-_0x54d398/0x2,_0x20b283[_0x194197(0xf7)],_0x20b283[_0x194197(0x11c)]);_0x55a5ef[_0x194197(0x10f)]=0.3+_0x20b283[_0x194197(0x148)]*0.1,_0x584679['push'](_0x55a5ef);}}}[_0x183474(0x130)](_0x20a56b,_0x3aae9e,_0x390300){const _0x5f49c4=_0x183474,_0x54fd84=this['liquidityZones'][_0x5f49c4(0xe3)](_0x20a56b),_0x32299c=_0x3aae9e[_0x390300],_0x323274=_0x32299c[_0x5f49c4(0x12b)],_0x54084e=this[_0x5f49c4(0xf9)]['sweep'],_0x4564a6=this['config']['zone'];for(const _0x25fcfa of _0x54fd84){if(_0x25fcfa[_0x5f49c4(0x10a)]>=0x0&&_0x390300-_0x25fcfa['lastUsedBarIndex']<_0x4564a6[_0x5f49c4(0x131)])continue;const _0x3f92e1=_0x25fcfa[_0x5f49c4(0xd3)](),_0x16d650=Math['abs'](_0x323274-_0x3f92e1)/this['tickSize'];if(_0x16d650>_0x4564a6[_0x5f49c4(0x144)])continue;const _0x5016eb=Math[_0x5f49c4(0xe9)](0x0,_0x390300-_0x54084e[_0x5f49c4(0x134)]*0x2);for(let _0x272a00=_0x5016eb;_0x272a00<_0x390300;_0x272a00++){const _0x411304=_0x3aae9e[_0x272a00];if(_0x25fcfa[_0x5f49c4(0x100)]===ZoneType[_0x5f49c4(0x12f)]){const _0x59b9f4=(_0x411304['high']-_0x25fcfa['priceHigh'])/this['tickSize'];if(_0x59b9f4>=_0x54084e['minPenetrationTicks']&&_0x59b9f4<=_0x54084e[_0x5f49c4(0xd1)]){if(_0x323274<_0x25fcfa['priceHigh']){const _0x553b90=_0x411304[_0x5f49c4(0xc3)]-_0x411304[_0x5f49c4(0xc6)],_0x4bd58b=Math[_0x5f49c4(0x127)](_0x411304[_0x5f49c4(0x12b)]-_0x411304['open']),_0x30930c=_0x553b90>0x0?_0x4bd58b/_0x553b90:0x0;if(_0x30930c>=_0x54084e['minBodyRatio']){const _0xa6607e=this['_getVolumeRatio'](_0x3aae9e,_0x272a00,0x14);if(_0xa6607e>=_0x54084e['minVolumeRatio']){const _0x231bd4=new SweepEvent(SweepType[_0x5f49c4(0xe8)],_0x25fcfa,_0x272a00,_0x272a00,_0x411304[_0x5f49c4(0xc3)]);_0x231bd4[_0x5f49c4(0x135)]=_0x390300,_0x231bd4[_0x5f49c4(0x113)]=_0x59b9f4,_0x231bd4[_0x5f49c4(0x10c)]=_0x390300-_0x272a00,_0x231bd4[_0x5f49c4(0xbf)]=_0xa6607e,_0x231bd4['qualityScore']=this['_scoreSweep'](_0x231bd4,_0x30930c),_0x231bd4['isValid']=_0x231bd4[_0x5f49c4(0x10f)]>=_0x54084e['minQualityScore'];if(_0x231bd4['isValid'])return _0x231bd4;}}}}}if(_0x25fcfa['type']===ZoneType[_0x5f49c4(0xd2)]){const _0x44b985=(_0x25fcfa[_0x5f49c4(0xcf)]-_0x411304['low'])/this[_0x5f49c4(0xcc)];if(_0x44b985>=_0x54084e[_0x5f49c4(0xff)]&&_0x44b985<=_0x54084e[_0x5f49c4(0xd1)]){if(_0x323274>_0x25fcfa['priceLow']){const _0x55e9b0=_0x411304[_0x5f49c4(0xc3)]-_0x411304['low'],_0x22210d=Math[_0x5f49c4(0x127)](_0x411304['close']-_0x411304[_0x5f49c4(0x137)]),_0x476b39=_0x55e9b0>0x0?_0x22210d/_0x55e9b0:0x0;if(_0x476b39>=_0x54084e['minBodyRatio']){const _0x5e3786=this[_0x5f49c4(0xe5)](_0x3aae9e,_0x272a00,0x14);if(_0x5e3786>=_0x54084e['minVolumeRatio']){const _0x1c8e66=new SweepEvent(SweepType[_0x5f49c4(0x129)],_0x25fcfa,_0x272a00,_0x272a00,_0x411304[_0x5f49c4(0xc6)]);_0x1c8e66['exitBarIndex']=_0x390300,_0x1c8e66[_0x5f49c4(0x113)]=_0x44b985,_0x1c8e66['durationBars']=_0x390300-_0x272a00,_0x1c8e66['volumeRatio']=_0x5e3786,_0x1c8e66['qualityScore']=this[_0x5f49c4(0x10e)](_0x1c8e66,_0x476b39),_0x1c8e66[_0x5f49c4(0x13a)]=_0x1c8e66[_0x5f49c4(0x10f)]>=_0x54084e['minQualityScore'];if(_0x1c8e66[_0x5f49c4(0x13a)])return _0x1c8e66;}}}}}}}return null;}['_getVolumeRatio'](_0x7831ab,_0x570a7d,_0x5f06b8){const _0x442402=_0x183474,_0x56af5c=Math[_0x442402(0xe9)](0x0,_0x570a7d-_0x5f06b8),_0x51ac8a=_0x7831ab['slice'](_0x56af5c,_0x570a7d);if(_0x51ac8a['length']===0x0)return 0x1;const _0x5ae545=_0x51ac8a[_0x442402(0xba)](_0x937266=>_0x937266[_0x442402(0x106)])['sort']((_0x170ed0,_0x27f300)=>_0x170ed0-_0x27f300),_0x260e90=Math[_0x442402(0x108)](_0x5ae545[_0x442402(0x123)]/0x2),_0x48b4bc=_0x5ae545[_0x260e90]||0x1;return _0x7831ab[_0x570a7d]['volume']/_0x48b4bc;}[_0x183474(0x10e)](_0x3025fc,_0x53f7bc){const _0x1ddc35=_0x183474;let _0x1801f0=0x0;const _0x2f9522=0x4,_0x3a126c=Math['abs'](_0x3025fc['penetrationTicks']-_0x2f9522);return _0x1801f0+=Math[_0x1ddc35(0xe9)](0x0,0.3-_0x3a126c*0.03),_0x1801f0+=Math[_0x1ddc35(0xe9)](0x0,0.25-_0x3025fc['durationBars']*0.05),_0x1801f0+=Math[_0x1ddc35(0xc9)](0.25,_0x3025fc['volumeRatio']*0.1),_0x1801f0+=Math[_0x1ddc35(0xc9)](0.2,_0x53f7bc*0.4),Math['min'](0x1,_0x1801f0);}[_0x183474(0xd0)](_0x51be2f,_0x40ff3f,_0xe0f10d,_0x32eaca){const _0x593a88=_0x183474;if(Date[_0x593a88(0x13f)]()-this[_0x593a88(0xe0)]<this[_0x593a88(0xf9)][_0x593a88(0x13c)]['cooldownMs'])return null;const _0x336686=this[_0x593a88(0xf9)]['execution'],_0x2f3793=_0x40ff3f[_0x593a88(0x12b)],_0x1cdc77=_0x32eaca[_0x593a88(0x14c)]===SweepType['HIGH_SWEEP']?_0x593a88(0xc7):'long';let _0x481f42,_0x2cfd8b,_0x4a4ca2,_0x2659bd;_0x1cdc77===_0x593a88(0x119)?(_0x481f42=_0x2f3793-_0x336686[_0x593a88(0xda)]*this[_0x593a88(0xcc)],_0x2cfd8b=_0x2f3793+_0x336686[_0x593a88(0x146)]*this[_0x593a88(0xcc)],_0x4a4ca2=_0x2f3793+_0x336686[_0x593a88(0x142)]*this[_0x593a88(0xcc)],_0x2659bd=_0x2f3793+_0x336686[_0x593a88(0xfd)]*this['tickSize']):(_0x481f42=_0x2f3793+_0x336686['stopTicks']*this['tickSize'],_0x2cfd8b=_0x2f3793-_0x336686['targetTicks']*this['tickSize'],_0x4a4ca2=_0x2f3793-_0x336686['breakevenTicks']*this[_0x593a88(0xcc)],_0x2659bd=_0x2f3793-_0x336686[_0x593a88(0xfd)]*this[_0x593a88(0xcc)]);const _0x1250f5=_0x336686[_0x593a88(0x146)]/_0x336686[_0x593a88(0xda)],_0x7dbf18=Math[_0x593a88(0xc9)](0x1,_0x32eaca[_0x593a88(0x10f)]*0.5+_0x32eaca[_0x593a88(0xd8)]['qualityScore']*0.3+(_0x32eaca[_0x593a88(0xbf)]>1.5?0.2:_0x32eaca[_0x593a88(0xbf)]*0.1));let _0x39b2f0=SignalStrength[_0x593a88(0xbc)];if(_0x7dbf18>=0.8)_0x39b2f0=SignalStrength[_0x593a88(0x114)];else{if(_0x7dbf18>=0.65)_0x39b2f0=SignalStrength['STRONG'];else{if(_0x7dbf18<0.5)_0x39b2f0=SignalStrength[_0x593a88(0x104)];}}const _0x5b3304=0.5+(_0x7dbf18-0.5)*0.4,_0x4b96de=_0x5b3304*Math['abs'](_0x2cfd8b-_0x2f3793)-(0x1-_0x5b3304)*Math[_0x593a88(0x127)](_0x2f3793-_0x481f42);_0x32eaca['zone'][_0x593a88(0x10a)]=_0xe0f10d,_0x32eaca[_0x593a88(0xd8)]['swept']=!![],_0x32eaca['zone'][_0x593a88(0xde)]=new Date(_0x40ff3f['timestamp']),this['lastSignalTime']=Date['now'](),this['stats']['signals']++;const _0x45a29b={'id':uuidv4(),'timestamp':Date[_0x593a88(0x13f)](),'symbol':_0x51be2f[_0x593a88(0xcb)]('.')[0x0]||_0x51be2f,'contractId':_0x51be2f,'side':_0x1cdc77===_0x593a88(0x119)?OrderSide[_0x593a88(0x11a)]:OrderSide[_0x593a88(0x120)],'direction':_0x1cdc77,'strategy':'HQX_2B_LIQUIDITY_SWEEP','strength':_0x39b2f0,'edge':_0x4b96de,'confidence':_0x7dbf18,'entry':_0x2f3793,'entryPrice':_0x2f3793,'stopLoss':_0x481f42,'takeProfit':_0x2cfd8b,'riskReward':_0x1250f5,'stopTicks':_0x336686['stopTicks'],'targetTicks':_0x336686[_0x593a88(0x146)],'breakevenTicks':_0x336686['breakevenTicks'],'trailTriggerTicks':_0x336686['trailTriggerTicks'],'trailDistanceTicks':_0x336686['trailDistanceTicks'],'beLevel':_0x4a4ca2,'trailTrigger':_0x2659bd,'sweepType':_0x32eaca[_0x593a88(0x14c)],'penetrationTicks':_0x32eaca[_0x593a88(0x113)],'sweepDurationBars':_0x32eaca['durationBars'],'sweepQuality':_0x32eaca[_0x593a88(0x10f)],'volumeRatio':_0x32eaca['volumeRatio'],'zoneType':_0x32eaca['zone'][_0x593a88(0x100)],'zoneLevel':_0x32eaca['zone']['getLevel'](),'zoneTouches':_0x32eaca[_0x593a88(0xd8)]['touches'],'zoneQuality':_0x32eaca['zone']['qualityScore'],'expires':Date[_0x593a88(0x13f)]()+0xea60};return this['emit'](_0x593a88(0xd6),{'side':_0x1cdc77===_0x593a88(0x119)?_0x593a88(0xdd):_0x593a88(0xb9),'action':_0x593a88(0x137),'reason':'2B\x20'+_0x32eaca['sweepType']+'\x20|\x20Pen:'+_0x32eaca[_0x593a88(0x113)]['toFixed'](0x1)+_0x593a88(0x14a)+_0x32eaca['volumeRatio']['toFixed'](0x1)+'x\x20|\x20Q:'+(_0x32eaca['qualityScore']*0x64)[_0x593a88(0x101)](0x0)+'%',..._0x45a29b}),this['emit']('log',{'type':'info','message':'[HQX-2B]\x20SIGNAL:\x20'+_0x1cdc77[_0x593a88(0x10d)]()+'\x20@\x20'+_0x2f3793[_0x593a88(0x101)](0x2)+'\x20|\x20'+_0x32eaca[_0x593a88(0x14c)]+_0x593a88(0x14b)+_0x32eaca['penetrationTicks']['toFixed'](0x1)+'t\x20Vol:'+_0x32eaca[_0x593a88(0xbf)]['toFixed'](0x1)+'x\x20|\x20Conf:'+(_0x7dbf18*0x64)[_0x593a88(0x101)](0x0)+'%'}),_0x45a29b;}['getAnalysisState'](_0x4f4fed,_0x17d1c9){const _0x1e3c2e=_0x183474,_0x4dcbd8=this[_0x1e3c2e(0x11e)]['get'](_0x4f4fed)||[],_0x14689b=this['liquidityZones'][_0x1e3c2e(0xe3)](_0x4f4fed)||[],_0x2065e8=this['swingPoints']['get'](_0x4f4fed)||[];if(_0x4dcbd8[_0x1e3c2e(0x123)]<0x5)return{'ready':![],'message':_0x1e3c2e(0x13b)+_0x4dcbd8['length']+'/5\x20bars'};const _0x5f152b=_0x14689b[_0x1e3c2e(0xba)](_0x3e5841=>({'zone':_0x3e5841,'distance':Math[_0x1e3c2e(0x127)](_0x17d1c9-_0x3e5841['getLevel']())}))['sort']((_0x472fc5,_0x1f8dbe)=>_0x472fc5[_0x1e3c2e(0xdf)]-_0x1f8dbe[_0x1e3c2e(0xdf)]),_0x371c94=_0x5f152b[_0x1e3c2e(0xc0)](_0x102ab5=>_0x102ab5['zone']['type']===ZoneType[_0x1e3c2e(0x12f)]),_0xbee416=_0x5f152b[_0x1e3c2e(0xc0)](_0x228c5c=>_0x228c5c['zone']['type']===ZoneType['SUPPORT']);return{'ready':!![],'barsProcessed':_0x4dcbd8[_0x1e3c2e(0x123)],'swingsDetected':_0x2065e8[_0x1e3c2e(0x123)],'activeZones':_0x14689b['length'],'nearestResistance':_0x371c94?_0x371c94[_0x1e3c2e(0xd8)][_0x1e3c2e(0xd3)]():null,'nearestSupport':_0xbee416?_0xbee416[_0x1e3c2e(0xd8)][_0x1e3c2e(0xd3)]():null,'stopTicks':this['config']['execution'][_0x1e3c2e(0xda)],'targetTicks':this['config'][_0x1e3c2e(0x13c)]['targetTicks'],'strategy':'HQX-2B\x20Liquidity\x20Sweep\x20(Optimized)'};}['recordTradeResult'](_0x4dbb62){const _0x10e3f5=_0x183474;this[_0x10e3f5(0x121)][_0x10e3f5(0x133)]({'netPnl':_0x4dbb62,'timestamp':Date['now']()});if(this[_0x10e3f5(0x121)]['length']>0x64)this[_0x10e3f5(0x121)]['shift']();_0x4dbb62>0x0?this['stats']['wins']++:this[_0x10e3f5(0xc1)][_0x10e3f5(0x145)]++,this[_0x10e3f5(0xc1)][_0x10e3f5(0xeb)]++,this['stats'][_0x10e3f5(0x143)]+=_0x4dbb62,this[_0x10e3f5(0x12a)](_0x10e3f5(0xc8),{'type':_0x10e3f5(0xbe),'message':'[HQX-2B]\x20Trade\x20result:\x20'+(_0x4dbb62>0x0?'WIN':_0x10e3f5(0xec))+'\x20$'+_0x4dbb62['toFixed'](0x2)});}[_0x183474(0xfa)](_0x5820ea){const _0x4df593=_0x183474;return this[_0x4df593(0x11e)][_0x4df593(0xe3)](_0x5820ea)||[];}['getStats'](){return this['stats'];}['reset'](_0x3c4e85){const _0x7f9bcb=_0x183474;this[_0x7f9bcb(0x11e)]['set'](_0x3c4e85,[]),this[_0x7f9bcb(0x102)][_0x7f9bcb(0xfc)](_0x3c4e85,[]),this[_0x7f9bcb(0x13e)]['set'](_0x3c4e85,[]),this[_0x7f9bcb(0xe7)]['set'](_0x3c4e85,[]),this[_0x7f9bcb(0xd9)][_0x7f9bcb(0xf5)](_0x3c4e85),this['emit'](_0x7f9bcb(0xc8),{'type':'info','message':'[HQX-2B]\x20Reset\x20state\x20for\x20'+_0x3c4e85});}[_0x183474(0xed)](_0x1fffc4,_0x351c9d){const _0x3c77f7=_0x183474;if(!_0x351c9d||_0x351c9d['length']===0x0){this[_0x3c77f7(0x12a)]('log',{'type':_0x3c77f7(0xbe),'message':'[HQX-2B]\x20No\x20historical\x20bars\x20to\x20preload'});return;}!this[_0x3c77f7(0x11e)][_0x3c77f7(0x118)](_0x1fffc4)&&this[_0x3c77f7(0xe1)](_0x1fffc4);const _0x50f2de=[..._0x351c9d]['sort']((_0x3b69a4,_0x2d35d0)=>_0x3b69a4[_0x3c77f7(0xf7)]-_0x2d35d0[_0x3c77f7(0xf7)]);this[_0x3c77f7(0x12a)]('log',{'type':'info','message':'[HQX-2B]\x20Preloading\x20'+_0x50f2de['length']+'\x20historical\x20bars...'});let _0x350b6f=0x0;for(const _0x4880c6 of _0x50f2de){const _0x12ffe8=this['processBar'](_0x1fffc4,_0x4880c6);if(_0x12ffe8)_0x350b6f++;}const _0x14e32c=this[_0x3c77f7(0x11e)]['get'](_0x1fffc4)||[],_0x12344c=this['swingPoints'][_0x3c77f7(0xe3)](_0x1fffc4)||[],_0x3e039b=this[_0x3c77f7(0x13e)][_0x3c77f7(0xe3)](_0x1fffc4)||[];this[_0x3c77f7(0x12a)]('log',{'type':_0x3c77f7(0x141),'message':_0x3c77f7(0xc5)+_0x14e32c[_0x3c77f7(0x123)]+_0x3c77f7(0xf1)+_0x12344c[_0x3c77f7(0x123)]+'\x20swings,\x20'+_0x3e039b[_0x3c77f7(0x123)]+_0x3c77f7(0x110)}),_0x350b6f>0x0&&this[_0x3c77f7(0x12a)]('log',{'type':'debug','message':_0x3c77f7(0xf6)+_0x350b6f+_0x3c77f7(0xfe)}),this['lastSignalTime']=0x0;}}class HQX2BStrategy extends EventEmitter{constructor(_0x38bdaf={}){const _0x2dcd7a=_0x183474;super(),this['config']=_0x38bdaf,this['strategy']=new HQX2BLiquiditySweep(_0x38bdaf),this[_0x2dcd7a(0x109)]['on']('signal',_0x405b46=>this['emit']('signal',_0x405b46)),this[_0x2dcd7a(0x109)]['on']('log',_0x2a6106=>this['emit']('log',_0x2a6106));}['processTick'](_0x45b7b5){const _0x59da87=_0x183474;return this[_0x59da87(0x109)][_0x59da87(0xd7)](_0x45b7b5);}['onTick'](_0x33842f){return this['strategy']['onTick'](_0x33842f);}['onTrade'](_0x23dcda){const _0x10c336=_0x183474;return this['strategy'][_0x10c336(0x13d)](_0x23dcda);}[_0x183474(0x116)](_0x389b1e,_0x381e23){const _0x4fa113=_0x183474;return this[_0x4fa113(0x109)]['processBar'](_0x389b1e,_0x381e23);}['initialize'](_0x5ee17e,_0x107058,_0x51ecff){const _0x2c0844=_0x183474;return this['strategy'][_0x2c0844(0xe1)](_0x5ee17e,_0x107058,_0x51ecff);}[_0x183474(0x117)](_0x2e94cd,_0x3dc562){const _0x3806d9=_0x183474;return this['strategy'][_0x3806d9(0x117)](_0x2e94cd,_0x3dc562);}[_0x183474(0xee)](_0x16d9eb){const _0x32aa62=_0x183474;return this[_0x32aa62(0x109)]['recordTradeResult'](_0x16d9eb);}[_0x183474(0x128)](_0x32870d){const _0x456d6f=_0x183474;return this[_0x456d6f(0x109)]['reset'](_0x32870d);}['getStats'](){return this['strategy']['getStats']();}['getBarHistory'](_0x378e93){return this['strategy']['getBarHistory'](_0x378e93);}['preloadBars'](_0x31a9ec,_0x2f4948){const _0x58f029=_0x183474;return this[_0x58f029(0x109)][_0x58f029(0xed)](_0x31a9ec,_0x2f4948);}[_0x183474(0x103)](_0x4c4775){return null;}}module['exports']={'HQX2BLiquiditySweep':HQX2BLiquiditySweep,'HQX2BStrategy':HQX2BStrategy,'M2':HQX2BStrategy,'S2':HQX2BLiquiditySweep,'OrderSide':OrderSide,'SignalStrength':SignalStrength,'SweepType':SweepType,'ZoneType':ZoneType,'DEFAULT_CONFIG':DEFAULT_CONFIG};
|