hedgequantx 2.9.210 → 2.9.212

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.210",
3
+ "version": "2.9.212",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -0,0 +1,391 @@
1
+ /**
2
+ * =============================================================================
3
+ * Smart Logs Context - Instrument & Strategy Specific Messages
4
+ * =============================================================================
5
+ * Messages adaptés au contexte:
6
+ * - Instrument (NQ/MNQ, ES/MES, CL, GC, etc.)
7
+ * - Stratégie (HQX Scalping = QUANT/ticks, HQX-2B = bars/zones)
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ // ============================================================================
13
+ // INSTRUMENT CONTEXTS - Vocabulary specific to each market
14
+ // ============================================================================
15
+
16
+ const INSTRUMENT_CONTEXT = {
17
+ // NASDAQ (NQ/MNQ) - Tech-heavy, high beta, momentum-driven
18
+ NQ: {
19
+ name: 'Nasdaq',
20
+ traits: ['tech-driven', 'high-beta', 'momentum'],
21
+ bullish: [
22
+ 'Tech rally lifting index, FAANG leading',
23
+ 'Risk-on sentiment, growth stocks bid',
24
+ 'Nasdaq outperforming, tech momentum strong',
25
+ 'Mega-caps driving index higher',
26
+ 'Tech sector rotation bullish',
27
+ 'Growth over value trade active',
28
+ 'NASDAQ bid on semiconductor strength',
29
+ 'Tech bulls in control, AI momentum',
30
+ 'Index lifting on NVDA/AAPL strength',
31
+ 'Risk appetite strong, tech leading',
32
+ ],
33
+ bearish: [
34
+ 'Tech weakness dragging index',
35
+ 'Risk-off rotation out of growth',
36
+ 'Nasdaq underperforming, rates pressure',
37
+ 'Mega-cap selling pressure',
38
+ 'Tech sector rotation bearish',
39
+ 'Value over growth trade active',
40
+ 'NASDAQ offered on chip weakness',
41
+ 'Tech bears pressing, yields rising',
42
+ 'Index heavy on FAANG weakness',
43
+ 'Risk aversion hitting growth names',
44
+ ],
45
+ neutral: [
46
+ 'Tech consolidating, awaiting catalyst',
47
+ 'Nasdaq range-bound, mixed sector action',
48
+ 'Index digesting recent move',
49
+ 'Tech in balance, watching yields',
50
+ 'Mega-caps mixed, no clear direction',
51
+ ],
52
+ },
53
+
54
+ // S&P 500 (ES/MES) - Broad market, institutional, balanced
55
+ ES: {
56
+ name: 'S&P 500',
57
+ traits: ['broad-market', 'institutional', 'balanced'],
58
+ bullish: [
59
+ 'Broad market strength, risk-on',
60
+ 'S&P breaking out, breadth improving',
61
+ 'Institutional buying across sectors',
62
+ 'Index bid on economic optimism',
63
+ 'Defensive to offensive rotation',
64
+ 'Market internals bullish',
65
+ 'S&P above key moving averages',
66
+ 'Broad rally, advancers leading',
67
+ 'Index strength on volume',
68
+ 'Risk appetite improving broadly',
69
+ ],
70
+ bearish: [
71
+ 'Broad market weakness, risk-off',
72
+ 'S&P breaking down, breadth weak',
73
+ 'Institutional selling pressure',
74
+ 'Index offered on macro concerns',
75
+ 'Offensive to defensive rotation',
76
+ 'Market internals bearish',
77
+ 'S&P below key support levels',
78
+ 'Broad selling, decliners leading',
79
+ 'Index weakness on volume',
80
+ 'Risk aversion across sectors',
81
+ ],
82
+ neutral: [
83
+ 'S&P consolidating at key level',
84
+ 'Broad market in balance',
85
+ 'Index awaiting economic data',
86
+ 'Sector rotation neutral',
87
+ 'Market digesting Fed commentary',
88
+ ],
89
+ },
90
+
91
+ // Crude Oil (CL/MCL) - Energy, geopolitical, supply/demand
92
+ CL: {
93
+ name: 'Crude Oil',
94
+ traits: ['energy', 'geopolitical', 'supply-demand'],
95
+ bullish: [
96
+ 'Crude bid on supply concerns',
97
+ 'Energy rally, OPEC supporting',
98
+ 'Oil lifting on inventory draw',
99
+ 'Geopolitical risk premium rising',
100
+ 'Demand outlook improving',
101
+ 'Refinery demand strong',
102
+ 'Crude above resistance, bulls in control',
103
+ 'Energy sector leading broader market',
104
+ 'WTI bid on dollar weakness',
105
+ 'Supply disruption fears lifting prices',
106
+ ],
107
+ bearish: [
108
+ 'Crude offered on demand fears',
109
+ 'Energy selling, OPEC uncertainty',
110
+ 'Oil dropping on inventory build',
111
+ 'Geopolitical risk premium fading',
112
+ 'Demand outlook weakening',
113
+ 'Refinery maintenance weighing',
114
+ 'Crude below support, bears pressing',
115
+ 'Energy sector lagging market',
116
+ 'WTI offered on dollar strength',
117
+ 'Supply glut concerns pressuring',
118
+ ],
119
+ neutral: [
120
+ 'Crude in balance, awaiting EIA data',
121
+ 'Energy consolidating near OPEC meeting',
122
+ 'Oil range-bound on mixed signals',
123
+ 'Supply/demand in equilibrium',
124
+ 'Crude watching dollar direction',
125
+ ],
126
+ },
127
+
128
+ // Gold (GC/MGC) - Safe haven, inflation hedge, rates sensitive
129
+ GC: {
130
+ name: 'Gold',
131
+ traits: ['safe-haven', 'inflation-hedge', 'rates-sensitive'],
132
+ bullish: [
133
+ 'Gold bid on safe-haven demand',
134
+ 'Precious metals rally, risk-off',
135
+ 'Gold lifting on inflation fears',
136
+ 'Real yields dropping, gold bullish',
137
+ 'Flight to safety supporting gold',
138
+ 'Central bank buying supportive',
139
+ 'Gold above resistance, bulls active',
140
+ 'Dollar weakness lifting gold',
141
+ 'Geopolitical uncertainty bid',
142
+ 'Inflation hedge demand rising',
143
+ ],
144
+ bearish: [
145
+ 'Gold offered on risk-on sentiment',
146
+ 'Precious metals selling, yields rising',
147
+ 'Gold dropping on Fed hawkishness',
148
+ 'Real yields rising, gold bearish',
149
+ 'Risk appetite reducing safe-haven demand',
150
+ 'ETF outflows pressuring gold',
151
+ 'Gold below support, bears in control',
152
+ 'Dollar strength weighing on gold',
153
+ 'Risk-on rotation out of metals',
154
+ 'Inflation expectations cooling',
155
+ ],
156
+ neutral: [
157
+ 'Gold consolidating near key level',
158
+ 'Precious metals in balance',
159
+ 'Gold watching Fed commentary',
160
+ 'Real yields stable, gold range-bound',
161
+ 'Safe-haven demand muted',
162
+ ],
163
+ },
164
+
165
+ // Russell 2000 (RTY/M2K) - Small caps, domestic, risk appetite
166
+ RTY: {
167
+ name: 'Russell 2000',
168
+ traits: ['small-cap', 'domestic', 'risk-appetite'],
169
+ bullish: [
170
+ 'Small caps outperforming, risk-on',
171
+ 'Russell leading, domestic strength',
172
+ 'Risk appetite lifting small caps',
173
+ 'RTY breaking out, breadth strong',
174
+ 'Regional banks supporting index',
175
+ 'Small cap rotation accelerating',
176
+ 'Domestic economy optimism',
177
+ 'Russell above key resistance',
178
+ 'Risk-on favoring high-beta names',
179
+ 'Small caps catching bid',
180
+ ],
181
+ bearish: [
182
+ 'Small caps underperforming, risk-off',
183
+ 'Russell lagging, credit concerns',
184
+ 'Risk aversion hitting small caps',
185
+ 'RTY breaking down, breadth weak',
186
+ 'Regional bank weakness dragging',
187
+ 'Small cap rotation reversing',
188
+ 'Domestic economy concerns',
189
+ 'Russell below support, bears active',
190
+ 'Risk-off hitting high-beta names',
191
+ 'Small caps under pressure',
192
+ ],
193
+ neutral: [
194
+ 'Small caps consolidating',
195
+ 'Russell in balance, watching rates',
196
+ 'RTY range-bound, mixed signals',
197
+ 'Small cap sentiment neutral',
198
+ 'Regional banks mixed',
199
+ ],
200
+ },
201
+
202
+ // Dow Jones (YM/MYM) - Blue chips, value, defensive
203
+ YM: {
204
+ name: 'Dow Jones',
205
+ traits: ['blue-chip', 'value', 'defensive'],
206
+ bullish: [
207
+ 'Blue chips leading, value rotation',
208
+ 'Dow outperforming, defensive strength',
209
+ 'Industrials lifting index',
210
+ 'Value over growth trade active',
211
+ 'Dow breaking out, old economy bid',
212
+ 'Blue chip accumulation',
213
+ 'Defensive sectors leading',
214
+ 'YM above resistance, bulls active',
215
+ 'Dow component strength broad',
216
+ 'Value rotation accelerating',
217
+ ],
218
+ bearish: [
219
+ 'Blue chips lagging, growth leading',
220
+ 'Dow underperforming, cyclicals weak',
221
+ 'Industrials dragging index',
222
+ 'Growth over value trade active',
223
+ 'Dow breaking down, selling pressure',
224
+ 'Blue chip distribution',
225
+ 'Defensive sectors under pressure',
226
+ 'YM below support, bears pressing',
227
+ 'Dow component weakness broad',
228
+ 'Value rotation reversing',
229
+ ],
230
+ neutral: [
231
+ 'Blue chips consolidating',
232
+ 'Dow in balance, rotation mixed',
233
+ 'YM range-bound, awaiting catalyst',
234
+ 'Value/growth neutral',
235
+ 'Industrials mixed',
236
+ ],
237
+ },
238
+ };
239
+
240
+ // Aliases for micro contracts
241
+ INSTRUMENT_CONTEXT.MNQ = INSTRUMENT_CONTEXT.NQ;
242
+ INSTRUMENT_CONTEXT.MES = INSTRUMENT_CONTEXT.ES;
243
+ INSTRUMENT_CONTEXT.MCL = INSTRUMENT_CONTEXT.CL;
244
+ INSTRUMENT_CONTEXT.MGC = INSTRUMENT_CONTEXT.GC;
245
+ INSTRUMENT_CONTEXT.M2K = INSTRUMENT_CONTEXT.RTY;
246
+ INSTRUMENT_CONTEXT.MYM = INSTRUMENT_CONTEXT.YM;
247
+
248
+ // ============================================================================
249
+ // STRATEGY CONTEXTS - Vocabulary specific to each strategy
250
+ // ============================================================================
251
+
252
+ const STRATEGY_CONTEXT = {
253
+ // HQX Ultra Scalping - QUANT based (Z-Score, VPIN, OFI)
254
+ 'ultra-scalping': {
255
+ name: 'QUANT Scalping',
256
+ metrics: ['Z-Score', 'VPIN', 'OFI'],
257
+ warmup: [
258
+ 'QUANT models calibrating',
259
+ 'Statistical edge computation',
260
+ 'Factor model initialization',
261
+ 'Z-Score baseline forming',
262
+ 'VPIN toxicity model loading',
263
+ 'OFI imbalance calc active',
264
+ 'Mean reversion params init',
265
+ 'Volatility regime detection',
266
+ 'Microstructure analysis loading',
267
+ 'Tick distribution fitting',
268
+ ],
269
+ scanning: [
270
+ 'Scanning for statistical edge',
271
+ 'Z-Score within normal bounds',
272
+ 'No mean reversion signal',
273
+ 'VPIN clean, no toxicity',
274
+ 'OFI balanced, no imbalance',
275
+ 'Waiting for regime shift',
276
+ 'Factor model neutral',
277
+ 'Statistical noise, no alpha',
278
+ 'Mean at fair value',
279
+ 'No exploitable edge detected',
280
+ ],
281
+ signal: [
282
+ 'Statistical edge detected',
283
+ 'Z-Score extreme, mean reversion due',
284
+ 'VPIN/OFI alignment confirmed',
285
+ 'Factor model signal active',
286
+ 'Mean reversion setup forming',
287
+ 'Statistical alpha opportunity',
288
+ 'Regime shift detected',
289
+ 'Edge exploitation window',
290
+ 'QUANT signal triggered',
291
+ 'Model confidence threshold met',
292
+ ],
293
+ },
294
+
295
+ // HQX-2B Liquidity Sweep - Price action based (bars, swings, zones)
296
+ 'hqx-2b': {
297
+ name: '2B Liquidity Sweep',
298
+ metrics: ['Swings', 'Zones', 'Sweeps'],
299
+ warmup: [
300
+ 'Building swing structure',
301
+ 'Mapping liquidity zones',
302
+ 'Detecting pivot points',
303
+ 'Zone formation analysis',
304
+ 'Swing detection active',
305
+ 'Price structure mapping',
306
+ 'Liquidity pool scanning',
307
+ 'Support/resistance mapping',
308
+ 'Order block detection',
309
+ 'Market structure loading',
310
+ ],
311
+ scanning: [
312
+ 'Monitoring swing structure',
313
+ 'No zone in range',
314
+ 'Waiting for sweep setup',
315
+ 'Price away from zones',
316
+ 'No liquidity sweep detected',
317
+ 'Watching for pivot break',
318
+ 'Structure intact, no setup',
319
+ 'Zones mapped, awaiting price',
320
+ 'Swing highs/lows holding',
321
+ 'No 2B pattern forming',
322
+ ],
323
+ signal: [
324
+ 'Liquidity sweep detected',
325
+ 'Zone penetration confirmed',
326
+ '2B reversal pattern forming',
327
+ 'Stop hunt completed',
328
+ 'Sweep and reject signal',
329
+ 'Zone touch with rejection',
330
+ 'Liquidity taken, reversing',
331
+ 'False break confirmed',
332
+ '2B setup triggered',
333
+ 'Zone sweep entry active',
334
+ ],
335
+ },
336
+ };
337
+
338
+ // ============================================================================
339
+ // MESSAGE GENERATOR
340
+ // ============================================================================
341
+
342
+ /**
343
+ * Get contextual message based on instrument and strategy
344
+ * @param {string} symbol - Contract symbol (e.g., 'MNQH6', 'ESH6')
345
+ * @param {string} strategyId - Strategy ID ('ultra-scalping' or 'hqx-2b')
346
+ * @param {string} context - Message context ('bullish', 'bearish', 'neutral', 'warmup', 'scanning', 'signal')
347
+ * @returns {string} Contextual message
348
+ */
349
+ function getContextualMessage(symbol, strategyId, context) {
350
+ // Extract base symbol (remove contract month)
351
+ const baseSymbol = symbol.replace(/[FGHJKMNQUVXZ]\d{1,2}(:|$).*/, '').toUpperCase();
352
+
353
+ // Get instrument context
354
+ const instrument = INSTRUMENT_CONTEXT[baseSymbol] || INSTRUMENT_CONTEXT.ES; // Default to ES
355
+
356
+ // Get strategy context
357
+ const strategy = STRATEGY_CONTEXT[strategyId] || STRATEGY_CONTEXT['hqx-2b'];
358
+
359
+ let pool = [];
360
+
361
+ // Market direction messages (from instrument)
362
+ if (context === 'bullish' || context === 'bearish' || context === 'neutral') {
363
+ pool = instrument[context] || [];
364
+ }
365
+ // Strategy state messages
366
+ else if (context === 'warmup' || context === 'scanning' || context === 'signal') {
367
+ pool = strategy[context] || [];
368
+ }
369
+
370
+ // Return random message from pool, or fallback
371
+ if (pool.length > 0) {
372
+ return pool[Math.floor(Math.random() * pool.length)];
373
+ }
374
+
375
+ return 'Analyzing market conditions...';
376
+ }
377
+
378
+ /**
379
+ * Get instrument name from symbol
380
+ */
381
+ function getInstrumentName(symbol) {
382
+ const baseSymbol = symbol.replace(/[FGHJKMNQUVXZ]\d{1,2}(:|$).*/, '').toUpperCase();
383
+ return INSTRUMENT_CONTEXT[baseSymbol]?.name || 'Futures';
384
+ }
385
+
386
+ module.exports = {
387
+ INSTRUMENT_CONTEXT,
388
+ STRATEGY_CONTEXT,
389
+ getContextualMessage,
390
+ getInstrumentName,
391
+ };
@@ -14,6 +14,7 @@
14
14
 
