hedgequantx 1.2.34 → 1.2.36

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 (51) hide show
  1. package/package.json +2 -1
  2. package/src/app.js +204 -6
  3. package/src/config/propfirms.js +8 -0
  4. package/src/services/rithmic/connection.js +203 -0
  5. package/src/services/rithmic/constants.js +156 -0
  6. package/src/services/rithmic/index.js +487 -0
  7. package/src/services/rithmic/proto/account_pnl_position_update.proto +59 -0
  8. package/src/services/rithmic/proto/base.proto +7 -0
  9. package/src/services/rithmic/proto/best_bid_offer.proto +39 -0
  10. package/src/services/rithmic/proto/exchange_order_notification.proto +140 -0
  11. package/src/services/rithmic/proto/instrument_pnl_position_update.proto +50 -0
  12. package/src/services/rithmic/proto/last_trade.proto +53 -0
  13. package/src/services/rithmic/proto/request_account_list.proto +20 -0
  14. package/src/services/rithmic/proto/request_cancel_all_orders.proto +15 -0
  15. package/src/services/rithmic/proto/request_heartbeat.proto +13 -0
  16. package/src/services/rithmic/proto/request_login.proto +28 -0
  17. package/src/services/rithmic/proto/request_login_info.proto +10 -0
  18. package/src/services/rithmic/proto/request_logout.proto +10 -0
  19. package/src/services/rithmic/proto/request_market_data_update.proto +42 -0
  20. package/src/services/rithmic/proto/request_new_order.proto +84 -0
  21. package/src/services/rithmic/proto/request_pnl_position_snapshot.proto +14 -0
  22. package/src/services/rithmic/proto/request_pnl_position_updates.proto +20 -0
  23. package/src/services/rithmic/proto/request_rithmic_system_info.proto +8 -0
  24. package/src/services/rithmic/proto/request_show_order_history.proto +16 -0
  25. package/src/services/rithmic/proto/request_show_order_history_dates.proto +10 -0
  26. package/src/services/rithmic/proto/request_show_order_history_summary.proto +14 -0
  27. package/src/services/rithmic/proto/request_show_orders.proto +14 -0
  28. package/src/services/rithmic/proto/request_subscribe_for_order_updates.proto +14 -0
  29. package/src/services/rithmic/proto/request_tick_bar_replay.proto +48 -0
  30. package/src/services/rithmic/proto/request_trade_routes.proto +11 -0
  31. package/src/services/rithmic/proto/response_account_list.proto +18 -0
  32. package/src/services/rithmic/proto/response_heartbeat.proto +14 -0
  33. package/src/services/rithmic/proto/response_login.proto +18 -0
  34. package/src/services/rithmic/proto/response_login_info.proto +24 -0
  35. package/src/services/rithmic/proto/response_logout.proto +11 -0
  36. package/src/services/rithmic/proto/response_market_data_update.proto +9 -0
  37. package/src/services/rithmic/proto/response_new_order.proto +18 -0
  38. package/src/services/rithmic/proto/response_pnl_position_snapshot.proto +11 -0
  39. package/src/services/rithmic/proto/response_pnl_position_updates.proto +11 -0
  40. package/src/services/rithmic/proto/response_rithmic_system_info.proto +12 -0
  41. package/src/services/rithmic/proto/response_show_order_history.proto +11 -0
  42. package/src/services/rithmic/proto/response_show_order_history_dates.proto +13 -0
  43. package/src/services/rithmic/proto/response_show_order_history_summary.proto +11 -0
  44. package/src/services/rithmic/proto/response_show_orders.proto +11 -0
  45. package/src/services/rithmic/proto/response_subscribe_for_order_updates.proto +11 -0
  46. package/src/services/rithmic/proto/response_tick_bar_replay.proto +40 -0
  47. package/src/services/rithmic/proto/response_trade_routes.proto +19 -0
  48. package/src/services/rithmic/proto/rithmic_order_notification.proto +124 -0
  49. package/src/services/rithmic/protobuf.js +259 -0
  50. package/src/services/tradovate/constants.js +109 -0
  51. package/src/services/tradovate/index.js +508 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "1.2.34",
