hedgequantx 2.4.4 → 2.4.6
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/api.jsc +0 -0
- package/dist/lib/api2.jsc +0 -0
- package/dist/lib/core.jsc +0 -0
- package/dist/lib/core2.jsc +0 -0
- package/dist/lib/data.jsc +0 -0
- package/dist/lib/data2.jsc +0 -0
- package/dist/lib/decoder.jsc +0 -0
- package/dist/lib/m/mod1.jsc +0 -0
- package/dist/lib/m/mod2.jsc +0 -0
- package/dist/lib/n/r1.jsc +0 -0
- package/dist/lib/n/r2.jsc +0 -0
- package/dist/lib/n/r3.jsc +0 -0
- package/dist/lib/n/r4.jsc +0 -0
- package/dist/lib/n/r5.jsc +0 -0
- package/dist/lib/n/r6.jsc +0 -0
- package/dist/lib/n/r7.jsc +0 -0
- package/dist/lib/o/util1.jsc +0 -0
- package/dist/lib/o/util2.jsc +0 -0
- package/package.json +1 -1
- package/src/lib/m/s1.js +357 -46
- package/src/pages/algo/one-account.js +2 -1
package/dist/lib/api.jsc
CHANGED
|
Binary file
|
package/dist/lib/api2.jsc
CHANGED
|
Binary file
|
package/dist/lib/core.jsc
CHANGED
|
Binary file
|
package/dist/lib/core2.jsc
CHANGED
|
Binary file
|
package/dist/lib/data.jsc
CHANGED
|
Binary file
|
package/dist/lib/data2.jsc
CHANGED
|
Binary file
|
package/dist/lib/decoder.jsc
CHANGED
|
Binary file
|
package/dist/lib/m/mod1.jsc
CHANGED
|
Binary file
|
package/dist/lib/m/mod2.jsc
CHANGED
|
Binary file
|
package/dist/lib/n/r1.jsc
CHANGED
|
Binary file
|
package/dist/lib/n/r2.jsc
CHANGED
|
Binary file
|
package/dist/lib/n/r3.jsc
CHANGED
|
Binary file
|
package/dist/lib/n/r4.jsc
CHANGED
|
Binary file
|
package/dist/lib/n/r5.jsc
CHANGED
|
Binary file
|
package/dist/lib/n/r6.jsc
CHANGED
|
Binary file
|
package/dist/lib/n/r7.jsc
CHANGED
|
Binary file
|
package/dist/lib/o/util1.jsc
CHANGED
|
Binary file
|
package/dist/lib/o/util2.jsc
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/src/lib/m/s1.js
CHANGED
|
@@ -1,61 +1,372 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* HQX Ultra Scalping Strategy
|
|
3
|
+
* Tick-based real-time scalping
|
|
3
4
|
*/
|
|
4
5
|
const EventEmitter = require('events');
|
|
5
6
|
const { v4: uuidv4 } = require('uuid');
|
|
6
|
-
const OS = { B: 0, A: 1 };
|
|
7
|
-
const SS = { W: 1, M: 2, S: 3, VS: 4, E: 5 };
|
|
8
7
|
|
|
8
|
+
const OS = { B: 0, A: 1 }; // Order Side: Buy, Ask(Sell)
|
|
9
|
+
const SS = { W: 1, M: 2, S: 3, VS: 4, E: 5 }; // Signal Strength
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* VWAP Calculator - Real-time tick-based
|
|
13
|
+
*/
|
|
9
14
|
class VC {
|
|
10
|
-
constructor() {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
constructor() {
|
|
16
|
+
this.data = new Map();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
getKey(c) {
|
|
20
|
+
return `${c}_${new Date().toISOString().split('T')[0]}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
init(c) {
|
|
24
|
+
const k = this.getKey(c);
|
|
25
|
+
if (!this.data.has(k)) {
|
|
26
|
+
this.data.set(k, {
|
|
27
|
+
vwap: 0,
|
|
28
|
+
cumVolume: 0,
|
|
29
|
+
cumTPV: 0, // cumulative typical price * volume
|
|
30
|
+
stdDev: 0,
|
|
31
|
+
prices: [],
|
|
32
|
+
high: -Infinity,
|
|
33
|
+
low: Infinity,
|
|
34
|
+
tickCount: 0
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Process each tick
|
|
40
|
+
addTick(c, price, volume = 1) {
|
|
41
|
+
const k = this.getKey(c);
|
|
42
|
+
let d = this.data.get(k);
|
|
43
|
+
if (!d) {
|
|
44
|
+
this.init(c);
|
|
45
|
+
d = this.data.get(k);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Update VWAP
|
|
49
|
+
d.cumVolume += volume;
|
|
50
|
+
d.cumTPV += price * volume;
|
|
51
|
+
if (d.cumVolume > 0) {
|
|
52
|
+
d.vwap = d.cumTPV / d.cumVolume;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Track prices for std dev (last 100)
|
|
56
|
+
d.prices.push(price);
|
|
57
|
+
if (d.prices.length > 100) d.prices.shift();
|
|
58
|
+
|
|
59
|
+
// Calculate standard deviation
|
|
60
|
+
if (d.prices.length >= 10) {
|
|
61
|
+
const mean = d.prices.reduce((a, b) => a + b, 0) / d.prices.length;
|
|
62
|
+
const variance = d.prices.reduce((s, p) => s + Math.pow(p - mean, 2), 0) / d.prices.length;
|
|
63
|
+
d.stdDev = Math.sqrt(variance);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Update high/low
|
|
67
|
+
if (price > d.high) d.high = price;
|
|
68
|
+
if (price < d.low) d.low = price;
|
|
69
|
+
d.tickCount++;
|
|
70
|
+
|
|
71
|
+
return d;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Analyze current price vs VWAP
|
|
75
|
+
analyze(c, currentPrice, tickSize = 0.25) {
|
|
76
|
+
const k = this.getKey(c);
|
|
77
|
+
const d = this.data.get(k);
|
|
78
|
+
if (!d || d.vwap === 0) return null;
|
|
79
|
+
|
|
80
|
+
const deviation = currentPrice - d.vwap;
|
|
81
|
+
const zScore = d.stdDev > 0 ? deviation / d.stdDev : 0;
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
vwap: d.vwap,
|
|
85
|
+
price: currentPrice,
|
|
86
|
+
deviation,
|
|
87
|
+
zScore,
|
|
88
|
+
stdDev: d.stdDev,
|
|
89
|
+
high: d.high,
|
|
90
|
+
low: d.low,
|
|
91
|
+
tickCount: d.tickCount
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
get(c) {
|
|
96
|
+
return this.data.get(this.getKey(c)) || null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
reset(c) {
|
|
100
|
+
this.data.delete(this.getKey(c));
|
|
101
|
+
}
|
|
18
102
|
}
|
|
19
103
|
|
|
104
|
+
/**
|
|
105
|
+
* S1 - HQX Ultra Scalping Strategy
|
|
106
|
+
* Pure tick-based, no bar conversion
|
|
107
|
+
*/
|
|
20
108
|
class S1 extends EventEmitter {
|
|
21
|
-
constructor(cfg = {}) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
109
|
+
constructor(cfg = {}) {
|
|
110
|
+
super();
|
|
111
|
+
|
|
112
|
+
// Config
|
|
113
|
+
this.tickSize = cfg.tickSize || 0.25;
|
|
114
|
+
this.minRR = cfg.minRR || 1.2; // Minimum risk/reward
|
|
115
|
+
this.cooldown = cfg.cooldown || 15000; // 15s between signals
|
|
116
|
+
this.minTicks = cfg.minTicks || 20; // Min ticks before first signal
|
|
117
|
+
|
|
118
|
+
// State
|
|
119
|
+
this.vc = new VC();
|
|
120
|
+
this.ticks = new Map(); // contractId -> tick history
|
|
121
|
+
this.lastSignalTime = 0;
|
|
122
|
+
this.inPosition = false;
|
|
123
|
+
|
|
124
|
+
// Order flow tracking
|
|
125
|
+
this.buyVolume = 0;
|
|
126
|
+
this.sellVolume = 0;
|
|
127
|
+
this.lastPrice = 0;
|
|
128
|
+
|
|
129
|
+
// Stats
|
|
130
|
+
this.stats = { signals: 0, trades: 0, wins: 0, losses: 0, pnl: 0 };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
initialize(contractId, tickSize = 0.25) {
|
|
134
|
+
this.tickSize = tickSize;
|
|
135
|
+
this.ticks.set(contractId, []);
|
|
136
|
+
this.vc.init(contractId);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Process incoming tick - MAIN ENTRY POINT
|
|
141
|
+
*/
|
|
142
|
+
processTick(tick) {
|
|
143
|
+
const { contractId, price, bid, ask, volume = 1, side, timestamp } = tick;
|
|
144
|
+
|
|
145
|
+
if (!price || price <= 0) return null;
|
|
146
|
+
|
|
147
|
+
// Store tick
|
|
148
|
+
let tickHistory = this.ticks.get(contractId);
|
|
149
|
+
if (!tickHistory) {
|
|
150
|
+
tickHistory = [];
|
|
151
|
+
this.ticks.set(contractId, tickHistory);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const tickData = {
|
|
155
|
+
price,
|
|
156
|
+
bid: bid || price,
|
|
157
|
+
ask: ask || price,
|
|
158
|
+
volume,
|
|
159
|
+
side: side || (price >= this.lastPrice ? 'buy' : 'sell'),
|
|
160
|
+
timestamp: timestamp || Date.now()
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
tickHistory.push(tickData);
|
|
164
|
+
if (tickHistory.length > 500) tickHistory.shift();
|
|
165
|
+
|
|
166
|
+
// Update VWAP
|
|
167
|
+
this.vc.addTick(contractId, price, volume);
|
|
168
|
+
|
|
169
|
+
// Update order flow
|
|
170
|
+
if (tickData.side === 'buy') {
|
|
171
|
+
this.buyVolume += volume;
|
|
172
|
+
} else {
|
|
173
|
+
this.sellVolume += volume;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
this.lastPrice = price;
|
|
177
|
+
|
|
178
|
+
// Check for signal
|
|
179
|
+
return this.checkSignal(contractId, price, tickHistory);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Check if conditions are met for a signal
|
|
184
|
+
*/
|
|
185
|
+
checkSignal(contractId, price, ticks) {
|
|
186
|
+
// Need minimum ticks
|
|
187
|
+
if (ticks.length < this.minTicks) return null;
|
|
188
|
+
|
|
189
|
+
// Cooldown check
|
|
190
|
+
if (Date.now() - this.lastSignalTime < this.cooldown) return null;
|
|
191
|
+
|
|
192
|
+
// Don't signal if already in position
|
|
193
|
+
if (this.inPosition) return null;
|
|
194
|
+
|
|
195
|
+
// Get VWAP analysis
|
|
196
|
+
const vwap = this.vc.analyze(contractId, price, this.tickSize);
|
|
197
|
+
if (!vwap || vwap.tickCount < this.minTicks) return null;
|
|
198
|
+
|
|
199
|
+
// Calculate order flow imbalance
|
|
200
|
+
const totalVolume = this.buyVolume + this.sellVolume;
|
|
201
|
+
const imbalance = totalVolume > 0 ? (this.buyVolume - this.sellVolume) / totalVolume : 0;
|
|
202
|
+
|
|
203
|
+
// Calculate momentum (last 10 ticks)
|
|
204
|
+
const recentTicks = ticks.slice(-10);
|
|
205
|
+
const momentum = recentTicks.length >= 2
|
|
206
|
+
? recentTicks[recentTicks.length - 1].price - recentTicks[0].price
|
|
207
|
+
: 0;
|
|
208
|
+
|
|
209
|
+
// Signal logic
|
|
210
|
+
let direction = null;
|
|
211
|
+
let confidence = 0;
|
|
212
|
+
|
|
213
|
+
// LONG conditions: Price below VWAP + buying pressure
|
|
214
|
+
if (vwap.zScore < -1.0 && imbalance > 0.1) {
|
|
215
|
+
direction = 'long';
|
|
216
|
+
confidence = Math.min(0.9, 0.5 + Math.abs(vwap.zScore) * 0.15 + imbalance * 0.3);
|
|
217
|
+
}
|
|
218
|
+
// SHORT conditions: Price above VWAP + selling pressure
|
|
219
|
+
else if (vwap.zScore > 1.0 && imbalance < -0.1) {
|
|
220
|
+
direction = 'short';
|
|
221
|
+
confidence = Math.min(0.9, 0.5 + Math.abs(vwap.zScore) * 0.15 + Math.abs(imbalance) * 0.3);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (!direction || confidence < 0.55) return null;
|
|
225
|
+
|
|
226
|
+
// Calculate SL/TP
|
|
227
|
+
const stopTicks = 6;
|
|
228
|
+
const targetTicks = 8;
|
|
229
|
+
|
|
230
|
+
let stopLoss, takeProfit;
|
|
231
|
+
if (direction === 'long') {
|
|
232
|
+
stopLoss = price - stopTicks * this.tickSize;
|
|
233
|
+
takeProfit = price + targetTicks * this.tickSize;
|
|
234
|
+
} else {
|
|
235
|
+
stopLoss = price + stopTicks * this.tickSize;
|
|
236
|
+
takeProfit = price - targetTicks * this.tickSize;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Check R:R
|
|
240
|
+
const risk = Math.abs(price - stopLoss);
|
|
241
|
+
const reward = Math.abs(takeProfit - price);
|
|
242
|
+
if (reward / risk < this.minRR) return null;
|
|
243
|
+
|
|
244
|
+
// Generate signal
|
|
245
|
+
this.lastSignalTime = Date.now();
|
|
246
|
+
this.stats.signals++;
|
|
247
|
+
|
|
248
|
+
const signal = {
|
|
249
|
+
id: uuidv4(),
|
|
250
|
+
timestamp: Date.now(),
|
|
251
|
+
contractId,
|
|
252
|
+
direction,
|
|
253
|
+
side: direction === 'long' ? OS.B : OS.A,
|
|
254
|
+
entry: price,
|
|
255
|
+
entryPrice: price,
|
|
256
|
+
stopLoss,
|
|
257
|
+
takeProfit,
|
|
258
|
+
stopTicks,
|
|
259
|
+
targetTicks,
|
|
260
|
+
confidence,
|
|
261
|
+
zScore: vwap.zScore,
|
|
262
|
+
vwap: vwap.vwap,
|
|
263
|
+
imbalance,
|
|
264
|
+
momentum,
|
|
265
|
+
riskReward: reward / risk
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
this.emit('signal', signal);
|
|
269
|
+
return signal;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Called when entering a position
|
|
274
|
+
*/
|
|
275
|
+
onPositionOpened() {
|
|
276
|
+
this.inPosition = true;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Called when position is closed
|
|
281
|
+
*/
|
|
282
|
+
onPositionClosed(pnl) {
|
|
283
|
+
this.inPosition = false;
|
|
284
|
+
this.recordTradeResult(pnl);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Record trade result
|
|
289
|
+
*/
|
|
290
|
+
recordTradeResult(pnl) {
|
|
291
|
+
this.stats.trades++;
|
|
292
|
+
this.stats.pnl += pnl;
|
|
293
|
+
if (pnl > 0) {
|
|
294
|
+
this.stats.wins++;
|
|
295
|
+
} else {
|
|
296
|
+
this.stats.losses++;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Reset order flow after trade
|
|
300
|
+
this.buyVolume = 0;
|
|
301
|
+
this.sellVolume = 0;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
getStats() {
|
|
305
|
+
return this.stats;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
reset(contractId) {
|
|
309
|
+
this.ticks.set(contractId, []);
|
|
310
|
+
this.vc.reset(contractId);
|
|
311
|
+
this.buyVolume = 0;
|
|
312
|
+
this.sellVolume = 0;
|
|
313
|
+
this.lastSignalTime = 0;
|
|
314
|
+
this.inPosition = false;
|
|
315
|
+
}
|
|
45
316
|
}
|
|
46
317
|
|
|
318
|
+
/**
|
|
319
|
+
* M1 - Main Strategy Interface
|
|
320
|
+
*/
|
|
47
321
|
class M1 extends EventEmitter {
|
|
48
|
-
constructor(cfg = {}) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
322
|
+
constructor(cfg = {}) {
|
|
323
|
+
super();
|
|
324
|
+
this.cfg = cfg;
|
|
325
|
+
this.s = new S1(cfg);
|
|
326
|
+
|
|
327
|
+
// Forward signals
|
|
328
|
+
this.s.on('signal', (sig) => {
|
|
329
|
+
this.emit('signal', {
|
|
330
|
+
...sig,
|
|
331
|
+
side: sig.direction === 'long' ? 'buy' : 'sell',
|
|
332
|
+
action: 'open',
|
|
333
|
+
reason: `Z=${sig.zScore.toFixed(2)}, conf=${(sig.confidence * 100).toFixed(0)}%`
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
processTick(t) {
|
|
339
|
+
return this.s.processTick(t);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
onTick(t) {
|
|
343
|
+
return this.processTick(t);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
initialize(contractId, tickSize) {
|
|
347
|
+
this.s.initialize(contractId, tickSize);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
onPositionOpened() {
|
|
351
|
+
this.s.onPositionOpened();
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
onPositionClosed(pnl) {
|
|
355
|
+
this.s.onPositionClosed(pnl);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
recordTradeResult(pnl) {
|
|
359
|
+
this.s.recordTradeResult(pnl);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
getStats() {
|
|
363
|
+
return this.s.getStats();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
reset(contractId) {
|
|
367
|
+
this.s.reset(contractId);
|
|
368
|
+
this.emit('log', { type: 'info', message: 'Strategy reset' });
|
|
369
|
+
}
|
|
59
370
|
}
|
|
60
371
|
|
|
61
372
|
module.exports = { S1, M1, VC, OS, SS };
|
|
@@ -313,7 +313,8 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
313
313
|
// Connect to market data
|
|
314
314
|
try {
|
|
315
315
|
const token = service.token || service.getToken?.();
|
|
316
|
-
|
|
316
|
+
const propfirmKey = (account.propfirm || 'topstep').toLowerCase().replace(/\s+/g, '_');
|
|
317
|
+
await marketFeed.connect(token, propfirmKey, contractId);
|
|
317
318
|
await marketFeed.subscribe(symbolName, contractId);
|
|
318
319
|
} catch (e) {
|
|
319
320
|
ui.addLog('error', `Failed to connect: ${e.message}`);
|