hedgequantx 2.9.20 → 2.9.22

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 (36) hide show
  1. package/package.json +1 -1
  2. package/src/app.js +64 -42
  3. package/src/menus/connect.js +17 -14
  4. package/src/menus/dashboard.js +76 -58
  5. package/src/pages/accounts.js +49 -38
  6. package/src/pages/ai-agents-ui.js +388 -0
  7. package/src/pages/ai-agents.js +494 -0
  8. package/src/pages/ai-models.js +389 -0
  9. package/src/pages/algo/algo-executor.js +307 -0
  10. package/src/pages/algo/copy-executor.js +331 -0
  11. package/src/pages/algo/copy-trading.js +178 -546
  12. package/src/pages/algo/custom-strategy.js +313 -0
  13. package/src/pages/algo/index.js +75 -18
  14. package/src/pages/algo/one-account.js +57 -322
  15. package/src/pages/algo/ui.js +15 -15
  16. package/src/pages/orders.js +22 -19
  17. package/src/pages/positions.js +22 -19
  18. package/src/pages/stats/index.js +16 -15
  19. package/src/pages/user.js +11 -7
  20. package/src/services/ai-supervision/consensus.js +284 -0
  21. package/src/services/ai-supervision/context.js +275 -0
  22. package/src/services/ai-supervision/directive.js +167 -0
  23. package/src/services/ai-supervision/health.js +47 -35
  24. package/src/services/ai-supervision/index.js +359 -0
  25. package/src/services/ai-supervision/parser.js +278 -0
  26. package/src/services/ai-supervision/symbols.js +259 -0
  27. package/src/services/cliproxy/index.js +256 -0
  28. package/src/services/cliproxy/installer.js +111 -0
  29. package/src/services/cliproxy/manager.js +387 -0
  30. package/src/services/index.js +9 -1
  31. package/src/services/llmproxy/index.js +166 -0
  32. package/src/services/llmproxy/manager.js +411 -0
  33. package/src/services/rithmic/accounts.js +6 -8
  34. package/src/ui/box.js +5 -9
  35. package/src/ui/index.js +18 -5
  36. package/src/ui/menu.js +4 -4
@@ -1,37 +1,18 @@
1
1
  /**
2
- * One Account Mode - HQX Algo Trading with Strategy Selection
2
+ * One Account Mode - HQX Ultra Scalping
3
+ * Supports multi-agent AI supervision
3
4
  */
4
5
 
5
6
  const chalk = require('chalk');
6
7
  const ora = require('ora');
7
- const readline = require('readline');
8
8
 
9
9
  const { connections } = require('../../services');
10
- const { AlgoUI, renderSessionSummary } = require('./ui');
11
10
  const { prompts } = require('../../utils');
12
11
  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');
13
15
 
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
- };
35
16
 
36
17
 
37
18
  /**
@@ -93,15 +74,60 @@ const oneAccountMenu = async (service) => {
93
74
  const contract = await selectSymbol(accountService, selectedAccount);
94
75
  if (!contract) return;
95
76
 
96
- // Select strategy
97
- const strategyId = await selectStrategy();
98
- if (!strategyId) return;
99
-
100
77
  // Configure algo
101
- const config = await configureAlgo(selectedAccount, contract, strategyId);
78
+ const config = await configureAlgo(selectedAccount, contract);
102
79
  if (!config) return;
103
80
 
104
- await launchAlgo(accountService, selectedAccount, contract, config, strategyId);
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
+ });
105
131
  };
106
132
 
107
133
  /**
@@ -156,12 +182,9 @@ const selectSymbol = async (service, account) => {
156
182
  /**
157
183
  * Configure algo
158
184
  */
