hedgequantx 2.9.153 → 2.9.154

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "2.9.153",
3
+ "version": "2.9.154",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -188,68 +188,59 @@ const QUANT = {
188
188
  `[${d.sym}] Sharpe estimation | ${d.bars} bars | Risk-adjusted return`,
189
189
  ]),
190
190
 
191
- zones: (d) => {
192
- const z = (Math.abs(d.delta) / 8).toFixed(2);
193
- return pick([
194
- `[${d.sym}] ${d.price} | Z: ${z}σ | VPIN elevated | ${d.zones} factors converging`,
195
- `[${d.sym}] ${d.price} | Statistical edge forming | Z-score: ${z}σ | OFI: ${d.delta}`,
196
- `[${d.sym}] Factor alignment | ${d.zones}/${d.zones + 2} criteria | Threshold proximity`,
197
- `[${d.sym}] ${d.price} | Multi-factor consensus: ${d.zones} | Z: ${z}σ approaching`,
198
- `[${d.sym}] Edge probability rising | Z: ${z}σ | VPIN: elevated | OFI: ${d.delta}`,
199
- `[${d.sym}] ${d.price} | Regime shift detected | ${d.zones} factors | Z: ${z}σ`,
200
- `[${d.sym}] Information ratio spike | ${d.price} | ${d.zones} signals converging`,
201
- `[${d.sym}] ${d.price} | Alpha opportunity | Z: ${z}σ | Mispricing detected`,
202
- `[${d.sym}] Signal strength: ${d.zones}/5 | ${d.price} | Z approaching ${z}σ`,
203
- `[${d.sym}] ${d.price} | Factor model alert | ${d.zones} criteria met | Z: ${z}σ`,
204
- `[${d.sym}] Convergence detected | ${d.price} | OFI: ${d.delta} | Threshold: ${z}σ`,
205
- `[${d.sym}] ${d.price} | Statistical tension | ${d.zones} factors | Snap expected`,
206
- `[${d.sym}] Edge forming @ ${d.price} | Z: ${z}σ | ${d.zones} confirmations`,
207
- `[${d.sym}] ${d.price} | Model confidence rising | ${d.zones} factors aligned`,
208
- `[${d.sym}] Fair value divergence | ${d.price} | Z: ${z}σ | Reversion setup`,
209
- ]);
210
- },
191
+ zones: (d) => pick([
192
+ `[${d.sym}] ${d.price} | Z: ${d.zScore}σ | VPIN: ${d.vpin}% | Factors converging`,
193
+ `[${d.sym}] ${d.price} | Statistical edge forming | Z: ${d.zScore}σ | OFI: ${d.ofi}`,
194
+ `[${d.sym}] Factor alignment | ${d.ticks} ticks | Z: ${d.zScore}σ | Threshold proximity`,
195
+ `[${d.sym}] ${d.price} | Multi-factor scan | Z: ${d.zScore}σ | VPIN: ${d.vpin}%`,
196
+ `[${d.sym}] Edge probability rising | Z: ${d.zScore}σ | VPIN: ${d.vpin}% | OFI: ${d.ofi}`,
197
+ `[${d.sym}] ${d.price} | Regime analysis | Z: ${d.zScore}σ | ${d.ticks} observations`,
198
+ `[${d.sym}] Information ratio calc | ${d.price} | Z: ${d.zScore}σ | Converging`,
199
+ `[${d.sym}] ${d.price} | Alpha scan | Z: ${d.zScore}σ | VPIN: ${d.vpin}%`,
200
+ `[${d.sym}] Signal strength building | ${d.price} | Z: ${d.zScore}σ | OFI: ${d.ofi}`,
201
+ `[${d.sym}] ${d.price} | Factor model active | Z: ${d.zScore}σ | VPIN: ${d.vpin}%`,
202
+ `[${d.sym}] Convergence scan | ${d.price} | OFI: ${d.ofi} | Z: ${d.zScore}σ`,
203
+ `[${d.sym}] ${d.price} | Statistical tension | Z: ${d.zScore}σ | Snap pending`,
204
+ `[${d.sym}] Edge forming @ ${d.price} | Z: ${d.zScore}σ | ${d.ticks} ticks`,
205
+ `[${d.sym}] ${d.price} | Model confidence: ${d.vpin}% VPIN | Z: ${d.zScore}σ`,
206
+ `[${d.sym}] Fair value analysis | ${d.price} | Z: ${d.zScore}σ | OFI: ${d.ofi}`,
207
+ ]),
211
208
 
