hedgequantx 2.9.227 → 2.9.229
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/ultra-scalping.js +91 -1
- package/package.json +1 -1
|
@@ -136,6 +136,26 @@ class HQXUltraScalpingStrategy extends EventEmitter {
|
|
|
136
136
|
this.tickBuffer.set(contractId, []);
|
|
137
137
|
this.lastBarTime.set(contractId, 0);
|
|
138
138
|
this.kalmanStates.set(contractId, { estimate: 0, errorCovariance: 1.0 });
|
|
139
|
+
|
|
140
|
+
// Start status log interval - emits every second regardless of tick flow
|
|
141
|
+
this._currentContractId = contractId;
|
|
142
|
+
this._lastPrice = 0;
|
|
143
|
+
if (this._statusInterval) clearInterval(this._statusInterval);
|
|
144
|
+
this._statusInterval = setInterval(() => {
|
|
145
|
+
if (this._lastPrice > 0) {
|
|
146
|
+
this._emitStatusLog(this._currentContractId, this._lastPrice);
|
|
147
|
+
}
|
|
148
|
+
}, 1000);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Stop the strategy and clean up interval
|
|
153
|
+
*/
|
|
154
|
+
stop() {
|
|
155
|
+
if (this._statusInterval) {
|
|
156
|
+
clearInterval(this._statusInterval);
|
|
157
|
+
this._statusInterval = null;
|
|
158
|
+
}
|
|
139
159
|
}
|
|
140
160
|
|
|
141
161
|
/**
|
|
@@ -151,9 +171,13 @@ class HQXUltraScalpingStrategy extends EventEmitter {
|
|
|
151
171
|
// Add tick to buffer
|
|
152
172
|
let ticks = this.tickBuffer.get(contractId);
|
|
153
173
|
ticks.push(tick);
|
|
174
|
+
|
|
175
|
+
// Track total ticks and last price for status log interval
|
|
176
|
+
this._totalTicks = (this._totalTicks || 0) + 1;
|
|
177
|
+
this._lastPrice = tick.price;
|
|
178
|
+
this._currentContractId = contractId;
|
|
154
179
|
|
|
155
180
|
// Check if we should form a new bar
|
|
156
|
-
const now = Date.now();
|
|
157
181
|
const lastBar = this.lastBarTime.get(contractId);
|
|
158
182
|
|
|
159
183
|
if (now - lastBar >= this.barIntervalMs && ticks.length > 0) {
|
|
@@ -171,6 +195,72 @@ class HQXUltraScalpingStrategy extends EventEmitter {
|
|
|
171
195
|
}
|
|
172
196
|
return null;
|
|
173
197
|
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Emit status log with QUANT metrics
|
|
201
|
+
*/
|
|
202
|
+
_emitStatusLog(contractId, currentPrice) {
|
|
203
|
+
const prices = this.priceBuffer.get(contractId) || [];
|
|
204
|
+
const volumes = this.volumeBuffer.get(contractId) || [];
|
|
205
|
+
const bars = this.barHistory.get(contractId) || [];
|
|
206
|
+
|
|
207
|
+
if (prices.length < 20) return; // Not enough data yet
|
|
208
|
+
|
|
209
|
+
// Compute current metrics
|
|
210
|
+
const zscore = computeZScore(prices);
|
|
211
|
+
const vpin = volumes.length >= 10 ? computeVPIN(volumes, this.vpinWindow) : 0;
|
|
212
|
+
const ofi = bars.length >= 10 ? computeOrderFlowImbalance(bars, this.ofiLookback) : 0;
|
|
213
|
+
|
|
214
|
+
// Determine market state
|
|
215
|
+
const absZ = Math.abs(zscore);
|
|
216
|
+
let zState = 'normal';
|
|
217
|
+
if (absZ >= 2.0) zState = 'EXTREME';
|
|
218
|
+
else if (absZ >= 1.5) zState = 'HIGH';
|
|
219
|
+
else if (absZ >= 1.0) zState = 'building';
|
|
220
|
+
|
|
221
|
+
// Determine direction bias
|
|
222
|
+
let bias = 'neutral';
|
|
223
|
+
if (zscore < -1.5 && ofi > 0.1) bias = 'LONG setup';
|
|
224
|
+
else if (zscore > 1.5 && ofi < -0.1) bias = 'SHORT setup';
|
|
225
|
+
else if (zscore < -1.0) bias = 'oversold';
|
|
226
|
+
else if (zscore > 1.0) bias = 'overbought';
|
|
227
|
+
|
|
228
|
+
// Extract symbol
|
|
229
|
+
const sym = (contractId || '').replace(/[A-Z]\d+$/, '');
|
|
230
|
+
|
|
231
|
+
// Build message based on state
|
|
232
|
+
let message;
|
|
233
|
+
if (!this.tradingEnabled) {
|
|
234
|
+
message = `[${sym}] ${currentPrice.toFixed(2)} | PAUSED (${this.lossStreak} losses) | Cooldown active`;
|
|
235
|
+
} else if (absZ >= 2.0) {
|
|
236
|
+
const dir = zscore < 0 ? 'LONG' : 'SHORT';
|
|
237
|
+
const ofiPct = (Math.abs(ofi) * 100).toFixed(0);
|
|
238
|
+
const ofiConfirm = (zscore < 0 && ofi > 0.15) || (zscore > 0 && ofi < -0.15);
|
|
239
|
+
if (ofiConfirm) {
|
|
240
|
+
message = `[${sym}] ${currentPrice.toFixed(2)} | Z: ${zscore.toFixed(1)}σ ${zState} | ${dir} | OFI ${ofiPct}% confirms`;
|
|
241
|
+
} else {
|
|
242
|
+
message = `[${sym}] ${currentPrice.toFixed(2)} | Z: ${zscore.toFixed(1)}σ ${zState} | ${dir} signal | OFI ${ofiPct}% pending`;
|
|
243
|
+
}
|
|
244
|
+
} else if (absZ >= 1.5) {
|
|
245
|
+
message = `[${sym}] ${currentPrice.toFixed(2)} | Z: ${zscore.toFixed(1)}σ ${zState} | ${bias} | Monitoring`;
|
|
246
|
+
} else if (absZ >= 1.0) {
|
|
247
|
+
message = `[${sym}] ${currentPrice.toFixed(2)} | Z: ${zscore.toFixed(1)}σ ${zState} | Awaiting extremity`;
|
|
248
|
+
} else {
|
|
249
|
+
// Normal state - show different context messages
|
|
250
|
+
const vpinPct = (vpin * 100).toFixed(0);
|
|
251
|
+
const contexts = [
|
|
252
|
+
`VPIN ${vpinPct}% | Mean reversion scan`,
|
|
253
|
+
`OFI balanced | Price discovery`,
|
|
254
|
+
`Z normalized | Statistical scan`,
|
|
255
|
+
`Tick flow stable | Edge detection`,
|
|
256
|
+
`Volatility normal | Alpha scan`,
|
|
257
|
+
];
|
|
258
|
+
const ctx = contexts[Math.floor(Date.now() / 5000) % contexts.length];
|
|
259
|
+
message = `[${sym}] ${currentPrice.toFixed(2)} | Z: ${zscore.toFixed(1)}σ | ${ctx}`;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
this.emit('log', { type: 'info', message });
|
|
263
|
+
}
|
|
174
264
|
|
|
175
265
|
/**
|
|
176
266
|
* Aggregate ticks into a bar
|