hedgequantx 1.2.34 → 1.2.35

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 (48) hide show
  1. package/package.json +2 -1
  2. package/src/app.js +114 -5
  3. package/src/services/rithmic/connection.js +203 -0
  4. package/src/services/rithmic/constants.js +156 -0
  5. package/src/services/rithmic/index.js +487 -0
  6. package/src/services/rithmic/proto/account_pnl_position_update.proto +59 -0
  7. package/src/services/rithmic/proto/base.proto +7 -0
  8. package/src/services/rithmic/proto/best_bid_offer.proto +39 -0
  9. package/src/services/rithmic/proto/exchange_order_notification.proto +140 -0
  10. package/src/services/rithmic/proto/instrument_pnl_position_update.proto +50 -0
  11. package/src/services/rithmic/proto/last_trade.proto +53 -0
  12. package/src/services/rithmic/proto/request_account_list.proto +20 -0
  13. package/src/services/rithmic/proto/request_cancel_all_orders.proto +15 -0
  14. package/src/services/rithmic/proto/request_heartbeat.proto +13 -0
  15. package/src/services/rithmic/proto/request_login.proto +28 -0
  16. package/src/services/rithmic/proto/request_login_info.proto +10 -0
  17. package/src/services/rithmic/proto/request_logout.proto +10 -0
  18. package/src/services/rithmic/proto/request_market_data_update.proto +42 -0
  19. package/src/services/rithmic/proto/request_new_order.proto +84 -0
  20. package/src/services/rithmic/proto/request_pnl_position_snapshot.proto +14 -0
  21. package/src/services/rithmic/proto/request_pnl_position_updates.proto +20 -0
  22. package/src/services/rithmic/proto/request_rithmic_system_info.proto +8 -0
  23. package/src/services/rithmic/proto/request_show_order_history.proto +16 -0
  24. package/src/services/rithmic/proto/request_show_order_history_dates.proto +10 -0
  25. package/src/services/rithmic/proto/request_show_order_history_summary.proto +14 -0
  26. package/src/services/rithmic/proto/request_show_orders.proto +14 -0
  27. package/src/services/rithmic/proto/request_subscribe_for_order_updates.proto +14 -0
  28. package/src/services/rithmic/proto/request_tick_bar_replay.proto +48 -0
  29. package/src/services/rithmic/proto/request_trade_routes.proto +11 -0
  30. package/src/services/rithmic/proto/response_account_list.proto +18 -0
  31. package/src/services/rithmic/proto/response_heartbeat.proto +14 -0
  32. package/src/services/rithmic/proto/response_login.proto +18 -0
  33. package/src/services/rithmic/proto/response_login_info.proto +24 -0
  34. package/src/services/rithmic/proto/response_logout.proto +11 -0
  35. package/src/services/rithmic/proto/response_market_data_update.proto +9 -0
  36. package/src/services/rithmic/proto/response_new_order.proto +18 -0
  37. package/src/services/rithmic/proto/response_pnl_position_snapshot.proto +11 -0
  38. package/src/services/rithmic/proto/response_pnl_position_updates.proto +11 -0
  39. package/src/services/rithmic/proto/response_rithmic_system_info.proto +12 -0
  40. package/src/services/rithmic/proto/response_show_order_history.proto +11 -0
  41. package/src/services/rithmic/proto/response_show_order_history_dates.proto +13 -0
  42. package/src/services/rithmic/proto/response_show_order_history_summary.proto +11 -0
  43. package/src/services/rithmic/proto/response_show_orders.proto +11 -0
  44. package/src/services/rithmic/proto/response_subscribe_for_order_updates.proto +11 -0
  45. package/src/services/rithmic/proto/response_tick_bar_replay.proto +40 -0
  46. package/src/services/rithmic/proto/response_trade_routes.proto +19 -0
  47. package/src/services/rithmic/proto/rithmic_order_notification.proto +124 -0
  48. package/src/services/rithmic/protobuf.js +259 -0