212
- bull: (d) => {
213
- const z = (Math.abs(d.delta) / 8).toFixed(2);
214
- return pick([
215
- `[${d.sym}] ${d.price} | Z: +${z}σ | OFI: +${Math.abs(d.delta)} | BUY edge detected`,
216
- `[${d.sym}] ${d.price} | VPIN buy signal | Toxic sell absorbed | Delta: +${Math.abs(d.delta)}`,
217
- `[${d.sym}] Statistical long | Z: +${z}σ | Mean reversion BUY | ${d.ticks} tick confirm`,
218
- `[${d.sym}] ${d.price} | Factor model: LONG | OFI/VPIN/Z aligned | Positive expectancy`,
219
- `[${d.sym}] Microstructure bullish | Z: +${z}σ | Order toxicity clearing`,
220
- `[${d.sym}] ${d.price} | Positive alpha | Z: +${z}σ | Long bias confirmed`,
221
- `[${d.sym}] Momentum factor bullish | ${d.price} | OFI: +${Math.abs(d.delta)} | Signal: BUY`,
222
- `[${d.sym}] ${d.price} | Information ratio: positive | Z: +${z}σ | Long edge`,
223
- `[${d.sym}] Mean below price | ${d.price} | Z: +${z}σ | Bullish regime`,
224
- `[${d.sym}] ${d.price} | Buy signal confidence | Delta: +${Math.abs(d.delta)} | Z: +${z}σ`,
225
- `[${d.sym}] Regime: bullish | ${d.price} | OFI: +${Math.abs(d.delta)} | Factor aligned`,
226
- `[${d.sym}] ${d.price} | Positive skew | Z: +${z}σ | Upside distribution`,
227
- `[${d.sym}] Alpha extraction: long | ${d.price} | OFI/Z positive | Execute`,
228
- `[${d.sym}] ${d.price} | Model output: BUY | Z: +${z}σ | ${d.zones} factors`,
229
- `[${d.sym}] Statistical momentum | ${d.price} | Delta: +${Math.abs(d.delta)} | Bullish`,
230
- ]);
231
- },
209
+ bull: (d) => pick([
210
+ `[${d.sym}] ${d.price} | Z: +${d.zScoreAbs}σ | OFI: ${d.ofi} | BUY edge detected`,
211
+ `[${d.sym}] ${d.price} | VPIN: ${d.vpin}% buy | Z: +${d.zScoreAbs}σ | Bullish`,
212
+ `[${d.sym}] Statistical long | Z: +${d.zScoreAbs}σ | Mean reversion BUY | ${d.ticks} ticks`,
213
+ `[${d.sym}] ${d.price} | Factor model: LONG | Z/VPIN/OFI aligned | +EV`,
214
+ `[${d.sym}] Microstructure bullish | Z: +${d.zScoreAbs}σ | VPIN: ${d.vpin}%`,
215
+ `[${d.sym}] ${d.price} | Positive alpha | Z: +${d.zScoreAbs}σ | OFI: ${d.ofi}`,
216
+ `[${d.sym}] Momentum factor bullish | ${d.price} | OFI: ${d.ofi} | Z: +${d.zScoreAbs}σ`,
217
+ `[${d.sym}] ${d.price} | Info ratio: positive | Z: +${d.zScoreAbs}σ | Long edge`,
218
+ `[${d.sym}] Mean below price | ${d.price} | Z: +${d.zScoreAbs}σ | VPIN: ${d.vpin}%`,
219
+ `[${d.sym}] ${d.price} | Buy confidence | OFI: ${d.ofi} | Z: +${d.zScoreAbs}σ`,
220
+ `[${d.sym}] Regime: bullish | ${d.price} | Z: +${d.zScoreAbs}σ | VPIN: ${d.vpin}%`,
221
+ `[${d.sym}] ${d.price} | Positive skew | Z: +${d.zScoreAbs}σ | OFI: ${d.ofi}`,
222
+ `[${d.sym}] Alpha extraction: long | ${d.price} | Z/OFI positive`,
223
+ `[${d.sym}] ${d.price} | Model: BUY | Z: +${d.zScoreAbs}σ | VPIN: ${d.vpin}%`,
224
+ `[${d.sym}] Statistical momentum | ${d.price} | OFI: ${d.ofi} | Bullish`,
225
+ ]),
232
226
 
