hedgequantx 2.9.174 → 2.9.176

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "2.9.174",
3
+ "version": "2.9.176",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -5,7 +5,7 @@
5
5
  * NO FAKE DATA - Only real values from Rithmic API
6
6
  */
7
7
 
8
- const { decodeFrontMonthContract } = require('./protobuf');
8
+ const { proto, decodeFrontMonthContract } = require('./protobuf');
9
9
  const { TIMEOUTS, CACHE } = require('../../config/settings');
10
10
  const { logger } = require('../../utils/logger');
11
11
  const { getContractDescription, getTickSize } = require('../../config/constants');
@@ -99,36 +99,58 @@ const fetchAllFrontMonths = (service) => {
99
99
 
100
100
  // Handler for ProductCodes responses
101
101
  const sampleProducts = [];
102
+ let decodeErrors = 0;
103
+ let firstError = null;
102
104
  const productHandler = (msg) => {
103
105
  msgCount++;
104
106
  if (msg.templateId !== 112) return;
105
107
  productMsgCount++;
106
108
 
107
- const decoded = decodeProductCodes(msg.data);
109
+ // Use official protobuf decoder instead of manual parsing
110
+ let decoded;
111
+ try {
112
+ decoded = proto.decode('ResponseProductCodes', msg.data);
113
+ } catch (e) {
114
+ decodeErrors++;
115
+ if (!firstError) {
116
+ firstError = e.message;
117
+ // Log raw buffer info for debugging
118
+ brokerLog('First decode error', {
119
+ error: e.message,
120
+ bufferLen: msg.data?.length,
121
+ first20bytes: msg.data?.slice(0, 20)?.toString('hex')
122
+ });
123
+ }
124
+ return;
125
+ }
126
+
127
+ const productCode = decoded.productCode;
128
+ const exchange = decoded.exchange;
129
+ const productName = decoded.productName;
108
130
 
109
131
  // Log first 5 decoded products
110
- if (sampleProducts.length < 5 && decoded.productCode) {
132
+ if (sampleProducts.length < 5 && productCode) {
111
133
  sampleProducts.push({
112
- code: decoded.productCode,
113
- exchange: decoded.exchange,
114
- name: decoded.productName?.substring(0, 30)
134
+ code: productCode,
135
+ exchange: exchange,
136
+ name: productName?.substring(0, 30)
115
137
  });
116
138
  }
117
139
 
118
- if (!decoded.productCode || !decoded.exchange) return;
140
+ if (!productCode || !exchange) return;
119
141
 
120
142
  const validExchanges = ['CME', 'CBOT', 'NYMEX', 'COMEX', 'NYBOT', 'CFE'];
121
- if (!validExchanges.includes(decoded.exchange)) return;
143
+ if (!validExchanges.includes(exchange)) return;
122
144
 
123
- const name = (decoded.productName || '').toLowerCase();
145
+ const name = (productName || '').toLowerCase();
124
146
  if (name.includes('option') || name.includes('swap') || name.includes('spread')) return;
125
147
 
126
- const key = `${decoded.productCode}:${decoded.exchange}`;
148
+ const key = `${productCode}:${exchange}`;
127
149
  if (!productsToCheck.has(key)) {
128
150
  productsToCheck.set(key, {
129
- productCode: decoded.productCode,
130
- productName: decoded.productName || decoded.productCode,
131
- exchange: decoded.exchange,
151
+ productCode: productCode,
152
+ productName: productName || productCode,
153
+ exchange: exchange,
132
154
  });
133
155
  }
134
156
  };
@@ -198,6 +220,8 @@ const fetchAllFrontMonths = (service) => {
198
220
  productsFound: productsToCheck.size,
199
221
  totalMsgs: msgCount,
200
222
  productMsgs: productMsgCount,
223
+ decodeErrors: decodeErrors,
224
+ firstError: firstError,
201
225
  sampleProducts: sampleProducts
202
226
  });
203
227
 
@@ -279,66 +303,8 @@ const fetchAllFrontMonths = (service) => {
279
303
  });
280
304
  };
281
305
 
282
- /**
283
- * Decode ProductCodes response
284
- * @param {Buffer} buffer - Protobuf buffer (with 4-byte length prefix)
285
- * @returns {Object} Decoded product data
286
- */
287
- const decodeProductCodes = (buffer) => {
288
- // Skip 4-byte length prefix
289
- const data = buffer.length > 4 ? buffer.slice(4) : buffer;
290
-
291
- const result = {};
292
- let offset = 0;
293
-
294
- const readVarint = (buf, off) => {
295
- let value = 0;
296
- let shift = 0;
297
- while (off < buf.length) {
298
- const byte = buf[off++];
299
- value |= (byte & 0x7F) << shift;
300
- if (!(byte & 0x80)) break;
301
- shift += 7;
302
- }
303
- return [value, off];
304
- };
305
-
306
- const readString = (buf, off) => {
307
- const [len, newOff] = readVarint(buf, off);
308
- return [buf.slice(newOff, newOff + len).toString('utf8'), newOff + len];
309
- };
310
-
311
- while (offset < data.length) {
312
- try {
313
- const [tag, tagOff] = readVarint(data, offset);
314
- const wireType = tag & 0x7;
315
- const fieldNumber = tag >>> 3;
316
- offset = tagOff;
317
-
318
- if (wireType === 0) {
319
- const [, newOff] = readVarint(data, offset);
320
- offset = newOff;
321
- } else if (wireType === 2) {
322
- const [val, newOff] = readString(data, offset);
323
- offset = newOff;
324
- // Field IDs from Rithmic API (response_product_codes.proto)
325
- if (fieldNumber === 110101) result.exchange = val; // exchange
326
- if (fieldNumber === 110102) result.productCode = val; // product_code (ES, MES, MNQ, etc.)
327
- if (fieldNumber === 110103) result.productName = val; // product_name
328
- } else {
329
- break;
330
- }
331
- } catch {
332
- break;
333
- }
334
- }
335
-
336
- return result;
337
- };
338
-
339
306
  module.exports = {
340
307
  getContracts,
341
308
  searchContracts,
342
309
  fetchAllFrontMonths,
343
- decodeProductCodes,
344
310
  };