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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "1.8.46",
3
+ "version": "1.8.48",
4
4
  "description": "Prop Futures Algo Trading CLI - Connect to Topstep, Alpha Futures, and other prop firms",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -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 2>/dev/null', {
129
- stdio: 'pipe', timeout: 15000, encoding: 'utf8'
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
- spinner.succeed(`Already up to date! (v${currentVersion})`);
143
- await new Promise(r => setTimeout(r, 2000));
149
+ console.log(chalk.green(' Already up to date!'));
150
+ await prompts.waitForEnter();
144
151
  return;
145
152
  }
146
153
 
147
- spinner.text = `Updating v${currentVersion} → v${latestVersion}...`;
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
- execSync('npm install -g hedgequantx@latest 2>/dev/null', {
151
- stdio: 'pipe', timeout: 120000, encoding: 'utf8'
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.yellow(' Try: npm install -g hedgequantx@latest'));
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: v${currentVersion} v${latestVersion}`);
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, 2000));
181
+ await new Promise(r => setTimeout(r, 1500));
164
182
 
165
183
  try {
166
- const child = spawn('hedgequantx', [], { stdio: 'inherit', detached: true, shell: true });
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 run: hedgequantx'));
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.yellow(' Try: npm install -g hedgequantx@latest'));
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
  };
@@ -28,8 +28,8 @@ const showAccounts = async (service) => {
28
28
  let spinner;
29
29
 
30
30
  try {
31
- // Step 1: Get connections
32
- spinner = ora({ text: 'Loading connections...', color: 'yellow' }).start();
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
- // Step 2: Fetch accounts from each connection
45
- for (let i = 0; i < allConns.length; i++) {
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
- spinner.fail(`${propfirmName}: Failed to fetch accounts`);
58
+ // Silent fail
67
59
  }
68
60
  }
69
61
 
70
- // Step 3: Check if we have accounts
71
62
  if (allAccounts.length === 0) {
72
- console.log(chalk.yellow('\n No accounts found across all connections.'));
63
+ spinner.fail('No accounts found');
73
64
  await prompts.waitForEnter();
74
65
  return;
75
66
  }
76
67
 
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;
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
- detailsLoaded++;
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.succeed(`Loaded ${allAccounts.length} account(s)`);
81
+ spinner.stop();
82
+ console.clear();
99
83
  console.log();
100
84
 
101
85
  // Display accounts
@@ -17,8 +17,8 @@ const showStats = async (service) => {
17
17
  let spinner;
18
18
 
19
19
  try {
20
- // Step 1: Get connections
21
- spinner = ora({ text: 'Loading connections...', color: 'yellow' }).start();
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
- // Step 2: Fetch accounts from each connection
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
- console.log(chalk.yellow('\n No accounts found.'));
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
- console.log(chalk.yellow('\n No active accounts found.'));
72
+ spinner.fail('No active accounts found');
80
73
  await prompts.waitForEnter();
81
74
  return;
82
75
  }
83
76
 
84
- // Step 3: Collect stats for each account
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,
@@ -46,13 +46,13 @@ const prepareStdin = () => {
46
46
  * Native readline prompt
47
47
  */
48
48
  const nativePrompt = (message) => {
49
- return new Promise((resolve, reject) => {
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
- // Handle readline close/error
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
  }