233
- bear: (d) => {
234
- const z = (Math.abs(d.delta) / 8).toFixed(2);
235
- return pick([
236
- `[${d.sym}] ${d.price} | Z: -${z}σ | OFI: ${d.delta} | SELL edge detected`,
237
- `[${d.sym}] ${d.price} | VPIN sell signal | Toxic buy absorbed | Delta: ${d.delta}`,
238
- `[${d.sym}] Statistical short | Z: -${z}σ | Mean reversion SELL | ${d.ticks} tick confirm`,
239
- `[${d.sym}] ${d.price} | Factor model: SHORT | OFI/VPIN/Z aligned | Negative expectancy`,
240
- `[${d.sym}] Microstructure bearish | Z: -${z}σ | Order toxicity rising`,
241
- `[${d.sym}] ${d.price} | Negative alpha | Z: -${z}σ | Short bias confirmed`,
242
- `[${d.sym}] Momentum factor bearish | ${d.price} | OFI: ${d.delta} | Signal: SELL`,
243
- `[${d.sym}] ${d.price} | Information ratio: negative | Z: -${z}σ | Short edge`,
244
- `[${d.sym}] Mean above price | ${d.price} | Z: -${z}σ | Bearish regime`,
245
- `[${d.sym}] ${d.price} | Sell signal confidence | Delta: ${d.delta} | Z: -${z}σ`,
246
- `[${d.sym}] Regime: bearish | ${d.price} | OFI: ${d.delta} | Factor aligned`,
247
- `[${d.sym}] ${d.price} | Negative skew | Z: -${z}σ | Downside distribution`,
248
- `[${d.sym}] Alpha extraction: short | ${d.price} | OFI/Z negative | Execute`,
249
- `[${d.sym}] ${d.price} | Model output: SELL | Z: -${z}σ | ${d.zones} factors`,
250
- `[${d.sym}] Statistical momentum | ${d.price} | Delta: ${d.delta} | Bearish`,
251
- ]);
252
- },
227
+ bear: (d) => pick([
228
+ `[${d.sym}] ${d.price} | Z: -${d.zScoreAbs}σ | OFI: ${d.ofi} | SELL edge detected`,
229
+ `[${d.sym}] ${d.price} | VPIN: ${d.vpin}% sell | Z: -${d.zScoreAbs}σ | Bearish`,
230
+ `[${d.sym}] Statistical short | Z: -${d.zScoreAbs}σ | Mean reversion SELL | ${d.ticks} ticks`,
231
+ `[${d.sym}] ${d.price} | Factor model: SHORT | Z/VPIN/OFI aligned | -EV`,
232
+ `[${d.sym}] Microstructure bearish | Z: -${d.zScoreAbs}σ | VPIN: ${d.vpin}%`,
233
+ `[${d.sym}] ${d.price} | Negative alpha | Z: -${d.zScoreAbs}σ | OFI: ${d.ofi}`,
234
+ `[${d.sym}] Momentum factor bearish | ${d.price} | OFI: ${d.ofi} | Z: -${d.zScoreAbs}σ`,
235
+ `[${d.sym}] ${d.price} | Info ratio: negative | Z: -${d.zScoreAbs}σ | Short edge`,
236
+ `[${d.sym}] Mean above price | ${d.price} | Z: -${d.zScoreAbs}σ | VPIN: ${d.vpin}%`,
237
+ `[${d.sym}] ${d.price} | Sell confidence | OFI: ${d.ofi} | Z: -${d.zScoreAbs}σ`,
238
+ `[${d.sym}] Regime: bearish | ${d.price} | Z: -${d.zScoreAbs}σ | VPIN: ${d.vpin}%`,
239
+ `[${d.sym}] ${d.price} | Negative skew | Z: -${d.zScoreAbs}σ | OFI: ${d.ofi}`,
240
+ `[${d.sym}] Alpha extraction: short | ${d.price} | Z/OFI negative`,
241
+ `[${d.sym}] ${d.price} | Model: SELL | Z: -${d.zScoreAbs}σ | VPIN: ${d.vpin}%`,
242
+ `[${d.sym}] Statistical momentum | ${d.price} | OFI: ${d.ofi} | Bearish`,
243
+ ]),
253
244
 
