hedgequantx 2.6.163 → 2.7.0
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/README.md +15 -88
- package/bin/cli.js +0 -11
- package/dist/lib/api.jsc +0 -0
- package/dist/lib/api2.jsc +0 -0
- package/dist/lib/core.jsc +0 -0
- package/dist/lib/core2.jsc +0 -0
- package/dist/lib/data.js +1 -1
- package/dist/lib/data.jsc +0 -0
- package/dist/lib/data2.jsc +0 -0
- package/dist/lib/decoder.jsc +0 -0
- package/dist/lib/m/mod1.jsc +0 -0
- package/dist/lib/m/mod2.jsc +0 -0
- package/dist/lib/n/r1.jsc +0 -0
- package/dist/lib/n/r2.jsc +0 -0
- package/dist/lib/n/r3.jsc +0 -0
- package/dist/lib/n/r4.jsc +0 -0
- package/dist/lib/n/r5.jsc +0 -0
- package/dist/lib/n/r6.jsc +0 -0
- package/dist/lib/n/r7.jsc +0 -0
- package/dist/lib/o/util1.jsc +0 -0
- package/dist/lib/o/util2.jsc +0 -0
- package/package.json +6 -3
- package/src/app.js +40 -162
- package/src/config/constants.js +31 -33
- package/src/config/propfirms.js +13 -217
- package/src/config/settings.js +0 -43
- package/src/lib/api.js +198 -0
- package/src/lib/api2.js +353 -0
- package/src/lib/core.js +539 -0
- package/src/lib/core2.js +341 -0
- package/src/lib/data.js +555 -0
- package/src/lib/data2.js +492 -0
- package/src/lib/decoder.js +599 -0
- package/src/lib/m/s1.js +804 -0
- package/src/lib/m/s2.js +34 -0
- package/src/lib/n/r1.js +454 -0
- package/src/lib/n/r2.js +514 -0
- package/src/lib/n/r3.js +631 -0
- package/src/lib/n/r4.js +401 -0
- package/src/lib/n/r5.js +335 -0
- package/src/lib/n/r6.js +425 -0
- package/src/lib/n/r7.js +530 -0
- package/src/lib/o/l1.js +44 -0
- package/src/lib/o/l2.js +427 -0
- package/src/lib/python-bridge.js +206 -0
- package/src/menus/connect.js +14 -176
- package/src/menus/dashboard.js +65 -110
- package/src/pages/accounts.js +18 -18
- package/src/pages/algo/copy-trading.js +210 -240
- package/src/pages/algo/index.js +41 -104
- package/src/pages/algo/one-account.js +386 -33
- package/src/pages/algo/ui.js +312 -151
- package/src/pages/orders.js +3 -3
- package/src/pages/positions.js +3 -3
- package/src/pages/stats/chart.js +74 -0
- package/src/pages/stats/display.js +228 -0
- package/src/pages/stats/index.js +236 -0
- package/src/pages/stats/metrics.js +213 -0
- package/src/pages/user.js +6 -6
- package/src/services/hqx-server/constants.js +55 -0
- package/src/services/hqx-server/index.js +401 -0
- package/src/services/hqx-server/latency.js +81 -0
- package/src/services/index.js +12 -3
- package/src/services/rithmic/accounts.js +7 -32
- package/src/services/rithmic/connection.js +1 -204
- package/src/services/rithmic/contracts.js +116 -99
- package/src/services/rithmic/handlers.js +21 -196
- package/src/services/rithmic/index.js +63 -120
- package/src/services/rithmic/market.js +31 -0
- package/src/services/rithmic/orders.js +5 -111
- package/src/services/rithmic/protobuf.js +384 -138
- package/src/services/session.js +22 -173
- package/src/ui/box.js +10 -18
- package/src/ui/index.js +1 -3
- package/src/ui/menu.js +1 -1
- package/src/utils/prompts.js +2 -2
- package/dist/lib/m/s1.js +0 -1
- package/src/menus/ai-agent-connect.js +0 -181
- package/src/menus/ai-agent-models.js +0 -219
- package/src/menus/ai-agent-oauth.js +0 -292
- package/src/menus/ai-agent-ui.js +0 -141
- package/src/menus/ai-agent.js +0 -484
- package/src/pages/algo/algo-config.js +0 -195
- package/src/pages/algo/algo-multi.js +0 -801
- package/src/pages/algo/algo-utils.js +0 -58
- package/src/pages/algo/copy-engine.js +0 -449
- package/src/pages/algo/custom-strategy.js +0 -459
- package/src/pages/algo/logger.js +0 -245
- package/src/pages/algo/smart-logs-data.js +0 -218
- package/src/pages/algo/smart-logs.js +0 -387
- package/src/pages/algo/ui-constants.js +0 -144
- package/src/pages/algo/ui-summary.js +0 -184
- package/src/pages/stats-calculations.js +0 -191
- package/src/pages/stats-ui.js +0 -381
- package/src/pages/stats.js +0 -339
- package/src/services/ai/client-analysis.js +0 -194
- package/src/services/ai/client-models.js +0 -333
- package/src/services/ai/client.js +0 -343
- package/src/services/ai/index.js +0 -384
- package/src/services/ai/oauth-anthropic.js +0 -265
- package/src/services/ai/oauth-gemini.js +0 -223
- package/src/services/ai/oauth-iflow.js +0 -269
- package/src/services/ai/oauth-openai.js +0 -233
- package/src/services/ai/oauth-qwen.js +0 -279
- package/src/services/ai/providers/direct-providers.js +0 -323
- package/src/services/ai/providers/index.js +0 -62
- package/src/services/ai/providers/other-providers.js +0 -104
- package/src/services/ai/proxy-install.js +0 -249
- package/src/services/ai/proxy-manager.js +0 -494
- package/src/services/ai/proxy-remote.js +0 -161
- package/src/services/ai/strategy-supervisor.js +0 -1312
- package/src/services/ai/supervisor-data.js +0 -195
- package/src/services/ai/supervisor-optimize.js +0 -215
- package/src/services/ai/supervisor-sync.js +0 -178
- package/src/services/ai/supervisor-utils.js +0 -158
- package/src/services/ai/supervisor.js +0 -484
- package/src/services/ai/validation.js +0 -250
- package/src/services/hqx-server-events.js +0 -110
- package/src/services/hqx-server-handlers.js +0 -217
- package/src/services/hqx-server-latency.js +0 -136
- package/src/services/hqx-server.js +0 -403
- package/src/services/position-constants.js +0 -28
- package/src/services/position-exit-logic.js +0 -174
- package/src/services/position-manager.js +0 -438
- package/src/services/position-momentum.js +0 -206
- package/src/services/projectx/accounts.js +0 -142
- package/src/services/projectx/index.js +0 -443
- package/src/services/projectx/market.js +0 -172
- package/src/services/projectx/stats.js +0 -110
- package/src/services/projectx/trading.js +0 -180
- package/src/services/rithmic/latency-tracker.js +0 -182
- package/src/services/rithmic/market-data-decoders.js +0 -229
- package/src/services/rithmic/market-data.js +0 -272
- package/src/services/rithmic/orders-fast.js +0 -246
- package/src/services/rithmic/proto-decoders.js +0 -403
- package/src/services/rithmic/specs.js +0 -146
- package/src/services/rithmic/trade-history.js +0 -254
- package/src/services/session-history.js +0 -475
- package/src/services/strategy/hft-signal-calc.js +0 -147
- package/src/services/strategy/hft-tick.js +0 -407
- package/src/services/strategy/recovery-math.js +0 -402
- package/src/services/tradovate/constants.js +0 -109
- package/src/services/tradovate/index.js +0 -392
- package/src/services/tradovate/market.js +0 -47
- package/src/services/tradovate/orders.js +0 -145
- package/src/services/tradovate/websocket.js +0 -97
|
@@ -7,20 +7,42 @@
|
|
|
7
7
|
|
|
8
8
|
const EventEmitter = require('events');
|
|
9
9
|
const { RithmicConnection } = require('./connection');
|
|
10
|
-
const { RITHMIC_ENDPOINTS, RITHMIC_SYSTEMS
|
|
11
|
-
const { createOrderHandler, createPnLHandler
|
|
12
|
-
const {
|
|
13
|
-
|
|
10
|
+
const { RITHMIC_ENDPOINTS, RITHMIC_SYSTEMS } = require('./constants');
|
|
11
|
+
const { createOrderHandler, createPnLHandler } = require('./handlers');
|
|
12
|
+
const {
|
|
13
|
+
fetchAccounts,
|
|
14
|
+
getTradingAccounts,
|
|
15
|
+
requestPnLSnapshot,
|
|
16
|
+
subscribePnLUpdates,
|
|
17
|
+
getPositions,
|
|
18
|
+
} = require('./accounts');
|
|
19
|
+
const { placeOrder, cancelOrder, getOrders, getOrderHistory, closePosition } = require('./orders');
|
|
20
|
+
const { getContracts, searchContracts } = require('./contracts');
|
|
14
21
|
const { TIMEOUTS } = require('../../config/settings');
|
|
15
22
|
const { logger } = require('../../utils/logger');
|
|
16
23
|
|
|
17
|
-
// Extracted modules
|
|
18
|
-
const { PROPFIRM_CONFIGS, checkMarketHours } = require('./specs');
|
|
19
|
-
const { getTradeHistory, setupOrderFillListener } = require('./trade-history');
|
|
20
|
-
const { getContracts, searchContracts } = require('./contracts');
|
|
21
|
-
|
|
22
24
|
const log = logger.scope('Rithmic');
|
|
23
25
|
|
|
26
|
+
/** PropFirm configurations */
|
|
27
|
+
const PROPFIRM_CONFIGS = {
|
|
28
|
+
apex: { name: 'Apex Trader Funding', systemName: 'Apex', gateway: RITHMIC_ENDPOINTS.CHICAGO },
|
|
29
|
+
apex_rithmic: { name: 'Apex Trader Funding', systemName: 'Apex', gateway: RITHMIC_ENDPOINTS.CHICAGO },
|
|
30
|
+
topstep_r: { name: 'Topstep (Rithmic)', systemName: RITHMIC_SYSTEMS.TOPSTEP, gateway: RITHMIC_ENDPOINTS.CHICAGO },
|
|
31
|
+
bulenox_r: { name: 'Bulenox (Rithmic)', systemName: RITHMIC_SYSTEMS.BULENOX, gateway: RITHMIC_ENDPOINTS.CHICAGO },
|
|
32
|
+
earn2trade: { name: 'Earn2Trade', systemName: RITHMIC_SYSTEMS.EARN_2_TRADE, gateway: RITHMIC_ENDPOINTS.CHICAGO },
|
|
33
|
+
mescapital: { name: 'MES Capital', systemName: RITHMIC_SYSTEMS.MES_CAPITAL, gateway: RITHMIC_ENDPOINTS.CHICAGO },
|
|
34
|
+
tradefundrr: { name: 'TradeFundrr', systemName: RITHMIC_SYSTEMS.TRADEFUNDRR, gateway: RITHMIC_ENDPOINTS.CHICAGO },
|
|
35
|
+
thetradingpit: { name: 'The Trading Pit', systemName: RITHMIC_SYSTEMS.THE_TRADING_PIT, gateway: RITHMIC_ENDPOINTS.CHICAGO },
|
|
36
|
+
fundedfutures: { name: 'Funded Futures Network', systemName: RITHMIC_SYSTEMS.FUNDED_FUTURES_NETWORK, gateway: RITHMIC_ENDPOINTS.CHICAGO },
|
|
37
|
+
propshop: { name: 'PropShop Trader', systemName: RITHMIC_SYSTEMS.PROPSHOP_TRADER, gateway: RITHMIC_ENDPOINTS.CHICAGO },
|
|
38
|
+
'4proptrader': { name: '4PropTrader', systemName: RITHMIC_SYSTEMS.FOUR_PROP_TRADER, gateway: RITHMIC_ENDPOINTS.CHICAGO },
|
|
39
|
+
daytraders: { name: 'DayTraders.com', systemName: RITHMIC_SYSTEMS.DAY_TRADERS, gateway: RITHMIC_ENDPOINTS.CHICAGO },
|
|
40
|
+
'10xfutures': { name: '10X Futures', systemName: RITHMIC_SYSTEMS.TEN_X_FUTURES, gateway: RITHMIC_ENDPOINTS.CHICAGO },
|
|
41
|
+
lucidtrading: { name: 'Lucid Trading', systemName: RITHMIC_SYSTEMS.LUCID_TRADING, gateway: RITHMIC_ENDPOINTS.CHICAGO },
|
|
42
|
+
thrivetrading: { name: 'Thrive Trading', systemName: RITHMIC_SYSTEMS.THRIVE_TRADING, gateway: RITHMIC_ENDPOINTS.CHICAGO },
|
|
43
|
+
legendstrading: { name: 'Legends Trading', systemName: RITHMIC_SYSTEMS.LEGENDS_TRADING, gateway: RITHMIC_ENDPOINTS.CHICAGO },
|
|
44
|
+
};
|
|
45
|
+
|
|
24
46
|
/**
|
|
25
47
|
* Rithmic Service for prop firm trading
|
|
26
48
|
*/
|
|
@@ -45,10 +67,8 @@ class RithmicService extends EventEmitter {
|
|
|
45
67
|
this.accountPnL = new Map();
|
|
46
68
|
this.positions = new Map();
|
|
47
69
|
this.orders = [];
|
|
48
|
-
this.completedTrades = [];
|
|
49
70
|
this.user = null;
|
|
50
71
|
this.credentials = null;
|
|
51
|
-
this.tradeRoutes = new Map();
|
|
52
72
|
|
|
53
73
|
// Cache
|
|
54
74
|
this._contractsCache = null;
|
|
@@ -83,15 +103,6 @@ class RithmicService extends EventEmitter {
|
|
|
83
103
|
this.loginInfo = data;
|
|
84
104
|
this.user = { userName: username, fcmId: data.fcmId, ibId: data.ibId };
|
|
85
105
|
|
|
86
|
-
// Fetch trade routes
|
|
87
|
-
try {
|
|
88
|
-
await this._fetchTradeRoutes();
|
|
89
|
-
log.debug('Fetched trade routes', { count: this.tradeRoutes.size });
|
|
90
|
-
} catch (err) {
|
|
91
|
-
log.warn('Failed to fetch trade routes', { error: err.message });
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Fetch accounts
|
|
95
106
|
try {
|
|
96
107
|
await fetchAccounts(this);
|
|
97
108
|
log.debug('Fetched accounts', { count: this.accounts.length });
|
|
@@ -99,28 +110,8 @@ class RithmicService extends EventEmitter {
|
|
|
99
110
|
log.warn('Failed to fetch accounts', { error: err.message });
|
|
100
111
|
}
|
|
101
112
|
|
|
102
|
-
// Subscribe to order updates
|
|
103
|
-
try {
|
|
104
|
-
for (const acc of this.accounts) {
|
|
105
|
-
this.orderConn.send('RequestSubscribeForOrderUpdates', {
|
|
106
|
-
templateId: REQ.ORDER_UPDATES,
|
|
107
|
-
userMsg: ['HQX'],
|
|
108
|
-
fcmId: acc.fcmId || data.fcmId,
|
|
109
|
-
ibId: acc.ibId || data.ibId,
|
|
110
|
-
accountId: acc.accountId,
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
} catch (err) {
|
|
114
|
-
log.warn('Failed to subscribe to order updates', { error: err.message });
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Setup order fill listener for P&L tracking
|
|
118
|
-
setupOrderFillListener(this);
|
|
119
|
-
|
|
120
|
-
// Store credentials for reconnection
|
|
121
113
|
this.credentials = { username, password };
|
|
122
114
|
|
|
123
|
-
// Connect to PNL_PLANT
|
|
124
115
|
try {
|
|
125
116
|
const pnlConnected = await this.connectPnL(username, password);
|
|
126
117
|
if (pnlConnected && this.pnlConn) {
|
|
@@ -229,45 +220,6 @@ class RithmicService extends EventEmitter {
|
|
|
229
220
|
}
|
|
230
221
|
}
|
|
231
222
|
|
|
232
|
-
// ==================== TRADE ROUTES ====================
|
|
233
|
-
|
|
234
|
-
async _fetchTradeRoutes() {
|
|
235
|
-
if (!this.orderConn || !this.loginInfo) return;
|
|
236
|
-
|
|
237
|
-
return new Promise((resolve) => {
|
|
238
|
-
const timeout = setTimeout(() => resolve(), 5000);
|
|
239
|
-
|
|
240
|
-
const onTradeRoute = (res) => {
|
|
241
|
-
if (res.tradeRoute && res.exchange) {
|
|
242
|
-
this.tradeRoutes.set(res.exchange, res.tradeRoute);
|
|
243
|
-
}
|
|
244
|
-
if (res.rpCode?.[0] === '0' && !res.tradeRoute) {
|
|
245
|
-
clearTimeout(timeout);
|
|
246
|
-
this.removeListener('tradeRoutes', onTradeRoute);
|
|
247
|
-
resolve();
|
|
248
|
-
}
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
this.on('tradeRoutes', onTradeRoute);
|
|
252
|
-
|
|
253
|
-
try {
|
|
254
|
-
this.orderConn.send('RequestTradeRoutes', {
|
|
255
|
-
templateId: REQ.TRADE_ROUTES,
|
|
256
|
-
userMsg: ['HQX'],
|
|
257
|
-
subscribeForUpdates: true,
|
|
258
|
-
});
|
|
259
|
-
} catch (e) {
|
|
260
|
-
clearTimeout(timeout);
|
|
261
|
-
this.removeListener('tradeRoutes', onTradeRoute);
|
|
262
|
-
resolve();
|
|
263
|
-
}
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
getTradeRoute(exchange) {
|
|
268
|
-
return this.tradeRoutes.get(exchange) || null;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
223
|
// ==================== DELEGATED METHODS ====================
|
|
272
224
|
|
|
273
225
|
async getTradingAccounts() { return getTradingAccounts(this); }
|
|
@@ -276,51 +228,22 @@ class RithmicService extends EventEmitter {
|
|
|
276
228
|
async getOrderHistory(date) { return getOrderHistory(this, date); }
|
|
277
229
|
async placeOrder(orderData) { return placeOrder(this, orderData); }
|
|
278
230
|
async cancelOrder(orderId) { return cancelOrder(this, orderId); }
|
|
279
|
-
async cancelAllOrders(accountId) { return cancelAllOrders(this, accountId); }
|
|
280
231
|
async closePosition(accountId, symbol) { return closePosition(this, accountId, symbol); }
|
|
281
|
-
async
|
|
282
|
-
async
|
|
283
|
-
|
|
284
|
-
// ==================== FAST SCALPING ====================
|
|
285
|
-
|
|
286
|
-
fastEntry(orderData) { return fastEntry(this, orderData); }
|
|
287
|
-
fastExit(orderData) { return fastExit(this, orderData); }
|
|
288
|
-
|
|
289
|
-
async warmup() {
|
|
290
|
-
const results = [];
|
|
291
|
-
if (this.orderConn) results.push(await this.orderConn.warmup());
|
|
292
|
-
if (this.pnlConn) results.push(await this.pnlConn.warmup());
|
|
293
|
-
log.debug('Connection warmup complete', { success: results.filter(Boolean).length, total: results.length });
|
|
294
|
-
return results.every(Boolean);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
getLatencyStats() { return LatencyTracker.getStats(); }
|
|
298
|
-
getRecentLatencies(n = 10) { return LatencyTracker.getRecent(n); }
|
|
299
|
-
|
|
300
|
-
getDiagnostics() {
|
|
301
|
-
return {
|
|
302
|
-
orderConn: this.orderConn?.getDiagnostics() || null,
|
|
303
|
-
pnlConn: this.pnlConn?.getDiagnostics() || null,
|
|
304
|
-
tickerConn: this.tickerConn?.getDiagnostics() || null,
|
|
305
|
-
latency: this.getLatencyStats(),
|
|
306
|
-
accounts: this.accounts.length,
|
|
307
|
-
positions: this.positions.size,
|
|
308
|
-
};
|
|
309
|
-
}
|
|
232
|
+
async getContracts() { return getContracts(this); }
|
|
233
|
+
async searchContracts(searchText) { return searchContracts(this, searchText); }
|
|
310
234
|
|
|
311
235
|
// ==================== STUBS ====================
|
|
312
236
|
|
|
313
237
|
async getUser() { return this.user; }
|
|
314
238
|
async getLifetimeStats() { return { success: true, stats: null }; }
|
|
315
239
|
async getDailyStats() { return { success: true, stats: [] }; }
|
|
316
|
-
async getTradeHistory(
|
|
240
|
+
async getTradeHistory() { return { success: true, trades: [] }; }
|
|
317
241
|
|
|
318
242
|
async getMarketStatus() {
|
|
319
|
-
const status = checkMarketHours();
|
|
243
|
+
const status = this.checkMarketHours();
|
|
320
244
|
return { success: true, isOpen: status.isOpen, message: status.message };
|
|
321
245
|
}
|
|
322
246
|
|
|
323
|
-
checkMarketHours() { return checkMarketHours(); }
|
|
324
247
|
getToken() { return this.loginInfo ? 'connected' : null; }
|
|
325
248
|
getPropfirm() { return this.propfirmKey || 'apex'; }
|
|
326
249
|
|
|
@@ -334,10 +257,28 @@ class RithmicService extends EventEmitter {
|
|
|
334
257
|
};
|
|
335
258
|
}
|
|
336
259
|
|
|
337
|
-
// ====================
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
260
|
+
// ==================== MARKET HOURS ====================
|
|
261
|
+
|
|
262
|
+
checkMarketHours() {
|
|
263
|
+
const now = new Date();
|
|
264
|
+
const utcDay = now.getUTCDay();
|
|
265
|
+
const utcHour = now.getUTCHours();
|
|
266
|
+
|
|
267
|
+
const isDST = now.getTimezoneOffset() < Math.max(
|
|
268
|
+
new Date(now.getFullYear(), 0, 1).getTimezoneOffset(),
|
|
269
|
+
new Date(now.getFullYear(), 6, 1).getTimezoneOffset()
|
|
270
|
+
);
|
|
271
|
+
const ctOffset = isDST ? 5 : 6;
|
|
272
|
+
const ctHour = (utcHour - ctOffset + 24) % 24;
|
|
273
|
+
const ctDay = utcHour < ctOffset ? (utcDay + 6) % 7 : utcDay;
|
|
274
|
+
|
|
275
|
+
if (ctDay === 6) return { isOpen: false, message: 'Market closed (Saturday)' };
|
|
276
|
+
if (ctDay === 0 && ctHour < 17) return { isOpen: false, message: 'Market opens Sunday 5:00 PM CT' };
|
|
277
|
+
if (ctDay === 5 && ctHour >= 16) return { isOpen: false, message: 'Market closed (Friday after 4PM CT)' };
|
|
278
|
+
if (ctHour === 16 && ctDay >= 1 && ctDay <= 4) return { isOpen: false, message: 'Daily maintenance (4:00-5:00 PM CT)' };
|
|
279
|
+
|
|
280
|
+
return { isOpen: true, message: 'Market is open' };
|
|
281
|
+
}
|
|
341
282
|
|
|
342
283
|
// ==================== CLEANUP ====================
|
|
343
284
|
|
|
@@ -346,7 +287,11 @@ class RithmicService extends EventEmitter {
|
|
|
346
287
|
|
|
347
288
|
for (const conn of connections) {
|
|
348
289
|
if (conn) {
|
|
349
|
-
try {
|
|
290
|
+
try {
|
|
291
|
+
await conn.disconnect();
|
|
292
|
+
} catch (err) {
|
|
293
|
+
log.warn('Disconnect error', { error: err.message });
|
|
294
|
+
}
|
|
350
295
|
}
|
|
351
296
|
}
|
|
352
297
|
|
|
@@ -357,8 +302,6 @@ class RithmicService extends EventEmitter {
|
|
|
357
302
|
this.accountPnL.clear();
|
|
358
303
|
this.positions.clear();
|
|
359
304
|
this.orders = [];
|
|
360
|
-
this.completedTrades = [];
|
|
361
|
-
this._openEntries = null;
|
|
362
305
|
this.loginInfo = null;
|
|
363
306
|
this.user = null;
|
|
364
307
|
this.credentials = null;
|
|
@@ -368,4 +311,4 @@ class RithmicService extends EventEmitter {
|
|
|
368
311
|
}
|
|
369
312
|
}
|
|
370
313
|
|
|
371
|
-
module.exports = { RithmicService, RITHMIC_SYSTEMS, RITHMIC_ENDPOINTS
|
|
314
|
+
module.exports = { RithmicService, RITHMIC_SYSTEMS, RITHMIC_ENDPOINTS };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Market hours utilities for Rithmic
|
|
3
|
+
* @module services/rithmic/market
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Check if market is currently open
|
|
8
|
+
* @returns {{isOpen: boolean, message: string}}
|
|
9
|
+
*/
|
|
10
|
+
const checkMarketHours = () => {
|
|
11
|
+
const now = new Date();
|
|
12
|
+
const utcDay = now.getUTCDay();
|
|
13
|
+
const utcHour = now.getUTCHours();
|
|
14
|
+
|
|
15
|
+
const isDST = now.getTimezoneOffset() < Math.max(
|
|
16
|
+
new Date(now.getFullYear(), 0, 1).getTimezoneOffset(),
|
|
17
|
+
new Date(now.getFullYear(), 6, 1).getTimezoneOffset()
|
|
18
|
+
);
|
|
19
|
+
const ctOffset = isDST ? 5 : 6;
|
|
20
|
+
const ctHour = (utcHour - ctOffset + 24) % 24;
|
|
21
|
+
const ctDay = utcHour < ctOffset ? (utcDay + 6) % 7 : utcDay;
|
|
22
|
+
|
|
23
|
+
if (ctDay === 6) return { isOpen: false, message: 'Market closed (Saturday)' };
|
|
24
|
+
if (ctDay === 0 && ctHour < 17) return { isOpen: false, message: 'Market opens Sunday 5:00 PM CT' };
|
|
25
|
+
if (ctDay === 5 && ctHour >= 16) return { isOpen: false, message: 'Market closed (Friday after 4PM CT)' };
|
|
26
|
+
if (ctHour === 16 && ctDay >= 1 && ctDay <= 4) return { isOpen: false, message: 'Daily maintenance (4:00-5:00 PM CT)' };
|
|
27
|
+
|
|
28
|
+
return { isOpen: true, message: 'Market is open' };
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
module.exports = { checkMarketHours };
|
|
@@ -4,10 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const { REQ } = require('./constants');
|
|
7
|
-
const { logger } = require('../../utils/logger');
|
|
8
|
-
const { fastEntry, fastExit, getEffectiveLoginInfo } = require('./orders-fast');
|
|
9
|
-
|
|
10
|
-
const log = logger.scope('RithmicOrders');
|
|
11
7
|
|
|
12
8
|
/**
|
|
13
9
|
* Place order via ORDER_PLANT
|
|
@@ -20,32 +16,19 @@ const placeOrder = async (service, orderData) => {
|
|
|
20
16
|
}
|
|
21
17
|
|
|
22
18
|
try {
|
|
23
|
-
// FIXED: Use account-specific fcmId/ibId if available
|
|
24
|
-
const account = service.accounts?.find(a =>
|
|
25
|
-
a.accountId === orderData.accountId || a.rithmicAccountId === orderData.accountId
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
// Get trade route from API (required by Rithmic)
|
|
29
|
-
const exchange = orderData.exchange || 'CME';
|
|
30
|
-
const tradeRoute = service.getTradeRoute?.(exchange);
|
|
31
|
-
if (!tradeRoute) {
|
|
32
|
-
return { success: false, error: `No trade route for exchange ${exchange}` };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
19
|
service.orderConn.send('RequestNewOrder', {
|
|
36
20
|
templateId: REQ.NEW_ORDER,
|
|
37
21
|
userMsg: ['HQX'],
|
|
38
|
-
fcmId:
|
|
39
|
-
ibId:
|
|
22
|
+
fcmId: service.loginInfo.fcmId,
|
|
23
|
+
ibId: service.loginInfo.ibId,
|
|
40
24
|
accountId: orderData.accountId,
|
|
41
25
|
symbol: orderData.symbol,
|
|
42
|
-
exchange: exchange,
|
|
26
|
+
exchange: orderData.exchange || 'CME',
|
|
43
27
|
quantity: orderData.size,
|
|
44
28
|
transactionType: orderData.side === 0 ? 1 : 2, // 1=Buy, 2=Sell
|
|
45
29
|
duration: 1, // DAY
|
|
46
|
-
|
|
30
|
+
orderType: orderData.type === 2 ? 1 : 2, // 1=Market, 2=Limit
|
|
47
31
|
price: orderData.price || 0,
|
|
48
|
-
tradeRoute: tradeRoute, // Required by Rithmic API - fetched from RequestTradeRoutes
|
|
49
32
|
});
|
|
50
33
|
|
|
51
34
|
return { success: true, message: 'Order submitted' };
|
|
@@ -200,99 +183,10 @@ const closePosition = async (service, accountId, symbol) => {
|
|
|
200
183
|
});
|
|
201
184
|
};
|
|
202
185
|
|
|
203
|
-
/**
|
|
204
|
-
* Cancel all open orders for an account
|
|
205
|
-
* @param {RithmicService} service - The Rithmic service instance
|
|
206
|
-
* @param {string} accountId - Account ID
|
|
207
|
-
*/
|
|
208
|
-
const cancelAllOrders = async (service, accountId) => {
|
|
209
|
-
if (!service.orderConn || !service.loginInfo) {
|
|
210
|
-
return { success: false, error: 'Not connected' };
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
try {
|
|
214
|
-
// Use RequestCancelAllOrders template
|
|
215
|
-
service.orderConn.send('RequestCancelAllOrders', {
|
|
216
|
-
templateId: 346, // CANCEL_ALL_ORDERS
|
|
217
|
-
userMsg: ['HQX-STOP'],
|
|
218
|
-
fcmId: service.loginInfo.fcmId,
|
|
219
|
-
ibId: service.loginInfo.ibId,
|
|
220
|
-
accountId: accountId,
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
log.info('Cancel all orders sent', { accountId });
|
|
224
|
-
return { success: true };
|
|
225
|
-
} catch (error) {
|
|
226
|
-
log.error('Cancel all orders failed', { error: error.message });
|
|
227
|
-
return { success: false, error: error.message };
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Flatten all positions for an account (close all positions)
|
|
233
|
-
* @param {RithmicService} service - The Rithmic service instance
|
|
234
|
-
* @param {string} accountId - Account ID
|
|
235
|
-
*/
|
|
236
|
-
const flattenAll = async (service, accountId) => {
|
|
237
|
-
const results = [];
|
|
238
|
-
const positions = Array.from(service.positions.values());
|
|
239
|
-
const accountPositions = positions.filter(p => p.accountId === accountId && p.quantity !== 0);
|
|
240
|
-
|
|
241
|
-
log.info('Flattening all positions', { accountId, count: accountPositions.length });
|
|
242
|
-
|
|
243
|
-
for (const position of accountPositions) {
|
|
244
|
-
try {
|
|
245
|
-
const result = await placeOrder(service, {
|
|
246
|
-
accountId,
|
|
247
|
-
symbol: position.symbol,
|
|
248
|
-
exchange: position.exchange,
|
|
249
|
-
size: Math.abs(position.quantity),
|
|
250
|
-
side: position.quantity > 0 ? 1 : 0, // Sell if long, Buy if short
|
|
251
|
-
type: 2, // Market
|
|
252
|
-
});
|
|
253
|
-
results.push({ symbol: position.symbol, ...result });
|
|
254
|
-
} catch (error) {
|
|
255
|
-
results.push({ symbol: position.symbol, success: false, error: error.message });
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
return { success: true, results };
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Emergency stop - Cancel all orders and flatten all positions
|
|
264
|
-
* @param {RithmicService} service - The Rithmic service instance
|
|
265
|
-
* @param {string} accountId - Account ID
|
|
266
|
-
*/
|
|
267
|
-
const emergencyStop = async (service, accountId) => {
|
|
268
|
-
log.warn('EMERGENCY STOP initiated', { accountId });
|
|
269
|
-
|
|
270
|
-
// 1. Cancel all orders first
|
|
271
|
-
const cancelResult = await cancelAllOrders(service, accountId);
|
|
272
|
-
|
|
273
|
-
// 2. Wait a moment for cancellations to process
|
|
274
|
-
await new Promise(r => setTimeout(r, 500));
|
|
275
|
-
|
|
276
|
-
// 3. Flatten all positions
|
|
277
|
-
const flattenResult = await flattenAll(service, accountId);
|
|
278
|
-
|
|
279
|
-
return {
|
|
280
|
-
success: cancelResult.success && flattenResult.success,
|
|
281
|
-
cancelled: cancelResult,
|
|
282
|
-
flattened: flattenResult,
|
|
283
|
-
};
|
|
284
|
-
};
|
|
285
|
-
|
|
286
186
|
module.exports = {
|
|
287
187
|
placeOrder,
|
|
288
188
|
cancelOrder,
|
|
289
|
-
cancelAllOrders,
|
|
290
189
|
getOrders,
|
|
291
190
|
getOrderHistory,
|
|
292
|
-
closePosition
|
|
293
|
-
flattenAll,
|
|
294
|
-
emergencyStop,
|
|
295
|
-
// Fast scalping - ultra-low latency
|
|
296
|
-
fastEntry,
|
|
297
|
-
fastExit,
|
|
191
|
+
closePosition
|
|
298
192
|
};
|