hedgequantx 2.6.162 → 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.
Files changed (138) hide show
  1. package/README.md +15 -88
  2. package/bin/cli.js +0 -11
  3. package/dist/lib/api.jsc +0 -0
  4. package/dist/lib/api2.jsc +0 -0
  5. package/dist/lib/core.jsc +0 -0
  6. package/dist/lib/core2.jsc +0 -0
  7. package/dist/lib/data.js +1 -1
  8. package/dist/lib/data.jsc +0 -0
  9. package/dist/lib/data2.jsc +0 -0
  10. package/dist/lib/decoder.jsc +0 -0
  11. package/dist/lib/m/mod1.jsc +0 -0
  12. package/dist/lib/m/mod2.jsc +0 -0
  13. package/dist/lib/n/r1.jsc +0 -0
  14. package/dist/lib/n/r2.jsc +0 -0
  15. package/dist/lib/n/r3.jsc +0 -0
  16. package/dist/lib/n/r4.jsc +0 -0
  17. package/dist/lib/n/r5.jsc +0 -0
  18. package/dist/lib/n/r6.jsc +0 -0
  19. package/dist/lib/n/r7.jsc +0 -0
  20. package/dist/lib/o/util1.jsc +0 -0
  21. package/dist/lib/o/util2.jsc +0 -0
  22. package/package.json +6 -3
  23. package/src/app.js +40 -162
  24. package/src/config/constants.js +31 -33
  25. package/src/config/propfirms.js +13 -217
  26. package/src/config/settings.js +0 -43
  27. package/src/lib/api.js +198 -0
  28. package/src/lib/api2.js +353 -0
  29. package/src/lib/core.js +539 -0
  30. package/src/lib/core2.js +341 -0
  31. package/src/lib/data.js +555 -0
  32. package/src/lib/data2.js +492 -0
  33. package/src/lib/decoder.js +599 -0
  34. package/src/lib/m/s1.js +804 -0
  35. package/src/lib/m/s2.js +34 -0
  36. package/src/lib/n/r1.js +454 -0
  37. package/src/lib/n/r2.js +514 -0
  38. package/src/lib/n/r3.js +631 -0
  39. package/src/lib/n/r4.js +401 -0
  40. package/src/lib/n/r5.js +335 -0
  41. package/src/lib/n/r6.js +425 -0
  42. package/src/lib/n/r7.js +530 -0
  43. package/src/lib/o/l1.js +44 -0
  44. package/src/lib/o/l2.js +427 -0
  45. package/src/lib/python-bridge.js +206 -0
  46. package/src/menus/connect.js +14 -176
  47. package/src/menus/dashboard.js +65 -110
  48. package/src/pages/accounts.js +18 -18
  49. package/src/pages/algo/copy-trading.js +210 -240
  50. package/src/pages/algo/index.js +41 -104
  51. package/src/pages/algo/one-account.js +386 -33
  52. package/src/pages/algo/ui.js +312 -151
  53. package/src/pages/orders.js +3 -3
  54. package/src/pages/positions.js +3 -3
  55. package/src/pages/stats/chart.js +74 -0
  56. package/src/pages/stats/display.js +228 -0
  57. package/src/pages/stats/index.js +236 -0
  58. package/src/pages/stats/metrics.js +213 -0
  59. package/src/pages/user.js +6 -6
  60. package/src/services/hqx-server/constants.js +55 -0
  61. package/src/services/hqx-server/index.js +401 -0
  62. package/src/services/hqx-server/latency.js +81 -0
  63. package/src/services/index.js +12 -3
  64. package/src/services/rithmic/accounts.js +7 -32
  65. package/src/services/rithmic/connection.js +1 -204
  66. package/src/services/rithmic/contracts.js +235 -0
  67. package/src/services/rithmic/handlers.js +21 -196
  68. package/src/services/rithmic/index.js +60 -291
  69. package/src/services/rithmic/market.js +31 -0
  70. package/src/services/rithmic/orders.js +5 -361
  71. package/src/services/rithmic/protobuf.js +5 -195
  72. package/src/services/session.js +22 -173
  73. package/src/ui/box.js +10 -18
  74. package/src/ui/index.js +1 -3
  75. package/src/ui/menu.js +1 -1
  76. package/src/utils/prompts.js +2 -2
  77. package/dist/lib/m/s1.js +0 -1
  78. package/src/menus/ai-agent-connect.js +0 -181
  79. package/src/menus/ai-agent-models.js +0 -219
  80. package/src/menus/ai-agent-oauth.js +0 -292
  81. package/src/menus/ai-agent-ui.js +0 -141
  82. package/src/menus/ai-agent.js +0 -484
  83. package/src/pages/algo/algo-config.js +0 -195
  84. package/src/pages/algo/algo-multi.js +0 -801
  85. package/src/pages/algo/algo-utils.js +0 -58
  86. package/src/pages/algo/copy-engine.js +0 -449
  87. package/src/pages/algo/custom-strategy.js +0 -459
  88. package/src/pages/algo/logger.js +0 -245
  89. package/src/pages/algo/smart-logs-data.js +0 -218
  90. package/src/pages/algo/smart-logs.js +0 -387
  91. package/src/pages/algo/ui-constants.js +0 -144
  92. package/src/pages/algo/ui-summary.js +0 -184
  93. package/src/pages/stats-calculations.js +0 -191
  94. package/src/pages/stats-ui.js +0 -381
  95. package/src/pages/stats.js +0 -339
  96. package/src/services/ai/client-analysis.js +0 -194
  97. package/src/services/ai/client-models.js +0 -333
  98. package/src/services/ai/client.js +0 -343
  99. package/src/services/ai/index.js +0 -384
  100. package/src/services/ai/oauth-anthropic.js +0 -265
  101. package/src/services/ai/oauth-gemini.js +0 -223
  102. package/src/services/ai/oauth-iflow.js +0 -269
  103. package/src/services/ai/oauth-openai.js +0 -233
  104. package/src/services/ai/oauth-qwen.js +0 -279
  105. package/src/services/ai/providers/index.js +0 -526
  106. package/src/services/ai/proxy-install.js +0 -249
  107. package/src/services/ai/proxy-manager.js +0 -494
  108. package/src/services/ai/proxy-remote.js +0 -161
  109. package/src/services/ai/strategy-supervisor.js +0 -1312
  110. package/src/services/ai/supervisor-data.js +0 -195
  111. package/src/services/ai/supervisor-optimize.js +0 -215
  112. package/src/services/ai/supervisor-sync.js +0 -178
  113. package/src/services/ai/supervisor-utils.js +0 -158
  114. package/src/services/ai/supervisor.js +0 -484
  115. package/src/services/ai/validation.js +0 -250
  116. package/src/services/hqx-server-events.js +0 -110
  117. package/src/services/hqx-server-handlers.js +0 -217
  118. package/src/services/hqx-server-latency.js +0 -136
  119. package/src/services/hqx-server.js +0 -403
  120. package/src/services/position-constants.js +0 -28
  121. package/src/services/position-manager.js +0 -528
  122. package/src/services/position-momentum.js +0 -206
  123. package/src/services/projectx/accounts.js +0 -142
  124. package/src/services/projectx/index.js +0 -443
  125. package/src/services/projectx/market.js +0 -172
  126. package/src/services/projectx/stats.js +0 -110
  127. package/src/services/projectx/trading.js +0 -180
  128. package/src/services/rithmic/latency-tracker.js +0 -182
  129. package/src/services/rithmic/market-data.js +0 -549
  130. package/src/services/rithmic/specs.js +0 -146
  131. package/src/services/rithmic/trade-history.js +0 -254
  132. package/src/services/session-history.js +0 -475
  133. package/src/services/strategy/hft-tick.js +0 -507
  134. package/src/services/strategy/recovery-math.js +0 -402
  135. package/src/services/tradovate/constants.js +0 -109
  136. package/src/services/tradovate/index.js +0 -505
  137. package/src/services/tradovate/market.js +0 -47
  138. 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, REQ } = require('./constants');
