hedgequantx 2.4.6 → 2.4.7

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/src/lib/m/s1.js DELETED
@@ -1,372 +0,0 @@
1
- /**
2
- * HQX Ultra Scalping Strategy
3
- * Tick-based real-time scalping
4
- */
5
- const EventEmitter = require('events');
6
- const { v4: uuidv4 } = require('uuid');
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
- */
14
- class VC {
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
- }
102
- }
103
-
104
- /**
105
- * S1 - HQX Ultra Scalping Strategy
106
- * Pure tick-based, no bar conversion
107
- */
108
- class S1 extends EventEmitter {
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
- }
316
- }
317
-
318
- /**
319
- * M1 - Main Strategy Interface
320
- */
321
- class M1 extends EventEmitter {
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
- }
370
- }
371
-
372
- module.exports = { S1, M1, VC, OS, SS };
package/src/lib/m/s2.js DELETED
@@ -1,34 +0,0 @@
1
- /**
2
- * M2 Module
3
- */
4
- const EventEmitter = require('events');
5
- const DP = { et: 0.6, vrng: [0.8, 2.5], cth: 0.45, mxl: 3, stk: 6, tgt: 8, ctr: 1, roc: true, mht: 10000, cd: 30000 };
6
-
7
- class S2 extends EventEmitter {
8
- constructor(cfg = {}) { super(); this.p = { ...DP, ...cfg }; this.ts = cfg.tickSize || 0.25; this.cid = null; this.bh = []; this.cd = { v: 0, cv: 0, tpv: 0, sd: 0 }; this.of = { bd: 0, ad: 0, lt: null }; this.st = { s: 0, t: 0, w: 0, l: 0, pnl: 0 }; this.lst = 0; this.lss = 0; }
9
- // tickSize for tick calculations, P&L comes from API
10
- initialize(c, ts = 0.25) { this.cid = c; this.ts = ts; this.bh = []; this.cd = { v: 0, cv: 0, tpv: 0, sd: 0 }; this.of = { bd: 0, ad: 0, lt: null }; }
11
- processTick(t) { const { price, volume, side, timestamp } = t; this.bh.push({ p: price, v: volume || 1, s: side, t: timestamp || Date.now() }); if (this.bh.length > 500) this.bh.shift(); const tp = price; this.cd.cv += volume || 1; this.cd.tpv += tp * (volume || 1); if (this.cd.cv > 0) this.cd.v = this.cd.tpv / this.cd.cv; if (side === 'buy') this.of.bd += volume || 1; else if (side === 'sell') this.of.ad += volume || 1; this.of.lt = side; }
12
- onTick(t) { return this.processTick(t); }
13
- onTrade(t) { this.processTick({ price: t.price, volume: t.size || t.volume || 1, side: t.side, timestamp: t.timestamp }); }
14
- generateSignal(cp) { if (this.bh.length < 50) return null; if (Date.now() - this.lst < this.p.cd) return null; if (this.lss >= this.p.mxl) return null; const v = this.cd.v; if (v <= 0) return null; const sd = this.csd(); if (sd <= 0) return null; const dv = (cp - v) / sd; const ad = Math.abs(dv); if (ad < this.p.vrng[0] || ad > this.p.vrng[1]) return null; let dir = null; if (dv < -this.p.et) dir = 'long'; else if (dv > this.p.et) dir = 'short'; if (!dir) return null; if (this.p.roc) { const ofc = this.cof(dir); if (!ofc) return null; } const cf = Math.min(1.0, 0.5 + ad * 0.15); if (cf < this.p.cth) return null; this.lst = Date.now(); this.st.s++; return { direction: dir, confidence: cf, zScore: dv, stopTicks: this.p.stk, targetTicks: this.p.tgt }; }
15
- csd() { if (this.bh.length < 20) return 0; const ps = this.bh.slice(-100).map(b => b.p); const m = ps.reduce((a, b) => a + b, 0) / ps.length; const va = ps.reduce((s, p) => s + Math.pow(p - m, 2), 0) / ps.length; return Math.sqrt(va); }
16
- cof(dir) { const tb = this.of.bd + this.of.ad; if (tb < 10) return true; const br = this.of.bd / tb; if (dir === 'long' && br > 0.45) return true; if (dir === 'short' && br < 0.55) return true; if (this.of.lt === 'buy' && dir === 'long') return true; if (this.of.lt === 'sell' && dir === 'short') return true; return false; }
17
- recordTradeResult(pnl) { this.st.t++; this.st.pnl += pnl; if (pnl > 0) { this.st.w++; this.lss = 0; } else { this.st.l++; this.lss++; } }
18
- getStats() { return this.st; }
19
- reset() { this.bh = []; this.cd = { v: 0, cv: 0, tpv: 0, sd: 0 }; this.of = { bd: 0, ad: 0, lt: null }; this.lss = 0; }
20
- }
21
-
22
- class M2 extends EventEmitter {
23
- constructor(cfg = {}) { super(); this.s = new S2(cfg); }
24
- initialize(c, ts, tv) { this.s.initialize(c, ts, tv); }
25
- processTick(t) { this.s.processTick(t); }
26
- onTick(t) { this.s.onTick(t); const sig = this.s.generateSignal(t.price); if (sig) this.emit('signal', { side: sig.direction === 'long' ? 'buy' : 'sell', action: 'open', ...sig }); }
27
- onTrade(t) { this.s.onTrade(t); }
28
- generateSignal(p) { return this.s.generateSignal(p); }
29
- recordTradeResult(p) { this.s.recordTradeResult(p); }
30
- getStats() { return this.s.getStats(); }
31
- reset() { this.s.reset(); this.emit('log', { type: 'info', message: 'Reset' }); }
32
- }
33
-
34
- module.exports = { S2, M2, DP };