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.
Files changed (146) 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 +116 -99
  67. package/src/services/rithmic/handlers.js +21 -196
  68. package/src/services/rithmic/index.js +63 -120
  69. package/src/services/rithmic/market.js +31 -0
  70. package/src/services/rithmic/orders.js +5 -111
  71. package/src/services/rithmic/protobuf.js +384 -138
  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/direct-providers.js +0 -323
  106. package/src/services/ai/providers/index.js +0 -62
  107. package/src/services/ai/providers/other-providers.js +0 -104
  108. package/src/services/ai/proxy-install.js +0 -249
  109. package/src/services/ai/proxy-manager.js +0 -494
  110. package/src/services/ai/proxy-remote.js +0 -161
  111. package/src/services/ai/strategy-supervisor.js +0 -1312
  112. package/src/services/ai/supervisor-data.js +0 -195
  113. package/src/services/ai/supervisor-optimize.js +0 -215
  114. package/src/services/ai/supervisor-sync.js +0 -178
  115. package/src/services/ai/supervisor-utils.js +0 -158
  116. package/src/services/ai/supervisor.js +0 -484
  117. package/src/services/ai/validation.js +0 -250
  118. package/src/services/hqx-server-events.js +0 -110
  119. package/src/services/hqx-server-handlers.js +0 -217
  120. package/src/services/hqx-server-latency.js +0 -136
  121. package/src/services/hqx-server.js +0 -403
  122. package/src/services/position-constants.js +0 -28
  123. package/src/services/position-exit-logic.js +0 -174
  124. package/src/services/position-manager.js +0 -438
  125. package/src/services/position-momentum.js +0 -206
  126. package/src/services/projectx/accounts.js +0 -142
  127. package/src/services/projectx/index.js +0 -443
  128. package/src/services/projectx/market.js +0 -172
  129. package/src/services/projectx/stats.js +0 -110
  130. package/src/services/projectx/trading.js +0 -180
  131. package/src/services/rithmic/latency-tracker.js +0 -182
  132. package/src/services/rithmic/market-data-decoders.js +0 -229
  133. package/src/services/rithmic/market-data.js +0 -272
  134. package/src/services/rithmic/orders-fast.js +0 -246
  135. package/src/services/rithmic/proto-decoders.js +0 -403
  136. package/src/services/rithmic/specs.js +0 -146
  137. package/src/services/rithmic/trade-history.js +0 -254
  138. package/src/services/session-history.js +0 -475
  139. package/src/services/strategy/hft-signal-calc.js +0 -147
  140. package/src/services/strategy/hft-tick.js +0 -407
  141. package/src/services/strategy/recovery-math.js +0 -402
  142. package/src/services/tradovate/constants.js +0 -109
  143. package/src/services/tradovate/index.js +0 -392
  144. package/src/services/tradovate/market.js +0 -47
  145. package/src/services/tradovate/orders.js +0 -145
  146. package/src/services/tradovate/websocket.js +0 -97
