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.
Files changed (57) hide show
  1. package/package.json +1 -1
  2. package/src/menus/ai-agent-connect.js +181 -0
  3. package/src/menus/ai-agent-models.js +219 -0
  4. package/src/menus/ai-agent-oauth.js +292 -0
  5. package/src/menus/ai-agent-ui.js +141 -0
  6. package/src/menus/ai-agent.js +88 -1489
  7. package/src/pages/algo/copy-engine.js +449 -0
  8. package/src/pages/algo/copy-trading.js +11 -543
  9. package/src/pages/algo/smart-logs-data.js +218 -0
  10. package/src/pages/algo/smart-logs.js +9 -214
  11. package/src/pages/algo/ui-constants.js +144 -0
  12. package/src/pages/algo/ui-summary.js +184 -0
  13. package/src/pages/algo/ui.js +42 -526
  14. package/src/pages/stats-calculations.js +191 -0
  15. package/src/pages/stats-ui.js +381 -0
  16. package/src/pages/stats.js +14 -507
  17. package/src/services/ai/client-analysis.js +194 -0
  18. package/src/services/ai/client-models.js +333 -0
  19. package/src/services/ai/client.js +6 -489
  20. package/src/services/ai/index.js +2 -257
  21. package/src/services/ai/providers/direct-providers.js +323 -0
  22. package/src/services/ai/providers/index.js +8 -472
  23. package/src/services/ai/providers/other-providers.js +104 -0
  24. package/src/services/ai/proxy-install.js +249 -0
  25. package/src/services/ai/proxy-manager.js +29 -411
  26. package/src/services/ai/proxy-remote.js +161 -0
  27. package/src/services/ai/supervisor-optimize.js +215 -0
  28. package/src/services/ai/supervisor-sync.js +178 -0
  29. package/src/services/ai/supervisor.js +50 -515
  30. package/src/services/ai/validation.js +250 -0
  31. package/src/services/hqx-server-events.js +110 -0
  32. package/src/services/hqx-server-handlers.js +217 -0
  33. package/src/services/hqx-server-latency.js +136 -0
  34. package/src/services/hqx-server.js +51 -403
  35. package/src/services/position-constants.js +28 -0
  36. package/src/services/position-exit-logic.js +174 -0
  37. package/src/services/position-manager.js +90 -629
  38. package/src/services/position-momentum.js +206 -0
  39. package/src/services/projectx/accounts.js +142 -0
  40. package/src/services/projectx/index.js +40 -289
  41. package/src/services/projectx/trading.js +180 -0
  42. package/src/services/rithmic/contracts.js +218 -0
  43. package/src/services/rithmic/handlers.js +2 -208
  44. package/src/services/rithmic/index.js +28 -712
  45. package/src/services/rithmic/latency-tracker.js +182 -0
  46. package/src/services/rithmic/market-data-decoders.js +229 -0
  47. package/src/services/rithmic/market-data.js +1 -278
  48. package/src/services/rithmic/orders-fast.js +246 -0
  49. package/src/services/rithmic/orders.js +1 -251
  50. package/src/services/rithmic/proto-decoders.js +403 -0
  51. package/src/services/rithmic/protobuf.js +7 -443
  52. package/src/services/rithmic/specs.js +146 -0
  53. package/src/services/rithmic/trade-history.js +254 -0
  54. package/src/services/strategy/hft-signal-calc.js +147 -0
  55. package/src/services/strategy/hft-tick.js +33 -133
  56. package/src/services/tradovate/index.js +6 -119
  57. package/src/services/tradovate/orders.js +145 -0
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Rithmic Latency Tracking
3
+ * @module services/rithmic/latency-tracker
4
+ *
5
+ * High-precision order-to-fill latency tracking.
6
+ * Uses circular buffer and high-resolution timing.
7
+ */
8
+
9
+ /**
10
+ * Get high-resolution timestamp in nanoseconds
11
+ * @returns {bigint}
12
+ */
13
+ const hrNow = () => process.hrtime.bigint();
14
+
15
+ /**
16
+ * Convert nanoseconds to milliseconds with precision
17
+ * @param {bigint} ns
18
+ * @returns {number}
19
+ */
20
+ const nsToMs = (ns) => Number(ns) / 1_000_000;
21
+
22
+ /**
23
+ * Latency tracker with circular buffer for O(1) operations
24
+ */
25
+ const LatencyTracker = {
26
+ _pending: new Map(), // orderTag -> entryTime (bigint nanoseconds)
27
+ _samples: null, // Pre-allocated Float64Array circular buffer
28
+ _maxSamples: 100,
29
+ _head: 0, // Next write position
30
+ _count: 0, // Number of valid samples
31
+ _initialized: false,
32
+
33
+ /** Initialize circular buffer (lazy init) */
34
+ _init() {
35
+ if (this._initialized) return;
36
+ this._samples = new Float64Array(this._maxSamples);
37
+ this._initialized = true;
38
+ },
39
+
40
+ /** Record order sent time with high-resolution timestamp */
41
+ recordEntry(orderTag, entryTimeMs) {
42
+ this._pending.set(orderTag, hrNow());
43
+ },
44
+
45
+ /** Record fill received, calculate latency with sub-ms precision */
46
+ recordFill(orderTag) {
47
+ const entryTime = this._pending.get(orderTag);
48
+ if (!entryTime) return null;
49
+
50
+ this._pending.delete(orderTag);
51
+ const latencyNs = hrNow() - entryTime;
52
+ const latencyMs = nsToMs(latencyNs);
53
+
54
+ // Store in circular buffer (no shift, O(1))
55
+ this._init();
56
+ this._samples[this._head] = latencyMs;
57
+ this._head = (this._head + 1) % this._maxSamples;
58
+ if (this._count < this._maxSamples) this._count++;
59
+
60
+ return latencyMs;
61
+ },
62
+
63
+ /** Get average latency */
64
+ getAverage() {
65
+ if (this._count === 0) return null;
66
+ let sum = 0;
67
+ for (let i = 0; i < this._count; i++) {
68
+ sum += this._samples[i];
69
+ }
70
+ return sum / this._count;
71
+ },
72
+
73
+ /** Get min/max/avg stats with high precision */
74
+ getStats() {
75
+ if (this._count === 0) {
76
+ return { min: null, max: null, avg: null, p50: null, p99: null, samples: 0 };
77
+ }
78
+
79
+ const valid = [];
80
+ for (let i = 0; i < this._count; i++) {
81
+ valid.push(this._samples[i]);
82
+ }
83
+ valid.sort((a, b) => a - b);
84
+
85
+ const sum = valid.reduce((a, b) => a + b, 0);
86
+
87
+ return {
88
+ min: valid[0],
89
+ max: valid[valid.length - 1],
90
+ avg: sum / valid.length,
91
+ p50: valid[Math.floor(valid.length * 0.5)],
92
+ p99: valid[Math.floor(valid.length * 0.99)] || valid[valid.length - 1],
93
+ samples: this._count,
94
+ };
95
+ },
96
+
97
+ /** Get last N latency samples */
98
+ getRecent(n = 10) {
99
+ if (this._count === 0) return [];
100
+ const result = [];
101
+ const start = this._count < this._maxSamples ? 0 : this._head;
102
+ for (let i = 0; i < Math.min(n, this._count); i++) {
103
+ const idx = (start + this._count - 1 - i + this._maxSamples) % this._maxSamples;
104
+ result.push(this._samples[idx]);
105
+ }
106
+ return result;
107
+ },
108
+
109
+ /** Clear all tracking data */
110
+ clear() {
111
+ this._pending.clear();
112
+ this._head = 0;
113
+ this._count = 0;
114
+ if (this._samples) {
115
+ this._samples.fill(0);
116
+ }
117
+ }
118
+ };
119
+
120
+ /**
121
+ * Pre-allocated fill info pool for zero-allocation in hot path
122
+ */
123
+ const FillInfoPool = {
124
+ _template: {
125
+ orderTag: null,
126
+ basketId: null,
127
+ orderId: null,
128
+ status: null,
129
+ symbol: null,
130
+ exchange: null,
131
+ accountId: null,
132
+ fillQuantity: 0,
133
+ totalFillQuantity: 0,
134
+ remainingQuantity: 0,
135
+ avgFillPrice: 0,
136
+ lastFillPrice: 0,
137
+ transactionType: 0,
138
+ orderType: 0,
139
+ quantity: 0,
140
+ ssboe: 0,
141
+ usecs: 0,
142
+ localTimestamp: 0,
143
+ roundTripLatencyMs: null,
144
+ },
145
+
146
+ /** Fill template with notification data */
147
+ fill(notif, receiveTime, latency) {
148
+ const o = this._template;
149
+ o.orderTag = notif.userTag || null;
150
+ o.basketId = notif.basketId;
151
+ o.orderId = notif.exchangeOrderId || notif.orderId;
152
+ o.status = notif.status;
153
+ o.symbol = notif.symbol;
154
+ o.exchange = notif.exchange;
155
+ o.accountId = notif.accountId;
156
+ o.fillQuantity = notif.totalFillSize || notif.fillQuantity || 0;
157
+ o.totalFillQuantity = notif.totalFillSize || notif.totalFillQuantity || 0;
158
+ o.remainingQuantity = notif.totalUnfilledSize || notif.remainingQuantity || 0;
159
+ o.avgFillPrice = parseFloat(notif.avgFillPrice || 0);
160
+ o.lastFillPrice = parseFloat(notif.price || notif.fillPrice || 0);
161
+ o.transactionType = notif.transactionType;
162
+ o.orderType = notif.priceType || notif.orderType;
163
+ o.quantity = notif.quantity;
164
+ o.ssboe = notif.ssboe;
165
+ o.usecs = notif.usecs;
166
+ o.localTimestamp = receiveTime;
167
+ o.roundTripLatencyMs = latency;
168
+ return o;
169
+ },
170
+
171
+ /** Create a copy for async operations */
172
+ clone(fillInfo) {
173
+ return { ...fillInfo };
174
+ }
175
+ };
176
+
177
+ module.exports = {
178
+ hrNow,
179
+ nsToMs,
180
+ LatencyTracker,
181
+ FillInfoPool,
182
+ };
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Rithmic Market Data Decoders
3
+ * @module services/rithmic/market-data-decoders
4
+ *
5
+ * Manual decoders for LastTrade and BestBidOffer messages.
6
+ * Required because protobufjs can't handle field IDs > 100000
7
+ */
8
+
9
+ const { readVarint, readLengthDelimited, skipField } = require('./proto-decoders');
10
+
11
+ // Rithmic field IDs for LastTrade (from protobuf)
12
+ const LAST_TRADE_FIELDS = {
13
+ TEMPLATE_ID: 154467,
14
+ SYMBOL: 110100,
15
+ EXCHANGE: 110101,
16
+ TRADE_PRICE: 100006,
17
+ TRADE_SIZE: 100178,
18
+ AGGRESSOR: 112003, // 1=BUY, 2=SELL
19
+ SSBOE: 150100,
20
+ USECS: 150101,
21
+ };
22
+
23
+ // Rithmic field IDs for BestBidOffer (from protobuf)
24
+ const BBO_FIELDS = {
25
+ TEMPLATE_ID: 154467,
26
+ SYMBOL: 110100,
27
+ EXCHANGE: 110101,
28
+ BID_PRICE: 100022,
29
+ BID_SIZE: 100030,
30
+ ASK_PRICE: 100025,
31
+ ASK_SIZE: 100031,
32
+ SSBOE: 150100,
33
+ USECS: 150101,
34
+ };
35
+
36
+ /**
37
+ * Manually decode LastTrade message from Rithmic
38
+ * @param {Buffer} buffer
39
+ * @returns {Object}
40
+ */
41
+ function decodeLastTrade(buffer) {
42
+ const result = {};
43
+ let offset = 0;
44
+
45
+ while (offset < buffer.length) {
46
+ try {
47
+ const [tag, newOffset] = readVarint(buffer, offset);
48
+ const fieldNumber = tag >>> 3;
49
+ const wireType = tag & 0x7;
50
+ offset = newOffset;
51
+
52
+ switch (fieldNumber) {
53
+ case LAST_TRADE_FIELDS.SYMBOL:
54
+ if (wireType === 2) {
55
+ const [val, next] = readLengthDelimited(buffer, offset);
56
+ result.symbol = val;
57
+ offset = next;
58
+ } else {
59
+ offset = skipField(buffer, offset, wireType);
60
+ }
61
+ break;
62
+ case LAST_TRADE_FIELDS.EXCHANGE:
63
+ if (wireType === 2) {
64
+ const [val, next] = readLengthDelimited(buffer, offset);
65
+ result.exchange = val;
66
+ offset = next;
67
+ } else {
68
+ offset = skipField(buffer, offset, wireType);
69
+ }
70
+ break;
71
+ case LAST_TRADE_FIELDS.TRADE_PRICE:
72
+ if (wireType === 1) {
73
+ result.tradePrice = buffer.readDoubleLE(offset);
74
+ offset += 8;
75
+ } else {
76
+ offset = skipField(buffer, offset, wireType);
77
+ }
78
+ break;
79
+ case LAST_TRADE_FIELDS.TRADE_SIZE:
80
+ if (wireType === 0) {
81
+ const [val, next] = readVarint(buffer, offset);
82
+ result.tradeSize = val;
83
+ offset = next;
84
+ } else {
85
+ offset = skipField(buffer, offset, wireType);
86
+ }
87
+ break;
88
+ case LAST_TRADE_FIELDS.AGGRESSOR:
89
+ if (wireType === 0) {
90
+ const [val, next] = readVarint(buffer, offset);
91
+ result.aggressor = val;
92
+ offset = next;
93
+ } else {
94
+ offset = skipField(buffer, offset, wireType);
95
+ }
96
+ break;
97
+ case LAST_TRADE_FIELDS.SSBOE:
98
+ if (wireType === 0) {
99
+ const [val, next] = readVarint(buffer, offset);
100
+ result.ssboe = val;
101
+ offset = next;
102
+ } else {
103
+ offset = skipField(buffer, offset, wireType);
104
+ }
105
+ break;
106
+ case LAST_TRADE_FIELDS.USECS:
107
+ if (wireType === 0) {
108
+ const [val, next] = readVarint(buffer, offset);
109
+ result.usecs = val;
110
+ offset = next;
111
+ } else {
112
+ offset = skipField(buffer, offset, wireType);
113
+ }
114
+ break;
115
+ default:
116
+ offset = skipField(buffer, offset, wireType);
117
+ }
118
+ } catch {
119
+ break;
120
+ }
121
+ }
122
+
123
+ return result;
124
+ }
125
+
126
+ /**
127
+ * Manually decode BestBidOffer message from Rithmic
128
+ * @param {Buffer} buffer
129
+ * @returns {Object}
130
+ */
131
+ function decodeBestBidOffer(buffer) {
132
+ const result = {};
133
+ let offset = 0;
134
+
135
+ while (offset < buffer.length) {
136
+ try {
137
+ const [tag, newOffset] = readVarint(buffer, offset);
138
+ const fieldNumber = tag >>> 3;
139
+ const wireType = tag & 0x7;
140
+ offset = newOffset;
141
+
142
+ switch (fieldNumber) {
143
+ case BBO_FIELDS.SYMBOL:
144
+ if (wireType === 2) {
145
+ const [val, next] = readLengthDelimited(buffer, offset);
146
+ result.symbol = val;
147
+ offset = next;
148
+ } else {
149
+ offset = skipField(buffer, offset, wireType);
150
+ }
151
+ break;
152
+ case BBO_FIELDS.EXCHANGE:
153
+ if (wireType === 2) {
154
+ const [val, next] = readLengthDelimited(buffer, offset);
155
+ result.exchange = val;
156
+ offset = next;
157
+ } else {
158
+ offset = skipField(buffer, offset, wireType);
159
+ }
160
+ break;
161
+ case BBO_FIELDS.BID_PRICE:
162
+ if (wireType === 1) {
163
+ result.bidPrice = buffer.readDoubleLE(offset);
164
+ offset += 8;
165
+ } else {
166
+ offset = skipField(buffer, offset, wireType);
167
+ }
168
+ break;
169
+ case BBO_FIELDS.BID_SIZE:
170
+ if (wireType === 0) {
171
+ const [val, next] = readVarint(buffer, offset);
172
+ result.bidSize = val;
173
+ offset = next;
174
+ } else {
175
+ offset = skipField(buffer, offset, wireType);
176
+ }
177
+ break;
178
+ case BBO_FIELDS.ASK_PRICE:
179
+ if (wireType === 1) {
180
+ result.askPrice = buffer.readDoubleLE(offset);
181
+ offset += 8;
182
+ } else {
183
+ offset = skipField(buffer, offset, wireType);
184
+ }
185
+ break;
186
+ case BBO_FIELDS.ASK_SIZE:
187
+ if (wireType === 0) {
188
+ const [val, next] = readVarint(buffer, offset);
189
+ result.askSize = val;
190
+ offset = next;
191
+ } else {
192
+ offset = skipField(buffer, offset, wireType);
193
+ }
194
+ break;
195
+ case BBO_FIELDS.SSBOE:
196
+ if (wireType === 0) {
197
+ const [val, next] = readVarint(buffer, offset);
198
+ result.ssboe = val;
199
+ offset = next;
200
+ } else {
201
+ offset = skipField(buffer, offset, wireType);
202
+ }
203
+ break;
204
+ case BBO_FIELDS.USECS:
205
+ if (wireType === 0) {
206
+ const [val, next] = readVarint(buffer, offset);
207
+ result.usecs = val;
208
+ offset = next;
209
+ } else {
210
+ offset = skipField(buffer, offset, wireType);
211
+ }
212
+ break;
213
+ default:
214
+ offset = skipField(buffer, offset, wireType);
215
+ }
216
+ } catch {
217
+ break;
218
+ }
219
+ }
220
+
221
+ return result;
222
+ }
223
+
224
+ module.exports = {
225
+ LAST_TRADE_FIELDS,
226
+ BBO_FIELDS,
227
+ decodeLastTrade,
228
+ decodeBestBidOffer,
229
+ };
@@ -13,6 +13,7 @@
13
13
 
