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.
@@ -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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "2.9.227",
3
+ "version": "2.9.229",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {