hedgequantx 1.2.33 → 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 +121 -9
  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,487 @@
1
+ /**
2
+ * Rithmic Service
3
+ * Main service for Rithmic prop firm connections
4
+ */
5
+
6
+ const EventEmitter = require('events');
7
+ const { RithmicConnection } = require('./connection');
8
+ const { proto, decodeAccountPnL } = require('./protobuf');
9
+ const { RITHMIC_ENDPOINTS, RITHMIC_SYSTEMS, REQ, RES, STREAM } = require('./constants');
10
+
11
+ class RithmicService extends EventEmitter {
12
+ constructor(propfirmKey) {
13
+ super();
14
+ this.propfirmKey = propfirmKey;
15
+ this.propfirm = this.getPropFirmConfig(propfirmKey);
16
+ this.orderConn = null;
17
+ this.pnlConn = null;
18
+ this.loginInfo = null;
19
+ this.accounts = [];
20
+ this.accountPnL = new Map(); // accountId -> pnl data
21
+ this.user = null;
22
+ }
23
+
24
+ /**
25
+ * Get PropFirm configuration
26
+ */
27
+ getPropFirmConfig(key) {
28
+ const propfirms = {
29
+ 'apex': { name: 'Apex Trader Funding', systemName: RITHMIC_SYSTEMS.APEX, defaultBalance: 300000 },
30
+ 'topstep_r': { name: 'Topstep (Rithmic)', systemName: RITHMIC_SYSTEMS.TOPSTEP, defaultBalance: 150000 },
31
+ 'bulenox_r': { name: 'Bulenox (Rithmic)', systemName: RITHMIC_SYSTEMS.BULENOX, defaultBalance: 150000 },
32
+ 'earn2trade': { name: 'Earn2Trade', systemName: RITHMIC_SYSTEMS.EARN_2_TRADE, defaultBalance: 150000 },
33
+ 'mescapital': { name: 'MES Capital', systemName: RITHMIC_SYSTEMS.MES_CAPITAL, defaultBalance: 150000 },
34
+ 'tradefundrr': { name: 'TradeFundrr', systemName: RITHMIC_SYSTEMS.TRADEFUNDRR, defaultBalance: 150000 },
35
+ 'thetradingpit': { name: 'The Trading Pit', systemName: RITHMIC_SYSTEMS.THE_TRADING_PIT, defaultBalance: 150000 },
36
+ 'fundedfutures': { name: 'Funded Futures Network', systemName: RITHMIC_SYSTEMS.FUNDED_FUTURES_NETWORK, defaultBalance: 150000 },
37
+ 'propshop': { name: 'PropShop Trader', systemName: RITHMIC_SYSTEMS.PROPSHOP_TRADER, defaultBalance: 150000 },
38
+ '4proptrader': { name: '4PropTrader', systemName: RITHMIC_SYSTEMS.FOUR_PROP_TRADER, defaultBalance: 150000 },
39
+ 'daytraders': { name: 'DayTraders.com', systemName: RITHMIC_SYSTEMS.DAY_TRADERS, defaultBalance: 150000 },
40
+ '10xfutures': { name: '10X Futures', systemName: RITHMIC_SYSTEMS.TEN_X_FUTURES, defaultBalance: 150000 },
41
+ 'lucidtrading': { name: 'Lucid Trading', systemName: RITHMIC_SYSTEMS.LUCID_TRADING, defaultBalance: 150000 },
42
+ 'thrivetrading': { name: 'Thrive Trading', systemName: RITHMIC_SYSTEMS.THRIVE_TRADING, defaultBalance: 150000 },
43
+ 'legendstrading': { name: 'Legends Trading', systemName: RITHMIC_SYSTEMS.LEGENDS_TRADING, defaultBalance: 150000 },
44
+ };
45
+ return propfirms[key] || { name: key, systemName: 'Rithmic Paper Trading', defaultBalance: 150000 };
46
+ }
47
+
48
+ /**
49
+ * Login to Rithmic
50
+ */
51
+ async login(username, password) {
52
+ try {
53
+ // Connect to ORDER_PLANT
54
+ this.orderConn = new RithmicConnection();
55
+
56
+ const config = {
57
+ uri: RITHMIC_ENDPOINTS.PAPER,
58
+ systemName: this.propfirm.systemName,
59
+ userId: username,
60
+ password: password,
61
+ appName: 'HQX-CLI',
62
+ appVersion: '1.0.0',
63
+ };
64
+
65
+ await this.orderConn.connect(config);
66
+
67
+ // Setup message handler for ORDER_PLANT
68
+ this.orderConn.on('message', (msg) => this.handleOrderMessage(msg));
69
+
70
+ // Login
71
+ return new Promise((resolve, reject) => {
72
+ const timeout = setTimeout(() => {
73
+ reject(new Error('Login timeout'));
74
+ }, 15000);
75
+
76
+ this.orderConn.once('loggedIn', async (data) => {
77
+ clearTimeout(timeout);
78
+ this.loginInfo = data;
79
+ this.user = { userName: username };
80
+
81
+ try {
82
+ // Get accounts
83
+ await this.fetchAccounts();
84
+ resolve({ success: true });
85
+ } catch (e) {
86
+ resolve({ success: false, error: e.message });
87
+ }
88
+ });
89
+
90
+ this.orderConn.once('loginFailed', (data) => {
91
+ clearTimeout(timeout);
92
+ resolve({ success: false, error: data.message || 'Login failed' });
93
+ });
94
+
95
+ this.orderConn.login('ORDER_PLANT');
96
+ });
97
+
98
+ } catch (error) {
99
+ return { success: false, error: error.message };
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Connect to PNL_PLANT for balance data
105
+ */
106
+ async connectPnL(username, password) {
107
+ try {
108
+ this.pnlConn = new RithmicConnection();
109
+
110
+ const config = {
111
+ uri: RITHMIC_ENDPOINTS.PAPER,
112
+ systemName: this.propfirm.systemName,
113
+ userId: username,
114
+ password: password,
115
+ appName: 'HQX-CLI',
116
+ appVersion: '1.0.0',
117
+ };
118
+
119
+ await this.pnlConn.connect(config);
120
+ this.pnlConn.on('message', (msg) => this.handlePnLMessage(msg));
121
+
122
+ return new Promise((resolve, reject) => {
123
+ const timeout = setTimeout(() => resolve(false), 10000);
124
+
125
+ this.pnlConn.once('loggedIn', () => {
126
+ clearTimeout(timeout);
127
+ resolve(true);
128
+ });
129
+
130
+ this.pnlConn.once('loginFailed', () => {
131
+ clearTimeout(timeout);
132
+ resolve(false);
133
+ });
134
+
135
+ this.pnlConn.login('PNL_PLANT');
136
+ });
137
+ } catch (e) {
138
+ return false;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Fetch accounts from ORDER_PLANT
144
+ */
145
+ async fetchAccounts() {
146
+ if (!this.orderConn || !this.loginInfo) {
147
+ throw new Error('Not connected');
148
+ }
149
+
150
+ // Request login info first
151
+ await this.requestLoginInfo();
152
+
153
+ // Then request accounts
154
+ return new Promise((resolve, reject) => {
155
+ const accounts = [];
156
+ let completed = false;
157
+
158
+ const timeout = setTimeout(() => {
159
+ if (!completed) {
160
+ completed = true;
161
+ this.accounts = accounts;
162
+ resolve(accounts);
163
+ }
164
+ }, 5000);
165
+
166
+ const handleAccount = (account) => {
167
+ accounts.push(account);
168
+ };
169
+
170
+ this.once('accountReceived', handleAccount);
171
+ this.once('accountListComplete', () => {
172
+ if (!completed) {
173
+ completed = true;
174
+ clearTimeout(timeout);
175
+ this.accounts = accounts;
176
+ resolve(accounts);
177
+ }
178
+ });
179
+
180
+ // Request account list
181
+ this.orderConn.send('RequestAccountList', {
182
+ templateId: REQ.ACCOUNT_LIST,
183
+ userMsg: ['HQX'],
184
+ fcmId: this.loginInfo.fcmId,
185
+ ibId: this.loginInfo.ibId,
186
+ });
187
+ });
188
+ }
189
+
190
+ /**
191
+ * Request login info
192
+ */
193
+ async requestLoginInfo() {
194
+ return new Promise((resolve) => {
195
+ const timeout = setTimeout(() => resolve(), 3000);
196
+
197
+ this.once('loginInfoReceived', (info) => {
198
+ clearTimeout(timeout);
199
+ this.loginInfo = { ...this.loginInfo, ...info };
200
+ resolve(info);
201
+ });
202
+
203
+ this.orderConn.send('RequestLoginInfo', {
204
+ templateId: REQ.LOGIN_INFO,
205
+ userMsg: ['HQX'],
206
+ });
207
+ });
208
+ }
209
+
210
+ /**
211
+ * Get trading accounts (formatted like ProjectX)
212
+ */
213
+ async getTradingAccounts() {
214
+ if (this.accounts.length === 0) {
215
+ await this.fetchAccounts();
216
+ }
217
+
218
+ const tradingAccounts = this.accounts.map((acc, index) => {
219
+ const pnl = this.accountPnL.get(acc.accountId) || {};
220
+ const balance = parseFloat(pnl.accountBalance || pnl.marginBalance || pnl.cashOnHand || 0) || this.propfirm.defaultBalance;
221
+ const startingBalance = this.propfirm.defaultBalance;
222
+ const profitAndLoss = balance - startingBalance;
223
+
224
+ return {
225
+ accountId: this.hashAccountId(acc.accountId),
226
+ rithmicAccountId: acc.accountId,
227
+ accountName: acc.accountName || acc.accountId,
228
+ name: acc.accountName || acc.accountId,
229
+ balance: balance,
230
+ startingBalance: startingBalance,
231
+ profitAndLoss: profitAndLoss,
232
+ status: 0, // Active
233
+ platform: 'Rithmic',
234
+ propfirm: this.propfirm.name,
235
+ };
236
+ });
237
+
238
+ return { success: true, accounts: tradingAccounts };
239
+ }
240
+
241
+ /**
242
+ * Request PnL snapshot for accounts
243
+ */
244
+ async requestPnLSnapshot() {
245
+ if (!this.pnlConn || !this.loginInfo) return;
246
+
247
+ for (const acc of this.accounts) {
248
+ this.pnlConn.send('RequestPnLPositionSnapshot', {
249
+ templateId: REQ.PNL_POSITION_SNAPSHOT,
250
+ userMsg: ['HQX'],
251
+ fcmId: acc.fcmId || this.loginInfo.fcmId,
252
+ ibId: acc.ibId || this.loginInfo.ibId,
253
+ accountId: acc.accountId,
254
+ });
255
+ }
256
+
257
+ // Wait for responses
258
+ await new Promise(resolve => setTimeout(resolve, 2000));
259
+ }
260
+
261
+ /**
262
+ * Subscribe to PnL updates
263
+ */
264
+ subscribePnLUpdates() {
265
+ if (!this.pnlConn || !this.loginInfo) return;
266
+
267
+ for (const acc of this.accounts) {
268
+ this.pnlConn.send('RequestPnLPositionUpdates', {
269
+ templateId: REQ.PNL_POSITION_UPDATES,
270
+ userMsg: ['HQX'],
271
+ request: 1, // Subscribe
272
+ fcmId: acc.fcmId || this.loginInfo.fcmId,
273
+ ibId: acc.ibId || this.loginInfo.ibId,
274
+ accountId: acc.accountId,
275
+ });
276
+ }
277
+ }
278
+
279
+ /**
280
+ * Handle ORDER_PLANT messages
281
+ */
282
+ handleOrderMessage(msg) {
283
+ const { templateId, data } = msg;
284
+
285
+ switch (templateId) {
286
+ case RES.LOGIN_INFO:
287
+ this.onLoginInfo(data);
288
+ break;
289
+ case RES.ACCOUNT_LIST:
290
+ this.onAccountList(data);
291
+ break;
292
+ case RES.TRADE_ROUTES:
293
+ this.onTradeRoutes(data);
294
+ break;
295
+ case STREAM.EXCHANGE_NOTIFICATION:
296
+ this.onExchangeNotification(data);
297
+ break;
298
+ case STREAM.ORDER_NOTIFICATION:
299
+ this.onOrderNotification(data);
300
+ break;
301
+ }
302
+ }
303
+
304
+ /**
305
+ * Handle PNL_PLANT messages
306
+ */
307
+ handlePnLMessage(msg) {
308
+ const { templateId, data } = msg;
309
+
310
+ switch (templateId) {
311
+ case RES.PNL_POSITION_SNAPSHOT:
312
+ case RES.PNL_POSITION_UPDATES:
313
+ // OK response
314
+ break;
315
+ case STREAM.ACCOUNT_PNL_UPDATE:
316
+ this.onAccountPnLUpdate(data);
317
+ break;
318
+ case STREAM.INSTRUMENT_PNL_UPDATE:
319
+ this.onInstrumentPnLUpdate(data);
320
+ break;
321
+ }
322
+ }
323
+
324
+ onLoginInfo(data) {
325
+ try {
326
+ const res = proto.decode('ResponseLoginInfo', data);
327
+ this.emit('loginInfoReceived', {
328
+ fcmId: res.fcmId,
329
+ ibId: res.ibId,
330
+ firstName: res.firstName,
331
+ lastName: res.lastName,
332
+ userType: res.userType,
333
+ });
334
+ } catch (e) {
335
+ // Ignore
336
+ }
337
+ }
338
+
339
+ onAccountList(data) {
340
+ try {
341
+ const res = proto.decode('ResponseAccountList', data);
342
+
343
+ if (res.rpCode?.[0] === '0') {
344
+ // End of list
345
+ this.emit('accountListComplete');
346
+ } else if (res.accountId) {
347
+ const account = {
348
+ fcmId: res.fcmId,
349
+ ibId: res.ibId,
350
+ accountId: res.accountId,
351
+ accountName: res.accountName,
352
+ accountCurrency: res.accountCurrency,
353
+ };
354
+ this.accounts.push(account);
355
+ this.emit('accountReceived', account);
356
+ }
357
+ } catch (e) {
358
+ // Ignore
359
+ }
360
+ }
361
+
362
+ onTradeRoutes(data) {
363
+ try {
364
+ const res = proto.decode('ResponseTradeRoutes', data);
365
+ this.emit('tradeRoutes', res);
366
+ } catch (e) {
367
+ // Ignore
368
+ }
369
+ }
370
+
371
+ onAccountPnLUpdate(data) {
372
+ try {
373
+ const pnl = decodeAccountPnL(data);
374
+ if (pnl.accountId) {
375
+ this.accountPnL.set(pnl.accountId, {
376
+ accountBalance: parseFloat(pnl.accountBalance || 0),
377
+ cashOnHand: parseFloat(pnl.cashOnHand || 0),
378
+ marginBalance: parseFloat(pnl.marginBalance || 0),
379
+ openPositionPnl: parseFloat(pnl.openPositionPnl || 0),
380
+ closedPositionPnl: parseFloat(pnl.closedPositionPnl || 0),
381
+ dayPnl: parseFloat(pnl.dayPnl || 0),
382
+ });
383
+ this.emit('pnlUpdate', pnl);
384
+ }
385
+ } catch (e) {
386
+ // Ignore
387
+ }
388
+ }
389
+
390
+ onInstrumentPnLUpdate(data) {
391
+ // Handle instrument-level PnL if needed
392
+ }
393
+
394
+ onExchangeNotification(data) {
395
+ this.emit('exchangeNotification', data);
396
+ }
397
+
398
+ onOrderNotification(data) {
399
+ this.emit('orderNotification', data);
400
+ }
401
+
402
+ /**
403
+ * Hash account ID to numeric (for compatibility)
404
+ */
405
+ hashAccountId(str) {
406
+ let hash = 0;
407
+ for (let i = 0; i < str.length; i++) {
408
+ const char = str.charCodeAt(i);
409
+ hash = (hash << 5) - hash + char;
410
+ hash = hash & hash;
411
+ }
412
+ return Math.abs(hash);
413
+ }
414
+
415
+ /**
416
+ * Get user info
417
+ */
418
+ async getUser() {
419
+ return this.user;
420
+ }
421
+
422
+ /**
423
+ * Check market hours (same as ProjectX)
424
+ */
425
+ checkMarketHours() {
426
+ const now = new Date();
427
+ const utcDay = now.getUTCDay();
428
+ const utcHour = now.getUTCHours();
429
+ const utcMinute = now.getUTCMinutes();
430
+ const utcTime = utcHour * 60 + utcMinute;
431
+
432
+ // CME Futures: Sunday 5PM CT - Friday 4PM CT
433
+ // CT = UTC-6 (CST) or UTC-5 (CDT)
434
+ const ctOffset = this.isDST(now) ? 5 : 6;
435
+ const ctHour = (utcHour - ctOffset + 24) % 24;
436
+ const ctDay = utcHour < ctOffset ? (utcDay + 6) % 7 : utcDay;
437
+
438
+ // Market closed Saturday all day
439
+ if (ctDay === 6) {
440
+ return { isOpen: false, message: 'Market closed (Saturday)' };
441
+ }
442
+
443
+ // Sunday before 5PM CT
444
+ if (ctDay === 0 && ctHour < 17) {
445
+ return { isOpen: false, message: 'Market opens Sunday 5:00 PM CT' };
446
+ }
447
+
448
+ // Friday after 4PM CT
449
+ if (ctDay === 5 && ctHour >= 16) {
450
+ return { isOpen: false, message: 'Market closed (Friday after 4PM CT)' };
451
+ }
452
+
453
+ // Daily maintenance 4PM-5PM CT (Mon-Thu)
454
+ if (ctHour === 16 && ctDay >= 1 && ctDay <= 4) {
455
+ return { isOpen: false, message: 'Daily maintenance (4:00-5:00 PM CT)' };
456
+ }
457
+
458
+ return { isOpen: true, message: 'Market is open' };
459
+ }
460
+
461
+ isDST(date) {
462
+ const jan = new Date(date.getFullYear(), 0, 1);
463
+ const jul = new Date(date.getFullYear(), 6, 1);
464
+ const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
465
+ return date.getTimezoneOffset() < stdOffset;
466
+ }
467
+
468
+ /**
469
+ * Disconnect all connections
470
+ */
471
+ async disconnect() {
472
+ if (this.orderConn) {
473
+ await this.orderConn.disconnect();
474
+ this.orderConn = null;
475
+ }
476
+ if (this.pnlConn) {
477
+ await this.pnlConn.disconnect();
478
+ this.pnlConn = null;
479
+ }
480
+ this.accounts = [];
481
+ this.accountPnL.clear();
482
+ this.loginInfo = null;
483
+ this.user = null;
484
+ }
485
+ }
486
+
487
+ module.exports = { RithmicService, RITHMIC_SYSTEMS, RITHMIC_ENDPOINTS };
@@ -0,0 +1,59 @@
1
+
2
+ package rti;
3
+
4
+ message AccountPnLPositionUpdate
5
+ {
6
+ required int32 template_id = 154467;
7
+ optional bool is_snapshot = 110121;
8
+
9
+ optional string fcm_id = 154013;
10
+ optional string ib_id = 154014;
11
+ optional string account_id = 154008;
12
+
13
+ optional int32 fill_buy_qty = 154041;
14
+ optional int32 fill_sell_qty = 154042;
15
+
16
+ optional int32 order_buy_qty = 154037;
17
+ optional int32 order_sell_qty = 154038;
18
+
19
+ optional int32 buy_qty = 154260;
20
+ optional int32 sell_qty = 154261;
21
+
22
+ optional string open_long_options_value = 157105;
23
+ optional string open_short_options_value = 157106;
24
+ optional string closed_options_value = 157107;
25
+ optional string option_cash_reserved = 157111;
26
+ optional string rms_account_commission = 157113;
27
+
28
+ optional string open_position_pnl = 156961;
29
+ optional int32 open_position_quantity = 156962;
30
+ optional string closed_position_pnl = 156963;
31
+
32
+ optional int32 closed_position_quantity = 156964;
33
+ optional int32 net_quantity = 156967;
34
+
35
+ optional string excess_buy_margin = 156991;
36
+ optional string margin_balance = 156977;
37
+ optional string min_margin_balance = 156976;
38
+ optional string min_account_balance = 156968;
39
+ optional string account_balance = 156970;
40
+
41
+ optional string cash_on_hand = 156971;
42
+ optional string option_closed_pnl = 157118;
43
+ optional string percent_maximum_allowable_loss = 156965;
44
+ optional string option_open_pnl = 157117;
45
+ optional string mtm_account = 154262;
46
+ optional string available_buying_power = 157015;
47
+ optional string used_buying_power = 157014;
48
+ optional string reserved_buying_power = 157013;
49
+ optional string excess_sell_margin = 156992;
50
+
51
+ optional string day_open_pnl = 157954;
52
+ optional string day_closed_pnl = 157955;
53
+ optional string day_pnl = 157956;
54
+ optional string day_open_pnl_offset = 157957;
55
+ optional string day_closed_pnl_offset = 157958;
56
+
57
+ optional int32 ssboe = 150100;
58
+ optional int32 usecs = 150101;
59
+ }
@@ -0,0 +1,7 @@
1
+
2
+ package rti;
3
+
4
+ message Base
5
+ {
6
+ required int32 template_id = 154467; // PB_OFFSET + MNM_TEMPLATE_ID
7
+ }
@@ -0,0 +1,39 @@
1
+
2
+ package rti;
3
+
4
+ message BestBidOffer
5
+ {
6
+ // PB_OFFSET = 100000, is the offset added for each MNM field id
7
+
8
+ // below enum is just for reference only, not used in this message
9
+ enum PresenceBits {
10
+ BID = 1;
11
+ ASK = 2;
12
+ LEAN_PRICE = 4;
13
+ }
14
+
15
+ required int32 template_id = 154467; // PB_OFFSET + MNM_TEMPLATE_ID
16
+ optional string symbol = 110100; // PB_OFFSET + MNM_SYMBOL
17
+ optional string exchange = 110101; // PB_OFFSET + MNM_EXCHANGE
18
+
19
+ optional uint32 presence_bits = 149138; // PB_OFFSET + MNM_PRICING_INDICATOR
20
+ optional uint32 clear_bits = 154571; // PB_OFFSET + MNM_DISPLAY_INDICATOR
21
+ optional bool is_snapshot = 110121; // PB_OFFSET + MNM_UPDATE_TYPE
22
+
23
+ optional double bid_price = 100022; // PB_OFFSET + MNM_BID_PRICE
24
+ optional int32 bid_size = 100030; // PB_OFFSET + MNM_BID_SIZE
25
+ optional int32 bid_orders = 154403; // PB_OFFSET + MNM_BID_NO_OF_ORDERS
26
+ optional int32 bid_implicit_size = 154867; // PB_OFFSET + MNM_BID_IMPLICIT_SIZE
27
+ optional string bid_time = 100266;
28
+
29
+ optional double ask_price = 100025; // PB_OFFSET + MNM_ASK_PRICE
30
+ optional int32 ask_size = 100031; // PB_OFFSET + MNM_ASK_SIZE
31
+ optional int32 ask_orders = 154404; // PB_OFFSET + MNM_ASK_NO_OF_ORDERS
32
+ optional int32 ask_implicit_size = 154868; // PB_OFFSET + MNM_ASK_IMPLICIT_SIZE
33
+ optional string ask_time = 100267;
34
+
35
+ optional double lean_price = 154909; // PB_OFFSET + MNM_LEAN_PRICE
36
+
37
+ optional int32 ssboe = 150100; // PB_OFFSET + MNM_SECONDS_SINCE_BOE
38
+ optional int32 usecs = 150101; // PB_OFFSET + MNM_USECS
39
+ }