hedgequantx 2.9.19 → 2.9.20

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.
Files changed (39) hide show
  1. package/package.json +1 -1
  2. package/src/app.js +42 -64
  3. package/src/lib/m/hqx-2b.js +7 -0
  4. package/src/lib/m/index.js +138 -0
  5. package/src/lib/m/ultra-scalping.js +7 -0
  6. package/src/menus/connect.js +14 -17
  7. package/src/menus/dashboard.js +58 -76
  8. package/src/pages/accounts.js +38 -49
  9. package/src/pages/algo/copy-trading.js +546 -178
  10. package/src/pages/algo/index.js +18 -75
  11. package/src/pages/algo/one-account.js +322 -57
  12. package/src/pages/algo/ui.js +15 -15
  13. package/src/pages/orders.js +19 -22
  14. package/src/pages/positions.js +19 -22
  15. package/src/pages/stats/index.js +15 -16
  16. package/src/pages/user.js +7 -11
  17. package/src/services/ai-supervision/health.js +35 -47
  18. package/src/services/index.js +1 -9
  19. package/src/services/rithmic/accounts.js +8 -6
  20. package/src/ui/box.js +9 -5
  21. package/src/ui/index.js +5 -18
  22. package/src/ui/menu.js +4 -4
  23. package/src/pages/ai-agents-ui.js +0 -388
  24. package/src/pages/ai-agents.js +0 -494
  25. package/src/pages/ai-models.js +0 -389
  26. package/src/pages/algo/algo-executor.js +0 -307
  27. package/src/pages/algo/copy-executor.js +0 -331
  28. package/src/pages/algo/custom-strategy.js +0 -313
  29. package/src/services/ai-supervision/consensus.js +0 -284
  30. package/src/services/ai-supervision/context.js +0 -275
  31. package/src/services/ai-supervision/directive.js +0 -167
  32. package/src/services/ai-supervision/index.js +0 -359
  33. package/src/services/ai-supervision/parser.js +0 -278
  34. package/src/services/ai-supervision/symbols.js +0 -259
  35. package/src/services/cliproxy/index.js +0 -256
  36. package/src/services/cliproxy/installer.js +0 -111
  37. package/src/services/cliproxy/manager.js +0 -387
  38. package/src/services/llmproxy/index.js +0 -166
  39. package/src/services/llmproxy/manager.js +0 -411
@@ -3,15 +3,13 @@
3
3
  */
4
4
 
5
5
  const chalk = require('chalk');
6
- const { getLogoWidth, centerText, displayBanner , clearScreen } = require('../../ui');
6
+ const { getSeparator } = require('../../ui');
7
7
  const { logger, prompts } = require('../../utils');
8
- const { getActiveProvider } = require('../ai-agents');
9
8
 
10
9
  const log = logger.scope('AlgoMenu');
11
10
 
12
11
  const { oneAccountMenu } = require('./one-account');
13
12
  const { copyTradingMenu } = require('./copy-trading');
14
- const { customStrategyMenu } = require('./custom-strategy');
15
13
 
