hedgequantx 2.6.161 → 2.6.162
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/ai-agent-connect.js +181 -0
- package/src/menus/ai-agent-models.js +219 -0
- package/src/menus/ai-agent-oauth.js +292 -0
- package/src/menus/ai-agent-ui.js +141 -0
- package/src/menus/ai-agent.js +88 -1489
- package/src/pages/algo/copy-engine.js +449 -0
- package/src/pages/algo/copy-trading.js +11 -543
- package/src/pages/algo/smart-logs-data.js +218 -0
- package/src/pages/algo/smart-logs.js +9 -214
- package/src/pages/algo/ui-constants.js +144 -0
- package/src/pages/algo/ui-summary.js +184 -0
- package/src/pages/algo/ui.js +42 -526
- package/src/pages/stats-calculations.js +191 -0
- package/src/pages/stats-ui.js +381 -0
- package/src/pages/stats.js +14 -507
- package/src/services/ai/client-analysis.js +194 -0
- package/src/services/ai/client-models.js +333 -0
- package/src/services/ai/client.js +6 -489
- package/src/services/ai/index.js +2 -257
- package/src/services/ai/proxy-install.js +249 -0
- package/src/services/ai/proxy-manager.js +29 -411
- package/src/services/ai/proxy-remote.js +161 -0
- package/src/services/ai/supervisor-optimize.js +215 -0
- package/src/services/ai/supervisor-sync.js +178 -0
- package/src/services/ai/supervisor.js +50 -515
- package/src/services/ai/validation.js +250 -0
- package/src/services/hqx-server-events.js +110 -0
- package/src/services/hqx-server-handlers.js +217 -0
- package/src/services/hqx-server-latency.js +136 -0
- package/src/services/hqx-server.js +51 -403
- package/src/services/position-constants.js +28 -0
- package/src/services/position-manager.js +105 -554
- package/src/services/position-momentum.js +206 -0
- package/src/services/projectx/accounts.js +142 -0
- package/src/services/projectx/index.js +40 -289
- package/src/services/projectx/trading.js +180 -0
- package/src/services/rithmic/handlers.js +2 -208
- package/src/services/rithmic/index.js +32 -542
- package/src/services/rithmic/latency-tracker.js +182 -0
- package/src/services/rithmic/specs.js +146 -0
- package/src/services/rithmic/trade-history.js +254 -0
|
@@ -20,6 +20,8 @@ const {
|
|
|
20
20
|
const { getMarketHolidays, checkHoliday, checkMarketHours } = require('./market');
|
|
21
21
|
const { calculateLifetimeStats, calculateDailyPnL, formatTrades } = require('./stats');
|
|
22
22
|
const { logger } = require('../../utils/logger');
|
|
23
|
+
const trading = require('./trading');
|
|
24
|
+
const accounts = require('./accounts');
|
|
23
25
|
|
|
24
26
|
const log = logger.scope('ProjectX');
|
|
25
27
|
|
|
@@ -203,322 +205,71 @@ class ProjectXService {
|
|
|
203
205
|
|
|
204
206
|
/**
|
|
205
207
|
* Get trading accounts with REAL P&L from API
|
|
206
|
-
*
|
|
207
|
-
* Data sources:
|
|
208
|
-
* - /TradingAccount: accountId, accountName, balance, status, type
|
|
209
|
-
* - /AccountTemplate/userTemplates: startingBalance
|
|
210
|
-
* - /Position?accountId=X: profitAndLoss (unrealized P&L)
|
|
211
|
-
*
|
|
212
208
|
* @returns {Promise<{success: boolean, accounts: Array, error?: string}>}
|
|
213
209
|
*/
|
|
214
210
|
async getTradingAccounts() {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if (response.statusCode !== 200) {
|
|
219
|
-
return { success: false, accounts: [], error: 'Failed to get accounts' };
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const accounts = Array.isArray(response.data) ? response.data : [];
|
|
223
|
-
|
|
224
|
-
// Get account templates for startingBalance
|
|
225
|
-
let templates = [];
|
|
226
|
-
try {
|
|
227
|
-
const templateRes = await this._request(this.propfirm.userApi, '/AccountTemplate/userTemplates', 'GET');
|
|
228
|
-
if (templateRes.statusCode === 200 && Array.isArray(templateRes.data)) {
|
|
229
|
-
templates = templateRes.data;
|
|
230
|
-
}
|
|
231
|
-
} catch {
|
|
232
|
-
log.debug('Failed to get templates');
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
const enrichedAccounts = await Promise.all(
|
|
236
|
-
accounts.map(account => this._enrichAccount(account, templates))
|
|
237
|
-
);
|
|
238
|
-
|
|
239
|
-
return { success: true, accounts: enrichedAccounts };
|
|
240
|
-
} catch (err) {
|
|
241
|
-
log.error('Failed to get accounts', { error: err.message });
|
|
242
|
-
return { success: false, accounts: [], error: err.message };
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Enrich account with P&L data
|
|
248
|
-
* @private
|
|
249
|
-
*/
|
|
250
|
-
async _enrichAccount(account, templates) {
|
|
251
|
-
const template = templates.find(t =>
|
|
252
|
-
account.accountName && (
|
|
253
|
-
account.accountName.includes(t.title) ||
|
|
254
|
-
t.title.includes(account.accountName)
|
|
255
|
-
)
|
|
211
|
+
return accounts.getTradingAccounts(
|
|
212
|
+
(host, path, method, data) => this._request(host, path, method, data),
|
|
213
|
+
this.propfirm
|
|
256
214
|
);
|
|
257
|
-
|
|
258
|
-
const enriched = {
|
|
259
|
-
accountId: account.accountId,
|
|
260
|
-
accountName: account.accountName,
|
|
261
|
-
balance: account.balance,
|
|
262
|
-
status: account.status,
|
|
263
|
-
type: account.type,
|
|
264
|
-
startingBalance: template?.startingBalance || null,
|
|
265
|
-
platform: 'ProjectX',
|
|
266
|
-
propfirm: this.propfirm.name,
|
|
267
|
-
openPnL: null,
|
|
268
|
-
todayPnL: null,
|
|
269
|
-
profitAndLoss: null,
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
// Only fetch P&L for active accounts
|
|
273
|
-
if (account.status !== 0) {
|
|
274
|
-
return enriched;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// Get unrealized P&L from open positions
|
|
278
|
-
let openPnL = 0;
|
|
279
|
-
try {
|
|
280
|
-
const posRes = await this._request(
|
|
281
|
-
this.propfirm.userApi,
|
|
282
|
-
`/Position?accountId=${account.accountId}`,
|
|
283
|
-
'GET'
|
|
284
|
-
);
|
|
285
|
-
|
|
286
|
-
if (posRes.statusCode === 200 && Array.isArray(posRes.data)) {
|
|
287
|
-
for (const pos of posRes.data) {
|
|
288
|
-
if (pos.profitAndLoss != null) {
|
|
289
|
-
openPnL += pos.profitAndLoss;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
} catch {
|
|
294
|
-
log.debug('Failed to get positions', { accountId: account.accountId });
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Get realized P&L from today's trades
|
|
298
|
-
let todayPnL = 0;
|
|
299
|
-
try {
|
|
300
|
-
const today = new Date();
|
|
301
|
-
today.setHours(0, 0, 0, 0);
|
|
302
|
-
|
|
303
|
-
const tradesRes = await this._request(
|
|
304
|
-
this.propfirm.gatewayApi,
|
|
305
|
-
'/api/Trade/search',
|
|
306
|
-
'POST',
|
|
307
|
-
{
|
|
308
|
-
accountId: account.accountId,
|
|
309
|
-
startTimestamp: today.toISOString(),
|
|
310
|
-
endTimestamp: new Date().toISOString(),
|
|
311
|
-
}
|
|
312
|
-
);
|
|
313
|
-
|
|
314
|
-
if (tradesRes.statusCode === 200) {
|
|
315
|
-
const trades = Array.isArray(tradesRes.data) ? tradesRes.data : (tradesRes.data.trades || []);
|
|
316
|
-
for (const trade of trades) {
|
|
317
|
-
if (trade.profitAndLoss != null) {
|
|
318
|
-
todayPnL += trade.profitAndLoss;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
} catch {
|
|
323
|
-
log.debug('Failed to get today trades', { accountId: account.accountId });
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
enriched.openPnL = openPnL;
|
|
327
|
-
enriched.todayPnL = todayPnL;
|
|
328
|
-
enriched.profitAndLoss = openPnL + todayPnL;
|
|
329
|
-
|
|
330
|
-
return enriched;
|
|
331
215
|
}
|
|
332
216
|
|
|
333
217
|
// ==================== TRADING ====================
|
|
334
218
|
|
|
335
|
-
/**
|
|
336
|
-
* Get open positions
|
|
337
|
-
* @param {number|string} accountId - Account ID
|
|
338
|
-
* @returns {Promise<{success: boolean, positions: Array, error?: string}>}
|
|
339
|
-
*/
|
|
219
|
+
/** Get open positions */
|
|
340
220
|
async getPositions(accountId) {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
'/api/Position/searchOpen',
|
|
346
|
-
'POST',
|
|
347
|
-
{ accountId: id }
|
|
348
|
-
);
|
|
349
|
-
|
|
350
|
-
if (response.statusCode === 200) {
|
|
351
|
-
const positions = response.data.positions || response.data || [];
|
|
352
|
-
return { success: true, positions: Array.isArray(positions) ? positions : [] };
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
return { success: true, positions: [] };
|
|
356
|
-
} catch (err) {
|
|
357
|
-
return { success: true, positions: [], error: err.message };
|
|
358
|
-
}
|
|
221
|
+
return trading.getPositions(
|
|
222
|
+
(host, path, method, data) => this._request(host, path, method, data),
|
|
223
|
+
this.propfirm.gatewayApi, accountId
|
|
224
|
+
);
|
|
359
225
|
}
|
|
360
226
|
|
|
361
|
-
/**
|
|
362
|
-
* Get open orders
|
|
363
|
-
* @param {number|string} accountId - Account ID
|
|
364
|
-
* @returns {Promise<{success: boolean, orders: Array, error?: string}>}
|
|
365
|
-
*/
|
|
227
|
+
/** Get open orders */
|
|
366
228
|
async getOrders(accountId) {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
'/api/Order/searchOpen',
|
|
372
|
-
'POST',
|
|
373
|
-
{ accountId: id }
|
|
374
|
-
);
|
|
375
|
-
|
|
376
|
-
if (response.statusCode === 200) {
|
|
377
|
-
const orders = response.data.orders || response.data || [];
|
|
378
|
-
return { success: true, orders: Array.isArray(orders) ? orders : [] };
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
return { success: true, orders: [] };
|
|
382
|
-
} catch (err) {
|
|
383
|
-
return { success: true, orders: [], error: err.message };
|
|
384
|
-
}
|
|
229
|
+
return trading.getOrders(
|
|
230
|
+
(host, path, method, data) => this._request(host, path, method, data),
|
|
231
|
+
this.propfirm.gatewayApi, accountId
|
|
232
|
+
);
|
|
385
233
|
}
|
|
386
234
|
|
|
387
|
-
/**
|
|
388
|
-
* Get trades history for today
|
|
389
|
-
* @param {number|string} accountId - Account ID
|
|
390
|
-
* @returns {Promise<{success: boolean, trades: Array, error?: string}>}
|
|
391
|
-
*/
|
|
235
|
+
/** Get trades for today */
|
|
392
236
|
async getTrades(accountId) {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
const now = new Date();
|
|
398
|
-
const startOfDay = new Date(now);
|
|
399
|
-
startOfDay.setUTCHours(0, 0, 0, 0);
|
|
400
|
-
|
|
401
|
-
const response = await this._request(
|
|
402
|
-
this.propfirm.gatewayApi,
|
|
403
|
-
'/api/Trade/search',
|
|
404
|
-
'POST',
|
|
405
|
-
{
|
|
406
|
-
accountId: id,
|
|
407
|
-
startTimestamp: startOfDay.toISOString(),
|
|
408
|
-
endTimestamp: now.toISOString()
|
|
409
|
-
}
|
|
410
|
-
);
|
|
411
|
-
|
|
412
|
-
if (response.statusCode === 200) {
|
|
413
|
-
const trades = response.data.trades || response.data || [];
|
|
414
|
-
return { success: true, trades: Array.isArray(trades) ? trades : [] };
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
return { success: true, trades: [] };
|
|
418
|
-
} catch (err) {
|
|
419
|
-
return { success: true, trades: [], error: err.message };
|
|
420
|
-
}
|
|
237
|
+
return trading.getTrades(
|
|
238
|
+
(host, path, method, data) => this._request(host, path, method, data),
|
|
239
|
+
this.propfirm.gatewayApi, accountId
|
|
240
|
+
);
|
|
421
241
|
}
|
|
422
242
|
|
|
423
|
-
/**
|
|
424
|
-
* Place an order
|
|
425
|
-
* @param {Object} orderData - Order details
|
|
426
|
-
* @returns {Promise<{success: boolean, order?: Object, error?: string}>}
|
|
427
|
-
*/
|
|
243
|
+
/** Place an order */
|
|
428
244
|
async placeOrder(orderData) {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
'POST',
|
|
434
|
-
orderData,
|
|
435
|
-
'orders'
|
|
436
|
-
);
|
|
437
|
-
|
|
438
|
-
if (response.statusCode === 200 && response.data.success) {
|
|
439
|
-
log.info('Order placed', { orderId: response.data.orderId });
|
|
440
|
-
return { success: true, order: response.data };
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
return { success: false, error: response.data.errorMessage || 'Order failed' };
|
|
444
|
-
} catch (err) {
|
|
445
|
-
return { success: false, error: err.message };
|
|
446
|
-
}
|
|
245
|
+
return trading.placeOrder(
|
|
246
|
+
(host, path, method, data, limiter) => this._request(host, path, method, data, limiter),
|
|
247
|
+
this.propfirm.gatewayApi, orderData
|
|
248
|
+
);
|
|
447
249
|
}
|
|
448
250
|
|
|
449
|
-
/**
|
|
450
|
-
* Cancel an order
|
|
451
|
-
* @param {number|string} orderId - Order ID
|
|
452
|
-
* @returns {Promise<{success: boolean, error?: string}>}
|
|
453
|
-
*/
|
|
251
|
+
/** Cancel an order */
|
|
454
252
|
async cancelOrder(orderId) {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
'POST',
|
|
460
|
-
{ orderId: parseInt(orderId, 10) },
|
|
461
|
-
'orders'
|
|
462
|
-
);
|
|
463
|
-
|
|
464
|
-
return { success: response.statusCode === 200 && response.data.success };
|
|
465
|
-
} catch (err) {
|
|
466
|
-
return { success: false, error: err.message };
|
|
467
|
-
}
|
|
253
|
+
return trading.cancelOrder(
|
|
254
|
+
(host, path, method, data, limiter) => this._request(host, path, method, data, limiter),
|
|
255
|
+
this.propfirm.gatewayApi, orderId
|
|
256
|
+
);
|
|
468
257
|
}
|
|
469
258
|
|
|
470
|
-
/**
|
|
471
|
-
* Cancel all open orders for an account
|
|
472
|
-
* @param {number|string} accountId - Account ID
|
|
473
|
-
* @returns {Promise<{success: boolean, cancelled: number, error?: string}>}
|
|
474
|
-
*/
|
|
259
|
+
/** Cancel all open orders */
|
|
475
260
|
async cancelAllOrders(accountId) {
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
if (!ordersResult.success || !ordersResult.orders?.length) {
|
|
481
|
-
return { success: true, cancelled: 0 };
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
const pendingOrders = ordersResult.orders.filter(o =>
|
|
485
|
-
o.status === 'Working' || o.status === 'Pending' ||
|
|
486
|
-
o.status === 0 || o.status === 1
|
|
487
|
-
);
|
|
488
|
-
|
|
489
|
-
let cancelled = 0;
|
|
490
|
-
for (const order of pendingOrders) {
|
|
491
|
-
const result = await this.cancelOrder(order.orderId || order.id);
|
|
492
|
-
if (result.success) cancelled++;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
return { success: true, cancelled };
|
|
496
|
-
} catch (err) {
|
|
497
|
-
return { success: false, cancelled: 0, error: err.message };
|
|
498
|
-
}
|
|
261
|
+
return trading.cancelAllOrders(
|
|
262
|
+
(host, path, method, data, limiter) => this._request(host, path, method, data, limiter),
|
|
263
|
+
this.propfirm.gatewayApi, accountId
|
|
264
|
+
);
|
|
499
265
|
}
|
|
500
266
|
|
|
501
|
-
/**
|
|
502
|
-
* Close a position
|
|
503
|
-
* @param {number|string} accountId - Account ID
|
|
504
|
-
* @param {number|string} contractId - Contract ID
|
|
505
|
-
* @returns {Promise<{success: boolean, error?: string}>}
|
|
506
|
-
*/
|
|
267
|
+
/** Close a position */
|
|
507
268
|
async closePosition(accountId, contractId) {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
'/api/Position/closeContract',
|
|
513
|
-
'POST',
|
|
514
|
-
{ accountId: id, contractId },
|
|
515
|
-
'orders'
|
|
516
|
-
);
|
|
517
|
-
|
|
518
|
-
return { success: response.statusCode === 200 && response.data.success };
|
|
519
|
-
} catch (err) {
|
|
520
|
-
return { success: false, error: err.message };
|
|
521
|
-
}
|
|
269
|
+
return trading.closePosition(
|
|
270
|
+
(host, path, method, data, limiter) => this._request(host, path, method, data, limiter),
|
|
271
|
+
this.propfirm.gatewayApi, accountId, contractId
|
|
272
|
+
);
|
|
522
273
|
}
|
|
523
274
|
|
|
524
275
|
// ==================== TRADES & STATS ====================
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProjectX Trading Operations
|
|
3
|
+
* @module services/projectx/trading
|
|
4
|
+
*
|
|
5
|
+
* Order management, position control, and trade history.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { validateAccountId } = require('../../security');
|
|
9
|
+
const { logger } = require('../../utils/logger');
|
|
10
|
+
|
|
11
|
+
const log = logger.scope('ProjectX');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get open positions
|
|
15
|
+
* @param {Function} request - Request function
|
|
16
|
+
* @param {string} gatewayApi - Gateway API host
|
|
17
|
+
* @param {number|string} accountId - Account ID
|
|
18
|
+
*/
|
|
19
|
+
async function getPositions(request, gatewayApi, accountId) {
|
|
20
|
+
try {
|
|
21
|
+
const id = validateAccountId(accountId);
|
|
22
|
+
const response = await request(gatewayApi, '/api/Position/searchOpen', 'POST', { accountId: id });
|
|
23
|
+
|
|
24
|
+
if (response.statusCode === 200) {
|
|
25
|
+
const positions = response.data.positions || response.data || [];
|
|
26
|
+
return { success: true, positions: Array.isArray(positions) ? positions : [] };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return { success: true, positions: [] };
|
|
30
|
+
} catch (err) {
|
|
31
|
+
return { success: true, positions: [], error: err.message };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get open orders
|
|
37
|
+
* @param {Function} request - Request function
|
|
38
|
+
* @param {string} gatewayApi - Gateway API host
|
|
39
|
+
* @param {number|string} accountId - Account ID
|
|
40
|
+
*/
|
|
41
|
+
async function getOrders(request, gatewayApi, accountId) {
|
|
42
|
+
try {
|
|
43
|
+
const id = validateAccountId(accountId);
|
|
44
|
+
const response = await request(gatewayApi, '/api/Order/searchOpen', 'POST', { accountId: id });
|
|
45
|
+
|
|
46
|
+
if (response.statusCode === 200) {
|
|
47
|
+
const orders = response.data.orders || response.data || [];
|
|
48
|
+
return { success: true, orders: Array.isArray(orders) ? orders : [] };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return { success: true, orders: [] };
|
|
52
|
+
} catch (err) {
|
|
53
|
+
return { success: true, orders: [], error: err.message };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get trades for today
|
|
59
|
+
* @param {Function} request - Request function
|
|
60
|
+
* @param {string} gatewayApi - Gateway API host
|
|
61
|
+
* @param {number|string} accountId - Account ID
|
|
62
|
+
*/
|
|
63
|
+
async function getTrades(request, gatewayApi, accountId) {
|
|
64
|
+
try {
|
|
65
|
+
const id = validateAccountId(accountId);
|
|
66
|
+
const now = new Date();
|
|
67
|
+
const startOfDay = new Date(now);
|
|
68
|
+
startOfDay.setUTCHours(0, 0, 0, 0);
|
|
69
|
+
|
|
70
|
+
const response = await request(gatewayApi, '/api/Trade/search', 'POST', {
|
|
71
|
+
accountId: id,
|
|
72
|
+
startTimestamp: startOfDay.toISOString(),
|
|
73
|
+
endTimestamp: now.toISOString()
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
if (response.statusCode === 200) {
|
|
77
|
+
const trades = response.data.trades || response.data || [];
|
|
78
|
+
return { success: true, trades: Array.isArray(trades) ? trades : [] };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return { success: true, trades: [] };
|
|
82
|
+
} catch (err) {
|
|
83
|
+
return { success: true, trades: [], error: err.message };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Place an order
|
|
89
|
+
* @param {Function} request - Request function
|
|
90
|
+
* @param {string} gatewayApi - Gateway API host
|
|
91
|
+
* @param {Object} orderData - Order details
|
|
92
|
+
*/
|
|
93
|
+
async function placeOrder(request, gatewayApi, orderData) {
|
|
94
|
+
try {
|
|
95
|
+
const response = await request(gatewayApi, '/api/Order/place', 'POST', orderData, 'orders');
|
|
96
|
+
|
|
97
|
+
if (response.statusCode === 200 && response.data.success) {
|
|
98
|
+
log.info('Order placed', { orderId: response.data.orderId });
|
|
99
|
+
return { success: true, order: response.data };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return { success: false, error: response.data.errorMessage || 'Order failed' };
|
|
103
|
+
} catch (err) {
|
|
104
|
+
return { success: false, error: err.message };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Cancel an order
|
|
110
|
+
* @param {Function} request - Request function
|
|
111
|
+
* @param {string} gatewayApi - Gateway API host
|
|
112
|
+
* @param {number|string} orderId - Order ID
|
|
113
|
+
*/
|
|
114
|
+
async function cancelOrder(request, gatewayApi, orderId) {
|
|
115
|
+
try {
|
|
116
|
+
const response = await request(gatewayApi, '/api/Order/cancel', 'POST', { orderId: parseInt(orderId, 10) }, 'orders');
|
|
117
|
+
return { success: response.statusCode === 200 && response.data.success };
|
|
118
|
+
} catch (err) {
|
|
119
|
+
return { success: false, error: err.message };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Cancel all open orders for an account
|
|
125
|
+
* @param {Function} request - Request function
|
|
126
|
+
* @param {string} gatewayApi - Gateway API host
|
|
127
|
+
* @param {number|string} accountId - Account ID
|
|
128
|
+
*/
|
|
129
|
+
async function cancelAllOrders(request, gatewayApi, accountId) {
|
|
130
|
+
try {
|
|
131
|
+
const id = validateAccountId(accountId);
|
|
132
|
+
const ordersResult = await getOrders(request, gatewayApi, id);
|
|
133
|
+
|
|
134
|
+
if (!ordersResult.success || !ordersResult.orders?.length) {
|
|
135
|
+
return { success: true, cancelled: 0 };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const pendingOrders = ordersResult.orders.filter(o =>
|
|
139
|
+
o.status === 'Working' || o.status === 'Pending' ||
|
|
140
|
+
o.status === 0 || o.status === 1
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
let cancelled = 0;
|
|
144
|
+
for (const order of pendingOrders) {
|
|
145
|
+
const result = await cancelOrder(request, gatewayApi, order.orderId || order.id);
|
|
146
|
+
if (result.success) cancelled++;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return { success: true, cancelled };
|
|
150
|
+
} catch (err) {
|
|
151
|
+
return { success: false, cancelled: 0, error: err.message };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Close a position
|
|
157
|
+
* @param {Function} request - Request function
|
|
158
|
+
* @param {string} gatewayApi - Gateway API host
|
|
159
|
+
* @param {number|string} accountId - Account ID
|
|
160
|
+
* @param {number|string} contractId - Contract ID
|
|
161
|
+
*/
|
|
162
|
+
async function closePosition(request, gatewayApi, accountId, contractId) {
|
|
163
|
+
try {
|
|
164
|
+
const id = validateAccountId(accountId);
|
|
165
|
+
const response = await request(gatewayApi, '/api/Position/closeContract', 'POST', { accountId: id, contractId }, 'orders');
|
|
166
|
+
return { success: response.statusCode === 200 && response.data.success };
|
|
167
|
+
} catch (err) {
|
|
168
|
+
return { success: false, error: err.message };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
module.exports = {
|
|
173
|
+
getPositions,
|
|
174
|
+
getOrders,
|
|
175
|
+
getTrades,
|
|
176
|
+
placeOrder,
|
|
177
|
+
cancelOrder,
|
|
178
|
+
cancelAllOrders,
|
|
179
|
+
closePosition,
|
|
180
|
+
};
|