hedgequantx 2.6.161 → 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/menus/ai-agent-connect.js +181 -0
- package/src/menus/ai-agent-models.js +219 -0
- package/src/menus/ai-agent-oauth.js +292 -0
- package/src/menus/ai-agent-ui.js +141 -0
- package/src/menus/ai-agent.js +88 -1489
- package/src/pages/algo/copy-engine.js +449 -0
- package/src/pages/algo/copy-trading.js +11 -543
- package/src/pages/algo/smart-logs-data.js +218 -0
- package/src/pages/algo/smart-logs.js +9 -214
- package/src/pages/algo/ui-constants.js +144 -0
- package/src/pages/algo/ui-summary.js +184 -0
- package/src/pages/algo/ui.js +42 -526
- package/src/pages/stats-calculations.js +191 -0
- package/src/pages/stats-ui.js +381 -0
- package/src/pages/stats.js +14 -507
- package/src/services/ai/client-analysis.js +194 -0
- package/src/services/ai/client-models.js +333 -0
- package/src/services/ai/client.js +6 -489
- package/src/services/ai/index.js +2 -257
- package/src/services/ai/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/ai/proxy-install.js +249 -0
- package/src/services/ai/proxy-manager.js +29 -411
- package/src/services/ai/proxy-remote.js +161 -0
- package/src/services/ai/supervisor-optimize.js +215 -0
- package/src/services/ai/supervisor-sync.js +178 -0
- package/src/services/ai/supervisor.js +50 -515
- package/src/services/ai/validation.js +250 -0
- package/src/services/hqx-server-events.js +110 -0
- package/src/services/hqx-server-handlers.js +217 -0
- package/src/services/hqx-server-latency.js +136 -0
- package/src/services/hqx-server.js +51 -403
- package/src/services/position-constants.js +28 -0
- package/src/services/position-exit-logic.js +174 -0
- package/src/services/position-manager.js +90 -629
- package/src/services/position-momentum.js +206 -0
- package/src/services/projectx/accounts.js +142 -0
- package/src/services/projectx/index.js +40 -289
- package/src/services/projectx/trading.js +180 -0
- package/src/services/rithmic/contracts.js +218 -0
- package/src/services/rithmic/handlers.js +2 -208
- package/src/services/rithmic/index.js +28 -712
- package/src/services/rithmic/latency-tracker.js +182 -0
- 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/rithmic/specs.js +146 -0
- package/src/services/rithmic/trade-history.js +254 -0
- 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
|
@@ -6,15 +6,12 @@
|
|
|
6
6
|
const protobuf = require('protobufjs');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const { PROTO_FILES } = require('./constants');
|
|
9
|
+
const {
|
|
10
|
+
readVarint, readLengthDelimited, skipField,
|
|
11
|
+
decodeAccountPnL, decodeInstrumentPnL, decodeProductCodes, decodeFrontMonthContract
|
|
12
|
+
} = require('./proto-decoders');
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
// Pre-allocated buffer pool for zero-allocation hot path
|
|
12
|
-
// Avoids GC pressure during high-frequency trading
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* High-performance buffer pool for zero-allocation encoding
|
|
16
|
-
* Uses ring buffer pattern for O(1) acquire/release
|
|
17
|
-
*/
|
|
14
|
+
/** High-performance buffer pool for zero-allocation encoding */
|
|
18
15
|
class BufferPool {
|
|
19
16
|
constructor(poolSize = 16, bufferSize = 512) {
|
|
20
17
|
this._pool = new Array(poolSize);
|
|
@@ -24,370 +21,32 @@ class BufferPool {
|
|
|
24
21
|
this._head = 0;
|
|
25
22
|
this._tail = 0;
|
|
26
23
|
this._count = poolSize;
|
|
27
|
-
|
|
28
|
-
// Pre-allocate all buffers
|
|
29
24
|
for (let i = 0; i < poolSize; i++) {
|
|
30
25
|
this._pool[i] = Buffer.allocUnsafe(bufferSize);
|
|
31
26
|
this._available[i] = i;
|
|
32
27
|
}
|
|
33
28
|
}
|
|
34
29
|
|
|
35
|
-
/**
|
|
36
|
-
* Acquire a buffer from the pool
|
|
37
|
-
* @returns {Buffer|null} Buffer or null if pool exhausted
|
|
38
|
-
*/
|
|
39
30
|
acquire() {
|
|
40
|
-
if (this._count === 0)
|
|
41
|
-
// Pool exhausted - allocate new (fallback)
|
|
42
|
-
return Buffer.allocUnsafe(this._bufferSize);
|
|
43
|
-
}
|
|
31
|
+
if (this._count === 0) return Buffer.allocUnsafe(this._bufferSize);
|
|
44
32
|
const idx = this._available[this._head];
|
|
45
33
|
this._head = (this._head + 1) % this._size;
|
|
46
34
|
this._count--;
|
|
47
35
|
return this._pool[idx];
|
|
48
36
|
}
|
|
49
37
|
|
|
50
|
-
/**
|
|
51
|
-
* Release a buffer back to pool
|
|
52
|
-
* Only releases buffers that belong to the pool
|
|
53
|
-
* @param {Buffer} buffer
|
|
54
|
-
*/
|
|
55
38
|
release(buffer) {
|
|
56
|
-
// Find if this buffer is from our pool
|
|
57
39
|
const idx = this._pool.indexOf(buffer);
|
|
58
40
|
if (idx !== -1 && this._count < this._size) {
|
|
59
41
|
this._available[this._tail] = idx;
|
|
60
42
|
this._tail = (this._tail + 1) % this._size;
|
|
61
43
|
this._count++;
|
|
62
44
|
}
|
|
63
|
-
// If not from pool, let GC handle it
|
|
64
45
|
}
|
|
65
46
|
|
|
66
|
-
/**
|
|
67
|
-
* Get pool stats
|
|
68
|
-
*/
|
|
69
47
|
getStats() {
|
|
70
|
-
return {
|
|
71
|
-
size: this._size,
|
|
72
|
-
available: this._count,
|
|
73
|
-
bufferSize: this._bufferSize,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// PnL field IDs (Rithmic uses very large field IDs)
|
|
79
|
-
const PNL_FIELDS = {
|
|
80
|
-
TEMPLATE_ID: 154467,
|
|
81
|
-
IS_SNAPSHOT: 110121,
|
|
82
|
-
FCM_ID: 154013,
|
|
83
|
-
IB_ID: 154014,
|
|
84
|
-
ACCOUNT_ID: 154008,
|
|
85
|
-
ACCOUNT_BALANCE: 156970,
|
|
86
|
-
CASH_ON_HAND: 156971,
|
|
87
|
-
MARGIN_BALANCE: 156977,
|
|
88
|
-
MIN_ACCOUNT_BALANCE: 156968,
|
|
89
|
-
OPEN_POSITION_PNL: 156961,
|
|
90
|
-
CLOSED_POSITION_PNL: 156963,
|
|
91
|
-
DAY_PNL: 157956,
|
|
92
|
-
DAY_OPEN_PNL: 157954,
|
|
93
|
-
DAY_CLOSED_PNL: 157955,
|
|
94
|
-
AVAILABLE_BUYING_POWER: 157015,
|
|
95
|
-
SSBOE: 150100,
|
|
96
|
-
USECS: 150101,
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
// Symbol/Contract field IDs (ResponseProductCodes, ResponseFrontMonthContract)
|
|
100
|
-
const SYMBOL_FIELDS = {
|
|
101
|
-
TEMPLATE_ID: 154467,
|
|
102
|
-
RP_CODE: 132766,
|
|
103
|
-
EXCHANGE: 110101,
|
|
104
|
-
PRODUCT_CODE: 110102, // Base symbol (ES, NQ, MNQ)
|
|
105
|
-
PRODUCT_NAME: 110103, // Product name
|
|
106
|
-
SYMBOL: 110100, // Full contract symbol (ESH26)
|
|
107
|
-
TRADING_SYMBOL: 157095, // Trading symbol
|
|
108
|
-
DESCRIPTION: 110114, // Contract description
|
|
109
|
-
USER_MSG: 132760,
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
// Instrument PnL Position Update field IDs
|
|
113
|
-
const INSTRUMENT_PNL_FIELDS = {
|
|
114
|
-
TEMPLATE_ID: 154467,
|
|
115
|
-
IS_SNAPSHOT: 110121,
|
|
116
|
-
FCM_ID: 154013,
|
|
117
|
-
IB_ID: 154014,
|
|
118
|
-
ACCOUNT_ID: 154008,
|
|
119
|
-
SYMBOL: 110100,
|
|
120
|
-
EXCHANGE: 110101,
|
|
121
|
-
PRODUCT_CODE: 100749,
|
|
122
|
-
INSTRUMENT_TYPE: 110116,
|
|
123
|
-
FILL_BUY_QTY: 154041,
|
|
124
|
-
FILL_SELL_QTY: 154042,
|
|
125
|
-
ORDER_BUY_QTY: 154037,
|
|
126
|
-
ORDER_SELL_QTY: 154038,
|
|
127
|
-
BUY_QTY: 154260,
|
|
128
|
-
SELL_QTY: 154261,
|
|
129
|
-
AVG_OPEN_FILL_PRICE: 154434,
|
|
130
|
-
DAY_OPEN_PNL: 157954,
|
|
131
|
-
DAY_CLOSED_PNL: 157955,
|
|
132
|
-
DAY_PNL: 157956,
|
|
133
|
-
OPEN_POSITION_PNL: 156961,
|
|
134
|
-
OPEN_POSITION_QUANTITY: 156962,
|
|
135
|
-
CLOSED_POSITION_PNL: 156963,
|
|
136
|
-
CLOSED_POSITION_QUANTITY: 156964,
|
|
137
|
-
NET_QUANTITY: 156967,
|
|
138
|
-
SSBOE: 150100,
|
|
139
|
-
USECS: 150101,
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Read a varint from buffer
|
|
144
|
-
*/
|
|
145
|
-
function readVarint(buffer, offset) {
|
|
146
|
-
let result = BigInt(0);
|
|
147
|
-
let shift = BigInt(0);
|
|
148
|
-
let pos = offset;
|
|
149
|
-
|
|
150
|
-
while (pos < buffer.length) {
|
|
151
|
-
const byte = buffer[pos++];
|
|
152
|
-
result |= BigInt(byte & 0x7f) << shift;
|
|
153
|
-
if ((byte & 0x80) === 0) {
|
|
154
|
-
return [Number(result), pos];
|
|
155
|
-
}
|
|
156
|
-
shift += BigInt(7);
|
|
157
|
-
if (shift > BigInt(63)) {
|
|
158
|
-
throw new Error('Varint too large');
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
throw new Error('Incomplete varint');
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Read a length-delimited field (string/bytes)
|
|
166
|
-
*/
|
|
167
|
-
function readLengthDelimited(buffer, offset) {
|
|
168
|
-
const [length, newOffset] = readVarint(buffer, offset);
|
|
169
|
-
const value = buffer.slice(newOffset, newOffset + length).toString('utf8');
|
|
170
|
-
return [value, newOffset + length];
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Skip a field based on wire type
|
|
175
|
-
*/
|
|
176
|
-
function skipField(buffer, offset, wireType) {
|
|
177
|
-
switch (wireType) {
|
|
178
|
-
case 0: // Varint
|
|
179
|
-
const [, newOffset] = readVarint(buffer, offset);
|
|
180
|
-
return newOffset;
|
|
181
|
-
case 1: // 64-bit
|
|
182
|
-
return offset + 8;
|
|
183
|
-
case 2: // Length-delimited
|
|
184
|
-
const [length, lenOffset] = readVarint(buffer, offset);
|
|
185
|
-
return lenOffset + length;
|
|
186
|
-
case 5: // 32-bit
|
|
187
|
-
return offset + 4;
|
|
188
|
-
default:
|
|
189
|
-
throw new Error(`Unknown wire type: ${wireType}`);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Manually decode AccountPnL from raw bytes
|
|
195
|
-
*/
|
|
196
|
-
function decodeAccountPnL(buffer) {
|
|
197
|
-
const result = {};
|
|
198
|
-
let offset = 0;
|
|
199
|
-
|
|
200
|
-
while (offset < buffer.length) {
|
|
201
|
-
try {
|
|
202
|
-
const [tag, tagOffset] = readVarint(buffer, offset);
|
|
203
|
-
const wireType = tag & 0x7;
|
|
204
|
-
const fieldNumber = tag >>> 3;
|
|
205
|
-
offset = tagOffset;
|
|
206
|
-
|
|
207
|
-
switch (fieldNumber) {
|
|
208
|
-
case PNL_FIELDS.TEMPLATE_ID:
|
|
209
|
-
[result.templateId, offset] = readVarint(buffer, offset);
|
|
210
|
-
break;
|
|
211
|
-
case PNL_FIELDS.IS_SNAPSHOT:
|
|
212
|
-
const [isSnap, snapOffset] = readVarint(buffer, offset);
|
|
213
|
-
result.isSnapshot = isSnap !== 0;
|
|
214
|
-
offset = snapOffset;
|
|
215
|
-
break;
|
|
216
|
-
case PNL_FIELDS.FCM_ID:
|
|
217
|
-
[result.fcmId, offset] = readLengthDelimited(buffer, offset);
|
|
218
|
-
break;
|
|
219
|
-
case PNL_FIELDS.IB_ID:
|
|
220
|
-
[result.ibId, offset] = readLengthDelimited(buffer, offset);
|
|
221
|
-
break;
|
|
222
|
-
case PNL_FIELDS.ACCOUNT_ID:
|
|
223
|
-
[result.accountId, offset] = readLengthDelimited(buffer, offset);
|
|
224
|
-
break;
|
|
225
|
-
case PNL_FIELDS.ACCOUNT_BALANCE:
|
|
226
|
-
[result.accountBalance, offset] = readLengthDelimited(buffer, offset);
|
|
227
|
-
break;
|
|
228
|
-
case PNL_FIELDS.CASH_ON_HAND:
|
|
229
|
-
[result.cashOnHand, offset] = readLengthDelimited(buffer, offset);
|
|
230
|
-
break;
|
|
231
|
-
case PNL_FIELDS.MARGIN_BALANCE:
|
|
232
|
-
[result.marginBalance, offset] = readLengthDelimited(buffer, offset);
|
|
233
|
-
break;
|
|
234
|
-
case PNL_FIELDS.MIN_ACCOUNT_BALANCE:
|
|
235
|
-
[result.minAccountBalance, offset] = readLengthDelimited(buffer, offset);
|
|
236
|
-
break;
|
|
237
|
-
case PNL_FIELDS.OPEN_POSITION_PNL:
|
|
238
|
-
[result.openPositionPnl, offset] = readLengthDelimited(buffer, offset);
|
|
239
|
-
break;
|
|
240
|
-
case PNL_FIELDS.CLOSED_POSITION_PNL:
|
|
241
|
-
[result.closedPositionPnl, offset] = readLengthDelimited(buffer, offset);
|
|
242
|
-
break;
|
|
243
|
-
case PNL_FIELDS.DAY_PNL:
|
|
244
|
-
[result.dayPnl, offset] = readLengthDelimited(buffer, offset);
|
|
245
|
-
break;
|
|
246
|
-
case PNL_FIELDS.DAY_OPEN_PNL:
|
|
247
|
-
[result.dayOpenPnl, offset] = readLengthDelimited(buffer, offset);
|
|
248
|
-
break;
|
|
249
|
-
case PNL_FIELDS.DAY_CLOSED_PNL:
|
|
250
|
-
[result.dayClosedPnl, offset] = readLengthDelimited(buffer, offset);
|
|
251
|
-
break;
|
|
252
|
-
case PNL_FIELDS.AVAILABLE_BUYING_POWER:
|
|
253
|
-
[result.availableBuyingPower, offset] = readLengthDelimited(buffer, offset);
|
|
254
|
-
break;
|
|
255
|
-
case PNL_FIELDS.SSBOE:
|
|
256
|
-
[result.ssboe, offset] = readVarint(buffer, offset);
|
|
257
|
-
break;
|
|
258
|
-
case PNL_FIELDS.USECS:
|
|
259
|
-
[result.usecs, offset] = readVarint(buffer, offset);
|
|
260
|
-
break;
|
|
261
|
-
default:
|
|
262
|
-
offset = skipField(buffer, offset, wireType);
|
|
263
|
-
}
|
|
264
|
-
} catch (error) {
|
|
265
|
-
break;
|
|
266
|
-
}
|
|
48
|
+
return { size: this._size, available: this._count, bufferSize: this._bufferSize };
|
|
267
49
|
}
|
|
268
|
-
|
|
269
|
-
return result;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Manually decode InstrumentPnLPositionUpdate from raw bytes
|
|
274
|
-
*/
|
|
275
|
-
function decodeInstrumentPnL(buffer) {
|
|
276
|
-
const result = {};
|
|
277
|
-
let offset = 0;
|
|
278
|
-
|
|
279
|
-
while (offset < buffer.length) {
|
|
280
|
-
try {
|
|
281
|
-
const [tag, tagOffset] = readVarint(buffer, offset);
|
|
282
|
-
const wireType = tag & 0x7;
|
|
283
|
-
const fieldNumber = tag >>> 3;
|
|
284
|
-
offset = tagOffset;
|
|
285
|
-
|
|
286
|
-
switch (fieldNumber) {
|
|
287
|
-
case INSTRUMENT_PNL_FIELDS.TEMPLATE_ID:
|
|
288
|
-
[result.templateId, offset] = readVarint(buffer, offset);
|
|
289
|
-
break;
|
|
290
|
-
case INSTRUMENT_PNL_FIELDS.IS_SNAPSHOT:
|
|
291
|
-
const [isSnap, snapOffset] = readVarint(buffer, offset);
|
|
292
|
-
result.isSnapshot = isSnap !== 0;
|
|
293
|
-
offset = snapOffset;
|
|
294
|
-
break;
|
|
295
|
-
case INSTRUMENT_PNL_FIELDS.FCM_ID:
|
|
296
|
-
[result.fcmId, offset] = readLengthDelimited(buffer, offset);
|
|
297
|
-
break;
|
|
298
|
-
case INSTRUMENT_PNL_FIELDS.IB_ID:
|
|
299
|
-
[result.ibId, offset] = readLengthDelimited(buffer, offset);
|
|
300
|
-
break;
|
|
301
|
-
case INSTRUMENT_PNL_FIELDS.ACCOUNT_ID:
|
|
302
|
-
[result.accountId, offset] = readLengthDelimited(buffer, offset);
|
|
303
|
-
break;
|
|
304
|
-
case INSTRUMENT_PNL_FIELDS.SYMBOL:
|
|
305
|
-
[result.symbol, offset] = readLengthDelimited(buffer, offset);
|
|
306
|
-
break;
|
|
307
|
-
case INSTRUMENT_PNL_FIELDS.EXCHANGE:
|
|
308
|
-
[result.exchange, offset] = readLengthDelimited(buffer, offset);
|
|
309
|
-
break;
|
|
310
|
-
case INSTRUMENT_PNL_FIELDS.PRODUCT_CODE:
|
|
311
|
-
[result.productCode, offset] = readLengthDelimited(buffer, offset);
|
|
312
|
-
break;
|
|
313
|
-
case INSTRUMENT_PNL_FIELDS.BUY_QTY:
|
|
314
|
-
[result.buyQty, offset] = readVarint(buffer, offset);
|
|
315
|
-
break;
|
|
316
|
-
case INSTRUMENT_PNL_FIELDS.SELL_QTY:
|
|
317
|
-
[result.sellQty, offset] = readVarint(buffer, offset);
|
|
318
|
-
break;
|
|
319
|
-
case INSTRUMENT_PNL_FIELDS.FILL_BUY_QTY:
|
|
320
|
-
[result.fillBuyQty, offset] = readVarint(buffer, offset);
|
|
321
|
-
break;
|
|
322
|
-
case INSTRUMENT_PNL_FIELDS.FILL_SELL_QTY:
|
|
323
|
-
[result.fillSellQty, offset] = readVarint(buffer, offset);
|
|
324
|
-
break;
|
|
325
|
-
case INSTRUMENT_PNL_FIELDS.NET_QUANTITY:
|
|
326
|
-
[result.netQuantity, offset] = readVarint(buffer, offset);
|
|
327
|
-
break;
|
|
328
|
-
case INSTRUMENT_PNL_FIELDS.OPEN_POSITION_QUANTITY:
|
|
329
|
-
[result.openPositionQuantity, offset] = readVarint(buffer, offset);
|
|
330
|
-
break;
|
|
331
|
-
case INSTRUMENT_PNL_FIELDS.AVG_OPEN_FILL_PRICE:
|
|
332
|
-
// Double is 64-bit fixed
|
|
333
|
-
if (wireType === 1) {
|
|
334
|
-
result.avgOpenFillPrice = buffer.readDoubleLE(offset);
|
|
335
|
-
offset += 8;
|
|
336
|
-
} else {
|
|
337
|
-
offset = skipField(buffer, offset, wireType);
|
|
338
|
-
}
|
|
339
|
-
break;
|
|
340
|
-
case INSTRUMENT_PNL_FIELDS.OPEN_POSITION_PNL:
|
|
341
|
-
[result.openPositionPnl, offset] = readLengthDelimited(buffer, offset);
|
|
342
|
-
break;
|
|
343
|
-
case INSTRUMENT_PNL_FIELDS.CLOSED_POSITION_PNL:
|
|
344
|
-
[result.closedPositionPnl, offset] = readLengthDelimited(buffer, offset);
|
|
345
|
-
break;
|
|
346
|
-
case INSTRUMENT_PNL_FIELDS.DAY_PNL:
|
|
347
|
-
// DAY_PNL is a double (wireType 1 = 64-bit fixed)
|
|
348
|
-
if (wireType === 1) {
|
|
349
|
-
result.dayPnl = buffer.readDoubleLE(offset);
|
|
350
|
-
offset += 8;
|
|
351
|
-
} else {
|
|
352
|
-
// Fallback: try string
|
|
353
|
-
[result.dayPnl, offset] = readLengthDelimited(buffer, offset);
|
|
354
|
-
}
|
|
355
|
-
break;
|
|
356
|
-
case INSTRUMENT_PNL_FIELDS.DAY_OPEN_PNL:
|
|
357
|
-
// DAY_OPEN_PNL is a double (wireType 1 = 64-bit fixed)
|
|
358
|
-
if (wireType === 1) {
|
|
359
|
-
result.dayOpenPnl = buffer.readDoubleLE(offset);
|
|
360
|
-
offset += 8;
|
|
361
|
-
} else {
|
|
362
|
-
// Fallback: try string
|
|
363
|
-
[result.dayOpenPnl, offset] = readLengthDelimited(buffer, offset);
|
|
364
|
-
}
|
|
365
|
-
break;
|
|
366
|
-
case INSTRUMENT_PNL_FIELDS.DAY_CLOSED_PNL:
|
|
367
|
-
// DAY_CLOSED_PNL is a double (wireType 1 = 64-bit fixed)
|
|
368
|
-
if (wireType === 1) {
|
|
369
|
-
result.dayClosedPnl = buffer.readDoubleLE(offset);
|
|
370
|
-
offset += 8;
|
|
371
|
-
} else {
|
|
372
|
-
// Fallback: try string
|
|
373
|
-
[result.dayClosedPnl, offset] = readLengthDelimited(buffer, offset);
|
|
374
|
-
}
|
|
375
|
-
break;
|
|
376
|
-
case INSTRUMENT_PNL_FIELDS.SSBOE:
|
|
377
|
-
[result.ssboe, offset] = readVarint(buffer, offset);
|
|
378
|
-
break;
|
|
379
|
-
case INSTRUMENT_PNL_FIELDS.USECS:
|
|
380
|
-
[result.usecs, offset] = readVarint(buffer, offset);
|
|
381
|
-
break;
|
|
382
|
-
default:
|
|
383
|
-
offset = skipField(buffer, offset, wireType);
|
|
384
|
-
}
|
|
385
|
-
} catch (error) {
|
|
386
|
-
break;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
return result;
|
|
391
50
|
}
|
|
392
51
|
|
|
393
52
|
/**
|
|
@@ -580,101 +239,6 @@ class ProtobufHandler {
|
|
|
580
239
|
}
|
|
581
240
|
}
|
|
582
241
|
|
|
583
|
-
/**
|
|
584
|
-
* Decode ResponseProductCodes (template 112) - list of available symbols
|
|
585
|
-
*/
|
|
586
|
-
function decodeProductCodes(buffer) {
|
|
587
|
-
const result = { rpCode: [] };
|
|
588
|
-
let offset = 0;
|
|
589
|
-
|
|
590
|
-
while (offset < buffer.length) {
|
|
591
|
-
try {
|
|
592
|
-
const [tag, tagOffset] = readVarint(buffer, offset);
|
|
593
|
-
const wireType = tag & 0x7;
|
|
594
|
-
const fieldNumber = tag >>> 3;
|
|
595
|
-
offset = tagOffset;
|
|
596
|
-
|
|
597
|
-
switch (fieldNumber) {
|
|
598
|
-
case SYMBOL_FIELDS.TEMPLATE_ID:
|
|
599
|
-
[result.templateId, offset] = readVarint(buffer, offset);
|
|
600
|
-
break;
|
|
601
|
-
case SYMBOL_FIELDS.RP_CODE:
|
|
602
|
-
let rpCode;
|
|
603
|
-
[rpCode, offset] = readLengthDelimited(buffer, offset);
|
|
604
|
-
result.rpCode.push(rpCode);
|
|
605
|
-
break;
|
|
606
|
-
case SYMBOL_FIELDS.EXCHANGE:
|
|
607
|
-
[result.exchange, offset] = readLengthDelimited(buffer, offset);
|
|
608
|
-
break;
|
|
609
|
-
case SYMBOL_FIELDS.PRODUCT_CODE:
|
|
610
|
-
[result.productCode, offset] = readLengthDelimited(buffer, offset);
|
|
611
|
-
break;
|
|
612
|
-
case SYMBOL_FIELDS.PRODUCT_NAME:
|
|
613
|
-
[result.productName, offset] = readLengthDelimited(buffer, offset);
|
|
614
|
-
break;
|
|
615
|
-
case SYMBOL_FIELDS.USER_MSG:
|
|
616
|
-
[result.userMsg, offset] = readLengthDelimited(buffer, offset);
|
|
617
|
-
break;
|
|
618
|
-
default:
|
|
619
|
-
offset = skipField(buffer, offset, wireType);
|
|
620
|
-
}
|
|
621
|
-
} catch (error) {
|
|
622
|
-
break;
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
return result;
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
/**
|
|
630
|
-
* Decode ResponseFrontMonthContract (template 114) - current tradeable contract
|
|
631
|
-
*/
|
|
632
|
-
function decodeFrontMonthContract(buffer) {
|
|
633
|
-
const result = { rpCode: [] };
|
|
634
|
-
let offset = 0;
|
|
635
|
-
|
|
636
|
-
while (offset < buffer.length) {
|
|
637
|
-
try {
|
|
638
|
-
const [tag, tagOffset] = readVarint(buffer, offset);
|
|
639
|
-
const wireType = tag & 0x7;
|
|
640
|
-
const fieldNumber = tag >>> 3;
|
|
641
|
-
offset = tagOffset;
|
|
642
|
-
|
|
643
|
-
switch (fieldNumber) {
|
|
644
|
-
case SYMBOL_FIELDS.TEMPLATE_ID:
|
|
645
|
-
[result.templateId, offset] = readVarint(buffer, offset);
|
|
646
|
-
break;
|
|
647
|
-
case SYMBOL_FIELDS.RP_CODE:
|
|
648
|
-
let rpCode;
|
|
649
|
-
[rpCode, offset] = readLengthDelimited(buffer, offset);
|
|
650
|
-
result.rpCode.push(rpCode);
|
|
651
|
-
break;
|
|
652
|
-
case SYMBOL_FIELDS.SYMBOL:
|
|
653
|
-
[result.symbol, offset] = readLengthDelimited(buffer, offset);
|
|
654
|
-
break;
|
|
655
|
-
case SYMBOL_FIELDS.EXCHANGE:
|
|
656
|
-
[result.exchange, offset] = readLengthDelimited(buffer, offset);
|
|
657
|
-
break;
|
|
658
|
-
case SYMBOL_FIELDS.TRADING_SYMBOL:
|
|
659
|
-
[result.tradingSymbol, offset] = readLengthDelimited(buffer, offset);
|
|
660
|
-
break;
|
|
661
|
-
case SYMBOL_FIELDS.DESCRIPTION:
|
|
662
|
-
[result.description, offset] = readLengthDelimited(buffer, offset);
|
|
663
|
-
break;
|
|
664
|
-
case SYMBOL_FIELDS.USER_MSG:
|
|
665
|
-
[result.userMsg, offset] = readLengthDelimited(buffer, offset);
|
|
666
|
-
break;
|
|
667
|
-
default:
|
|
668
|
-
offset = skipField(buffer, offset, wireType);
|
|
669
|
-
}
|
|
670
|
-
} catch (error) {
|
|
671
|
-
break;
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
return result;
|
|
676
|
-
}
|
|
677
|
-
|
|
678
242
|
// Singleton
|
|
679
243
|
const proto = new ProtobufHandler();
|
|
680
244
|
|
|
@@ -0,0 +1,146 @@
|
|
|
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
|
+
};
|