3
+ "version": "1.2.36",
4
4
  "description": "Prop Futures Algo Trading CLI - Connect to Topstep, Alpha Futures, and other prop firms",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -47,6 +47,7 @@
47
47
  "figlet": "^1.7.0",
48
48
  "inquirer": "^7.3.3",
49
49
  "ora": "^5.4.1",
50
+ "protobufjs": "^8.0.0",
50
51
  "ws": "^8.18.3"
51
52
  }
52
53
  }
package/src/app.js CHANGED
@@ -11,7 +11,9 @@ const { execSync } = require('child_process');
11
11
  const path = require('path');
12
12
 
13
13
  const { ProjectXService, connections } = require('./services');
14
- const { PROPFIRM_CHOICES, getPropFirmsByPlatform } = require('./config');
14
+ const { RithmicService } = require('./services/rithmic');
15
+ const { TradovateService } = require('./services/tradovate');
16
+ const { PROPFIRM_CHOICES, getPropFirmsByPlatform, getPropFirm } = require('./config');
15
17
  const { getDevice, getSeparator, printLogo, getLogoWidth, drawBoxHeader, drawBoxFooter, centerText, createBoxMenu } = require('./ui');
16
18
  const { validateUsername, validatePassword, maskSensitive } = require('./security');
17
19
 
@@ -22,6 +24,7 @@ const { algoTradingMenu } = require('./pages/algo');
22
24
 
23
25
  // Current service reference
24
26
  let currentService = null;
27
+ let currentPlatform = null; // 'projectx' or 'rithmic'
25
28
 