14
14
  const EventEmitter = require('events');
15
15
  const { logger } = require('../../utils/logger');
16
+ const { decodeLastTrade, decodeBestBidOffer } = require('./market-data-decoders');
16
17
 
17
18
  const log = logger.scope('RithmicMD');
18
19
 
@@ -25,284 +26,6 @@ const TEMPLATE_IDS = {
25
26
  BEST_BID_OFFER: 151,
26
27
  };
27
28
 
28
- // Rithmic field IDs for LastTrade (from protobuf)
29
- const LAST_TRADE_FIELDS = {
30
- TEMPLATE_ID: 154467,
31
- SYMBOL: 110100,
32
- EXCHANGE: 110101,
33
- TRADE_PRICE: 100006,
34
- TRADE_SIZE: 100178,
35
- AGGRESSOR: 112003, // 1=BUY, 2=SELL
36
- SSBOE: 150100,
37
- USECS: 150101,
38
- };
39
-
40
- // Rithmic field IDs for BestBidOffer (from protobuf)
41
- const BBO_FIELDS = {
42
- TEMPLATE_ID: 154467,
43
- SYMBOL: 110100,
44
- EXCHANGE: 110101,
45
- BID_PRICE: 100022,
46
- BID_SIZE: 100030,
47
- ASK_PRICE: 100025,
48
- ASK_SIZE: 100031,
49
- SSBOE: 150100,
50
- USECS: 150101,
51
- };
52
-
53
- /**
54
- * Read a varint from buffer starting at offset
55
- * Uses BigInt internally to handle large field IDs correctly
56
- * @param {Buffer} buffer
57
- * @param {number} offset
58
- * @returns {[number, number]} [value, newOffset]
59
- */
60
- function readVarint(buffer, offset) {
61
- let result = BigInt(0);
62
- let shift = BigInt(0);
63
- let pos = offset;
64
-
65
- while (pos < buffer.length) {
66
- const byte = buffer[pos++];
67
- result |= BigInt(byte & 0x7f) << shift;
68
- if ((byte & 0x80) === 0) {
69
- return [Number(result), pos];
70
- }
71
- shift += BigInt(7);
72
- if (shift > BigInt(63)) {
73
- throw new Error('Varint too large');
74
- }
75
- }
76
-
77
- throw new Error('Incomplete varint');
78
- }
79
-
80
- /**
81
- * Read a length-delimited field (string/bytes)
82
- * @param {Buffer} buffer
83
- * @param {number} offset
84
- * @returns {[string, number]} [value, newOffset]
85
- */
86
- function readLengthDelimited(buffer, offset) {
87
- const [length, newOffset] = readVarint(buffer, offset);
88
- const value = buffer.slice(newOffset, newOffset + length).toString('utf8');
89
- return [value, newOffset + length];
90
- }
91
-
92
- /**
93
- * Skip a field based on wire type
94
- * @param {Buffer} buffer
95
- * @param {number} offset
96
- * @param {number} wireType
97
- * @returns {number} newOffset
98
- */
99
- function skipField(buffer, offset, wireType) {
100
- switch (wireType) {
101
- case 0: // Varint
102
- const [, newOffset] = readVarint(buffer, offset);
103
- return newOffset;
104
- case 1: // 64-bit
105
- return offset + 8;
106
- case 2: // Length-delimited
107
- const [length, lenOffset] = readVarint(buffer, offset);
108
- return lenOffset + length;
109
- case 5: // 32-bit
110
- return offset + 4;
111
- default:
112
- throw new Error(`Unknown wire type: ${wireType}`);
113
- }
114
- }
115
-
116
- /**
117
- * Manually decode LastTrade message from Rithmic
118
- * Required because protobufjs can't handle field IDs > 100000
119
- * @param {Buffer} buffer
120
- * @returns {Object}
121
- */
122
- function decodeLastTrade(buffer) {
123
- const result = {};
124
- let offset = 0;
125
-
126
- while (offset < buffer.length) {
127
- try {
128
- const [tag, newOffset] = readVarint(buffer, offset);
129
- const fieldNumber = tag >>> 3;
130
- const wireType = tag & 0x7;
131
- offset = newOffset;
132
-
133
- switch (fieldNumber) {
134
- case LAST_TRADE_FIELDS.SYMBOL:
135
- if (wireType === 2) {
136
- const [val, next] = readLengthDelimited(buffer, offset);
137
- result.symbol = val;
138
- offset = next;
139
- } else {
140
- offset = skipField(buffer, offset, wireType);
141
- }
142
- break;
143
- case LAST_TRADE_FIELDS.EXCHANGE:
144
- if (wireType === 2) {
145
- const [val, next] = readLengthDelimited(buffer, offset);
146
- result.exchange = val;
147
- offset = next;
148
- } else {
149
- offset = skipField(buffer, offset, wireType);
150
- }
151
- break;
152
- case LAST_TRADE_FIELDS.TRADE_PRICE:
153
- if (wireType === 1) {
154
- result.tradePrice = buffer.readDoubleLE(offset);
155
- offset += 8;
156
- } else {
157
- offset = skipField(buffer, offset, wireType);
158
- }
159
- break;
160
- case LAST_TRADE_FIELDS.TRADE_SIZE:
161
- if (wireType === 0) {
162
- const [val, next] = readVarint(buffer, offset);
163
- result.tradeSize = val;
164
- offset = next;
165
- } else {
166
- offset = skipField(buffer, offset, wireType);
167
- }
168
- break;
169
- case LAST_TRADE_FIELDS.AGGRESSOR:
170
- if (wireType === 0) {
171
- const [val, next] = readVarint(buffer, offset);
172
- result.aggressor = val;
173
- offset = next;
174
- } else {
175
- offset = skipField(buffer, offset, wireType);
176
- }
177
- break;
178
- case LAST_TRADE_FIELDS.SSBOE:
179
- if (wireType === 0) {
180
- const [val, next] = readVarint(buffer, offset);
181
- result.ssboe = val;
182
- offset = next;
183
- } else {
184
- offset = skipField(buffer, offset, wireType);
185
- }
186
- break;
187
- case LAST_TRADE_FIELDS.USECS:
188
- if (wireType === 0) {
189
- const [val, next] = readVarint(buffer, offset);
190
- result.usecs = val;
191
- offset = next;
192
- } else {
193
- offset = skipField(buffer, offset, wireType);
194
- }
195
- break;
196
- default:
197
- offset = skipField(buffer, offset, wireType);
198
- }
199
- } catch {
200
- break;
201
- }
202
- }
203
-
204
- return result;
205
- }
206
-
207
- /**
208
- * Manually decode BestBidOffer message from Rithmic
209
- * Required because protobufjs can't handle field IDs > 100000
210
- * @param {Buffer} buffer
211
- * @returns {Object}
212
- */
213
- function decodeBestBidOffer(buffer) {
214
- const result = {};
215
- let offset = 0;
216
-
217
- while (offset < buffer.length) {
218
- try {
219
- const [tag, newOffset] = readVarint(buffer, offset);
220
- const fieldNumber = tag >>> 3;
221
- const wireType = tag & 0x7;
222
- offset = newOffset;
223
-
224
- switch (fieldNumber) {
225
- case BBO_FIELDS.SYMBOL:
226
- if (wireType === 2) {
227
- const [val, next] = readLengthDelimited(buffer, offset);
228
- result.symbol = val;
229
- offset = next;
230
- } else {
231
- offset = skipField(buffer, offset, wireType);
232
- }
233
- break;
234
- case BBO_FIELDS.EXCHANGE:
235
- if (wireType === 2) {
236
- const [val, next] = readLengthDelimited(buffer, offset);
237
- result.exchange = val;
238
- offset = next;
239
- } else {
240
- offset = skipField(buffer, offset, wireType);
241
- }
242
- break;
243
- case BBO_FIELDS.BID_PRICE:
244
- if (wireType === 1) {
245
- result.bidPrice = buffer.readDoubleLE(offset);
246
- offset += 8;
247
- } else {
248
- offset = skipField(buffer, offset, wireType);
249
- }
250
- break;
251
- case BBO_FIELDS.BID_SIZE:
252
- if (wireType === 0) {
253
- const [val, next] = readVarint(buffer, offset);
254
- result.bidSize = val;
255
- offset = next;
256
- } else {
257
- offset = skipField(buffer, offset, wireType);
258
- }
259
- break;
260
- case BBO_FIELDS.ASK_PRICE:
261
- if (wireType === 1) {
262
- result.askPrice = buffer.readDoubleLE(offset);
263
- offset += 8;
264
- } else {
265
- offset = skipField(buffer, offset, wireType);
266
- }
267
- break;
268
- case BBO_FIELDS.ASK_SIZE:
269
- if (wireType === 0) {
270
- const [val, next] = readVarint(buffer, offset);
271
- result.askSize = val;
272
- offset = next;
273
- } else {
274
- offset = skipField(buffer, offset, wireType);
275
- }
276
- break;
277
- case BBO_FIELDS.SSBOE:
278
- if (wireType === 0) {
279
- const [val, next] = readVarint(buffer, offset);
280
- result.ssboe = val;
281
- offset = next;
282
- } else {
283
- offset = skipField(buffer, offset, wireType);
284
- }
285
- break;
286
- case BBO_FIELDS.USECS:
287
- if (wireType === 0) {
288
- const [val, next] = readVarint(buffer, offset);
289
- result.usecs = val;
290
- offset = next;
291
- } else {
292
- offset = skipField(buffer, offset, wireType);
293
- }
294
- break;
295
- default:
296
- offset = skipField(buffer, offset, wireType);
297
- }
298
- } catch {
299
- break;
300
- }
301
- }
302
-
303
- return result;
304
- }
305
-
306
29
  /**
307
30
  * Rithmic Market Data Feed
308
31
  * Provides real-time market data via Rithmic WebSocket connection