hedgequantx 2.4.29 → 2.4.31

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.4.29",
3
+ "version": "2.4.31",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -11,6 +11,7 @@ const { connections } = require('../../services');
11
11
  const { AlgoUI, renderSessionSummary } = require('./ui');
12
12
  const { logger, prompts } = require('../../utils');
13
13
  const { checkMarketHours } = require('../../services/projectx/market');
14
+ const { algoLogger } = require('./logger');
14
15
 
15
16
  const log = logger.scope('CopyTrading');
16
17
 
@@ -299,10 +300,17 @@ const launchCopyTrading = async (config) => {
299
300
  }
300
301
  };
301
302
 
302
- // Local copy trading - no external server needed
303
- ui.addLog('info', `Starting copy trading on ${stats.platform}...`);
304
- ui.addLog('info', `Lead: ${stats.leadName} -> Follower: ${stats.followerName}`);
305
- ui.addLog('info', `Symbol: ${stats.symbol} | Target: $${dailyTarget} | Risk: $${maxRisk}`);
303
+ // Smart startup logs (same as HQX-TG)
304
+ const market = checkMarketHours();
305
+ const sessionName = market.session || 'AMERICAN';
306
+ const etTime = new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', timeZone: 'America/New_York' });
307
+
308
+ algoLogger.connectingToEngine(ui, lead.account.accountId);
309
+ algoLogger.engineStarting(ui, stats.platform, dailyTarget, maxRisk);
310
+ algoLogger.marketOpen(ui, sessionName.toUpperCase(), etTime);
311
+ algoLogger.info(ui, 'COPY MODE', `Lead: ${lead.propfirm} -> Follower: ${follower.propfirm}`);
312
+ algoLogger.dataConnected(ui, 'API');
313
+ algoLogger.algoOperational(ui, stats.platform);
306
314
  stats.connected = true;
307
315
 
308
316
  // Track lead positions and copy to follower
@@ -321,7 +329,12 @@ const launchCopyTrading = async (config) => {
321
329
  const existing = lastLeadPositions.find(p => p.contractId === pos.contractId);
322
330
  if (!existing && pos.quantity !== 0) {
323
331
  // New position opened - copy to follower
324
- ui.addLog('trade', `Lead opened: ${pos.quantity > 0 ? 'LONG' : 'SHORT'} ${Math.abs(pos.quantity)}x ${pos.symbol || pos.contractId}`);
332
+ const side = pos.quantity > 0 ? 'LONG' : 'SHORT';
333
+ const symbol = pos.symbol || pos.contractId;
334
+ const size = Math.abs(pos.quantity);
335
+ const entry = pos.averagePrice || 0;
336
+ algoLogger.positionOpened(ui, symbol, side, size, entry);
337
+ algoLogger.info(ui, 'COPYING TO FOLLOWER', `${side} ${size}x ${symbol}`);
325
338
  // TODO: Place order on follower account
326
339
  }
327
340
  }