26
29
  /**
27
30
  * Displays the application banner with stats if connected
@@ -257,6 +260,189 @@ const projectXMenu = async () => {
257
260
  await service.getUser();
258
261
  connections.add('projectx', service, service.propfirm.name);
259
262
  currentService = service;
263
+ currentPlatform = 'projectx';
264
+ spinner.succeed(`Connected to ${service.propfirm.name}`);
265
+ return service;
266
+ } else {
267
+ spinner.fail(result.error || 'Authentication failed');
268
+ return null;
269
+ }
270
+ } catch (error) {
271
+ spinner.fail(error.message);
272
+ return null;
273
+ }
274
+ };
275
+
276
+ /**
277
+ * Rithmic platform connection menu
278
+ */
279
+ const rithmicMenu = async () => {
280
+ const propfirms = getPropFirmsByPlatform('Rithmic');
281
+ const boxWidth = getLogoWidth();
282
+ const innerWidth = boxWidth - 2;
283
+ const numCols = 3;
284
+ const colWidth = Math.floor(innerWidth / numCols);
285
+
286
+ // Build numbered list
287
+ const numbered = propfirms.map((pf, i) => ({
288
+ num: i + 1,
289
+ key: pf.key,
290
+ name: pf.displayName,
291
+ systemName: pf.rithmicSystem
292
+ }));
293
+
294
+ // PropFirm selection box
295
+ console.log();
296
+ console.log(chalk.cyan('╔' + '═'.repeat(innerWidth) + '╗'));
297
+ console.log(chalk.cyan('║') + chalk.white.bold(centerText('SELECT PROPFIRM (RITHMIC)', innerWidth)) + chalk.cyan('║'));
298
+ console.log(chalk.cyan('║') + ' '.repeat(innerWidth) + chalk.cyan('║'));
299
+
300
+ // Display in 3 columns with fixed width alignment
301
+ const rows = Math.ceil(numbered.length / numCols);
302
+
303
+ for (let row = 0; row < rows; row++) {
304
+ let line = '';
305
+ for (let col = 0; col < numCols; col++) {
306
+ const idx = row + col * rows;
307
+ if (idx < numbered.length) {
308
+ const item = numbered[idx];
309
+ const numStr = item.num.toString().padStart(2, ' ');
310
+ const coloredText = chalk.cyan(`[${numStr}]`) + ' ' + chalk.white(item.name);
311
+ const textLen = 4 + 1 + item.name.length;
312
+ const padding = colWidth - textLen - 2;
313
+ line += ' ' + coloredText + ' '.repeat(Math.max(0, padding));
314
+ } else {
315
+ line += ' '.repeat(colWidth);
316
+ }
317
+ }
318
+ const lineLen = line.replace(/\x1b\[[0-9;]*m/g, '').length;
319
+ const adjust = innerWidth - lineLen;
320
+ console.log(chalk.cyan('║') + line + ' '.repeat(Math.max(0, adjust)) + chalk.cyan('║'));
321
+ }
322
+
323
+ console.log(chalk.cyan('║') + ' '.repeat(innerWidth) + chalk.cyan('║'));
324
+ const backText = ' ' + chalk.red('[X] Back');
325
+ const backLen = '[X] Back'.length + 2;
326
+ console.log(chalk.cyan('║') + backText + ' '.repeat(innerWidth - backLen) + chalk.cyan('║'));
327
+ console.log(chalk.cyan('╚' + '═'.repeat(innerWidth) + '╝'));
328
+ console.log();
329
+
330
+ const validInputs = numbered.map(n => n.num.toString());
331
+ validInputs.push('x', 'X');
332
+
333
+ const { action } = await inquirer.prompt([
334
+ {
335
+ type: 'input',
336
+ name: 'action',
337
+ message: chalk.cyan(`Enter choice (1-${numbered.length}/X):`),
338
+ validate: (input) => {
339
+ if (validInputs.includes(input)) return true;
340
+ return `Please enter 1-${numbered.length} or X`;
341
+ }
342
+ }
343
+ ]);
344
+
345
+ if (action.toLowerCase() === 'x') return null;
346
+
347
+ const selectedIdx = parseInt(action) - 1;
348
+ const selectedPropfirm = numbered[selectedIdx];
349
+
350
+ const credentials = await loginPrompt(selectedPropfirm.name);
351
+ const spinner = ora('Connecting to Rithmic...').start();
352
+
353
+ try {
354
+ const service = new RithmicService(selectedPropfirm.key);
355
+ const result = await service.login(credentials.username, credentials.password);
356
+
357
+ if (result.success) {
358
+ spinner.text = 'Fetching accounts...';
359
+ await service.getTradingAccounts();
360
+
361
+ connections.add('rithmic', service, service.propfirm.name);
362
+ currentService = service;
363
+ currentPlatform = 'rithmic';
364
+ spinner.succeed(`Connected to ${service.propfirm.name}`);
365
+ return service;
366
+ } else {
367
+ spinner.fail(result.error || 'Authentication failed');
368
+ return null;
369
+ }
370
+ } catch (error) {
371
+ spinner.fail(error.message);
372
+ return null;
373
+ }
374
+ };
375
+
376
+ /**
377
+ * Tradovate platform connection menu
378
+ */
379
+ const tradovateMenu = async () => {
380
+ const propfirms = getPropFirmsByPlatform('Tradovate');
381
+ const boxWidth = getLogoWidth();
382
+ const innerWidth = boxWidth - 2;
383
+
384
+ // Build numbered list
385
+ const numbered = propfirms.map((pf, i) => ({
386
+ num: i + 1,
387
+ key: pf.key,
388
+ name: pf.displayName
389
+ }));
390
+
391
+ // PropFirm selection box
392
+ console.log();
393
+ console.log(chalk.cyan('╔' + '═'.repeat(innerWidth) + '╗'));
394
+ console.log(chalk.cyan('║') + chalk.white.bold(centerText('SELECT PROPFIRM (TRADOVATE)', innerWidth)) + chalk.cyan('║'));
395
+ console.log(chalk.cyan('║') + ' '.repeat(innerWidth) + chalk.cyan('║'));
396
+
397
+ // Display propfirms
398
+ for (const item of numbered) {
399
+ const numStr = item.num.toString().padStart(2, ' ');
400
+ const text = ' ' + chalk.cyan(`[${numStr}]`) + ' ' + chalk.white(item.name);
401
+ const textLen = 4 + 1 + item.name.length + 2;
402
+ console.log(chalk.cyan('║') + text + ' '.repeat(innerWidth - textLen) + chalk.cyan('║'));
403
+ }
404
+
405
+ console.log(chalk.cyan('║') + ' '.repeat(innerWidth) + chalk.cyan('║'));
406
+ const backText = ' ' + chalk.red('[X] Back');
407
+ const backLen = '[X] Back'.length + 2;
408
+ console.log(chalk.cyan('║') + backText + ' '.repeat(innerWidth - backLen) + chalk.cyan('║'));
409
+ console.log(chalk.cyan('╚' + '═'.repeat(innerWidth) + '╝'));
410
+ console.log();
411
+
412
+ const validInputs = numbered.map(n => n.num.toString());
413
+ validInputs.push('x', 'X');
414
+
415
+ const { action } = await inquirer.prompt([
416
+ {
417
+ type: 'input',
418
+ name: 'action',
419
+ message: chalk.cyan(`Enter choice (1-${numbered.length}/X):`),
420
+ validate: (input) => {
421
+ if (validInputs.includes(input)) return true;
422
+ return `Please enter 1-${numbered.length} or X`;
423
+ }
424
+ }
425
+ ]);
426
+
427
+ if (action.toLowerCase() === 'x') return null;
428
+
429
+ const selectedIdx = parseInt(action) - 1;
430
+ const selectedPropfirm = numbered[selectedIdx];
431
+
432
+ const credentials = await loginPrompt(selectedPropfirm.name);
433
+ const spinner = ora('Connecting to Tradovate...').start();
434
+
435
+ try {
436
+ const service = new TradovateService(selectedPropfirm.key);
437
+ const result = await service.login(credentials.username, credentials.password);
438
+
439
+ if (result.success) {
440
+ spinner.text = 'Fetching accounts...';
441
+ await service.getTradingAccounts();
442
+
443
+ connections.add('tradovate', service, service.propfirm.name);
444
+ currentService = service;
445
+ currentPlatform = 'tradovate';
260
446
  spinner.succeed(`Connected to ${service.propfirm.name}`);
261
447
  return service;
262
448
  } else {
@@ -294,8 +480,8 @@ const mainMenu = async () => {
294
480
  console.log(chalk.cyan('║') + leftText + ' '.repeat(Math.max(0, leftPad)) + rightText + ' '.repeat(Math.max(0, rightPad)) + chalk.cyan('║'));
295
481
  };
296
482
 
297
- menuRow(chalk.cyan('[1] ProjectX'), chalk.gray('[2] Rithmic (Coming Soon)'));
298
- menuRow(chalk.gray('[3] Tradovate (Coming Soon)'), chalk.red('[X] Exit'));
483
+ menuRow(chalk.cyan('[1] ProjectX'), chalk.cyan('[2] Rithmic'));
484
+ menuRow(chalk.cyan('[3] Tradovate'), chalk.red('[X] Exit'));
299
485
 
300
486
  console.log(chalk.cyan('╚' + '═'.repeat(innerWidth) + '╝'));
301
487
  console.log();
@@ -304,11 +490,11 @@ const mainMenu = async () => {
304
490
  {
305
491
  type: 'input',
306
492
  name: 'action',
307
- message: chalk.cyan('Enter choice (1/X):'),
493
+ message: chalk.cyan('Enter choice (1/2/3/X):'),
308
494
  validate: (input) => {
309
- const valid = ['1', 'x', 'X'];
495
+ const valid = ['1', '2', '3', 'x', 'X'];
310
496
  if (valid.includes(input)) return true;
311
- return 'Please enter 1 or X';
497
+ return 'Please enter 1, 2, 3 or X';
312
498
  }
313
499
  }
314
500
  ]);
@@ -316,6 +502,8 @@ const mainMenu = async () => {
316
502
  // Map input to action
317
503
  const actionMap = {
318
504
  '1': 'projectx',
505
+ '2': 'rithmic',
506
+ '3': 'tradovate',
319
507
  'x': 'exit',
320
508
  'X': 'exit'
321
509
  };
@@ -512,6 +700,16 @@ const run = async () => {
512
700
  const service = await projectXMenu();
513
701
  if (service) currentService = service;
514
702
  }
703
+
704
+ if (choice === 'rithmic') {
705
+ const service = await rithmicMenu();
706
+ if (service) currentService = service;
707
+ }
708
+
709
+ if (choice === 'tradovate') {
710
+ const service = await tradovateMenu();
711
+ if (service) currentService = service;
712
+ }
515
713
  } else {
516
714
  const action = await dashboardMenu(currentService);
517
715
 
@@ -179,6 +179,14 @@ const PROPFIRMS = {
179
179
  userApi: 'userapi.takeprofittrader.tradovate.com',
180
180
  gatewayApi: 'api.takeprofittrader.tradovate.com'
181
181
  },
182
+ myfundedfutures: {
183
+ id: 'myfundedfutures',
184
+ name: 'MyFundedFutures',
185
+ displayName: 'MyFundedFutures',
186
+ platform: 'Tradovate',
187
+ userApi: 'live.tradovateapi.com',
188
+ gatewayApi: 'live.tradovateapi.com'
189
+ },
182
190
 
183
191
  // ==================== Rithmic Platform ====================
184
192
  apex_rithmic: {
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Rithmic Connection Manager
3
+ * Handles WebSocket connection and heartbeat
4
+ */
5
+
6
+ const WebSocket = require('ws');
7
+ const EventEmitter = require('events');
8
+ const { proto } = require('./protobuf');
9
+ const { REQ, RES, INFRA_TYPE } = require('./constants');
10
+
11
+ class RithmicConnection extends EventEmitter {
12
+ constructor() {
13
+ super();
14
+ this.ws = null;
15
+ this.config = null;
16
+ this.state = 'DISCONNECTED';
17
+ this.heartbeatTimer = null;
18
+ }
19
+
20
+ get isConnected() {
21
+ return this.ws?.readyState === WebSocket.OPEN;
22
+ }
23
+
24
+ get connectionState() {
25
+ return this.state;
26
+ }
27
+
28
+ /**
29
+ * Connect to Rithmic server
30
+ */
31
+ async connect(config) {
32
+ this.config = config;
33
+ this.state = 'CONNECTING';
34
+
35
+ await proto.load();
36
+
37
+ return new Promise((resolve, reject) => {
38
+ this.ws = new WebSocket(config.uri, { rejectUnauthorized: false });
39
+
40
+ this.ws.on('open', () => {
41
+ this.state = 'CONNECTED';
42
+ this.emit('connected');
43
+ resolve(true);
44
+ });
45
+
46
+ this.ws.on('message', (data) => {
47
+ this.handleMessage(data);
48
+ });
49
+
50
+ this.ws.on('error', (err) => {
51
+ this.state = 'ERROR';
52
+ this.emit('error', err);
53
+ reject(err);
54
+ });
55
+
56
+ this.ws.on('close', (code, reason) => {
57
+ this.state = 'DISCONNECTED';
58
+ this.stopHeartbeat();
59
+ this.emit('disconnected', { code, reason: reason?.toString() });
60
+ });
61
+
62
+ // Timeout
63
+ setTimeout(() => {
64
+ if (this.state === 'CONNECTING') {
65
+ reject(new Error('Connection timeout'));
66
+ }
67
+ }, 15000);
68
+ });
69
+ }
70
+
71
+ /**
72
+ * Disconnect
73
+ */
74
+ async disconnect() {
75
+ this.stopHeartbeat();
76
+ if (this.ws?.readyState === WebSocket.OPEN) {
77
+ this.send('RequestLogout', { templateId: REQ.LOGOUT, userMsg: ['HQX'] });
78
+ this.ws.close(1000, 'bye');
79
+ }
80
+ this.ws = null;
81
+ this.state = 'DISCONNECTED';
82
+ }
83
+
84
+ /**
85
+ * Send a protobuf message
86
+ */
87
+ send(typeName, data) {
88
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
89
+ throw new Error('Not connected');
90
+ }
91
+ const buffer = proto.encode(typeName, data);
92
+ this.ws.send(buffer);
93
+ }
94
+
95
+ /**
96
+ * Login to system
97
+ */
98
+ login(infraType = 'ORDER_PLANT') {
99
+ if (!this.config) throw new Error('No config');
100
+
101
+ this.send('RequestLogin', {
102
+ templateId: REQ.LOGIN,
103
+ templateVersion: '3.9',
104
+ userMsg: ['HQX'],
105
+ user: this.config.userId,
106
+ password: this.config.password,
107
+ appName: this.config.appName || 'HQX-CLI',
108
+ appVersion: this.config.appVersion || '1.0.0',
109
+ systemName: this.config.systemName,
110
+ infraType: INFRA_TYPE[infraType],
111
+ });
112
+ }
113
+
114
+ /**
115
+ * List available systems
116
+ */
117
+ listSystems() {
118
+ this.send('RequestRithmicSystemInfo', {
119
+ templateId: REQ.SYSTEM_INFO,
120
+ userMsg: ['HQX'],
121
+ });
122
+ }
123
+
124
+ /**
125
+ * Handle incoming message
126
+ */
127
+ handleMessage(data) {
128
+ const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
129
+ const templateId = proto.getTemplateId(buffer);
130
+
131
+ switch (templateId) {
132
+ case RES.LOGIN:
133
+ this.onLoginResponse(buffer);
134
+ break;
135
+
136
+ case RES.HEARTBEAT:
137
+ // OK
138
+ break;
139
+
140
+ case RES.SYSTEM_INFO:
141
+ this.onSystemInfo(buffer);
142
+ break;
143
+
144
+ default:
145
+ // Forward to listeners
146
+ this.emit('message', { templateId, data: buffer });
147
+ }
148
+ }
149
+
150
+ onLoginResponse(data) {
151
+ try {
152
+ const res = proto.decode('ResponseLogin', data);
153
+
154
+ if (res.rpCode?.[0] === '0') {
155
+ this.state = 'LOGGED_IN';
156
+ this.startHeartbeat(res.heartbeatInterval || 60);
157
+ this.emit('loggedIn', {
158
+ fcmId: res.fcmId,
159
+ ibId: res.ibId,
160
+ heartbeatInterval: res.heartbeatInterval,
161
+ });
162
+ } else {
163
+ const errorCode = res.rpCode?.[0] || 'UNKNOWN';
164
+ const errorMsg = res.rpCode?.[1] || 'Login failed';
165
+ this.emit('loginFailed', { code: errorCode, message: errorMsg });
166
+ }
167
+ } catch (e) {
168
+ this.emit('loginFailed', { code: 'DECODE_ERROR', message: e.message });
169
+ }
170
+ }
171
+
172
+ onSystemInfo(data) {
173
+ try {
174
+ const res = proto.decode('ResponseRithmicSystemInfo', data);
175
+
176
+ if (res.rpCode?.[0] === '0') {
177
+ this.emit('systems', res.systemName || []);
178
+ }
179
+ } catch (e) {
180
+ // Ignore
181
+ }
182
+ }
183
+
184
+ startHeartbeat(intervalSec) {
185
+ this.stopHeartbeat();
186
+ this.heartbeatTimer = setInterval(() => {
187
+ try {
188
+ this.send('RequestHeartbeat', { templateId: REQ.HEARTBEAT });
189
+ } catch (e) {
190
+ // Ignore
191
+ }
192
+ }, (intervalSec - 5) * 1000);
193
+ }
194
+
195
+ stopHeartbeat() {
196
+ if (this.heartbeatTimer) {
197
+ clearInterval(this.heartbeatTimer);
198
+ this.heartbeatTimer = null;
199
+ }
200
+ }
201
+ }
202
+
203
+ module.exports = { RithmicConnection };
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Rithmic Constants
3
+ */
4
+
5
+ // Endpoints
6
+ const RITHMIC_ENDPOINTS = {
7
+ TEST: 'wss://rituz00100.rithmic.com:443',
8
+ PAPER: 'wss://ritpa11120.11.rithmic.com:443',
9
+ LIVE: 'wss://ritpz01000.01.rithmic.com:443',
10
+ };
11
+
12
+ // System names for PropFirms
13
+ const RITHMIC_SYSTEMS = {
14
+ TEST: 'Rithmic Test',
15
+ PAPER: 'Rithmic Paper Trading',
16
+ APEX: 'Apex',
17
+ TOPSTEP: 'TopstepTrader',
18
+ MES_CAPITAL: 'MES Capital',
19
+ BULENOX: 'Bulenox',
20
+ TRADEFUNDRR: 'TradeFundrr',
21
+ THE_TRADING_PIT: 'TheTradingPit',
22
+ FUNDED_FUTURES_NETWORK: 'FundedFuturesNetwork',
23
+ PROPSHOP_TRADER: 'PropShopTrader',
24
+ FOUR_PROP_TRADER: '4PropTrader',
25
+ DAY_TRADERS: 'DayTraders.com',
26
+ TEN_X_FUTURES: '10XFutures',
27
+ LUCID_TRADING: 'LucidTrading',
28
+ THRIVE_TRADING: 'ThriveTrading',
29
+ LEGENDS_TRADING: 'LegendsTrading',
30
+ EARN_2_TRADE: 'Earn2Trade',
31
+ };
32
+
33
+ // Infrastructure types
34
+ const INFRA_TYPE = {
35
+ TICKER_PLANT: 1,
36
+ ORDER_PLANT: 2,
37
+ HISTORY_PLANT: 3,
38
+ PNL_PLANT: 4,
39
+ REPOSITORY_PLANT: 5,
40
+ };
41
+
42
+ // Request template IDs
43
+ const REQ = {
44
+ LOGIN: 10,
45
+ LOGOUT: 12,
46
+ SYSTEM_INFO: 16,
47
+ HEARTBEAT: 18,
48
+ MARKET_DATA: 100,
49
+ LOGIN_INFO: 300,
50
+ ACCOUNT_LIST: 302,
51
+ ACCOUNT_RMS: 304,
52
+ PRODUCT_RMS: 306,
53
+ ORDER_UPDATES: 308,
54
+ TRADE_ROUTES: 310,
55
+ NEW_ORDER: 312,
56
+ MODIFY_ORDER: 314,
57
+ CANCEL_ORDER: 316,
58
+ SHOW_ORDER_HISTORY_DATES: 318,
59
+ SHOW_ORDERS: 320,
60
+ SHOW_ORDER_HISTORY: 324,
61
+ BRACKET_ORDER: 330,
62
+ CANCEL_ALL_ORDERS: 346,
63
+ EXIT_POSITION: 3504,
64
+ PNL_POSITION_SNAPSHOT: 400,
65
+ PNL_POSITION_UPDATES: 402,
66
+ };
67
+
68
+ // Response template IDs
69
+ const RES = {
70
+ LOGIN: 11,
71
+ LOGOUT: 13,
72
+ SYSTEM_INFO: 17,
73
+ HEARTBEAT: 19,
74
+ MARKET_DATA: 101,
75
+ LOGIN_INFO: 301,
76
+ ACCOUNT_LIST: 303,
77
+ ACCOUNT_RMS: 305,
78
+ PRODUCT_RMS: 307,
79
+ ORDER_UPDATES: 309,
80
+ TRADE_ROUTES: 311,
81
+ NEW_ORDER: 313,
82
+ MODIFY_ORDER: 315,
83
+ CANCEL_ORDER: 317,
84
+ SHOW_ORDER_HISTORY_DATES: 319,
85
+ SHOW_ORDERS: 321,
86
+ SHOW_ORDER_HISTORY: 325,
87
+ BRACKET_ORDER: 331,
88
+ CANCEL_ALL_ORDERS: 347,
89
+ EXIT_POSITION: 3505,
90
+ PNL_POSITION_SNAPSHOT: 401,
91
+ PNL_POSITION_UPDATES: 403,
92
+ };
93
+
94
+ // Streaming template IDs
95
+ const STREAM = {
96
+ LAST_TRADE: 150,
97
+ BBO: 151,
98
+ TRADE_ROUTE_UPDATE: 350,
99
+ ORDER_NOTIFICATION: 351,
100
+ EXCHANGE_NOTIFICATION: 352,
101
+ BRACKET_UPDATE: 353,
102
+ INSTRUMENT_PNL_UPDATE: 450,
103
+ ACCOUNT_PNL_UPDATE: 451,
104
+ };
105
+
106
+ // Proto files to load
107
+ const PROTO_FILES = [
108
+ 'base.proto',
109
+ 'request_heartbeat.proto',
110
+ 'response_heartbeat.proto',
111
+ 'request_rithmic_system_info.proto',
112
+ 'response_rithmic_system_info.proto',
113
+ 'request_login.proto',
114
+ 'response_login.proto',
115
+ 'request_logout.proto',
116
+ 'response_logout.proto',
117
+ 'request_login_info.proto',
118
+ 'response_login_info.proto',
119
+ 'request_account_list.proto',
120
+ 'response_account_list.proto',
121
+ 'request_trade_routes.proto',
122
+ 'response_trade_routes.proto',
123
+ 'request_subscribe_for_order_updates.proto',
124
+ 'response_subscribe_for_order_updates.proto',
125
+ 'request_new_order.proto',
126
+ 'response_new_order.proto',
127
+ 'request_cancel_all_orders.proto',
128
+ 'rithmic_order_notification.proto',
129
+ 'exchange_order_notification.proto',
130
+ 'request_show_orders.proto',
131
+ 'response_show_orders.proto',
132
+ 'request_show_order_history.proto',
133
+ 'response_show_order_history.proto',
134
+ 'request_show_order_history_dates.proto',
135
+ 'response_show_order_history_dates.proto',
136
+ 'request_market_data_update.proto',
137
+ 'response_market_data_update.proto',
138
+ 'last_trade.proto',
139
+ 'best_bid_offer.proto',
140
+ 'request_pnl_position_snapshot.proto',
141
+ 'response_pnl_position_snapshot.proto',
142
+ 'request_pnl_position_updates.proto',
143
+ 'response_pnl_position_updates.proto',
144
+ 'account_pnl_position_update.proto',
145
+ 'instrument_pnl_position_update.proto',
146
+ ];
147
+
148
+ module.exports = {
149
+ RITHMIC_ENDPOINTS,
150
+ RITHMIC_SYSTEMS,
151
+ INFRA_TYPE,
152
+ REQ,
153
+ RES,
154
+ STREAM,
155
+ PROTO_FILES,
156
+ };