16
14
  /**
17
15
  * Algo Trading Menu
@@ -20,94 +18,39 @@ const algoTradingMenu = async (service) => {
20
18
  log.info('Algo Trading menu opened');
21
19
 
22
20
  try {
23
- // Clear screen and show banner
24
- clearScreen();
25
- displayBanner();
26
-
27
- const boxWidth = getLogoWidth();
28
- const W = boxWidth - 2;
29
-
30
- // Check if AI agent is connected
31
- const aiProvider = getActiveProvider();
32
- const hasAI = !!aiProvider;
33
-
34
- // Draw menu rectangle
35
- console.log(chalk.cyan('╔' + '═'.repeat(W) + '╗'));
36
- console.log(chalk.cyan('║') + chalk.magenta.bold(centerText('ALGO-TRADING', W)) + chalk.cyan('║'));
37
- console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
38
-
39
- // 2 or 3 columns layout based on AI availability
40
- const col1 = '[1] ONE ACCOUNT';
41
- const col2 = '[2] COPY TRADING';
42
- const col3 = hasAI ? '[3] CUSTOM STRATEGY' : '';
43
-
44
- if (hasAI) {
45
- // 3 columns
46
- const colWidth = Math.floor(W / 3);
47
- const lastColWidth = W - 2 * colWidth;
48
-
49
- const pad1 = Math.floor((colWidth - col1.length) / 2);
50
- const pad2 = Math.floor((colWidth - col2.length) / 2);
51
- const pad3 = Math.floor((lastColWidth - col3.length) / 2);
52
-
53
- const col1Str = ' '.repeat(pad1) + chalk.cyan(col1) + ' '.repeat(colWidth - col1.length - pad1);
54
- const col2Str = ' '.repeat(pad2) + chalk.yellow(col2) + ' '.repeat(colWidth - col2.length - pad2);
55
- const col3Str = ' '.repeat(pad3) + chalk.green(col3) + ' '.repeat(lastColWidth - col3.length - pad3);
56
-
57
- console.log(chalk.cyan('║') + col1Str + col2Str + col3Str + chalk.cyan('║'));
58
- } else {
59
- // 2 columns only (no AI connected)
60
- const colWidth = Math.floor(W / 2);
61
- const pad1 = Math.floor((colWidth - col1.length) / 2);
62
- const pad2 = Math.floor((W - colWidth - col2.length) / 2);
63
-
64
- const col1Str = ' '.repeat(pad1) + chalk.cyan(col1) + ' '.repeat(colWidth - col1.length - pad1);
65
- const col2Str = ' '.repeat(pad2) + chalk.yellow(col2) + ' '.repeat(W - colWidth - col2.length - pad2);
66
-
67
- console.log(chalk.cyan('║') + col1Str + col2Str + chalk.cyan('║'));
68
- }
69
-
70
- console.log(chalk.cyan('╠' + '─'.repeat(W) + '╣'));
71
- console.log(chalk.cyan('║') + chalk.red(centerText('[B] BACK', W)) + chalk.cyan('║'));
72
- console.log(chalk.cyan('╚' + '═'.repeat(W) + '╝'));
21
+ console.log();
22
+ console.log(chalk.gray(getSeparator()));
23
+ console.log(chalk.yellow.bold(' Algo-Trading'));
24
+ console.log(chalk.gray(getSeparator()));
25
+ console.log();
73
26
 
74
- const promptText = hasAI ? 'SELECT (1/2/3/B): ' : 'SELECT (1/2/B): ';
75
- const input = await prompts.textInput(chalk.cyan(promptText));
76
- const choice = (input || '').toLowerCase().trim();
27
+ const action = await prompts.selectOption(chalk.yellow('Select Mode:'), [
28
+ { value: 'one_account', label: 'One Account' },
29
+ { value: 'copy_trading', label: 'Copy Trading' },
30
+ { value: 'back', label: '< Back' }
31
+ ]);
77
32
 
78
- log.debug('Algo mode selected', { choice });
33
+ log.debug('Algo mode selected', { action });
79
34
 
80
- if (choice === 'b' || choice === '') {
35
+ if (!action || action === 'back') {
81
36
  return 'back';
82
37
  }
83
38
 
84
- switch (choice) {
85
- case '1':
39
+ switch (action) {
40
+ case 'one_account':
86
41
  log.info('Starting One Account mode');
87
42
  await oneAccountMenu(service);
88
43
  break;
89
- case '2':
44
+ case 'copy_trading':
90
45
  log.info('Starting Copy Trading mode');
91
46
  await copyTradingMenu();
92
47
  break;
93
- case '3':
94
- if (hasAI) {
95
- log.info('Starting Custom Strategy mode');
96
- await customStrategyMenu(service);
97
- } else {
98
- console.log(chalk.red(' INVALID OPTION'));
99
- await new Promise(r => setTimeout(r, 1000));
100
- }
101
- break;
102
- default:
103
- console.log(chalk.red(' INVALID OPTION'));
104
- await new Promise(r => setTimeout(r, 1000));
105
48
  }
106
49
 
107
- return choice;
50
+ return action;
108
51
  } catch (err) {
109
52
  log.error('Algo menu error:', err.message);
110
- console.log(chalk.red(` ERROR: ${err.message.toUpperCase()}`));
53
+ console.log(chalk.red(` Error: ${err.message}`));
111
54
  await prompts.waitForEnter();
112
55
  return 'back';
113
56
  }
@@ -1,18 +1,37 @@
1
1
  /**
2
- * One Account Mode - HQX Ultra Scalping
3
- * Supports multi-agent AI supervision
2
+ * One Account Mode - HQX Algo Trading with Strategy Selection
4
3
  */
