hedgequantx 2.6.162 → 2.7.0

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 (138) hide show
  1. package/README.md +15 -88
  2. package/bin/cli.js +0 -11
  3. package/dist/lib/api.jsc +0 -0
  4. package/dist/lib/api2.jsc +0 -0
  5. package/dist/lib/core.jsc +0 -0
  6. package/dist/lib/core2.jsc +0 -0
  7. package/dist/lib/data.js +1 -1
  8. package/dist/lib/data.jsc +0 -0
  9. package/dist/lib/data2.jsc +0 -0
  10. package/dist/lib/decoder.jsc +0 -0
  11. package/dist/lib/m/mod1.jsc +0 -0
  12. package/dist/lib/m/mod2.jsc +0 -0
  13. package/dist/lib/n/r1.jsc +0 -0
  14. package/dist/lib/n/r2.jsc +0 -0
  15. package/dist/lib/n/r3.jsc +0 -0
  16. package/dist/lib/n/r4.jsc +0 -0
  17. package/dist/lib/n/r5.jsc +0 -0
  18. package/dist/lib/n/r6.jsc +0 -0
  19. package/dist/lib/n/r7.jsc +0 -0
  20. package/dist/lib/o/util1.jsc +0 -0
  21. package/dist/lib/o/util2.jsc +0 -0
  22. package/package.json +6 -3
  23. package/src/app.js +40 -162
  24. package/src/config/constants.js +31 -33
  25. package/src/config/propfirms.js +13 -217
  26. package/src/config/settings.js +0 -43
  27. package/src/lib/api.js +198 -0
  28. package/src/lib/api2.js +353 -0
  29. package/src/lib/core.js +539 -0
  30. package/src/lib/core2.js +341 -0
  31. package/src/lib/data.js +555 -0
  32. package/src/lib/data2.js +492 -0
  33. package/src/lib/decoder.js +599 -0
  34. package/src/lib/m/s1.js +804 -0
  35. package/src/lib/m/s2.js +34 -0
  36. package/src/lib/n/r1.js +454 -0
  37. package/src/lib/n/r2.js +514 -0
  38. package/src/lib/n/r3.js +631 -0
  39. package/src/lib/n/r4.js +401 -0
  40. package/src/lib/n/r5.js +335 -0
  41. package/src/lib/n/r6.js +425 -0
  42. package/src/lib/n/r7.js +530 -0
  43. package/src/lib/o/l1.js +44 -0
  44. package/src/lib/o/l2.js +427 -0
  45. package/src/lib/python-bridge.js +206 -0
  46. package/src/menus/connect.js +14 -176
  47. package/src/menus/dashboard.js +65 -110
  48. package/src/pages/accounts.js +18 -18
  49. package/src/pages/algo/copy-trading.js +210 -240
  50. package/src/pages/algo/index.js +41 -104
  51. package/src/pages/algo/one-account.js +386 -33
  52. package/src/pages/algo/ui.js +312 -151
  53. package/src/pages/orders.js +3 -3
  54. package/src/pages/positions.js +3 -3
  55. package/src/pages/stats/chart.js +74 -0
  56. package/src/pages/stats/display.js +228 -0
  57. package/src/pages/stats/index.js +236 -0
  58. package/src/pages/stats/metrics.js +213 -0
  59. package/src/pages/user.js +6 -6
  60. package/src/services/hqx-server/constants.js +55 -0
  61. package/src/services/hqx-server/index.js +401 -0
  62. package/src/services/hqx-server/latency.js +81 -0
  63. package/src/services/index.js +12 -3
  64. package/src/services/rithmic/accounts.js +7 -32
  65. package/src/services/rithmic/connection.js +1 -204
  66. package/src/services/rithmic/contracts.js +235 -0
  67. package/src/services/rithmic/handlers.js +21 -196
  68. package/src/services/rithmic/index.js +60 -291
  69. package/src/services/rithmic/market.js +31 -0
  70. package/src/services/rithmic/orders.js +5 -361
  71. package/src/services/rithmic/protobuf.js +5 -195
  72. package/src/services/session.js +22 -173
  73. package/src/ui/box.js +10 -18
  74. package/src/ui/index.js +1 -3
  75. package/src/ui/menu.js +1 -1
  76. package/src/utils/prompts.js +2 -2
  77. package/dist/lib/m/s1.js +0 -1
  78. package/src/menus/ai-agent-connect.js +0 -181
  79. package/src/menus/ai-agent-models.js +0 -219
  80. package/src/menus/ai-agent-oauth.js +0 -292
  81. package/src/menus/ai-agent-ui.js +0 -141
  82. package/src/menus/ai-agent.js +0 -484
  83. package/src/pages/algo/algo-config.js +0 -195
  84. package/src/pages/algo/algo-multi.js +0 -801
  85. package/src/pages/algo/algo-utils.js +0 -58
  86. package/src/pages/algo/copy-engine.js +0 -449
  87. package/src/pages/algo/custom-strategy.js +0 -459
  88. package/src/pages/algo/logger.js +0 -245
  89. package/src/pages/algo/smart-logs-data.js +0 -218
  90. package/src/pages/algo/smart-logs.js +0 -387
  91. package/src/pages/algo/ui-constants.js +0 -144
  92. package/src/pages/algo/ui-summary.js +0 -184
  93. package/src/pages/stats-calculations.js +0 -191
  94. package/src/pages/stats-ui.js +0 -381
  95. package/src/pages/stats.js +0 -339
  96. package/src/services/ai/client-analysis.js +0 -194
  97. package/src/services/ai/client-models.js +0 -333
  98. package/src/services/ai/client.js +0 -343
  99. package/src/services/ai/index.js +0 -384
  100. package/src/services/ai/oauth-anthropic.js +0 -265
  101. package/src/services/ai/oauth-gemini.js +0 -223
  102. package/src/services/ai/oauth-iflow.js +0 -269
  103. package/src/services/ai/oauth-openai.js +0 -233
  104. package/src/services/ai/oauth-qwen.js +0 -279
  105. package/src/services/ai/providers/index.js +0 -526
  106. package/src/services/ai/proxy-install.js +0 -249
  107. package/src/services/ai/proxy-manager.js +0 -494
  108. package/src/services/ai/proxy-remote.js +0 -161
  109. package/src/services/ai/strategy-supervisor.js +0 -1312
  110. package/src/services/ai/supervisor-data.js +0 -195
  111. package/src/services/ai/supervisor-optimize.js +0 -215
  112. package/src/services/ai/supervisor-sync.js +0 -178
  113. package/src/services/ai/supervisor-utils.js +0 -158
  114. package/src/services/ai/supervisor.js +0 -484
  115. package/src/services/ai/validation.js +0 -250
  116. package/src/services/hqx-server-events.js +0 -110
  117. package/src/services/hqx-server-handlers.js +0 -217
  118. package/src/services/hqx-server-latency.js +0 -136
  119. package/src/services/hqx-server.js +0 -403
  120. package/src/services/position-constants.js +0 -28
  121. package/src/services/position-manager.js +0 -528
  122. package/src/services/position-momentum.js +0 -206
  123. package/src/services/projectx/accounts.js +0 -142
  124. package/src/services/projectx/index.js +0 -443
  125. package/src/services/projectx/market.js +0 -172
  126. package/src/services/projectx/stats.js +0 -110
  127. package/src/services/projectx/trading.js +0 -180
  128. package/src/services/rithmic/latency-tracker.js +0 -182
  129. package/src/services/rithmic/market-data.js +0 -549
  130. package/src/services/rithmic/specs.js +0 -146
  131. package/src/services/rithmic/trade-history.js +0 -254
  132. package/src/services/session-history.js +0 -475
  133. package/src/services/strategy/hft-tick.js +0 -507
  134. package/src/services/strategy/recovery-math.js +0 -402
  135. package/src/services/tradovate/constants.js +0 -109
  136. package/src/services/tradovate/index.js +0 -505
  137. package/src/services/tradovate/market.js +0 -47
  138. package/src/services/tradovate/websocket.js +0 -97