11
- const { createOrderHandler, createPnLHandler, LatencyTracker } = require('./handlers');
12
- const { fetchAccounts, getTradingAccounts, requestPnLSnapshot, subscribePnLUpdates, getPositions } = require('./accounts');
13
- const { placeOrder, cancelOrder, cancelAllOrders, getOrders, getOrderHistory, closePosition, flattenAll, emergencyStop, fastEntry, fastExit } = require('./orders');
14
- const { decodeFrontMonthContract } = require('./protobuf');
15
- const { TIMEOUTS, CACHE } = require('../../config/settings');
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');
21
+ const { TIMEOUTS } = require('../../config/settings');
16
22
  const { logger } = require('../../utils/logger');
17
23
 
18
- // Extracted modules
19
- const { CME_CONTRACT_SPECS, PROPFIRM_CONFIGS, checkMarketHours } = require('./specs');
20
- const { getTradeHistory, setupOrderFillListener } = require('./trade-history');
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 flattenAll(accountId) { return flattenAll(this, accountId); }
282
- async emergencyStop(accountId) { return emergencyStop(this, accountId); }
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(accountId, days = 30) { return getTradeHistory(this, accountId, days); }
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,183 +257,27 @@ class RithmicService extends EventEmitter {
334
257
  };
335
258
  }
