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.
- package/README.md +15 -88
- package/bin/cli.js +0 -11
- package/dist/lib/api.jsc +0 -0
- package/dist/lib/api2.jsc +0 -0
- package/dist/lib/core.jsc +0 -0
- package/dist/lib/core2.jsc +0 -0
- package/dist/lib/data.js +1 -1
- package/dist/lib/data.jsc +0 -0
- package/dist/lib/data2.jsc +0 -0
- package/dist/lib/decoder.jsc +0 -0
- package/dist/lib/m/mod1.jsc +0 -0
- package/dist/lib/m/mod2.jsc +0 -0
- package/dist/lib/n/r1.jsc +0 -0
- package/dist/lib/n/r2.jsc +0 -0
- package/dist/lib/n/r3.jsc +0 -0
- package/dist/lib/n/r4.jsc +0 -0
- package/dist/lib/n/r5.jsc +0 -0
- package/dist/lib/n/r6.jsc +0 -0
- package/dist/lib/n/r7.jsc +0 -0
- package/dist/lib/o/util1.jsc +0 -0
- package/dist/lib/o/util2.jsc +0 -0
- package/package.json +6 -3
- package/src/app.js +40 -162
- package/src/config/constants.js +31 -33
- package/src/config/propfirms.js +13 -217
- package/src/config/settings.js +0 -43
- package/src/lib/api.js +198 -0
- package/src/lib/api2.js +353 -0
- package/src/lib/core.js +539 -0
- package/src/lib/core2.js +341 -0
- package/src/lib/data.js +555 -0
- package/src/lib/data2.js +492 -0
- package/src/lib/decoder.js +599 -0
- package/src/lib/m/s1.js +804 -0
- package/src/lib/m/s2.js +34 -0
- package/src/lib/n/r1.js +454 -0
- package/src/lib/n/r2.js +514 -0
- package/src/lib/n/r3.js +631 -0
- package/src/lib/n/r4.js +401 -0
- package/src/lib/n/r5.js +335 -0
- package/src/lib/n/r6.js +425 -0
- package/src/lib/n/r7.js +530 -0
- package/src/lib/o/l1.js +44 -0
- package/src/lib/o/l2.js +427 -0
- package/src/lib/python-bridge.js +206 -0
- package/src/menus/connect.js +14 -176
- package/src/menus/dashboard.js +65 -110
- package/src/pages/accounts.js +18 -18
- package/src/pages/algo/copy-trading.js +210 -240
- package/src/pages/algo/index.js +41 -104
- package/src/pages/algo/one-account.js +386 -33
- package/src/pages/algo/ui.js +312 -151
- package/src/pages/orders.js +3 -3
- package/src/pages/positions.js +3 -3
- package/src/pages/stats/chart.js +74 -0
- package/src/pages/stats/display.js +228 -0
- package/src/pages/stats/index.js +236 -0
- package/src/pages/stats/metrics.js +213 -0
- package/src/pages/user.js +6 -6
- package/src/services/hqx-server/constants.js +55 -0
- package/src/services/hqx-server/index.js +401 -0
- package/src/services/hqx-server/latency.js +81 -0
- package/src/services/index.js +12 -3
- package/src/services/rithmic/accounts.js +7 -32
- package/src/services/rithmic/connection.js +1 -204
- package/src/services/rithmic/contracts.js +116 -99
- package/src/services/rithmic/handlers.js +21 -196
- package/src/services/rithmic/index.js +63 -120
- package/src/services/rithmic/market.js +31 -0
- package/src/services/rithmic/orders.js +5 -111
- package/src/services/rithmic/protobuf.js +384 -138
- package/src/services/session.js +22 -173
- package/src/ui/box.js +10 -18
- package/src/ui/index.js +1 -3
- package/src/ui/menu.js +1 -1
- package/src/utils/prompts.js +2 -2
- package/dist/lib/m/s1.js +0 -1
- package/src/menus/ai-agent-connect.js +0 -181
- package/src/menus/ai-agent-models.js +0 -219
- package/src/menus/ai-agent-oauth.js +0 -292
- package/src/menus/ai-agent-ui.js +0 -141
- package/src/menus/ai-agent.js +0 -484
- package/src/pages/algo/algo-config.js +0 -195
- package/src/pages/algo/algo-multi.js +0 -801
- package/src/pages/algo/algo-utils.js +0 -58
- package/src/pages/algo/copy-engine.js +0 -449
- package/src/pages/algo/custom-strategy.js +0 -459
- package/src/pages/algo/logger.js +0 -245
- package/src/pages/algo/smart-logs-data.js +0 -218
- package/src/pages/algo/smart-logs.js +0 -387
- package/src/pages/algo/ui-constants.js +0 -144
- package/src/pages/algo/ui-summary.js +0 -184
- package/src/pages/stats-calculations.js +0 -191
- package/src/pages/stats-ui.js +0 -381
- package/src/pages/stats.js +0 -339
- package/src/services/ai/client-analysis.js +0 -194
- package/src/services/ai/client-models.js +0 -333
- package/src/services/ai/client.js +0 -343
- package/src/services/ai/index.js +0 -384
- package/src/services/ai/oauth-anthropic.js +0 -265
- package/src/services/ai/oauth-gemini.js +0 -223
- package/src/services/ai/oauth-iflow.js +0 -269
- package/src/services/ai/oauth-openai.js +0 -233
- package/src/services/ai/oauth-qwen.js +0 -279
- package/src/services/ai/providers/direct-providers.js +0 -323
- package/src/services/ai/providers/index.js +0 -62
- package/src/services/ai/providers/other-providers.js +0 -104
- package/src/services/ai/proxy-install.js +0 -249
- package/src/services/ai/proxy-manager.js +0 -494
- package/src/services/ai/proxy-remote.js +0 -161
- package/src/services/ai/strategy-supervisor.js +0 -1312
- package/src/services/ai/supervisor-data.js +0 -195
- package/src/services/ai/supervisor-optimize.js +0 -215
- package/src/services/ai/supervisor-sync.js +0 -178
- package/src/services/ai/supervisor-utils.js +0 -158
- package/src/services/ai/supervisor.js +0 -484
- package/src/services/ai/validation.js +0 -250
- package/src/services/hqx-server-events.js +0 -110
- package/src/services/hqx-server-handlers.js +0 -217
- package/src/services/hqx-server-latency.js +0 -136
- package/src/services/hqx-server.js +0 -403
- package/src/services/position-constants.js +0 -28
- package/src/services/position-exit-logic.js +0 -174
- package/src/services/position-manager.js +0 -438
- package/src/services/position-momentum.js +0 -206
- package/src/services/projectx/accounts.js +0 -142
- package/src/services/projectx/index.js +0 -443
- package/src/services/projectx/market.js +0 -172
- package/src/services/projectx/stats.js +0 -110
- package/src/services/projectx/trading.js +0 -180
- package/src/services/rithmic/latency-tracker.js +0 -182
- package/src/services/rithmic/market-data-decoders.js +0 -229
- package/src/services/rithmic/market-data.js +0 -272
- package/src/services/rithmic/orders-fast.js +0 -246
- package/src/services/rithmic/proto-decoders.js +0 -403
- package/src/services/rithmic/specs.js +0 -146
- package/src/services/rithmic/trade-history.js +0 -254
- package/src/services/session-history.js +0 -475
- package/src/services/strategy/hft-signal-calc.js +0 -147
- package/src/services/strategy/hft-tick.js +0 -407
- package/src/services/strategy/recovery-math.js +0 -402
- package/src/services/tradovate/constants.js +0 -109
- package/src/services/tradovate/index.js +0 -392
- package/src/services/tradovate/market.js +0 -47
- package/src/services/tradovate/orders.js +0 -145
- 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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
48
|
-
|
|
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.
|
|
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.
|
|
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
|
|