@@ -0,0 +1,18 @@
1
+
2
+ package rti;
3
+
4
+ message ResponseAccountList
5
+ {
6
+ required int32 template_id = 154467;
7
+ repeated string user_msg = 132760;
8
+ repeated string rq_handler_rp_code = 132764;
9
+ repeated string rp_code = 132766;
10
+
11
+ optional string fcm_id = 154013;
12
+ optional string ib_id = 154014;
13
+ optional string account_id = 154008;
14
+ optional string account_name = 154002;
15
+ optional string account_currency = 154383;
16
+ optional string account_auto_liquidate = 131035;
17
+ optional string auto_liq_threshold_current_value = 131040;
18
+ }
@@ -0,0 +1,14 @@
1
+
2
+ package rti;
3
+
4
+ message ResponseHeartbeat
5
+ {
6
+ // PB_OFFSET = 100000, is the offset added for each MNM field id
7
+
8
+ required int32 template_id = 154467; // PB_OFFSET + MNM_TEMPLATE_ID
9
+ repeated string user_msg = 132760; // PB_OFFSET + MNM_USER_MSG
10
+ repeated string rp_code = 132766; // PB_OFFSET + MNM_RESPONSE_CODE
11
+
12
+ optional int32 ssboe = 150100; // PB_OFFSET + MNM_SECONDS_SINCE_BOE
13
+ optional int32 usecs = 150101; // PB_OFFSET + MNM_USECS
14
+ }
@@ -0,0 +1,18 @@
1
+
2
+ package rti;
3
+
4
+ message ResponseLogin
5
+ {
6
+ required int32 template_id = 154467;
7
+ optional string template_version = 153634;
8
+ repeated string user_msg = 132760;
9
+ repeated string rp_code = 132766;
10
+
11
+ optional string fcm_id = 154013;
12
+ optional string ib_id = 154014;
13
+ optional string country_code = 154712;
14
+ optional string state_code = 154713;
15
+
16
+ optional string unique_user_id = 153428;
17
+ optional double heartbeat_interval = 153633;
18
+ }
@@ -0,0 +1,24 @@
1
+
2
+ package rti;
3
+
4
+ message ResponseLoginInfo
5
+ {
6
+ // PB_OFFSET = 100000, is the offset added for each MNM field id
7
+
8
+ enum UserType {
9
+ USER_TYPE_ADMIN = 0;
10
+ USER_TYPE_FCM = 1;
11
+ USER_TYPE_IB = 2;
12
+ USER_TYPE_TRADER = 3;
13
+ }
14
+
15
+ required int32 template_id = 154467; // PB_OFFSET + MNM_TEMPLATE_ID
16
+ repeated string user_msg = 132760; // PB_OFFSET + MNM_USER_MSG
17
+ repeated string rp_code = 132766; // PB_OFFSET + MNM_RESPONSE_CODE
18
+
19
+ optional string fcm_id = 154013; // PB_OFFSET + MNM_FCM_ID
20
+ optional string ib_id = 154014; // PB_OFFSET + MNM_IB_ID
21
+ optional string first_name = 154216; // PB_OFFSET + MNM_FIRST_NAME
22
+ optional string last_name = 154217; // PB_OFFSET + MNM_LAST_NAME
23
+ optional UserType user_type = 154036; // PB_OFFSET + MNM_USER_TYPE
24
+ }
@@ -0,0 +1,11 @@
1
+
2
+ package rti;
3
+
4
+ message ResponseLogout
5
+ {
6
+ // PB_OFFSET = 100000 , is the offset added for each MNM field id
7
+
8
+ required int32 template_id = 154467; // PB_OFFSET + MNM_TEMPLATE_ID
9
+ repeated string user_msg = 132760; // PB_OFFSET + MNM_USER_MSG
10
+ repeated string rp_code = 132766; // PB_OFFSET + MNM_RESPONSE_CODE
11
+ }
@@ -0,0 +1,9 @@
1
+
2
+ package rti;
3
+
4
+ message ResponseMarketDataUpdate
5
+ {
6
+ required int32 template_id = 154467;
7
+ repeated string user_msg = 132760;
8
+ repeated string rp_code = 132766;
9
+ }
@@ -0,0 +1,18 @@
1
+
2
+ package rti;
3
+
4
+ message ResponseNewOrder
5
+ {
6
+ // PB_OFFSET = 100000, is the offset added for each MNM field id
7
+
8
+ required int32 template_id = 154467; // PB_OFFSET + MNM_TEMPLATE_ID
9
+ repeated string user_msg = 132760; // PB_OFFSET + MNM_USER_MSG
10
+ optional string user_tag = 154119; // PB_OFFSET + MNM_USER_ORIGIN
11
+ repeated string rq_handler_rp_code = 132764; // PB_OFFSET + MNM_REQUEST_HANDLER_RESPONSE_CODE
12
+ repeated string rp_code = 132766; // PB_OFFSET + MNM_RESPONSE_CODE
13
+
14
+ optional string basket_id = 110300; // PB_OFFSET + MNM_BASKET_ID
15
+
16
+ optional int32 ssboe = 150100; // PB_OFFSET + MNM_SECONDS_SINCE_BOE
17
+ optional int32 usecs = 150101; // PB_OFFSET + MNM_USECS
18
+ }
@@ -0,0 +1,11 @@
1
+
2
+ package rti;
3
+
4
+ message ResponsePnLPositionSnapshot
5
+ {
6
+ // PB_OFFSET = 100000, is the offset added for each MNM field id
7
+
8
+ required int32 template_id = 154467; // PB_OFFSET + MNM_TEMPLATE_ID
9
+ repeated string user_msg = 132760; // PB_OFFSET + MNM_USER_MSG
10
+ repeated string rp_code = 132766; // PB_OFFSET + MNM_RESPONSE_CODE
11
+ }
@@ -0,0 +1,11 @@
1
+
2
+ package rti;
3
+
4
+ message ResponsePnLPositionUpdates
5
+ {
6
+ // PB_OFFSET = 100000, is the offset added for each MNM field id
7
+
8
+ required int32 template_id = 154467; // PB_OFFSET + MNM_TEMPLATE_ID
9
+ repeated string user_msg = 132760; // PB_OFFSET + MNM_USER_MSG
10
+ repeated string rp_code = 132766; // PB_OFFSET + MNM_RESPONSE_CODE
11
+ }
@@ -0,0 +1,12 @@
1
+
2
+ package rti;
3
+
4
+ message ResponseRithmicSystemInfo
5
+ {
6
+ required int32 template_id = 154467;
7
+ repeated string user_msg = 132760;
8
+ repeated string rp_code = 132766;
9
+
10
+ repeated string system_name = 153628;
11
+ repeated bool has_aggregated_quotes = 153649;
12
+ }
@@ -0,0 +1,11 @@
1
+
2
+ package rti;
3
+
4
+ message ResponseShowOrderHistory
5
+ {
6
+ // PB_OFFSET = 100000, is the offset added for each MNM field id
7
+
8
+ required int32 template_id = 154467; // PB_OFFSET + MNM_TEMPLATE_ID
9
+ repeated string user_msg = 132760; // PB_OFFSET + MNM_USER_MSG
10
+ repeated string rp_code = 132766; // PB_OFFSET + MNM_RESPONSE_CODE
11
+ }
@@ -0,0 +1,13 @@
1
+
2
+ package rti;
3
+
4
+ message ResponseShowOrderHistoryDates
5
+ {
6
+ // CORRECT field IDs from rundef/async_rithmic (verified Dec 2025)
7
+ // PB_OFFSET = 100000
8
+ optional int32 template_id = 154467;
9
+ repeated string user_msg = 132760;
10
+ repeated string rq_handler_rp_code = 132764;
11
+ repeated string rp_code = 132766;
12
+ repeated string date = 110615; // Available dates (YYYYMMDD)
13
+ }
@@ -0,0 +1,11 @@
1
+
2
+ package rti;
3
+
4
+ message ResponseShowOrderHistorySummary
5
+ {
6
+ // Based on async_rithmic proto definitions
7
+ optional int32 template_id = 156580;
8
+ repeated string user_msg = 142744;
9
+ repeated string rp_code = 142750;
10
+ optional int32 fills_collected = 156569;
11
+ }
@@ -0,0 +1,11 @@
1
+
2
+ package rti;
3
+
4
+ message ResponseShowOrders
5
+ {
6
+ // PB_OFFSET = 100000, is the offset added for each MNM field id
7
+
8
+ required int32 template_id = 154467; // PB_OFFSET + MNM_TEMPLATE_ID
9
+ repeated string user_msg = 132760; // PB_OFFSET + MNM_USER_MSG
10
+ repeated string rp_code = 132766; // PB_OFFSET + MNM_RESPONSE_CODE
11
+ }
@@ -0,0 +1,11 @@
1
+
2
+ package rti;
3
+
4
+ message ResponseSubscribeForOrderUpdates
5
+ {
6
+ // PB_OFFSET = 100000, is the offset added for each MNM field id
7
+
8
+ required int32 template_id = 154467; // PB_OFFSET + MNM_TEMPLATE_ID
9
+ repeated string user_msg = 132760; // PB_OFFSET + MNM_USER_MSG
10
+ repeated string rp_code = 132766; // PB_OFFSET + MNM_RESPONSE_CODE
11
+ }
@@ -0,0 +1,40 @@
1
+
2
+ package rti;
3
+
4
+ message ResponseTickBarReplay
5
+ {
6
+ enum BarType {
7
+ TICK_BAR = 1;
8
+ RANGE_BAR = 2;
9
+ VOLUME_BAR = 3;
10
+ }
11
+
12
+ enum BarSubType {
13
+ REGULAR = 1;
14
+ CUSTOM = 2;
15
+ }
16
+
17
+ required int32 template_id = 154467;
18
+ optional string request_key = 132758;
19
+ repeated string user_msg = 132760;
20
+ repeated string rq_handler_rp_code = 132764;
21
+ repeated string rp_code = 132766;
22
+
23
+ optional string symbol = 110100;
24
+ optional string exchange = 110101;
25
+
26
+ optional BarType type = 119200;
27
+ optional BarSubType sub_type = 119208;
28
+ optional string type_specifier = 148162;
29
+ optional uint64 num_trades = 119204;
30
+ optional uint64 volume = 119205;
31
+ optional uint64 bid_volume = 119213;
32
+ optional uint64 ask_volume = 119214;
33
+ optional double open_price = 100019;
34
+ optional double close_price = 100021;
35
+ optional double high_price = 100012;
36
+ optional double low_price = 100013;
37
+ optional int32 custom_session_open_ssm = 119209;
38
+ repeated int32 data_bar_ssboe = 119202;
39
+ repeated int32 data_bar_usecs = 119203;
40
+ }
@@ -0,0 +1,19 @@
1
+
2
+ package rti;
3
+
4
+ message ResponseTradeRoutes
5
+ {
6
+ // PB_OFFSET = 100000, is the offset added for each MNM field id
7
+
8
+ required int32 template_id = 154467; // PB_OFFSET + MNM_TEMPLATE_ID
9
+ repeated string user_msg = 132760; // PB_OFFSET + MNM_USER_MSG
10
+ repeated string rq_handler_rp_code = 132764; // PB_OFFSET + MNM_REQUEST_HANDLER_RESPONSE_CODE
11
+ repeated string rp_code = 132766; // PB_OFFSET + MNM_RESPONSE_CODE
12
+
13
+ optional string fcm_id = 154013; // PB_OFFSET + MNM_FCM_ID
14
+ optional string ib_id = 154014; // PB_OFFSET + MNM_IB_ID
15
+ optional string exchange = 110101; // PB_OFFSET + MNM_EXCHANGE
16
+ optional string trade_route = 112016; // PB_OFFSET + MNM_TRADE_ROUTE
17
+ optional string status = 131407; // PB_OFFSET + MNM_SERVICE_STATE
18
+ optional bool is_default = 154689; // PB_OFFSET + MNM_DEFAULT_ROUTE
19
+ }
@@ -0,0 +1,124 @@
1
+
2
+ package rti;
3
+
4
+ message RithmicOrderNotification
5
+ {
6
+ enum NotifyType {
7
+ ORDER_RCVD_FROM_CLNT = 1;
8
+ MODIFY_RCVD_FROM_CLNT = 2;
9
+ CANCEL_RCVD_FROM_CLNT = 3;
10
+ OPEN_PENDING = 4;
11
+ MODIFY_PENDING = 5;
12
+ CANCEL_PENDING = 6;
13
+ ORDER_RCVD_BY_EXCH_GTWY = 7;
14
+ MODIFY_RCVD_BY_EXCH_GTWY = 8;
15
+ CANCEL_RCVD_BY_EXCH_GTWY = 9;
16
+ ORDER_SENT_TO_EXCH = 10;
17
+ MODIFY_SENT_TO_EXCH = 11;
18
+ CANCEL_SENT_TO_EXCH = 12;
19
+ OPEN = 13;
20
+ MODIFIED = 14;
21
+ COMPLETE = 15;
22
+ MODIFICATION_FAILED = 16;
23
+ CANCELLATION_FAILED = 17;
24
+ TRIGGER_PENDING = 18;
25
+ GENERIC = 19;
26
+ LINK_ORDERS_FAILED = 20;
27
+ }
28
+
29
+ enum TransactionType {
30
+ BUY = 1;
31
+ SELL = 2;
32
+ SS = 3;
33
+ }
34
+
35
+ enum Duration {
36
+ DAY = 1;
37
+ GTC = 2;
38
+ IOC = 3;
39
+ FOK = 4;
40
+ }
41
+
42
+ enum PriceType {
43
+ LIMIT = 1;
44
+ MARKET = 2;
45
+ STOP_LIMIT = 3;
46
+ STOP_MARKET = 4;
47
+ }
48
+
49
+ enum BracketType {
50
+ STOP_ONLY = 1;
51
+ TARGET_ONLY = 2;
52
+ TARGET_AND_STOP = 3;
53
+ STOP_ONLY_STATIC = 4;
54
+ TARGET_ONLY_STATIC = 5;
55
+ TARGET_AND_STOP_STATIC = 6;
56
+ }
57
+
58
+ enum OrderPlacement {
59
+ MANUAL = 1;
60
+ AUTO = 2;
61
+ }
62
+
63
+ required int32 template_id = 154467;
64
+ optional string user_tag = 154119;
65
+
66
+ optional NotifyType notify_type = 153625;
67
+ optional bool is_snapshot = 110121;
68
+
69
+ optional string status = 110303;
70
+ optional string basket_id = 110300;
71
+ optional string original_basket_id = 154497;
72
+ optional string linked_basket_ids = 110358;
73
+
74
+ optional string fcm_id = 154013;
75
+ optional string ib_id = 154014;
76
+ optional string user_id = 131003;
77
+ optional string account_id = 154008;
78
+
79
+ optional string symbol = 110100;
80
+ optional string exchange = 110101;
81
+ optional string trade_exchange = 112021;
82
+ optional string trade_route = 112016;
83
+ optional string exchange_order_id = 149238;
84
+ optional string instrument_type = 110116;
85
+ optional string completion_reason = 149273;
86
+
87
+ optional int32 quantity = 112004;
88
+ optional int32 quan_release_pending = 112027;
89
+ optional double price = 110306;
90
+ optional double trigger_price = 149247;
91
+
92
+ optional TransactionType transaction_type = 112003;
93
+ optional Duration duration = 112005;
94
+ optional PriceType price_type = 112008;
95
+ optional PriceType orig_price_type = 154770;
96
+ optional OrderPlacement manual_or_auto = 154710;
97
+ optional BracketType bracket_type = 157087;
98
+
99
+ optional double avg_fill_price = 110322;
100
+
101
+ optional int32 total_fill_size = 154111;
102
+ optional int32 total_unfilled_size = 154112;
103
+
104
+ optional string sequence_number = 112002;
105
+ optional string orig_sequence_number = 149263;
106
+ optional string cor_sequence_number = 149264;
107
+
108
+ optional string currency = 154382;
109
+ optional string country_code = 154172;
110
+
111
+ optional string text = 120008;
112
+ optional string report_text = 120028;
113
+ optional string remarks = 154806;
114
+
115
+ optional string window_name = 154629;
116
+ optional string originator_window_name = 154671;
117
+
118
+ optional int32 cancel_at_ssboe = 157085;
119
+ optional int32 cancel_at_usecs = 157086;
120
+ optional int32 cancel_after_secs = 154488;
121
+
122
+ optional int32 ssboe = 150100;
123
+ optional int32 usecs = 150101;
124
+ }
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Rithmic Protobuf Handler
3
+ * Handles encoding/decoding of Rithmic protobuf messages
4
+ */
5
+
6
+ const protobuf = require('protobufjs');
7
+ const path = require('path');
8
+ 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
+ /**
32
+ * Read a varint from buffer
33
+ */
34
+ function readVarint(buffer, offset) {
35
+ let result = BigInt(0);
36
+ let shift = BigInt(0);
37
+ let pos = offset;
38
+
39
+ while (pos < buffer.length) {
40
+ const byte = buffer[pos++];
41
+ result |= BigInt(byte & 0x7f) << shift;
42
+ if ((byte & 0x80) === 0) {
43
+ return [Number(result), pos];
44
+ }
45
+ shift += BigInt(7);
46
+ if (shift > BigInt(63)) {
47
+ throw new Error('Varint too large');
48
+ }
49
+ }
50
+ throw new Error('Incomplete varint');
51
+ }
52
+
53
+ /**
54
+ * Read a length-delimited field (string/bytes)
55
+ */
56
+ function readLengthDelimited(buffer, offset) {
57
+ const [length, newOffset] = readVarint(buffer, offset);
58
+ const value = buffer.slice(newOffset, newOffset + length).toString('utf8');
59
+ return [value, newOffset + length];
60
+ }
61
+
62
+ /**
63
+ * Skip a field based on wire type
64
+ */
65
+ function skipField(buffer, offset, wireType) {
66
+ switch (wireType) {
67
+ case 0: // Varint
68
+ const [, newOffset] = readVarint(buffer, offset);
69
+ return newOffset;
70
+ case 1: // 64-bit
71
+ return offset + 8;
72
+ case 2: // Length-delimited
73
+ const [length, lenOffset] = readVarint(buffer, offset);
74
+ return lenOffset + length;
75
+ case 5: // 32-bit
76
+ return offset + 4;
77
+ default:
78
+ throw new Error(`Unknown wire type: ${wireType}`);
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Manually decode AccountPnL from raw bytes
84
+ */
85
+ function decodeAccountPnL(buffer) {
86
+ const result = {};
87
+ let offset = 0;
88
+
89
+ while (offset < buffer.length) {
90
+ try {
91
+ const [tag, tagOffset] = readVarint(buffer, offset);
92
+ const wireType = tag & 0x7;
93
+ const fieldNumber = tag >>> 3;
94
+ offset = tagOffset;
95
+
96
+ switch (fieldNumber) {
97
+ case PNL_FIELDS.TEMPLATE_ID:
98
+ [result.templateId, offset] = readVarint(buffer, offset);
99
+ break;
100
+ case PNL_FIELDS.IS_SNAPSHOT:
101
+ const [isSnap, snapOffset] = readVarint(buffer, offset);
102
+ result.isSnapshot = isSnap !== 0;
103
+ offset = snapOffset;
104
+ break;
105
+ case PNL_FIELDS.FCM_ID:
106
+ [result.fcmId, offset] = readLengthDelimited(buffer, offset);
107
+ break;
108
+ case PNL_FIELDS.IB_ID:
109
+ [result.ibId, offset] = readLengthDelimited(buffer, offset);
110
+ break;
111
+ case PNL_FIELDS.ACCOUNT_ID:
112
+ [result.accountId, offset] = readLengthDelimited(buffer, offset);
113
+ break;
114
+ case PNL_FIELDS.ACCOUNT_BALANCE:
115
+ [result.accountBalance, offset] = readLengthDelimited(buffer, offset);
116
+ break;
117
+ case PNL_FIELDS.CASH_ON_HAND:
118
+ [result.cashOnHand, offset] = readLengthDelimited(buffer, offset);
119
+ break;
120
+ case PNL_FIELDS.MARGIN_BALANCE:
121
+ [result.marginBalance, offset] = readLengthDelimited(buffer, offset);
122
+ break;
123
+ case PNL_FIELDS.MIN_ACCOUNT_BALANCE:
124
+ [result.minAccountBalance, offset] = readLengthDelimited(buffer, offset);
125
+ break;
126
+ case PNL_FIELDS.OPEN_POSITION_PNL:
127
+ [result.openPositionPnl, offset] = readLengthDelimited(buffer, offset);
128
+ break;
129
+ case PNL_FIELDS.CLOSED_POSITION_PNL:
130
+ [result.closedPositionPnl, offset] = readLengthDelimited(buffer, offset);
131
+ break;
132
+ case PNL_FIELDS.DAY_PNL:
133
+ [result.dayPnl, offset] = readLengthDelimited(buffer, offset);
134
+ break;
135
+ case PNL_FIELDS.DAY_OPEN_PNL:
136
+ [result.dayOpenPnl, offset] = readLengthDelimited(buffer, offset);
137
+ break;
138
+ case PNL_FIELDS.DAY_CLOSED_PNL:
139
+ [result.dayClosedPnl, offset] = readLengthDelimited(buffer, offset);
140
+ break;
141
+ case PNL_FIELDS.AVAILABLE_BUYING_POWER:
142
+ [result.availableBuyingPower, offset] = readLengthDelimited(buffer, offset);
143
+ break;
144
+ case PNL_FIELDS.SSBOE:
145
+ [result.ssboe, offset] = readVarint(buffer, offset);
146
+ break;
147
+ case PNL_FIELDS.USECS:
148
+ [result.usecs, offset] = readVarint(buffer, offset);
149
+ break;
150
+ default:
151
+ offset = skipField(buffer, offset, wireType);
152
+ }
153
+ } catch (error) {
154
+ break;
155
+ }
156
+ }
157
+
158
+ return result;
159
+ }
160
+
161
+ /**
162
+ * Protobuf Handler class
163
+ */
164
+ class ProtobufHandler {
165
+ constructor() {
166
+ this.root = null;
167
+ this.loaded = false;
168
+ this.protoPath = path.join(__dirname, 'proto');
169
+ }
170
+
171
+ /**
172
+ * Load all proto files
173
+ */
174
+ async load() {
175
+ if (this.loaded) return;
176
+
177
+ this.root = new protobuf.Root();
178
+
179
+ for (const file of PROTO_FILES) {
180
+ try {
181
+ await this.root.load(path.join(this.protoPath, file));
182
+ } catch (e) {
183
+ // Some files may not exist, that's ok
184
+ }
185
+ }
186
+
187
+ this.loaded = true;
188
+ }
189
+
190
+ /**
191
+ * Encode a message to Buffer
192
+ */
193
+ encode(typeName, data) {
194
+ if (!this.root) throw new Error('Proto not loaded');
195
+
196
+ const Type = this.root.lookupType(typeName);
197
+ const msg = Type.create(data);
198
+ return Buffer.from(Type.encode(msg).finish());
199
+ }
200
+
201
+ /**
202
+ * Decode a Buffer to object
203
+ */
204
+ decode(typeName, buffer) {
205
+ if (!this.root) throw new Error('Proto not loaded');
206
+
207
+ const Type = this.root.lookupType(typeName);
208
+ return Type.decode(buffer);
209
+ }
210
+
211
+ /**
212
+ * Get template ID from buffer (manual decode for large field IDs)
213
+ */
214
+ getTemplateId(buffer) {
215
+ const TEMPLATE_ID_FIELD = 154467;
216
+
217
+ let offset = 0;
218
+ while (offset < buffer.length) {
219
+ try {
220
+ const [tag, newOffset] = readVarint(buffer, offset);
221
+ const fieldNumber = tag >>> 3;
222
+ const wireType = tag & 0x7;
223
+ offset = newOffset;
224
+
225
+ if (fieldNumber === TEMPLATE_ID_FIELD && wireType === 0) {
226
+ const [templateId] = readVarint(buffer, offset);
227
+ return templateId;
228
+ }
229
+
230
+ offset = skipField(buffer, offset, wireType);
231
+ } catch (e) {
232
+ break;
233
+ }
234
+ }
235
+
236
+ // Fallback
237
+ if (this.root) {
238
+ try {
239
+ const Base = this.root.lookupType('Base');
240
+ const base = Base.decode(buffer);
241
+ return base.templateId;
242
+ } catch (e) {
243
+ return -1;
244
+ }
245
+ }
246
+ return -1;
247
+ }
248
+ }
249
+
250
+ // Singleton
251
+ const proto = new ProtobufHandler();
252
+
253
+ module.exports = {
254
+ proto,
255
+ decodeAccountPnL,
256
+ readVarint,
257
+ readLengthDelimited,
258
+ skipField,
259
+ };