hedgequantx 2.9.140 → 2.9.142

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.
@@ -37,6 +37,15 @@ const {
37
37
  STRATEGY_MESSAGES,
38
38
  } = require('./smart-logs-messages');
39
39
 
40
+ const {
41
+ LIVE_BULLISH,
42
+ LIVE_BEARISH,
43
+ LIVE_NEUTRAL,
44
+ LIVE_BUILDING,
45
+ LIVE_ZONES,
46
+ LIVE_READY,
47
+ } = require('./smart-logs-live');
48
+
40
49
  // Current strategy ID for context-aware messages
41
50
  let currentStrategyId = null;
42
51
 
@@ -245,6 +254,66 @@ function getPriceChangeLog(direction, price, change) {
245
254
  return { message: `${arrow} ${price.toFixed(2)}`, details: `${direction} ${change.toFixed(2)}` };
246
255
  }
247
256
 
257
+ /**
258
+ * Get live analysis log based on market and strategy state
259
+ * @param {Object} state - Current state
260
+ * @param {string} state.trend - 'bullish', 'bearish', or 'neutral'
261
+ * @param {number} state.bars - Number of bars processed
262
+ * @param {number} state.swings - Number of swings detected
263
+ * @param {number} state.zones - Number of active zones
264
+ * @param {number} state.price - Current price
265
+ * @param {boolean} state.nearZone - Whether price is near a zone
266
+ * @param {boolean} state.setupForming - Whether a setup is forming
267
+ * @returns {string} Contextual analysis message
268
+ */
269
+ function getLiveAnalysisLog(state = {}) {
270
+ const { trend, bars, swings, zones, nearZone, setupForming } = state;
271
+
272
+ let pool;
273
+ let category;
274
+
275
+ // Determine which pool to use based on state priority
276
+ if (setupForming && zones > 0) {
277
+ // Setup is forming - high priority
278
+ pool = LIVE_READY;
279
+ category = 'live_ready';
280
+ } else if (nearZone && zones > 0) {
281
+ // Near a zone - watching
282
+ pool = LIVE_ZONES;
283
+ category = 'live_zones';
284
+ } else if (zones > 0 && swings >= 2) {
285
+ // Have zones and swings - market pool based on trend
286
+ if (trend === 'bullish') {
287
+ pool = LIVE_BULLISH;
288
+ category = 'live_bullish';
289
+ } else if (trend === 'bearish') {
290
+ pool = LIVE_BEARISH;
291
+ category = 'live_bearish';
292
+ } else {
293
+ pool = LIVE_NEUTRAL;
294
+ category = 'live_neutral';
295
+ }
296
+ } else if (bars < 10 || swings < 2) {
297
+ // Still building structure
298
+ pool = LIVE_BUILDING;
299
+ category = 'live_building';
300
+ } else {
301
+ // Default to market trend
302
+ if (trend === 'bullish') {
303
+ pool = LIVE_BULLISH;
304
+ category = 'live_bullish';
305
+ } else if (trend === 'bearish') {
306
+ pool = LIVE_BEARISH;
307
+ category = 'live_bearish';
308
+ } else {
309
+ pool = LIVE_NEUTRAL;
310
+ category = 'live_neutral';
311
+ }
312
+ }
313
+
314
+ return getVariedMessage(category, pool, 'Analyzing market conditions...');
315
+ }
316
+
248
317
  module.exports = {
249
318
  getMarketBiasLog,
250
319
  getSignalLog,
@@ -260,5 +329,6 @@ module.exports = {
260
329
  getModelAnalysisLog,
261
330
  getPositionUpdateLog,
262
331
  getPriceChangeLog,
332
+ getLiveAnalysisLog,
263
333
  setStrategy,
264
334
  };
@@ -7,6 +7,7 @@ const { loadStrategy } = require('../../lib/m');
7
7
  const { MarketDataFeed } = require('../../lib/data');
8
8
  const { SupervisionEngine } = require('../../services/ai-supervision');
9
9
  const smartLogs = require('../../lib/smart-logs');
10
+ const { createEngine: createLogsEngine } = require('../../lib/smart-logs-engine');
10
11
  const { sessionLogger } = require('../../services/session-logger');
11
12
 
12
13
  /**
@@ -45,29 +46,13 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
45
46
  });
46
47
 
47
48
  const stats = {
48
- accountName,
49
- symbol: symbolName,
50
- qty: contracts,
51
- target: dailyTarget,
52
- risk: maxRisk,
53
- propfirm: account.propfirm || 'Unknown',
54
- platform: account.platform || 'Rithmic',
55
- pnl: 0,
56
- trades: 0,
57
- wins: 0,
58
- losses: 0,
59
- latency: 0,
60
- connected: false,
61
- startTime: Date.now()
49
+ accountName, symbol: symbolName, qty: contracts, target: dailyTarget, risk: maxRisk,
50
+ propfirm: account.propfirm || 'Unknown', platform: account.platform || 'Rithmic',
51
+ pnl: 0, trades: 0, wins: 0, losses: 0, latency: 0, connected: false, startTime: Date.now()
62
52
  };
63
53
 
64
- let running = true;
65
- let stopReason = null;
66
- let startingPnL = null;
67
- let currentPosition = 0;
68
- let pendingOrder = false;
69
- let tickCount = 0;
70
- let lastBias = 'FLAT';
54
+ let running = true, stopReason = null, startingPnL = null;
55
+ let currentPosition = 0, pendingOrder = false, tickCount = 0, lastBias = 'FLAT';
71
56
 
72
57
  const aiContext = { recentTicks: [], recentSignals: [], recentTrades: [], maxTicks: 100 };
73
58
 
@@ -76,6 +61,7 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
76
61
 
77
62
  // Set strategy for context-aware smart logs
78
63
  smartLogs.setStrategy(strategyId);
64
+ const logsEngine = createLogsEngine(strategyId, symbolCode);
79
65
 
80
66
  // Start session logger for persistent logs
81
67
  const logFile = sessionLogger.start({
@@ -234,28 +220,18 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
234
220
  pendingOrder = false;
235
221
  });
236
222
 
237
- let lastPrice = null;
238
- let lastBid = null;
239
- let lastAsk = null;
240
- let ticksPerSecond = 0;
241
- let lastTickSecond = Math.floor(Date.now() / 1000);
242
- let lastBiasLogSecond = 0;
243
- let lastDebugLogSecond = 0;
244
- let lastStateLogSecond = 0;
245
- let buyVolume = 0;
246
- let sellVolume = 0;
247
-
248
-
249
- let lastTickTime = 0;
250
- let tickLatencies = [];
223
+ let lastPrice = null, lastBid = null, lastAsk = null;
224
+ let ticksPerSecond = 0, lastTickSecond = Math.floor(Date.now() / 1000);
225
+ let lastBiasLogSecond = 0, lastStateLogSecond = 0;
226
+ let buyVolume = 0, sellVolume = 0, lastTickTime = 0, tickLatencies = [];
251
227
 
252
228
  marketFeed.on('tick', (tick) => {
253
229
  tickCount++;
254
230
  const now = Date.now();
255
231
  const currentSecond = Math.floor(now / 1000);
256
232
 
257
- // Debug first 5 ticks to verify data
258
- if (tickCount <= 5) {
233
+ // Debug first tick
234
+ if (tickCount === 1) {
259
235
  const p = Number(tick.price) || Number(tick.tradePrice) || 'NULL';
260
236
  sessionLogger.log('TICK', `#${tickCount} price=${p} symbol=${tick.symbol || tick.contractId || 'N/A'}`);
261
237
  }
@@ -284,66 +260,27 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
284
260
  else if (price < lastPrice) sellVolume += volume;
285
261
  }
286
262
 
287
- // Log first tick and periodic tick count
263
+ // Log first tick
288
264
  if (tickCount === 1) {
289
265
  ui.addLog('connected', `First tick @ ${price?.toFixed(2) || 'N/A'}`);
290
266
  }
291
267
 
292
- // Log tick count every 60 seconds to confirm data flow
293
- if (currentSecond - lastDebugLogSecond >= 60) {
294
- lastDebugLogSecond = currentSecond;
295
- const state = strategy.getAnalysisState?.(contractId, price);
296
- const bars = state?.barsProcessed || 0;
297
- ui.addLog('debug', `Ticks: ${tickCount} | Bars: ${bars} | Price: ${price?.toFixed(2)}`);
298
- }
299
-
300
- // === SMART LOGS - REDUCED FREQUENCY ===
301
- // Log bias every 30 seconds (was 5s - too verbose)
268
+ // Update bias from volume + log tick stats (every 30s)
302
269
  if (currentSecond - lastBiasLogSecond >= 30 && tickCount > 1) {
303
270
  lastBiasLogSecond = currentSecond;
304
-
305
271
  const totalVol = buyVolume + sellVolume;
306
272
  const buyPressure = totalVol > 0 ? (buyVolume / totalVol) * 100 : 50;
307
- const delta = buyVolume - sellVolume;
308
-
309
- let bias = buyPressure > 55 ? 'LONG' : buyPressure < 45 ? 'SHORT' : 'FLAT';
310
- const biasLog = smartLogs.getMarketBiasLog(bias, delta, buyPressure);
311
- const biasType = bias === 'LONG' ? 'bullish' : bias === 'SHORT' ? 'bearish' : 'analysis';
312
- ui.addLog(biasType, `${biasLog.message} ${biasLog.details || ''}`);
313
- lastBias = bias;
314
- // Reset volume after logging to avoid accumulation
315
- buyVolume = 0;
316
- sellVolume = 0;
273
+ lastBias = buyPressure > 55 ? 'LONG' : buyPressure < 45 ? 'SHORT' : 'FLAT';
274
+ sessionLogger.log('TICK', `count=${tickCount} last=${price?.toFixed(2)} bias=${lastBias} vol=${totalVol}`);
275
+ buyVolume = 0; sellVolume = 0;
317
276
  }
318
277
 
319
- // Strategy state log every 60 seconds (was 30s - too verbose)
278
+ // Strategy state log for session logger (every 60s)
320
279
  if (currentSecond - lastStateLogSecond >= 60 && tickCount > 1) {
321
280
  lastStateLogSecond = currentSecond;
322
281
  const state = strategy.getAnalysisState?.(contractId, price);
323
282
  if (state) {
324
- const bars = state.barsProcessed || 0;
325
- sessionLogger.state(state.activeZones || 0, state.swingsDetected || 0, bars, lastBias);
326
- if (!state.ready) {
327
- ui.addLog('system', `${state.message} (${bars} bars)`);
328
- } else {
329
- const resStr = state.nearestResistance ? state.nearestResistance.toFixed(2) : '--';
330
- const supStr = state.nearestSupport ? state.nearestSupport.toFixed(2) : '--';
331
-
332
- ui.addLog('analysis', `Zones: ${state.activeZones} | R: ${resStr} | S: ${supStr} | Swings: ${state.swingsDetected}`);
333
- if (price && state.nearestResistance) {
334
- const gapR = state.nearestResistance - price, ticksR = Math.abs(Math.round(gapR / tickSize));
335
- if (ticksR <= 50) ui.addLog('analysis', `PROX R: ${Math.abs(gapR).toFixed(2)} pts (${ticksR} ticks) | Sweep ABOVE then reject`);
336
- }
337
- if (price && state.nearestSupport) {
338
- const gapS = price - state.nearestSupport, ticksS = Math.abs(Math.round(gapS / tickSize));
339
- if (ticksS <= 50) ui.addLog('analysis', `PROX S: ${Math.abs(gapS).toFixed(2)} pts (${ticksS} ticks) | Sweep BELOW then reject`);
340
- }
341
- if (state.activeZones === 0) ui.addLog('risk', 'Building liquidity map...');
342
- else if (!state.nearestSupport && !state.nearestResistance) ui.addLog('risk', 'Zones outside range');
343
- else if (!state.nearestSupport) ui.addLog('analysis', 'Monitoring R for SHORT sweep');
344
- else if (!state.nearestResistance) ui.addLog('analysis', 'Monitoring S for LONG sweep');
345
- else ui.addLog('ready', 'Both zones active - awaiting sweep');
346
- }
283
+ sessionLogger.state(state.activeZones || 0, state.swingsDetected || 0, state.barsProcessed || 0, lastBias);
347
284
  }
348
285
  }
349
286
 
@@ -468,6 +405,33 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
468
405
  const pnlInterval = setInterval(() => { if (running) pollPnL(); }, 2000);
469
406
  pollPnL();
470
407
 
408
+ // Live analysis logs every 1 second
409
+ let lastLiveLogSecond = 0;
410
+ const liveLogInterval = setInterval(() => {
411
+ if (!running) return;
412
+ const now = Math.floor(Date.now() / 1000);
413
+ if (now === lastLiveLogSecond) return;
414
+ lastLiveLogSecond = now;
415
+
416
+ // Get strategy state for context
417
+ const state = strategy.getAnalysisState?.(contractId, lastPrice);
418
+ const logState = {
419
+ bars: state?.barsProcessed || 0,
420
+ swings: state?.swingsDetected || 0,
421
+ zones: state?.activeZones || 0,
422
+ trend: lastBias === 'LONG' ? 'bullish' : lastBias === 'SHORT' ? 'bearish' : 'neutral',
423
+ nearZone: (state?.nearestSupport || state?.nearestResistance) ? true : false,
424
+ setupForming: state?.ready && state?.activeZones > 0,
425
+ position: currentPosition,
426
+ price: lastPrice || 0,
427
+ tickCount,
428
+ };
429
+
430
+ const log = logsEngine.getLog(logState);
431
+ ui.addLog(log.type, log.message);
432
+ if (log.logToSession) sessionLogger.log('ANALYSIS', log.message);
433
+ }, 1000);
434
+
471
435
  const setupKeyHandler = () => {
472
436
  if (!process.stdin.isTTY) return null;
473
437
  readline.emitKeypressEvents(process.stdin);
@@ -493,6 +457,7 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
493
457
 
494
458
  clearInterval(refreshInterval);
495
459
  clearInterval(pnlInterval);
460
+ clearInterval(liveLogInterval);
496
461
  await marketFeed.disconnect();
497
462
  if (cleanupKeys) cleanupKeys();
498
463
  ui.cleanup();
@@ -7,6 +7,7 @@ const { AlgoUI, renderSessionSummary } = require('./ui');
7
7
  const { loadStrategy } = require('../../lib/m');
8
8
  const { MarketDataFeed } = require('../../lib/data');
9
9
  const smartLogs = require('../../lib/smart-logs');
10
+ const { createEngine: createLogsEngine } = require('../../lib/smart-logs-engine');
10
11
  const { sessionLogger } = require('../../services/session-logger');
11
12
 
12
13
  /**
@@ -106,6 +107,7 @@ const executeMultiSymbol = async ({ service, account, contracts, config, strateg
106
107
  });
107
108
 
108
109
  smartLogs.setStrategy(strategyId);
110
+ const logsEngine = createLogsEngine(strategyId);
109
111
 
110
112
  // Start session logger
111
113
  const logFile = sessionLogger.start({
@@ -212,6 +214,7 @@ const executeMultiSymbol = async ({ service, account, contracts, config, strateg
212
214
 
213
215
  if (data.stats.tickCount === 1) {
214
216
  ui.addLog('connected', `[${symbolCode}] First tick @ ${price?.toFixed(2) || 'N/A'}`);
217
+ sessionLogger.log('TICK', `[${symbolCode}] #1 price=${price} symbol=${symbolCode}`);
215
218
  }
216
219
 
217
220
  data.stats.lastPrice = price;
@@ -237,23 +240,24 @@ const executeMultiSymbol = async ({ service, account, contracts, config, strateg
237
240
  }
238
241
  });
239
242
 
240
- // Log aggregated stats periodically (every 3 minutes)
243
+ // Log aggregated stats periodically (every 30s to session, every 3min to UI)
241
244
  const logInterval = setInterval(() => {
242
245
  const now = Math.floor(Date.now() / 1000);
243
- if (now - lastLogSecond >= 180) {
244
- lastLogSecond = now;
245
- let totalTicks = 0;
246
- let totalBars = 0;
247
- let totalZones = 0;
248
- let totalSwings = 0;
249
- for (const [sym, data] of symbolData) {
250
- totalTicks += data.stats.tickCount;
251
- const state = data.strategy.getAnalysisState?.(sym, data.stats.lastPrice);
252
- totalBars += state?.barsProcessed || 0;
253
- totalZones += state?.activeZones || 0;
254
- totalSwings += state?.swingsDetected || 0;
246
+ let totalTicks = 0, totalBars = 0, totalZones = 0, totalSwings = 0;
247
+ for (const [sym, data] of symbolData) {
248
+ totalTicks += data.stats.tickCount;
249
+ const state = data.strategy.getAnalysisState?.(sym, data.stats.lastPrice);
250
+ totalBars += state?.barsProcessed || 0;
251
+ totalZones += state?.activeZones || 0;
252
+ totalSwings += state?.swingsDetected || 0;
253
+ }
254
+ // Session log every 30s
255
+ if (now - lastLogSecond >= 30) {
256
+ sessionLogger.log('TICK', `count=${totalTicks} bars=${totalBars} zones=${totalZones} swings=${totalSwings}`);
257
+ if (now - lastLogSecond >= 180) {
258
+ ui.addLog('analysis', `Stats: ${totalTicks} ticks | ${totalBars} bars | ${totalZones} zones | ${totalSwings} swings`);
255
259
  }
256
- ui.addLog('analysis', `Stats: ${totalTicks} ticks | ${totalBars} bars | ${totalZones} zones | ${totalSwings} swings`);
260
+ lastLogSecond = now;
257
261
  }
258
262
  }, 10000);
259
263
 
@@ -314,6 +318,44 @@ const executeMultiSymbol = async ({ service, account, contracts, config, strateg
314
318
  const pnlInterval = setInterval(() => { if (running) pollPnL(); }, 2000);
315
319
  pollPnL();
316
320
 
321
+ // Live analysis logs every 1 second (rotates through symbols)
322
+ let liveLogSymbolIndex = 0;
323
+ let lastLiveLogSecond = 0;
324
+ const liveLogInterval = setInterval(() => {
325
+ if (!running) return;
326
+ const now = Math.floor(Date.now() / 1000);
327
+ if (now === lastLiveLogSecond) return;
328
+ lastLiveLogSecond = now;
329
+
330
+ // Get a symbol to log (rotate through all)
331
+ const symbolCodes = Array.from(symbolData.keys());
332
+ if (symbolCodes.length === 0) return;
333
+
334
+ const symbolCode = symbolCodes[liveLogSymbolIndex % symbolCodes.length];
335
+ liveLogSymbolIndex++;
336
+
337
+ const data = symbolData.get(symbolCode);
338
+ if (!data) return;
339
+
340
+ const state = data.strategy.getAnalysisState?.(symbolCode, data.stats.lastPrice);
341
+ const logState = {
342
+ bars: state?.barsProcessed || 0,
343
+ swings: state?.swingsDetected || 0,
344
+ zones: state?.activeZones || 0,
345
+ trend: 'neutral',
346
+ nearZone: (state?.nearestSupport || state?.nearestResistance) ? true : false,
347
+ setupForming: state?.ready && state?.activeZones > 0,
348
+ position: data.stats.position || 0,
349
+ price: data.stats.lastPrice || 0,
350
+ tickCount: data.stats.tickCount || 0,
351
+ };
352
+
353
+ logsEngine.setSymbol(symbolCode);
354
+ const log = logsEngine.getLog(logState);
355
+ ui.addLog(log.type, log.message);
356
+ if (log.logToSession) sessionLogger.log('ANALYSIS', `[${symbolCode}] ${log.message}`);
357
+ }, 1000);
358
+
317
359
  // Key handler
318
360
  const setupKeyHandler = () => {
319
361
  if (!process.stdin.isTTY) return null;
@@ -344,6 +386,7 @@ const executeMultiSymbol = async ({ service, account, contracts, config, strateg
344
386
  clearInterval(refreshInterval);
345
387
  clearInterval(pnlInterval);
346
388
  clearInterval(logInterval);
389
+ clearInterval(liveLogInterval);
347
390
  await marketFeed.disconnect();
348
391
  if (cleanupKeys) cleanupKeys();
349
392
  ui.cleanup();