5
4
 
6
5
  const chalk = require('chalk');
7
6
  const ora = require('ora');
7
+ const readline = require('readline');
8
8
 
9
9
  const { connections } = require('../../services');
10
+ const { AlgoUI, renderSessionSummary } = require('./ui');
10
11
  const { prompts } = require('../../utils');
11
12
  const { checkMarketHours } = require('../../services/rithmic/market');
12
- const { executeAlgo } = require('./algo-executor');
13
- const { getActiveAgentCount, getSupervisionConfig, getActiveAgents } = require('../ai-agents');
14
- const { runPreflightCheck, formatPreflightResults, getPreflightSummary } = require('../../services/ai-supervision');
15
13
 
14
+ // Strategy Registry & Market Data
15
+ const { getAvailableStrategies, loadStrategy, getStrategy } = require('../../lib/m');
16
+ const { MarketDataFeed } = require('../../lib/data');
17
+
18
+
19
+ /**
20
+ * Strategy Selection
21
+ * @returns {Promise<string|null>} Selected strategy ID or null
22
+ */
23
+ const selectStrategy = async () => {
24
+ const strategies = getAvailableStrategies();
25
+
26
+ const options = strategies.map(s => ({
27
+ label: s.id === 'ultra-scalping' ? 'HQX Scalping' : 'HQX Sweep',
28
+ value: s.id
29
+ }));
30
+ options.push({ label: chalk.gray('< Back'), value: 'back' });
31
+
32
+ const selected = await prompts.selectOption('Select Strategy:', options);
33
+ return selected === 'back' ? null : selected;
34
+ };
16
35
 
17
36
 
18
37
  /**
@@ -74,60 +93,15 @@ const oneAccountMenu = async (service) => {
74
93
  const contract = await selectSymbol(accountService, selectedAccount);
75
94
  if (!contract) return;
76
95
 
96
+ // Select strategy
97
+ const strategyId = await selectStrategy();
98
+ if (!strategyId) return;
99
+
77
100
  // Configure algo
78
- const config = await configureAlgo(selectedAccount, contract);
101
+ const config = await configureAlgo(selectedAccount, contract, strategyId);
79
102
  if (!config) return;
80
103
 
81
- // Check for AI Supervision
82
- const agentCount = getActiveAgentCount();
83
- let supervisionConfig = null;
84
-
85
- if (agentCount > 0) {
86
- console.log();
87
- console.log(chalk.cyan(` ${agentCount} AI Agent(s) available for supervision`));
88
- const enableAI = await prompts.confirmPrompt('Enable AI Supervision?', true);
89
-
90
- if (enableAI) {
91
- // Run pre-flight check - ALL agents must pass
92
- console.log();
93
- console.log(chalk.yellow(' Running AI pre-flight check...'));
94
- console.log();
95
-
96
- const agents = getActiveAgents();
97
- const preflightResults = await runPreflightCheck(agents);
98
-
99
- // Display results
100
- const lines = formatPreflightResults(preflightResults, 60);
101
- for (const line of lines) {
102
- console.log(line);
103
- }
104
-
105
- const summary = getPreflightSummary(preflightResults);
106
- console.log();
107
- console.log(` ${summary.text}`);
108
- console.log();
109
-
110
- if (!preflightResults.success) {
111
- console.log(chalk.red(' Cannot start algo - fix agent connections first.'));
112
- await prompts.waitForEnter();
113
- return;
114
- }
115
-
116
- supervisionConfig = getSupervisionConfig();
117
- console.log(chalk.green(` ✓ AI Supervision ready with ${agentCount} agent(s)`));
118
-
119
- const proceedWithAI = await prompts.confirmPrompt('Start algo with AI supervision?', true);
120
- if (!proceedWithAI) return;
121
- }
122
- }
123
-
124
- await executeAlgo({
125
- service: accountService,
126
- account: selectedAccount,
127
- contract,
128
- config,
129
- options: { supervisionConfig }
130
- });
104
+ await launchAlgo(accountService, selectedAccount, contract, config, strategyId);
131
105
  };
132
106
 
133
107
  /**
@@ -182,9 +156,12 @@ const selectSymbol = async (service, account) => {
182
156
  /**
183
157
  * Configure algo
184
158
  */
