hedgequantx 2.6.162 → 2.6.163
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/services/ai/providers/direct-providers.js +323 -0
- package/src/services/ai/providers/index.js +8 -472
- package/src/services/ai/providers/other-providers.js +104 -0
- package/src/services/position-exit-logic.js +174 -0
- package/src/services/position-manager.js +18 -108
- package/src/services/rithmic/contracts.js +218 -0
- package/src/services/rithmic/index.js +5 -179
- package/src/services/rithmic/market-data-decoders.js +229 -0
- package/src/services/rithmic/market-data.js +1 -278
- package/src/services/rithmic/orders-fast.js +246 -0
- package/src/services/rithmic/orders.js +1 -251
- package/src/services/rithmic/proto-decoders.js +403 -0
- package/src/services/rithmic/protobuf.js +7 -443
- package/src/services/strategy/hft-signal-calc.js +147 -0
- package/src/services/strategy/hft-tick.js +33 -133
- package/src/services/tradovate/index.js +6 -119
- package/src/services/tradovate/orders.js +145 -0
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
const EventEmitter = require('events');
|
|
22
22
|
const { logger } = require('../../utils/logger');
|
|
23
|
+
const { calculateSignal, buildSignal } = require('./hft-signal-calc');
|
|
23
24
|
|
|
24
25
|
const log = logger.scope('HFT');
|
|
25
26
|
|
|
@@ -306,23 +307,49 @@ class HFTTickStrategy extends EventEmitter {
|
|
|
306
307
|
* Check for trading signal
|
|
307
308
|
*/
|
|
308
309
|
_checkSignal(tick) {
|
|
309
|
-
// Need minimum data
|
|
310
310
|
if (this.tickCount < 50) return;
|
|
311
311
|
|
|
312
|
-
// Cooldown check
|
|
313
312
|
const now = Date.now();
|
|
314
313
|
if (now - this.lastSignalTime < this.cooldownMs) return;
|
|
315
314
|
|
|
316
|
-
|
|
317
|
-
|
|
315
|
+
const state = {
|
|
316
|
+
ofiValue: this.ofiValue,
|
|
317
|
+
zscore: this.zscore,
|
|
318
|
+
momentum: this.momentum,
|
|
319
|
+
buyVolume: this.buyVolume,
|
|
320
|
+
sellVolume: this.sellVolume,
|
|
321
|
+
cumulativeDelta: this.cumulativeDelta,
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
const config = {
|
|
325
|
+
ofiThreshold: this.ofiThreshold,
|
|
326
|
+
zscoreThreshold: this.zscoreThreshold,
|
|
327
|
+
momentumThreshold: this.momentumThreshold,
|
|
328
|
+
minConfidence: this.minConfidence,
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const { direction, confidence, scores } = calculateSignal(state, config);
|
|
318
332
|
|
|
319
333
|
if (direction === 'none' || confidence < this.minConfidence) return;
|
|
320
334
|
|
|
321
|
-
// Generate signal
|
|
322
335
|
this.signalCount++;
|
|
323
336
|
this.lastSignalTime = now;
|
|
324
337
|
|
|
325
|
-
const
|
|
338
|
+
const signalState = {
|
|
339
|
+
...state,
|
|
340
|
+
std: this.std,
|
|
341
|
+
tickCount: this.tickCount,
|
|
342
|
+
signalCount: this.signalCount,
|
|
343
|
+
contractId: this.contractId,
|
|
344
|
+
tickSize: this.tickSize,
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
const signalConfig = {
|
|
348
|
+
baseStopTicks: this.baseStopTicks,
|
|
349
|
+
baseTargetTicks: this.baseTargetTicks,
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
const signal = buildSignal(direction, confidence, scores, tick, signalState, signalConfig);
|
|
326
353
|
|
|
327
354
|
log.info(`SIGNAL #${this.signalCount}: ${direction.toUpperCase()} @ ${tick.price} | ` +
|
|
328
355
|
`OFI=${this.ofiValue.toFixed(2)} Z=${this.zscore.toFixed(2)} Mom=${this.momentum.toFixed(1)} | ` +
|
|
@@ -331,133 +358,6 @@ class HFTTickStrategy extends EventEmitter {
|
|
|
331
358
|
this.emit('signal', signal);
|
|
332
359
|
}
|
|
333
360
|
|
|
334
|
-
/**
|
|
335
|
-
* Calculate trading signal from all models
|
|
336
|
-
*/
|
|
337
|
-
_calculateSignal() {
|
|
338
|
-
let direction = 'none';
|
|
339
|
-
let confidence = 0;
|
|
340
|
-
|
|
341
|
-
const scores = {
|
|
342
|
-
ofi: 0,
|
|
343
|
-
zscore: 0,
|
|
344
|
-
momentum: 0,
|
|
345
|
-
delta: 0,
|
|
346
|
-
composite: 0,
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
// === MODEL 1: OFI ===
|
|
350
|
-
const absOfi = Math.abs(this.ofiValue);
|
|
351
|
-
if (absOfi > this.ofiThreshold) {
|
|
352
|
-
scores.ofi = Math.min(1.0, absOfi / 0.6);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// === MODEL 2: Z-Score Mean Reversion ===
|
|
356
|
-
const absZ = Math.abs(this.zscore);
|
|
357
|
-
if (absZ > this.zscoreThreshold) {
|
|
358
|
-
scores.zscore = Math.min(1.0, absZ / 3.0);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// === MODEL 3: Momentum ===
|
|
362
|
-
const absMom = Math.abs(this.momentum);
|
|
363
|
-
if (absMom > this.momentumThreshold) {
|
|
364
|
-
scores.momentum = Math.min(1.0, absMom / 3.0);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// === MODEL 4: Delta ===
|
|
368
|
-
const totalVol = this.buyVolume + this.sellVolume;
|
|
369
|
-
if (totalVol > 0) {
|
|
370
|
-
const deltaRatio = this.cumulativeDelta / totalVol;
|
|
371
|
-
scores.delta = Math.min(1.0, Math.abs(deltaRatio) * 2);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// === COMPOSITE SCORE ===
|
|
375
|
-
scores.composite =
|
|
376
|
-
scores.ofi * 0.35 + // OFI: 35%
|
|
377
|
-
scores.zscore * 0.25 + // Z-Score: 25%
|
|
378
|
-
scores.momentum * 0.20 + // Momentum: 20%
|
|
379
|
-
scores.delta * 0.20; // Delta: 20%
|
|
380
|
-
|
|
381
|
-
confidence = scores.composite;
|
|
382
|
-
|
|
383
|
-
// === DETERMINE DIRECTION ===
|
|
384
|
-
// Mean reversion: go opposite to z-score
|
|
385
|
-
// Momentum: confirm with OFI and delta
|
|
386
|
-
|
|
387
|
-
if (scores.composite >= this.minConfidence) {
|
|
388
|
-
// Primary: Mean reversion
|
|
389
|
-
if (absZ > this.zscoreThreshold) {
|
|
390
|
-
direction = this.zscore > 0 ? 'short' : 'long';
|
|
391
|
-
|
|
392
|
-
// Confirm with OFI
|
|
393
|
-
const ofiConfirms =
|
|
394
|
-
(direction === 'long' && this.ofiValue > 0) ||
|
|
395
|
-
(direction === 'short' && this.ofiValue < 0);
|
|
396
|
-
|
|
397
|
-
if (ofiConfirms) {
|
|
398
|
-
confidence += 0.1;
|
|
399
|
-
} else if (Math.abs(this.ofiValue) > 0.2) {
|
|
400
|
-
// OFI contradicts - reduce confidence
|
|
401
|
-
confidence -= 0.15;
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
// Fallback: Momentum breakout
|
|
405
|
-
else if (absMom > this.momentumThreshold * 2 && absOfi > this.ofiThreshold) {
|
|
406
|
-
direction = this.momentum > 0 ? 'long' : 'short';
|
|
407
|
-
// Must be confirmed by OFI
|
|
408
|
-
if ((direction === 'long' && this.ofiValue < 0) ||
|
|
409
|
-
(direction === 'short' && this.ofiValue > 0)) {
|
|
410
|
-
direction = 'none';
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
confidence = Math.min(1.0, Math.max(0, confidence));
|
|
416
|
-
|
|
417
|
-
return { direction, confidence, scores };
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
/**
|
|
421
|
-
* Build signal object
|
|
422
|
-
*/
|
|
423
|
-
_buildSignal(direction, confidence, scores, tick) {
|
|
424
|
-
const entry = tick.price;
|
|
425
|
-
const isLong = direction === 'long';
|
|
426
|
-
|
|
427
|
-
// Adaptive stops based on volatility (std dev)
|
|
428
|
-
const volMult = Math.max(0.5, Math.min(2.0, this.std / this.tickSize / 4));
|
|
429
|
-
const stopTicks = Math.round(this.baseStopTicks * volMult);
|
|
430
|
-
const targetTicks = Math.round(this.baseTargetTicks * volMult);
|
|
431
|
-
|
|
432
|
-
const stopLoss = isLong
|
|
433
|
-
? entry - stopTicks * this.tickSize
|
|
434
|
-
: entry + stopTicks * this.tickSize;
|
|
435
|
-
|
|
436
|
-
const takeProfit = isLong
|
|
437
|
-
? entry + targetTicks * this.tickSize
|
|
438
|
-
: entry - targetTicks * this.tickSize;
|
|
439
|
-
|
|
440
|
-
return {
|
|
441
|
-
id: `hft-${Date.now()}-${this.signalCount}`,
|
|
442
|
-
timestamp: Date.now(),
|
|
443
|
-
contractId: this.contractId,
|
|
444
|
-
direction,
|
|
445
|
-
side: isLong ? 0 : 1,
|
|
446
|
-
entry,
|
|
447
|
-
stopLoss,
|
|
448
|
-
takeProfit,
|
|
449
|
-
confidence,
|
|
450
|
-
scores,
|
|
451
|
-
// Model values for logging
|
|
452
|
-
ofi: this.ofiValue,
|
|
453
|
-
zscore: this.zscore,
|
|
454
|
-
momentum: this.momentum,
|
|
455
|
-
delta: this.cumulativeDelta,
|
|
456
|
-
// Tick count
|
|
457
|
-
tickCount: this.tickCount,
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
|
|
461
361
|
/**
|
|
462
362
|
* Get current model values for monitoring
|
|
463
363
|
*/
|
|
@@ -13,6 +13,7 @@ const { TIMEOUTS } = require('../../config/settings');
|
|
|
13
13
|
const { TRADOVATE_URLS, API_PATHS, getBaseUrl, getTradingWebSocketUrl } = require('./constants');
|
|
14
14
|
const { checkMarketHours, isDST } = require('./market');
|
|
15
15
|
const { connectWebSocket, wsSend, disconnectWebSocket } = require('./websocket');
|
|
16
|
+
const { getOrders, placeOrder, cancelOrder, closePosition, getOrderHistory } = require('./orders');
|
|
16
17
|
const { logger } = require('../../utils/logger');
|
|
17
18
|
|
|
18
19
|
const log = logger.scope('Tradovate');
|
|
@@ -252,112 +253,10 @@ class TradovateService extends EventEmitter {
|
|
|
252
253
|
|
|
253
254
|
// ==================== ORDERS ====================
|
|
254
255
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
*/
|
|
260
|
-
async getOrders(accountId) {
|
|
261
|
-
try {
|
|
262
|
-
const result = await this._request(API_PATHS.ORDER_LIST, 'GET');
|
|
263
|
-
const orders = Array.isArray(result.data) ? result.data : [];
|
|
264
|
-
|
|
265
|
-
const filtered = accountId
|
|
266
|
-
? orders.filter(o => o.accountId === accountId)
|
|
267
|
-
: orders;
|
|
268
|
-
|
|
269
|
-
return {
|
|
270
|
-
success: true,
|
|
271
|
-
orders: filtered.map(o => ({
|
|
272
|
-
orderId: o.id,
|
|
273
|
-
accountId: o.accountId,
|
|
274
|
-
symbol: o.contractId,
|
|
275
|
-
side: o.action === 'Buy' ? 0 : 1,
|
|
276
|
-
quantity: o.orderQty,
|
|
277
|
-
filledQuantity: o.filledQty || 0,
|
|
278
|
-
price: o.price,
|
|
279
|
-
status: o.ordStatus === 'Working' ? 1 : (o.ordStatus === 'Filled' ? 2 : 0),
|
|
280
|
-
orderType: o.orderType,
|
|
281
|
-
})),
|
|
282
|
-
};
|
|
283
|
-
} catch (err) {
|
|
284
|
-
return { success: false, error: err.message, orders: [] };
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* Place an order
|
|
290
|
-
* @param {Object} orderData - Order details
|
|
291
|
-
* @returns {Promise<{success: boolean, orderId?: number, error?: string}>}
|
|
292
|
-
*/
|
|
293
|
-
async placeOrder(orderData) {
|
|
294
|
-
try {
|
|
295
|
-
const result = await this._request(API_PATHS.ORDER_PLACE, 'POST', {
|
|
296
|
-
accountId: orderData.accountId,
|
|
297
|
-
action: orderData.side === 0 ? 'Buy' : 'Sell',
|
|
298
|
-
symbol: orderData.symbol,
|
|
299
|
-
orderQty: orderData.size,
|
|
300
|
-
orderType: orderData.type === 2 ? 'Market' : 'Limit',
|
|
301
|
-
price: orderData.price,
|
|
302
|
-
isAutomated: true,
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
if (result.data.errorText || result.data.failureReason) {
|
|
306
|
-
return { success: false, error: result.data.errorText || result.data.failureText };
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
log.info('Order placed', { orderId: result.data.orderId });
|
|
310
|
-
return { success: true, orderId: result.data.orderId };
|
|
311
|
-
} catch (err) {
|
|
312
|
-
return { success: false, error: err.message };
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Cancel an order
|
|
318
|
-
* @param {number} orderId - Order ID
|
|
319
|
-
* @returns {Promise<{success: boolean, error?: string}>}
|
|
320
|
-
*/
|
|
321
|
-
async cancelOrder(orderId) {
|
|
322
|
-
try {
|
|
323
|
-
const result = await this._request(API_PATHS.ORDER_CANCEL, 'POST', {
|
|
324
|
-
orderId,
|
|
325
|
-
isAutomated: true,
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
if (result.data.errorText) {
|
|
329
|
-
return { success: false, error: result.data.errorText };
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
return { success: true };
|
|
333
|
-
} catch (err) {
|
|
334
|
-
return { success: false, error: err.message };
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Close a position
|
|
340
|
-
* @param {number} accountId - Account ID
|
|
341
|
-
* @param {number} contractId - Contract ID
|
|
342
|
-
* @returns {Promise<{success: boolean, error?: string}>}
|
|
343
|
-
*/
|
|
344
|
-
async closePosition(accountId, contractId) {
|
|
345
|
-
try {
|
|
346
|
-
const result = await this._request(API_PATHS.ORDER_LIQUIDATE_POSITION, 'POST', {
|
|
347
|
-
accountId,
|
|
348
|
-
contractId,
|
|
349
|
-
isAutomated: true,
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
if (result.data.errorText) {
|
|
353
|
-
return { success: false, error: result.data.errorText };
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
return { success: true };
|
|
357
|
-
} catch (err) {
|
|
358
|
-
return { success: false, error: err.message };
|
|
359
|
-
}
|
|
360
|
-
}
|
|
256
|
+
async getOrders(accountId) { return getOrders(this, accountId); }
|
|
257
|
+
async placeOrder(orderData) { return placeOrder(this, orderData); }
|
|
258
|
+
async cancelOrder(orderId) { return cancelOrder(this, orderId); }
|
|
259
|
+
async closePosition(accountId, contractId) { return closePosition(this, accountId, contractId); }
|
|
361
260
|
|
|
362
261
|
// ==================== TRADES ====================
|
|
363
262
|
|
|
@@ -404,19 +303,7 @@ class TradovateService extends EventEmitter {
|
|
|
404
303
|
}
|
|
405
304
|
}
|
|
406
305
|
|
|
407
|
-
|
|
408
|
-
* Get order history
|
|
409
|
-
* @param {number} [days=30] - Days of history
|
|
410
|
-
* @returns {Promise<{success: boolean, orders: Array, error?: string}>}
|
|
411
|
-
*/
|
|
412
|
-
async getOrderHistory(days = 30) {
|
|
413
|
-
try {
|
|
414
|
-
const result = await this._request(API_PATHS.ORDER_LIST, 'GET');
|
|
415
|
-
return { success: true, orders: result.data || [] };
|
|
416
|
-
} catch (err) {
|
|
417
|
-
return { success: false, error: err.message, orders: [] };
|
|
418
|
-
}
|
|
419
|
-
}
|
|
306
|
+
async getOrderHistory(days = 30) { return getOrderHistory(this, days); }
|
|
420
307
|
|
|
421
308
|
// ==================== CONTRACTS ====================
|
|
422
309
|
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tradovate Orders Module
|
|
3
|
+
* @module services/tradovate/orders
|
|
4
|
+
*
|
|
5
|
+
* Order placement, cancellation, and queries
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { API_PATHS } = require('./constants');
|
|
9
|
+
const { logger } = require('../../utils/logger');
|
|
10
|
+
|
|
11
|
+
const log = logger.scope('TradovateOrders');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get orders
|
|
15
|
+
* @param {TradovateService} service
|
|
16
|
+
* @param {number} [accountId]
|
|
17
|
+
* @returns {Promise<{success: boolean, orders: Array, error?: string}>}
|
|
18
|
+
*/
|
|
19
|
+
async function getOrders(service, accountId) {
|
|
20
|
+
try {
|
|
21
|
+
const result = await service._request(API_PATHS.ORDER_LIST, 'GET');
|
|
22
|
+
const orders = Array.isArray(result.data) ? result.data : [];
|
|
23
|
+
|
|
24
|
+
const filtered = accountId
|
|
25
|
+
? orders.filter(o => o.accountId === accountId)
|
|
26
|
+
: orders;
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
success: true,
|
|
30
|
+
orders: filtered.map(o => ({
|
|
31
|
+
orderId: o.id,
|
|
32
|
+
accountId: o.accountId,
|
|
33
|
+
symbol: o.contractId,
|
|
34
|
+
side: o.action === 'Buy' ? 0 : 1,
|
|
35
|
+
quantity: o.orderQty,
|
|
36
|
+
filledQuantity: o.filledQty || 0,
|
|
37
|
+
price: o.price,
|
|
38
|
+
status: o.ordStatus === 'Working' ? 1 : (o.ordStatus === 'Filled' ? 2 : 0),
|
|
39
|
+
orderType: o.orderType,
|
|
40
|
+
})),
|
|
41
|
+
};
|
|
42
|
+
} catch (err) {
|
|
43
|
+
return { success: false, error: err.message, orders: [] };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Place an order
|
|
49
|
+
* @param {TradovateService} service
|
|
50
|
+
* @param {Object} orderData
|
|
51
|
+
* @returns {Promise<{success: boolean, orderId?: number, error?: string}>}
|
|
52
|
+
*/
|
|
53
|
+
async function placeOrder(service, orderData) {
|
|
54
|
+
try {
|
|
55
|
+
const result = await service._request(API_PATHS.ORDER_PLACE, 'POST', {
|
|
56
|
+
accountId: orderData.accountId,
|
|
57
|
+
action: orderData.side === 0 ? 'Buy' : 'Sell',
|
|
58
|
+
symbol: orderData.symbol,
|
|
59
|
+
orderQty: orderData.size,
|
|
60
|
+
orderType: orderData.type === 2 ? 'Market' : 'Limit',
|
|
61
|
+
price: orderData.price,
|
|
62
|
+
isAutomated: true,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (result.data.errorText || result.data.failureReason) {
|
|
66
|
+
return { success: false, error: result.data.errorText || result.data.failureText };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
log.info('Order placed', { orderId: result.data.orderId });
|
|
70
|
+
return { success: true, orderId: result.data.orderId };
|
|
71
|
+
} catch (err) {
|
|
72
|
+
return { success: false, error: err.message };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Cancel an order
|
|
78
|
+
* @param {TradovateService} service
|
|
79
|
+
* @param {number} orderId
|
|
80
|
+
* @returns {Promise<{success: boolean, error?: string}>}
|
|
81
|
+
*/
|
|
82
|
+
async function cancelOrder(service, orderId) {
|
|
83
|
+
try {
|
|
84
|
+
const result = await service._request(API_PATHS.ORDER_CANCEL, 'POST', {
|
|
85
|
+
orderId,
|
|
86
|
+
isAutomated: true,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
if (result.data.errorText) {
|
|
90
|
+
return { success: false, error: result.data.errorText };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { success: true };
|
|
94
|
+
} catch (err) {
|
|
95
|
+
return { success: false, error: err.message };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Close a position
|
|
101
|
+
* @param {TradovateService} service
|
|
102
|
+
* @param {number} accountId
|
|
103
|
+
* @param {number} contractId
|
|
104
|
+
* @returns {Promise<{success: boolean, error?: string}>}
|
|
105
|
+
*/
|
|
106
|
+
async function closePosition(service, accountId, contractId) {
|
|
107
|
+
try {
|
|
108
|
+
const result = await service._request(API_PATHS.ORDER_LIQUIDATE_POSITION, 'POST', {
|
|
109
|
+
accountId,
|
|
110
|
+
contractId,
|
|
111
|
+
isAutomated: true,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
if (result.data.errorText) {
|
|
115
|
+
return { success: false, error: result.data.errorText };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return { success: true };
|
|
119
|
+
} catch (err) {
|
|
120
|
+
return { success: false, error: err.message };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get order history
|
|
126
|
+
* @param {TradovateService} service
|
|
127
|
+
* @param {number} [days=30]
|
|
128
|
+
* @returns {Promise<{success: boolean, orders: Array, error?: string}>}
|
|
129
|
+
*/
|
|
130
|
+
async function getOrderHistory(service, days = 30) {
|
|
131
|
+
try {
|
|
132
|
+
const result = await service._request(API_PATHS.ORDER_LIST, 'GET');
|
|
133
|
+
return { success: true, orders: result.data || [] };
|
|
134
|
+
} catch (err) {
|
|
135
|
+
return { success: false, error: err.message, orders: [] };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
module.exports = {
|
|
140
|
+
getOrders,
|
|
141
|
+
placeOrder,
|
|
142
|
+
cancelOrder,
|
|
143
|
+
closePosition,
|
|
144
|
+
getOrderHistory,
|
|
145
|
+
};
|