254
245
  neutral: (d) => pick([
255
246
  `[${d.sym}] ${d.price} | Z: 0σ | OFI: neutral | No statistical edge | HOLD`,
@@ -270,19 +261,20 @@ const QUANT = {
270
261
  ]),
271
262
 
272
263
  ready: (d) => {
273
- const side = d.delta > 0 ? 'LONG' : 'SHORT';
274
- const z = (Math.abs(d.delta) / 8).toFixed(2);
264
+ // Use real zScore for direction (rawZScore keeps the sign)
265
+ const side = d.rawZScore > 0 ? 'LONG' : 'SHORT';
266
+ const zSign = d.rawZScore > 0 ? '+' : '-';
275
267
  return pick([
276
- `${chalk.green('■')} [${d.sym}] Z-SCORE BREACH | ${z}σ @ ${d.price} | EXECUTE ${side}`,
277
- `${chalk.green('■')} [${d.sym}] FACTOR CONSENSUS | OFI: ${d.delta} | ${side} SIGNAL @ ${d.price}`,
278
- `${chalk.green('■')} [${d.sym}] STATISTICAL TRIGGER | VPIN spike | Z: ${z}σ | ${side} ENTRY`,
279
- `${chalk.green('■')} [${d.sym}] ALPHA DETECTED | ${d.price} | Multi-factor ${side} | EXECUTE`,
280
- `${chalk.green('■')} [${d.sym}] REGIME CONFIRMED | Z: ${z}σ | ${side} @ ${d.price}`,
281
- `${chalk.green('■')} [${d.sym}] MODEL SIGNAL | Factor alignment | ${side} TRIGGER | ${d.price}`,
282
- `${chalk.green('■')} [${d.sym}] THRESHOLD BREACH | OFI: ${d.delta} | EXECUTE ${side} @ ${d.price}`,
283
- `${chalk.green('■')} [${d.sym}] MEAN REVERSION | Z: ${z}σ | ${side} CONFIRMED | ${d.price}`,
284
- `${chalk.green('■')} [${d.sym}] EDGE CONFIRMED | ${d.price} | ${side} | Expectancy: +`,
285
- `${chalk.green('■')} [${d.sym}] FACTOR SIGNAL | Z: ${z}σ | OFI: ${d.delta} | ${side} ACTIVE`,
268
+ `${chalk.green('■')} [${d.sym}] Z-SCORE BREACH | ${zSign}${d.zScoreAbs}σ @ ${d.price} | EXECUTE ${side}`,
269
+ `${chalk.green('■')} [${d.sym}] FACTOR CONSENSUS | Z: ${zSign}${d.zScoreAbs}σ | OFI: ${d.ofi} | ${side} @ ${d.price}`,
270
+ `${chalk.green('■')} [${d.sym}] STATISTICAL TRIGGER | VPIN: ${d.vpin}% | Z: ${zSign}${d.zScoreAbs}σ | ${side}`,
271
+ `${chalk.green('■')} [${d.sym}] ALPHA DETECTED | ${d.price} | Z: ${zSign}${d.zScoreAbs}σ | EXECUTE ${side}`,
272
+ `${chalk.green('■')} [${d.sym}] REGIME CONFIRMED | Z: ${zSign}${d.zScoreAbs}σ | OFI: ${d.ofi} | ${side}`,
273
+ `${chalk.green('■')} [${d.sym}] MODEL SIGNAL | Z: ${zSign}${d.zScoreAbs}σ | VPIN: ${d.vpin}% | ${side} TRIGGER`,
274
+ `${chalk.green('■')} [${d.sym}] THRESHOLD BREACH | Z: ${zSign}${d.zScoreAbs}σ | OFI: ${d.ofi} | ${side} @ ${d.price}`,
275
+ `${chalk.green('■')} [${d.sym}] MEAN REVERSION | Z: ${zSign}${d.zScoreAbs}σ | ${side} CONFIRMED | ${d.price}`,
276
+ `${chalk.green('■')} [${d.sym}] EDGE CONFIRMED | ${d.price} | Z: ${zSign}${d.zScoreAbs}σ | OFI: ${d.ofi} | ${side}`,
277
+ `${chalk.green('■')} [${d.sym}] FACTOR SIGNAL | Z: ${zSign}${d.zScoreAbs}σ | VPIN: ${d.vpin}% | ${side} ACTIVE`,
286
278
  ]);
287
279
  },
288
280
  };
