hedgequantx 1.8.43 → 1.8.45
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/pages/accounts.js +118 -63
- package/src/pages/orders.js +104 -58
- package/src/pages/positions.js +105 -59
- package/src/pages/stats.js +451 -418
- package/src/pages/user.js +68 -47
package/package.json
CHANGED
package/src/pages/accounts.js
CHANGED
|
@@ -14,7 +14,6 @@ const { prompts } = require('../utils');
|
|
|
14
14
|
* Show all accounts
|
|
15
15
|
*/
|
|
16
16
|
const showAccounts = async (service) => {
|
|
17
|
-
const spinner = ora({ text: 'Fetching accounts...', color: 'yellow' }).start();
|
|
18
17
|
const boxWidth = getLogoWidth();
|
|
19
18
|
const { col1, col2 } = getColWidths(boxWidth);
|
|
20
19
|
|
|
@@ -26,80 +25,136 @@ const showAccounts = async (service) => {
|
|
|
26
25
|
};
|
|
27
26
|
|
|
28
27
|
let allAccounts = [];
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
let spinner;
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
// Step 1: Get connections
|
|
32
|
+
spinner = ora({ text: 'Loading connections...', color: 'yellow' }).start();
|
|
33
|
+
|
|
34
|
+
const allConns = connections.count() > 0 ? connections.getAll() : (service ? [{ service, propfirm: service.propfirm?.name || 'Unknown', type: 'single' }] : []);
|
|
35
|
+
|
|
36
|
+
if (allConns.length === 0) {
|
|
37
|
+
spinner.fail('No connections found');
|
|
38
|
+
await prompts.waitForEnter();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
spinner.succeed(`Found ${allConns.length} connection(s)`);
|
|
43
|
+
|
|
44
|
+
// Step 2: Fetch accounts from each connection
|
|
45
|
+
for (let i = 0; i < allConns.length; i++) {
|
|
46
|
+
const conn = allConns[i];
|
|
47
|
+
const propfirmName = conn.propfirm || conn.type || 'Unknown';
|
|
48
|
+
|
|
49
|
+
spinner = ora({ text: `Fetching accounts from ${propfirmName}...`, color: 'yellow' }).start();
|
|
50
|
+
|
|
32
51
|
try {
|
|
33
52
|
const result = await conn.service.getTradingAccounts();
|
|
34
|
-
if (result.success && result.accounts) {
|
|
53
|
+
if (result.success && result.accounts && result.accounts.length > 0) {
|
|
35
54
|
result.accounts.forEach(account => {
|
|
36
|
-
allAccounts.push({
|
|
55
|
+
allAccounts.push({
|
|
56
|
+
...account,
|
|
57
|
+
propfirm: propfirmName,
|
|
58
|
+
service: conn.service
|
|
59
|
+
});
|
|
37
60
|
});
|
|
61
|
+
spinner.succeed(`${propfirmName}: ${result.accounts.length} account(s)`);
|
|
62
|
+
} else {
|
|
63
|
+
spinner.warn(`${propfirmName}: No accounts found`);
|
|
38
64
|
}
|
|
39
|
-
} catch (e) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const result = await service.getTradingAccounts();
|
|
43
|
-
if (result.success && result.accounts) {
|
|
44
|
-
allAccounts = result.accounts.map(a => ({ ...a, service, propfirm: service.propfirm.name }));
|
|
65
|
+
} catch (e) {
|
|
66
|
+
spinner.fail(`${propfirmName}: Failed to fetch accounts`);
|
|
67
|
+
}
|
|
45
68
|
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (allAccounts.length === 0) {
|
|
49
|
-
spinner.fail('No accounts found');
|
|
50
|
-
await prompts.waitForEnter();
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
spinner.succeed(`Found ${allAccounts.length} account(s)`);
|
|
55
|
-
console.log();
|
|
56
|
-
|
|
57
|
-
drawBoxHeader('TRADING ACCOUNTS', boxWidth);
|
|
58
|
-
|
|
59
|
-
for (let i = 0; i < allAccounts.length; i += 2) {
|
|
60
|
-
const acc1 = allAccounts[i];
|
|
61
|
-
const acc2 = allAccounts[i + 1];
|
|
62
|
-
|
|
63
|
-
const name1 = String(acc1.accountId || `Account #${acc1.accountId}`);
|
|
64
|
-
const name2 = acc2 ? String(acc2.accountId || `Account #${acc2.accountId}`) : '';
|
|
65
69
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// Balance
|
|
74
|
-
const bal1 = acc1.balance;
|
|
75
|
-
const bal2 = acc2 ? acc2.balance : null;
|
|
76
|
-
const balStr1 = bal1 !== null && bal1 !== undefined ? '$' + bal1.toLocaleString() : '--';
|
|
77
|
-
const balStr2 = bal2 !== null && bal2 !== undefined ? '$' + bal2.toLocaleString() : '--';
|
|
78
|
-
const balColor1 = bal1 === null ? chalk.gray : (bal1 >= 0 ? chalk.green : chalk.red);
|
|
79
|
-
const balColor2 = bal2 === null ? chalk.gray : (bal2 >= 0 ? chalk.green : chalk.red);
|
|
80
|
-
console.log(chalk.cyan('║') + fmtRow('Balance:', balColor1(balStr1), col1) + chalk.cyan('│') + (acc2 ? fmtRow('Balance:', balColor2(balStr2), col2) : ' '.repeat(col2)) + chalk.cyan('║'));
|
|
81
|
-
|
|
82
|
-
// Status
|
|
83
|
-
const status1 = ACCOUNT_STATUS[acc1.status] || { text: 'Unknown', color: 'gray' };
|
|
84
|
-
const status2 = acc2 ? (ACCOUNT_STATUS[acc2.status] || { text: 'Unknown', color: 'gray' }) : null;
|
|
85
|
-
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('║'));
|
|
70
|
+
// Step 3: Check if we have accounts
|
|
71
|
+
if (allAccounts.length === 0) {
|
|
72
|
+
console.log(chalk.yellow('\n No accounts found across all connections.'));
|
|
73
|
+
await prompts.waitForEnter();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
86
76
|
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
77
|
+
// Step 4: Fetch additional data for each account (balance, P&L)
|
|
78
|
+
spinner = ora({ text: 'Loading account details...', color: 'yellow' }).start();
|
|
79
|
+
|
|
80
|
+
let detailsLoaded = 0;
|
|
81
|
+
for (const account of allAccounts) {
|
|
82
|
+
try {
|
|
83
|
+
// Try to get fresh balance/P&L if available
|
|
84
|
+
if (account.service && typeof account.service.getAccountBalance === 'function') {
|
|
85
|
+
const balanceResult = await account.service.getAccountBalance(account.accountId);
|
|
86
|
+
if (balanceResult.success) {
|
|
87
|
+
account.balance = balanceResult.balance;
|
|
88
|
+
account.profitAndLoss = balanceResult.profitAndLoss;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
detailsLoaded++;
|
|
92
|
+
spinner.text = `Loading account details... (${detailsLoaded}/${allAccounts.length})`;
|
|
93
|
+
} catch (e) {
|
|
94
|
+
// Continue with existing data
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
spinner.succeed(`Loaded ${allAccounts.length} account(s)`);
|
|
99
|
+
console.log();
|
|
100
|
+
|
|
101
|
+
// Display accounts
|
|
102
|
+
drawBoxHeader('TRADING ACCOUNTS', boxWidth);
|
|
103
|
+
|
|
104
|
+
for (let i = 0; i < allAccounts.length; i += 2) {
|
|
105
|
+
const acc1 = allAccounts[i];
|
|
106
|
+
const acc2 = allAccounts[i + 1];
|
|
107
|
+
|
|
108
|
+
const name1 = String(acc1.accountId || acc1.accountName || `Account #${i + 1}`);
|
|
109
|
+
const name2 = acc2 ? String(acc2.accountId || acc2.accountName || `Account #${i + 2}`) : '';
|
|
110
|
+
|
|
111
|
+
draw2ColHeader(name1.substring(0, col1 - 4), name2 ? name2.substring(0, col2 - 4) : '', boxWidth);
|
|
112
|
+
|
|
113
|
+
// PropFirm
|
|
114
|
+
const pf1 = chalk.magenta(acc1.propfirm || 'Unknown');
|
|
115
|
+
const pf2 = acc2 ? chalk.magenta(acc2.propfirm || 'Unknown') : '';
|
|
116
|
+
console.log(chalk.cyan('║') + fmtRow('PropFirm:', pf1, col1) + chalk.cyan('│') + (acc2 ? fmtRow('PropFirm:', pf2, col2) : ' '.repeat(col2)) + chalk.cyan('║'));
|
|
117
|
+
|
|
118
|
+
// Balance
|
|
119
|
+
const bal1 = acc1.balance;
|
|
120
|
+
const bal2 = acc2 ? acc2.balance : null;
|
|
121
|
+
const balStr1 = bal1 !== null && bal1 !== undefined ? '$' + Number(bal1).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) : '--';
|
|
122
|
+
const balStr2 = bal2 !== null && bal2 !== undefined ? '$' + Number(bal2).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) : '--';
|
|
123
|
+
const balColor1 = bal1 === null || bal1 === undefined ? chalk.gray : (bal1 >= 0 ? chalk.green : chalk.red);
|
|
124
|
+
const balColor2 = bal2 === null || bal2 === undefined ? chalk.gray : (bal2 >= 0 ? chalk.green : chalk.red);
|
|
125
|
+
console.log(chalk.cyan('║') + fmtRow('Balance:', balColor1(balStr1), col1) + chalk.cyan('│') + (acc2 ? fmtRow('Balance:', balColor2(balStr2), col2) : ' '.repeat(col2)) + chalk.cyan('║'));
|
|
126
|
+
|
|
127
|
+
// P&L
|
|
128
|
+
const pnl1 = acc1.profitAndLoss;
|
|
129
|
+
const pnl2 = acc2 ? acc2.profitAndLoss : null;
|
|
130
|
+
const pnlStr1 = pnl1 !== null && pnl1 !== undefined ? (pnl1 >= 0 ? '+' : '') + '$' + Number(pnl1).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) : '--';
|
|
131
|
+
const pnlStr2 = pnl2 !== null && pnl2 !== undefined ? (pnl2 >= 0 ? '+' : '') + '$' + Number(pnl2).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) : '--';
|
|
132
|
+
const pnlColor1 = pnl1 === null || pnl1 === undefined ? chalk.gray : (pnl1 >= 0 ? chalk.green : chalk.red);
|
|
133
|
+
const pnlColor2 = pnl2 === null || pnl2 === undefined ? chalk.gray : (pnl2 >= 0 ? chalk.green : chalk.red);
|
|
134
|
+
console.log(chalk.cyan('║') + fmtRow('P&L:', pnlColor1(pnlStr1), col1) + chalk.cyan('│') + (acc2 ? fmtRow('P&L:', pnlColor2(pnlStr2), col2) : ' '.repeat(col2)) + chalk.cyan('║'));
|
|
135
|
+
|
|
136
|
+
// Status
|
|
137
|
+
const status1 = ACCOUNT_STATUS[acc1.status] || { text: 'Unknown', color: 'gray' };
|
|
138
|
+
const status2 = acc2 ? (ACCOUNT_STATUS[acc2.status] || { text: 'Unknown', color: 'gray' }) : null;
|
|
139
|
+
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('║'));
|
|
140
|
+
|
|
141
|
+
// Type
|
|
142
|
+
const type1 = ACCOUNT_TYPE[acc1.type] || { text: 'Unknown', color: 'white' };
|
|
143
|
+
const type2 = acc2 ? (ACCOUNT_TYPE[acc2.type] || { text: 'Unknown', color: 'white' }) : null;
|
|
144
|
+
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('║'));
|
|
145
|
+
|
|
146
|
+
if (i + 2 < allAccounts.length) {
|
|
147
|
+
console.log(chalk.cyan('╠') + chalk.cyan('═'.repeat(col1)) + chalk.cyan('╪') + chalk.cyan('═'.repeat(col2)) + chalk.cyan('╣'));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
91
150
|
|
|
92
|
-
|
|
93
|
-
console.log(
|
|
151
|
+
drawBoxFooter(boxWidth);
|
|
152
|
+
console.log();
|
|
94
153
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
154
|
+
} catch (error) {
|
|
155
|
+
if (spinner) spinner.fail('Error loading accounts: ' + error.message);
|
|
98
156
|
}
|
|
99
157
|
|
|
100
|
-
drawBoxFooter(boxWidth);
|
|
101
|
-
console.log();
|
|
102
|
-
|
|
103
158
|
await prompts.waitForEnter();
|
|
104
159
|
};
|
|
105
160
|
|
package/src/pages/orders.js
CHANGED
|
@@ -14,78 +14,124 @@ const { prompts } = require('../utils');
|
|
|
14
14
|
* Show all orders
|
|
15
15
|
*/
|
|
16
16
|
const showOrders = async (service) => {
|
|
17
|
-
const spinner = ora({ text: 'Fetching orders...', color: 'yellow' }).start();
|
|
18
17
|
const boxWidth = getLogoWidth();
|
|
18
|
+
let spinner;
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
try {
|
|
21
|
+
// Step 1: Get connections
|
|
22
|
+
spinner = ora({ text: 'Loading connections...', color: 'yellow' }).start();
|
|
23
|
+
|
|
24
|
+
const allConns = connections.count() > 0
|
|
25
|
+
? connections.getAll()
|
|
26
|
+
: (service ? [{ service, propfirm: service.propfirm?.name || 'Unknown', type: 'single' }] : []);
|
|
27
|
+
|
|
28
|
+
if (allConns.length === 0) {
|
|
29
|
+
spinner.fail('No connections found');
|
|
30
|
+
await prompts.waitForEnter();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
spinner.succeed(`Found ${allConns.length} connection(s)`);
|
|
34
|
+
|
|
35
|
+
// Step 2: Fetch accounts
|
|
36
|
+
let allAccounts = [];
|
|
37
|
+
|
|
38
|
+
for (const conn of allConns) {
|
|
39
|
+
const propfirmName = conn.propfirm || conn.type || 'Unknown';
|
|
40
|
+
spinner = ora({ text: `Fetching accounts from ${propfirmName}...`, color: 'yellow' }).start();
|
|
41
|
+
|
|
24
42
|
try {
|
|
25
43
|
const result = await conn.service.getTradingAccounts();
|
|
26
|
-
if (result.success && result.accounts) {
|
|
44
|
+
if (result.success && result.accounts && result.accounts.length > 0) {
|
|
27
45
|
result.accounts.forEach(account => {
|
|
28
|
-
allAccounts.push({
|
|
46
|
+
allAccounts.push({
|
|
47
|
+
...account,
|
|
48
|
+
propfirm: propfirmName,
|
|
49
|
+
service: conn.service
|
|
50
|
+
});
|
|
29
51
|
});
|
|
52
|
+
spinner.succeed(`${propfirmName}: ${result.accounts.length} account(s)`);
|
|
53
|
+
} else {
|
|
54
|
+
spinner.warn(`${propfirmName}: No accounts`);
|
|
30
55
|
}
|
|
31
|
-
} catch (e) {
|
|
56
|
+
} catch (e) {
|
|
57
|
+
spinner.fail(`${propfirmName}: Failed`);
|
|
58
|
+
}
|
|
32
59
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
60
|
+
|
|
61
|
+
if (allAccounts.length === 0) {
|
|
62
|
+
console.log(chalk.yellow('\n No accounts found.'));
|
|
63
|
+
await prompts.waitForEnter();
|
|
64
|
+
return;
|
|
37
65
|
}
|
|
38
|
-
}
|
|
39
66
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
67
|
+
// Step 3: Fetch orders for each account
|
|
68
|
+
let allOrders = [];
|
|
69
|
+
|
|
70
|
+
for (const account of allAccounts) {
|
|
71
|
+
const accName = String(account.accountId || account.accountName || 'Unknown').substring(0, 20);
|
|
72
|
+
spinner = ora({ text: `Fetching orders for ${accName}...`, color: 'yellow' }).start();
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const result = await account.service.getOrders(account.accountId);
|
|
76
|
+
if (result.success && result.orders && result.orders.length > 0) {
|
|
77
|
+
result.orders.forEach(order => {
|
|
78
|
+
allOrders.push({
|
|
79
|
+
...order,
|
|
80
|
+
accountName: account.accountId,
|
|
81
|
+
propfirm: account.propfirm
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
spinner.succeed(`${accName}: ${result.orders.length} order(s)`);
|
|
85
|
+
} else {
|
|
86
|
+
spinner.succeed(`${accName}: No orders`);
|
|
87
|
+
}
|
|
88
|
+
} catch (e) {
|
|
89
|
+
spinner.fail(`${accName}: Failed to fetch orders`);
|
|
49
90
|
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
91
|
+
}
|
|
52
92
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
93
|
+
spinner = ora({ text: 'Preparing display...', color: 'yellow' }).start();
|
|
94
|
+
spinner.succeed(`Total: ${allOrders.length} order(s)`);
|
|
95
|
+
console.log();
|
|
96
|
+
|
|
97
|
+
// Display
|
|
98
|
+
drawBoxHeader('ORDERS', boxWidth);
|
|
99
|
+
|
|
100
|
+
if (allOrders.length === 0) {
|
|
101
|
+
drawBoxRow(chalk.gray(' No orders found'), boxWidth);
|
|
102
|
+
} else {
|
|
103
|
+
const header = ' ' + 'Symbol'.padEnd(12) + 'Side'.padEnd(6) + 'Type'.padEnd(8) + 'Qty'.padEnd(6) + 'Price'.padEnd(10) + 'Status'.padEnd(12) + 'Account';
|
|
104
|
+
drawBoxRow(chalk.white.bold(header), boxWidth);
|
|
105
|
+
drawBoxSeparator(boxWidth);
|
|
106
|
+
|
|
107
|
+
for (const order of allOrders) {
|
|
108
|
+
const symbol = String(order.contractId || order.symbol || 'Unknown').substring(0, 11);
|
|
109
|
+
const sideInfo = ORDER_SIDE[order.side] || { text: '?', color: 'white' };
|
|
110
|
+
const type = ORDER_TYPE[order.type] || 'Unknown';
|
|
111
|
+
const qty = order.size || order.quantity || 0;
|
|
112
|
+
const price = order.limitPrice || order.price || 0;
|
|
113
|
+
const statusInfo = ORDER_STATUS[order.status] || { text: 'Unknown', color: 'gray', icon: '[?]' };
|
|
114
|
+
const account = String(order.accountName || 'Unknown').substring(0, 12);
|
|
115
|
+
|
|
116
|
+
const row = ' ' +
|
|
117
|
+
chalk.white(symbol.padEnd(12)) +
|
|
118
|
+
chalk[sideInfo.color](sideInfo.text.substring(0, 4).padEnd(6)) +
|
|
119
|
+
chalk.white(String(type).substring(0, 7).padEnd(8)) +
|
|
120
|
+
chalk.white(String(qty).padEnd(6)) +
|
|
121
|
+
chalk.white((price > 0 ? price.toFixed(2) : 'MKT').padEnd(10)) +
|
|
122
|
+
chalk[statusInfo.color]((statusInfo.icon + ' ' + statusInfo.text).substring(0, 11).padEnd(12)) +
|
|
123
|
+
chalk.gray(account);
|
|
124
|
+
|
|
125
|
+
drawBoxRow(row, boxWidth);
|
|
126
|
+
}
|
|
84
127
|
}
|
|
85
|
-
}
|
|
86
128
|
|
|
87
|
-
|
|
88
|
-
|
|
129
|
+
drawBoxFooter(boxWidth);
|
|
130
|
+
console.log();
|
|
131
|
+
|
|
132
|
+
} catch (error) {
|
|
133
|
+
if (spinner) spinner.fail('Error: ' + error.message);
|
|
134
|
+
}
|
|
89
135
|
|
|
90
136
|
await prompts.waitForEnter();
|
|
91
137
|
};
|
package/src/pages/positions.js
CHANGED
|
@@ -14,79 +14,125 @@ const { prompts } = require('../utils');
|
|
|
14
14
|
* Show all open positions
|
|
15
15
|
*/
|
|
16
16
|
const showPositions = async (service) => {
|
|
17
|
-
const spinner = ora({ text: 'Fetching positions...', color: 'yellow' }).start();
|
|
18
17
|
const boxWidth = getLogoWidth();
|
|
18
|
+
let spinner;
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
try {
|
|
21
|
+
// Step 1: Get connections
|
|
22
|
+
spinner = ora({ text: 'Loading connections...', color: 'yellow' }).start();
|
|
23
|
+
|
|
24
|
+
const allConns = connections.count() > 0
|
|
25
|
+
? connections.getAll()
|
|
26
|
+
: (service ? [{ service, propfirm: service.propfirm?.name || 'Unknown', type: 'single' }] : []);
|
|
27
|
+
|
|
28
|
+
if (allConns.length === 0) {
|
|
29
|
+
spinner.fail('No connections found');
|
|
30
|
+
await prompts.waitForEnter();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
spinner.succeed(`Found ${allConns.length} connection(s)`);
|
|
34
|
+
|
|
35
|
+
// Step 2: Fetch accounts
|
|
36
|
+
let allAccounts = [];
|
|
37
|
+
|
|
38
|
+
for (const conn of allConns) {
|
|
39
|
+
const propfirmName = conn.propfirm || conn.type || 'Unknown';
|
|
40
|
+
spinner = ora({ text: `Fetching accounts from ${propfirmName}...`, color: 'yellow' }).start();
|
|
41
|
+
|
|
24
42
|
try {
|
|
25
43
|
const result = await conn.service.getTradingAccounts();
|
|
26
|
-
if (result.success && result.accounts) {
|
|
44
|
+
if (result.success && result.accounts && result.accounts.length > 0) {
|
|
27
45
|
result.accounts.forEach(account => {
|
|
28
|
-
allAccounts.push({
|
|
46
|
+
allAccounts.push({
|
|
47
|
+
...account,
|
|
48
|
+
propfirm: propfirmName,
|
|
49
|
+
service: conn.service
|
|
50
|
+
});
|
|
29
51
|
});
|
|
52
|
+
spinner.succeed(`${propfirmName}: ${result.accounts.length} account(s)`);
|
|
53
|
+
} else {
|
|
54
|
+
spinner.warn(`${propfirmName}: No accounts`);
|
|
30
55
|
}
|
|
31
|
-
} catch (e) {
|
|
56
|
+
} catch (e) {
|
|
57
|
+
spinner.fail(`${propfirmName}: Failed`);
|
|
58
|
+
}
|
|
32
59
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
60
|
+
|
|
61
|
+
if (allAccounts.length === 0) {
|
|
62
|
+
console.log(chalk.yellow('\n No accounts found.'));
|
|
63
|
+
await prompts.waitForEnter();
|
|
64
|
+
return;
|
|
37
65
|
}
|
|
38
|
-
}
|
|
39
66
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
67
|
+
// Step 3: Fetch positions for each account
|
|
68
|
+
let allPositions = [];
|
|
69
|
+
|
|
70
|
+
for (const account of allAccounts) {
|
|
71
|
+
const accName = String(account.accountId || account.accountName || 'Unknown').substring(0, 20);
|
|
72
|
+
spinner = ora({ text: `Fetching positions for ${accName}...`, color: 'yellow' }).start();
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const result = await account.service.getPositions(account.accountId);
|
|
76
|
+
if (result.success && result.positions && result.positions.length > 0) {
|
|
77
|
+
result.positions.forEach(pos => {
|
|
78
|
+
allPositions.push({
|
|
79
|
+
...pos,
|
|
80
|
+
accountName: account.accountId,
|
|
81
|
+
propfirm: account.propfirm
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
spinner.succeed(`${accName}: ${result.positions.length} position(s)`);
|
|
85
|
+
} else {
|
|
86
|
+
spinner.succeed(`${accName}: No positions`);
|
|
87
|
+
}
|
|
88
|
+
} catch (e) {
|
|
89
|
+
spinner.fail(`${accName}: Failed to fetch positions`);
|
|
49
90
|
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
91
|
+
}
|
|
52
92
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
93
|
+
spinner = ora({ text: 'Preparing display...', color: 'yellow' }).start();
|
|
94
|
+
spinner.succeed(`Total: ${allPositions.length} position(s)`);
|
|
95
|
+
console.log();
|
|
96
|
+
|
|
97
|
+
// Display
|
|
98
|
+
drawBoxHeader('OPEN POSITIONS', boxWidth);
|
|
99
|
+
|
|
100
|
+
if (allPositions.length === 0) {
|
|
101
|
+
drawBoxRow(chalk.gray(' No open positions'), boxWidth);
|
|
102
|
+
} else {
|
|
103
|
+
const header = ' ' + 'Symbol'.padEnd(15) + 'Side'.padEnd(8) + 'Size'.padEnd(8) + 'Entry'.padEnd(12) + 'P&L'.padEnd(12) + 'Account';
|
|
104
|
+
drawBoxRow(chalk.white.bold(header), boxWidth);
|
|
105
|
+
drawBoxSeparator(boxWidth);
|
|
106
|
+
|
|
107
|
+
for (const pos of allPositions) {
|
|
108
|
+
const symbol = String(pos.contractId || pos.symbol || 'Unknown').substring(0, 14);
|
|
109
|
+
const sideInfo = ORDER_SIDE[pos.side] || { text: 'Unknown', color: 'white' };
|
|
110
|
+
const size = Math.abs(pos.size || pos.quantity || 0);
|
|
111
|
+
const entry = pos.averagePrice || pos.entryPrice || 0;
|
|
112
|
+
const pnl = pos.profitAndLoss || pos.unrealizedPnl || 0;
|
|
113
|
+
const account = String(pos.accountName || 'Unknown').substring(0, 15);
|
|
114
|
+
|
|
115
|
+
const pnlColor = pnl >= 0 ? chalk.green : chalk.red;
|
|
116
|
+
const pnlStr = (pnl >= 0 ? '+' : '') + '$' + pnl.toFixed(2);
|
|
117
|
+
|
|
118
|
+
const row = ' ' +
|
|
119
|
+
chalk.white(symbol.padEnd(15)) +
|
|
120
|
+
chalk[sideInfo.color](sideInfo.text.padEnd(8)) +
|
|
121
|
+
chalk.white(String(size).padEnd(8)) +
|
|
122
|
+
chalk.white(entry.toFixed(2).padEnd(12)) +
|
|
123
|
+
pnlColor(pnlStr.padEnd(12)) +
|
|
124
|
+
chalk.gray(account);
|
|
125
|
+
|
|
126
|
+
drawBoxRow(row, boxWidth);
|
|
127
|
+
}
|
|
85
128
|
}
|
|
86
|
-
}
|
|
87
129
|
|
|
88
|
-
|
|
89
|
-
|
|
130
|
+
drawBoxFooter(boxWidth);
|
|
131
|
+
console.log();
|
|
132
|
+
|
|
133
|
+
} catch (error) {
|
|
134
|
+
if (spinner) spinner.fail('Error: ' + error.message);
|
|
135
|
+
}
|
|
90
136
|
|
|
91
137
|
await prompts.waitForEnter();
|
|
92
138
|
};
|