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
|
@@ -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
|
-
|
|
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 &&
|
|
132
|
+
if (sampleProducts.length < 5 && productCode) {
|
|
111
133
|
sampleProducts.push({
|
|
112
|
-
code:
|
|
113
|
-
exchange:
|
|
114
|
-
name:
|
|
134
|
+
code: productCode,
|
|
135
|
+
exchange: exchange,
|
|
136
|
+
name: productName?.substring(0, 30)
|
|
115
137
|
});
|
|
116
138
|
}
|
|
117
139
|
|
|
118
|
-
if (!
|
|
140
|
+
if (!productCode || !exchange) return;
|
|
119
141
|
|
|
120
142
|
const validExchanges = ['CME', 'CBOT', 'NYMEX', 'COMEX', 'NYBOT', 'CFE'];
|
|
121
|
-
if (!validExchanges.includes(
|
|
143
|
+
if (!validExchanges.includes(exchange)) return;
|
|
122
144
|
|
|
123
|
-
const name = (
|
|
145
|
+
const name = (productName || '').toLowerCase();
|
|
124
146
|
if (name.includes('option') || name.includes('swap') || name.includes('spread')) return;
|
|
125
147
|
|
|
126
|
-
const key = `${
|
|
148
|
+
const key = `${productCode}:${exchange}`;
|
|
127
149
|
if (!productsToCheck.has(key)) {
|
|
128
150
|
productsToCheck.set(key, {
|
|
129
|
-
productCode:
|
|
130
|
-
productName:
|
|
131
|
-
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
|
};
|