@@ -1,14 +1,13 @@
1
1
  /**
2
- * Connection Menus - PropFirm platform selection and login
2
+ * Connection Menus - Rithmic Only
3
3
  */
4
4
 
5
5
  const chalk = require('chalk');
6
6
  const ora = require('ora');
7
7
 
8
- const { ProjectXService, connections } = require('../services');
8
+ const { connections } = require('../services');
9
9
  const { RithmicService } = require('../services/rithmic');
10
- const { TradovateService } = require('../services/tradovate');
11
- const { getPropFirmsByPlatform } = require('../config');
10
+ const { PROPFIRM_CHOICES } = require('../config');
12
11
  const { getLogoWidth, centerText, prepareStdin } = require('../ui');
13
12
  const { validateUsername, validatePassword } = require('../security');
14
13
  const { prompts } = require('../utils');
@@ -22,12 +21,12 @@ const loginPrompt = async (propfirmName) => {
22
21
  console.log(chalk.cyan(`Connecting to ${propfirmName}...`));
23
22
  console.log();
24
23
 
25
- const username = await prompts.textInput('USERNAME:', '', (input) => {
24
+ const username = await prompts.textInput('Username:', '', (input) => {
26
25
  try { validateUsername(input); return undefined; } catch (e) { return e.message; }
27
26
  });
28
27
  if (!username) return null;
29
28
 
30
- const pwd = await prompts.passwordInput('PASSWORD:', (input) => {
29
+ const pwd = await prompts.passwordInput('Password:', (input) => {
31
30
  try { validatePassword(input); return undefined; } catch (e) { return e.message; }
32
31
  });
33
32
  if (!pwd) return null;
@@ -36,87 +35,20 @@ const loginPrompt = async (propfirmName) => {
36
35
  };
37
36
 
38
37
  /**
39
- * ProjectX menu
40
- */
41
- const projectXMenu = async () => {
42
- const propfirms = getPropFirmsByPlatform('ProjectX');
43
- const boxWidth = getLogoWidth();
44
- const W = boxWidth - 2;
45
- const col1Width = Math.floor(W / 2);
46
-
47
- const numbered = propfirms.map((pf, i) => ({ num: i + 1, key: pf.key, name: pf.displayName }));
48
-
49
- console.log();
50
- console.log(chalk.cyan('╔' + '═'.repeat(W) + '╗'));
51
- console.log(chalk.cyan('║') + chalk.white.bold(centerText('SELECT PROPFIRM (ProjectX)', W)) + chalk.cyan('║'));
52
- console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
53
-
54
- const menuRow = (left, right) => {
55
- const leftPlain = left ? left.replace(/\x1b\[[0-9;]*m/g, '') : '';
56
- const rightPlain = right ? right.replace(/\x1b\[[0-9;]*m/g, '') : '';
57
- const leftPadded = ' ' + (left || '') + ' '.repeat(Math.max(0, col1Width - leftPlain.length - 2));
58
- const rightPadded = (right || '') + ' '.repeat(Math.max(0, W - col1Width - rightPlain.length));
59
- console.log(chalk.cyan('║') + leftPadded + rightPadded + chalk.cyan('║'));
60
- };
61
-
62
- for (let i = 0; i < numbered.length; i += 2) {
63
- const left = numbered[i];
64
- const right = numbered[i + 1];
65
- const leftText = chalk.cyan(`[${left.num.toString().padStart(2, ' ')}]`) + ' ' + chalk.white(left.name);
66
- const rightText = right ? chalk.cyan(`[${right.num.toString().padStart(2, ' ')}]`) + ' ' + chalk.white(right.name) : '';
67
- menuRow(leftText, rightText);
68
- }
69
-
70
- console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
71
- console.log(chalk.cyan('║') + ' ' + chalk.red('[X] BACK') + ' '.repeat(W - 10) + chalk.cyan('║'));
72
- console.log(chalk.cyan('╚' + '═'.repeat(W) + '╝'));
73
-
74
- const input = await prompts.textInput(chalk.cyan('SELECT NUMBER (OR X):'));
75
- if (!input || input.toLowerCase() === 'x') return null;
76
-
77
- const action = parseInt(input);
78
- if (isNaN(action) || action < 1 || action > numbered.length) return null;
79
-
80
- const selectedPropfirm = numbered[action - 1];
81
- const credentials = await loginPrompt(selectedPropfirm.name);
82
- if (!credentials) return null;
83
-
84
- const spinner = ora({ text: 'AUTHENTICATING...', color: 'yellow' }).start();
85
-
86
- try {
87
- const service = new ProjectXService(selectedPropfirm.key);
88
- const result = await service.login(credentials.username, credentials.password);
89
-
90
- if (result.success) {
91
- await service.getUser();
92
- connections.add('projectx', service, service.propfirm.name);
93
- spinner.succeed(`Connected to ${service.propfirm.name}`);
94
- return service;
95
- } else {
96
- spinner.fail(result.error || 'AUTHENTICATION FAILED');
97
- return null;
98
- }
99
- } catch (error) {
100
- spinner.fail(error.message);
101
- return null;
102
- }
103
- };
104
-
105
- /**
106
- * Rithmic menu
38
+ * Rithmic menu - Main connection menu
107
39
  */
108
40
  const rithmicMenu = async () => {
109
- const propfirms = getPropFirmsByPlatform('Rithmic');
41
+ const propfirms = PROPFIRM_CHOICES;
110
42
  const boxWidth = getLogoWidth();
111
43
  const innerWidth = boxWidth - 2;
112
44
  const numCols = 3;
113
45
  const colWidth = Math.floor(innerWidth / numCols);
114
46
 
115
- const numbered = propfirms.map((pf, i) => ({ num: i + 1, key: pf.key, name: pf.displayName, systemName: pf.rithmicSystem }));
47
+ const numbered = propfirms.map((pf, i) => ({ num: i + 1, key: pf.value, name: pf.name }));
116
48
 
117
49
  console.log();
118
50
  console.log(chalk.cyan('╔' + '═'.repeat(innerWidth) + '╗'));
119
- console.log(chalk.cyan('║') + chalk.white.bold(centerText('SELECT PROPFIRM (RITHMIC)', innerWidth)) + chalk.cyan('║'));
51
+ console.log(chalk.cyan('║') + chalk.white.bold(centerText('SELECT PROPFIRM', innerWidth)) + chalk.cyan('║'));
120
52
  console.log(chalk.cyan('║') + ' '.repeat(innerWidth) + chalk.cyan('║'));
121
53
 
122
54
  const rows = Math.ceil(numbered.length / numCols);
@@ -139,10 +71,10 @@ const rithmicMenu = async () => {
139
71
  }
140
72
 
141
73
  console.log(chalk.cyan('║') + ' '.repeat(innerWidth) + chalk.cyan('║'));
142
- console.log(chalk.cyan('║') + ' ' + chalk.red('[X] BACK') + ' '.repeat(innerWidth - 10) + chalk.cyan('║'));
74
+ console.log(chalk.cyan('║') + ' ' + chalk.red('[X] Back') + ' '.repeat(innerWidth - 10) + chalk.cyan('║'));
143
75
  console.log(chalk.cyan('╚' + '═'.repeat(innerWidth) + '╝'));
144
76
 
145
- const input = await prompts.textInput(chalk.cyan('SELECT NUMBER (OR X):'));
77
+ const input = await prompts.textInput(chalk.cyan('Select number (or X):'));
146
78
  if (!input || input.toLowerCase() === 'x') return null;
147
79
 
148
80
  const action = parseInt(input);
@@ -152,7 +84,7 @@ const rithmicMenu = async () => {
152
84
  const credentials = await loginPrompt(selectedPropfirm.name);
153
85
  if (!credentials) return null;
154
86
 
155
- const spinner = ora({ text: 'CONNECTING TO RITHMIC...', color: 'yellow' }).start();
87
+ const spinner = ora({ text: 'Connecting to Rithmic...', color: 'yellow' }).start();
156
88
 
157
89
  try {
158
90
  const service = new RithmicService(selectedPropfirm.key);
@@ -166,7 +98,7 @@ const rithmicMenu = async () => {
166
98
  await new Promise(r => setTimeout(r, 1500));
167
99
  return service;
168
100
  } else {
169
- spinner.fail(result.error || 'AUTHENTICATION FAILED');
101
+ spinner.fail(result.error || 'Authentication failed');
170
102
  await new Promise(r => setTimeout(r, 2000));
171
103
  return null;
172
104
  }
@@ -177,98 +109,4 @@ const rithmicMenu = async () => {
177
109
  }
178
110
  };
179
111
 
180
- /**
181
- * Tradovate menu
182
- */
183
- const tradovateMenu = async () => {
184
- const propfirms = getPropFirmsByPlatform('Tradovate');
185
- const boxWidth = getLogoWidth();
186
- const innerWidth = boxWidth - 2;
187
-
188
- const numbered = propfirms.map((pf, i) => ({ num: i + 1, key: pf.key, name: pf.displayName }));
189
-
190
- console.log();
191
- console.log(chalk.cyan('╔' + '═'.repeat(innerWidth) + '╗'));
192
- console.log(chalk.cyan('║') + chalk.white.bold(centerText('SELECT PROPFIRM (TRADOVATE)', innerWidth)) + chalk.cyan('║'));
193
- console.log(chalk.cyan('║') + ' '.repeat(innerWidth) + chalk.cyan('║'));
194
-
195
- for (const item of numbered) {
196
- const numStr = item.num.toString().padStart(2, ' ');
197
- const text = ' ' + chalk.cyan(`[${numStr}]`) + ' ' + chalk.white(item.name);
198
- const textLen = 4 + 1 + item.name.length + 2;
199
- console.log(chalk.cyan('║') + text + ' '.repeat(innerWidth - textLen) + chalk.cyan('║'));
200
- }
201
-
202
- console.log(chalk.cyan('║') + ' '.repeat(innerWidth) + chalk.cyan('║'));
203
- console.log(chalk.cyan('║') + ' ' + chalk.red('[X] BACK') + ' '.repeat(innerWidth - 10) + chalk.cyan('║'));
204
- console.log(chalk.cyan('╚' + '═'.repeat(innerWidth) + '╝'));
205
-
206
- const input = await prompts.textInput(chalk.cyan('SELECT NUMBER (OR X):'));
207
- if (!input || input.toLowerCase() === 'x') return null;
208
-
209
- const action = parseInt(input);
210
- if (isNaN(action) || action < 1 || action > numbered.length) return null;
211
-
212
- const selectedPropfirm = numbered[action - 1];
213
- const credentials = await loginPrompt(selectedPropfirm.name);
214
- if (!credentials) return null;
215
-
216
- const spinner = ora({ text: 'CONNECTING TO TRADOVATE...', color: 'yellow' }).start();
217
-
218
- try {
219
- const service = new TradovateService(selectedPropfirm.key);
220
- const result = await service.login(credentials.username, credentials.password);
221
-
222
- if (result.success) {
223
- spinner.text = 'Fetching accounts...';
224
- await service.getTradingAccounts();
225
- connections.add('tradovate', service, service.propfirm.name);
226
- spinner.succeed(`Connected to ${service.propfirm.name}`);
227
- return service;
228
- } else {
229
- spinner.fail(result.error || 'AUTHENTICATION FAILED');
230
- return null;
231
- }
232
- } catch (error) {
233
- spinner.fail(error.message);
234
- return null;
235
- }
236
- };
237
-
238
- /**
239
- * Add Prop Account menu
240
- */
241
- const addPropAccountMenu = async () => {
242
- const boxWidth = getLogoWidth();
243
- const W = boxWidth - 2;
244
- const col1Width = Math.floor(W / 2);
245
-
246
- console.log();
247
- console.log(chalk.cyan('╔' + '═'.repeat(W) + '╗'));
248
- console.log(chalk.cyan('║') + chalk.white.bold(centerText('ADD PROP ACCOUNT', W)) + chalk.cyan('║'));
249
- console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
250
-
251
- const menuRow = (left, right) => {
252
- const leftPlain = left.replace(/\x1b\[[0-9;]*m/g, '');
253
- const rightPlain = right.replace(/\x1b\[[0-9;]*m/g, '');
254
- const leftPadded = ' ' + left + ' '.repeat(Math.max(0, col1Width - leftPlain.length - 2));
255
- const rightPadded = right + ' '.repeat(Math.max(0, W - col1Width - rightPlain.length));
256
- console.log(chalk.cyan('║') + leftPadded + rightPadded + chalk.cyan('║'));
257
- };
258
-
259
- menuRow(chalk.cyan('[1] ProjectX'), chalk.cyan('[2] Rithmic'));
260
- menuRow(chalk.cyan('[3] Tradovate'), chalk.red('[X] BACK'));
261
-
262
- console.log(chalk.cyan('╚' + '═'.repeat(W) + '╝'));
263
-
264
- const input = await prompts.textInput(chalk.cyan('SELECT (1/2/3/X):'));
265
- if (!input || input.toLowerCase() === 'x') return null;
266
-
267
- const num = parseInt(input);
268
- if (num === 1) return 'projectx';
269
- if (num === 2) return 'rithmic';
270
- if (num === 3) return 'tradovate';
271
- return null;
272
- };
273
-
274
- module.exports = { loginPrompt, projectXMenu, rithmicMenu, tradovateMenu, addPropAccountMenu };
112
+ module.exports = { loginPrompt, rithmicMenu };
@@ -10,8 +10,6 @@ const { connections } = require('../services');
10
10
  const { getLogoWidth, centerText, prepareStdin } = require('../ui');
11
11
  const { getCachedStats } = require('../services/stats-cache');
12
12
  const { prompts } = require('../utils');
13
- const aiService = require('../services/ai');
14
-
15
13
 
16
14
  /**
17
15
  * Dashboard menu after login
@@ -22,10 +20,6 @@ const dashboardMenu = async (service) => {
22
20
  const boxWidth = getLogoWidth();
23
21
  const W = boxWidth - 2;
24
22
 
25
- // Check AI connection status
26
- const aiConnected = aiService.isConnected();
27
- const aiAgentCount = aiService.getAgentCount();
28
-
29
23
  const makeLine = (content, align = 'left') => {
30
24
  const plainLen = content.replace(/\x1b\[[0-9;]*m/g, '').length;
31
25
  const padding = W - plainLen;
@@ -38,111 +32,74 @@ const dashboardMenu = async (service) => {
38
32
 
39
33
  // Continue from banner (use ╠ not ╔)
40
34
  console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
41
- console.log(makeLine(chalk.cyan.bold('WELCOME, HQX TRADER!'), 'center'));
35
+ console.log(makeLine(chalk.yellow.bold('Welcome, HQX Trader!'), 'center'));
42
36
  console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
43
37
 
44
38
  // Show connected propfirms
45
39
  const allConns = connections.getAll();
46
40
  if (allConns.length > 0) {
47
- const propfirms = allConns.slice(0, 3).map(c => (c.propfirm || c.type || 'CONNECTED').toUpperCase());
41
+ const propfirms = allConns.slice(0, 3).map(c => c.propfirm || c.type || 'Connected');
48
42
  const propfirmText = propfirms.map(p => chalk.green('● ') + chalk.white(p)).join(' ');
49
43
  console.log(makeLine(propfirmText, 'center'));
50
44
  }
51
45
 
52
- // Stats bar with icons
46
+ // Stats bar with yellow icons
53
47
  const statsInfo = getCachedStats();
54
48
  if (statsInfo) {
55
49
  console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
56
50
 
57
- const balStr = statsInfo.balance !== null ? `$${statsInfo.balance.toLocaleString()}` : '--';
51
+ const balStr = statsInfo.balance !== null ? `$${statsInfo.balance.toLocaleString()}` : '--';
58
52
  const balColor = statsInfo.balance !== null ? chalk.green : chalk.gray;
59
53
 
60
- // Build plain text for length calculation
61
- // Format: "✔ CONNECTIONS: X ✔ ACCOUNTS: X ✔ BALANCE: $X ✔ AI: X AGENT(S)"
62
- const aiText = aiAgentCount > 0
63
- ? `${aiAgentCount} AGENT${aiAgentCount > 1 ? 'S' : ''}`
64
- : 'NONE';
65
- const plainText = `* CONNECTIONS: ${statsInfo.connections} * ACCOUNTS: ${statsInfo.accounts} * BALANCE: ${balStr} * AI: ${aiText}`;
66
- const statsLen = plainText.length;
67
- const statsLeftPad = Math.max(0, Math.floor((W - statsLen) / 2));
68
- const statsRightPad = Math.max(0, W - statsLen - statsLeftPad);
54
+ let pnlDisplay, pnlColor;
55
+ if (statsInfo.pnl !== null) {
56
+ pnlColor = statsInfo.pnl >= 0 ? chalk.green : chalk.red;
57
+ pnlDisplay = `${statsInfo.pnl >= 0 ? '+' : ''}$${Math.abs(statsInfo.pnl).toLocaleString()}`;
58
+ } else {
59
+ pnlColor = chalk.gray;
60
+ pnlDisplay = '--';
61
+ }
69
62
 
70
- // Build with unicode icons and colors
71
- const checkIcon = chalk.yellow('✔ ');
72
- const aiIcon = aiAgentCount > 0 ? chalk.magenta('') : chalk.gray('○ ');
73
- const aiTextColored = aiAgentCount > 0
74
- ? chalk.magenta(`${aiAgentCount} AGENT${aiAgentCount > 1 ? 'S' : ''}`)
75
- : chalk.gray('NONE');
63
+ // Yellow icons: for each stat
64
+ const icon = chalk.yellow('✔ ');
65
+ const statsPlain = `✔ Connections: ${statsInfo.connections} ✔ Accounts: ${statsInfo.accounts} Balance: ${balStr} ✔ P&L: ${pnlDisplay}`;
66
+ const statsLeftPad = Math.floor((W - statsPlain.length) / 2);
67
+ const statsRightPad = W - statsPlain.length - statsLeftPad;
76
68
 
77
69
  console.log(chalk.cyan('║') + ' '.repeat(statsLeftPad) +
78
- checkIcon + chalk.white(`CONNECTIONS: ${statsInfo.connections}`) + ' ' +
79
- checkIcon + chalk.white(`ACCOUNTS: ${statsInfo.accounts}`) + ' ' +
80
- checkIcon + chalk.white('BALANCE: ') + balColor(balStr) + ' ' +
81
- aiIcon + chalk.white('AI: ') + aiTextColored +
82
- ' '.repeat(statsRightPad) + chalk.cyan('║'));
70
+ icon + chalk.white(`Connections: ${statsInfo.connections}`) + ' ' +
71
+ icon + chalk.white(`Accounts: ${statsInfo.accounts}`) + ' ' +
72
+ icon + chalk.white('Balance: ') + balColor(balStr) + ' ' +
73
+ icon + chalk.white('P&L: ') + pnlColor(pnlDisplay) +
74
+ ' '.repeat(Math.max(0, statsRightPad)) + chalk.cyan('║'));
83
75
  }
84
76
 
85
77
  console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
86
78
 
87
- // Menu in 3 columns - fixed width columns for perfect alignment
88
- const colWidth = Math.floor(W / 3);
89
-
90
- const menuRow3 = (col1, col2, col3) => {
91
- const c1Plain = col1.replace(/\x1b\[[0-9;]*m/g, '');
92
- const c2Plain = col2.replace(/\x1b\[[0-9;]*m/g, '');
93
- const c3Plain = col3.replace(/\x1b\[[0-9;]*m/g, '');
94
-
95
- // Center each item within its fixed-width column
96
- const pad1Left = Math.floor((colWidth - c1Plain.length) / 2);
97
- const pad1Right = colWidth - c1Plain.length - pad1Left;
98
-
99
- const pad2Left = Math.floor((colWidth - c2Plain.length) / 2);
100
- const pad2Right = colWidth - c2Plain.length - pad2Left;
101
-
102
- // Third column gets remaining width
103
- const col3Width = W - (colWidth * 2);
104
- const pad3Left = Math.floor((col3Width - c3Plain.length) / 2);
105
- const pad3Right = col3Width - c3Plain.length - pad3Left;
106
-
107
- const line =
108
- ' '.repeat(pad1Left) + col1 + ' '.repeat(pad1Right) +
109
- ' '.repeat(pad2Left) + col2 + ' '.repeat(pad2Right) +
110
- ' '.repeat(pad3Left) + col3 + ' '.repeat(pad3Right);
111
-
112
- console.log(chalk.cyan('║') + line + chalk.cyan('║'));
79
+ // Menu in 2 columns
80
+ const col1Width = Math.floor(W / 2);
81
+ const menuRow = (left, right) => {
82
+ const leftPlain = left.replace(/\x1b\[[0-9;]*m/g, '');
83
+ const rightPlain = right.replace(/\x1b\[[0-9;]*m/g, '');
84
+ const leftPadded = ' ' + left + ' '.repeat(Math.max(0, col1Width - leftPlain.length - 2));
85
+ const rightPadded = right + ' '.repeat(Math.max(0, W - col1Width - rightPlain.length));
86
+ console.log(chalk.cyan('║') + leftPadded + rightPadded + chalk.cyan('║'));
113
87
  };
114
88
 
115
- const centerLine = (content) => {
116
- const plainLen = content.replace(/\x1b\[[0-9;]*m/g, '').length;
117
- const padding = W - plainLen;
118
- const leftPad = Math.floor(padding / 2);
119
- console.log(chalk.cyan('║') + ' '.repeat(leftPad) + content + ' '.repeat(padding - leftPad) + chalk.cyan('║'));
120
- };
121
-
122
- // Fixed-width menu items for perfect alignment
123
- const menuItem = (key, label, color) => {
124
- const text = `[${key}] ${label.padEnd(14)}`;
125
- return color(text);
126
- };
127
-
128
- menuRow3(menuItem('1', 'VIEW ACCOUNTS', chalk.cyan), menuItem('2', 'VIEW STATS', chalk.cyan), menuItem('+', 'ADD ACCOUNT', chalk.cyan));
129
- menuRow3(menuItem('A', 'ALGO TRADING', chalk.magenta), menuItem('I', 'AI AGENT', chalk.magenta), menuItem('U', 'UPDATE HQX', chalk.yellow));
130
-
131
- // Separator and disconnect button centered
132
- console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
133
- centerLine(chalk.red('[X] DISCONNECT'));
89
+ menuRow(chalk.cyan('[1] View Accounts'), chalk.cyan('[2] View Stats'));
90
+ menuRow(chalk.cyan('[+] Add Prop-Account'), chalk.magenta('[A] Algo-Trading'));
91
+ menuRow(chalk.yellow('[U] Update HQX'), chalk.red('[X] Disconnect'));
134
92
 
135
93
  console.log(chalk.cyan('╚' + '═'.repeat(W) + '╝'));
136
94
 
137
95
  // Simple input - no duplicate menu
138
- const input = await prompts.textInput(chalk.cyan('SELECT (1/2/+/A/I/U/X)'));
96
+ const input = await prompts.textInput(chalk.cyan('Select (1/2/+/A/U/X)'));
139
97
 
140
98
  const actionMap = {
141
99
  '1': 'accounts',
142
100
  '2': 'stats',
143
101
  '+': 'add_prop_account',
144
102
  'a': 'algotrading',
145
- 'i': 'ai_agent',
146
103
  'u': 'update',
147
104
  'x': 'disconnect'
148
105
  };
@@ -164,8 +121,8 @@ const handleUpdate = async () => {
164
121
  currentVersion = require('../../package.json').version || 'unknown';
165
122
  } catch (e) {}
166
123
 
167
- console.log(chalk.cyan(`\n CURRENT VERSION: v${currentVersion}`));
168
- spinner = ora({ text: 'CHECKING FOR UPDATES...', color: 'yellow' }).start();
124
+ console.log(chalk.cyan(`\n Current version: v${currentVersion}`));
125
+ spinner = ora({ text: 'Checking for updates...', color: 'yellow' }).start();
169
126
 
170
127
  let latestVersion;
171
128
  try {
@@ -179,23 +136,23 @@ const handleUpdate = async () => {
179
136
  throw new Error('Invalid version format');
180
137
  }
181
138
  } catch (e) {
182
- spinner.fail('CANNOT REACH NPM REGISTRY');
183
- console.log(chalk.gray(` ERROR: ${e.message}`));
184
- console.log(chalk.yellow(' TRY MANUALLY: npm install -g hedgequantx@latest'));
139
+ spinner.fail('Cannot reach npm registry');
140
+ console.log(chalk.gray(` Error: ${e.message}`));
141
+ console.log(chalk.yellow(' Try manually: npm install -g hedgequantx@latest'));
185
142
  await prompts.waitForEnter();
186
143
  return;
187
144
  }
188
145
 
189
- spinner.succeed(`LATEST VERSION: v${latestVersion}`);
146
+ spinner.succeed(`Latest version: v${latestVersion}`);
190
147
 
191
148
  if (currentVersion === latestVersion) {
192
- console.log(chalk.green(' ALREADY UP TO DATE!'));
149
+ console.log(chalk.green(' Already up to date!'));
193
150
  await prompts.waitForEnter();
194
151
  return;
195
152
  }
196
153
 
197
- console.log(chalk.yellow(` UPDATE AVAILABLE: v${currentVersion} → v${latestVersion}`));
198
- spinner = ora({ text: 'INSTALLING UPDATE...', color: 'yellow' }).start();
154
+ console.log(chalk.yellow(` Update available: v${currentVersion} → v${latestVersion}`));
155
+ spinner = ora({ text: 'Installing update...', color: 'yellow' }).start();
199
156
 
200
157
  try {
201
158
  // Try with sudo first on Unix systems
@@ -210,39 +167,37 @@ const handleUpdate = async () => {
210
167
  encoding: 'utf8'
211
168
  });
212
169
  } catch (e) {
213
- spinner.fail('UPDATE FAILED - PERMISSION DENIED?');
214
- console.log(chalk.gray(` ERROR: ${e.message}`));
215
- console.log(chalk.yellow(' TRY MANUALLY WITH SUDO:'));
170
+ spinner.fail('Update failed - permission denied?');
171
+ console.log(chalk.gray(` Error: ${e.message}`));
172
+ console.log(chalk.yellow(' Try manually with sudo:'));
216
173
  console.log(chalk.white(' sudo npm install -g hedgequantx@latest'));
217
174
  await prompts.waitForEnter();
218
175
  return;
219
176
  }
220
177
 
221
- spinner.succeed(`UPDATED TO v${latestVersion}!`);
222
- console.log(chalk.cyan('\n RESTARTING HQX...\n'));
223
-
224
- // Clean terminal state before restart
225
- if (process.stdin.isTTY) {
226
- process.stdin.setRawMode(false);
227
- }
228
- process.stdin.pause();
229
-
230
- // Small delay then restart with clean stdio
231
- await new Promise(r => setTimeout(r, 500));
178
+ spinner.succeed(`Updated to v${latestVersion}!`);
179
+ console.log(chalk.cyan(' Restarting HQX...'));
232
180
 
233
- // Use spawnSync to restart - replaces current process
234
- const { spawnSync } = require('child_process');
235
- spawnSync('hqx', [], {
236
- stdio: 'inherit',
237
- shell: true
238
- });
181
+ await new Promise(r => setTimeout(r, 1500));
239
182
 
240
- process.exit(0);
183
+ try {
184
+ const child = spawn('hqx', [], {
185
+ stdio: 'inherit',
186
+ detached: true,
187
+ shell: true
188
+ });
189
+ child.unref();
190
+ process.exit(0);
191
+ } catch (e) {
192
+ console.log(chalk.yellow('\n Please restart HQX manually:'));
193
+ console.log(chalk.white(' hqx'));
194
+ await prompts.waitForEnter();
195
+ }
241
196
 
242
197
  } catch (error) {
243
- if (spinner) spinner.fail('UPDATE ERROR');
244
- console.log(chalk.gray(` ERROR: ${error.message}`));
245
- console.log(chalk.yellow(' TRY MANUALLY: npm install -g hedgequantx@latest'));
198
+ if (spinner) spinner.fail('Update error');
199
+ console.log(chalk.gray(` Error: ${error.message}`));
200
+ console.log(chalk.yellow(' Try manually: npm install -g hedgequantx@latest'));
246
201
  await prompts.waitForEnter();
247
202
  }
248
203
  };
@@ -7,7 +7,7 @@ const ora = require('ora');
7
7
 
8
8
  const { connections } = require('../services');
9
9
  const { ACCOUNT_STATUS, ACCOUNT_TYPE } = require('../config');
10
- const { getLogoWidth, getColWidths, drawBoxHeader, drawBoxFooter, draw2ColHeader, draw2ColSeparator, visibleLength } = require('../ui');
10
+ const { getLogoWidth, getColWidths, drawBoxHeader, drawBoxFooter, draw2ColHeader, visibleLength } = require('../ui');
11
11
  const { prompts } = require('../utils');
12
12
 
13
13
  /**
@@ -29,19 +29,19 @@ const showAccounts = async (service) => {
29
29
 
30
30
  try {
31
31
  // Single spinner for loading (appears below the dashboard header)
32
- spinner = ora({ text: 'LOADING ACCOUNTS...', color: 'yellow' }).start();
32
+ spinner = ora({ text: 'Loading accounts...', color: 'yellow' }).start();
33
33
 
34
- const allConns = connections.count() > 0 ? connections.getAll() : (service ? [{ service, propfirm: service.propfirm?.name || 'UNKNOWN', type: 'single' }] : []);
34
+ const allConns = connections.count() > 0 ? connections.getAll() : (service ? [{ service, propfirm: service.propfirm?.name || 'Unknown', type: 'single' }] : []);
35
35
 
36
36
  if (allConns.length === 0) {
37
- spinner.fail('NO CONNECTIONS FOUND');
37
+ spinner.fail('No connections found');
38
38
  await prompts.waitForEnter();
39
39
  return;
40
40
  }
41
41
 
42
42
  // Fetch accounts from each connection
43
43
  for (const conn of allConns) {
44
- const propfirmName = conn.propfirm || conn.type || 'UNKNOWN';
44
+ const propfirmName = conn.propfirm || conn.type || 'Unknown';
45
45
 
46
46
  try {
47
47
  const result = await conn.service.getTradingAccounts();
@@ -60,7 +60,7 @@ const showAccounts = async (service) => {
60
60
  }
61
61
 
62
62
  if (allAccounts.length === 0) {
63
- spinner.fail('NO ACCOUNTS FOUND');
63
+ spinner.fail('No accounts found');
64
64
  await prompts.waitForEnter();
65
65
  return;
66
66
  }
@@ -78,7 +78,7 @@ const showAccounts = async (service) => {
78
78
  } catch (e) {}
79
79
  }
80
80
 
81
- spinner.succeed('ACCOUNTS LOADED');
81
+ spinner.succeed('Accounts loaded');
82
82
  console.log();
83
83
 
84
84
  // Display accounts
@@ -94,9 +94,9 @@ const showAccounts = async (service) => {
94
94
  draw2ColHeader(name1.substring(0, col1 - 4), name2 ? name2.substring(0, col2 - 4) : '', boxWidth);
95
95
 
96
96
  // PropFirm
97
- const pf1 = chalk.magenta(acc1.propfirm || 'UNKNOWN');
98
- const pf2 = acc2 ? chalk.magenta(acc2.propfirm || 'UNKNOWN') : '';
99
- console.log(chalk.cyan('║') + fmtRow('PROPFIRM:', pf1, col1) + chalk.cyan('│') + (acc2 ? fmtRow('PROPFIRM:', pf2, col2) : ' '.repeat(col2)) + chalk.cyan('║'));
97
+ const pf1 = chalk.magenta(acc1.propfirm || 'Unknown');
98
+ const pf2 = acc2 ? chalk.magenta(acc2.propfirm || 'Unknown') : '';
99
+ console.log(chalk.cyan('║') + fmtRow('PropFirm:', pf1, col1) + chalk.cyan('│') + (acc2 ? fmtRow('PropFirm:', pf2, col2) : ' '.repeat(col2)) + chalk.cyan('║'));
100
100
 
101
101
  // Balance
102
102
  const bal1 = acc1.balance;
@@ -105,7 +105,7 @@ const showAccounts = async (service) => {
105
105
  const balStr2 = bal2 !== null && bal2 !== undefined ? '$' + Number(bal2).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) : '--';
106
106
  const balColor1 = bal1 === null || bal1 === undefined ? chalk.gray : (bal1 >= 0 ? chalk.green : chalk.red);
107
107
  const balColor2 = bal2 === null || bal2 === undefined ? chalk.gray : (bal2 >= 0 ? chalk.green : chalk.red);
108
- console.log(chalk.cyan('║') + fmtRow('BALANCE:', balColor1(balStr1), col1) + chalk.cyan('│') + (acc2 ? fmtRow('BALANCE:', balColor2(balStr2), col2) : ' '.repeat(col2)) + chalk.cyan('║'));
108
+ console.log(chalk.cyan('║') + fmtRow('Balance:', balColor1(balStr1), col1) + chalk.cyan('│') + (acc2 ? fmtRow('Balance:', balColor2(balStr2), col2) : ' '.repeat(col2)) + chalk.cyan('║'));
109
109
 
110
110
  // P&L
111
111
  const pnl1 = acc1.profitAndLoss;
@@ -117,17 +117,17 @@ const showAccounts = async (service) => {
117
117
  console.log(chalk.cyan('║') + fmtRow('P&L:', pnlColor1(pnlStr1), col1) + chalk.cyan('│') + (acc2 ? fmtRow('P&L:', pnlColor2(pnlStr2), col2) : ' '.repeat(col2)) + chalk.cyan('║'));
118
118
 
119
119
  // Status
120
- const status1 = ACCOUNT_STATUS[acc1.status] || { text: 'UNKNOWN', color: 'gray' };
121
- const status2 = acc2 ? (ACCOUNT_STATUS[acc2.status] || { text: 'UNKNOWN', color: 'gray' }) : null;
122
- console.log(chalk.cyan('║') + fmtRow('STATUS:', chalk[status1.color](status1.text), col1) + chalk.cyan('│') + (acc2 ? fmtRow('STATUS:', chalk[status2.color](status2.text), col2) : ' '.repeat(col2)) + chalk.cyan('║'));
120
+ const status1 = ACCOUNT_STATUS[acc1.status] || { text: 'Unknown', color: 'gray' };
121
+ const status2 = acc2 ? (ACCOUNT_STATUS[acc2.status] || { text: 'Unknown', color: 'gray' }) : null;
122
+ console.log(chalk.cyan('║') + fmtRow('Status:', chalk[status1.color](status1.text), col1) + chalk.cyan('│') + (acc2 ? fmtRow('Status:', chalk[status2.color](status2.text), col2) : ' '.repeat(col2)) + chalk.cyan('║'));
123
123
 
124
124
  // Type
125
- const type1 = ACCOUNT_TYPE[acc1.type] || { text: 'UNKNOWN', color: 'white' };
126
- const type2 = acc2 ? (ACCOUNT_TYPE[acc2.type] || { text: 'UNKNOWN', color: 'white' }) : null;
127
- console.log(chalk.cyan('║') + fmtRow('TYPE:', chalk[type1.color](type1.text), col1) + chalk.cyan('│') + (acc2 ? fmtRow('TYPE:', chalk[type2.color](type2.text), col2) : ' '.repeat(col2)) + chalk.cyan('║'));
125
+ const type1 = ACCOUNT_TYPE[acc1.type] || { text: 'Unknown', color: 'white' };
126
+ const type2 = acc2 ? (ACCOUNT_TYPE[acc2.type] || { text: 'Unknown', color: 'white' }) : null;
127
+ console.log(chalk.cyan('║') + fmtRow('Type:', chalk[type1.color](type1.text), col1) + chalk.cyan('│') + (acc2 ? fmtRow('Type:', chalk[type2.color](type2.text), col2) : ' '.repeat(col2)) + chalk.cyan('║'));
128
128
 
129
129
  if (i + 2 < allAccounts.length) {
130
- draw2ColSeparator(boxWidth);
130
+ console.log(chalk.cyan('╠') + chalk.cyan('═'.repeat(col1)) + chalk.cyan('╪') + chalk.cyan('═'.repeat(col2)) + chalk.cyan('╣'));
131
131
  }
132
132
  }
133
133