@@ -6,79 +6,313 @@
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');
13
-
14
- /** High-performance buffer pool for zero-allocation encoding */
15
- class BufferPool {
16
- constructor(poolSize = 16, bufferSize = 512) {
17
- this._pool = new Array(poolSize);
18
- this._available = new Array(poolSize);
19
- this._size = poolSize;
20
- this._bufferSize = bufferSize;
21
- this._head = 0;
22
- this._tail = 0;
23
- this._count = poolSize;
24
- for (let i = 0; i < poolSize; i++) {
25
- this._pool[i] = Buffer.allocUnsafe(bufferSize);
26
- this._available[i] = i;
9
+
10
+ // PnL field IDs (Rithmic uses very large field IDs)
11
+ const PNL_FIELDS = {
12
+ TEMPLATE_ID: 154467,
13
+ IS_SNAPSHOT: 110121,
14
+ FCM_ID: 154013,
15
+ IB_ID: 154014,
16
+ ACCOUNT_ID: 154008,
17
+ ACCOUNT_BALANCE: 156970,
18
+ CASH_ON_HAND: 156971,
19
+ MARGIN_BALANCE: 156977,
20
+ MIN_ACCOUNT_BALANCE: 156968,
21
+ OPEN_POSITION_PNL: 156961,
22
+ CLOSED_POSITION_PNL: 156963,
23
+ DAY_PNL: 157956,
24
+ DAY_OPEN_PNL: 157954,
25
+ DAY_CLOSED_PNL: 157955,
26
+ AVAILABLE_BUYING_POWER: 157015,
27
+ SSBOE: 150100,
28
+ USECS: 150101,
29
+ };
30
+
31
+ // Symbol/Contract field IDs (ResponseProductCodes, ResponseFrontMonthContract)
32
+ const SYMBOL_FIELDS = {
33
+ TEMPLATE_ID: 154467,
34
+ RP_CODE: 132766,
35
+ EXCHANGE: 110101,
36
+ PRODUCT_CODE: 110102, // Base symbol (ES, NQ, MNQ)
37
+ PRODUCT_NAME: 110103, // Product name
38
+ SYMBOL: 110100, // Full contract symbol (ESH26)
39
+ TRADING_SYMBOL: 157095, // Trading symbol
40
+ DESCRIPTION: 110114, // Contract description
41
+ USER_MSG: 132760,
42
+ };
43
+
44
+ // Instrument PnL Position Update field IDs
45
+ const INSTRUMENT_PNL_FIELDS = {
46
+ TEMPLATE_ID: 154467,
47
+ IS_SNAPSHOT: 110121,
48
+ FCM_ID: 154013,
49
+ IB_ID: 154014,
50
+ ACCOUNT_ID: 154008,
51
+ SYMBOL: 110100,
52
+ EXCHANGE: 110101,
53
+ PRODUCT_CODE: 100749,
54
+ INSTRUMENT_TYPE: 110116,
55
+ FILL_BUY_QTY: 154041,
56
+ FILL_SELL_QTY: 154042,
57
+ ORDER_BUY_QTY: 154037,
58
+ ORDER_SELL_QTY: 154038,
59
+ BUY_QTY: 154260,
60
+ SELL_QTY: 154261,
61
+ AVG_OPEN_FILL_PRICE: 154434,
62
+ DAY_OPEN_PNL: 157954,
63
+ DAY_CLOSED_PNL: 157955,
64
+ DAY_PNL: 157956,
65
+ OPEN_POSITION_PNL: 156961,
66
+ OPEN_POSITION_QUANTITY: 156962,
67
+ CLOSED_POSITION_PNL: 156963,
68
+ CLOSED_POSITION_QUANTITY: 156964,
69
+ NET_QUANTITY: 156967,
70
+ SSBOE: 150100,
71
+ USECS: 150101,
72
+ };
73
+
74
+ /**
75
+ * Read a varint from buffer
76
+ */
77
+ function readVarint(buffer, offset) {
78
+ let result = BigInt(0);
79
+ let shift = BigInt(0);
80
+ let pos = offset;
81
+
82
+ while (pos < buffer.length) {
83
+ const byte = buffer[pos++];
84
+ result |= BigInt(byte & 0x7f) << shift;
85
+ if ((byte & 0x80) === 0) {
86
+ return [Number(result), pos];
87
+ }
88
+ shift += BigInt(7);
89
+ if (shift > BigInt(63)) {
90
+ throw new Error('Varint too large');
27
91
  }
28
92
  }
29
-
30
- acquire() {
31
- if (this._count === 0) return Buffer.allocUnsafe(this._bufferSize);
32
- const idx = this._available[this._head];
33
- this._head = (this._head + 1) % this._size;
34
- this._count--;
35
- return this._pool[idx];
93
+ throw new Error('Incomplete varint');
94
+ }
95
+
96
+ /**
97
+ * Read a length-delimited field (string/bytes)
98
+ */
99
+ function readLengthDelimited(buffer, offset) {
100
+ const [length, newOffset] = readVarint(buffer, offset);
101
+ const value = buffer.slice(newOffset, newOffset + length).toString('utf8');
102
+ return [value, newOffset + length];
103
+ }
104
+
105
+ /**
106
+ * Skip a field based on wire type
107
+ */
108
+ function skipField(buffer, offset, wireType) {
109
+ switch (wireType) {
110
+ case 0: // Varint
111
+ const [, newOffset] = readVarint(buffer, offset);
112
+ return newOffset;
113
+ case 1: // 64-bit
114
+ return offset + 8;
115
+ case 2: // Length-delimited
116
+ const [length, lenOffset] = readVarint(buffer, offset);
117
+ return lenOffset + length;
118
+ case 5: // 32-bit
119
+ return offset + 4;
120
+ default:
121
+ throw new Error(`Unknown wire type: ${wireType}`);
36
122
  }
37
-
38
- release(buffer) {
39
- const idx = this._pool.indexOf(buffer);
40
- if (idx !== -1 && this._count < this._size) {
41
- this._available[this._tail] = idx;
42
- this._tail = (this._tail + 1) % this._size;
43
- this._count++;
123
+ }
124
+
125
+ /**
126
+ * Manually decode AccountPnL from raw bytes
127
+ */
128
+ function decodeAccountPnL(buffer) {
129
+ const result = {};
130
+ let offset = 0;
131
+
132
+ while (offset < buffer.length) {
133
+ try {
134
+ const [tag, tagOffset] = readVarint(buffer, offset);
135
+ const wireType = tag & 0x7;
136
+ const fieldNumber = tag >>> 3;
137
+ offset = tagOffset;
138
+
139
+ switch (fieldNumber) {
140
+ case PNL_FIELDS.TEMPLATE_ID:
141
+ [result.templateId, offset] = readVarint(buffer, offset);
142
+ break;
143
+ case PNL_FIELDS.IS_SNAPSHOT:
144
+ const [isSnap, snapOffset] = readVarint(buffer, offset);
145
+ result.isSnapshot = isSnap !== 0;
146
+ offset = snapOffset;
147
+ break;
148
+ case PNL_FIELDS.FCM_ID:
149
+ [result.fcmId, offset] = readLengthDelimited(buffer, offset);
150
+ break;
151
+ case PNL_FIELDS.IB_ID:
152
+ [result.ibId, offset] = readLengthDelimited(buffer, offset);
153
+ break;
154
+ case PNL_FIELDS.ACCOUNT_ID:
155
+ [result.accountId, offset] = readLengthDelimited(buffer, offset);
156
+ break;
157
+ case PNL_FIELDS.ACCOUNT_BALANCE:
158
+ [result.accountBalance, offset] = readLengthDelimited(buffer, offset);
159
+ break;
160
+ case PNL_FIELDS.CASH_ON_HAND:
161
+ [result.cashOnHand, offset] = readLengthDelimited(buffer, offset);
162
+ break;
163
+ case PNL_FIELDS.MARGIN_BALANCE:
164
+ [result.marginBalance, offset] = readLengthDelimited(buffer, offset);
165
+ break;
166
+ case PNL_FIELDS.MIN_ACCOUNT_BALANCE:
167
+ [result.minAccountBalance, offset] = readLengthDelimited(buffer, offset);
168
+ break;
169
+ case PNL_FIELDS.OPEN_POSITION_PNL:
170
+ [result.openPositionPnl, offset] = readLengthDelimited(buffer, offset);
171
+ break;
172
+ case PNL_FIELDS.CLOSED_POSITION_PNL:
173
+ [result.closedPositionPnl, offset] = readLengthDelimited(buffer, offset);
174
+ break;
175
+ case PNL_FIELDS.DAY_PNL:
176
+ [result.dayPnl, offset] = readLengthDelimited(buffer, offset);
177
+ break;
178
+ case PNL_FIELDS.DAY_OPEN_PNL:
179
+ [result.dayOpenPnl, offset] = readLengthDelimited(buffer, offset);
180
+ break;
181
+ case PNL_FIELDS.DAY_CLOSED_PNL:
182
+ [result.dayClosedPnl, offset] = readLengthDelimited(buffer, offset);
183
+ break;
184
+ case PNL_FIELDS.AVAILABLE_BUYING_POWER:
185
+ [result.availableBuyingPower, offset] = readLengthDelimited(buffer, offset);
186
+ break;
187
+ case PNL_FIELDS.SSBOE:
188
+ [result.ssboe, offset] = readVarint(buffer, offset);
189
+ break;
190
+ case PNL_FIELDS.USECS:
191
+ [result.usecs, offset] = readVarint(buffer, offset);
192
+ break;
193
+ default:
194
+ offset = skipField(buffer, offset, wireType);
195
+ }
196
+ } catch (error) {
197
+ break;
44
198
  }
45
199
  }
46
-
47
- getStats() {
48
- return { size: this._size, available: this._count, bufferSize: this._bufferSize };
200
+
201
+ return result;
202
+ }
203
+
204
+ /**
205
+ * Manually decode InstrumentPnLPositionUpdate from raw bytes
206
+ */
207
+ function decodeInstrumentPnL(buffer) {
208
+ const result = {};
209
+ let offset = 0;
210
+
211
+ while (offset < buffer.length) {
212
+ try {
213
+ const [tag, tagOffset] = readVarint(buffer, offset);
214
+ const wireType = tag & 0x7;
215
+ const fieldNumber = tag >>> 3;
216
+ offset = tagOffset;
217
+
218
+ switch (fieldNumber) {
219
+ case INSTRUMENT_PNL_FIELDS.TEMPLATE_ID:
220
+ [result.templateId, offset] = readVarint(buffer, offset);
221
+ break;
222
+ case INSTRUMENT_PNL_FIELDS.IS_SNAPSHOT:
223
+ const [isSnap, snapOffset] = readVarint(buffer, offset);
224
+ result.isSnapshot = isSnap !== 0;
225
+ offset = snapOffset;
226
+ break;
227
+ case INSTRUMENT_PNL_FIELDS.FCM_ID:
228
+ [result.fcmId, offset] = readLengthDelimited(buffer, offset);
229
+ break;
230
+ case INSTRUMENT_PNL_FIELDS.IB_ID:
231
+ [result.ibId, offset] = readLengthDelimited(buffer, offset);
232
+ break;
233
+ case INSTRUMENT_PNL_FIELDS.ACCOUNT_ID:
234
+ [result.accountId, offset] = readLengthDelimited(buffer, offset);
235
+ break;
236
+ case INSTRUMENT_PNL_FIELDS.SYMBOL:
237
+ [result.symbol, offset] = readLengthDelimited(buffer, offset);
238
+ break;
239
+ case INSTRUMENT_PNL_FIELDS.EXCHANGE:
240
+ [result.exchange, offset] = readLengthDelimited(buffer, offset);
241
+ break;
242
+ case INSTRUMENT_PNL_FIELDS.PRODUCT_CODE:
243
+ [result.productCode, offset] = readLengthDelimited(buffer, offset);
244
+ break;
245
+ case INSTRUMENT_PNL_FIELDS.BUY_QTY:
246
+ [result.buyQty, offset] = readVarint(buffer, offset);
247
+ break;
248
+ case INSTRUMENT_PNL_FIELDS.SELL_QTY:
249
+ [result.sellQty, offset] = readVarint(buffer, offset);
250
+ break;
251
+ case INSTRUMENT_PNL_FIELDS.FILL_BUY_QTY:
252
+ [result.fillBuyQty, offset] = readVarint(buffer, offset);
253
+ break;
254
+ case INSTRUMENT_PNL_FIELDS.FILL_SELL_QTY:
255
+ [result.fillSellQty, offset] = readVarint(buffer, offset);
256
+ break;
257
+ case INSTRUMENT_PNL_FIELDS.NET_QUANTITY:
258
+ [result.netQuantity, offset] = readVarint(buffer, offset);
259
+ break;
260
+ case INSTRUMENT_PNL_FIELDS.OPEN_POSITION_QUANTITY:
261
+ [result.openPositionQuantity, offset] = readVarint(buffer, offset);
262
+ break;
263
+ case INSTRUMENT_PNL_FIELDS.AVG_OPEN_FILL_PRICE:
264
+ // Double is 64-bit fixed
265
+ if (wireType === 1) {
266
+ result.avgOpenFillPrice = buffer.readDoubleLE(offset);
267
+ offset += 8;
268
+ } else {
269
+ offset = skipField(buffer, offset, wireType);
270
+ }
271
+ break;
272
+ case INSTRUMENT_PNL_FIELDS.OPEN_POSITION_PNL:
273
+ [result.openPositionPnl, offset] = readLengthDelimited(buffer, offset);
274
+ break;
275
+ case INSTRUMENT_PNL_FIELDS.CLOSED_POSITION_PNL:
276
+ [result.closedPositionPnl, offset] = readLengthDelimited(buffer, offset);
277
+ break;
278
+ case INSTRUMENT_PNL_FIELDS.DAY_PNL:
279
+ [result.dayPnl, offset] = readLengthDelimited(buffer, offset);
280
+ break;
281
+ case INSTRUMENT_PNL_FIELDS.DAY_OPEN_PNL:
282
+ [result.dayOpenPnl, offset] = readLengthDelimited(buffer, offset);
283
+ break;
284
+ case INSTRUMENT_PNL_FIELDS.DAY_CLOSED_PNL:
285
+ [result.dayClosedPnl, offset] = readLengthDelimited(buffer, offset);
286
+ break;
287
+ case INSTRUMENT_PNL_FIELDS.SSBOE:
288
+ [result.ssboe, offset] = readVarint(buffer, offset);
289
+ break;
290
+ case INSTRUMENT_PNL_FIELDS.USECS:
291
+ [result.usecs, offset] = readVarint(buffer, offset);
292
+ break;
293
+ default:
294
+ offset = skipField(buffer, offset, wireType);
295
+ }
296
+ } catch (error) {
297
+ break;
298
+ }
49
299
  }
300
+
301
+ return result;
50
302
  }
51
303
 
52
304
  /**
53
305
  * Protobuf Handler class
54
- * OPTIMIZED: Pre-compile types, cache encoders, buffer pooling
55
- *
56
- * ULTRA-LOW LATENCY FEATURES:
57
- * - Pre-allocated buffer pool (zero allocation in hot path)
58
- * - Reusable protobuf Writer
59
- * - Cached compiled message types
60
- * - Direct buffer encoding without intermediate objects
61
306
  */
62
307
  class ProtobufHandler {
63
308
  constructor() {
64
309
  this.root = null;
65
310
  this.loaded = false;
66
311
  this.protoPath = path.join(__dirname, 'proto');
67
-
68
- // OPTIMIZATION: Cache compiled types for hot path
69
- this._typeCache = new Map();
70
- this._encoderCache = new Map();
71
-
72
- // OPTIMIZATION: Pre-allocated buffer pool for zero-allocation encoding
73
- this._bufferPool = new BufferPool(16, 512); // 16 buffers of 512 bytes each
74
-
75
- // OPTIMIZATION: Reusable protobuf Writer instance
76
- this._writer = null;
77
312
  }
78
313
 
79
314
  /**
80
315
  * Load all proto files
81
- * Call once at startup, not per-connection
82
316
  */
83
317
  async load() {
84
318
  if (this.loaded) return;
@@ -94,109 +328,26 @@ class ProtobufHandler {
94
328
  }
95
329
 
96
330
  this.loaded = true;
97
-
98
- // Pre-compile frequently used types
99
- this._precompileTypes();
100
- }
101
-
102
- /**
103
- * Pre-compile frequently used message types
104
- * @private
105
- */
106
- _precompileTypes() {
107
- const hotTypes = [
108
- 'RequestNewOrder',
109
- 'RequestCancelOrder',
110
- 'RequestHeartbeat',
111
- 'RequestLogin',
112
- 'ResponseLogin',
113
- 'RithmicOrderNotification',
114
- 'ExchangeOrderNotification',
115
- ];
116
-
117
- for (const typeName of hotTypes) {
118
- try {
119
- const Type = this.root.lookupType(typeName);
120
- this._typeCache.set(typeName, Type);
121
- } catch (e) {
122
- // Type may not exist in all proto files
123
- }
124
- }
125
- }
126
-
127
- /**
128
- * Get cached type or lookup
129
- * @private
130
- */
131
- _getType(typeName) {
132
- let Type = this._typeCache.get(typeName);
133
- if (!Type) {
134
- Type = this.root.lookupType(typeName);
135
- this._typeCache.set(typeName, Type);
136
- }
137
- return Type;
138
331
  }
139
332
 
140
333
  /**
141
334
  * Encode a message to Buffer
142
- * OPTIMIZED: Uses cached type lookup
143
335
  */
144
336
  encode(typeName, data) {
145
337
  if (!this.root) throw new Error('Proto not loaded');
146
338
 
147
- const Type = this._getType(typeName);
339
+ const Type = this.root.lookupType(typeName);
148
340
  const msg = Type.create(data);
149
341
  return Buffer.from(Type.encode(msg).finish());
150
342
  }
151
343
 
152
- /**
153
- * Fast encode for hot path - uses buffer pool and reuses writer
154
- * ULTRA-LOW LATENCY: Zero allocation in typical case
155
- *
156
- * @param {string} typeName
157
- * @param {Object} data
158
- * @returns {Buffer}
159
- */
160
- fastEncode(typeName, data) {
161
- const Type = this._typeCache.get(typeName);
162
- if (!Type) return this.encode(typeName, data);
163
-
164
- // OPTIMIZATION: Create message without validation (faster)
165
- const msg = Type.fromObject(data);
166
-
167
- // OPTIMIZATION: Get length first to check if pool buffer fits
168
- const len = Type.encode(msg).len;
169
-
170
- if (len <= 512) {
171
- // Use pooled buffer for small messages (typical orders are ~100-200 bytes)
172
- const poolBuf = this._bufferPool.acquire();
173
- const writer = protobuf.Writer.create();
174
- Type.encode(msg, writer);
175
- const encoded = writer.finish();
176
-
177
- // Copy to pooled buffer and return a slice
178
- encoded.copy(poolBuf, 0, 0, len);
179
-
180
- // Return a NEW buffer (copy) because pooled buffer will be reused
181
- // This is still faster than allocating fresh each time due to copy being optimized
182
- const result = Buffer.allocUnsafe(len);
183
- poolBuf.copy(result, 0, 0, len);
184
- this._bufferPool.release(poolBuf);
185
- return result;
186
- }
187
-
188
- // Large message - use standard path
189
- return Buffer.from(Type.encode(msg).finish());
190
- }
191
-
192
344
  /**
193
345
  * Decode a Buffer to object
194
- * OPTIMIZED: Uses cached type lookup
195
346
  */
196
347
  decode(typeName, buffer) {
197
348
  if (!this.root) throw new Error('Proto not loaded');
198
349
 
199
- const Type = this._getType(typeName);
350
+ const Type = this.root.lookupType(typeName);
200
351
  return Type.decode(buffer);
201
352
  }
202
353
 
@@ -239,6 +390,101 @@ class ProtobufHandler {
239
390
  }
240
391
  }
241
392
 
393
+ /**
394
+ * Decode ResponseProductCodes (template 112) - list of available symbols
395
+ */
396
+ function decodeProductCodes(buffer) {
397
+ const result = { rpCode: [] };
398
+ let offset = 0;
399
+
400
+ while (offset < buffer.length) {
401
+ try {
402
+ const [tag, tagOffset] = readVarint(buffer, offset);
403
+ const wireType = tag & 0x7;
404
+ const fieldNumber = tag >>> 3;
405
+ offset = tagOffset;
406
+
407
+ switch (fieldNumber) {
408
+ case SYMBOL_FIELDS.TEMPLATE_ID:
409
+ [result.templateId, offset] = readVarint(buffer, offset);
410
+ break;
411
+ case SYMBOL_FIELDS.RP_CODE:
412
+ let rpCode;
413
+ [rpCode, offset] = readLengthDelimited(buffer, offset);
414
+ result.rpCode.push(rpCode);
415
+ break;
416
+ case SYMBOL_FIELDS.EXCHANGE:
417
+ [result.exchange, offset] = readLengthDelimited(buffer, offset);
418
+ break;
419
+ case SYMBOL_FIELDS.PRODUCT_CODE:
420
+ [result.productCode, offset] = readLengthDelimited(buffer, offset);
421
+ break;
422
+ case SYMBOL_FIELDS.PRODUCT_NAME:
423
+ [result.productName, offset] = readLengthDelimited(buffer, offset);
424
+ break;
425
+ case SYMBOL_FIELDS.USER_MSG:
426
+ [result.userMsg, offset] = readLengthDelimited(buffer, offset);
427
+ break;
428
+ default:
429
+ offset = skipField(buffer, offset, wireType);
430
+ }
431
+ } catch (error) {
432
+ break;
433
+ }
434
+ }
435
+
436
+ return result;
437
+ }
438
+
439
+ /**
440
+ * Decode ResponseFrontMonthContract (template 114) - current tradeable contract
441
+ */
442
+ function decodeFrontMonthContract(buffer) {
443
+ const result = { rpCode: [] };
444
+ let offset = 0;
445
+
446
+ while (offset < buffer.length) {
447
+ try {
448
+ const [tag, tagOffset] = readVarint(buffer, offset);
449
+ const wireType = tag & 0x7;
450
+ const fieldNumber = tag >>> 3;
451
+ offset = tagOffset;
452
+
453
+ switch (fieldNumber) {
454
+ case SYMBOL_FIELDS.TEMPLATE_ID:
455
+ [result.templateId, offset] = readVarint(buffer, offset);
456
+ break;
457
+ case SYMBOL_FIELDS.RP_CODE:
458
+ let rpCode;
459
+ [rpCode, offset] = readLengthDelimited(buffer, offset);
460
+ result.rpCode.push(rpCode);
461
+ break;
462
+ case SYMBOL_FIELDS.SYMBOL:
463
+ [result.symbol, offset] = readLengthDelimited(buffer, offset);
464
+ break;
465
+ case SYMBOL_FIELDS.EXCHANGE:
466
+ [result.exchange, offset] = readLengthDelimited(buffer, offset);
467
+ break;
468
+ case SYMBOL_FIELDS.TRADING_SYMBOL:
469
+ [result.tradingSymbol, offset] = readLengthDelimited(buffer, offset);
470
+ break;
471
+ case SYMBOL_FIELDS.DESCRIPTION:
472
+ [result.description, offset] = readLengthDelimited(buffer, offset);
473
+ break;
474
+ case SYMBOL_FIELDS.USER_MSG:
475
+ [result.userMsg, offset] = readLengthDelimited(buffer, offset);
476
+ break;
477
+ default:
478
+ offset = skipField(buffer, offset, wireType);
479
+ }
480
+ } catch (error) {
481
+ break;
482
+ }
483
+ }
484
+
485
+ return result;
486
+ }
487
+
242
488
  // Singleton
243
489
  const proto = new ProtobufHandler();
244
490