@@ -330,7 +343,13 @@ const launchCopyTrading = async (config) => {
330
343
  for (const oldPos of lastLeadPositions) {
331
344
  const stillOpen = currentPositions.find(p => p.contractId === oldPos.contractId);
332
345
  if (!stillOpen || stillOpen.quantity === 0) {
333
- ui.addLog('info', `Lead closed: ${oldPos.symbol || oldPos.contractId}`);
346
+ const side = oldPos.quantity > 0 ? 'LONG' : 'SHORT';
347
+ const symbol = oldPos.symbol || oldPos.contractId;
348
+ const size = Math.abs(oldPos.quantity);
349
+ const exit = stillOpen?.averagePrice || oldPos.averagePrice || 0;
350
+ const pnl = oldPos.profitAndLoss || 0;
351
+ algoLogger.positionClosed(ui, symbol, side, size, exit, pnl);
352
+ algoLogger.info(ui, 'CLOSING ON FOLLOWER', symbol);
334
353
  // TODO: Close position on follower account
335
354
  }
336
355
  }
@@ -353,11 +372,13 @@ const launchCopyTrading = async (config) => {
353
372
  if (stats.pnl >= dailyTarget) {
354
373
  stopReason = 'target';
355
374
  running = false;
356
- ui.addLog('success', `TARGET REACHED! +$${stats.pnl.toFixed(2)}`);
375
+ algoLogger.targetHit(ui, lead.symbol.name, 0, stats.pnl);
376
+ algoLogger.info(ui, 'DAILY TARGET REACHED', `+$${stats.pnl.toFixed(2)} - Stopping algo`);
357
377
  } else if (stats.pnl <= -maxRisk) {
358
378
  stopReason = 'risk';
359
379
  running = false;
360
- ui.addLog('error', `MAX RISK HIT! -$${Math.abs(stats.pnl).toFixed(2)}`);
380
+ algoLogger.dailyLimitWarning(ui, stats.pnl, -maxRisk);
381
+ algoLogger.error(ui, 'MAX RISK HIT', `-$${Math.abs(stats.pnl).toFixed(2)} - Stopping algo`);
361
382
  }
362
383
  } catch (e) {
363
384
  // Silent fail - will retry
@@ -0,0 +1,245 @@
1
+ /**
2
+ * =============================================================================
3
+ * HQX Algo Trading System - Smart Algo Logger
4
+ * =============================================================================
5
+ * Provides rich, detailed logs for the algo UI
6
+ * Copied from HQX-TG algo-logger.ts - All 47 log types
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ /**
12
+ * Smart Algo Logger - All 47 log types from HQX-TG
13
+ */
14
+ const algoLogger = {
15
+ // === MARKET DATA LOGS ===
16
+ quote(ui, symbol, bid, ask, spread) {
17
+ ui.addLog('info', `QUOTE ${symbol} - bid: ${bid.toFixed(2)} | ask: ${ask.toFixed(2)} | spread: ${spread.toFixed(2)}`);
18
+ },
19
+
20
+ tape(ui, symbol, buyVol, sellVol, delta) {
21
+ const sign = delta > 0 ? '+' : '';
22
+ ui.addLog('info', `TAPE ${symbol} - buy: ${buyVol} | sell: ${sellVol} | delta: ${sign}${delta}`);
23
+ },
24
+
25
+ dom(ui, symbol, bidDepth, askDepth, imbalance) {
26
+ const direction = imbalance > 0 ? '▲' : imbalance < 0 ? '▼' : '=';
27
+ ui.addLog('info', `DOM ${symbol} - bidDepth: ${bidDepth} | askDepth: ${askDepth} | imbalance: ${direction}${Math.abs(imbalance).toFixed(1)}%`);
28
+ },
29
+
30
+ volumeSpike(ui, symbol, volume, avgVolume, ratio) {
31
+ ui.addLog('analysis', `VOLUME SPIKE ${symbol} - vol: ${volume} | avg: ${avgVolume.toFixed(0)} | ratio: ${ratio.toFixed(1)}x`);
32
+ },
33
+
34
+ // === ANALYSIS LOGS ===
35
+ orderFlow(ui, symbol, score, direction) {
36
+ const arrow = direction === 'LONG' ? '▲' : direction === 'SHORT' ? '▼' : '=';
37
+ ui.addLog('analysis', `ORDER FLOW ${symbol} - score: ${score.toFixed(0)} | direction: ${arrow} ${direction}`);
38
+ },
39
+
40
+ absorption(ui, symbol, level, side, strength) {
41
+ ui.addLog('analysis', `ABSORPTION ${side} - price: ${level.toFixed(2)} | strength: ${strength.toFixed(0)}%`);
42
+ },
43
+
44
+ sweep(ui, symbol, side, levels, volume) {
45
+ ui.addLog('analysis', `SWEEP ${side} - levels: ${levels} | volume: ${volume}`);
46
+ },
47
+
48
+ iceberg(ui, symbol, price, hiddenSize) {
49
+ ui.addLog('analysis', `ICEBERG DETECTED - price: ${price.toFixed(2)} | hidden: ${hiddenSize}`);
50
+ },
51
+
52
+ deltaDivergence(ui, symbol, priceDir, deltaDir) {
53
+ ui.addLog('analysis', `DELTA DIVERGENCE - price: ${priceDir} | delta: ${deltaDir}`);
54
+ },
55
+
56
+ vpoc(ui, symbol, poc, valueHigh, valueLow) {
57
+ ui.addLog('analysis', `VPOC ${symbol} - poc: ${poc.toFixed(2)} | VAH: ${valueHigh.toFixed(2)} | VAL: ${valueLow.toFixed(2)}`);
58
+ },
59
+
60
+ regime(ui, symbol, regime, confidence) {
61
+ ui.addLog('analysis', `REGIME ${symbol} - ${regime} | confidence: ${confidence.toFixed(0)}%`);
62
+ },
63
+
64
+ volatility(ui, symbol, atr, regime) {
65
+ ui.addLog('analysis', `VOLATILITY ${symbol} - ATR: ${atr.toFixed(2)} | regime: ${regime}`);
66
+ },
67
+
68
+ // === SIGNAL LOGS ===
69
+ signalGenerated(ui, symbol, direction, confidence, strategy) {
70
+ const arrow = direction === 'LONG' ? '▲' : '▼';
71
+ ui.addLog('signal', `SIGNAL ${arrow} ${direction} - ${symbol} | conf: ${confidence.toFixed(0)}% | strategy: ${strategy}`);
72
+ },
73
+
74
+ signalRejected(ui, symbol, reason) {
75
+ ui.addLog('warning', `SIGNAL REJECTED - ${symbol} | reason: ${reason}`);
76
+ },
77
+
78
+ signalExpired(ui, symbol) {
79
+ ui.addLog('info', `SIGNAL EXPIRED - ${symbol}`);
80
+ },
81
+
82
+ // === TRADE LOGS ===
83
+ orderSubmitted(ui, symbol, side, size, price) {
84
+ ui.addLog('trade', `ORDER SUBMITTED - ${side} ${size} ${symbol} @ ${price.toFixed(2)}`);
85
+ },
86
+
87
+ orderFilled(ui, symbol, side, size, price) {
88
+ ui.addLog('trade', `ORDER FILLED - ${side} ${size} ${symbol} @ ${price.toFixed(2)}`);
89
+ },
90
+
91
+ orderRejected(ui, symbol, reason) {
92
+ ui.addLog('error', `ORDER REJECTED - ${symbol} | ${reason}`);
93
+ },
94
+
95
+ orderCancelled(ui, symbol, orderId) {
96
+ ui.addLog('warning', `ORDER CANCELLED - ${symbol} | id: ${orderId}`);
97
+ },
98
+
99
+ positionOpened(ui, symbol, side, size, entry) {
100
+ const arrow = side === 'LONG' ? '▲' : '▼';
101
+ ui.addLog('trade', `POSITION OPENED ${arrow} - ${side} ${size} ${symbol} @ ${entry.toFixed(2)}`);
102
+ },
103
+
104
+ positionClosed(ui, symbol, side, size, exit, pnl) {
105
+ const pnlStr = pnl >= 0 ? `+$${pnl.toFixed(2)}` : `-$${Math.abs(pnl).toFixed(2)}`;
106
+ const type = pnl >= 0 ? 'trade' : 'warning';
107
+ ui.addLog(type, `POSITION CLOSED - ${side} ${size} ${symbol} @ ${exit.toFixed(2)} | PnL: ${pnlStr}`);
108
+ },
109
+
110
+ stopHit(ui, symbol, price, loss) {
111
+ ui.addLog('warning', `STOP LOSS HIT - ${symbol} @ ${price.toFixed(2)} | loss: -$${Math.abs(loss).toFixed(2)}`);
112
+ },
113
+
114
+ targetHit(ui, symbol, price, profit) {
115
+ ui.addLog('trade', `TARGET HIT - ${symbol} @ ${price.toFixed(2)} | profit: +$${profit.toFixed(2)}`);
116
+ },
117
+
118
+ trailingStopMoved(ui, symbol, oldStop, newStop) {
119
+ ui.addLog('info', `TRAILING STOP MOVED - ${symbol} | ${oldStop.toFixed(2)} → ${newStop.toFixed(2)}`);
120
+ },
121
+
122
+ fillSync(ui, side, size, price, orderId) {
123
+ ui.addLog('trade', `FILL (SYNC) - ${side} ${size} @ ${price.toFixed(2)} (order #${orderId})`);
124
+ },
125
+
126
+ entryConfirmed(ui, side, size, symbol, price) {
127
+ ui.addLog('info', `ENTRY CONFIRMED - ${side} ${size}x ${symbol} @ ${price.toFixed(2)}`);
128
+ },
129
+
130
+ stopsSet(ui, sl, tp) {
131
+ ui.addLog('info', `STOPS SET - SL: ${sl.toFixed(2)} | TP: ${tp.toFixed(2)}`);
132
+ },
133
+
134
+ // === RISK LOGS ===
135
+ riskCheck(ui, passed, reason) {
136
+ const type = passed ? 'info' : 'warning';
137
+ ui.addLog(type, `RISK CHECK ${passed ? 'PASSED' : 'BLOCKED'} - ${reason}`);
138
+ },
139
+
140
+ dailyLimitWarning(ui, current, limit) {
141
+ ui.addLog('warning', `DAILY LIMIT WARNING - PnL: $${current.toFixed(2)} / limit: $${limit.toFixed(2)}`);
142
+ },
143
+
144
+ maxDrawdownWarning(ui, current, max) {
145
+ ui.addLog('warning', `DRAWDOWN WARNING - current: ${current.toFixed(1)}% / max: ${max.toFixed(1)}%`);
146
+ },
147
+
148
+ positionSized(ui, contracts, kelly, riskAmount, riskPct) {
149
+ ui.addLog('info', `POSITION SIZE - ${contracts} contracts | kelly: ${kelly.toFixed(2)} | risk: $${riskAmount} (${riskPct}% of max)`);
150
+ },
151
+
152
+ bracketSet(ui, stopTicks, targetTicks, rr) {
153
+ ui.addLog('info', `BRACKET SET - SL: ${stopTicks}t | TP: ${targetTicks}t | R:R: ${rr.toFixed(1)}`);
154
+ },
155
+
156
+ // === STRATEGY LOGS ===
157
+ strategySelected(ui, strategy, session, regime) {
158
+ ui.addLog('info', `STRATEGY SELECTED - ${strategy} | session: ${session} | regime: ${regime}`);
159
+ },
160
+
161
+ strategySwitch(ui, from, to, reason) {
162
+ ui.addLog('info', `STRATEGY SWITCH - ${from} → ${to} | ${reason}`);
163
+ },
164
+
165
+ // === SESSION LOGS ===
166
+ sessionStart(ui, session) {
167
+ ui.addLog('info', `SESSION START - ${session}`);
168
+ },
169
+
170
+ sessionEnd(ui, session) {
171
+ ui.addLog('info', `SESSION END - ${session}`);
172
+ },
173
+
174
+ marketOpen(ui, session, etTime) {
175
+ ui.addLog('info', `MARKET OPEN - ${session} SESSION | ET: ${etTime} ET`);
176
+ },
177
+
178
+ marketClosed(ui) {
179
+ ui.addLog('warning', `MARKET CLOSED - Trading paused`);
180
+ },
181
+
182
+ // === SYSTEM LOGS ===
183
+ connectingToEngine(ui, accountId) {
184
+ ui.addLog('info', `CONNECTING TO ALGO ENGINE... - Account: ${accountId}`);
185
+ },
186
+
187
+ engineStarting(ui, platform, dailyTarget, dailyRisk) {
188
+ ui.addLog('info', `ALGO ENGINE STARTING... - Platform: ${platform} | Daily Target: $${dailyTarget} | Daily Risk: $${dailyRisk}`);
189
+ },
190
+
191
+ engineStarted(ui, platform, status) {
192
+ ui.addLog('info', `ALGO ENGINE STARTED - Status: ${status}`);
193
+ },
194
+
195
+ engineStopped(ui, reason) {
196
+ ui.addLog('info', `ENGINE STOPPED - ${reason || 'All positions flat'}`);
197
+ },
198
+
199
+ algoOperational(ui, platform) {
200
+ ui.addLog('info', `ALGO FULLY OPERATIONAL - Connected to ${platform} - Scanning for alpha...`);
201
+ },
202
+
203
+ dataConnected(ui, source) {
204
+ ui.addLog('info', `WEBSOCKET CONNECTED - Real-time updates enabled`);
205
+ },
206
+
207
+ dataDisconnected(ui, source, reason) {
208
+ ui.addLog('warning', `WEBSOCKET DISCONNECTED - Attempting to reconnect...`);
209
+ },
210
+
211
+ heartbeat(ui, tps, latency) {
212
+ ui.addLog('info', `HEARTBEAT - tps: ${tps} | latency: ${latency}ms`);
213
+ },
214
+
215
+ latencyReport(ui, latency) {
216
+ ui.addLog('analysis', `LATENCY - ${latency}ms order-to-fill`);
217
+ },
218
+
219
+ // === GENERIC ===
220
+ info(ui, message, details) {
221
+ ui.addLog('info', details ? `${message} - ${details}` : message);
222
+ },
223
+
224
+ warning(ui, message, details) {
225
+ ui.addLog('warning', details ? `${message} - ${details}` : message);
226
+ },
227
+
228
+ error(ui, message, details) {
229
+ ui.addLog('error', details ? `${message} - ${details}` : message);
230
+ },
231
+
232
+ signal(ui, message, details) {
233
+ ui.addLog('signal', details ? `${message} - ${details}` : message);
234
+ },
235
+
236
+ trade(ui, message, details) {
237
+ ui.addLog('trade', details ? `${message} - ${details}` : message);
238
+ },
239
+
240
+ analysis(ui, message, details) {
241
+ ui.addLog('analysis', details ? `${message} - ${details}` : message);
242
+ },
243
+ };
244
+
245
+ module.exports = { algoLogger };
@@ -14,6 +14,7 @@ const { checkMarketHours } = require('../../services/projectx/market');
14
14
  // Strategy & Market Data (obfuscated)
15
15
  const { M1 } = require('../../../dist/lib/m/s1');
16
16
  const { MarketDataFeed } = require('../../../dist/lib/data');
17
+ const { algoLogger } = require('./logger');
17
18
 
18
19
 
19
20
 
@@ -155,6 +156,11 @@ const configureAlgo = async (account, contract) => {
155
156
  const confirm = await prompts.confirmPrompt('Start algo trading?', true);
156
157
  if (!confirm) return null;
157
158
 
159
+ // Show spinner while initializing
160
+ const initSpinner = ora({ text: 'Initializing algo trading...', color: 'yellow' }).start();
161
+ await new Promise(r => setTimeout(r, 500));
162
+ initSpinner.succeed('Launching algo...');
163
+
158
164
  return { contracts, dailyTarget, maxRisk, showName };
159
165
  };
160
166
 
@@ -208,12 +214,14 @@ const launchAlgo = async (service, account, contract, config) => {
208
214
  // Initialize Market Data Feed
209
215
  const marketFeed = new MarketDataFeed({ propfirm: account.propfirm });
210
216
 
211
- // Log startup - UPPERCASE
212
- ui.addLog('info', `CONNECTION: ${connectionType.toUpperCase()}`);
213
- ui.addLog('info', `ACCOUNT: ${accountName.toUpperCase()}`);
214
- ui.addLog('info', `SYMBOL: ${symbolName.toUpperCase()} | QTY: ${contracts}`);
215
- ui.addLog('info', `TARGET: $${dailyTarget} | MAX RISK: $${maxRisk}`);
216
- ui.addLog('info', 'CONNECTING TO MARKET DATA...');
217
+ // Smart startup logs (same as HQX-TG)
218
+ const market = checkMarketHours();
219
+ const sessionName = market.session || 'AMERICAN';
220
+ const etTime = new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', timeZone: 'America/New_York' });
221
+
222
+ algoLogger.connectingToEngine(ui, account.accountId);
223
+ algoLogger.engineStarting(ui, connectionType, dailyTarget, maxRisk);
224
+ algoLogger.marketOpen(ui, sessionName.toUpperCase(), etTime);
217
225
 
218
226
  // Handle strategy signals
219
227
  strategy.on('signal', async (signal) => {
@@ -221,7 +229,12 @@ const launchAlgo = async (service, account, contract, config) => {
221
229
 
222
230
  const { side, direction, entry, stopLoss, takeProfit, confidence } = signal;
223
231
 
224
- ui.addLog('signal', `${direction.toUpperCase()} signal @ ${entry.toFixed(2)} (${(confidence * 100).toFixed(0)}%)`);
232
+ // Calculate position size with kelly
233
+ const kelly = Math.min(0.25, confidence);
234
+ const riskAmount = Math.round(maxRisk * kelly);
235
+ const riskPct = Math.round((riskAmount / maxRisk) * 100);
236
+
237
+ algoLogger.positionSized(ui, contracts, kelly, riskAmount, riskPct);
225
238
 
226
239
  // Place order via API
227
240
  pendingOrder = true;
@@ -238,7 +251,13 @@ const launchAlgo = async (service, account, contract, config) => {
238
251
  if (orderResult.success) {
239
252
  currentPosition = direction === 'long' ? contracts : -contracts;
240
253
  stats.trades++;
241
- ui.addLog('trade', `OPENED ${direction.toUpperCase()} ${contracts}x @ market`);
254
+ const sideStr = direction === 'long' ? 'BUY' : 'SELL';
255
+ const positionSide = direction === 'long' ? 'LONG' : 'SHORT';
256
+
257
+ algoLogger.orderSubmitted(ui, symbolName, sideStr, contracts, entry);
258
+ algoLogger.orderFilled(ui, symbolName, sideStr, contracts, entry);
259
+ algoLogger.positionOpened(ui, symbolName, positionSide, contracts, entry);
260
+ algoLogger.entryConfirmed(ui, sideStr, contracts, symbolName, entry);
242
261
 
243
262
  // Place bracket orders (SL/TP)
244
263
  if (stopLoss && takeProfit) {
@@ -262,20 +281,24 @@ const launchAlgo = async (service, account, contract, config) => {
262
281
  limitPrice: takeProfit
263
282
  });
264
283
 
265
- ui.addLog('info', `SL: ${stopLoss.toFixed(2)} | TP: ${takeProfit.toFixed(2)}`);
284
+ algoLogger.stopsSet(ui, stopLoss, takeProfit);
266
285
  }
267
286
  } else {
268
- ui.addLog('error', `Order failed: ${orderResult.error}`);
287
+ algoLogger.orderRejected(ui, symbolName, orderResult.error || 'Unknown error');
269
288
  }
270
289
  } catch (e) {
271
- ui.addLog('error', `Order error: ${e.message}`);
290
+ algoLogger.error(ui, 'ORDER ERROR', e.message);
272
291
  }
273
292
  pendingOrder = false;
274
293
  });
275
294
 
276
295
  // Handle market data ticks
296
+ let lastHeartbeat = Date.now();
297
+ let tps = 0;
298
+
277
299
  marketFeed.on('tick', (tick) => {
278
300
  tickCount++;
301
+ tps++;
279
302
  const latencyStart = Date.now();
280
303
 
281
304
  // Feed tick to strategy
@@ -291,25 +314,27 @@ const launchAlgo = async (service, account, contract, config) => {
291
314
 
292
315
  stats.latency = Date.now() - latencyStart;
293
316
 
294
- // Log every 100th tick to show activity
295
- if (tickCount % 100 === 0) {
296
- ui.addLog('info', `Tick #${tickCount} @ ${tick.price?.toFixed(2) || 'N/A'}`);
317
+ // Heartbeat every 30 seconds (smart log instead of tick count)
318
+ if (Date.now() - lastHeartbeat > 30000) {
319
+ algoLogger.heartbeat(ui, tps, stats.latency);
320
+ lastHeartbeat = Date.now();
321
+ tps = 0;
297
322
  }
298
323
  });
299
324
 
300
325
  marketFeed.on('connected', () => {
301
326
  stats.connected = true;
302
- ui.addLog('success', 'MARKET DATA CONNECTED');
327
+ algoLogger.dataConnected(ui, 'RTC');
328
+ algoLogger.algoOperational(ui, connectionType);
303
329
  });
304
330
 
305
331
  marketFeed.on('error', (err) => {
306
- ui.addLog('error', `MARKET: ${err.message.toUpperCase()}`);
332
+ algoLogger.error(ui, 'MARKET ERROR', err.message);
307
333
  });
308
334
 
309
335
  marketFeed.on('disconnected', (err) => {
310
336
  stats.connected = false;
311
- const reason = err?.message || 'UNKNOWN';
312
- ui.addLog('error', `DISC: ${reason.substring(0, 70).toUpperCase()}`);
337
+ algoLogger.dataDisconnected(ui, 'WEBSOCKET', err?.message);
313
338
  });
314
339
 
315
340
  // Connect to market data
@@ -318,14 +343,14 @@ const launchAlgo = async (service, account, contract, config) => {
318
343
 
319
344
  // CRITICAL: Get a fresh token for WebSocket connection
320
345
  // TopStep invalidates WebSocket sessions for old tokens
321
- ui.addLog('info', 'REFRESHING AUTH TOKEN...');
346
+ algoLogger.info(ui, 'REFRESHING AUTH TOKEN...');
322
347
  const token = await service.getFreshToken?.() || service.token || service.getToken?.();
323
348
 
324
349
  if (!token) {
325
- ui.addLog('error', 'NO AUTH TOKEN - PLEASE RECONNECT');
350
+ algoLogger.error(ui, 'NO AUTH TOKEN', 'Please reconnect');
326
351
  } else {
327
- ui.addLog('info', `TOKEN OK (${token.length} CHARS)`);
328
- ui.addLog('info', `RTC: ${propfirmKey.toUpperCase()} | ${contractId}`);
352
+ algoLogger.info(ui, 'TOKEN OK', `${token.length} chars`);
353
+ algoLogger.info(ui, 'CONNECTING', `${propfirmKey.toUpperCase()} | ${contractId}`);
329
354
 
330
355
  await marketFeed.connect(token, propfirmKey);
331
356
 
@@ -334,13 +359,13 @@ const launchAlgo = async (service, account, contract, config) => {
334
359
 
335
360
  if (marketFeed.isConnected()) {
336
361
  await marketFeed.subscribe(symbolName, contractId);
337
- ui.addLog('success', 'SUBSCRIBED TO MARKET DATA');
362
+ algoLogger.info(ui, 'SUBSCRIBED', `${symbolName} real-time feed active`);
338
363
  } else {
339
- ui.addLog('error', 'CONNECTION LOST BEFORE SUBSCRIBE');
364
+ algoLogger.error(ui, 'CONNECTION LOST', 'Before subscribe');
340
365
  }
341
366
  }
342
367
  } catch (e) {
343
- ui.addLog('error', `ERR: ${e.message.substring(0, 60).toUpperCase()}`);
368
+ algoLogger.error(ui, 'CONNECTION ERROR', e.message.substring(0, 50));
344
369
  }
345
370
 
346
371
  // Poll account P&L from API
@@ -385,11 +410,11 @@ const launchAlgo = async (service, account, contract, config) => {
385
410
  if (stats.pnl >= dailyTarget) {
386
411
  stopReason = 'target';
387
412
  running = false;
388
- ui.addLog('success', `TARGET REACHED +$${stats.pnl.toFixed(2)}`);
413
+ algoLogger.targetHit(ui, symbolName, 0, stats.pnl);
389
414
  } else if (stats.pnl <= -maxRisk) {
390
415
  stopReason = 'risk';
391
416
  running = false;
392
- ui.addLog('error', `MAX RISK -$${Math.abs(stats.pnl).toFixed(2)}`);
417
+ algoLogger.dailyLimitWarning(ui, stats.pnl, -maxRisk);
393
418
  }
394
419
  } catch (e) {
395
420
  // Silently handle polling errors