hedgequantx 2.9.57 → 2.9.59

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.
@@ -1,316 +1,23 @@
1
1
  /**
2
2
  * Rithmic Protobuf Handler
3
- * Handles encoding/decoding of Rithmic protobuf messages
3
+ * Main interface for encoding/decoding Rithmic protobuf messages
4
+ * @module services/rithmic/protobuf
4
5
  */
5
6
 
6
7
  const protobuf = require('protobufjs');
7
8
  const path = require('path');
8
9
  const { PROTO_FILES } = require('./constants');
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');
91
- }
92
- }
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}`);
122
- }
123
- }
124
-
125
- /**
126
- * Manually decode AccountPnL from raw bytes
127
- * Skips 4-byte length prefix if present
128
- */
129
- function decodeAccountPnL(buffer) {
130
- // Skip 4-byte length prefix
131
- const data = buffer.length > 4 ? buffer.slice(4) : buffer;
132
-
133
- const result = {};
134
- let offset = 0;
135
-
136
- while (offset < data.length) {
137
- try {
138
- const [tag, tagOffset] = readVarint(data, offset);
139
- const wireType = tag & 0x7;
140
- const fieldNumber = tag >>> 3;
141
- offset = tagOffset;
142
-
143
- switch (fieldNumber) {
144
- case PNL_FIELDS.TEMPLATE_ID:
145
- [result.templateId, offset] = readVarint(data, offset);
146
- break;
147
- case PNL_FIELDS.IS_SNAPSHOT:
148
- const [isSnap, snapOffset] = readVarint(data, offset);
149
- result.isSnapshot = isSnap !== 0;
150
- offset = snapOffset;
151
- break;
152
- case PNL_FIELDS.FCM_ID:
153
- [result.fcmId, offset] = readLengthDelimited(data, offset);
154
- break;
155
- case PNL_FIELDS.IB_ID:
156
- [result.ibId, offset] = readLengthDelimited(data, offset);
157
- break;
158
- case PNL_FIELDS.ACCOUNT_ID:
159
- [result.accountId, offset] = readLengthDelimited(data, offset);
160
- break;
161
- case PNL_FIELDS.ACCOUNT_BALANCE:
162
- [result.accountBalance, offset] = readLengthDelimited(data, offset);
163
- break;
164
- case PNL_FIELDS.CASH_ON_HAND:
165
- [result.cashOnHand, offset] = readLengthDelimited(data, offset);
166
- break;
167
- case PNL_FIELDS.MARGIN_BALANCE:
168
- [result.marginBalance, offset] = readLengthDelimited(data, offset);
169
- break;
170
- case PNL_FIELDS.MIN_ACCOUNT_BALANCE:
171
- [result.minAccountBalance, offset] = readLengthDelimited(data, offset);
172
- break;
173
- case PNL_FIELDS.OPEN_POSITION_PNL:
174
- [result.openPositionPnl, offset] = readLengthDelimited(data, offset);
175
- break;
176
- case PNL_FIELDS.CLOSED_POSITION_PNL:
177
- [result.closedPositionPnl, offset] = readLengthDelimited(data, offset);
178
- break;
179
- case PNL_FIELDS.DAY_PNL:
180
- [result.dayPnl, offset] = readLengthDelimited(data, offset);
181
- break;
182
- case PNL_FIELDS.DAY_OPEN_PNL:
183
- [result.dayOpenPnl, offset] = readLengthDelimited(data, offset);
184
- break;
185
- case PNL_FIELDS.DAY_CLOSED_PNL:
186
- [result.dayClosedPnl, offset] = readLengthDelimited(data, offset);
187
- break;
188
- case PNL_FIELDS.AVAILABLE_BUYING_POWER:
189
- [result.availableBuyingPower, offset] = readLengthDelimited(data, offset);
190
- break;
191
- case PNL_FIELDS.SSBOE:
192
- [result.ssboe, offset] = readVarint(data, offset);
193
- break;
194
- case PNL_FIELDS.USECS:
195
- [result.usecs, offset] = readVarint(data, offset);
196
- break;
197
- default:
198
- offset = skipField(data, offset, wireType);
199
- }
200
- } catch (error) {
201
- break;
202
- }
203
- }
204
-
205
- return result;
206
- }
207
-
208
- /**
209
- * Manually decode InstrumentPnLPositionUpdate from raw bytes
210
- * Skips 4-byte length prefix if present
211
- */
212
- function decodeInstrumentPnL(buffer) {
213
- // Skip 4-byte length prefix
214
- const data = buffer.length > 4 ? buffer.slice(4) : buffer;
215
-
216
- const result = {};
217
- let offset = 0;
218
-
219
- while (offset < data.length) {
220
- try {
221
- const [tag, tagOffset] = readVarint(data, offset);
222
- const wireType = tag & 0x7;
223
- const fieldNumber = tag >>> 3;
224
- offset = tagOffset;
225
-
226
- switch (fieldNumber) {
227
- case INSTRUMENT_PNL_FIELDS.TEMPLATE_ID:
228
- [result.templateId, offset] = readVarint(data, offset);
229
- break;
230
- case INSTRUMENT_PNL_FIELDS.IS_SNAPSHOT:
231
- const [isSnap, snapOffset] = readVarint(data, offset);
232
- result.isSnapshot = isSnap !== 0;
233
- offset = snapOffset;
234
- break;
235
- case INSTRUMENT_PNL_FIELDS.FCM_ID:
236
- [result.fcmId, offset] = readLengthDelimited(data, offset);
237
- break;
238
- case INSTRUMENT_PNL_FIELDS.IB_ID:
239
- [result.ibId, offset] = readLengthDelimited(data, offset);
240
- break;
241
- case INSTRUMENT_PNL_FIELDS.ACCOUNT_ID:
242
- [result.accountId, offset] = readLengthDelimited(data, offset);
243
- break;
244
- case INSTRUMENT_PNL_FIELDS.SYMBOL:
245
- [result.symbol, offset] = readLengthDelimited(data, offset);
246
- break;
247
- case INSTRUMENT_PNL_FIELDS.EXCHANGE:
248
- [result.exchange, offset] = readLengthDelimited(data, offset);
249
- break;
250
- case INSTRUMENT_PNL_FIELDS.PRODUCT_CODE:
251
- [result.productCode, offset] = readLengthDelimited(data, offset);
252
- break;
253
- case INSTRUMENT_PNL_FIELDS.BUY_QTY:
254
- [result.buyQty, offset] = readVarint(data, offset);
255
- break;
256
- case INSTRUMENT_PNL_FIELDS.SELL_QTY:
257
- [result.sellQty, offset] = readVarint(data, offset);
258
- break;
259
- case INSTRUMENT_PNL_FIELDS.FILL_BUY_QTY:
260
- [result.fillBuyQty, offset] = readVarint(data, offset);
261
- break;
262
- case INSTRUMENT_PNL_FIELDS.FILL_SELL_QTY:
263
- [result.fillSellQty, offset] = readVarint(data, offset);
264
- break;
265
- case INSTRUMENT_PNL_FIELDS.NET_QUANTITY:
266
- [result.netQuantity, offset] = readVarint(data, offset);
267
- break;
268
- case INSTRUMENT_PNL_FIELDS.OPEN_POSITION_QUANTITY:
269
- [result.openPositionQuantity, offset] = readVarint(data, offset);
270
- break;
271
- case INSTRUMENT_PNL_FIELDS.AVG_OPEN_FILL_PRICE:
272
- // Double is 64-bit fixed
273
- if (wireType === 1) {
274
- result.avgOpenFillPrice = data.readDoubleLE(offset);
275
- offset += 8;
276
- } else {
277
- offset = skipField(data, offset, wireType);
278
- }
279
- break;
280
- case INSTRUMENT_PNL_FIELDS.OPEN_POSITION_PNL:
281
- [result.openPositionPnl, offset] = readLengthDelimited(data, offset);
282
- break;
283
- case INSTRUMENT_PNL_FIELDS.CLOSED_POSITION_PNL:
284
- [result.closedPositionPnl, offset] = readLengthDelimited(data, offset);
285
- break;
286
- case INSTRUMENT_PNL_FIELDS.DAY_PNL:
287
- [result.dayPnl, offset] = readLengthDelimited(data, offset);
288
- break;
289
- case INSTRUMENT_PNL_FIELDS.DAY_OPEN_PNL:
290
- [result.dayOpenPnl, offset] = readLengthDelimited(data, offset);
291
- break;
292
- case INSTRUMENT_PNL_FIELDS.DAY_CLOSED_PNL:
293
- [result.dayClosedPnl, offset] = readLengthDelimited(data, offset);
294
- break;
295
- case INSTRUMENT_PNL_FIELDS.SSBOE:
296
- [result.ssboe, offset] = readVarint(data, offset);
297
- break;
298
- case INSTRUMENT_PNL_FIELDS.USECS:
299
- [result.usecs, offset] = readVarint(data, offset);
300
- break;
301
- default:
302
- offset = skipField(data, offset, wireType);
303
- }
304
- } catch (error) {
305
- break;
306
- }
307
- }
308
-
309
- return result;
310
- }
10
+ const { readVarint, readLengthDelimited, skipField } = require('./protobuf-utils');
11
+ const {
12
+ decodeAccountPnL,
13
+ decodeInstrumentPnL,
14
+ decodeProductCodes,
15
+ decodeFrontMonthContract,
16
+ } = require('./protobuf-decoders');
311
17
 
312
18
  /**
313
19
  * Protobuf Handler class
20
+ * Handles loading proto files and encoding/decoding messages
314
21
  */
315
22
  class ProtobufHandler {
316
23
  constructor() {
@@ -340,6 +47,9 @@ class ProtobufHandler {
340
47
 
341
48
  /**
342
49
  * Encode a message to Buffer with 4-byte length prefix
50
+ * @param {string} typeName - Protobuf type name
51
+ * @param {Object} data - Data to encode
52
+ * @returns {Buffer} Encoded buffer with length prefix
343
53
  */
344
54
  encode(typeName, data) {
345
55
  if (!this.root) throw new Error('Proto not loaded');
@@ -357,6 +67,9 @@ class ProtobufHandler {
357
67
 
358
68
  /**
359
69
  * Decode a Buffer to object (skip 4-byte length prefix)
70
+ * @param {string} typeName - Protobuf type name
71
+ * @param {Buffer} buffer - Buffer to decode
72
+ * @returns {Object} Decoded message
360
73
  */
361
74
  decode(typeName, buffer) {
362
75
  if (!this.root) throw new Error('Proto not loaded');
@@ -371,6 +84,8 @@ class ProtobufHandler {
371
84
  /**
372
85
  * Get template ID from buffer (manual decode for large field IDs)
373
86
  * Skips 4-byte length prefix if present
87
+ * @param {Buffer} buffer - Buffer to parse
88
+ * @returns {number} Template ID or -1 if not found
374
89
  */
375
90
  getTemplateId(buffer) {
376
91
  const TEMPLATE_ID_FIELD = 154467;
@@ -411,106 +126,7 @@ class ProtobufHandler {
411
126
  }
412
127
  }
413
128
 
414
- /**
415
- * Decode ResponseProductCodes (template 112) - list of available symbols
416
- */
417
- function decodeProductCodes(buffer) {
418
- const result = { rpCode: [] };
419
- let offset = 0;
420
-
421
- while (offset < buffer.length) {
422
- try {
423
- const [tag, tagOffset] = readVarint(buffer, offset);
424
- const wireType = tag & 0x7;
425
- const fieldNumber = tag >>> 3;
426
- offset = tagOffset;
427
-
428
- switch (fieldNumber) {
429
- case SYMBOL_FIELDS.TEMPLATE_ID:
430
- [result.templateId, offset] = readVarint(buffer, offset);
431
- break;
432
- case SYMBOL_FIELDS.RP_CODE:
433
- let rpCode;
434
- [rpCode, offset] = readLengthDelimited(buffer, offset);
435
- result.rpCode.push(rpCode);
436
- break;
437
- case SYMBOL_FIELDS.EXCHANGE:
438
- [result.exchange, offset] = readLengthDelimited(buffer, offset);
439
- break;
440
- case SYMBOL_FIELDS.PRODUCT_CODE:
441
- [result.productCode, offset] = readLengthDelimited(buffer, offset);
442
- break;
443
- case SYMBOL_FIELDS.PRODUCT_NAME:
444
- [result.productName, offset] = readLengthDelimited(buffer, offset);
445
- break;
446
- case SYMBOL_FIELDS.USER_MSG:
447
- [result.userMsg, offset] = readLengthDelimited(buffer, offset);
448
- break;
449
- default:
450
- offset = skipField(buffer, offset, wireType);
451
- }
452
- } catch (error) {
453
- break;
454
- }
455
- }
456
-
457
- return result;
458
- }
459
-
460
- /**
461
- * Decode ResponseFrontMonthContract (template 114) - current tradeable contract
462
- * Skips 4-byte length prefix if present
463
- */
464
- function decodeFrontMonthContract(buffer) {
465
- // Skip 4-byte length prefix
466
- const data = buffer.length > 4 ? buffer.slice(4) : buffer;
467
-
468
- const result = { rpCode: [] };
469
- let offset = 0;
470
-
471
- while (offset < data.length) {
472
- try {
473
- const [tag, tagOffset] = readVarint(data, offset);
474
- const wireType = tag & 0x7;
475
- const fieldNumber = tag >>> 3;
476
- offset = tagOffset;
477
-
478
- switch (fieldNumber) {
479
- case SYMBOL_FIELDS.TEMPLATE_ID:
480
- [result.templateId, offset] = readVarint(data, offset);
481
- break;
482
- case SYMBOL_FIELDS.RP_CODE:
483
- let rpCode;
484
- [rpCode, offset] = readLengthDelimited(data, offset);
485
- result.rpCode.push(rpCode);
486
- break;
487
- case SYMBOL_FIELDS.SYMBOL:
488
- [result.symbol, offset] = readLengthDelimited(data, offset);
489
- break;
490
- case SYMBOL_FIELDS.EXCHANGE:
491
- [result.exchange, offset] = readLengthDelimited(data, offset);
492
- break;
493
- case SYMBOL_FIELDS.TRADING_SYMBOL:
494
- [result.tradingSymbol, offset] = readLengthDelimited(data, offset);
495
- break;
496
- case SYMBOL_FIELDS.DESCRIPTION:
497
- [result.description, offset] = readLengthDelimited(data, offset);
498
- break;
499
- case SYMBOL_FIELDS.USER_MSG:
500
- [result.userMsg, offset] = readLengthDelimited(data, offset);
501
- break;
502
- default:
503
- offset = skipField(data, offset, wireType);
504
- }
505
- } catch (error) {
506
- break;
507
- }
508
- }
509
-
510
- return result;
511
- }
512
-
513
- // Singleton
129
+ // Singleton instance
514
130
  const proto = new ProtobufHandler();
515
131
 
516
132
  module.exports = {
package/src/ui/menu.js CHANGED
@@ -48,7 +48,12 @@ const createBoxMenu = async (title, items, options = {}) => {
48
48
  });
49
49
 
50
50
  console.log(chalk.cyan('╠' + '═'.repeat(innerWidth) + '╣'));
51
- console.log(chalk.cyan('║') + chalk.yellow(centerText(`PROP FUTURES ALGO TRADING V${version}`, innerWidth)) + chalk.cyan('║'));
51
+ const taglineBase = 'PROP FUTURES ALGO TRADING ';
52
+ const taglineVersion = `V${version}`;
53
+ const totalLen = taglineBase.length + taglineVersion.length;
54
+ const padLeft = Math.floor((innerWidth - totalLen) / 2);
55
+ const padRight = innerWidth - totalLen - padLeft;
56
+ console.log(chalk.cyan('║') + ' '.repeat(padLeft) + chalk.yellow(taglineBase) + chalk.magenta(taglineVersion) + ' '.repeat(padRight) + chalk.cyan('║'));
52
57
 
53
58
  // Stats bar if provided
54
59
  if (options.statsLine) {