@@ -301,12 +293,6 @@ class SmartLogsEngine {
301
293
 
302
294
  setSymbol(s) { this.symbolCode = s; }
303
295
 
304
- _phase(st) {
305
- if (st.setupForming && st.zones > 0) return 'ready';
306
- if (st.zones > 0 || st.swings >= 2) return 'zones';
307
- return 'building';
308
- }
309
-
310
296
  _unique(gen, d) {
311
297
  let msg, i = 0;
312
298
  do { msg = gen(d); i++; } while (this.recent.includes(msg) && i < 15);
@@ -318,18 +304,23 @@ class SmartLogsEngine {
318
304
  getLog(state = {}) {
319
305
  this.counter++;
320
306
  const { trend = 'neutral', position = 0, zones = 0, swings = 0, bars = 0,
321
- price = 0, delta = 0, buyPct = 50, tickCount = 0 } = state;
307
+ price = 0, delta = 0, buyPct = 50, tickCount = 0,
308
+ zScore = 0, vpin = 0, ofi = 0, setupForming = false } = state;
322
309
 
323
- const phase = this._phase(state);
324
- const bull = trend === 'bullish' || buyPct > 55;
325
- const bear = trend === 'bearish' || buyPct < 45;
326
- const T = this.strategyId === 'hqx-2b' ? HQX2B : QUANT;
310
+ const isQuant = this.strategyId !== 'hqx-2b';
311
+ const T = isQuant ? QUANT : HQX2B;
327
312
 
328
313
  const d = {
329
314
  sym: getSym(this.symbolCode),
330
315
  price: price > 0 ? price.toFixed(2) : '-.--',
331
316
  delta, zones, swings, bars,
332
317
  ticks: tickCount > 1000 ? `${(tickCount/1000).toFixed(0)}k` : String(tickCount),
318
+ // Real QUANT metrics from strategy (keep sign for direction)
319
+ zScore: zScore.toFixed(2),
320
+ zScoreAbs: Math.abs(zScore).toFixed(2),
321
+ vpin: (vpin * 100).toFixed(0),
322
+ ofi: ofi > 0 ? `+${ofi.toFixed(0)}` : ofi.toFixed(0),
323
+ rawZScore: zScore, // For direction calculation
333
324
  };
334
325
 
335
326
  if (position !== 0) {
@@ -342,14 +333,38 @@ class SmartLogsEngine {
342
333
  };
343
334
  }
344
335
 
345
- // Priority: ready > building > bull/bear > zones > neutral
336
+ // Determine phase and message type based on strategy
346
337
  let gen, type;
347
- if (phase === 'ready') { gen = T.ready; type = 'signal'; }
348
- else if (phase === 'building') { gen = T.building; type = 'system'; }
349
- else if (bull) { gen = T.bull; type = 'bullish'; }
350
- else if (bear) { gen = T.bear; type = 'bearish'; }
351
- else if (phase === 'zones') { gen = T.zones; type = 'analysis'; }
352
- else { gen = T.neutral; type = 'analysis'; }
338
+
339
+ if (isQuant) {
340
+ // QUANT: tick-based, uses zScore/vpin/ofi
341
+ const minTicks = 50;
342
+ const isBuilding = bars < minTicks;
343
+ const bull = trend === 'bullish' || zScore > 1.5 || buyPct > 58;
344
+ const bear = trend === 'bearish' || zScore < -1.5 || buyPct < 42;
345
+ const ready = Math.abs(zScore) > 2.0 && setupForming;
346
+
347
+ if (ready) { gen = T.ready; type = 'signal'; }
348
+ else if (isBuilding) { gen = T.building; type = 'system'; }
349
+ else if (bull) { gen = T.bull; type = 'bullish'; }
350
+ else if (bear) { gen = T.bear; type = 'bearish'; }
351
+ else { gen = T.zones; type = 'analysis'; } // zones = analysis for QUANT
352
+ } else {
353
+ // HQX-2B: bar-based, uses zones/swings
354
+ const minBars = 3;
355
+ const isBuilding = bars < minBars;
356
+ const hasZones = zones > 0 || swings >= 2;
357
+ const bull = trend === 'bullish' || buyPct > 55;
358
+ const bear = trend === 'bearish' || buyPct < 45;
359
+ const ready = setupForming && zones > 0;
360
+
361
+ if (ready) { gen = T.ready; type = 'signal'; }
362
+ else if (isBuilding) { gen = T.building; type = 'system'; }
363
+ else if (bull) { gen = T.bull; type = 'bullish'; }
364
+ else if (bear) { gen = T.bear; type = 'bearish'; }
365
+ else if (hasZones) { gen = T.zones; type = 'analysis'; }
366
+ else { gen = T.neutral; type = 'analysis'; }
367
+ }
353
368
 
354
369
  return {
355
370
  type,
@@ -224,6 +224,7 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
224
224
  let ticksPerSecond = 0, lastTickSecond = Math.floor(Date.now() / 1000);
225
225
  let lastBiasLogSecond = 0, lastStateLogSecond = 0;
226
226
  let buyVolume = 0, sellVolume = 0, lastTickTime = 0, tickLatencies = [];
227
+ let runningDelta = 0, runningBuyPct = 50; // For live logs
227
228
 
228
229
  marketFeed.on('tick', (tick) => {
229
230
  tickCount++;
@@ -271,6 +272,8 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
271
272
  const totalVol = buyVolume + sellVolume;
272
273
  const buyPressure = totalVol > 0 ? (buyVolume / totalVol) * 100 : 50;
273
274
  lastBias = buyPressure > 55 ? 'LONG' : buyPressure < 45 ? 'SHORT' : 'FLAT';
275
+ runningDelta = buyVolume - sellVolume;
276
+ runningBuyPct = buyPressure;
274
277
  sessionLogger.log('TICK', `count=${tickCount} last=${price?.toFixed(2)} bias=${lastBias} vol=${totalVol}`);
275
278
  buyVolume = 0; sellVolume = 0;
276
279
  }
@@ -294,9 +297,10 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
294
297
  lastAsk = ask;
295
298
 
296
299
  // Only process tick if we have a valid price
300
+ // IMPORTANT: Always use our contractId for consistency with getAnalysisState
297
301
  if (price && price > 0) {
298
302
  strategy.processTick({
299
- contractId: tick.contractId || contractId,
303
+ contractId: contractId,
300
304
  price: price, bid: bid, ask: ask,
301
305
  volume: volume,
302
306
  side: tick.side || tick.lastTradeSide || 'unknown',
@@ -437,7 +441,13 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
437
441
  setupForming: state?.ready && state?.activeZones > 0,
438
442
  position: currentPosition,
439
443
  price: lastPrice || 0,
444
+ delta: runningDelta,
445
+ buyPct: runningBuyPct,
440
446
  tickCount,
447
+ // QUANT strategy metrics (real from strategy)
448
+ zScore: state?.zScore || 0,
449
+ vpin: state?.vpin || 0,
450
+ ofi: state?.ofi || 0,
441
451
  };
442
452
 
443
453
  const log = logsEngine.getLog(logState);
@@ -56,7 +56,11 @@ const executeMultiSymbol = async ({ service, account, contracts, config, strateg
56
56
  losses: 0,
57
57
  tickCount: 0,
58
58
  lastPrice: null,
59
- position: 0
59
+ position: 0,
60
+ buyVolume: 0,
61
+ sellVolume: 0,
62
+ runningDelta: 0,
63
+ runningBuyPct: 50
60
64
  },
61
65
  pendingOrder: false,
62
66
  startingPnL: null
@@ -219,6 +223,27 @@ const executeMultiSymbol = async ({ service, account, contracts, config, strateg
219
223
 
220
224
  data.stats.lastPrice = price;
221
225
 
226
+ // Track buy/sell volume for delta calculation
227
+ if (tick.side === 'buy' || tick.aggressor === 1) {
228
+ data.stats.buyVolume += volume;
229
+ } else if (tick.side === 'sell' || tick.aggressor === 2) {
230
+ data.stats.sellVolume += volume;
231
+ } else if (price && data.stats.lastPrice) {
232
+ if (price > data.stats.lastPrice) data.stats.buyVolume += volume;
233
+ else if (price < data.stats.lastPrice) data.stats.sellVolume += volume;
234
+ }
235
+
236
+ // Update running delta/buyPct every 1000 ticks
237
+ if (data.stats.tickCount % 1000 === 0) {
238
+ const totalVol = data.stats.buyVolume + data.stats.sellVolume;
239
+ if (totalVol > 0) {
240
+ data.stats.runningDelta = data.stats.buyVolume - data.stats.sellVolume;
241
+ data.stats.runningBuyPct = (data.stats.buyVolume / totalVol) * 100;
242
+ }
243
+ data.stats.buyVolume = 0;
244
+ data.stats.sellVolume = 0;
245
+ }
246
+
222
247
  // Process tick through strategy
223
248
  if (price && price > 0) {
224
249
  data.strategy.processTick({
@@ -363,16 +388,23 @@ const executeMultiSymbol = async ({ service, account, contracts, config, strateg
363
388
  if (!data) return;
364
389
 
365
390
  const state = data.strategy.getAnalysisState?.(symbolCode, data.stats.lastPrice);
391
+ const buyPct = data.stats.runningBuyPct || 50;
366
392
  const logState = {
367
393
  bars: state?.barsProcessed || 0,
368
394
  swings: state?.swingsDetected || 0,
369
395
  zones: state?.activeZones || 0,
370
- trend: 'neutral',
396
+ trend: buyPct > 55 ? 'bullish' : buyPct < 45 ? 'bearish' : 'neutral',
371
397
  nearZone: (state?.nearestSupport || state?.nearestResistance) ? true : false,
372
398
  setupForming: state?.ready && state?.activeZones > 0,
373
399
  position: data.stats.position || 0,
374
400
  price: data.stats.lastPrice || 0,
401
+ delta: data.stats.runningDelta || 0,
402
+ buyPct: buyPct,
375
403
  tickCount: data.stats.tickCount || 0,
404
+ // QUANT metrics - REAL values from strategy
405
+ zScore: state?.zScore || 0,
406
+ vpin: state?.vpin || 0,
407
+ ofi: state?.ofi || 0,
376
408
  };
377
409
 
378
410
  // Only log if we have meaningful data