hedgequantx 2.4.37 → 2.4.39
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 +1 -1
- package/src/app.js +1 -1
- package/src/config/constants.js +27 -27
- package/src/menus/dashboard.js +5 -5
- package/src/pages/accounts.js +12 -12
- package/src/pages/algo/ui.js +14 -14
- package/src/pages/stats.js +36 -36
- package/src/pages/user.js +2 -2
- package/src/ui/box.js +1 -1
- package/src/ui/index.js +1 -1
- package/src/ui/menu.js +1 -1
- package/src/utils/prompts.js +1 -0
package/package.json
CHANGED
package/src/app.js
CHANGED
|
@@ -140,7 +140,7 @@ const banner = async () => {
|
|
|
140
140
|
|
|
141
141
|
console.log(chalk.cyan('╠' + '═'.repeat(innerWidth) + '╣'));
|
|
142
142
|
|
|
143
|
-
const tagline = isMobile ? `HQX v${version}` : `
|
|
143
|
+
const tagline = isMobile ? `HQX v${version}` : `PROP FUTURES ALGO TRADING v${version}`;
|
|
144
144
|
console.log(chalk.cyan('║') + chalk.white(centerText(tagline, innerWidth)) + chalk.cyan('║'));
|
|
145
145
|
};
|
|
146
146
|
|
package/src/config/constants.js
CHANGED
|
@@ -4,49 +4,49 @@
|
|
|
4
4
|
|
|
5
5
|
// Account Status Codes (ProjectX UserAPI)
|
|
6
6
|
const ACCOUNT_STATUS = {
|
|
7
|
-
0: { text: '
|
|
8
|
-
1: { text: '
|
|
9
|
-
2: { text: '
|
|
10
|
-
3: { text: '
|
|
11
|
-
4: { text: '
|
|
12
|
-
5: { text: '
|
|
13
|
-
6: { text: '
|
|
14
|
-
7: { text: '
|
|
15
|
-
8: { text: '
|
|
16
|
-
9: { text: '
|
|
7
|
+
0: { text: 'ACTIVE', color: 'green' },
|
|
8
|
+
1: { text: 'END OF DAY', color: 'cyan' },
|
|
9
|
+
2: { text: 'HALTED', color: 'red' },
|
|
10
|
+
3: { text: 'PAUSED', color: 'yellow' },
|
|
11
|
+
4: { text: 'HOLIDAY', color: 'blue' },
|
|
12
|
+
5: { text: 'EXPIRED', color: 'gray' },
|
|
13
|
+
6: { text: 'TERMINATED', color: 'red' },
|
|
14
|
+
7: { text: 'CANCELLED', color: 'red' },
|
|
15
|
+
8: { text: 'FAILED', color: 'red' },
|
|
16
|
+
9: { text: 'PASSED', color: 'green' }
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
// Account Types (ProjectX UserAPI)
|
|
20
20
|
const ACCOUNT_TYPE = {
|
|
21
|
-
0: { text: '
|
|
22
|
-
1: { text: '
|
|
23
|
-
2: { text: '
|
|
24
|
-
3: { text: '
|
|
25
|
-
4: { text: '
|
|
21
|
+
0: { text: 'PRACTICE', color: 'blue' },
|
|
22
|
+
1: { text: 'EVALUATION', color: 'yellow' },
|
|
23
|
+
2: { text: 'LIVE', color: 'green' },
|
|
24
|
+
3: { text: 'EXPRESS', color: 'magenta' },
|
|
25
|
+
4: { text: 'SIM', color: 'gray' }
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
// Order Status
|
|
29
29
|
const ORDER_STATUS = {
|
|
30
|
-
0: { text: '
|
|
31
|
-
1: { text: '
|
|
32
|
-
2: { text: '
|
|
33
|
-
3: { text: '
|
|
34
|
-
4: { text: '
|
|
35
|
-
5: { text: '
|
|
30
|
+
0: { text: 'PENDING', color: 'yellow', icon: '[~]' },
|
|
31
|
+
1: { text: 'WORKING', color: 'cyan', icon: '[>]' },
|
|
32
|
+
2: { text: 'FILLED', color: 'green', icon: '[OK]' },
|
|
33
|
+
3: { text: 'CANCELLED', color: 'gray', icon: '[X]' },
|
|
34
|
+
4: { text: 'REJECTED', color: 'red', icon: '[!]' },
|
|
35
|
+
5: { text: 'EXPIRED', color: 'gray', icon: '[-]' }
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
// Order Types
|
|
39
39
|
const ORDER_TYPE = {
|
|
40
|
-
1: '
|
|
41
|
-
2: '
|
|
42
|
-
3: '
|
|
43
|
-
4: '
|
|
40
|
+
1: 'MARKET',
|
|
41
|
+
2: 'LIMIT',
|
|
42
|
+
3: 'STOP',
|
|
43
|
+
4: 'STOP LIMIT'
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
// Order Side
|
|
47
47
|
const ORDER_SIDE = {
|
|
48
|
-
0: { text: '
|
|
49
|
-
1: { text: '
|
|
48
|
+
0: { text: 'BUY', color: 'green' },
|
|
49
|
+
1: { text: 'SELL', color: 'red' }
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
// NO STATIC CONTRACT DATA - All symbols/contracts come from API
|
package/src/menus/dashboard.js
CHANGED
|
@@ -38,7 +38,7 @@ const dashboardMenu = async (service) => {
|
|
|
38
38
|
// Show connected propfirms
|
|
39
39
|
const allConns = connections.getAll();
|
|
40
40
|
if (allConns.length > 0) {
|
|
41
|
-
const propfirms = allConns.slice(0, 3).map(c => c.propfirm || c.type || '
|
|
41
|
+
const propfirms = allConns.slice(0, 3).map(c => (c.propfirm || c.type || 'CONNECTED').toUpperCase());
|
|
42
42
|
const propfirmText = propfirms.map(p => chalk.green('● ') + chalk.white(p)).join(' ');
|
|
43
43
|
console.log(makeLine(propfirmText, 'center'));
|
|
44
44
|
}
|
|
@@ -62,14 +62,14 @@ const dashboardMenu = async (service) => {
|
|
|
62
62
|
|
|
63
63
|
// Yellow icons: ✔ for each stat
|
|
64
64
|
const icon = chalk.yellow('✔ ');
|
|
65
|
-
const statsPlain = `✔
|
|
65
|
+
const statsPlain = `✔ CONNECTIONS: ${statsInfo.connections} ✔ ACCOUNTS: ${statsInfo.accounts} ✔ BALANCE: ${balStr} ✔ P&L: ${pnlDisplay}`;
|
|
66
66
|
const statsLeftPad = Math.max(0, Math.floor((W - statsPlain.length) / 2));
|
|
67
67
|
const statsRightPad = Math.max(0, W - statsPlain.length - statsLeftPad);
|
|
68
68
|
|
|
69
69
|
console.log(chalk.cyan('║') + ' '.repeat(statsLeftPad) +
|
|
70
|
-
icon + chalk.white(`
|
|
71
|
-
icon + chalk.white(`
|
|
72
|
-
icon + chalk.white('
|
|
70
|
+
icon + chalk.white(`CONNECTIONS: ${statsInfo.connections}`) + ' ' +
|
|
71
|
+
icon + chalk.white(`ACCOUNTS: ${statsInfo.accounts}`) + ' ' +
|
|
72
|
+
icon + chalk.white('BALANCE: ') + balColor(balStr) + ' ' +
|
|
73
73
|
icon + chalk.white('P&L: ') + pnlColor(pnlDisplay) +
|
|
74
74
|
' '.repeat(Math.max(0, statsRightPad)) + chalk.cyan('║'));
|
|
75
75
|
}
|
package/src/pages/accounts.js
CHANGED
|
@@ -31,7 +31,7 @@ const showAccounts = async (service) => {
|
|
|
31
31
|
// Single spinner for loading (appears below the dashboard header)
|
|
32
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 || '
|
|
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
37
|
spinner.fail('NO CONNECTIONS FOUND');
|
|
@@ -41,7 +41,7 @@ const showAccounts = async (service) => {
|
|
|
41
41
|
|
|
42
42
|
// Fetch accounts from each connection
|
|
43
43
|
for (const conn of allConns) {
|
|
44
|
-
const propfirmName = conn.propfirm || conn.type || '
|
|
44
|
+
const propfirmName = conn.propfirm || conn.type || 'UNKNOWN';
|
|
45
45
|
|
|
46
46
|
try {
|
|
47
47
|
const result = await conn.service.getTradingAccounts();
|
|
@@ -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 || '
|
|
98
|
-
const pf2 = acc2 ? chalk.magenta(acc2.propfirm || '
|
|
99
|
-
console.log(chalk.cyan('║') + fmtRow('
|
|
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('
|
|
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,14 +117,14 @@ 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: '
|
|
121
|
-
const status2 = acc2 ? (ACCOUNT_STATUS[acc2.status] || { text: '
|
|
122
|
-
console.log(chalk.cyan('║') + fmtRow('
|
|
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: '
|
|
126
|
-
const type2 = acc2 ? (ACCOUNT_TYPE[acc2.type] || { text: '
|
|
127
|
-
console.log(chalk.cyan('║') + fmtRow('
|
|
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
130
|
console.log(chalk.cyan('╠') + chalk.cyan('═'.repeat(col1)) + chalk.cyan('╪') + chalk.cyan('═'.repeat(col2)) + chalk.cyan('╣'));
|
package/src/pages/algo/ui.js
CHANGED
|
@@ -136,7 +136,7 @@ class AlgoUI {
|
|
|
136
136
|
|
|
137
137
|
// Separator + title
|
|
138
138
|
this._line(chalk.cyan(BOX.ML + BOX.H.repeat(W) + BOX.MR));
|
|
139
|
-
this._line(chalk.cyan(BOX.V) + chalk.white(center(`
|
|
139
|
+
this._line(chalk.cyan(BOX.V) + chalk.white(center(`PROP FUTURES ALGO TRADING v${version}`, W)) + chalk.cyan(BOX.V));
|
|
140
140
|
this._line(chalk.cyan(BOX.ML + BOX.H.repeat(W) + BOX.MR));
|
|
141
141
|
this._line(chalk.cyan(BOX.V) + chalk.yellow.bold(center((this.config.subtitle || 'HQX ALGO TRADING').toUpperCase(), W)) + chalk.cyan(BOX.V));
|
|
142
142
|
}
|
|
@@ -183,7 +183,7 @@ class AlgoUI {
|
|
|
183
183
|
this._line(chalk.cyan(GM));
|
|
184
184
|
|
|
185
185
|
// Row 2: Qty | P&L
|
|
186
|
-
const r2c1 = buildCell('
|
|
186
|
+
const r2c1 = buildCell('QTY', (stats.qty || '1').toString(), chalk.cyan, colL);
|
|
187
187
|
const r2c2 = buildCell('P&L', pnlStr, pnlColor, colR);
|
|
188
188
|
row(r2c1.padded, r2c2.padded);
|
|
189
189
|
|
|
@@ -192,8 +192,8 @@ class AlgoUI {
|
|
|
192
192
|
// Row 3: Target | Risk
|
|
193
193
|
const targetStr = stats.target !== null && stats.target !== undefined ? '$' + stats.target.toFixed(2) : '--';
|
|
194
194
|
const riskStr = stats.risk !== null && stats.risk !== undefined ? '$' + stats.risk.toFixed(2) : '--';
|
|
195
|
-
const r3c1 = buildCell('
|
|
196
|
-
const r3c2 = buildCell('
|
|
195
|
+
const r3c1 = buildCell('TARGET', targetStr, chalk.green, colL);
|
|
196
|
+
const r3c2 = buildCell('RISK', riskStr, chalk.red, colR);
|
|
197
197
|
row(r3c1.padded, r3c2.padded);
|
|
198
198
|
|
|
199
199
|
this._line(chalk.cyan(GM));
|
|
@@ -201,15 +201,15 @@ class AlgoUI {
|
|
|
201
201
|
// Row 4: Trades | Latency (API response time) - UPPERCASE BOLD
|
|
202
202
|
const r4c1t = ` ${chalk.bold('TRADES')}: ${chalk.cyan.bold(stats.trades || 0)} ${chalk.bold('W/L')}: ${chalk.green.bold(stats.wins || 0)}/${chalk.red.bold(stats.losses || 0)}`;
|
|
203
203
|
const r4c1p = ` TRADES: ${stats.trades || 0} W/L: ${stats.wins || 0}/${stats.losses || 0}`;
|
|
204
|
-
const r4c2 = buildCell('
|
|
204
|
+
const r4c2 = buildCell('LATENCY', `${stats.latency || 0}MS`, latencyColor, colR);
|
|
205
205
|
row(r4c1t + pad(colL - r4c1p.length), r4c2.padded);
|
|
206
206
|
|
|
207
207
|
this._line(chalk.cyan(GM));
|
|
208
208
|
|
|
209
209
|
// Row 5: Connection | Propfirm
|
|
210
210
|
const connection = stats.platform || 'ProjectX';
|
|
211
|
-
const r5c1 = buildCell('
|
|
212
|
-
const r5c2 = buildCell('
|
|
211
|
+
const r5c1 = buildCell('CONNECTION', connection, chalk.cyan, colL);
|
|
212
|
+
const r5c2 = buildCell('PROPFIRM', stats.propfirm || 'N/A', chalk.cyan, colR);
|
|
213
213
|
row(r5c1.padded, r5c2.padded);
|
|
214
214
|
|
|
215
215
|
this._line(chalk.cyan(GB));
|
|
@@ -244,22 +244,22 @@ class AlgoUI {
|
|
|
244
244
|
|
|
245
245
|
// Row 2: Symbol (centered, single row)
|
|
246
246
|
const symbol = (stats.symbol || stats.leadSymbol || 'N/A').substring(0, 60);
|
|
247
|
-
const symbolText = `
|
|
247
|
+
const symbolText = `SYMBOL: ${symbol}`;
|
|
248
248
|
const symbolPadded = center(symbolText, W);
|
|
249
249
|
this._line(chalk.cyan(BOX.V) + chalk.yellow(symbolPadded) + chalk.cyan(BOX.V));
|
|
250
250
|
|
|
251
251
|
this._line(chalk.cyan(GT));
|
|
252
252
|
|
|
253
253
|
// Row 3: Lead Qty | Follower Qty
|
|
254
|
-
const r3c1 = buildCell('
|
|
255
|
-
const r3c2 = buildCell('
|
|
254
|
+
const r3c1 = buildCell('QTY', (stats.leadQty || '1').toString(), chalk.cyan, colL);
|
|
255
|
+
const r3c2 = buildCell('QTY', (stats.followerQty || '1').toString(), chalk.cyan, colR);
|
|
256
256
|
row(r3c1.padded, r3c2.padded);
|
|
257
257
|
|
|
258
258
|
this._line(chalk.cyan(GM));
|
|
259
259
|
|
|
260
260
|
// Row 4: Target | Risk
|
|
261
|
-
const r4c1 = buildCell('
|
|
262
|
-
const r4c2 = buildCell('
|
|
261
|
+
const r4c1 = buildCell('TARGET', '$' + (stats.target || 0).toFixed(2), chalk.green, colL);
|
|
262
|
+
const r4c2 = buildCell('RISK', '$' + (stats.risk || 0).toFixed(2), chalk.red, colR);
|
|
263
263
|
row(r4c1.padded, r4c2.padded);
|
|
264
264
|
|
|
265
265
|
this._line(chalk.cyan(GM));
|
|
@@ -396,7 +396,7 @@ const renderSessionSummary = (stats, stopReason) => {
|
|
|
396
396
|
|
|
397
397
|
// Separator + title
|
|
398
398
|
console.log(chalk.cyan(BOX.ML + BOX.H.repeat(W) + BOX.MR));
|
|
399
|
-
console.log(chalk.cyan(BOX.V) + chalk.white(center(`
|
|
399
|
+
console.log(chalk.cyan(BOX.V) + chalk.white(center(`PROP FUTURES ALGO TRADING v${version}`, W)) + chalk.cyan(BOX.V));
|
|
400
400
|
console.log(chalk.cyan(BOX.ML + BOX.H.repeat(W) + BOX.MR));
|
|
401
401
|
console.log(chalk.cyan(BOX.V) + chalk.yellow.bold(center('SESSION SUMMARY', W)) + chalk.cyan(BOX.V));
|
|
402
402
|
|
|
@@ -443,7 +443,7 @@ const renderSessionSummary = (stats, stopReason) => {
|
|
|
443
443
|
const pnlStr = `${pnl >= 0 ? '+' : ''}$${Math.abs(pnl).toFixed(2)}`;
|
|
444
444
|
const pnlColor = pnl >= 0 ? chalk.green : chalk.red;
|
|
445
445
|
const targetStr = `$${(stats.target || 0).toFixed(2)}`;
|
|
446
|
-
row('P&L', pnlStr, pnlColor, '
|
|
446
|
+
row('P&L', pnlStr, pnlColor, 'TARGET', targetStr, chalk.cyan);
|
|
447
447
|
|
|
448
448
|
// Bottom border
|
|
449
449
|
console.log(chalk.cyan(BOX.BOT + BOX.H.repeat(W) + BOX.BR));
|
package/src/pages/stats.js
CHANGED
|
@@ -337,13 +337,13 @@ const showStats = async (service) => {
|
|
|
337
337
|
: 'N/A';
|
|
338
338
|
const startBalStr = totalStartingBalance > 0 ? '$' + totalStartingBalance.toLocaleString(undefined, {minimumFractionDigits: 2}) : 'N/A';
|
|
339
339
|
|
|
340
|
-
console.log(chalk.cyan('\u2551') + fmtRow('
|
|
341
|
-
console.log(chalk.cyan('\u2551') + fmtRow('
|
|
342
|
-
console.log(chalk.cyan('\u2551') + fmtRow('
|
|
343
|
-
console.log(chalk.cyan('\u2551') + fmtRow('
|
|
344
|
-
console.log(chalk.cyan('\u2551') + fmtRow('
|
|
345
|
-
console.log(chalk.cyan('\u2551') + fmtRow('
|
|
346
|
-
console.log(chalk.cyan('\u2551') + fmtRow('
|
|
340
|
+
console.log(chalk.cyan('\u2551') + fmtRow('CONNECTIONS:', chalk.cyan(connTypeStr.join(', ') || String(connections.count() || 1)), col1) + chalk.cyan('\u2502') + fmtRow('TOTAL TRADES:', hasTradeData || stats.totalTrades > 0 ? chalk.white(String(stats.totalTrades)) : chalk.gray('N/A'), col2) + chalk.cyan('\u2551'));
|
|
341
|
+
console.log(chalk.cyan('\u2551') + fmtRow('TOTAL ACCOUNTS:', chalk.cyan(String(activeAccounts.length)), col1) + chalk.cyan('\u2502') + fmtRow('WINNING TRADES:', hasTradeData || stats.winningTrades > 0 ? chalk.green(String(stats.winningTrades)) : chalk.gray('N/A'), col2) + chalk.cyan('\u2551'));
|
|
342
|
+
console.log(chalk.cyan('\u2551') + fmtRow('TOTAL BALANCE:', totalBalanceColor(balanceStr), col1) + chalk.cyan('\u2502') + fmtRow('LOSING TRADES:', hasTradeData || stats.losingTrades > 0 ? chalk.red(String(stats.losingTrades)) : chalk.gray('N/A'), col2) + chalk.cyan('\u2551'));
|
|
343
|
+
console.log(chalk.cyan('\u2551') + fmtRow('STARTING BALANCE:', chalk.white(startBalStr), col1) + chalk.cyan('\u2502') + fmtRow('WIN RATE:', winRate !== 'N/A' ? (parseFloat(winRate) >= 50 ? chalk.green(winRate + '%') : chalk.yellow(winRate + '%')) : chalk.gray('N/A'), col2) + chalk.cyan('\u2551'));
|
|
344
|
+
console.log(chalk.cyan('\u2551') + fmtRow('TOTAL P&L:', pnlColor(pnlStr), col1) + chalk.cyan('\u2502') + fmtRow('LONG TRADES:', hasTradeData ? chalk.white(stats.longTrades + (longWinRate !== 'N/A' ? ' (' + longWinRate + '%)' : '')) : chalk.gray('N/A'), col2) + chalk.cyan('\u2551'));
|
|
345
|
+
console.log(chalk.cyan('\u2551') + fmtRow('OPEN POSITIONS:', chalk.white(String(totalOpenPositions)), col1) + chalk.cyan('\u2502') + fmtRow('SHORT TRADES:', hasTradeData ? chalk.white(stats.shortTrades + (shortWinRate !== 'N/A' ? ' (' + shortWinRate + '%)' : '')) : chalk.gray('N/A'), col2) + chalk.cyan('\u2551'));
|
|
346
|
+
console.log(chalk.cyan('\u2551') + fmtRow('OPEN ORDERS:', chalk.white(String(totalOpenOrders)), col1) + chalk.cyan('\u2502') + fmtRow('VOLUME:', hasTradeData ? chalk.white(stats.totalVolume + ' CONTRACTS') : chalk.gray('N/A'), col2) + chalk.cyan('\u2551'));
|
|
347
347
|
|
|
348
348
|
// ========== P&L METRICS ==========
|
|
349
349
|
draw2ColSeparator(boxWidth);
|
|
@@ -361,11 +361,11 @@ const showStats = async (service) => {
|
|
|
361
361
|
|
|
362
362
|
const netPnLStr = hasTradeData ? (netPnL >= 0 ? chalk.green('$' + netPnL.toFixed(2)) : chalk.red('-$' + Math.abs(netPnL).toFixed(2))) : chalk.gray('N/A');
|
|
363
363
|
|
|
364
|
-
console.log(chalk.cyan('\u2551') + fmtRow('
|
|
365
|
-
console.log(chalk.cyan('\u2551') + fmtRow('
|
|
366
|
-
console.log(chalk.cyan('\u2551') + fmtRow('
|
|
367
|
-
console.log(chalk.cyan('\u2551') + fmtRow('
|
|
368
|
-
console.log(chalk.cyan('\u2551') + fmtRow('
|
|
364
|
+
console.log(chalk.cyan('\u2551') + fmtRow('NET P&L:', netPnLStr, col1) + chalk.cyan('\u2502') + fmtRow('PROFIT FACTOR:', pfColor, col2) + chalk.cyan('\u2551'));
|
|
365
|
+
console.log(chalk.cyan('\u2551') + fmtRow('GROSS PROFIT:', hasTradeData ? chalk.green('$' + stats.totalWinAmount.toFixed(2)) : chalk.gray('N/A'), col1) + chalk.cyan('\u2502') + fmtRow('MAX CONSEC. WINS:', hasTradeData ? chalk.green(String(stats.maxConsecutiveWins)) : chalk.gray('N/A'), col2) + chalk.cyan('\u2551'));
|
|
366
|
+
console.log(chalk.cyan('\u2551') + fmtRow('GROSS LOSS:', hasTradeData ? chalk.red('-$' + stats.totalLossAmount.toFixed(2)) : chalk.gray('N/A'), col1) + chalk.cyan('\u2502') + fmtRow('MAX CONSEC. LOSS:', hasTradeData ? (stats.maxConsecutiveLosses > 0 ? chalk.red(String(stats.maxConsecutiveLosses)) : chalk.green('0')) : chalk.gray('N/A'), col2) + chalk.cyan('\u2551'));
|
|
367
|
+
console.log(chalk.cyan('\u2551') + fmtRow('AVG WIN:', hasTradeData ? chalk.green('$' + avgWin) : chalk.gray('N/A'), col1) + chalk.cyan('\u2502') + fmtRow('BEST TRADE:', hasTradeData ? chalk.green('$' + stats.bestTrade.toFixed(2)) : chalk.gray('N/A'), col2) + chalk.cyan('\u2551'));
|
|
368
|
+
console.log(chalk.cyan('\u2551') + fmtRow('AVG LOSS:', hasTradeData ? (stats.losingTrades > 0 ? chalk.red('-$' + avgLoss) : chalk.green('$0.00')) : chalk.gray('N/A'), col1) + chalk.cyan('\u2502') + fmtRow('WORST TRADE:', hasTradeData ? (stats.worstTrade < 0 ? chalk.red(worstTradeStr) : chalk.green(worstTradeStr)) : chalk.gray('N/A'), col2) + chalk.cyan('\u2551'));
|
|
369
369
|
|
|
370
370
|
// ========== QUANTITATIVE METRICS ==========
|
|
371
371
|
draw2ColSeparator(boxWidth);
|
|
@@ -376,10 +376,10 @@ const showStats = async (service) => {
|
|
|
376
376
|
const ddColor = maxDrawdown === 0 ? chalk.gray : maxDrawdown <= 5 ? chalk.green : maxDrawdown <= 15 ? chalk.yellow : chalk.red;
|
|
377
377
|
const rrColor = riskRewardRatio === 'N/A' ? chalk.gray : parseFloat(riskRewardRatio) >= 2 ? chalk.green : parseFloat(riskRewardRatio) >= 1 ? chalk.yellow : chalk.red;
|
|
378
378
|
|
|
379
|
-
console.log(chalk.cyan('\u2551') + fmtRow('
|
|
380
|
-
console.log(chalk.cyan('\u2551') + fmtRow('
|
|
381
|
-
console.log(chalk.cyan('\u2551') + fmtRow('
|
|
382
|
-
console.log(chalk.cyan('\u2551') + fmtRow('
|
|
379
|
+
console.log(chalk.cyan('\u2551') + fmtRow('SHARPE RATIO:', sharpeColor(sharpeRatio), col1) + chalk.cyan('\u2502') + fmtRow('RISK/REWARD:', rrColor(riskRewardRatio), col2) + chalk.cyan('\u2551'));
|
|
380
|
+
console.log(chalk.cyan('\u2551') + fmtRow('SORTINO RATIO:', sortinoColor(sortinoRatio), col1) + chalk.cyan('\u2502') + fmtRow('CALMAR RATIO:', calmarRatio === 'N/A' ? chalk.gray(calmarRatio) : chalk.white(calmarRatio), col2) + chalk.cyan('\u2551'));
|
|
381
|
+
console.log(chalk.cyan('\u2551') + fmtRow('MAX DRAWDOWN:', hasTradeData && maxDrawdown > 0 ? ddColor(maxDrawdown.toFixed(2) + '%') : chalk.gray('N/A'), col1) + chalk.cyan('\u2502') + fmtRow('EXPECTANCY:', hasTradeData ? (expectancy >= 0 ? chalk.green('$' + expectancy.toFixed(2)) : chalk.red('$' + expectancy.toFixed(2))) : chalk.gray('N/A'), col2) + chalk.cyan('\u2551'));
|
|
382
|
+
console.log(chalk.cyan('\u2551') + fmtRow('STD DEVIATION:', hasTradeData ? chalk.white('$' + stdDev.toFixed(2)) : chalk.gray('N/A'), col1) + chalk.cyan('\u2502') + fmtRow('AVG TRADE:', hasTradeData ? (avgReturn >= 0 ? chalk.green('$' + avgReturn.toFixed(2)) : chalk.red('$' + avgReturn.toFixed(2))) : chalk.gray('N/A'), col2) + chalk.cyan('\u2551'));
|
|
383
383
|
|
|
384
384
|
drawBoxFooter(boxWidth);
|
|
385
385
|
|
|
@@ -421,8 +421,8 @@ const showStats = async (service) => {
|
|
|
421
421
|
});
|
|
422
422
|
} else {
|
|
423
423
|
const msg = connectionTypes.rithmic > 0
|
|
424
|
-
? '
|
|
425
|
-
: '
|
|
424
|
+
? ' NO TRADE HISTORY (RITHMIC DOES NOT PROVIDE TRADE HISTORY API)'
|
|
425
|
+
: ' NO TRADE DATA AVAILABLE';
|
|
426
426
|
console.log(chalk.cyan('\u2551') + chalk.gray(msg) + ' '.repeat(Math.max(0, chartInnerWidth - msg.length)) + chalk.cyan('\u2551'));
|
|
427
427
|
}
|
|
428
428
|
|
|
@@ -467,13 +467,13 @@ const showStats = async (service) => {
|
|
|
467
467
|
|
|
468
468
|
// Header - build with exact spacing
|
|
469
469
|
const headerParts = [
|
|
470
|
-
' ' + '
|
|
471
|
-
'
|
|
472
|
-
'
|
|
470
|
+
' ' + 'TIME'.padEnd(colTime),
|
|
471
|
+
'SYMBOL'.padEnd(colSymbol),
|
|
472
|
+
'SIDE'.padEnd(colSide),
|
|
473
473
|
'P&L'.padEnd(colPnl),
|
|
474
|
-
'
|
|
475
|
-
'
|
|
476
|
-
'
|
|
474
|
+
'FEES'.padEnd(colFees),
|
|
475
|
+
'NET'.padEnd(colNet),
|
|
476
|
+
'ACCOUNT'.padEnd(colAccount)
|
|
477
477
|
];
|
|
478
478
|
const header = headerParts.join('| ');
|
|
479
479
|
console.log(chalk.cyan('\u2551') + chalk.white(header) + chalk.cyan('\u2551'));
|
|
@@ -540,13 +540,13 @@ const showStats = async (service) => {
|
|
|
540
540
|
}
|
|
541
541
|
|
|
542
542
|
if (sortedTrades.length === 0) {
|
|
543
|
-
const msg = '
|
|
543
|
+
const msg = ' NO COMPLETED TRADES YET';
|
|
544
544
|
console.log(chalk.cyan('\u2551') + chalk.gray(msg.padEnd(innerWidth)) + chalk.cyan('\u2551'));
|
|
545
545
|
}
|
|
546
546
|
} else {
|
|
547
547
|
const msg = connectionTypes.rithmic > 0
|
|
548
|
-
? '
|
|
549
|
-
: '
|
|
548
|
+
? ' NO TRADE HISTORY (RITHMIC API LIMITATION)'
|
|
549
|
+
: ' NO TRADE HISTORY AVAILABLE';
|
|
550
550
|
console.log(chalk.cyan('\u2551') + chalk.gray(msg.padEnd(innerWidth)) + chalk.cyan('\u2551'));
|
|
551
551
|
}
|
|
552
552
|
|
|
@@ -581,18 +581,18 @@ const showStats = async (service) => {
|
|
|
581
581
|
};
|
|
582
582
|
|
|
583
583
|
const metricsDisplay = [
|
|
584
|
-
{ name: '
|
|
585
|
-
{ name: '
|
|
586
|
-
{ name: '
|
|
587
|
-
{ name: '
|
|
588
|
-
{ name: '
|
|
589
|
-
{ name: '
|
|
584
|
+
{ name: 'WIN RATE', score: winRateScore },
|
|
585
|
+
{ name: 'PROFIT FACTOR', score: profitFactorScore },
|
|
586
|
+
{ name: 'CONSISTENCY', score: consistencyScore },
|
|
587
|
+
{ name: 'RISK MANAGEMENT', score: riskScore },
|
|
588
|
+
{ name: 'VOLUME', score: volumeScore },
|
|
589
|
+
{ name: 'RETURNS', score: returnScore }
|
|
590
590
|
];
|
|
591
591
|
|
|
592
592
|
const barWidth = 30;
|
|
593
593
|
const labelWidth = 18;
|
|
594
594
|
|
|
595
|
-
const overallLine = ` OVERALL SCORE: ${scoreColor(String(hqxScore))} / 100 [
|
|
595
|
+
const overallLine = ` OVERALL SCORE: ${scoreColor(String(hqxScore))} / 100 [GRADE: ${scoreColor(scoreGrade)}]`;
|
|
596
596
|
const overallVisLen = overallLine.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
597
597
|
console.log(chalk.cyan('\u2551') + overallLine + ' '.repeat(innerWidth - overallVisLen) + chalk.cyan('\u2551'));
|
|
598
598
|
console.log(chalk.cyan('\u2551') + chalk.gray('\u2500'.repeat(innerWidth)) + chalk.cyan('\u2551'));
|
|
@@ -613,9 +613,9 @@ const showStats = async (service) => {
|
|
|
613
613
|
|
|
614
614
|
// Show data source notice
|
|
615
615
|
if (connectionTypes.rithmic > 0 && connectionTypes.projectx === 0) {
|
|
616
|
-
console.log(chalk.gray('
|
|
616
|
+
console.log(chalk.gray(' NOTE: RITHMIC API PROVIDES BALANCE/P&L ONLY. TRADE HISTORY NOT AVAILABLE.'));
|
|
617
617
|
} else if (connectionTypes.rithmic > 0 && connectionTypes.projectx > 0) {
|
|
618
|
-
console.log(chalk.gray('
|
|
618
|
+
console.log(chalk.gray(' NOTE: TRADE HISTORY SHOWN FROM PROJECTX ACCOUNTS ONLY.'));
|
|
619
619
|
}
|
|
620
620
|
|
|
621
621
|
} catch (error) {
|
package/src/pages/user.js
CHANGED
|
@@ -71,10 +71,10 @@ const showUserInfo = async (service) => {
|
|
|
71
71
|
|
|
72
72
|
const username = userInfo.userName || userInfo.username || 'Unknown';
|
|
73
73
|
const connCount = connections.count() || 1;
|
|
74
|
-
console.log(chalk.cyan('║') + fmtRow('
|
|
74
|
+
console.log(chalk.cyan('║') + fmtRow('USERNAME:', chalk.cyan(username.toUpperCase()), col1) + chalk.cyan('│') + fmtRow('CONNECTIONS:', chalk.cyan(String(connCount)), col2) + chalk.cyan('║'));
|
|
75
75
|
|
|
76
76
|
const email = userInfo.email || 'N/A';
|
|
77
|
-
console.log(chalk.cyan('║') + fmtRow('
|
|
77
|
+
console.log(chalk.cyan('║') + fmtRow('EMAIL:', chalk.white(email), col1) + chalk.cyan('│') + fmtRow('ACCOUNTS:', chalk.cyan(String(accountCount)), col2) + chalk.cyan('║'));
|
|
78
78
|
|
|
79
79
|
const userId = userInfo.userId || userInfo.id || 'N/A';
|
|
80
80
|
const platform = service?.propfirm?.name || 'ProjectX';
|
package/src/ui/box.js
CHANGED
|
@@ -97,7 +97,7 @@ const drawBoxSeparator = (width) => {
|
|
|
97
97
|
const printLogo = () => {
|
|
98
98
|
const logoText = figlet.textSync('HEDGEQUANTX', { font: 'ANSI Shadow' });
|
|
99
99
|
console.log(chalk.cyan(logoText));
|
|
100
|
-
console.log(chalk.gray.italic('
|
|
100
|
+
console.log(chalk.gray.italic(' PROP FUTURES ALGO TRADING CLI'));
|
|
101
101
|
console.log();
|
|
102
102
|
};
|
|
103
103
|
|
package/src/ui/index.js
CHANGED
|
@@ -70,7 +70,7 @@ const displayBanner = () => {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
console.log(chalk.cyan('╠' + '═'.repeat(innerWidth) + '╣'));
|
|
73
|
-
const tagline = isMobile ? `HQX v${version}` : `
|
|
73
|
+
const tagline = isMobile ? `HQX v${version}` : `PROP FUTURES ALGO TRADING v${version}`;
|
|
74
74
|
console.log(chalk.cyan('║') + chalk.white(centerText(tagline, innerWidth)) + chalk.cyan('║'));
|
|
75
75
|
};
|
|
76
76
|
|
package/src/ui/menu.js
CHANGED
|
@@ -48,7 +48,7 @@ const createBoxMenu = async (title, items, options = {}) => {
|
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
console.log(chalk.cyan('╠' + '═'.repeat(innerWidth) + '╣'));
|
|
51
|
-
console.log(chalk.cyan('║') + chalk.white(centerText(`
|
|
51
|
+
console.log(chalk.cyan('║') + chalk.white(centerText(`PROP FUTURES ALGO TRADING v${version}`, innerWidth)) + chalk.cyan('║'));
|
|
52
52
|
|
|
53
53
|
// Stats bar if provided
|
|
54
54
|
if (options.statsLine) {
|