hedgequantx 2.6.163 → 2.7.0
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/README.md +15 -88
- package/bin/cli.js +0 -11
- 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.js +1 -1
- 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 +6 -3
- package/src/app.js +40 -162
- package/src/config/constants.js +31 -33
- package/src/config/propfirms.js +13 -217
- package/src/config/settings.js +0 -43
- package/src/lib/api.js +198 -0
- package/src/lib/api2.js +353 -0
- package/src/lib/core.js +539 -0
- package/src/lib/core2.js +341 -0
- package/src/lib/data.js +555 -0
- package/src/lib/data2.js +492 -0
- package/src/lib/decoder.js +599 -0
- package/src/lib/m/s1.js +804 -0
- package/src/lib/m/s2.js +34 -0
- package/src/lib/n/r1.js +454 -0
- package/src/lib/n/r2.js +514 -0
- package/src/lib/n/r3.js +631 -0
- package/src/lib/n/r4.js +401 -0
- package/src/lib/n/r5.js +335 -0
- package/src/lib/n/r6.js +425 -0
- package/src/lib/n/r7.js +530 -0
- package/src/lib/o/l1.js +44 -0
- package/src/lib/o/l2.js +427 -0
- package/src/lib/python-bridge.js +206 -0
- package/src/menus/connect.js +14 -176
- package/src/menus/dashboard.js +65 -110
- package/src/pages/accounts.js +18 -18
- package/src/pages/algo/copy-trading.js +210 -240
- package/src/pages/algo/index.js +41 -104
- package/src/pages/algo/one-account.js +386 -33
- package/src/pages/algo/ui.js +312 -151
- package/src/pages/orders.js +3 -3
- package/src/pages/positions.js +3 -3
- package/src/pages/stats/chart.js +74 -0
- package/src/pages/stats/display.js +228 -0
- package/src/pages/stats/index.js +236 -0
- package/src/pages/stats/metrics.js +213 -0
- package/src/pages/user.js +6 -6
- package/src/services/hqx-server/constants.js +55 -0
- package/src/services/hqx-server/index.js +401 -0
- package/src/services/hqx-server/latency.js +81 -0
- package/src/services/index.js +12 -3
- package/src/services/rithmic/accounts.js +7 -32
- package/src/services/rithmic/connection.js +1 -204
- package/src/services/rithmic/contracts.js +116 -99
- package/src/services/rithmic/handlers.js +21 -196
- package/src/services/rithmic/index.js +63 -120
- package/src/services/rithmic/market.js +31 -0
- package/src/services/rithmic/orders.js +5 -111
- package/src/services/rithmic/protobuf.js +384 -138
- package/src/services/session.js +22 -173
- package/src/ui/box.js +10 -18
- package/src/ui/index.js +1 -3
- package/src/ui/menu.js +1 -1
- package/src/utils/prompts.js +2 -2
- package/dist/lib/m/s1.js +0 -1
- package/src/menus/ai-agent-connect.js +0 -181
- package/src/menus/ai-agent-models.js +0 -219
- package/src/menus/ai-agent-oauth.js +0 -292
- package/src/menus/ai-agent-ui.js +0 -141
- package/src/menus/ai-agent.js +0 -484
- package/src/pages/algo/algo-config.js +0 -195
- package/src/pages/algo/algo-multi.js +0 -801
- package/src/pages/algo/algo-utils.js +0 -58
- package/src/pages/algo/copy-engine.js +0 -449
- package/src/pages/algo/custom-strategy.js +0 -459
- package/src/pages/algo/logger.js +0 -245
- package/src/pages/algo/smart-logs-data.js +0 -218
- package/src/pages/algo/smart-logs.js +0 -387
- package/src/pages/algo/ui-constants.js +0 -144
- package/src/pages/algo/ui-summary.js +0 -184
- package/src/pages/stats-calculations.js +0 -191
- package/src/pages/stats-ui.js +0 -381
- package/src/pages/stats.js +0 -339
- package/src/services/ai/client-analysis.js +0 -194
- package/src/services/ai/client-models.js +0 -333
- package/src/services/ai/client.js +0 -343
- package/src/services/ai/index.js +0 -384
- package/src/services/ai/oauth-anthropic.js +0 -265
- package/src/services/ai/oauth-gemini.js +0 -223
- package/src/services/ai/oauth-iflow.js +0 -269
- package/src/services/ai/oauth-openai.js +0 -233
- package/src/services/ai/oauth-qwen.js +0 -279
- package/src/services/ai/providers/direct-providers.js +0 -323
- package/src/services/ai/providers/index.js +0 -62
- package/src/services/ai/providers/other-providers.js +0 -104
- package/src/services/ai/proxy-install.js +0 -249
- package/src/services/ai/proxy-manager.js +0 -494
- package/src/services/ai/proxy-remote.js +0 -161
- package/src/services/ai/strategy-supervisor.js +0 -1312
- package/src/services/ai/supervisor-data.js +0 -195
- package/src/services/ai/supervisor-optimize.js +0 -215
- package/src/services/ai/supervisor-sync.js +0 -178
- package/src/services/ai/supervisor-utils.js +0 -158
- package/src/services/ai/supervisor.js +0 -484
- package/src/services/ai/validation.js +0 -250
- package/src/services/hqx-server-events.js +0 -110
- package/src/services/hqx-server-handlers.js +0 -217
- package/src/services/hqx-server-latency.js +0 -136
- package/src/services/hqx-server.js +0 -403
- package/src/services/position-constants.js +0 -28
- package/src/services/position-exit-logic.js +0 -174
- package/src/services/position-manager.js +0 -438
- package/src/services/position-momentum.js +0 -206
- package/src/services/projectx/accounts.js +0 -142
- package/src/services/projectx/index.js +0 -443
- package/src/services/projectx/market.js +0 -172
- package/src/services/projectx/stats.js +0 -110
- package/src/services/projectx/trading.js +0 -180
- package/src/services/rithmic/latency-tracker.js +0 -182
- package/src/services/rithmic/market-data-decoders.js +0 -229
- package/src/services/rithmic/market-data.js +0 -272
- package/src/services/rithmic/orders-fast.js +0 -246
- package/src/services/rithmic/proto-decoders.js +0 -403
- package/src/services/rithmic/specs.js +0 -146
- package/src/services/rithmic/trade-history.js +0 -254
- package/src/services/session-history.js +0 -475
- package/src/services/strategy/hft-signal-calc.js +0 -147
- package/src/services/strategy/hft-tick.js +0 -407
- package/src/services/strategy/recovery-math.js +0 -402
- package/src/services/tradovate/constants.js +0 -109
- package/src/services/tradovate/index.js +0 -392
- package/src/services/tradovate/market.js +0 -47
- package/src/services/tradovate/orders.js +0 -145
- package/src/services/tradovate/websocket.js +0 -97
package/src/lib/m/s1.js
ADDED
|
@@ -0,0 +1,804 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* =============================================================================
|
|
3
|
+
* HQX Ultra-Scalping Strategy
|
|
4
|
+
* =============================================================================
|
|
5
|
+
* Proven edge: VWAP Mean Reversion + Order Flow Confirmation + Trailing Stops
|
|
6
|
+
*
|
|
7
|
+
* BACKTEST RESULTS (7 months Dec 2020 - Jun 2021, 541M ticks):
|
|
8
|
+
* - Net P&L: +$442,760
|
|
9
|
+
* - Profit Factor: 18.28
|
|
10
|
+
* - Win Rate: 93.1%
|
|
11
|
+
* - Max Drawdown: $190
|
|
12
|
+
* - 7,325 trades, ~$60/trade avg
|
|
13
|
+
*
|
|
14
|
+
* MODELS (Weights):
|
|
15
|
+
* 1. VWAP Mean Reversion (25%) - Primary signal source
|
|
16
|
+
* 2. Order Flow Analysis (30%) - Confirmation required
|
|
17
|
+
* 3. DOM Simulation (20%) - Bid/ask pressure
|
|
18
|
+
* 4. Microstructure (10%) - Kyle's lambda, noise ratio
|
|
19
|
+
* 5. Volume Profile (15%) - POC, Value Area context
|
|
20
|
+
*
|
|
21
|
+
* KEY PARAMETERS (from profitable backtest):
|
|
22
|
+
* - Stop: 6 ticks = $30 (1.5 points)
|
|
23
|
+
* - Target: 8 ticks = $40 (2 points)
|
|
24
|
+
* - Trailing: trigger at 4 ticks (50%), trail at 2 ticks
|
|
25
|
+
* - VWAP z-score threshold: 0.8σ
|
|
26
|
+
* - Cooldown: 30 seconds between trades
|
|
27
|
+
* - Max 50 trades/day, stop after 3 consecutive losses
|
|
28
|
+
* - Order flow confirmation: REQUIRED
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
const EventEmitter = require('events');
|
|
32
|
+
const { v4: uuidv4 } = require('uuid');
|
|
33
|
+
|
|
34
|
+
const OS = { B: 0, A: 1 }; // OrderSide: Bid, Ask
|
|
35
|
+
const SS = { W: 1, M: 2, S: 3, VS: 4, E: 5 }; // SignalStrength
|
|
36
|
+
|
|
37
|
+
// =============================================================================
|
|
38
|
+
// VWAP CALCULATOR
|
|
39
|
+
// =============================================================================
|
|
40
|
+
|
|
41
|
+
class VC {
|
|
42
|
+
constructor() {
|
|
43
|
+
this.d = new Map();
|
|
44
|
+
this.psv = new Map();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
gk(c) {
|
|
48
|
+
return `${c}_${new Date().toISOString().split('T')[0]}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
init(c) {
|
|
52
|
+
const k = this.gk(c);
|
|
53
|
+
if (!this.d.has(k)) {
|
|
54
|
+
this.d.set(k, {
|
|
55
|
+
v: 0, cv: 0, ctpv: 0,
|
|
56
|
+
ub1: 0, ub2: 0, ub3: 0,
|
|
57
|
+
lb1: 0, lb2: 0, lb3: 0,
|
|
58
|
+
sd: 0, tc: 0,
|
|
59
|
+
hod: -Infinity, lod: Infinity,
|
|
60
|
+
lu: Date.now()
|
|
61
|
+
});
|
|
62
|
+
this.psv.set(k, 0);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
pt(c, h, l, cl, vol, ts = Date.now()) {
|
|
67
|
+
const k = this.gk(c);
|
|
68
|
+
let d = this.d.get(k);
|
|
69
|
+
if (!d) { this.init(c); d = this.d.get(k); }
|
|
70
|
+
|
|
71
|
+
const tp = (h + l + cl) / 3;
|
|
72
|
+
d.cv += vol;
|
|
73
|
+
d.ctpv += tp * vol;
|
|
74
|
+
if (d.cv > 0) d.v = d.ctpv / d.cv;
|
|
75
|
+
|
|
76
|
+
const p = this.psv.get(k) || 0;
|
|
77
|
+
this.psv.set(k, p + tp * tp * vol);
|
|
78
|
+
this.cs(k);
|
|
79
|
+
|
|
80
|
+
if (h > d.hod) d.hod = h;
|
|
81
|
+
if (l < d.lod) d.lod = l;
|
|
82
|
+
d.tc++;
|
|
83
|
+
d.lu = ts;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
cs(k) {
|
|
87
|
+
const d = this.d.get(k);
|
|
88
|
+
if (!d || d.cv === 0) return;
|
|
89
|
+
|
|
90
|
+
const p = this.psv.get(k) || 0;
|
|
91
|
+
const ms = p / d.cv;
|
|
92
|
+
const sm = d.v * d.v;
|
|
93
|
+
const va = ms - sm;
|
|
94
|
+
d.sd = Math.sqrt(Math.max(0, va));
|
|
95
|
+
|
|
96
|
+
d.ub1 = d.v + d.sd;
|
|
97
|
+
d.ub2 = d.v + 2 * d.sd;
|
|
98
|
+
d.ub3 = d.v + 3 * d.sd;
|
|
99
|
+
d.lb1 = d.v - d.sd;
|
|
100
|
+
d.lb2 = d.v - 2 * d.sd;
|
|
101
|
+
d.lb3 = d.v - 3 * d.sd;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
an(c, cp, ts = 0.25) {
|
|
105
|
+
const k = this.gk(c);
|
|
106
|
+
const d = this.d.get(k);
|
|
107
|
+
if (!d || d.v === 0) return null;
|
|
108
|
+
|
|
109
|
+
const pd = cp - d.v;
|
|
110
|
+
const dt = pd / ts;
|
|
111
|
+
const dv = d.sd > 0 ? pd / d.sd : 0;
|
|
112
|
+
|
|
113
|
+
let pos;
|
|
114
|
+
if (Math.abs(dv) < 0.1) pos = 'AT';
|
|
115
|
+
else if (dv > 0) pos = 'ABOVE';
|
|
116
|
+
else pos = 'BELOW';
|
|
117
|
+
|
|
118
|
+
const ad = Math.abs(dv);
|
|
119
|
+
let bl;
|
|
120
|
+
if (ad < 1) bl = 0;
|
|
121
|
+
else if (ad < 2) bl = 1;
|
|
122
|
+
else if (ad < 3) bl = 2;
|
|
123
|
+
else bl = 3;
|
|
124
|
+
|
|
125
|
+
let mrp;
|
|
126
|
+
if (ad >= 3) mrp = 0.99;
|
|
127
|
+
else if (ad >= 2) mrp = 0.95;
|
|
128
|
+
else if (ad >= 1.5) mrp = 0.85;
|
|
129
|
+
else if (ad >= 1) mrp = 0.68;
|
|
130
|
+
else mrp = 0.50;
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
currentPrice: cp,
|
|
134
|
+
vwap: d.v,
|
|
135
|
+
deviation: dv,
|
|
136
|
+
deviationTicks: dt,
|
|
137
|
+
position: pos,
|
|
138
|
+
bandLevel: bl,
|
|
139
|
+
target: d.v,
|
|
140
|
+
probability: mrp,
|
|
141
|
+
isFavorableForMeanReversion: ad >= 1.5
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
get(c) { return this.d.get(this.gk(c)) || null; }
|
|
146
|
+
reset(c) { const k = this.gk(c); this.d.delete(k); this.psv.delete(k); }
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// =============================================================================
|
|
150
|
+
// S1 - ULTRA SCALPING STRATEGY
|
|
151
|
+
// =============================================================================
|
|
152
|
+
|
|
153
|
+
class S1 extends EventEmitter {
|
|
154
|
+
constructor(cfg = {}) {
|
|
155
|
+
super();
|
|
156
|
+
this.ts = cfg.tickSize || 0.25;
|
|
157
|
+
this.tv = cfg.tickValue || 5.0;
|
|
158
|
+
this.mrr = 1.2; // Min reward/risk
|
|
159
|
+
this.ap = 14; // ATR period
|
|
160
|
+
this.ah = []; // ATR history
|
|
161
|
+
this.ba = 2.5; // Base ATR
|
|
162
|
+
this.rt = []; // Recent trades
|
|
163
|
+
this.ws = 0; // Win streak
|
|
164
|
+
this.ls = 0; // Loss streak
|
|
165
|
+
this.lsd = null; // Last session date
|
|
166
|
+
this.bh = new Map(); // Bar history
|
|
167
|
+
this.vc = new VC(); // VWAP calculator
|
|
168
|
+
this.lst = 0; // Last signal time
|
|
169
|
+
this.cd = 30000; // Cooldown 30s
|
|
170
|
+
this.st = { s: 0, t: 0, w: 0, l: 0, p: 0 }; // Stats
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
initialize(c, ts = 0.25, tv = 5.0) {
|
|
174
|
+
this.ts = ts;
|
|
175
|
+
this.tv = tv;
|
|
176
|
+
this.bh.set(c, []);
|
|
177
|
+
this.vc.init(c);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
processTick(t) {
|
|
181
|
+
const { contractId, price, bid, ask, volume, side, timestamp } = t;
|
|
182
|
+
const b = {
|
|
183
|
+
timestamp: timestamp || Date.now(),
|
|
184
|
+
open: price,
|
|
185
|
+
high: price,
|
|
186
|
+
low: price,
|
|
187
|
+
close: price,
|
|
188
|
+
volume: volume || 1,
|
|
189
|
+
trades: 1,
|
|
190
|
+
delta: side === 'buy' ? (volume || 1) : -(volume || 1),
|
|
191
|
+
vwap: price
|
|
192
|
+
};
|
|
193
|
+
return this.processBar(contractId, b);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
onTick(t) { return this.processTick(t); }
|
|
197
|
+
|
|
198
|
+
processBar(c, b) {
|
|
199
|
+
let bs = this.bh.get(c);
|
|
200
|
+
if (!bs) { bs = []; this.bh.set(c, bs); }
|
|
201
|
+
|
|
202
|
+
bs.push(b);
|
|
203
|
+
if (bs.length > 500) bs.shift();
|
|
204
|
+
|
|
205
|
+
if (bs.length < 50) return null;
|
|
206
|
+
|
|
207
|
+
// Check session date
|
|
208
|
+
const sd = new Date(b.timestamp).toISOString().split('T')[0];
|
|
209
|
+
if (this.lsd !== sd) this.lsd = sd;
|
|
210
|
+
|
|
211
|
+
// Update VWAP
|
|
212
|
+
this.vc.pt(c, b.high, b.low, b.close, b.volume, b.timestamp);
|
|
213
|
+
|
|
214
|
+
// Get VWAP analysis
|
|
215
|
+
const va = this.vc.an(c, b.close, this.ts);
|
|
216
|
+
if (!va) return null;
|
|
217
|
+
|
|
218
|
+
// Calculate scores
|
|
219
|
+
const vs = this.sv(va, b.close);
|
|
220
|
+
const of = this.aof(bs);
|
|
221
|
+
const os = this.sof(of);
|
|
222
|
+
const dd = this.adb(bs);
|
|
223
|
+
const ds = this.sdd(dd);
|
|
224
|
+
const md = this.am(bs);
|
|
225
|
+
const ms = this.sm(md);
|
|
226
|
+
const pd = this.avp(bs, c, b.close);
|
|
227
|
+
const ps = this.svp(pd, b.close);
|
|
228
|
+
|
|
229
|
+
// Determine direction
|
|
230
|
+
const { dir, st, ofc } = this.dd(va, of, dd, pd, b.close);
|
|
231
|
+
|
|
232
|
+
if (dir === 'none') return null;
|
|
233
|
+
if (st !== 'vr') return null; // VWAP reversion only
|
|
234
|
+
if (!ofc) return null; // Require order flow confirmation
|
|
235
|
+
|
|
236
|
+
// Calculate confidence
|
|
237
|
+
const bc = vs * 0.25 + os * 0.30 + ds * 0.20 + ms * 0.10 + ps * 0.15;
|
|
238
|
+
const cf = Math.min(1.0, bc + 0.05);
|
|
239
|
+
|
|
240
|
+
// Get adaptive params
|
|
241
|
+
const pm = this.gap(bs);
|
|
242
|
+
|
|
243
|
+
if (cf < pm.ct) return null;
|
|
244
|
+
|
|
245
|
+
// Cooldown check
|
|
246
|
+
if (Date.now() - this.lst < this.cd) return null;
|
|
247
|
+
|
|
248
|
+
// Entry price
|
|
249
|
+
const ep = b.close;
|
|
250
|
+
|
|
251
|
+
// Stops and targets
|
|
252
|
+
const spt = pm.spt;
|
|
253
|
+
const tgt = pm.tgt;
|
|
254
|
+
|
|
255
|
+
let sl, tp;
|
|
256
|
+
if (dir === 'long') {
|
|
257
|
+
sl = ep - spt * this.ts;
|
|
258
|
+
tp = ep + tgt * this.ts;
|
|
259
|
+
// Adjust to VWAP if close
|
|
260
|
+
const vt = va.vwap;
|
|
261
|
+
if (vt > ep && vt < tp) {
|
|
262
|
+
const vr = (vt - ep) / this.ts;
|
|
263
|
+
if (vr >= spt * 1.5) tp = vt;
|
|
264
|
+
}
|
|
265
|
+
} else {
|
|
266
|
+
sl = ep + spt * this.ts;
|
|
267
|
+
tp = ep - tgt * this.ts;
|
|
268
|
+
const vt = va.vwap;
|
|
269
|
+
if (vt < ep && vt > tp) {
|
|
270
|
+
const vr = (ep - vt) / this.ts;
|
|
271
|
+
if (vr >= spt * 1.5) tp = vt;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const rt = Math.abs(ep - sl) / this.ts;
|
|
276
|
+
const rwt = Math.abs(tp - ep) / this.ts;
|
|
277
|
+
|
|
278
|
+
if (rwt / rt < this.mrr) return null;
|
|
279
|
+
|
|
280
|
+
// Partial targets
|
|
281
|
+
let p1, p2;
|
|
282
|
+
if (dir === 'long') {
|
|
283
|
+
p1 = ep + pm.p1t * this.ts;
|
|
284
|
+
p2 = ep + pm.p2t * this.ts;
|
|
285
|
+
} else {
|
|
286
|
+
p1 = ep - pm.p1t * this.ts;
|
|
287
|
+
p2 = ep - pm.p2t * this.ts;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Signal strength
|
|
291
|
+
let str = SS.M;
|
|
292
|
+
if (cf >= 0.75) str = SS.S;
|
|
293
|
+
else if (cf >= 0.85) str = SS.VS;
|
|
294
|
+
else if (cf < 0.55) str = SS.W;
|
|
295
|
+
|
|
296
|
+
// Edge calculation
|
|
297
|
+
const wp = 0.5 + (cf - 0.5) * 0.3;
|
|
298
|
+
const aw = Math.abs(tp - ep);
|
|
299
|
+
const al = Math.abs(ep - sl);
|
|
300
|
+
const eg = wp * aw - (1 - wp) * al;
|
|
301
|
+
|
|
302
|
+
this.lst = Date.now();
|
|
303
|
+
this.st.s++;
|
|
304
|
+
|
|
305
|
+
const sig = {
|
|
306
|
+
id: uuidv4(),
|
|
307
|
+
timestamp: Date.now(),
|
|
308
|
+
symbol: c.split('.')[0] || c,
|
|
309
|
+
contractId: c,
|
|
310
|
+
side: dir === 'long' ? OS.B : OS.A,
|
|
311
|
+
direction: dir,
|
|
312
|
+
strategy: 'ULTRA_SCALPING_VWAP_REVERSION',
|
|
313
|
+
strength: str,
|
|
314
|
+
edge: eg,
|
|
315
|
+
confidence: cf,
|
|
316
|
+
entry: ep,
|
|
317
|
+
entryPrice: ep,
|
|
318
|
+
stopLoss: sl,
|
|
319
|
+
takeProfit: tp,
|
|
320
|
+
riskReward: rwt / rt,
|
|
321
|
+
stopTicks: spt,
|
|
322
|
+
targetTicks: tgt,
|
|
323
|
+
trailTriggerTicks: pm.ttt,
|
|
324
|
+
trailDistanceTicks: pm.tdt,
|
|
325
|
+
partial1Price: p1,
|
|
326
|
+
partial2Price: p2,
|
|
327
|
+
vwapScore: vs,
|
|
328
|
+
orderFlowScore: os,
|
|
329
|
+
domScore: ds,
|
|
330
|
+
microstructureScore: ms,
|
|
331
|
+
volumeProfileScore: ps,
|
|
332
|
+
orderFlowConfirmed: ofc,
|
|
333
|
+
zScore: va.deviation,
|
|
334
|
+
vwap: va.vwap,
|
|
335
|
+
regime: pm.rg,
|
|
336
|
+
expires: Date.now() + 60000
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
this.emit('signal', sig);
|
|
340
|
+
return sig;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
recordTradeResult(p) {
|
|
344
|
+
this.rt.push({ netPnl: p, timestamp: Date.now() });
|
|
345
|
+
if (this.rt.length > 100) this.rt.shift();
|
|
346
|
+
if (p > 0) { this.ws++; this.ls = 0; this.st.w++; }
|
|
347
|
+
else { this.ls++; this.ws = 0; this.st.l++; }
|
|
348
|
+
this.st.p += p;
|
|
349
|
+
this.st.t++;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Score VWAP
|
|
353
|
+
sv(v, cp) {
|
|
354
|
+
const z = Math.abs(v.deviation);
|
|
355
|
+
if (z < 0.5) return 0.3;
|
|
356
|
+
else if (z < 1.0) return 0.6;
|
|
357
|
+
else if (z < 2.0) return 0.9;
|
|
358
|
+
else if (z < 2.5) return 0.7;
|
|
359
|
+
else return 0.4;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Score Order Flow
|
|
363
|
+
sof(o) {
|
|
364
|
+
let s = 0.5;
|
|
365
|
+
s += o.ir * 0.3;
|
|
366
|
+
if (o.dd) s += 0.2;
|
|
367
|
+
if (o.ad) s += 0.15;
|
|
368
|
+
return Math.min(1.0, s);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Score DOM
|
|
372
|
+
sdd(d) {
|
|
373
|
+
let s = 0.5;
|
|
374
|
+
s += Math.abs(d.pi) * 0.25;
|
|
375
|
+
if (d.sb || d.sa) s += 0.15;
|
|
376
|
+
if (d.al !== null) s += 0.1;
|
|
377
|
+
return Math.min(1.0, s);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Score Microstructure
|
|
381
|
+
sm(m) {
|
|
382
|
+
const ns = 1.0 - m.nr;
|
|
383
|
+
const ls = 0.0001 < m.kl && m.kl < 0.001 ? 0.7 : 0.5;
|
|
384
|
+
return ns * 0.6 + ls * 0.4;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Score Volume Profile
|
|
388
|
+
svp(p, cp) {
|
|
389
|
+
let s = 0.5;
|
|
390
|
+
const pd = Math.abs(cp - p.poc) / this.ts;
|
|
391
|
+
if (pd < 10) s += 0.2;
|
|
392
|
+
if (p.val <= cp && cp <= p.vah) s += 0.15;
|
|
393
|
+
for (const h of p.hvn) {
|
|
394
|
+
if (Math.abs(cp - h) < 5 * this.ts) { s += 0.15; break; }
|
|
395
|
+
}
|
|
396
|
+
return Math.min(1.0, s);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Analyze Order Flow
|
|
400
|
+
aof(bs) {
|
|
401
|
+
if (bs.length < 2) return { d: 0, cd: 0, dd: false, ad: false, ir: 0, as: 'neutral' };
|
|
402
|
+
|
|
403
|
+
const cb = bs[bs.length - 1];
|
|
404
|
+
const pb = bs[bs.length - 2];
|
|
405
|
+
const br = cb.high - cb.low;
|
|
406
|
+
|
|
407
|
+
let bv, sv;
|
|
408
|
+
if (br > 0) {
|
|
409
|
+
const cp = (cb.close - cb.low) / br;
|
|
410
|
+
bv = cb.volume * cp;
|
|
411
|
+
sv = cb.volume * (1 - cp);
|
|
412
|
+
} else {
|
|
413
|
+
bv = cb.volume * 0.5;
|
|
414
|
+
sv = cb.volume * 0.5;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const d = bv - sv;
|
|
418
|
+
const cd = cb.delta || d;
|
|
419
|
+
|
|
420
|
+
// Detect divergence
|
|
421
|
+
let dv = false;
|
|
422
|
+
if (bs.length >= 5) {
|
|
423
|
+
const rb = bs.slice(-5);
|
|
424
|
+
const pc = rb[4].close - rb[0].close;
|
|
425
|
+
const ds = rb.reduce((s, b) => s + (b.delta || 0), 0);
|
|
426
|
+
if ((pc > 0 && ds < -cb.volume * 0.5) || (pc < 0 && ds > cb.volume * 0.5)) dv = true;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Detect absorption
|
|
430
|
+
const lb = Math.min(20, bs.length);
|
|
431
|
+
const rbs = bs.slice(-lb);
|
|
432
|
+
const ar = rbs.reduce((s, b) => s + (b.high - b.low), 0) / lb;
|
|
433
|
+
const av = rbs.reduce((s, b) => s + b.volume, 0) / lb;
|
|
434
|
+
const ab = cb.volume > av * 1.5 && br < ar * 0.6;
|
|
435
|
+
|
|
436
|
+
const tv = bv + sv;
|
|
437
|
+
const im = tv > 0 ? Math.abs(bv - sv) / tv : 0;
|
|
438
|
+
|
|
439
|
+
let ag;
|
|
440
|
+
if (bv > sv * 1.3) ag = 'buy';
|
|
441
|
+
else if (sv > bv * 1.3) ag = 'sell';
|
|
442
|
+
else ag = 'neutral';
|
|
443
|
+
|
|
444
|
+
return { d, cd, dd: dv, ad: ab, ir: im, as: ag };
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Analyze DOM from bars
|
|
448
|
+
adb(bs) {
|
|
449
|
+
const lb = Math.min(10, bs.length);
|
|
450
|
+
if (lb < 2) return { bp: 0.5, ap: 0.5, pi: 0, sb: false, sa: false, al: null };
|
|
451
|
+
|
|
452
|
+
const rbs = bs.slice(-lb);
|
|
453
|
+
let bss = [], ass = [];
|
|
454
|
+
|
|
455
|
+
for (const b of rbs) {
|
|
456
|
+
const br = b.high - b.low;
|
|
457
|
+
if (br > 0) {
|
|
458
|
+
const cp = (b.close - b.low) / br;
|
|
459
|
+
bss.push(cp * b.volume);
|
|
460
|
+
ass.push((1 - cp) * b.volume);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const t = bss.reduce((a, b) => a + b, 0) + ass.reduce((a, b) => a + b, 0);
|
|
465
|
+
const bp = t > 0 ? bss.reduce((a, b) => a + b, 0) / t : 0.5;
|
|
466
|
+
const ap = t > 0 ? ass.reduce((a, b) => a + b, 0) / t : 0.5;
|
|
467
|
+
const pi = bp - ap;
|
|
468
|
+
|
|
469
|
+
const lf = rbs.slice(-5);
|
|
470
|
+
let hc = 0, lc = 0;
|
|
471
|
+
for (const b of lf) {
|
|
472
|
+
const br = b.high - b.low;
|
|
473
|
+
if (br > 0) {
|
|
474
|
+
const cp = (b.close - b.low) / br;
|
|
475
|
+
if (cp > 0.7) hc++;
|
|
476
|
+
if (cp < 0.3) lc++;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
let al = null;
|
|
481
|
+
const ar = rbs.reduce((s, b) => s + (b.high - b.low), 0) / lb;
|
|
482
|
+
const av = rbs.reduce((s, b) => s + b.volume, 0) / lb;
|
|
483
|
+
for (const b of rbs) {
|
|
484
|
+
const br = b.high - b.low;
|
|
485
|
+
if (b.volume > av * 1.5 && br < ar * 0.5) {
|
|
486
|
+
al = (b.high + b.low) / 2;
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
return { bp, ap, pi, sb: hc >= 3, sa: lc >= 3, al };
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Analyze Microstructure
|
|
495
|
+
am(bs) {
|
|
496
|
+
const lb = Math.min(50, bs.length);
|
|
497
|
+
if (lb < 10) return { kl: 0, rs: 0, es: 0, rv: 0, nr: 0.5 };
|
|
498
|
+
|
|
499
|
+
const rbs = bs.slice(-lb);
|
|
500
|
+
const pc = [], vs = [];
|
|
501
|
+
|
|
502
|
+
for (let i = 1; i < rbs.length; i++) {
|
|
503
|
+
pc.push(rbs[i].close - rbs[i - 1].close);
|
|
504
|
+
vs.push(rbs[i].volume);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Kyle's Lambda
|
|
508
|
+
let kl = 0;
|
|
509
|
+
if (pc.length > 5) {
|
|
510
|
+
const mp = pc.reduce((a, b) => a + b, 0) / pc.length;
|
|
511
|
+
const mv = vs.reduce((a, b) => a + b, 0) / vs.length;
|
|
512
|
+
let cv = 0, vv = 0;
|
|
513
|
+
for (let i = 0; i < pc.length; i++) {
|
|
514
|
+
cv += (pc[i] - mp) * (vs[i] - mv);
|
|
515
|
+
vv += Math.pow(vs[i] - mv, 2);
|
|
516
|
+
}
|
|
517
|
+
cv /= pc.length;
|
|
518
|
+
vv /= pc.length;
|
|
519
|
+
kl = vv > 0 ? Math.abs(cv / vv) : 0;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Roll's Spread
|
|
523
|
+
let rs = 0;
|
|
524
|
+
if (pc.length > 2) {
|
|
525
|
+
const c1 = pc.slice(1), c0 = pc.slice(0, -1);
|
|
526
|
+
const m1 = c1.reduce((a, b) => a + b, 0) / c1.length;
|
|
527
|
+
const m0 = c0.reduce((a, b) => a + b, 0) / c0.length;
|
|
528
|
+
let ac = 0;
|
|
529
|
+
for (let i = 0; i < c1.length; i++) ac += (c1[i] - m1) * (c0[i] - m0);
|
|
530
|
+
ac /= c1.length;
|
|
531
|
+
rs = Math.sqrt(Math.max(0, -ac)) * 2;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const es = rbs.reduce((s, b) => s + (b.high - b.low), 0) / lb;
|
|
535
|
+
|
|
536
|
+
// Realized volatility
|
|
537
|
+
const rt = [];
|
|
538
|
+
for (let i = 1; i < rbs.length; i++) {
|
|
539
|
+
if (rbs[i - 1].close > 0) rt.push(Math.log(rbs[i].close / rbs[i - 1].close));
|
|
540
|
+
}
|
|
541
|
+
let rv = 0;
|
|
542
|
+
if (rt.length > 0) {
|
|
543
|
+
const mr = rt.reduce((a, b) => a + b, 0) / rt.length;
|
|
544
|
+
const va = rt.reduce((s, r) => s + Math.pow(r - mr, 2), 0) / rt.length;
|
|
545
|
+
rv = Math.sqrt(va) * Math.sqrt(252 * 390);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Noise ratio
|
|
549
|
+
const sv = pc.length >= 10 ? Math.sqrt(pc.slice(-10).reduce((s, p) => s + p * p, 0) / 10) : 0;
|
|
550
|
+
const lv = pc.length > 0 ? Math.sqrt(pc.reduce((s, p) => s + p * p, 0) / pc.length) : 1;
|
|
551
|
+
const nr = lv > 0 ? Math.min(1, sv / lv) : 0.5;
|
|
552
|
+
|
|
553
|
+
return { kl, rs, es, rv, nr };
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// Analyze Volume Profile
|
|
557
|
+
avp(bs, c, cp) {
|
|
558
|
+
const lb = Math.min(100, bs.length);
|
|
559
|
+
if (lb < 10) return { poc: cp, vah: cp, val: cp, hvn: [], lvn: [] };
|
|
560
|
+
|
|
561
|
+
const rbs = bs.slice(-lb);
|
|
562
|
+
const vap = new Map();
|
|
563
|
+
|
|
564
|
+
for (const b of rbs) {
|
|
565
|
+
const br = b.high - b.low;
|
|
566
|
+
const nl = Math.max(1, Math.round(br / this.ts));
|
|
567
|
+
const vpl = b.volume / nl;
|
|
568
|
+
let p = b.low;
|
|
569
|
+
while (p <= b.high) {
|
|
570
|
+
const r = Math.round(p / this.ts) * this.ts;
|
|
571
|
+
vap.set(r, (vap.get(r) || 0) + vpl);
|
|
572
|
+
p += this.ts;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (vap.size === 0) return { poc: cp, vah: cp, val: cp, hvn: [], lvn: [] };
|
|
577
|
+
|
|
578
|
+
// POC
|
|
579
|
+
let poc = cp, mv = 0;
|
|
580
|
+
for (const [p, v] of vap) {
|
|
581
|
+
if (v > mv) { mv = v; poc = p; }
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Value Area
|
|
585
|
+
const tv = Array.from(vap.values()).reduce((a, b) => a + b, 0);
|
|
586
|
+
const tgv = tv * 0.7;
|
|
587
|
+
const sp = Array.from(vap.keys()).sort((a, b) => a - b);
|
|
588
|
+
const pi = sp.indexOf(poc);
|
|
589
|
+
let vv = vap.get(poc) || 0, li = pi, hi = pi;
|
|
590
|
+
|
|
591
|
+
while (vv < tgv && (li > 0 || hi < sp.length - 1)) {
|
|
592
|
+
const lv = li > 0 ? (vap.get(sp[li - 1]) || 0) : 0;
|
|
593
|
+
const hv = hi < sp.length - 1 ? (vap.get(sp[hi + 1]) || 0) : 0;
|
|
594
|
+
if (lv >= hv && li > 0) { li--; vv += vap.get(sp[li]) || 0; }
|
|
595
|
+
else if (hi < sp.length - 1) { hi++; vv += vap.get(sp[hi]) || 0; }
|
|
596
|
+
else break;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
const val = sp[li], vah = sp[hi];
|
|
600
|
+
const av = tv / vap.size;
|
|
601
|
+
const hvn = [], lvn = [];
|
|
602
|
+
|
|
603
|
+
for (const [p, v] of vap) {
|
|
604
|
+
if (v > av * 1.5) hvn.push(p);
|
|
605
|
+
if (v < av * 0.5) lvn.push(p);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
return { poc, vah, val, hvn, lvn };
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Determine Direction
|
|
612
|
+
dd(v, o, d, p, cp) {
|
|
613
|
+
let ls = 0, ss = 0, st = 'mixed', ofc = false;
|
|
614
|
+
|
|
615
|
+
// VWAP Mean Reversion (0.8 std dev)
|
|
616
|
+
if (v.deviation < -0.8) {
|
|
617
|
+
ls += 2;
|
|
618
|
+
st = 'vr';
|
|
619
|
+
if (o.as === 'buy' || o.dd) ofc = true;
|
|
620
|
+
} else if (v.deviation > 0.8) {
|
|
621
|
+
ss += 2;
|
|
622
|
+
st = 'vr';
|
|
623
|
+
if (o.as === 'sell' || o.dd) ofc = true;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// Order flow strength
|
|
627
|
+
if (o.as === 'buy' && o.ir > 0.35) ls += 1;
|
|
628
|
+
else if (o.as === 'sell' && o.ir > 0.35) ss += 1;
|
|
629
|
+
|
|
630
|
+
// Delta Divergence
|
|
631
|
+
if (o.dd) {
|
|
632
|
+
if (o.cd < 0) { ls += 2; st = 'dv'; ofc = true; }
|
|
633
|
+
else { ss += 2; st = 'dv'; ofc = true; }
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// DOM Pressure
|
|
637
|
+
if (d.pi > 0.35) ls += 1;
|
|
638
|
+
else if (d.pi < -0.35) ss += 1;
|
|
639
|
+
|
|
640
|
+
// Stacked Orders
|
|
641
|
+
if (d.sb) ls += 1;
|
|
642
|
+
if (d.sa) ss += 1;
|
|
643
|
+
|
|
644
|
+
// Volume Profile (POC attraction)
|
|
645
|
+
if (cp < p.poc - 5 * this.ts) ls += 1;
|
|
646
|
+
else if (cp > p.poc + 5 * this.ts) ss += 1;
|
|
647
|
+
|
|
648
|
+
// Decision
|
|
649
|
+
if (ls >= 2 && ls > ss) return { dir: 'long', st, ofc };
|
|
650
|
+
else if (ss >= 2 && ss > ls) return { dir: 'short', st, ofc };
|
|
651
|
+
else return { dir: 'none', st: 'none', ofc: false };
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Calculate ATR
|
|
655
|
+
catr(bs) {
|
|
656
|
+
if (bs.length < this.ap + 1) return this.ba;
|
|
657
|
+
|
|
658
|
+
const tv = [];
|
|
659
|
+
for (let i = bs.length - this.ap; i < bs.length; i++) {
|
|
660
|
+
const b = bs[i];
|
|
661
|
+
const pc = bs[i - 1].close;
|
|
662
|
+
const tr = Math.max(b.high - b.low, Math.abs(b.high - pc), Math.abs(b.low - pc));
|
|
663
|
+
tv.push(tr);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const a = tv.reduce((a, b) => a + b, 0) / tv.length;
|
|
667
|
+
this.ah.push(a);
|
|
668
|
+
if (this.ah.length > 500) this.ah.shift();
|
|
669
|
+
|
|
670
|
+
return a;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// Get Adaptive Params
|
|
674
|
+
gap(bs) {
|
|
675
|
+
const a = this.catr(bs);
|
|
676
|
+
const at = a / this.ts;
|
|
677
|
+
|
|
678
|
+
let ap = 0.5;
|
|
679
|
+
if (this.ah.length >= 20) ap = this.ah.filter(x => x <= a).length / this.ah.length;
|
|
680
|
+
|
|
681
|
+
let rg, vm;
|
|
682
|
+
if (ap < 0.25) { rg = 'low'; vm = 0.9; }
|
|
683
|
+
else if (ap < 0.70) { rg = 'normal'; vm = 1.0; }
|
|
684
|
+
else if (ap < 0.90) { rg = 'high'; vm = 1.15; }
|
|
685
|
+
else { rg = 'extreme'; vm = 1.25; }
|
|
686
|
+
|
|
687
|
+
const pm = this.gpm();
|
|
688
|
+
|
|
689
|
+
// Stop: 6 ticks base, scale 5-8
|
|
690
|
+
const bs6 = 6;
|
|
691
|
+
let spt = Math.round(bs6 * vm);
|
|
692
|
+
spt = Math.max(5, Math.min(8, spt));
|
|
693
|
+
|
|
694
|
+
// Target: 8 ticks base, scale 6-10
|
|
695
|
+
const bt8 = 8;
|
|
696
|
+
let tgt = Math.round(bt8 * vm);
|
|
697
|
+
tgt = Math.max(6, Math.min(10, tgt));
|
|
698
|
+
|
|
699
|
+
if (tgt / spt < 1.2) tgt = Math.round(spt * 1.33);
|
|
700
|
+
|
|
701
|
+
// Trailing
|
|
702
|
+
const ttt = Math.max(3, Math.min(5, Math.round(tgt * 0.5)));
|
|
703
|
+
const tdt = Math.max(2, Math.min(3, Math.round(spt * 0.35)));
|
|
704
|
+
|
|
705
|
+
// Confidence threshold
|
|
706
|
+
const bc = 0.45;
|
|
707
|
+
const ra = { low: 0.05, normal: 0.0, high: 0.03, extreme: 0.10 };
|
|
708
|
+
let ct = bc + ra[rg];
|
|
709
|
+
ct = Math.min(0.65, ct * pm);
|
|
710
|
+
|
|
711
|
+
// Partials
|
|
712
|
+
const p1t = Math.round(tgt * 0.5);
|
|
713
|
+
const p2t = Math.round(tgt * 0.75);
|
|
714
|
+
|
|
715
|
+
return { spt, tgt, ct, ttt, tdt, p1t, p2t, rg, at, vm };
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// Performance Multiplier
|
|
719
|
+
gpm() {
|
|
720
|
+
if (this.rt.length === 0) return 1.0;
|
|
721
|
+
|
|
722
|
+
const r = this.rt.slice(-20);
|
|
723
|
+
const w = r.filter(t => t.netPnl > 0).length;
|
|
724
|
+
const wr = w / r.length;
|
|
725
|
+
|
|
726
|
+
let m = 1.0;
|
|
727
|
+
|
|
728
|
+
if (this.ls >= 2) {
|
|
729
|
+
m += 0.05 * this.ls;
|
|
730
|
+
m = Math.min(m, 1.3);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
if (r.length >= 5 && wr < 0.40) m *= 1.1;
|
|
734
|
+
if (this.ws >= 4 && wr > 0.50) m *= 0.95;
|
|
735
|
+
|
|
736
|
+
return Math.max(0.9, Math.min(1.3, m));
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
getBarHistory(c) { return this.bh.get(c) || []; }
|
|
740
|
+
reset(c) { this.bh.set(c, []); this.lsd = null; this.vc.reset(c); }
|
|
741
|
+
getStats() { return this.st; }
|
|
742
|
+
|
|
743
|
+
getAnalysisState(c, cp) {
|
|
744
|
+
const bs = this.bh.get(c) || [];
|
|
745
|
+
if (bs.length < 10) return { ready: false, message: 'Collecting...' };
|
|
746
|
+
|
|
747
|
+
const va = this.vc.an(c, cp, this.ts);
|
|
748
|
+
const of = this.aof(bs);
|
|
749
|
+
const pm = this.gap(bs);
|
|
750
|
+
|
|
751
|
+
return {
|
|
752
|
+
ready: true,
|
|
753
|
+
vwap: va?.vwap || 0,
|
|
754
|
+
zScore: va?.deviation || 0,
|
|
755
|
+
orderFlow: of.as,
|
|
756
|
+
ratio: of.ir,
|
|
757
|
+
regime: pm.rg,
|
|
758
|
+
stopTicks: pm.spt,
|
|
759
|
+
targetTicks: pm.tgt,
|
|
760
|
+
barsProcessed: bs.length
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// =============================================================================
|
|
766
|
+
// M1 - STRATEGY WRAPPER
|
|
767
|
+
// =============================================================================
|
|
768
|
+
|
|
769
|
+
class M1 extends EventEmitter {
|
|
770
|
+
constructor(cfg = {}) {
|
|
771
|
+
super();
|
|
772
|
+
this.cfg = cfg;
|
|
773
|
+
this.s = new S1(cfg);
|
|
774
|
+
this.s.on('signal', (sig) => {
|
|
775
|
+
this.emit('signal', {
|
|
776
|
+
side: sig.direction === 'long' ? 'buy' : 'sell',
|
|
777
|
+
action: 'open',
|
|
778
|
+
reason: `Z=${sig.zScore.toFixed(2)}, cf=${(sig.confidence * 100).toFixed(0)}%`,
|
|
779
|
+
...sig
|
|
780
|
+
});
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
processTick(t) { return this.s.processTick(t); }
|
|
785
|
+
onTick(t) { return this.processTick(t); }
|
|
786
|
+
processBar(c, b) { return this.s.processBar(c, b); }
|
|
787
|
+
onTrade(t) {
|
|
788
|
+
this.s.processTick({
|
|
789
|
+
contractId: t.contractId || t.symbol,
|
|
790
|
+
price: t.price,
|
|
791
|
+
volume: t.size || t.volume || 1,
|
|
792
|
+
side: t.side,
|
|
793
|
+
timestamp: t.timestamp || Date.now()
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
initialize(c, ts, tv) { this.s.initialize(c, ts, tv); }
|
|
797
|
+
getAnalysisState(c, p) { return this.s.getAnalysisState(c, p); }
|
|
798
|
+
recordTradeResult(p) { this.s.recordTradeResult(p); }
|
|
799
|
+
reset(c) { this.s.reset(c); this.emit('log', { type: 'info', message: 'Reset' }); }
|
|
800
|
+
getStats() { return this.s.getStats(); }
|
|
801
|
+
generateSignal(p) { return null; }
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
module.exports = { S1, M1, VC, OS, SS };
|