hedgequantx 1.3.8 → 1.3.10

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.3.8",
3
+ "version": "1.3.10",
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": {
@@ -19,15 +19,16 @@ const { AlgoUI, checkMarketStatus } = require('./ui');
19
19
  const oneAccountMenu = async (service) => {
20
20
  const spinner = ora('Fetching active accounts...').start();
21
21
 
22
- const result = await service.getTradingAccounts();
22
+ // Get ALL accounts from ALL connections
23
+ const allAccounts = await connections.getAllAccounts();
23
24
 
24
- if (!result.success || !result.accounts?.length) {
25
+ if (!allAccounts?.length) {
25
26
  spinner.fail('No accounts found');
26
27
  await inquirer.prompt([{ type: 'input', name: 'c', message: 'Press Enter...' }]);
27
28
  return;
28
29
  }
29
30
 
30
- const activeAccounts = result.accounts.filter(acc => acc.status === 0);
31
+ const activeAccounts = allAccounts.filter(acc => acc.status === 0);
31
32
 
32
33
  if (!activeAccounts.length) {
33
34
  spinner.fail('No active accounts');
@@ -37,14 +38,16 @@ const oneAccountMenu = async (service) => {
37
38
 
38
39
  spinner.succeed(`Found ${activeAccounts.length} active account(s)`);
39
40
 
40
- // Select account
41
+ // Select account - show propfirm for clarity
41
42
  const { selectedAccount } = await inquirer.prompt([{
42
43
  type: 'list',
43
44
  name: 'selectedAccount',
44
45
  message: 'Select Account:',
45
46
  choices: [
46
47
  ...activeAccounts.map(acc => ({
47
- name: chalk.cyan(`${acc.accountName || acc.accountId} - $${acc.balance.toLocaleString()}`),
48
+ name: chalk.cyan(`${acc.accountName || acc.accountId}`) +
49
+ chalk.gray(` (${acc.propfirm || 'Unknown'})`) +
50
+ chalk.white(` - $${(acc.balance || 0).toLocaleString()}`),
48
51
  value: acc
49
52
  })),
50
53
  new inquirer.Separator(),
@@ -54,16 +57,19 @@ const oneAccountMenu = async (service) => {
54
57
 
55
58
  if (selectedAccount === 'back') return;
56
59
 
60
+ // Find the service for this account
61
+ const accountService = connections.getServiceForAccount(selectedAccount.accountId) || service;
62
+
57
63
  // Select symbol
58
- const contract = await selectSymbol(service, selectedAccount);
64
+ const contract = await selectSymbol(accountService, selectedAccount);
59
65
  if (!contract) return;
60
66
 
61
67
  // Configure algo
62
68
  const config = await configureAlgo(selectedAccount, contract);
63
69
  if (!config) return;
64
70
 
65
- // Launch
66
- await launchAlgo(service, selectedAccount, contract, config);
71
+ // Launch with the correct service for this account
72
+ await launchAlgo(accountService, selectedAccount, contract, config);
67
73
  };
68
74
 
69
75
  /**
@@ -211,7 +211,10 @@ class ProjectXService {
211
211
 
212
212
  // Get P&L for active accounts only
213
213
  if (account.status === 0) {
214
- // Get unrealized P&L from /Position endpoint (userApi)
214
+ let openPnL = 0;
215
+ let todayPnL = 0;
216
+
217
+ // 1. Get unrealized P&L from open positions
215
218
  try {
216
219
  const posRes = await this._request(
217
220
  this.propfirm.userApi,
@@ -221,18 +224,49 @@ class ProjectXService {
221
224
  debug(`Positions for ${account.accountId}:`, JSON.stringify(posRes.data, null, 2));
222
225
 
223
226
  if (posRes.statusCode === 200 && Array.isArray(posRes.data)) {
224
- let openPnL = 0;
225
227
  for (const pos of posRes.data) {
226
228
  if (pos.profitAndLoss !== undefined && pos.profitAndLoss !== null) {
227
229
  openPnL += pos.profitAndLoss;
228
230
  }
229
231
  }
230
- enriched.openPnL = openPnL;
231
- enriched.profitAndLoss = openPnL; // Open P&L from positions
232
232
  }
233
233
  } catch (e) {
234
234
  debug('Failed to get positions:', e.message);
235
235
  }
236
+
237
+ // 2. Get realized P&L from today's closed trades
238
+ try {
239
+ const today = new Date();
240
+ today.setHours(0, 0, 0, 0);
241
+ const now = new Date();
242
+
243
+ const tradesRes = await this._request(
244
+ this.propfirm.gatewayApi,
245
+ '/api/Trade/search',
246
+ 'POST',
247
+ {
248
+ accountId: account.accountId,
249
+ startTimestamp: today.toISOString(),
250
+ endTimestamp: now.toISOString()
251
+ }
252
+ );
253
+ debug(`Today trades for ${account.accountId}:`, JSON.stringify(tradesRes.data, null, 2));
254
+
255
+ if (tradesRes.statusCode === 200) {
256
+ const trades = Array.isArray(tradesRes.data) ? tradesRes.data : (tradesRes.data.trades || []);
257
+ for (const trade of trades) {
258
+ if (trade.profitAndLoss !== undefined && trade.profitAndLoss !== null) {
259
+ todayPnL += trade.profitAndLoss;
260
+ }
261
+ }
262
+ }
263
+ } catch (e) {
264
+ debug('Failed to get today trades:', e.message);
265
+ }
266
+
267
+ enriched.openPnL = openPnL;
268
+ enriched.todayPnL = todayPnL;
269
+ enriched.profitAndLoss = openPnL + todayPnL; // Total day P&L = unrealized + realized
236
270
  }
237
271
 
238
272
  debug(`Account ${account.accountId}:`, {
@@ -261,6 +261,30 @@ const connections = {
261
261
  return allAccounts;
262
262
  },
263
263
 
264
+ /**
265
+ * Gets the service for a specific account
266
+ * @param {string|number} accountId - Account ID to find
267
+ * @returns {Object|null} Service instance or null
268
+ */
269
+ getServiceForAccount(accountId) {
270
+ for (const conn of this.services) {
271
+ try {
272
+ // Check if this service has this account
273
+ if (conn.service && conn.service.accounts) {
274
+ const found = conn.service.accounts.find(acc =>
275
+ acc.accountId == accountId ||
276
+ acc.rithmicAccountId == accountId ||
277
+ acc.accountName == accountId
278
+ );
279
+ if (found) return conn.service;
280
+ }
281
+ } catch (e) {
282
+ // Skip
283
+ }
284
+ }
285
+ return null;
286
+ },
287
+
264
288
  /**
265
289
  * Checks if any connection is active
266
290
  * @returns {boolean} True if connected