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
|
@@ -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
|
-
|
|
22
|
+
// Get ALL accounts from ALL connections
|
|
23
|
+
const allAccounts = await connections.getAllAccounts();
|
|
23
24
|
|
|
24
|
-
if (!
|
|
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 =
|
|
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}
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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}:`, {
|
package/src/services/session.js
CHANGED
|
@@ -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
|