15
15
  const chalk = require('chalk');
16
16
  const smartLogs = require('./smart-logs');
17
+ const { getContextualMessage } = require('./smart-logs-context');
17
18
 
18
19
  const CONFIG = {
19
20
  SESSION_LOG_INTERVAL: 10,
@@ -51,6 +52,7 @@ class SmartLogsEngine {
51
52
  this.symbolCode = symbol;
52
53
  this.counter = 0;
53
54
  this.lastState = null;
55
+ this.lastHeartbeat = 0;
54
56
 
55
57
  // State tracking for event detection (both strategies)
56
58
  this.lastBias = null;
@@ -112,34 +114,36 @@ class SmartLogsEngine {
112
114
  if (bars >= 10 && !this.warmupLogged) {
113
115
  this.warmupLogged = true;
114
116
  event = 'warmup';
115
- message = `[${sym}] Strategy ready | ${bars} bars | Scanning for setups`;
117
+ const warmupMsg = getContextualMessage(this.symbolCode, this.strategyId, 'warmup');
118
+ message = `[${sym}] 2B ready | ${bars} bars | ${warmupMsg}`;
116
119
  logType = 'system';
117
120
  }
118
121
  // EVENT 2: New zone created
119
122
  else if (zones > this.lastZones && zones > 0) {
120
123
  event = 'new_zone';
121
- const liveMsg = smartLogs.getLiveAnalysisLog({ trend, bars, swings, zones, nearZone, setupForming: false });
122
- message = `[${sym}] ${price} | Zone #${zones} created | ${liveMsg}`;
124
+ const signalMsg = getContextualMessage(this.symbolCode, this.strategyId, 'signal');
125
+ message = `[${sym}] ${price} | Zone #${zones} | ${signalMsg}`;
123
126
  logType = 'signal';
124
127
  }
125
128
  // EVENT 3: New swing detected
126
129
  else if (swings > this.lastSwings && swings > 0) {
127
130
  event = 'new_swing';
128
- const liveMsg = smartLogs.getLiveAnalysisLog({ trend, bars, swings, zones, nearZone, setupForming: false });
129
- message = `[${sym}] ${price} | Swing #${swings} | ${liveMsg}`;
131
+ const scanMsg = getContextualMessage(this.symbolCode, this.strategyId, 'scanning');
132
+ message = `[${sym}] ${price} | Swing #${swings} | ${scanMsg}`;
130
133
  }
131
134
  // EVENT 4: Zone approach (price near zone)
132
135
  else if (nearZone && !this.lastNearZone && zones > 0) {
133
136
  event = 'zone_approach';
134
- const liveMsg = smartLogs.getLiveAnalysisLog({ trend, bars, swings, zones, nearZone: true, setupForming: true });
135
- message = `[${sym}] ${price} | Approaching zone | ${liveMsg}`;
137
+ const signalMsg = getContextualMessage(this.symbolCode, this.strategyId, 'signal');
138
+ message = `[${sym}] ${price} | Zone approach | ${signalMsg}`;
136
139
  logType = 'signal';
137
140
  }
138
141
  // EVENT 5: Bias flip
139
142
  else if (this.lastBias && trend !== this.lastBias && trend !== 'neutral' && this.lastBias !== 'neutral') {
140
143
  event = 'bias_flip';
141
144
  const arrow = trend === 'bullish' ? chalk.green('▲') : chalk.red('▼');
142
- message = `[${sym}] ${arrow} Bias: ${this.lastBias} → ${trend} | Delta: ${delta}`;
145
+ const flipMsg = getContextualMessage(this.symbolCode, this.strategyId, trend);
146
+ message = `[${sym}] ${arrow} ${this.lastBias} → ${trend} | ${flipMsg}`;
143
147
  }
144
148
 
145
149
  // Update state tracking
@@ -175,33 +179,38 @@ class SmartLogsEngine {
175
179
  if (ticks >= CONFIG.QUANT_WARMUP_TICKS && !this.warmupLogged) {
176
180
  this.warmupLogged = true;
177
181
  event = 'warmup';
178
- message = `[${sym}] QUANT models ready | ${ticks} ticks | Z-Score/VPIN/OFI active`;
182
+ const warmupMsg = getContextualMessage(this.symbolCode, this.strategyId, 'warmup');
183
+ message = `[${sym}] QUANT ready | ${ticks} ticks | ${warmupMsg}`;
179
184
  logType = 'system';
180
185
  }
181
186
  // EVENT 2: Z-Score regime change
182
187
  else if (this.lastZRegime !== null && zRegime !== this.lastZRegime) {
183
188
  event = 'z_regime';
184
- const liveState = { trend: bias, bars: ticks, swings: absZ >= 1.0 ? 1 : 0, zones: absZ >= 1.5 ? 1 : 0, nearZone: absZ >= 1.5, setupForming: absZ >= 2.0 };
185
- const liveMsg = smartLogs.getLiveAnalysisLog(liveState);
189
+ // Get instrument-specific market context message
190
+ const marketCtx = bias === 'bullish' ? 'bullish' : bias === 'bearish' ? 'bearish' : 'neutral';
191
+ const instrumentMsg = getContextualMessage(this.symbolCode, this.strategyId, marketCtx);
186
192
 
187
193
  if (zRegime === 'extreme') {
188
194
  logType = 'signal';
189
195
  const dir = zScore < 0 ? 'LONG' : 'SHORT';
190
- message = `[${sym}] ${price} | Z: ${zScore.toFixed(1)}σ | ${dir} edge | ${liveMsg}`;
196
+ const signalMsg = getContextualMessage(this.symbolCode, this.strategyId, 'signal');
197
+ message = `[${sym}] ${price} | Z: ${zScore.toFixed(1)}σ | ${dir} | ${signalMsg}`;
191
198
  } else if (zRegime === 'high') {
192
199
  logType = 'signal';
193
- message = `[${sym}] ${price} | Z: ${zScore.toFixed(1)}σ approaching | ${liveMsg}`;
200
+ message = `[${sym}] ${price} | Z: ${zScore.toFixed(1)}σ | ${instrumentMsg}`;
194
201
  } else if (zRegime === 'building') {
195
- message = `[${sym}] ${price} | Z building (${zScore.toFixed(1)}σ) | ${liveMsg}`;
202
+ message = `[${sym}] ${price} | Z building (${zScore.toFixed(1)}σ) | ${instrumentMsg}`;
196
203
  } else {
197
- message = `[${sym}] ${price} | Z normalized | ${liveMsg}`;
204
+ const scanMsg = getContextualMessage(this.symbolCode, this.strategyId, 'scanning');
205
+ message = `[${sym}] ${price} | Z normalized | ${scanMsg}`;
198
206
  }
199
207
  }
200
208
  // EVENT 3: Bias flip (OFI direction change)
201
209
  else if (this.lastBias !== null && bias !== this.lastBias && bias !== 'neutral' && this.lastBias !== 'neutral') {
202
210
  event = 'bias_flip';
203
211
  const arrow = bias === 'bullish' ? chalk.green('▲') : chalk.red('▼');
204
- message = `[${sym}] ${arrow} OFI flip: ${this.lastBias} → ${bias} | ${(ofi * 100).toFixed(0)}%`;
212
+ const flipMsg = getContextualMessage(this.symbolCode, this.strategyId, bias);
213
+ message = `[${sym}] ${arrow} OFI: ${this.lastBias} → ${bias} | ${flipMsg}`;
205
214
  }
206
215
  // EVENT 4: VPIN toxicity change
207
216
  else if (this.lastVpinToxic !== null && vpinToxic !== this.lastVpinToxic) {
@@ -220,8 +229,24 @@ class SmartLogsEngine {
220
229
  this.lastVpinToxic = vpinToxic;
221
230
 
222
231
  if (event && message) {
232
+ this.lastHeartbeat = Date.now();
223
233
  return { type: logType, message, logToSession: event === 'z_regime' || event === 'bias_flip' };
224
234
  }
235
+
236
+ // HEARTBEAT: Show status every 30s when no events (proves strategy is active)
237
+ const now = Date.now();
238
+ if (this.warmupLogged && now - this.lastHeartbeat >= 30000) {
239
+ this.lastHeartbeat = now;
240
+ // Use instrument + strategy contextual message
241
+ const marketCtx = bias === 'bullish' ? 'bullish' : bias === 'bearish' ? 'bearish' : 'neutral';
242
+ const ctxMsg = getContextualMessage(this.symbolCode, this.strategyId, marketCtx);
243
+ return {
244
+ type: 'analysis',
245
+ message: `[${sym}] ${price} | Z: ${zScore.toFixed(1)}σ | OFI: ${(ofi * 100).toFixed(0)}% | ${ctxMsg}`,
246
+ logToSession: false
247
+ };
248
+ }
249
+
225
250
  return null;
226
251
  }
227
252
 
@@ -230,6 +255,7 @@ class SmartLogsEngine {
230
255
  this.counter = 0;
231
256
  this.lastBias = null;
232
257
  this.warmupLogged = false;
258
+ this.lastHeartbeat = 0;
233
259
  // HQX-2B
234
260
  this.lastBars = 0;
235
261
  this.lastSwings = 0;
@@ -344,20 +344,25 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
344
344
  await marketFeed.connect(rithmicCredentials);
345
345
  await marketFeed.subscribe(symbolCode, contract.exchange || 'CME');
346
346
 
347
- // Load historical bars for instant warmup
347
+ // Load historical data for model warmup (optional - works with live data too)
348
348
  if (strategy.preloadBars) {
349
- ui.addLog('system', 'Loading historical data...');
349
+ ui.addLog('system', 'Loading warmup data...');
350
350
  try {
351
351
  const histBars = await marketFeed.getHistoricalBars(symbolCode, contract.exchange || 'CME', 30);
352
352
  if (histBars && histBars.length > 0) {
353
353
  strategy.preloadBars(contractId, histBars);
354
- ui.addLog('system', `Loaded ${histBars.length} historical bars - ready to trade!`);
355
- sessionLogger.log('HISTORY', `Preloaded ${histBars.length} bars`);
354
+ // Different message for tick-based vs bar-based strategies
355
+ const isTickBased = strategyId === 'ultra-scalping';
356
+ const msg = isTickBased
357
+ ? `Warmup data loaded - QUANT models initializing...`
358
+ : `Loaded ${histBars.length} historical bars - ready to trade!`;
359
+ ui.addLog('system', msg);
360
+ sessionLogger.log('HISTORY', `Preloaded ${histBars.length} bars for warmup`);
356
361
  } else {
357
- ui.addLog('system', 'No history available - warming up with live data...');
362
+ ui.addLog('system', 'No history - warming up with live ticks...');
358
363
  }
359
364
  } catch (histErr) {
360
- ui.addLog('system', `History load failed: ${histErr.message} - using live data`);
365
+ ui.addLog('system', `Warmup skipped - using live data`);
361
366
  sessionLogger.log('HISTORY', `Failed: ${histErr.message}`);
362
367
  }
363
368
  }