185
- const configureAlgo = async (account, contract) => {
159
+ const configureAlgo = async (account, contract, strategyId) => {
160
+ const strategyInfo = getStrategy(strategyId);
161
+
186
162
  console.log();
187
163
  console.log(chalk.cyan(' Configure Algo Parameters'));
164
+ console.log(chalk.gray(` Strategy: ${strategyInfo.name}`));
188
165
  console.log();
189
166
 
190
167
  const contracts = await prompts.numberInput('Number of contracts:', 1, 1, 10);
@@ -205,4 +182,292 @@ const configureAlgo = async (account, contract) => {
205
182
  return { contracts, dailyTarget, maxRisk, showName };
206
183
  };
207
184
 
185
+ /**
186
+ * Launch algo trading - Dynamic Strategy Loading
187
+ * Real-time market data + Strategy signals + Auto order execution
188
+ */
189
+ const launchAlgo = async (service, account, contract, config, strategyId) => {
190
+ const { contracts, dailyTarget, maxRisk, showName } = config;
191
+
192
+ // Load strategy dynamically
193
+ const strategyInfo = getStrategy(strategyId);
194
+ const strategyModule = loadStrategy(strategyId);
195
+
196
+ // Use RAW API fields
197
+ const accountName = showName
198
+ ? (account.accountName || account.rithmicAccountId || account.accountId)
199
+ : 'HQX *****';
200
+ const symbolName = contract.name;
201
+ const contractId = contract.id;
202
+ const connectionType = account.platform || 'Rithmic';
203
+ const tickSize = contract.tickSize || 0.25;
204
+
205
+ const ui = new AlgoUI({ subtitle: strategyInfo.name, mode: 'one-account' });
206
+
207
+ const stats = {
208
+ accountName,
209
+ symbol: symbolName,
210
+ qty: contracts,
211
+ target: dailyTarget,
212
+ risk: maxRisk,
213
+ propfirm: account.propfirm || 'Unknown',
214
+ platform: connectionType,
215
+ pnl: 0,
216
+ trades: 0,
217
+ wins: 0,
218
+ losses: 0,
219
+ latency: 0,
220
+ connected: false,
221
+ startTime: Date.now()
222
+ };
223
+
224
+ let running = true;
225
+ let stopReason = null;
226
+ let startingPnL = null;
227
+ let currentPosition = 0; // Current position qty (+ long, - short)
228
+ let pendingOrder = false; // Prevent duplicate orders
229
+ let tickCount = 0;
230
+
231
+ // Initialize Strategy dynamically
232
+ const strategy = new strategyModule.M1({ tickSize });
233
+ strategy.initialize(contractId, tickSize);
234
+
235
+ // Initialize Market Data Feed
236
+ const marketFeed = new MarketDataFeed({ propfirm: account.propfirm });
237
+
238
+ // Log startup
239
+ ui.addLog('info', `Strategy: ${strategyInfo.name}`);
240
+ ui.addLog('info', `Connection: ${connectionType}`);
241
+ ui.addLog('info', `Account: ${accountName}`);
242
+ ui.addLog('info', `Symbol: ${symbolName} | Qty: ${contracts}`);
243
+ ui.addLog('info', `Target: $${dailyTarget} | Max Risk: $${maxRisk}`);
244
+ ui.addLog('info', `Params: ${strategyInfo.params.stopTicks}t stop, ${strategyInfo.params.targetTicks}t target (${strategyInfo.params.riskReward})`);
245
+ ui.addLog('info', 'Connecting to market data...');
246
+
247
+ // Handle strategy signals
248
+ strategy.on('signal', async (signal) => {
249
+ if (!running || pendingOrder || currentPosition !== 0) return;
250
+
251
+ const { side, direction, entry, stopLoss, takeProfit, confidence } = signal;
252
+
253
+ ui.addLog('signal', `${direction.toUpperCase()} signal @ ${entry.toFixed(2)} (${(confidence * 100).toFixed(0)}%)`);
254
+
255
+ // Place order via API
256
+ pendingOrder = true;
257
+ try {
258
+ const orderSide = direction === 'long' ? 0 : 1; // 0=Buy, 1=Sell
259
+ const orderResult = await service.placeOrder({
260
+ accountId: account.accountId,
261
+ contractId: contractId,
262
+ type: 2, // Market order
263
+ side: orderSide,
264
+ size: contracts
265
+ });
266
+
267
+ if (orderResult.success) {
268
+ currentPosition = direction === 'long' ? contracts : -contracts;
269
+ stats.trades++;
270
+ ui.addLog('trade', `OPENED ${direction.toUpperCase()} ${contracts}x @ market`);
271
+
272
+ // Place bracket orders (SL/TP)
273
+ if (stopLoss && takeProfit) {
274
+ // Stop Loss
275
+ await service.placeOrder({
276
+ accountId: account.accountId,
277
+ contractId: contractId,
278
+ type: 4, // Stop order
279
+ side: direction === 'long' ? 1 : 0, // Opposite side
280
+ size: contracts,
281
+ stopPrice: stopLoss
282
+ });
283
+
284
+ // Take Profit
285
+ await service.placeOrder({
286
+ accountId: account.accountId,
287
+ contractId: contractId,
288
+ type: 1, // Limit order
289
+ side: direction === 'long' ? 1 : 0,
290
+ size: contracts,
291
+ limitPrice: takeProfit
292
+ });
293
+
294
+ ui.addLog('info', `SL: ${stopLoss.toFixed(2)} | TP: ${takeProfit.toFixed(2)}`);
295
+ }
296
+ } else {
297
+ ui.addLog('error', `Order failed: ${orderResult.error}`);
298
+ }
299
+ } catch (e) {
300
+ ui.addLog('error', `Order error: ${e.message}`);
301
+ }
302
+ pendingOrder = false;
303
+ });
304
+
305
+ // Handle market data ticks
306
+ marketFeed.on('tick', (tick) => {
307
+ tickCount++;
308
+ const latencyStart = Date.now();
309
+
310
+ // Feed tick to strategy
311
+ strategy.processTick({
312
+ contractId: tick.contractId || contractId,
313
+ price: tick.price,
314
+ bid: tick.bid,
315
+ ask: tick.ask,
316
+ volume: tick.volume || 1,
317
+ side: tick.lastTradeSide || 'unknown',
318
+ timestamp: tick.timestamp || Date.now()
319
+ });
320
+
321
+ stats.latency = Date.now() - latencyStart;
322
+
323
+ // Log every 100th tick to show activity
324
+ if (tickCount % 100 === 0) {
325
+ ui.addLog('info', `Tick #${tickCount} @ ${tick.price?.toFixed(2) || 'N/A'}`);
326
+ }
327
+ });
328
+
329
+ marketFeed.on('connected', () => {
330
+ stats.connected = true;
331
+ ui.addLog('success', 'Market data connected!');
332
+ });
333
+
334
+ marketFeed.on('error', (err) => {
335
+ ui.addLog('error', `Market: ${err.message}`);
336
+ });
337
+
338
+ marketFeed.on('disconnected', () => {
339
+ stats.connected = false;
340
+ ui.addLog('error', 'Market data disconnected');
341
+ });
342
+
343
+ // Connect to market data
344
+ try {
345
+ const token = service.token || service.getToken?.();
346
+ const propfirmKey = (account.propfirm || 'topstep').toLowerCase().replace(/\s+/g, '_');
347
+ await marketFeed.connect(token, propfirmKey, contractId);
348
+ await marketFeed.subscribe(symbolName, contractId);
349
+ } catch (e) {
350
+ ui.addLog('error', `Failed to connect: ${e.message}`);
351
+ }
352
+
353
+ // Poll account P&L from API
354
+ const pollPnL = async () => {
355
+ try {
356
+ const accountResult = await service.getTradingAccounts();
357
+ if (accountResult.success && accountResult.accounts) {
358
+ const acc = accountResult.accounts.find(a => a.accountId === account.accountId);
359
+ if (acc && acc.profitAndLoss !== undefined) {
360
+ if (startingPnL === null) startingPnL = acc.profitAndLoss;
361
+ stats.pnl = acc.profitAndLoss - startingPnL;
362
+
363
+ // Record trade result in strategy
364
+ if (stats.pnl !== 0) {
365
+ strategy.recordTradeResult(stats.pnl);
366
+ }
367
+ }
368
+ }
369
+
370
+ // Check positions
371
+ const posResult = await service.getPositions(account.accountId);
372
+ if (posResult.success && posResult.positions) {
373
+ const pos = posResult.positions.find(p => {
374
+ const sym = p.contractId || p.symbol || '';
375
+ return sym.includes(contract.name) || sym.includes(contractId);
376
+ });
377
+
378
+ if (pos && pos.quantity !== 0) {
379
+ currentPosition = pos.quantity;
380
+ const side = pos.quantity > 0 ? 'LONG' : 'SHORT';
381
+ const pnl = pos.profitAndLoss || 0;
382
+
383
+ // Check if position closed (win/loss)
384
+ if (pnl > 0) stats.wins = Math.max(stats.wins, 1);
385
+ else if (pnl < 0) stats.losses = Math.max(stats.losses, 1);
386
+ } else {
387
+ currentPosition = 0;
388
+ }
389
+ }
390
+
391
+ // Check target/risk
392
+ if (stats.pnl >= dailyTarget) {
393
+ stopReason = 'target';
394
+ running = false;
395
+ ui.addLog('success', `TARGET REACHED! +$${stats.pnl.toFixed(2)}`);
396
+ } else if (stats.pnl <= -maxRisk) {
397
+ stopReason = 'risk';
398
+ running = false;
399
+ ui.addLog('error', `MAX RISK! -$${Math.abs(stats.pnl).toFixed(2)}`);
400
+ }
401
+ } catch (e) {
402
+ // Silently handle polling errors
403
+ }
404
+ };
405
+
406
+ // Start polling and UI refresh
407
+ const refreshInterval = setInterval(() => { if (running) ui.render(stats); }, 250);
408
+ const pnlInterval = setInterval(() => { if (running) pollPnL(); }, 2000);
409
+ pollPnL(); // Initial poll
410
+
411
+ // Keyboard handler
412
+ const setupKeyHandler = () => {
413
+ if (!process.stdin.isTTY) return;
414
+ readline.emitKeypressEvents(process.stdin);
415
+ process.stdin.setRawMode(true);
416
+ process.stdin.resume();
417
+
418
+ const onKey = (str, key) => {
419
+ if (key && (key.name === 'x' || key.name === 'X' || (key.ctrl && key.name === 'c'))) {
420
+ running = false;
421
+ stopReason = 'manual';
422
+ }
423
+ };
424
+ process.stdin.on('keypress', onKey);
425
+ return () => {
426
+ process.stdin.removeListener('keypress', onKey);
427
+ if (process.stdin.isTTY) process.stdin.setRawMode(false);
428
+ };
429
+ };
430
+
431
+ const cleanupKeys = setupKeyHandler();
432
+
433
+ // Wait for stop
434
+ await new Promise(resolve => {
435
+ const check = setInterval(() => {
436
+ if (!running) {
437
+ clearInterval(check);
438
+ resolve();
439
+ }
440
+ }, 100);
441
+ });
442
+
443
+ // Cleanup
444
+ clearInterval(refreshInterval);
445
+ clearInterval(pnlInterval);
446
+ await marketFeed.disconnect();
447
+ if (cleanupKeys) cleanupKeys();
448
+ ui.cleanup();
449
+
450
+ if (process.stdin.isTTY) {
451
+ process.stdin.setRawMode(false);
452
+ }
453
+ process.stdin.resume();
454
+
455
+ // Duration
456
+ const durationMs = Date.now() - stats.startTime;
457
+ const hours = Math.floor(durationMs / 3600000);
458
+ const minutes = Math.floor((durationMs % 3600000) / 60000);
459
+ const seconds = Math.floor((durationMs % 60000) / 1000);
460
+ stats.duration = hours > 0
461
+ ? `${hours}h ${minutes}m ${seconds}s`
462
+ : minutes > 0
463
+ ? `${minutes}m ${seconds}s`
464
+ : `${seconds}s`;
465
+
466
+ // Summary
467
+ renderSessionSummary(stats, stopReason);
468
+
469
+ console.log('\n Returning to menu in 3 seconds...');
470
+ await new Promise(resolve => setTimeout(resolve, 3000));
471
+ };
472
+
208
473
  module.exports = { oneAccountMenu };
@@ -127,9 +127,9 @@ class AlgoUI {
127
127
 
128
128
  // Separator + title
129
129
  this._line(chalk.cyan(BOX.ML + BOX.H.repeat(W) + BOX.MR));
130
- this._line(chalk.cyan(BOX.V) + chalk.yellow(center(`PROP FUTURES ALGO TRADING V${version}`, W)) + chalk.cyan(BOX.V));
130
+ this._line(chalk.cyan(BOX.V) + chalk.white(center(`Prop Futures Algo Trading v${version}`, W)) + chalk.cyan(BOX.V));
131
131
  this._line(chalk.cyan(BOX.ML + BOX.H.repeat(W) + BOX.MR));
132
- this._line(chalk.cyan(BOX.V) + chalk.yellow(center((this.config.subtitle || 'HQX ALGO TRADING').toUpperCase(), W)) + chalk.cyan(BOX.V));
132
+ this._line(chalk.cyan(BOX.V) + chalk.yellow(center(this.config.subtitle || 'HQX Algo Trading', W)) + chalk.cyan(BOX.V));
133
133
  }
134
134
 
135
135
  _drawStats(stats) {
@@ -293,7 +293,7 @@ class AlgoUI {
293
293
  const visible = logs.slice(-maxLogs).reverse();
294
294
 
295
295
  if (visible.length === 0) {
296
- this._line(chalk.cyan(BOX.V) + chalk.gray(fitToWidth(' AWAITING MARKET SIGNALS...', W)) + chalk.cyan(BOX.V));
296
+ this._line(chalk.cyan(BOX.V) + chalk.gray(fitToWidth(' Awaiting market signals...', W)) + chalk.cyan(BOX.V));
297
297
  for (let i = 0; i < maxLogs - 1; i++) {
298
298
  this._line(chalk.cyan(BOX.V) + ' '.repeat(W) + chalk.cyan(BOX.V));
299
299
  }
@@ -355,11 +355,11 @@ const checkMarketStatus = () => {
355
355
  const ctHour = (utcHour - ctOffset + 24) % 24;
356
356
  const ctDay = utcHour < ctOffset ? (utcDay + 6) % 7 : utcDay;
357
357
 
358
- if (ctDay === 6) return { isOpen: false, message: 'MARKET CLOSED (SATURDAY)' };
359
- if (ctDay === 0 && ctHour < 17) return { isOpen: false, message: 'MARKET OPENS SUNDAY 5:00 PM CT' };
360
- if (ctDay === 5 && ctHour >= 16) return { isOpen: false, message: 'MARKET CLOSED (FRIDAY AFTER 4PM CT)' };
361
- if (ctHour === 16 && ctDay >= 1 && ctDay <= 4) return { isOpen: false, message: 'DAILY MAINTENANCE' };
362
- return { isOpen: true, message: 'MARKET OPEN' };
358
+ if (ctDay === 6) return { isOpen: false, message: 'Market closed (Saturday)' };
359
+ if (ctDay === 0 && ctHour < 17) return { isOpen: false, message: 'Market opens Sunday 5:00 PM CT' };
360
+ if (ctDay === 5 && ctHour >= 16) return { isOpen: false, message: 'Market closed (Friday after 4PM CT)' };
361
+ if (ctHour === 16 && ctDay >= 1 && ctDay <= 4) return { isOpen: false, message: 'Daily maintenance' };
362
+ return { isOpen: true, message: 'Market OPEN' };
363
363
  };
364
364
 
365
365
  /**
@@ -371,7 +371,7 @@ const renderSessionSummary = (stats, stopReason) => {
371
371
  const colR = W - colL - 1;
372
372
  const version = require('../../../package.json').version;
373
373
 
374
- process.stdout.write('\x1B[2J\x1B[H');
374
+ console.clear();
375
375
  console.log();
376
376
 
377
377
  // Top border
@@ -387,9 +387,9 @@ const renderSessionSummary = (stats, stopReason) => {
387
387
 
388
388
  // Separator + title
389
389
  console.log(chalk.cyan(BOX.ML + BOX.H.repeat(W) + BOX.MR));
390
- console.log(chalk.cyan(BOX.V) + chalk.yellow(center(`PROP FUTURES ALGO TRADING V${version}`, W)) + chalk.cyan(BOX.V));
390
+ console.log(chalk.cyan(BOX.V) + chalk.white(center(`Prop Futures Algo Trading v${version}`, W)) + chalk.cyan(BOX.V));
391
391
  console.log(chalk.cyan(BOX.ML + BOX.H.repeat(W) + BOX.MR));
392
- console.log(chalk.cyan(BOX.V) + chalk.yellow(center('SESSION SUMMARY', W)) + chalk.cyan(BOX.V));
392
+ console.log(chalk.cyan(BOX.V) + chalk.yellow(center('Session Summary', W)) + chalk.cyan(BOX.V));
393
393
 
394
394
  // Grid separators
395
395
  const GT = BOX.ML + BOX.H.repeat(colL) + BOX.TM + BOX.H.repeat(colR) + BOX.MR;
@@ -410,18 +410,18 @@ const renderSessionSummary = (stats, stopReason) => {
410
410
  // Row 1: Stop Reason | Duration
411
411
  const duration = stats.duration || '--';
412
412
  const reasonColor = stopReason === 'target' ? chalk.green : stopReason === 'risk' ? chalk.red : chalk.yellow;
413
- row('STOP REASON', (stopReason || 'MANUAL').toUpperCase(), reasonColor, 'DURATION', duration, chalk.white);
413
+ row('Stop Reason', (stopReason || 'manual').toUpperCase(), reasonColor, 'Duration', duration, chalk.white);
414
414
 
415
415
  console.log(chalk.cyan(GM));
416
416
 
417
417
  // Row 2: Trades | Win Rate
418
418
  const winRate = stats.trades > 0 ? ((stats.wins / stats.trades) * 100).toFixed(1) + '%' : '0%';
419
- row('TRADES', String(stats.trades || 0), chalk.white, 'WIN RATE', winRate, stats.wins >= stats.losses ? chalk.green : chalk.red);
419
+ row('Trades', String(stats.trades || 0), chalk.white, 'Win Rate', winRate, stats.wins >= stats.losses ? chalk.green : chalk.red);
420
420
 
421
421
  console.log(chalk.cyan(GM));
422
422
 
423
423
  // Row 3: Wins | Losses
424
- row('WINS', String(stats.wins || 0), chalk.green, 'LOSSES', String(stats.losses || 0), chalk.red);
424
+ row('Wins', String(stats.wins || 0), chalk.green, 'Losses', String(stats.losses || 0), chalk.red);
425
425
 
426
426
  console.log(chalk.cyan(GM));
427
427
 
@@ -430,7 +430,7 @@ const renderSessionSummary = (stats, stopReason) => {
430
430
  const pnlStr = `${pnl >= 0 ? '+' : ''}$${Math.abs(pnl).toFixed(2)}`;
431
431
  const pnlColor = pnl >= 0 ? chalk.green : chalk.red;
432
432
  const targetStr = `$${(stats.target || 0).toFixed(2)}`;
433
- row('P&L', pnlStr, pnlColor, 'TARGET', targetStr, chalk.cyan);
433
+ row('P&L', pnlStr, pnlColor, 'Target', targetStr, chalk.cyan);
434
434
 
435
435
  // Bottom border
436
436
  console.log(chalk.cyan(BOX.BOT + BOX.H.repeat(W) + BOX.BR));