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
@@ -1,549 +0,0 @@
1
- /**
2
- * =============================================================================
3
- * Rithmic Market Data Feed
4
- * =============================================================================
5
- * Handles real-time quotes and trades streaming via Rithmic WebSocket
6
- *
7
- * Based on HQX-TG RithmicMarketData implementation
8
- *
9
- * IMPORTANT: Use continuous/front-month symbols for subscriptions:
10
- * - ES, NQ, MES, MNQ (NOT ESH25, NQH25, etc.)
11
- * - Rithmic automatically routes to the current front-month contract
12
- */
13
-
14
- const EventEmitter = require('events');
15
- const { logger } = require('../../utils/logger');
16
-
17
- const log = logger.scope('RithmicMD');
18
-
19
- // Template IDs for Rithmic messages
20
- const TEMPLATE_IDS = {
21
- // Request
22
- MARKET_DATA_UPDATE: 100,
23
- // Response/Stream
24
- LAST_TRADE: 150,
25
- BEST_BID_OFFER: 151,
26
- };
27
-
28
- // Rithmic field IDs for LastTrade (from protobuf)
29
- const LAST_TRADE_FIELDS = {
30
- TEMPLATE_ID: 154467,
31
- SYMBOL: 110100,
32
- EXCHANGE: 110101,
33
- TRADE_PRICE: 100006,
34
- TRADE_SIZE: 100178,
35
- AGGRESSOR: 112003, // 1=BUY, 2=SELL
36
- SSBOE: 150100,
37
- USECS: 150101,
38
- };
39
-
40
- // Rithmic field IDs for BestBidOffer (from protobuf)
41
- const BBO_FIELDS = {
42
- TEMPLATE_ID: 154467,
43
- SYMBOL: 110100,
44
- EXCHANGE: 110101,
45
- BID_PRICE: 100022,
46
- BID_SIZE: 100030,
47
- ASK_PRICE: 100025,
48
- ASK_SIZE: 100031,
49
- SSBOE: 150100,
50
- USECS: 150101,
51
- };
52
-
53
- /**
54
- * Read a varint from buffer starting at offset
55
- * Uses BigInt internally to handle large field IDs correctly
56
- * @param {Buffer} buffer
57
- * @param {number} offset
58
- * @returns {[number, number]} [value, newOffset]
59
- */
60
- function readVarint(buffer, offset) {
61
- let result = BigInt(0);
62
- let shift = BigInt(0);
63
- let pos = offset;
64
-
65
- while (pos < buffer.length) {
66
- const byte = buffer[pos++];
67
- result |= BigInt(byte & 0x7f) << shift;
68
- if ((byte & 0x80) === 0) {
69
- return [Number(result), pos];
70
- }
71
- shift += BigInt(7);
72
- if (shift > BigInt(63)) {
73
- throw new Error('Varint too large');
74
- }
75
- }
76
-
77
- throw new Error('Incomplete varint');
78
- }
79
-
80
- /**
81
- * Read a length-delimited field (string/bytes)
82
- * @param {Buffer} buffer
83
- * @param {number} offset
84
- * @returns {[string, number]} [value, newOffset]
85
- */
86
- function readLengthDelimited(buffer, offset) {
87
- const [length, newOffset] = readVarint(buffer, offset);
88
- const value = buffer.slice(newOffset, newOffset + length).toString('utf8');
89
- return [value, newOffset + length];
90
- }
91
-
92
- /**
93
- * Skip a field based on wire type
94
- * @param {Buffer} buffer
95
- * @param {number} offset
96
- * @param {number} wireType
97
- * @returns {number} newOffset
98
- */
99
- function skipField(buffer, offset, wireType) {
100
- switch (wireType) {
101
- case 0: // Varint
102
- const [, newOffset] = readVarint(buffer, offset);
103
- return newOffset;
104
- case 1: // 64-bit
105
- return offset + 8;
106
- case 2: // Length-delimited
107
- const [length, lenOffset] = readVarint(buffer, offset);
108
- return lenOffset + length;
109
- case 5: // 32-bit
110
- return offset + 4;
111
- default:
112
- throw new Error(`Unknown wire type: ${wireType}`);
113
- }
114
- }
115
-
116
- /**
117
- * Manually decode LastTrade message from Rithmic
118
- * Required because protobufjs can't handle field IDs > 100000
119
- * @param {Buffer} buffer
120
- * @returns {Object}
121
- */
122
- function decodeLastTrade(buffer) {
123
- const result = {};
124
- let offset = 0;
125
-
126
- while (offset < buffer.length) {
127
- try {
128
- const [tag, newOffset] = readVarint(buffer, offset);
129
- const fieldNumber = tag >>> 3;
130
- const wireType = tag & 0x7;
131
- offset = newOffset;
132
-
133
- switch (fieldNumber) {
134
- case LAST_TRADE_FIELDS.SYMBOL:
135
- if (wireType === 2) {
136
- const [val, next] = readLengthDelimited(buffer, offset);
137
- result.symbol = val;
138
- offset = next;
139
- } else {
140
- offset = skipField(buffer, offset, wireType);
141
- }
142
- break;
143
- case LAST_TRADE_FIELDS.EXCHANGE:
144
- if (wireType === 2) {
145
- const [val, next] = readLengthDelimited(buffer, offset);
146
- result.exchange = val;
147
- offset = next;
148
- } else {
149
- offset = skipField(buffer, offset, wireType);
150
- }
151
- break;
152
- case LAST_TRADE_FIELDS.TRADE_PRICE:
153
- if (wireType === 1) {
154
- result.tradePrice = buffer.readDoubleLE(offset);
155
- offset += 8;
156
- } else {
157
- offset = skipField(buffer, offset, wireType);
158
- }
159
- break;
160
- case LAST_TRADE_FIELDS.TRADE_SIZE:
161
- if (wireType === 0) {
162
- const [val, next] = readVarint(buffer, offset);
163
- result.tradeSize = val;
164
- offset = next;
165
- } else {
166
- offset = skipField(buffer, offset, wireType);
167
- }
168
- break;
169
- case LAST_TRADE_FIELDS.AGGRESSOR:
170
- if (wireType === 0) {
171
- const [val, next] = readVarint(buffer, offset);
172
- result.aggressor = val;
173
- offset = next;
174
- } else {
175
- offset = skipField(buffer, offset, wireType);
176
- }
177
- break;
178
- case LAST_TRADE_FIELDS.SSBOE:
179
- if (wireType === 0) {
180
- const [val, next] = readVarint(buffer, offset);
181
- result.ssboe = val;
182
- offset = next;
183
- } else {
184
- offset = skipField(buffer, offset, wireType);
185
- }
186
- break;
187
- case LAST_TRADE_FIELDS.USECS:
188
- if (wireType === 0) {
189
- const [val, next] = readVarint(buffer, offset);
190
- result.usecs = val;
191
- offset = next;
192
- } else {
193
- offset = skipField(buffer, offset, wireType);
194
- }
195
- break;
196
- default:
197
- offset = skipField(buffer, offset, wireType);
198
- }
199
- } catch {
200
- break;
201
- }
202
- }
203
-
204
- return result;
205
- }
206
-
207
- /**
208
- * Manually decode BestBidOffer message from Rithmic
209
- * Required because protobufjs can't handle field IDs > 100000
210
- * @param {Buffer} buffer
211
- * @returns {Object}
212
- */
213
- function decodeBestBidOffer(buffer) {
214
- const result = {};
215
- let offset = 0;
216
-
217
- while (offset < buffer.length) {
218
- try {
219
- const [tag, newOffset] = readVarint(buffer, offset);
220
- const fieldNumber = tag >>> 3;
221
- const wireType = tag & 0x7;
222
- offset = newOffset;
223
-
224
- switch (fieldNumber) {
225
- case BBO_FIELDS.SYMBOL:
226
- if (wireType === 2) {
227
- const [val, next] = readLengthDelimited(buffer, offset);
228
- result.symbol = val;
229
- offset = next;
230
- } else {
231
- offset = skipField(buffer, offset, wireType);
232
- }
233
- break;
234
- case BBO_FIELDS.EXCHANGE:
235
- if (wireType === 2) {
236
- const [val, next] = readLengthDelimited(buffer, offset);
237
- result.exchange = val;
238
- offset = next;
239
- } else {
240
- offset = skipField(buffer, offset, wireType);
241
- }
242
- break;
243
- case BBO_FIELDS.BID_PRICE:
244
- if (wireType === 1) {
245
- result.bidPrice = buffer.readDoubleLE(offset);
246
- offset += 8;
247
- } else {
248
- offset = skipField(buffer, offset, wireType);
249
- }
250
- break;
251
- case BBO_FIELDS.BID_SIZE:
252
- if (wireType === 0) {
253
- const [val, next] = readVarint(buffer, offset);
254
- result.bidSize = val;
255
- offset = next;
256
- } else {
257
- offset = skipField(buffer, offset, wireType);
258
- }
259
- break;
260
- case BBO_FIELDS.ASK_PRICE:
261
- if (wireType === 1) {
262
- result.askPrice = buffer.readDoubleLE(offset);
263
- offset += 8;
264
- } else {
265
- offset = skipField(buffer, offset, wireType);
266
- }
267
- break;
268
- case BBO_FIELDS.ASK_SIZE:
269
- if (wireType === 0) {
270
- const [val, next] = readVarint(buffer, offset);
271
- result.askSize = val;
272
- offset = next;
273
- } else {
274
- offset = skipField(buffer, offset, wireType);
275
- }
276
- break;
277
- case BBO_FIELDS.SSBOE:
278
- if (wireType === 0) {
279
- const [val, next] = readVarint(buffer, offset);
280
- result.ssboe = val;
281
- offset = next;
282
- } else {
283
- offset = skipField(buffer, offset, wireType);
284
- }
285
- break;
286
- case BBO_FIELDS.USECS:
287
- if (wireType === 0) {
288
- const [val, next] = readVarint(buffer, offset);
289
- result.usecs = val;
290
- offset = next;
291
- } else {
292
- offset = skipField(buffer, offset, wireType);
293
- }
294
- break;
295
- default:
296
- offset = skipField(buffer, offset, wireType);
297
- }
298
- } catch {
299
- break;
300
- }
301
- }
302
-
303
- return result;
304
- }
305
-
306
- /**
307
- * Rithmic Market Data Feed
308
- * Provides real-time market data via Rithmic WebSocket connection
309
- */
310
- class RithmicMarketDataFeed extends EventEmitter {
311
- constructor(rithmicService) {
312
- super();
313
- this.service = rithmicService;
314
- this.subscriptions = new Set();
315
- this.connected = false;
316
- this.messageHandler = null;
317
-
318
- // Stats for debugging
319
- this.tradeCount = 0;
320
- this.quoteCount = 0;
321
- this.lastLogTime = 0;
322
- }
323
-
324
- /**
325
- * Check if connected
326
- * @returns {boolean}
327
- */
328
- isConnected() {
329
- return this.connected && this.service?.tickerConn?.isConnected;
330
- }
331
-
332
- /**
333
- * Connect to market data (uses existing tickerConn from RithmicService)
334
- * Will attempt to reconnect if tickerConn is not connected
335
- * @returns {Promise<boolean>}
336
- */
337
- async connect() {
338
- if (!this.service) {
339
- throw new Error('RithmicService not available');
340
- }
341
-
342
- // Check if ticker connection is ready, reconnect if needed
343
- if (!this.service.tickerConn?.isConnected) {
344
- log.info('Ticker connection not ready, attempting to reconnect...');
345
-
346
- // Try to reconnect using stored credentials
347
- if (this.service.credentials) {
348
- try {
349
- const connected = await this.service.connectTicker(
350
- this.service.credentials.username,
351
- this.service.credentials.password
352
- );
353
-
354
- if (!connected || !this.service.tickerConn?.isConnected) {
355
- throw new Error('Failed to reconnect to TICKER_PLANT');
356
- }
357
-
358
- log.info('Ticker connection re-established');
359
- } catch (err) {
360
- throw new Error(`Ticker reconnection failed: ${err.message}`);
361
- }
362
- } else {
363
- throw new Error('Ticker connection not established and no credentials available');
364
- }
365
- }
366
-
367
- // Setup message handler
368
- this.messageHandler = ({ templateId, data }) => {
369
- if (templateId === TEMPLATE_IDS.LAST_TRADE) {
370
- this._handleTrade(data);
371
- } else if (templateId === TEMPLATE_IDS.BEST_BID_OFFER) {
372
- this._handleQuote(data);
373
- }
374
- };
375
-
376
- this.service.tickerConn.on('message', this.messageHandler);
377
- this.connected = true;
378
-
379
- log.info('Market data feed connected');
380
- this.emit('connected');
381
-
382
- return true;
383
- }
384
-
385
- /**
386
- * Disconnect from market data
387
- */
388
- disconnect() {
389
- if (this.messageHandler && this.service?.tickerConn) {
390
- this.service.tickerConn.removeListener('message', this.messageHandler);
391
- }
392
-
393
- // Unsubscribe all
394
- for (const key of this.subscriptions) {
395
- const [exchange, symbol] = key.split(':');
396
- this._sendUnsubscribe(exchange, symbol);
397
- }
398
-
399
- this.subscriptions.clear();
400
- this.connected = false;
401
- this.messageHandler = null;
402
-
403
- log.info('Market data feed disconnected');
404
- this.emit('disconnected');
405
- }
406
-
407
- /**
408
- * Subscribe to market data for a symbol
409
- * @param {string} symbol - Trading symbol (e.g., ESH6, NQH6)
410
- * @param {string} [exchange='CME'] - Exchange
411
- */
412
- subscribe(symbol, exchange = 'CME') {
413
- const key = `${exchange}:${symbol}`;
414
- if (this.subscriptions.has(key)) return;
415
-
416
- if (!this.service?.tickerConn?.isConnected) {
417
- log.warn('Cannot subscribe - ticker connection not ready');
418
- return;
419
- }
420
-
421
- // Extract base symbol for subscription (ES from ESH6)
422
- // Rithmic uses continuous symbols for subscriptions
423
- const baseSymbol = symbol.replace(/[A-Z]\d+$/, '');
424
-
425
- this.service.tickerConn.send('RequestMarketDataUpdate', {
426
- templateId: TEMPLATE_IDS.MARKET_DATA_UPDATE,
427
- userMsg: ['HQX-CLI'],
428
- symbol: baseSymbol,
429
- exchange,
430
- request: 1, // SUBSCRIBE
431
- updateBits: 3, // LAST_TRADE | BBO
432
- });
433
-
434
- this.subscriptions.add(key);
435
- log.info(`Subscribed to ${key} (base: ${baseSymbol})`);
436
- }
437
-
438
- /**
439
- * Unsubscribe from market data for a symbol
440
- * @param {string} symbol
441
- * @param {string} [exchange='CME']
442
- */
443
- unsubscribe(symbol, exchange = 'CME') {
444
- const key = `${exchange}:${symbol}`;
445
- if (!this.subscriptions.has(key)) return;
446
-
447
- this._sendUnsubscribe(exchange, symbol);
448
- this.subscriptions.delete(key);
449
- log.info(`Unsubscribed from ${key}`);
450
- }
451
-
452
- /**
453
- * Send unsubscribe request
454
- * @private
455
- */
456
- _sendUnsubscribe(exchange, symbol) {
457
- if (!this.service?.tickerConn?.isConnected) return;
458
-
459
- const baseSymbol = symbol.replace(/[A-Z]\d+$/, '');
460
-
461
- this.service.tickerConn.send('RequestMarketDataUpdate', {
462
- templateId: TEMPLATE_IDS.MARKET_DATA_UPDATE,
463
- userMsg: ['HQX-CLI'],
464
- symbol: baseSymbol,
465
- exchange,
466
- request: 2, // UNSUBSCRIBE
467
- updateBits: 3,
468
- });
469
- }
470
-
471
- /**
472
- * Handle incoming trade data
473
- * @private
474
- */
475
- _handleTrade(data) {
476
- const t = decodeLastTrade(data);
477
-
478
- if (!t.symbol || t.tradePrice === undefined || t.tradeSize === undefined) {
479
- return;
480
- }
481
-
482
- this.tradeCount++;
483
-
484
- // Log periodically
485
- const now = Date.now();
486
- if (now - this.lastLogTime > 30000) {
487
- this.lastLogTime = now;
488
- log.debug(`Stats: ${this.tradeCount} trades, ${this.quoteCount} quotes`);
489
- }
490
-
491
- // First trade and every 500th
492
- if (this.tradeCount === 1 || this.tradeCount % 500 === 0) {
493
- log.info(`Trade #${this.tradeCount}: ${t.symbol} ${t.tradeSize}@${t.tradePrice}`);
494
- }
495
-
496
- const trade = {
497
- symbol: t.symbol,
498
- exchange: t.exchange || 'CME',
499
- price: t.tradePrice,
500
- lastPrice: t.tradePrice,
501
- size: t.tradeSize,
502
- volume: t.tradeSize,
503
- side: t.aggressor === 1 ? 'BUY' : 'SELL',
504
- lastTradeSide: t.aggressor === 1 ? 'BUY' : 'SELL',
505
- timestamp: (t.ssboe || 0) * 1000 + Math.floor((t.usecs || 0) / 1000),
506
- };
507
-
508
- this.emit('tick', trade);
509
- this.emit('trade', trade);
510
- }
511
-
512
- /**
513
- * Handle incoming quote data
514
- * @private
515
- */
516
- _handleQuote(data) {
517
- const q = decodeBestBidOffer(data);
518
-
519
- if (!q.symbol) return;
520
-
521
- this.quoteCount++;
522
-
523
- const quote = {
524
- symbol: q.symbol,
525
- exchange: q.exchange || 'CME',
526
- bid: q.bidPrice || 0,
527
- bidPrice: q.bidPrice || 0,
528
- bidSize: q.bidSize || 0,
529
- ask: q.askPrice || 0,
530
- askPrice: q.askPrice || 0,
531
- askSize: q.askSize || 0,
532
- timestamp: (q.ssboe || 0) * 1000 + Math.floor((q.usecs || 0) / 1000),
533
- };
534
-
535
- // Emit as tick with mid price
536
- if (quote.bid && quote.ask) {
537
- const tick = {
538
- ...quote,
539
- price: (quote.bid + quote.ask) / 2,
540
- lastPrice: (quote.bid + quote.ask) / 2,
541
- };
542
- this.emit('tick', tick);
543
- }
544
-
545
- this.emit('quote', quote);
546
- }
547
- }
548
-
549
- module.exports = { RithmicMarketDataFeed };
@@ -1,146 +0,0 @@
1
- /**
2
- * @fileoverview Rithmic Specifications and Configurations
3
- * CME Contract Specifications and PropFirm configurations
4
- *
5
- * NO FAKE DATA - These are official exchange constants
6
- */
7
-
8
- const { RITHMIC_ENDPOINTS, RITHMIC_SYSTEMS } = require('./constants');
9
-
10
- /**
11
- * CME Contract Specifications - Official exchange tick sizes, values, and names
12
- * These are technical constants defined by the exchange, not market data.
13
- * Source: CME Group contract specifications
14
- */
15
- const CME_CONTRACT_SPECS = {
16
- // E-mini Index Futures (CME)
17
- ES: { tickSize: 0.25, tickValue: 12.50, name: 'E-mini S&P 500' },
18
- NQ: { tickSize: 0.25, tickValue: 5.00, name: 'E-mini NASDAQ-100' },
19
- RTY: { tickSize: 0.10, tickValue: 5.00, name: 'E-mini Russell 2000' },
20
- YM: { tickSize: 1.00, tickValue: 5.00, name: 'E-mini Dow' },
21
-
22
- // Micro Index Futures (CME)
23
- MES: { tickSize: 0.25, tickValue: 1.25, name: 'Micro E-mini S&P 500' },
24
- MNQ: { tickSize: 0.25, tickValue: 0.50, name: 'Micro E-mini NASDAQ-100' },
25
- M2K: { tickSize: 0.10, tickValue: 0.50, name: 'Micro E-mini Russell 2000' },
26
- MYM: { tickSize: 1.00, tickValue: 0.50, name: 'Micro E-mini Dow' },
27
-
28
- // Energy Futures (NYMEX)
29
- CL: { tickSize: 0.01, tickValue: 10.00, name: 'Crude Oil' },
30
- QM: { tickSize: 0.025, tickValue: 12.50, name: 'E-mini Crude Oil' },
31
- MCL: { tickSize: 0.01, tickValue: 1.00, name: 'Micro Crude Oil' },
32
- NG: { tickSize: 0.001, tickValue: 10.00, name: 'Natural Gas' },
33
- QG: { tickSize: 0.005, tickValue: 12.50, name: 'E-mini Natural Gas' },
34
-
35
- // Metal Futures (COMEX)
36
- GC: { tickSize: 0.10, tickValue: 10.00, name: 'Gold' },
37
- MGC: { tickSize: 0.10, tickValue: 1.00, name: 'Micro Gold' },
38
- SI: { tickSize: 0.005, tickValue: 25.00, name: 'Silver' },
39
- SIL: { tickSize: 0.005, tickValue: 2.50, name: '1000oz Silver' },
40
- HG: { tickSize: 0.0005, tickValue: 12.50, name: 'Copper' },
41
- MHG: { tickSize: 0.0005, tickValue: 1.25, name: 'Micro Copper' },
42
-
43
- // Treasury Futures (CBOT)
44
- ZB: { tickSize: 0.03125, tickValue: 31.25, name: '30-Year T-Bond' },
45
- ZN: { tickSize: 0.015625, tickValue: 15.625, name: '10-Year T-Note' },
46
- ZF: { tickSize: 0.0078125, tickValue: 7.8125, name: '5-Year T-Note' },
47
- ZT: { tickSize: 0.0078125, tickValue: 15.625, name: '2-Year T-Note' },
48
-
49
- // Agricultural Futures (CBOT)
50
- ZC: { tickSize: 0.25, tickValue: 12.50, name: 'Corn' },
51
- ZS: { tickSize: 0.25, tickValue: 12.50, name: 'Soybeans' },
52
- ZW: { tickSize: 0.25, tickValue: 12.50, name: 'Wheat' },
53
- ZL: { tickSize: 0.01, tickValue: 6.00, name: 'Soybean Oil' },
54
- ZM: { tickSize: 0.10, tickValue: 10.00, name: 'Soybean Meal' },
55
-
56
- // Currency Futures (CME)
57
- '6E': { tickSize: 0.00005, tickValue: 6.25, name: 'Euro FX' },
58
- '6J': { tickSize: 0.0000005, tickValue: 6.25, name: 'Japanese Yen' },
59
- '6B': { tickSize: 0.0001, tickValue: 6.25, name: 'British Pound' },
60
- '6A': { tickSize: 0.0001, tickValue: 10.00, name: 'Australian Dollar' },
61
- '6C': { tickSize: 0.00005, tickValue: 5.00, name: 'Canadian Dollar' },
62
- '6M': { tickSize: 0.0001, tickValue: 5.00, name: 'Mexican Peso' },
63
-
64
- // Nikkei (CME)
65
- NKD: { tickSize: 5.0, tickValue: 25.00, name: 'Nikkei 225' },
66
-
67
- // VIX Futures (CFE)
68
- VX: { tickSize: 0.05, tickValue: 50.00, name: 'VIX Futures' },
69
- };
70
-
71
- /**
72
- * PropFirm configurations
73
- */
74
- const PROPFIRM_CONFIGS = {
75
- apex: { name: 'Apex Trader Funding', systemName: 'Apex', gateway: RITHMIC_ENDPOINTS.CHICAGO },
76
- apex_rithmic: { name: 'Apex Trader Funding', systemName: 'Apex', gateway: RITHMIC_ENDPOINTS.CHICAGO },
77
- topstep_r: { name: 'Topstep (Rithmic)', systemName: RITHMIC_SYSTEMS.TOPSTEP, gateway: RITHMIC_ENDPOINTS.CHICAGO },
78
- bulenox_r: { name: 'Bulenox (Rithmic)', systemName: RITHMIC_SYSTEMS.BULENOX, gateway: RITHMIC_ENDPOINTS.CHICAGO },
79
- earn2trade: { name: 'Earn2Trade', systemName: RITHMIC_SYSTEMS.EARN_2_TRADE, gateway: RITHMIC_ENDPOINTS.CHICAGO },
80
- mescapital: { name: 'MES Capital', systemName: RITHMIC_SYSTEMS.MES_CAPITAL, gateway: RITHMIC_ENDPOINTS.CHICAGO },
81
- tradefundrr: { name: 'TradeFundrr', systemName: RITHMIC_SYSTEMS.TRADEFUNDRR, gateway: RITHMIC_ENDPOINTS.CHICAGO },
82
- thetradingpit: { name: 'The Trading Pit', systemName: RITHMIC_SYSTEMS.THE_TRADING_PIT, gateway: RITHMIC_ENDPOINTS.CHICAGO },
83
- fundedfutures: { name: 'Funded Futures Network', systemName: RITHMIC_SYSTEMS.FUNDED_FUTURES_NETWORK, gateway: RITHMIC_ENDPOINTS.CHICAGO },
84
- propshop: { name: 'PropShop Trader', systemName: RITHMIC_SYSTEMS.PROPSHOP_TRADER, gateway: RITHMIC_ENDPOINTS.CHICAGO },
85
- '4proptrader': { name: '4PropTrader', systemName: RITHMIC_SYSTEMS.FOUR_PROP_TRADER, gateway: RITHMIC_ENDPOINTS.CHICAGO },
86
- daytraders: { name: 'DayTraders.com', systemName: RITHMIC_SYSTEMS.DAY_TRADERS, gateway: RITHMIC_ENDPOINTS.CHICAGO },
87
- '10xfutures': { name: '10X Futures', systemName: RITHMIC_SYSTEMS.TEN_X_FUTURES, gateway: RITHMIC_ENDPOINTS.CHICAGO },
88
- lucidtrading: { name: 'Lucid Trading', systemName: RITHMIC_SYSTEMS.LUCID_TRADING, gateway: RITHMIC_ENDPOINTS.CHICAGO },
89
- thrivetrading: { name: 'Thrive Trading', systemName: RITHMIC_SYSTEMS.THRIVE_TRADING, gateway: RITHMIC_ENDPOINTS.CHICAGO },
90
- legendstrading: { name: 'Legends Trading', systemName: RITHMIC_SYSTEMS.LEGENDS_TRADING, gateway: RITHMIC_ENDPOINTS.CHICAGO },
91
- };
92
-
93
- /**
94
- * Get tick multiplier for P&L calculation
95
- * @param {string} symbol - Trading symbol
96
- * @returns {number} Multiplier for P&L calculation
97
- */
98
- const getTickMultiplier = (symbol) => {
99
- const sym = (symbol || '').toUpperCase();
100
- if (sym.startsWith('ES')) return 50; // E-mini S&P 500: $50 per point
101
- if (sym.startsWith('NQ')) return 20; // E-mini Nasdaq: $20 per point
102
- if (sym.startsWith('YM')) return 5; // E-mini Dow: $5 per point
103
- if (sym.startsWith('RTY')) return 50; // E-mini Russell: $50 per point
104
- if (sym.startsWith('MES')) return 5; // Micro E-mini S&P: $5 per point
105
- if (sym.startsWith('MNQ')) return 2; // Micro E-mini Nasdaq: $2 per point
106
- if (sym.startsWith('GC')) return 100; // Gold: $100 per point
107
- if (sym.startsWith('SI')) return 5000; // Silver: $5000 per point
108
- if (sym.startsWith('CL')) return 1000; // Crude Oil: $1000 per point
109
- if (sym.startsWith('NG')) return 10000; // Natural Gas: $10000 per point
110
- if (sym.startsWith('ZB') || sym.startsWith('ZN')) return 1000; // Bonds
111
- if (sym.startsWith('6E')) return 125000; // Euro FX
112
- if (sym.startsWith('6J')) return 12500000; // Japanese Yen
113
- return 1; // Default
114
- };
115
-
116
- /**
117
- * Check market hours
118
- * @returns {{isOpen: boolean, message: string}}
119
- */
120
- const checkMarketHours = () => {
121
- const now = new Date();
122
- const utcDay = now.getUTCDay();
123
- const utcHour = now.getUTCHours();
124
-
125
- const isDST = now.getTimezoneOffset() < Math.max(
126
- new Date(now.getFullYear(), 0, 1).getTimezoneOffset(),
127
- new Date(now.getFullYear(), 6, 1).getTimezoneOffset()
128
- );
129
- const ctOffset = isDST ? 5 : 6;
130
- const ctHour = (utcHour - ctOffset + 24) % 24;
131
- const ctDay = utcHour < ctOffset ? (utcDay + 6) % 7 : utcDay;
132
-
133
- if (ctDay === 6) return { isOpen: false, message: 'Market closed (Saturday)' };
134
- if (ctDay === 0 && ctHour < 17) return { isOpen: false, message: 'Market opens Sunday 5:00 PM CT' };
135
- if (ctDay === 5 && ctHour >= 16) return { isOpen: false, message: 'Market closed (Friday after 4PM CT)' };
136
- if (ctHour === 16 && ctDay >= 1 && ctDay <= 4) return { isOpen: false, message: 'Daily maintenance (4:00-5:00 PM CT)' };
137
-
138
- return { isOpen: true, message: 'Market is open' };
139
- };
140
-
141
- module.exports = {
142
- CME_CONTRACT_SPECS,
143
- PROPFIRM_CONFIGS,
144
- getTickMultiplier,
145
- checkMarketHours,
146
- };