hedgequantx 1.8.46 → 1.8.48
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/menus/dashboard.js +40 -16
- package/src/pages/accounts.js +10 -26
- package/src/pages/stats.js +8 -27
- package/src/utils/prompts.js +15 -10
package/package.json
CHANGED
package/src/menus/dashboard.js
CHANGED
|
@@ -121,59 +121,83 @@ const handleUpdate = async () => {
|
|
|
121
121
|
currentVersion = require('../../package.json').version || 'unknown';
|
|
122
122
|
} catch (e) {}
|
|
123
123
|
|
|
124
|
+
console.log(chalk.cyan(`\n Current version: v${currentVersion}`));
|
|
124
125
|
spinner = ora({ text: 'Checking for updates...', color: 'yellow' }).start();
|
|
125
126
|
|
|
126
127
|
let latestVersion;
|
|
127
128
|
try {
|
|
128
|
-
latestVersion = execSync('npm view hedgequantx version
|
|
129
|
-
stdio: 'pipe',
|
|
129
|
+
latestVersion = execSync('npm view hedgequantx version', {
|
|
130
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
131
|
+
timeout: 30000,
|
|
132
|
+
encoding: 'utf8'
|
|
130
133
|
}).trim();
|
|
131
134
|
|
|
132
135
|
if (!latestVersion || !/^\d+\.\d+\.\d+/.test(latestVersion)) {
|
|
133
|
-
throw new Error('Invalid version');
|
|
136
|
+
throw new Error('Invalid version format');
|
|
134
137
|
}
|
|
135
138
|
} catch (e) {
|
|
136
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'));
|
|
137
142
|
await prompts.waitForEnter();
|
|
138
143
|
return;
|
|
139
144
|
}
|
|
140
145
|
|
|
146
|
+
spinner.succeed(`Latest version: v${latestVersion}`);
|
|
147
|
+
|
|
141
148
|
if (currentVersion === latestVersion) {
|
|
142
|
-
|
|
143
|
-
await
|
|
149
|
+
console.log(chalk.green(' Already up to date!'));
|
|
150
|
+
await prompts.waitForEnter();
|
|
144
151
|
return;
|
|
145
152
|
}
|
|
146
153
|
|
|
147
|
-
|
|
154
|
+
console.log(chalk.yellow(` Update available: v${currentVersion} → v${latestVersion}`));
|
|
155
|
+
spinner = ora({ text: 'Installing update...', color: 'yellow' }).start();
|
|
148
156
|
|
|
149
157
|
try {
|
|
150
|
-
|
|
151
|
-
|
|
158
|
+
// Try with sudo first on Unix systems
|
|
159
|
+
const isWindows = process.platform === 'win32';
|
|
160
|
+
const cmd = isWindows
|
|
161
|
+
? 'npm install -g hedgequantx@latest'
|
|
162
|
+
: 'npm install -g hedgequantx@latest';
|
|
163
|
+
|
|
164
|
+
execSync(cmd, {
|
|
165
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
166
|
+
timeout: 180000,
|
|
167
|
+
encoding: 'utf8'
|
|
152
168
|
});
|
|
153
169
|
} catch (e) {
|
|
154
|
-
spinner.fail('Update failed');
|
|
155
|
-
console.log(chalk.
|
|
170
|
+
spinner.fail('Update failed - permission denied?');
|
|
171
|
+
console.log(chalk.gray(` Error: ${e.message}`));
|
|
172
|
+
console.log(chalk.yellow(' Try manually with sudo:'));
|
|
173
|
+
console.log(chalk.white(' sudo npm install -g hedgequantx@latest'));
|
|
156
174
|
await prompts.waitForEnter();
|
|
157
175
|
return;
|
|
158
176
|
}
|
|
159
177
|
|
|
160
|
-
spinner.succeed(`Updated
|
|
161
|
-
console.log(chalk.cyan(' Restarting...'));
|
|
178
|
+
spinner.succeed(`Updated to v${latestVersion}!`);
|
|
179
|
+
console.log(chalk.cyan(' Restarting HQX...'));
|
|
162
180
|
|
|
163
|
-
await new Promise(r => setTimeout(r,
|
|
181
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
164
182
|
|
|
165
183
|
try {
|
|
166
|
-
const child = spawn('
|
|
184
|
+
const child = spawn('hqx', [], {
|
|
185
|
+
stdio: 'inherit',
|
|
186
|
+
detached: true,
|
|
187
|
+
shell: true
|
|
188
|
+
});
|
|
167
189
|
child.unref();
|
|
168
190
|
process.exit(0);
|
|
169
191
|
} catch (e) {
|
|
170
|
-
console.log(chalk.yellow(' Please
|
|
192
|
+
console.log(chalk.yellow('\n Please restart HQX manually:'));
|
|
193
|
+
console.log(chalk.white(' hqx'));
|
|
171
194
|
await prompts.waitForEnter();
|
|
172
195
|
}
|
|
173
196
|
|
|
174
197
|
} catch (error) {
|
|
175
198
|
if (spinner) spinner.fail('Update error');
|
|
176
|
-
console.log(chalk.
|
|
199
|
+
console.log(chalk.gray(` Error: ${error.message}`));
|
|
200
|
+
console.log(chalk.yellow(' Try manually: npm install -g hedgequantx@latest'));
|
|
177
201
|
await prompts.waitForEnter();
|
|
178
202
|
}
|
|
179
203
|
};
|
package/src/pages/accounts.js
CHANGED
|
@@ -28,8 +28,8 @@ const showAccounts = async (service) => {
|
|
|
28
28
|
let spinner;
|
|
29
29
|
|
|
30
30
|
try {
|
|
31
|
-
//
|
|
32
|
-
spinner = ora({ text: 'Loading
|
|
31
|
+
// Single spinner for loading
|
|
32
|
+
spinner = ora({ text: 'Loading accounts...', color: 'yellow' }).start();
|
|
33
33
|
|
|
34
34
|
const allConns = connections.count() > 0 ? connections.getAll() : (service ? [{ service, propfirm: service.propfirm?.name || 'Unknown', type: 'single' }] : []);
|
|
35
35
|
|
|
@@ -38,16 +38,11 @@ const showAccounts = async (service) => {
|
|
|
38
38
|
await prompts.waitForEnter();
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
|
-
|
|
42
|
-
spinner.succeed(`Found ${allConns.length} connection(s)`);
|
|
43
41
|
|
|
44
|
-
//
|
|
45
|
-
for (
|
|
46
|
-
const conn = allConns[i];
|
|
42
|
+
// Fetch accounts from each connection
|
|
43
|
+
for (const conn of allConns) {
|
|
47
44
|
const propfirmName = conn.propfirm || conn.type || 'Unknown';
|
|
48
45
|
|
|
49
|
-
spinner = ora({ text: `Fetching accounts from ${propfirmName}...`, color: 'yellow' }).start();
|
|
50
|
-
|
|
51
46
|
try {
|
|
52
47
|
const result = await conn.service.getTradingAccounts();
|
|
53
48
|
if (result.success && result.accounts && result.accounts.length > 0) {
|
|
@@ -58,29 +53,21 @@ const showAccounts = async (service) => {
|
|
|
58
53
|
service: conn.service
|
|
59
54
|
});
|
|
60
55
|
});
|
|
61
|
-
spinner.succeed(`${propfirmName}: ${result.accounts.length} account(s)`);
|
|
62
|
-
} else {
|
|
63
|
-
spinner.warn(`${propfirmName}: No accounts found`);
|
|
64
56
|
}
|
|
65
57
|
} catch (e) {
|
|
66
|
-
|
|
58
|
+
// Silent fail
|
|
67
59
|
}
|
|
68
60
|
}
|
|
69
61
|
|
|
70
|
-
// Step 3: Check if we have accounts
|
|
71
62
|
if (allAccounts.length === 0) {
|
|
72
|
-
|
|
63
|
+
spinner.fail('No accounts found');
|
|
73
64
|
await prompts.waitForEnter();
|
|
74
65
|
return;
|
|
75
66
|
}
|
|
76
67
|
|
|
77
|
-
//
|
|
78
|
-
spinner = ora({ text: 'Loading account details...', color: 'yellow' }).start();
|
|
79
|
-
|
|
80
|
-
let detailsLoaded = 0;
|
|
68
|
+
// Fetch additional data for each account
|
|
81
69
|
for (const account of allAccounts) {
|
|
82
70
|
try {
|
|
83
|
-
// Try to get fresh balance/P&L if available
|
|
84
71
|
if (account.service && typeof account.service.getAccountBalance === 'function') {
|
|
85
72
|
const balanceResult = await account.service.getAccountBalance(account.accountId);
|
|
86
73
|
if (balanceResult.success) {
|
|
@@ -88,14 +75,11 @@ const showAccounts = async (service) => {
|
|
|
88
75
|
account.profitAndLoss = balanceResult.profitAndLoss;
|
|
89
76
|
}
|
|
90
77
|
}
|
|
91
|
-
|
|
92
|
-
spinner.text = `Loading account details... (${detailsLoaded}/${allAccounts.length})`;
|
|
93
|
-
} catch (e) {
|
|
94
|
-
// Continue with existing data
|
|
95
|
-
}
|
|
78
|
+
} catch (e) {}
|
|
96
79
|
}
|
|
97
80
|
|
|
98
|
-
spinner.
|
|
81
|
+
spinner.stop();
|
|
82
|
+
console.clear();
|
|
99
83
|
console.log();
|
|
100
84
|
|
|
101
85
|
// Display accounts
|
package/src/pages/stats.js
CHANGED
|
@@ -17,8 +17,8 @@ const showStats = async (service) => {
|
|
|
17
17
|
let spinner;
|
|
18
18
|
|
|
19
19
|
try {
|
|
20
|
-
//
|
|
21
|
-
spinner = ora({ text: 'Loading
|
|
20
|
+
// Single spinner for loading
|
|
21
|
+
spinner = ora({ text: 'Loading stats...', color: 'yellow' }).start();
|
|
22
22
|
|
|
23
23
|
const allConns = connections.count() > 0
|
|
24
24
|
? connections.getAll()
|
|
@@ -29,14 +29,12 @@ const showStats = async (service) => {
|
|
|
29
29
|
await prompts.waitForEnter();
|
|
30
30
|
return;
|
|
31
31
|
}
|
|
32
|
-
spinner.succeed(`Found ${allConns.length} connection(s)`);
|
|
33
32
|
|
|
34
|
-
//
|
|
33
|
+
// Fetch accounts from each connection
|
|
35
34
|
let allAccountsData = [];
|
|
36
35
|
|
|
37
36
|
for (const conn of allConns) {
|
|
38
37
|
const propfirmName = conn.propfirm || conn.type || 'Unknown';
|
|
39
|
-
spinner = ora({ text: `Fetching accounts from ${propfirmName}...`, color: 'yellow' }).start();
|
|
40
38
|
|
|
41
39
|
try {
|
|
42
40
|
const result = await conn.service.getTradingAccounts();
|
|
@@ -48,17 +46,12 @@ const showStats = async (service) => {
|
|
|
48
46
|
service: conn.service
|
|
49
47
|
});
|
|
50
48
|
});
|
|
51
|
-
spinner.succeed(`${propfirmName}: ${result.accounts.length} account(s)`);
|
|
52
|
-
} else {
|
|
53
|
-
spinner.warn(`${propfirmName}: No accounts`);
|
|
54
49
|
}
|
|
55
|
-
} catch (e) {
|
|
56
|
-
spinner.fail(`${propfirmName}: Failed`);
|
|
57
|
-
}
|
|
50
|
+
} catch (e) {}
|
|
58
51
|
}
|
|
59
52
|
|
|
60
53
|
if (allAccountsData.length === 0) {
|
|
61
|
-
|
|
54
|
+
spinner.fail('No accounts found');
|
|
62
55
|
await prompts.waitForEnter();
|
|
63
56
|
return;
|
|
64
57
|
}
|
|
@@ -76,12 +69,12 @@ const showStats = async (service) => {
|
|
|
76
69
|
const activeAccounts = allAccountsData.filter(acc => acc.status === 0);
|
|
77
70
|
|
|
78
71
|
if (activeAccounts.length === 0) {
|
|
79
|
-
|
|
72
|
+
spinner.fail('No active accounts found');
|
|
80
73
|
await prompts.waitForEnter();
|
|
81
74
|
return;
|
|
82
75
|
}
|
|
83
76
|
|
|
84
|
-
//
|
|
77
|
+
// Collect stats for each account
|
|
85
78
|
let totalBalance = 0;
|
|
86
79
|
let totalPnL = 0;
|
|
87
80
|
let totalStartingBalance = 0;
|
|
@@ -94,9 +87,6 @@ const showStats = async (service) => {
|
|
|
94
87
|
for (let i = 0; i < activeAccounts.length; i++) {
|
|
95
88
|
const account = activeAccounts[i];
|
|
96
89
|
const svc = account.service;
|
|
97
|
-
const accName = String(account.accountId || account.accountName || `Account ${i+1}`).substring(0, 20);
|
|
98
|
-
|
|
99
|
-
spinner = ora({ text: `Loading stats for ${accName}...`, color: 'yellow' }).start();
|
|
100
90
|
|
|
101
91
|
try {
|
|
102
92
|
// Balance
|
|
@@ -118,7 +108,6 @@ const showStats = async (service) => {
|
|
|
118
108
|
}
|
|
119
109
|
|
|
120
110
|
// Positions
|
|
121
|
-
spinner.text = `Loading positions for ${accName}...`;
|
|
122
111
|
try {
|
|
123
112
|
const posResult = await svc.getPositions(account.accountId);
|
|
124
113
|
if (posResult.success && posResult.positions) {
|
|
@@ -127,7 +116,6 @@ const showStats = async (service) => {
|
|
|
127
116
|
} catch (e) {}
|
|
128
117
|
|
|
129
118
|
// Orders
|
|
130
|
-
spinner.text = `Loading orders for ${accName}...`;
|
|
131
119
|
try {
|
|
132
120
|
const ordResult = await svc.getOrders(account.accountId);
|
|
133
121
|
if (ordResult.success && ordResult.orders) {
|
|
@@ -136,7 +124,6 @@ const showStats = async (service) => {
|
|
|
136
124
|
} catch (e) {}
|
|
137
125
|
|
|
138
126
|
// Lifetime stats
|
|
139
|
-
spinner.text = `Loading lifetime stats for ${accName}...`;
|
|
140
127
|
if (typeof svc.getLifetimeStats === 'function') {
|
|
141
128
|
try {
|
|
142
129
|
const lifetimeResult = await svc.getLifetimeStats(account.accountId);
|
|
@@ -147,7 +134,6 @@ const showStats = async (service) => {
|
|
|
147
134
|
}
|
|
148
135
|
|
|
149
136
|
// Trade history
|
|
150
|
-
spinner.text = `Loading trade history for ${accName}...`;
|
|
151
137
|
if (typeof svc.getTradeHistory === 'function') {
|
|
152
138
|
try {
|
|
153
139
|
const tradesResult = await svc.getTradeHistory(account.accountId, 30);
|
|
@@ -160,15 +146,10 @@ const showStats = async (service) => {
|
|
|
160
146
|
}
|
|
161
147
|
} catch (e) {}
|
|
162
148
|
}
|
|
163
|
-
|
|
164
|
-
spinner.succeed(`${accName}: Stats loaded`);
|
|
165
|
-
} catch (e) {
|
|
166
|
-
spinner.fail(`${accName}: Failed to load stats`);
|
|
167
|
-
}
|
|
149
|
+
} catch (e) {}
|
|
168
150
|
}
|
|
169
151
|
|
|
170
152
|
// Aggregate stats
|
|
171
|
-
spinner = ora({ text: 'Calculating metrics...', color: 'yellow' }).start();
|
|
172
153
|
|
|
173
154
|
let stats = {
|
|
174
155
|
totalTrades: 0, winningTrades: 0, losingTrades: 0,
|
package/src/utils/prompts.js
CHANGED
|
@@ -46,13 +46,13 @@ const prepareStdin = () => {
|
|
|
46
46
|
* Native readline prompt
|
|
47
47
|
*/
|
|
48
48
|
const nativePrompt = (message) => {
|
|
49
|
-
return new Promise((resolve
|
|
49
|
+
return new Promise((resolve) => {
|
|
50
50
|
try {
|
|
51
51
|
prepareStdin();
|
|
52
52
|
|
|
53
53
|
// Always create a fresh readline for each prompt to avoid state issues
|
|
54
54
|
if (rl && !rl.closed) {
|
|
55
|
-
rl.close();
|
|
55
|
+
try { rl.close(); } catch (e) {}
|
|
56
56
|
rl = null;
|
|
57
57
|
}
|
|
58
58
|
|
|
@@ -62,18 +62,23 @@ const nativePrompt = (message) => {
|
|
|
62
62
|
terminal: true
|
|
63
63
|
});
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
rl.on('close', () => {
|
|
67
|
-
resolve('');
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
rl.on('error', (err) => {
|
|
71
|
-
resolve('');
|
|
72
|
-
});
|
|
65
|
+
let answered = false;
|
|
73
66
|
|
|
74
67
|
rl.question(message + ' ', (answer) => {
|
|
68
|
+
answered = true;
|
|
69
|
+
try { rl.close(); } catch (e) {}
|
|
70
|
+
rl = null;
|
|
75
71
|
resolve(answer || '');
|
|
76
72
|
});
|
|
73
|
+
|
|
74
|
+
// Handle readline close (e.g., Ctrl+C)
|
|
75
|
+
rl.on('close', () => {
|
|
76
|
+
if (!answered) {
|
|
77
|
+
rl = null;
|
|
78
|
+
resolve('');
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
77
82
|
} catch (e) {
|
|
78
83
|
resolve('');
|
|
79
84
|
}
|