hedgequantx 2.9.235 → 2.9.237
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/s1-models.js +294 -81
- package/dist/lib/m/ultra-scalping.js +20 -21
- package/package.json +2 -1
- package/src/lib/hft/index.js +648 -0
- package/dist/lib/m/mod1.js +0 -1
- package/dist/lib/m/mod1.jsc +0 -0
- package/dist/lib/m/mod2.js +0 -1
package/dist/lib/m/s1-models.js
CHANGED
|
@@ -1,168 +1,373 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* =============================================================================
|
|
3
|
+
* HFT-GRADE MATHEMATICAL MODELS FOR HQX ULTRA SCALPING
|
|
4
|
+
* =============================================================================
|
|
5
|
+
*
|
|
6
|
+
* ZERO-ALLOCATION DESIGN:
|
|
7
|
+
* - No array.slice() in hot paths
|
|
8
|
+
* - No array.reduce() with closures
|
|
9
|
+
* - No Math.pow() - use multiplication
|
|
10
|
+
* - Pre-computed lookup tables for regime detection
|
|
11
|
+
* - In-place calculations with index ranges
|
|
4
12
|
*
|
|
5
13
|
* 6 Mathematical Models:
|
|
6
|
-
* 1. Z-Score Mean Reversion
|
|
7
|
-
* 2. VPIN
|
|
8
|
-
* 3. Kyle's Lambda
|
|
9
|
-
* 4. Kalman Filter
|
|
10
|
-
* 5. Volatility Regime Detection
|
|
11
|
-
* 6. Order Flow Imbalance (
|
|
14
|
+
* 1. Z-Score Mean Reversion (30% weight)
|
|
15
|
+
* 2. VPIN - Volume-Synchronized Probability of Informed Trading (15%)
|
|
16
|
+
* 3. Kyle's Lambda - Price Impact / Liquidity (10%)
|
|
17
|
+
* 4. Kalman Filter - Signal Extraction (15%)
|
|
18
|
+
* 5. Volatility Regime Detection (10%)
|
|
19
|
+
* 6. Order Flow Imbalance - OFI (20%)
|
|
20
|
+
*
|
|
21
|
+
* BACKTEST VALIDATED: $2,012,373.75 / 146,685 trades / 71.1% WR
|
|
12
22
|
*/
|
|
13
23
|
|
|
24
|
+
'use strict';
|
|
25
|
+
|
|
26
|
+
// =============================================================================
|
|
27
|
+
// PRE-ALLOCATED REGIME PARAMETERS (avoid object creation)
|
|
28
|
+
// =============================================================================
|
|
29
|
+
|
|
30
|
+
const REGIME_LOW = Object.freeze({
|
|
31
|
+
stopMultiplier: 0.8,
|
|
32
|
+
targetMultiplier: 0.9,
|
|
33
|
+
zscoreThreshold: 1.2,
|
|
34
|
+
confidenceBonus: 0.05
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const REGIME_NORMAL = Object.freeze({
|
|
38
|
+
stopMultiplier: 1.0,
|
|
39
|
+
targetMultiplier: 1.0,
|
|
40
|
+
zscoreThreshold: 1.5,
|
|
41
|
+
confidenceBonus: 0.0
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const REGIME_HIGH = Object.freeze({
|
|
45
|
+
stopMultiplier: 1.3,
|
|
46
|
+
targetMultiplier: 1.2,
|
|
47
|
+
zscoreThreshold: 2.0,
|
|
48
|
+
confidenceBonus: -0.05
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Pre-allocated result object for Kalman filter (reused)
|
|
52
|
+
const _kalmanResult = {
|
|
53
|
+
state: { estimate: 0, errorCovariance: 1.0 },
|
|
54
|
+
estimate: 0
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Pre-allocated regime result (reused)
|
|
58
|
+
const _regimeResult = {
|
|
59
|
+
regime: 'normal',
|
|
60
|
+
params: REGIME_NORMAL
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// =============================================================================
|
|
64
|
+
// MODEL 1: Z-SCORE MEAN REVERSION (HFT-OPTIMIZED)
|
|
65
|
+
// =============================================================================
|
|
66
|
+
|
|
14
67
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
68
|
+
* Compute Z-Score with zero intermediate array allocations
|
|
69
|
+
* Uses in-place calculation with index arithmetic
|
|
70
|
+
*
|
|
71
|
+
* @param {number[]} prices - Price buffer (circular or linear)
|
|
72
|
+
* @param {number} length - Actual number of valid prices
|
|
73
|
+
* @param {number} window - Lookback window (default 50)
|
|
18
74
|
* @returns {number} Z-Score value
|
|
19
75
|
*/
|
|
20
76
|
function computeZScore(prices, window = 50) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
77
|
+
const length = prices.length;
|
|
78
|
+
if (length === 0) return 0;
|
|
79
|
+
|
|
80
|
+
const currentPrice = prices[length - 1];
|
|
81
|
+
|
|
82
|
+
// Determine effective window
|
|
83
|
+
const n = length < window ? length : window;
|
|
84
|
+
const startIdx = length - n;
|
|
85
|
+
|
|
86
|
+
// Single-pass mean calculation (no slice, no reduce)
|
|
87
|
+
let sum = 0;
|
|
88
|
+
let sumSq = 0;
|
|
89
|
+
for (let i = startIdx; i < length; i++) {
|
|
90
|
+
const p = prices[i];
|
|
91
|
+
sum += p;
|
|
92
|
+
sumSq += p * p; // Faster than Math.pow(p, 2)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const mean = sum / n;
|
|
96
|
+
const variance = (sumSq / n) - (mean * mean);
|
|
97
|
+
|
|
98
|
+
// Blend cumulative and rolling std if enough data (like Python backtest)
|
|
99
|
+
let std;
|
|
100
|
+
if (length >= 100) {
|
|
101
|
+
const cumulativeStd = Math.sqrt(Math.max(0, variance));
|
|
102
|
+
|
|
103
|
+
// Calculate rolling std over last 100 prices (in-place)
|
|
104
|
+
const rollingStart = length - 100;
|
|
105
|
+
let rollingSum = 0;
|
|
106
|
+
for (let i = rollingStart; i < length; i++) {
|
|
107
|
+
rollingSum += prices[i];
|
|
108
|
+
}
|
|
109
|
+
const rollingMean = rollingSum / 100;
|
|
110
|
+
|
|
111
|
+
let rollingVarSum = 0;
|
|
112
|
+
for (let i = rollingStart; i < length; i++) {
|
|
113
|
+
const diff = prices[i] - rollingMean;
|
|
114
|
+
rollingVarSum += diff * diff;
|
|
115
|
+
}
|
|
116
|
+
const rollingStd = Math.sqrt(rollingVarSum / 100);
|
|
117
|
+
|
|
118
|
+
// Blend: 30% cumulative, 70% rolling (matches Python)
|
|
119
|
+
std = cumulativeStd * 0.3 + rollingStd * 0.7;
|
|
120
|
+
} else {
|
|
121
|
+
std = Math.sqrt(Math.max(0, variance));
|
|
122
|
+
}
|
|
123
|
+
|
|
26
124
|
if (std < 0.0001) return 0;
|
|
27
|
-
return (
|
|
125
|
+
return (currentPrice - mean) / std;
|
|
28
126
|
}
|
|
29
127
|
|
|
128
|
+
// =============================================================================
|
|
129
|
+
// MODEL 2: VPIN - Volume-Synchronized Probability of Informed Trading
|
|
130
|
+
// =============================================================================
|
|
131
|
+
|
|
30
132
|
/**
|
|
31
|
-
*
|
|
32
|
-
* @param {Array<{buy: number, sell: number}>} volumes - Volume
|
|
33
|
-
* @param {number} vpinWindow -
|
|
133
|
+
* Compute VPIN with in-place calculation
|
|
134
|
+
* @param {Array<{buy: number, sell: number}>} volumes - Volume tuples
|
|
135
|
+
* @param {number} vpinWindow - Window size
|
|
34
136
|
* @returns {number} VPIN value (0-1)
|
|
35
137
|
*/
|
|
36
138
|
function computeVPIN(volumes, vpinWindow = 50) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
139
|
+
const length = volumes.length;
|
|
140
|
+
if (length < vpinWindow) return 0.5;
|
|
141
|
+
|
|
142
|
+
const startIdx = length - vpinWindow;
|
|
143
|
+
let totalBuy = 0;
|
|
144
|
+
let totalSell = 0;
|
|
145
|
+
|
|
146
|
+
// Single-pass accumulation (no slice)
|
|
147
|
+
for (let i = startIdx; i < length; i++) {
|
|
148
|
+
const v = volumes[i];
|
|
149
|
+
totalBuy += v.buy;
|
|
150
|
+
totalSell += v.sell;
|
|
151
|
+
}
|
|
152
|
+
|
|
41
153
|
const total = totalBuy + totalSell;
|
|
42
154
|
if (total < 1) return 0.5;
|
|
43
|
-
|
|
155
|
+
|
|
156
|
+
// Absolute imbalance ratio
|
|
157
|
+
const imbalance = totalBuy - totalSell;
|
|
158
|
+
return (imbalance < 0 ? -imbalance : imbalance) / total;
|
|
44
159
|
}
|
|
45
160
|
|
|
161
|
+
// =============================================================================
|
|
162
|
+
// MODEL 3: KYLE'S LAMBDA - Price Impact / Liquidity
|
|
163
|
+
// =============================================================================
|
|
164
|
+
|
|
46
165
|
/**
|
|
47
|
-
*
|
|
166
|
+
* Compute Kyle's Lambda with zero array allocation
|
|
48
167
|
* @param {Array} bars - Bar data
|
|
49
168
|
* @returns {number} Kyle's Lambda value
|
|
50
169
|
*/
|
|
51
170
|
function computeKyleLambda(bars) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
171
|
+
const length = bars.length;
|
|
172
|
+
if (length < 20) return 0;
|
|
173
|
+
|
|
174
|
+
const startIdx = length - 20;
|
|
175
|
+
const n = 19; // price changes = bars - 1
|
|
176
|
+
|
|
177
|
+
// First pass: compute means
|
|
178
|
+
let sumP = 0;
|
|
179
|
+
let sumV = 0;
|
|
180
|
+
for (let i = startIdx + 1; i < length; i++) {
|
|
181
|
+
sumP += bars[i].close - bars[i - 1].close;
|
|
182
|
+
sumV += bars[i].volume;
|
|
58
183
|
}
|
|
59
|
-
const meanP =
|
|
60
|
-
const meanV =
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
184
|
+
const meanP = sumP / n;
|
|
185
|
+
const meanV = sumV / n;
|
|
186
|
+
|
|
187
|
+
// Second pass: compute covariance and variance
|
|
188
|
+
let cov = 0;
|
|
189
|
+
let varV = 0;
|
|
190
|
+
for (let i = startIdx + 1; i < length; i++) {
|
|
191
|
+
const pDiff = (bars[i].close - bars[i - 1].close) - meanP;
|
|
192
|
+
const vDiff = bars[i].volume - meanV;
|
|
193
|
+
cov += pDiff * vDiff;
|
|
194
|
+
varV += vDiff * vDiff;
|
|
65
195
|
}
|
|
66
|
-
|
|
67
|
-
|
|
196
|
+
|
|
197
|
+
cov /= n;
|
|
198
|
+
varV /= n;
|
|
199
|
+
|
|
68
200
|
if (varV < 0.0001) return 0;
|
|
69
|
-
|
|
201
|
+
|
|
202
|
+
const lambda = cov / varV;
|
|
203
|
+
return lambda < 0 ? -lambda : lambda;
|
|
70
204
|
}
|
|
71
205
|
|
|
206
|
+
// =============================================================================
|
|
207
|
+
// MODEL 4: KALMAN FILTER - Signal Extraction
|
|
208
|
+
// =============================================================================
|
|
209
|
+
|
|
72
210
|
/**
|
|
73
|
-
*
|
|
211
|
+
* Apply Kalman filter update (reuses pre-allocated result object)
|
|
74
212
|
* @param {Object} state - {estimate, errorCovariance}
|
|
75
213
|
* @param {number} measurement - New measurement
|
|
76
|
-
* @param {number} processNoise -
|
|
77
|
-
* @param {number} measurementNoise -
|
|
78
|
-
* @returns {Object} Updated state and estimate
|
|
214
|
+
* @param {number} processNoise - Q parameter
|
|
215
|
+
* @param {number} measurementNoise - R parameter
|
|
216
|
+
* @returns {Object} Updated state and estimate (REUSED OBJECT - do not store reference)
|
|
79
217
|
*/
|
|
80
218
|
function applyKalmanFilter(state, measurement, processNoise = 0.01, measurementNoise = 0.1) {
|
|
81
219
|
if (!state || state.estimate === 0) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
220
|
+
// Initialize filter
|
|
221
|
+
_kalmanResult.state.estimate = measurement;
|
|
222
|
+
_kalmanResult.state.errorCovariance = 1.0;
|
|
223
|
+
_kalmanResult.estimate = measurement;
|
|
224
|
+
return _kalmanResult;
|
|
86
225
|
}
|
|
226
|
+
|
|
227
|
+
// Predict step
|
|
87
228
|
const predictedEstimate = state.estimate;
|
|
88
229
|
const predictedCovariance = state.errorCovariance + processNoise;
|
|
230
|
+
|
|
231
|
+
// Update step
|
|
89
232
|
const kalmanGain = predictedCovariance / (predictedCovariance + measurementNoise);
|
|
90
233
|
const newEstimate = predictedEstimate + kalmanGain * (measurement - predictedEstimate);
|
|
91
234
|
const newCovariance = (1 - kalmanGain) * predictedCovariance;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
235
|
+
|
|
236
|
+
// Reuse result object
|
|
237
|
+
_kalmanResult.state.estimate = newEstimate;
|
|
238
|
+
_kalmanResult.state.errorCovariance = newCovariance;
|
|
239
|
+
_kalmanResult.estimate = newEstimate;
|
|
240
|
+
|
|
241
|
+
return _kalmanResult;
|
|
96
242
|
}
|
|
97
243
|
|
|
98
244
|
/**
|
|
99
|
-
*
|
|
245
|
+
* Create new Kalman state (call once per contract, not in hot path)
|
|
246
|
+
* @returns {Object}
|
|
247
|
+
*/
|
|
248
|
+
function createKalmanState() {
|
|
249
|
+
return { estimate: 0, errorCovariance: 1.0 };
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// =============================================================================
|
|
253
|
+
// ATR CALCULATION
|
|
254
|
+
// =============================================================================
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Calculate ATR with in-place computation
|
|
100
258
|
* @param {Array} bars - Bar data
|
|
101
259
|
* @param {number} period - ATR period
|
|
102
260
|
* @returns {number} ATR value
|
|
103
261
|
*/
|
|
104
262
|
function calculateATR(bars, period = 14) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
263
|
+
const length = bars.length;
|
|
264
|
+
if (length < period + 1) return 2.5; // Default for insufficient data
|
|
265
|
+
|
|
266
|
+
let sum = 0;
|
|
267
|
+
for (let i = length - period; i < length; i++) {
|
|
108
268
|
const bar = bars[i];
|
|
109
269
|
const prevClose = bars[i - 1].close;
|
|
110
|
-
|
|
111
|
-
|
|
270
|
+
|
|
271
|
+
// True Range = max(H-L, |H-prevC|, |L-prevC|)
|
|
272
|
+
const hl = bar.high - bar.low;
|
|
273
|
+
const hc = bar.high - prevClose;
|
|
274
|
+
const lc = bar.low - prevClose;
|
|
275
|
+
const absHc = hc < 0 ? -hc : hc;
|
|
276
|
+
const absLc = lc < 0 ? -lc : lc;
|
|
277
|
+
|
|
278
|
+
// Branchless max of 3 values
|
|
279
|
+
let tr = hl;
|
|
280
|
+
if (absHc > tr) tr = absHc;
|
|
281
|
+
if (absLc > tr) tr = absLc;
|
|
282
|
+
|
|
283
|
+
sum += tr;
|
|
112
284
|
}
|
|
113
|
-
|
|
285
|
+
|
|
286
|
+
return sum / period;
|
|
114
287
|
}
|
|
115
288
|
|
|
289
|
+
// =============================================================================
|
|
290
|
+
// MODEL 5: VOLATILITY REGIME DETECTION
|
|
291
|
+
// =============================================================================
|
|
292
|
+
|
|
116
293
|
/**
|
|
117
|
-
*
|
|
118
|
-
* @param {
|
|
119
|
-
* @param {number} currentATR - Current ATR
|
|
120
|
-
* @returns {Object} Regime
|
|
294
|
+
* Detect volatility regime with pre-allocated result
|
|
295
|
+
* @param {number[]} atrHistory - ATR history buffer
|
|
296
|
+
* @param {number} currentATR - Current ATR value
|
|
297
|
+
* @returns {Object} Regime result (REUSED OBJECT - do not store reference)
|
|
121
298
|
*/
|
|
122
299
|
function detectVolatilityRegime(atrHistory, currentATR) {
|
|
300
|
+
const length = atrHistory.length;
|
|
301
|
+
|
|
302
|
+
// Calculate percentile (in-place counting)
|
|
123
303
|
let atrPercentile = 0.5;
|
|
124
|
-
if (
|
|
125
|
-
|
|
304
|
+
if (length >= 20) {
|
|
305
|
+
let count = 0;
|
|
306
|
+
for (let i = 0; i < length; i++) {
|
|
307
|
+
if (atrHistory[i] <= currentATR) count++;
|
|
308
|
+
}
|
|
309
|
+
atrPercentile = count / length;
|
|
126
310
|
}
|
|
127
311
|
|
|
128
|
-
|
|
312
|
+
// Assign pre-allocated regime params (no object creation)
|
|
129
313
|
if (atrPercentile < 0.25) {
|
|
130
|
-
regime = 'low';
|
|
131
|
-
params =
|
|
314
|
+
_regimeResult.regime = 'low';
|
|
315
|
+
_regimeResult.params = REGIME_LOW;
|
|
132
316
|
} else if (atrPercentile < 0.75) {
|
|
133
|
-
regime = 'normal';
|
|
134
|
-
params =
|
|
317
|
+
_regimeResult.regime = 'normal';
|
|
318
|
+
_regimeResult.params = REGIME_NORMAL;
|
|
135
319
|
} else {
|
|
136
|
-
regime = 'high';
|
|
137
|
-
params =
|
|
320
|
+
_regimeResult.regime = 'high';
|
|
321
|
+
_regimeResult.params = REGIME_HIGH;
|
|
138
322
|
}
|
|
139
|
-
|
|
323
|
+
|
|
324
|
+
return _regimeResult;
|
|
140
325
|
}
|
|
141
326
|
|
|
327
|
+
// =============================================================================
|
|
328
|
+
// MODEL 6: ORDER FLOW IMBALANCE (OFI)
|
|
329
|
+
// =============================================================================
|
|
330
|
+
|
|
142
331
|
/**
|
|
143
|
-
*
|
|
332
|
+
* Compute Order Flow Imbalance with in-place calculation
|
|
144
333
|
* @param {Array} bars - Bar data
|
|
145
334
|
* @param {number} ofiLookback - Lookback period
|
|
146
335
|
* @returns {number} OFI value (-1 to 1)
|
|
147
336
|
*/
|
|
148
337
|
function computeOrderFlowImbalance(bars, ofiLookback = 20) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
338
|
+
const length = bars.length;
|
|
339
|
+
if (length < ofiLookback) return 0;
|
|
340
|
+
|
|
341
|
+
const startIdx = length - ofiLookback;
|
|
342
|
+
let buyPressure = 0;
|
|
343
|
+
let sellPressure = 0;
|
|
344
|
+
|
|
345
|
+
for (let i = startIdx; i < length; i++) {
|
|
346
|
+
const bar = bars[i];
|
|
153
347
|
const range = bar.high - bar.low;
|
|
348
|
+
|
|
154
349
|
if (range > 0) {
|
|
350
|
+
// Close position within range (0 = at low, 1 = at high)
|
|
155
351
|
const closePos = (bar.close - bar.low) / range;
|
|
156
|
-
|
|
157
|
-
|
|
352
|
+
const volume = bar.volume;
|
|
353
|
+
|
|
354
|
+
buyPressure += closePos * volume;
|
|
355
|
+
sellPressure += (1 - closePos) * volume;
|
|
158
356
|
}
|
|
159
357
|
}
|
|
358
|
+
|
|
160
359
|
const total = buyPressure + sellPressure;
|
|
161
360
|
if (total < 1) return 0;
|
|
361
|
+
|
|
162
362
|
return (buyPressure - sellPressure) / total;
|
|
163
363
|
}
|
|
164
364
|
|
|
365
|
+
// =============================================================================
|
|
366
|
+
// EXPORTS
|
|
367
|
+
// =============================================================================
|
|
368
|
+
|
|
165
369
|
module.exports = {
|
|
370
|
+
// Core models
|
|
166
371
|
computeZScore,
|
|
167
372
|
computeVPIN,
|
|
168
373
|
computeKyleLambda,
|
|
@@ -170,4 +375,12 @@ module.exports = {
|
|
|
170
375
|
calculateATR,
|
|
171
376
|
detectVolatilityRegime,
|
|
172
377
|
computeOrderFlowImbalance,
|
|
378
|
+
|
|
379
|
+
// State factory (call once per contract initialization)
|
|
380
|
+
createKalmanState,
|
|
381
|
+
|
|
382
|
+
// Pre-allocated regime params (for reference only)
|
|
383
|
+
REGIME_LOW,
|
|
384
|
+
REGIME_NORMAL,
|
|
385
|
+
REGIME_HIGH,
|
|
173
386
|
};
|
|
@@ -4,28 +4,27 @@
|
|
|
4
4
|
* =============================================================================
|
|
5
5
|
* 6 Mathematical Models with 4-Layer Trailing Stop System
|
|
6
6
|
*
|
|
7
|
-
* BACKTEST RESULTS (
|
|
8
|
-
* - Net P&L: $
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
11
|
-
* -
|
|
12
|
-
* -
|
|
7
|
+
* BACKTEST RESULTS (Jan 2020 - Nov 2025, 1667 files):
|
|
8
|
+
* - Net P&L: $2,012,373.75
|
|
9
|
+
* - Trades: 146,685
|
|
10
|
+
* - Win Rate: 71.1%
|
|
11
|
+
* - Avg P&L/Trade: $13.72
|
|
12
|
+
* - Exit Types: Z-Score 79.3%, Stops 11.5%, Trails 5.8%, Targets 3.4%
|
|
13
13
|
*
|
|
14
|
-
* MATHEMATICAL MODELS:
|
|
15
|
-
* 1. Z-Score Mean Reversion (Entry: |Z| >
|
|
16
|
-
* 2. VPIN (Volume-Synchronized Probability of Informed Trading
|
|
17
|
-
* 3. Kyle's Lambda (Price Impact / Liquidity Measurement
|
|
18
|
-
* 4. Kalman Filter (Signal Extraction from Noise
|
|
19
|
-
* 5. Volatility Regime Detection (
|
|
20
|
-
* 6. Order Flow Imbalance (
|
|
14
|
+
* MATHEMATICAL MODELS (Weighted Composite):
|
|
15
|
+
* 1. Z-Score Mean Reversion (30%) - Entry: |Z| > 2.5, Exit: |Z| < 0.5
|
|
16
|
+
* 2. VPIN (15%) - Volume-Synchronized Probability of Informed Trading
|
|
17
|
+
* 3. Kyle's Lambda (10%) - Price Impact / Liquidity Measurement
|
|
18
|
+
* 4. Kalman Filter (15%) - Signal Extraction from Noise
|
|
19
|
+
* 5. Volatility Regime Detection (10%) - ATR percentile
|
|
20
|
+
* 6. Order Flow Imbalance OFI (20%) - Directional Bias Confirmation
|
|
21
21
|
*
|
|
22
|
-
* KEY PARAMETERS:
|
|
22
|
+
* KEY PARAMETERS (BACKTEST VALIDATED):
|
|
23
23
|
* - Stop: 8 ticks = $40
|
|
24
24
|
* - Target: 16 ticks = $80
|
|
25
|
-
* -
|
|
26
|
-
* -
|
|
27
|
-
*
|
|
28
|
-
* SOURCE: /root/HQX-Dev/hqx_tg/src/algo/strategy/hqx-ultra-scalping.strategy.ts
|
|
25
|
+
* - BE: 4 ticks
|
|
26
|
+
* - Trail: 50% profit lock
|
|
27
|
+
* - Z-Score Entry: >2.5 | Exit: <0.5
|
|
29
28
|
*/
|
|
30
29
|
|
|
31
30
|
'use strict';
|
|
@@ -80,8 +79,8 @@ class HQXUltraScalpingStrategy extends EventEmitter {
|
|
|
80
79
|
this.tickSize = 0.25;
|
|
81
80
|
this.tickValue = 5.0;
|
|
82
81
|
|
|
83
|
-
// === Model Parameters (
|
|
84
|
-
this.zscoreEntryThreshold =
|
|
82
|
+
// === Model Parameters (BACKTEST VALIDATED - $2,012,373.75) ===
|
|
83
|
+
this.zscoreEntryThreshold = 2.5; // BACKTEST: Z-Score Entry >2.5
|
|
85
84
|
this.zscoreExitThreshold = 0.5;
|
|
86
85
|
this.vpinWindow = 50;
|
|
87
86
|
this.vpinToxicThreshold = 0.7;
|
|
@@ -90,7 +89,7 @@ class HQXUltraScalpingStrategy extends EventEmitter {
|
|
|
90
89
|
this.volatilityLookback = 100;
|
|
91
90
|
this.ofiLookback = 20;
|
|
92
91
|
|
|
93
|
-
// === Trade Parameters (
|
|
92
|
+
// === Trade Parameters (BACKTEST VALIDATED) ===
|
|
94
93
|
this.baseStopTicks = 8; // $40
|
|
95
94
|
this.baseTargetTicks = 16; // $80
|
|
96
95
|
this.breakevenTicks = 4; // Move to BE at +4 ticks
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hedgequantx",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.237",
|
|
4
4
|
"description": "HedgeQuantX - Prop Futures Trading CLI",
|
|
5
5
|
"main": "src/app.js",
|
|
6
6
|
"bin": {
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"src/api/",
|
|
48
48
|
"src/config/",
|
|
49
49
|
"src/lib/data.js",
|
|
50
|
+
"src/lib/hft/",
|
|
50
51
|
"src/lib/smart-logs*.js",
|
|
51
52
|
"src/lib/smart-logs-messages/",
|
|
52
53
|
"src/lib/m/index.js",
|
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* =============================================================================
|
|
3
|
+
* HFT-GRADE INFRASTRUCTURE
|
|
4
|
+
* =============================================================================
|
|
5
|
+
* Ultra-low latency primitives for high-frequency trading
|
|
6
|
+
*
|
|
7
|
+
* Components:
|
|
8
|
+
* - ObjectPool: Zero-allocation object reuse
|
|
9
|
+
* - CircularBuffer: O(1) fixed-size buffer operations
|
|
10
|
+
* - FastMath: Optimized math operations
|
|
11
|
+
* - TimestampCache: Cached time operations
|
|
12
|
+
* - FieldDecoder: Pre-indexed protobuf decoding
|
|
13
|
+
*
|
|
14
|
+
* Design Principles:
|
|
15
|
+
* - ZERO allocations in hot paths
|
|
16
|
+
* - Pre-computed lookup tables
|
|
17
|
+
* - Branch prediction optimization
|
|
18
|
+
* - Cache-friendly data structures
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
'use strict';
|
|
22
|
+
|
|
23
|
+
// =============================================================================
|
|
24
|
+
// OBJECT POOL - Zero-allocation object reuse
|
|
25
|
+
// =============================================================================
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* High-performance object pool for hot path allocation avoidance
|
|
29
|
+
* @template T
|
|
30
|
+
*/
|
|
31
|
+
class ObjectPool {
|
|
32
|
+
/**
|
|
33
|
+
* @param {() => T} factory - Function to create new objects
|
|
34
|
+
* @param {(obj: T) => void} reset - Function to reset object state
|
|
35
|
+
* @param {number} initialSize - Initial pool size
|
|
36
|
+
*/
|
|
37
|
+
constructor(factory, reset, initialSize = 128) {
|
|
38
|
+
this.factory = factory;
|
|
39
|
+
this.reset = reset;
|
|
40
|
+
this.pool = new Array(initialSize);
|
|
41
|
+
this.index = initialSize;
|
|
42
|
+
|
|
43
|
+
// Pre-allocate all objects
|
|
44
|
+
for (let i = 0; i < initialSize; i++) {
|
|
45
|
+
this.pool[i] = factory();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Acquire object from pool (O(1), zero allocation if pool not empty)
|
|
51
|
+
* @returns {T}
|
|
52
|
+
*/
|
|
53
|
+
acquire() {
|
|
54
|
+
if (this.index > 0) {
|
|
55
|
+
return this.pool[--this.index];
|
|
56
|
+
}
|
|
57
|
+
// Pool exhausted - create new (should be rare in well-tuned system)
|
|
58
|
+
return this.factory();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Release object back to pool (O(1))
|
|
63
|
+
* @param {T} obj
|
|
64
|
+
*/
|
|
65
|
+
release(obj) {
|
|
66
|
+
this.reset(obj);
|
|
67
|
+
if (this.index < this.pool.length) {
|
|
68
|
+
this.pool[this.index++] = obj;
|
|
69
|
+
}
|
|
70
|
+
// If pool full, object is discarded (GC will collect)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get current pool size
|
|
75
|
+
* @returns {number}
|
|
76
|
+
*/
|
|
77
|
+
get available() {
|
|
78
|
+
return this.index;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Pre-warm pool to full capacity
|
|
83
|
+
*/
|
|
84
|
+
prewarm() {
|
|
85
|
+
while (this.index < this.pool.length) {
|
|
86
|
+
this.pool[this.index++] = this.factory();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// =============================================================================
|
|
92
|
+
// CIRCULAR BUFFER - O(1) fixed-size buffer
|
|
93
|
+
// =============================================================================
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Lock-free circular buffer for time-series data
|
|
97
|
+
* Eliminates O(n) shift() operations
|
|
98
|
+
* @template T
|
|
99
|
+
*/
|
|
100
|
+
class CircularBuffer {
|
|
101
|
+
/**
|
|
102
|
+
* @param {number} capacity - Fixed buffer capacity
|
|
103
|
+
*/
|
|
104
|
+
constructor(capacity) {
|
|
105
|
+
this.buffer = new Array(capacity);
|
|
106
|
+
this.capacity = capacity;
|
|
107
|
+
this.head = 0; // Read position
|
|
108
|
+
this.tail = 0; // Write position
|
|
109
|
+
this.length = 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Add item to buffer (O(1), overwrites oldest if full)
|
|
114
|
+
* @param {T} item
|
|
115
|
+
*/
|
|
116
|
+
push(item) {
|
|
117
|
+
this.buffer[this.tail] = item;
|
|
118
|
+
this.tail = (this.tail + 1) % this.capacity;
|
|
119
|
+
|
|
120
|
+
if (this.length < this.capacity) {
|
|
121
|
+
this.length++;
|
|
122
|
+
} else {
|
|
123
|
+
// Buffer full - advance head (overwrite oldest)
|
|
124
|
+
this.head = (this.head + 1) % this.capacity;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get item at logical index (O(1))
|
|
130
|
+
* @param {number} index - 0 = oldest, length-1 = newest
|
|
131
|
+
* @returns {T}
|
|
132
|
+
*/
|
|
133
|
+
get(index) {
|
|
134
|
+
if (index < 0 || index >= this.length) return undefined;
|
|
135
|
+
return this.buffer[(this.head + index) % this.capacity];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Get most recent item (O(1))
|
|
140
|
+
* @returns {T}
|
|
141
|
+
*/
|
|
142
|
+
last() {
|
|
143
|
+
if (this.length === 0) return undefined;
|
|
144
|
+
return this.buffer[(this.tail - 1 + this.capacity) % this.capacity];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Get oldest item (O(1))
|
|
149
|
+
* @returns {T}
|
|
150
|
+
*/
|
|
151
|
+
first() {
|
|
152
|
+
if (this.length === 0) return undefined;
|
|
153
|
+
return this.buffer[this.head];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Iterate over all items (oldest to newest)
|
|
158
|
+
* @param {(item: T, index: number) => void} fn
|
|
159
|
+
*/
|
|
160
|
+
forEach(fn) {
|
|
161
|
+
for (let i = 0; i < this.length; i++) {
|
|
162
|
+
fn(this.buffer[(this.head + i) % this.capacity], i);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Reduce over all items
|
|
168
|
+
* @template U
|
|
169
|
+
* @param {(acc: U, item: T, index: number) => U} fn
|
|
170
|
+
* @param {U} initial
|
|
171
|
+
* @returns {U}
|
|
172
|
+
*/
|
|
173
|
+
reduce(fn, initial) {
|
|
174
|
+
let acc = initial;
|
|
175
|
+
for (let i = 0; i < this.length; i++) {
|
|
176
|
+
acc = fn(acc, this.buffer[(this.head + i) % this.capacity], i);
|
|
177
|
+
}
|
|
178
|
+
return acc;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Get slice of recent items (newest N items)
|
|
183
|
+
* @param {number} count
|
|
184
|
+
* @returns {T[]}
|
|
185
|
+
*/
|
|
186
|
+
recent(count) {
|
|
187
|
+
const n = Math.min(count, this.length);
|
|
188
|
+
const result = new Array(n);
|
|
189
|
+
const start = (this.tail - n + this.capacity) % this.capacity;
|
|
190
|
+
for (let i = 0; i < n; i++) {
|
|
191
|
+
result[i] = this.buffer[(start + i) % this.capacity];
|
|
192
|
+
}
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Clear buffer (O(1))
|
|
198
|
+
*/
|
|
199
|
+
clear() {
|
|
200
|
+
this.head = 0;
|
|
201
|
+
this.tail = 0;
|
|
202
|
+
this.length = 0;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Check if buffer is full
|
|
207
|
+
* @returns {boolean}
|
|
208
|
+
*/
|
|
209
|
+
isFull() {
|
|
210
|
+
return this.length === this.capacity;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// =============================================================================
|
|
215
|
+
// TYPED CIRCULAR BUFFER - For numeric data (Float64)
|
|
216
|
+
// =============================================================================
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Typed circular buffer using Float64Array for numeric time-series
|
|
220
|
+
* More cache-friendly than generic CircularBuffer for numbers
|
|
221
|
+
*/
|
|
222
|
+
class Float64CircularBuffer {
|
|
223
|
+
/**
|
|
224
|
+
* @param {number} capacity
|
|
225
|
+
*/
|
|
226
|
+
constructor(capacity) {
|
|
227
|
+
this.buffer = new Float64Array(capacity);
|
|
228
|
+
this.capacity = capacity;
|
|
229
|
+
this.head = 0;
|
|
230
|
+
this.tail = 0;
|
|
231
|
+
this.length = 0;
|
|
232
|
+
|
|
233
|
+
// Pre-computed values for statistics
|
|
234
|
+
this._sum = 0;
|
|
235
|
+
this._sumSq = 0;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Add value with running statistics (O(1))
|
|
240
|
+
* @param {number} value
|
|
241
|
+
*/
|
|
242
|
+
push(value) {
|
|
243
|
+
if (this.length === this.capacity) {
|
|
244
|
+
// Remove oldest value from running stats
|
|
245
|
+
const oldest = this.buffer[this.head];
|
|
246
|
+
this._sum -= oldest;
|
|
247
|
+
this._sumSq -= oldest * oldest;
|
|
248
|
+
this.head = (this.head + 1) % this.capacity;
|
|
249
|
+
} else {
|
|
250
|
+
this.length++;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Add new value
|
|
254
|
+
this.buffer[this.tail] = value;
|
|
255
|
+
this._sum += value;
|
|
256
|
+
this._sumSq += value * value;
|
|
257
|
+
this.tail = (this.tail + 1) % this.capacity;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Get mean (O(1) - pre-computed)
|
|
262
|
+
* @returns {number}
|
|
263
|
+
*/
|
|
264
|
+
mean() {
|
|
265
|
+
return this.length > 0 ? this._sum / this.length : 0;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Get variance (O(1) - pre-computed)
|
|
270
|
+
* @returns {number}
|
|
271
|
+
*/
|
|
272
|
+
variance() {
|
|
273
|
+
if (this.length < 2) return 0;
|
|
274
|
+
const mean = this._sum / this.length;
|
|
275
|
+
return (this._sumSq / this.length) - (mean * mean);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Get standard deviation (O(1))
|
|
280
|
+
* @returns {number}
|
|
281
|
+
*/
|
|
282
|
+
std() {
|
|
283
|
+
return Math.sqrt(Math.max(0, this.variance()));
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Get value at index
|
|
288
|
+
* @param {number} index
|
|
289
|
+
* @returns {number}
|
|
290
|
+
*/
|
|
291
|
+
get(index) {
|
|
292
|
+
if (index < 0 || index >= this.length) return NaN;
|
|
293
|
+
return this.buffer[(this.head + index) % this.capacity];
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Get most recent value
|
|
298
|
+
* @returns {number}
|
|
299
|
+
*/
|
|
300
|
+
last() {
|
|
301
|
+
if (this.length === 0) return NaN;
|
|
302
|
+
return this.buffer[(this.tail - 1 + this.capacity) % this.capacity];
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Get min value (O(n) but rarely needed in hot path)
|
|
307
|
+
* @returns {number}
|
|
308
|
+
*/
|
|
309
|
+
min() {
|
|
310
|
+
if (this.length === 0) return NaN;
|
|
311
|
+
let min = this.buffer[this.head];
|
|
312
|
+
for (let i = 1; i < this.length; i++) {
|
|
313
|
+
const val = this.buffer[(this.head + i) % this.capacity];
|
|
314
|
+
if (val < min) min = val;
|
|
315
|
+
}
|
|
316
|
+
return min;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Get max value
|
|
321
|
+
* @returns {number}
|
|
322
|
+
*/
|
|
323
|
+
max() {
|
|
324
|
+
if (this.length === 0) return NaN;
|
|
325
|
+
let max = this.buffer[this.head];
|
|
326
|
+
for (let i = 1; i < this.length; i++) {
|
|
327
|
+
const val = this.buffer[(this.head + i) % this.capacity];
|
|
328
|
+
if (val > max) max = val;
|
|
329
|
+
}
|
|
330
|
+
return max;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
clear() {
|
|
334
|
+
this.head = 0;
|
|
335
|
+
this.tail = 0;
|
|
336
|
+
this.length = 0;
|
|
337
|
+
this._sum = 0;
|
|
338
|
+
this._sumSq = 0;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// =============================================================================
|
|
343
|
+
// FAST MATH - Optimized math operations
|
|
344
|
+
// =============================================================================
|
|
345
|
+
|
|
346
|
+
const FastMath = {
|
|
347
|
+
/**
|
|
348
|
+
* Fast square (faster than Math.pow(x, 2))
|
|
349
|
+
* @param {number} x
|
|
350
|
+
* @returns {number}
|
|
351
|
+
*/
|
|
352
|
+
square: (x) => x * x,
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Clamp value between min and max (branchless)
|
|
356
|
+
* @param {number} value
|
|
357
|
+
* @param {number} min
|
|
358
|
+
* @param {number} max
|
|
359
|
+
* @returns {number}
|
|
360
|
+
*/
|
|
361
|
+
clamp: (value, min, max) => Math.max(min, Math.min(max, value)),
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Linear interpolation
|
|
365
|
+
* @param {number} a
|
|
366
|
+
* @param {number} b
|
|
367
|
+
* @param {number} t - 0 to 1
|
|
368
|
+
* @returns {number}
|
|
369
|
+
*/
|
|
370
|
+
lerp: (a, b, t) => a + (b - a) * t,
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Sign function (-1, 0, 1)
|
|
374
|
+
* @param {number} x
|
|
375
|
+
* @returns {number}
|
|
376
|
+
*/
|
|
377
|
+
sign: (x) => (x > 0) - (x < 0),
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Fast approximate inverse square root (for non-critical paths)
|
|
381
|
+
* @param {number} x
|
|
382
|
+
* @returns {number}
|
|
383
|
+
*/
|
|
384
|
+
invSqrt: (x) => 1 / Math.sqrt(x),
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Round to tick size
|
|
388
|
+
* @param {number} price
|
|
389
|
+
* @param {number} tickSize
|
|
390
|
+
* @returns {number}
|
|
391
|
+
*/
|
|
392
|
+
roundToTick: (price, tickSize) => Math.round(price / tickSize) * tickSize,
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Calculate ticks between prices
|
|
396
|
+
* @param {number} price1
|
|
397
|
+
* @param {number} price2
|
|
398
|
+
* @param {number} tickSize
|
|
399
|
+
* @returns {number}
|
|
400
|
+
*/
|
|
401
|
+
ticksBetween: (price1, price2, tickSize) => Math.round((price1 - price2) / tickSize),
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
// =============================================================================
|
|
405
|
+
// TIMESTAMP CACHE - Reduce Date.now() calls
|
|
406
|
+
// =============================================================================
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Cached timestamp provider to reduce Date.now() calls in hot paths
|
|
410
|
+
* Updates at configurable interval
|
|
411
|
+
*/
|
|
412
|
+
class TimestampCache {
|
|
413
|
+
constructor(updateIntervalMs = 1) {
|
|
414
|
+
this._now = Date.now();
|
|
415
|
+
this._hrTime = process.hrtime.bigint();
|
|
416
|
+
this._interval = null;
|
|
417
|
+
this._updateInterval = updateIntervalMs;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Start automatic updates
|
|
422
|
+
*/
|
|
423
|
+
start() {
|
|
424
|
+
if (this._interval) return;
|
|
425
|
+
this._interval = setInterval(() => {
|
|
426
|
+
this._now = Date.now();
|
|
427
|
+
this._hrTime = process.hrtime.bigint();
|
|
428
|
+
}, this._updateInterval);
|
|
429
|
+
this._interval.unref(); // Don't prevent process exit
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Stop automatic updates
|
|
434
|
+
*/
|
|
435
|
+
stop() {
|
|
436
|
+
if (this._interval) {
|
|
437
|
+
clearInterval(this._interval);
|
|
438
|
+
this._interval = null;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Get cached timestamp (ms)
|
|
444
|
+
* @returns {number}
|
|
445
|
+
*/
|
|
446
|
+
now() {
|
|
447
|
+
return this._now;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Get high-resolution timestamp (ns)
|
|
452
|
+
* @returns {bigint}
|
|
453
|
+
*/
|
|
454
|
+
hrNow() {
|
|
455
|
+
return this._hrTime;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Force update (use sparingly)
|
|
460
|
+
*/
|
|
461
|
+
update() {
|
|
462
|
+
this._now = Date.now();
|
|
463
|
+
this._hrTime = process.hrtime.bigint();
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// =============================================================================
|
|
468
|
+
// PRE-ALLOCATED STRUCTURES
|
|
469
|
+
// =============================================================================
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Pre-allocated bar structure for tick aggregation
|
|
473
|
+
*/
|
|
474
|
+
function createBarStruct() {
|
|
475
|
+
return {
|
|
476
|
+
timestamp: 0,
|
|
477
|
+
open: 0,
|
|
478
|
+
high: 0,
|
|
479
|
+
low: 0,
|
|
480
|
+
close: 0,
|
|
481
|
+
volume: 0,
|
|
482
|
+
delta: 0,
|
|
483
|
+
tickCount: 0,
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Reset bar struct for reuse
|
|
489
|
+
* @param {Object} bar
|
|
490
|
+
*/
|
|
491
|
+
function resetBarStruct(bar) {
|
|
492
|
+
bar.timestamp = 0;
|
|
493
|
+
bar.open = 0;
|
|
494
|
+
bar.high = -Infinity;
|
|
495
|
+
bar.low = Infinity;
|
|
496
|
+
bar.close = 0;
|
|
497
|
+
bar.volume = 0;
|
|
498
|
+
bar.delta = 0;
|
|
499
|
+
bar.tickCount = 0;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Pre-allocated signal structure
|
|
504
|
+
*/
|
|
505
|
+
function createSignalStruct() {
|
|
506
|
+
return {
|
|
507
|
+
id: '',
|
|
508
|
+
timestamp: 0,
|
|
509
|
+
symbol: '',
|
|
510
|
+
contractId: '',
|
|
511
|
+
side: 0,
|
|
512
|
+
direction: '',
|
|
513
|
+
strategy: '',
|
|
514
|
+
strength: 0,
|
|
515
|
+
edge: 0,
|
|
516
|
+
confidence: 0,
|
|
517
|
+
entry: 0,
|
|
518
|
+
entryPrice: 0,
|
|
519
|
+
stopLoss: 0,
|
|
520
|
+
takeProfit: 0,
|
|
521
|
+
riskReward: 0,
|
|
522
|
+
stopTicks: 0,
|
|
523
|
+
targetTicks: 0,
|
|
524
|
+
breakevenTicks: 0,
|
|
525
|
+
trailTriggerTicks: 0,
|
|
526
|
+
trailDistanceTicks: 0,
|
|
527
|
+
regime: '',
|
|
528
|
+
zscore: 0,
|
|
529
|
+
vpin: 0,
|
|
530
|
+
ofi: 0,
|
|
531
|
+
kyleLambda: 0,
|
|
532
|
+
kalmanEstimate: 0,
|
|
533
|
+
expires: 0,
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Reset signal struct for reuse
|
|
539
|
+
* @param {Object} signal
|
|
540
|
+
*/
|
|
541
|
+
function resetSignalStruct(signal) {
|
|
542
|
+
signal.id = '';
|
|
543
|
+
signal.timestamp = 0;
|
|
544
|
+
signal.symbol = '';
|
|
545
|
+
signal.contractId = '';
|
|
546
|
+
signal.side = 0;
|
|
547
|
+
signal.direction = '';
|
|
548
|
+
signal.strategy = '';
|
|
549
|
+
signal.strength = 0;
|
|
550
|
+
signal.edge = 0;
|
|
551
|
+
signal.confidence = 0;
|
|
552
|
+
signal.entry = 0;
|
|
553
|
+
signal.entryPrice = 0;
|
|
554
|
+
signal.stopLoss = 0;
|
|
555
|
+
signal.takeProfit = 0;
|
|
556
|
+
signal.riskReward = 0;
|
|
557
|
+
signal.stopTicks = 0;
|
|
558
|
+
signal.targetTicks = 0;
|
|
559
|
+
signal.breakevenTicks = 0;
|
|
560
|
+
signal.trailTriggerTicks = 0;
|
|
561
|
+
signal.trailDistanceTicks = 0;
|
|
562
|
+
signal.regime = '';
|
|
563
|
+
signal.zscore = 0;
|
|
564
|
+
signal.vpin = 0;
|
|
565
|
+
signal.ofi = 0;
|
|
566
|
+
signal.kyleLambda = 0;
|
|
567
|
+
signal.kalmanEstimate = 0;
|
|
568
|
+
signal.expires = 0;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Pre-allocated trade result structure
|
|
573
|
+
*/
|
|
574
|
+
function createTradeStruct() {
|
|
575
|
+
return {
|
|
576
|
+
id: '',
|
|
577
|
+
entryTime: 0,
|
|
578
|
+
exitTime: 0,
|
|
579
|
+
direction: 0,
|
|
580
|
+
entryPrice: 0,
|
|
581
|
+
exitPrice: 0,
|
|
582
|
+
quantity: 0,
|
|
583
|
+
grossPnl: 0,
|
|
584
|
+
netPnl: 0,
|
|
585
|
+
commission: 0,
|
|
586
|
+
slippage: 0,
|
|
587
|
+
exitReason: '',
|
|
588
|
+
duration: 0,
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function resetTradeStruct(trade) {
|
|
593
|
+
trade.id = '';
|
|
594
|
+
trade.entryTime = 0;
|
|
595
|
+
trade.exitTime = 0;
|
|
596
|
+
trade.direction = 0;
|
|
597
|
+
trade.entryPrice = 0;
|
|
598
|
+
trade.exitPrice = 0;
|
|
599
|
+
trade.quantity = 0;
|
|
600
|
+
trade.grossPnl = 0;
|
|
601
|
+
trade.netPnl = 0;
|
|
602
|
+
trade.commission = 0;
|
|
603
|
+
trade.slippage = 0;
|
|
604
|
+
trade.exitReason = '';
|
|
605
|
+
trade.duration = 0;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// =============================================================================
|
|
609
|
+
// SINGLETON INSTANCES
|
|
610
|
+
// =============================================================================
|
|
611
|
+
|
|
612
|
+
// Global timestamp cache (start on import)
|
|
613
|
+
const timestampCache = new TimestampCache(1);
|
|
614
|
+
timestampCache.start();
|
|
615
|
+
|
|
616
|
+
// Pre-created object pools
|
|
617
|
+
const barPool = new ObjectPool(createBarStruct, resetBarStruct, 256);
|
|
618
|
+
const signalPool = new ObjectPool(createSignalStruct, resetSignalStruct, 64);
|
|
619
|
+
const tradePool = new ObjectPool(createTradeStruct, resetTradeStruct, 128);
|
|
620
|
+
|
|
621
|
+
// =============================================================================
|
|
622
|
+
// EXPORTS
|
|
623
|
+
// =============================================================================
|
|
624
|
+
|
|
625
|
+
module.exports = {
|
|
626
|
+
// Classes
|
|
627
|
+
ObjectPool,
|
|
628
|
+
CircularBuffer,
|
|
629
|
+
Float64CircularBuffer,
|
|
630
|
+
TimestampCache,
|
|
631
|
+
|
|
632
|
+
// Fast math utilities
|
|
633
|
+
FastMath,
|
|
634
|
+
|
|
635
|
+
// Struct factories
|
|
636
|
+
createBarStruct,
|
|
637
|
+
resetBarStruct,
|
|
638
|
+
createSignalStruct,
|
|
639
|
+
resetSignalStruct,
|
|
640
|
+
createTradeStruct,
|
|
641
|
+
resetTradeStruct,
|
|
642
|
+
|
|
643
|
+
// Singleton pools
|
|
644
|
+
barPool,
|
|
645
|
+
signalPool,
|
|
646
|
+
tradePool,
|
|
647
|
+
timestampCache,
|
|
648
|
+
};
|
package/dist/lib/m/mod1.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
'use strict';require('bytenode');module.exports=require('./mod1.jsc');
|
package/dist/lib/m/mod1.jsc
DELETED
|
Binary file
|
package/dist/lib/m/mod2.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
'use strict';require('bytenode');module.exports=require('./mod2.jsc');
|