159
- const configureAlgo = async (account, contract, strategyId) => {
160
- const strategyInfo = getStrategy(strategyId);
161
-
185
+ const configureAlgo = async (account, contract) => {
162
186
  console.log();
163
187
  console.log(chalk.cyan(' Configure Algo Parameters'));
164
- console.log(chalk.gray(` Strategy: ${strategyInfo.name}`));
165
188
  console.log();
166
189
 
167
190
  const contracts = await prompts.numberInput('Number of contracts:', 1, 1, 10);
@@ -182,292 +205,4 @@ const configureAlgo = async (account, contract, strategyId) => {
182
205
  return { contracts, dailyTarget, maxRisk, showName };
183
206
  };
184
207
 
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
-
473
208
  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.white(center(`Prop Futures Algo Trading v${version}`, W)) + chalk.cyan(BOX.V));
130
+ this._line(chalk.cyan(BOX.V) + chalk.yellow(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', W)) + chalk.cyan(BOX.V));
132
+ this._line(chalk.cyan(BOX.V) + chalk.yellow(center((this.config.subtitle || 'HQX ALGO TRADING').toUpperCase(), 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
- console.clear();
374
+ process.stdout.write('\x1B[2J\x1B[H');
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.white(center(`Prop Futures Algo Trading v${version}`, W)) + chalk.cyan(BOX.V));
390
+ console.log(chalk.cyan(BOX.V) + chalk.yellow(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));
@@ -7,37 +7,40 @@ const ora = require('ora');
7
7
 
8
8
  const { connections } = require('../services');
9
9
  const { ORDER_STATUS, ORDER_TYPE, ORDER_SIDE } = require('../config');
10
- const { getLogoWidth, drawBoxHeader, drawBoxFooter, drawBoxRow, drawBoxSeparator } = require('../ui');
10
+ const { getLogoWidth, drawBoxHeader, drawBoxFooter, drawBoxRow, drawBoxSeparator, displayBanner, clearScreen } = require('../ui');
11
11
  const { prompts } = require('../utils');
12
12
 
13
13
  /**
14
14
  * Show all orders
15
15
  */
16
16
  const showOrders = async (service) => {
17
+ // Clear screen and show banner
18
+ clearScreen();
19
+ displayBanner();
20
+
17
21
  const boxWidth = getLogoWidth();
18
22
  let spinner;
19
23
 
20
24
  try {
21
- // Step 1: Get connections
22
- spinner = ora({ text: 'Loading connections...', color: 'yellow' }).start();
25
+ spinner = ora({ text: 'LOADING ORDERS...', color: 'yellow' }).start();
23
26
 
24
27
  const allConns = connections.count() > 0
25
28
  ? connections.getAll()
26
29
  : (service ? [{ service, propfirm: service.propfirm?.name || 'Unknown', type: 'single' }] : []);
27
30
 
28
31
  if (allConns.length === 0) {
29
- spinner.fail('No connections found');
32
+ spinner.fail('NO CONNECTIONS FOUND');
30
33
  await prompts.waitForEnter();
31
34
  return;
32
35
  }
33
- spinner.succeed(`Found ${allConns.length} connection(s)`);
36
+ spinner.succeed(`FOUND ${allConns.length} CONNECTION(S)`);
34
37
 
35
38
  // Step 2: Fetch accounts
36
39
  let allAccounts = [];
37
40
 
38
41
  for (const conn of allConns) {
39
42
  const propfirmName = conn.propfirm || conn.type || 'Unknown';
40
- spinner = ora({ text: `Fetching accounts from ${propfirmName}...`, color: 'yellow' }).start();
43
+ spinner = ora({ text: `FETCHING ACCOUNTS FROM ${propfirmName.toUpperCase()}...`, color: 'yellow' }).start();
41
44
 
42
45
  try {
43
46
  const result = await conn.service.getTradingAccounts();
@@ -49,17 +52,17 @@ const showOrders = async (service) => {
49
52
  service: conn.service
50
53
  });
51
54
  });
52
- spinner.succeed(`${propfirmName}: ${result.accounts.length} account(s)`);
55
+ spinner.succeed(`${propfirmName.toUpperCase()}: ${result.accounts.length} ACCOUNT(S)`);
53
56
  } else {
54
- spinner.warn(`${propfirmName}: No accounts`);
57
+ spinner.warn(`${propfirmName.toUpperCase()}: NO ACCOUNTS`);
55
58
  }
56
59
  } catch (e) {
57
- spinner.fail(`${propfirmName}: Failed`);
60
+ spinner.fail(`${propfirmName.toUpperCase()}: FAILED`);
58
61
  }
59
62
  }
60
63
 
61
64
  if (allAccounts.length === 0) {
62
- console.log(chalk.yellow('\n No accounts found.'));
65
+ console.log(chalk.yellow('\n NO ACCOUNTS FOUND.'));
63
66
  await prompts.waitForEnter();
64
67
  return;
65
68
  }
@@ -69,7 +72,7 @@ const showOrders = async (service) => {
69
72
 
70
73
  for (const account of allAccounts) {
71
74
  const accName = String(account.accountName || account.rithmicAccountId || account.accountId || 'Unknown').substring(0, 20);
72
- spinner = ora({ text: `Fetching orders for ${accName}...`, color: 'yellow' }).start();
75
+ spinner = ora({ text: `FETCHING ORDERS FOR ${accName.toUpperCase()}...`, color: 'yellow' }).start();
73
76
 
74
77
  try {
75
78
  const result = await account.service.getOrders(account.accountId);
@@ -81,26 +84,26 @@ const showOrders = async (service) => {
81
84
  propfirm: account.propfirm
82
85
  });
83
86
  });
84
- spinner.succeed(`${accName}: ${result.orders.length} order(s)`);
87
+ spinner.succeed(`${accName.toUpperCase()}: ${result.orders.length} ORDER(S)`);
85
88
  } else {
86
- spinner.succeed(`${accName}: No orders`);
89
+ spinner.succeed(`${accName.toUpperCase()}: NO ORDERS`);
87
90
  }
88
91
  } catch (e) {
89
- spinner.fail(`${accName}: Failed to fetch orders`);
92
+ spinner.fail(`${accName.toUpperCase()}: FAILED TO FETCH ORDERS`);
90
93
  }
91
94
  }