336
259
 
337
- // ==================== CONTRACTS ====================
338
-
339
- async getContracts() {
340
- if (this._contractsCache && Date.now() - this._contractsCacheTime < CACHE.CONTRACTS_TTL) {
341
- return { success: true, contracts: this._contractsCache, source: 'cache' };
342
- }
260
+ // ==================== MARKET HOURS ====================
343
261
 
344
- if (!this.credentials) return { success: false, error: 'Not logged in' };
262
+ checkMarketHours() {
263
+ const now = new Date();
264
+ const utcDay = now.getUTCDay();
265
+ const utcHour = now.getUTCHours();
345
266
 
346
- try {
347
- if (!this.tickerConn) {
348
- const connected = await this.connectTicker(this.credentials.username, this.credentials.password);
349
- if (!connected) return { success: false, error: 'Failed to connect to TICKER_PLANT' };
350
- }
351
-
352
- this.tickerConn.setMaxListeners(5000);
353
- log.debug('Fetching contracts from Rithmic API');
354
- const contracts = await this._fetchAllFrontMonths();
355
-
356
- if (!contracts.length) return { success: false, error: 'No tradeable contracts found' };
357
-
358
- this._contractsCache = contracts;
359
- this._contractsCacheTime = Date.now();
360
-
361
- return { success: true, contracts, source: 'api' };
362
- } catch (err) {
363
- log.error('getContracts error', { error: err.message });
364
- return { success: false, error: err.message };
365
- }
366
- }
367
-
368
- async searchContracts(searchText) {
369
- const result = await this.getContracts();
370
- if (!searchText || !result.success) return result.contracts || [];
371
-
372
- const search = searchText.toUpperCase();
373
- return result.contracts.filter(c =>
374
- c.symbol.toUpperCase().includes(search) ||
375
- c.name.toUpperCase().includes(search)
267
+ const isDST = now.getTimezoneOffset() < Math.max(
268
+ new Date(now.getFullYear(), 0, 1).getTimezoneOffset(),
269
+ new Date(now.getFullYear(), 6, 1).getTimezoneOffset()
376
270
  );
377
- }
378
-
379
- async _fetchAllFrontMonths() {
380
- if (!this.tickerConn) throw new Error('TICKER_PLANT not connected');
381
-
382
- return new Promise((resolve) => {
383
- const contracts = new Map();
384
- const productsToCheck = new Map();
385
-
386
- const productHandler = (msg) => {
387
- if (msg.templateId !== 112) return;
388
-
389
- const decoded = this._decodeProductCodes(msg.data);
390
- if (!decoded.productCode || !decoded.exchange) return;
391
-
392
- const validExchanges = ['CME', 'CBOT', 'NYMEX', 'COMEX', 'NYBOT', 'CFE'];
393
- if (!validExchanges.includes(decoded.exchange)) return;
394
-
395
- const name = (decoded.productName || '').toLowerCase();
396
- if (name.includes('option') || name.includes('swap') || name.includes('spread')) return;
397
-
398
- const key = `${decoded.productCode}:${decoded.exchange}`;
399
- if (!productsToCheck.has(key)) {
400
- productsToCheck.set(key, {
401
- productCode: decoded.productCode,
402
- productName: decoded.productName || decoded.productCode,
403
- exchange: decoded.exchange,
404
- });
405
- }
406
- };
407
-
408
- const frontMonthHandler = (msg) => {
409
- if (msg.templateId !== 114) return;
410
-
411
- const decoded = decodeFrontMonthContract(msg.data);
412
- if (decoded.rpCode[0] === '0' && decoded.tradingSymbol) {
413
- contracts.set(decoded.userMsg, {
414
- symbol: decoded.tradingSymbol,
415
- baseSymbol: decoded.userMsg,
416
- exchange: decoded.exchange,
417
- });
418
- }
419
- };
420
-
421
- this.tickerConn.on('message', productHandler);
422
- this.tickerConn.on('message', frontMonthHandler);
423
-
424
- this.tickerConn.send('RequestProductCodes', {
425
- templateId: 111,
426
- userMsg: ['get-products'],
427
- });
428
-
429
- setTimeout(() => {
430
- this.tickerConn.removeListener('message', productHandler);
431
- log.debug('Collected products', { count: productsToCheck.size });
432
-
433
- for (const product of productsToCheck.values()) {
434
- this.tickerConn.send('RequestFrontMonthContract', {
435
- templateId: 113,
436
- userMsg: [product.productCode],
437
- symbol: product.productCode,
438
- exchange: product.exchange,
439
- });
440
- }
441
-
442
- setTimeout(() => {
443
- this.tickerConn.removeListener('message', frontMonthHandler);
444
-
445
- const results = [];
446
- for (const [baseSymbol, contract] of contracts) {
447
- const productKey = `${baseSymbol}:${contract.exchange}`;
448
- const product = productsToCheck.get(productKey);
449
- const specs = CME_CONTRACT_SPECS[baseSymbol] || null;
450
- const productName = specs?.name || product?.productName || baseSymbol;
451
-
452
- results.push({
453
- symbol: contract.symbol,
454
- baseSymbol,
455
- name: contract.symbol,
456
- description: productName,
457
- exchange: contract.exchange,
458
- tickSize: specs?.tickSize ?? null,
459
- tickValue: specs?.tickValue ?? null,
460
- });
461
- }
462
-
463
- results.sort((a, b) => a.baseSymbol.localeCompare(b.baseSymbol));
464
- log.debug('Got contracts from API', { count: results.length });
465
- resolve(results);
466
- }, TIMEOUTS.RITHMIC_PRODUCTS);
467
- }, TIMEOUTS.RITHMIC_CONTRACTS);
468
- });
469
- }
470
-
471
- _decodeProductCodes(buffer) {
472
- const result = {};
473
- let offset = 0;
474
-
475
- const readVarint = (buf, off) => {
476
- let value = 0, shift = 0;
477
- while (off < buf.length) {
478
- const byte = buf[off++];
479
- value |= (byte & 0x7F) << shift;
480
- if (!(byte & 0x80)) break;
481
- shift += 7;
482
- }
483
- return [value, off];
484
- };
485
-
486
- const readString = (buf, off) => {
487
- const [len, newOff] = readVarint(buf, off);
488
- return [buf.slice(newOff, newOff + len).toString('utf8'), newOff + len];
489
- };
490
-
491
- while (offset < buffer.length) {
492
- try {
493
- const [tag, tagOff] = readVarint(buffer, offset);
494
- const wireType = tag & 0x7;
495
- const fieldNumber = tag >>> 3;
496
- offset = tagOff;
497
-
498
- if (wireType === 0) {
499
- const [, newOff] = readVarint(buffer, offset);
500
- offset = newOff;
501
- } else if (wireType === 2) {
502
- const [val, newOff] = readString(buffer, offset);
503
- offset = newOff;
504
- if (fieldNumber === 110101) result.exchange = val;
505
- if (fieldNumber === 100749) result.productCode = val;
506
- if (fieldNumber === 100003) result.productName = val;
507
- } else {
508
- break;
509
- }
510
- } catch { break; }
511
- }
512
-
513
- return result;
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' };
514
281
  }
515
282
 
516
283
  // ==================== CLEANUP ====================
@@ -520,7 +287,11 @@ class RithmicService extends EventEmitter {
520
287
 
521
288
  for (const conn of connections) {
522
289
  if (conn) {
523
- try { await conn.disconnect(); } catch (err) { log.warn('Disconnect error', { error: err.message }); }
290
+ try {
291
+ await conn.disconnect();
292
+ } catch (err) {
293
+ log.warn('Disconnect error', { error: err.message });
294
+ }
524
295
  }
525
296
  }
526
297
 
@@ -531,8 +302,6 @@ class RithmicService extends EventEmitter {
531
302
  this.accountPnL.clear();
532
303
  this.positions.clear();
533
304
  this.orders = [];
534
- this.completedTrades = [];
535
- this._openEntries = null;
536
305
  this.loginInfo = null;
537
306
  this.user = null;
538
307
  this.credentials = null;
@@ -542,4 +311,4 @@ class RithmicService extends EventEmitter {
542
311
  }
543
312
  }
544
313
 
545
- module.exports = { RithmicService, RITHMIC_SYSTEMS, RITHMIC_ENDPOINTS, LatencyTracker };
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 };