92
95
 
93
- spinner = ora({ text: 'Preparing display...', color: 'yellow' }).start();
94
- spinner.succeed(`Total: ${allOrders.length} order(s)`);
96
+ spinner = ora({ text: 'PREPARING DISPLAY...', color: 'yellow' }).start();
97
+ spinner.succeed(`TOTAL: ${allOrders.length} ORDER(S)`);
95
98
  console.log();
96
99
 
97
100
  // Display
98
101
  drawBoxHeader('ORDERS', boxWidth);
99
102
 
100
103
  if (allOrders.length === 0) {
101
- drawBoxRow(chalk.gray(' No orders found'), boxWidth);
104
+ drawBoxRow(chalk.gray(' NO ORDERS FOUND'), boxWidth);
102
105
  } else {
103
- const header = ' ' + 'Symbol'.padEnd(12) + 'Side'.padEnd(6) + 'Type'.padEnd(8) + 'Qty'.padEnd(6) + 'Price'.padEnd(10) + 'Status'.padEnd(12) + 'Account';
106
+ const header = ' ' + 'SYMBOL'.padEnd(12) + 'SIDE'.padEnd(6) + 'TYPE'.padEnd(8) + 'QTY'.padEnd(6) + 'PRICE'.padEnd(10) + 'STATUS'.padEnd(12) + 'ACCOUNT';
104
107
  drawBoxRow(chalk.white.bold(header), boxWidth);
105
108
  drawBoxSeparator(boxWidth);
106
109
 
@@ -130,7 +133,7 @@ const showOrders = async (service) => {
130
133
  console.log();
131
134
 
132
135
  } catch (error) {
133
- if (spinner) spinner.fail('Error: ' + error.message);
136
+ if (spinner) spinner.fail('ERROR: ' + error.message.toUpperCase());
134
137
  }
135
138
 
136
139
  await prompts.waitForEnter();