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.
- package/package.json +2 -1
- package/src/app.js +114 -5
- package/src/services/rithmic/connection.js +203 -0
- package/src/services/rithmic/constants.js +156 -0
- package/src/services/rithmic/index.js +487 -0
- package/src/services/rithmic/proto/account_pnl_position_update.proto +59 -0
- package/src/services/rithmic/proto/base.proto +7 -0
- package/src/services/rithmic/proto/best_bid_offer.proto +39 -0
- package/src/services/rithmic/proto/exchange_order_notification.proto +140 -0
- package/src/services/rithmic/proto/instrument_pnl_position_update.proto +50 -0
- package/src/services/rithmic/proto/last_trade.proto +53 -0
- package/src/services/rithmic/proto/request_account_list.proto +20 -0
- package/src/services/rithmic/proto/request_cancel_all_orders.proto +15 -0
- package/src/services/rithmic/proto/request_heartbeat.proto +13 -0
- package/src/services/rithmic/proto/request_login.proto +28 -0
- package/src/services/rithmic/proto/request_login_info.proto +10 -0
- package/src/services/rithmic/proto/request_logout.proto +10 -0
- package/src/services/rithmic/proto/request_market_data_update.proto +42 -0
- package/src/services/rithmic/proto/request_new_order.proto +84 -0
- package/src/services/rithmic/proto/request_pnl_position_snapshot.proto +14 -0
- package/src/services/rithmic/proto/request_pnl_position_updates.proto +20 -0
- package/src/services/rithmic/proto/request_rithmic_system_info.proto +8 -0
- package/src/services/rithmic/proto/request_show_order_history.proto +16 -0
- package/src/services/rithmic/proto/request_show_order_history_dates.proto +10 -0
- package/src/services/rithmic/proto/request_show_order_history_summary.proto +14 -0
- package/src/services/rithmic/proto/request_show_orders.proto +14 -0
- package/src/services/rithmic/proto/request_subscribe_for_order_updates.proto +14 -0
- package/src/services/rithmic/proto/request_tick_bar_replay.proto +48 -0
- package/src/services/rithmic/proto/request_trade_routes.proto +11 -0
- package/src/services/rithmic/proto/response_account_list.proto +18 -0
- package/src/services/rithmic/proto/response_heartbeat.proto +14 -0
- package/src/services/rithmic/proto/response_login.proto +18 -0
- package/src/services/rithmic/proto/response_login_info.proto +24 -0
- package/src/services/rithmic/proto/response_logout.proto +11 -0
- package/src/services/rithmic/proto/response_market_data_update.proto +9 -0
- package/src/services/rithmic/proto/response_new_order.proto +18 -0
- package/src/services/rithmic/proto/response_pnl_position_snapshot.proto +11 -0
- package/src/services/rithmic/proto/response_pnl_position_updates.proto +11 -0
- package/src/services/rithmic/proto/response_rithmic_system_info.proto +12 -0
- package/src/services/rithmic/proto/response_show_order_history.proto +11 -0
- package/src/services/rithmic/proto/response_show_order_history_dates.proto +13 -0
- package/src/services/rithmic/proto/response_show_order_history_summary.proto +11 -0
- package/src/services/rithmic/proto/response_show_orders.proto +11 -0
- package/src/services/rithmic/proto/response_subscribe_for_order_updates.proto +11 -0
- package/src/services/rithmic/proto/response_tick_bar_replay.proto +40 -0
- package/src/services/rithmic/proto/response_trade_routes.proto +19 -0
- package/src/services/rithmic/proto/rithmic_order_notification.proto +124 -0
- package/src/services/rithmic/protobuf.js +259 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hedgequantx",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.35",
|
|
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,8 @@ const { execSync } = require('child_process');
|
|
|
11
11
|
const path = require('path');
|
|
12
12
|
|
|
13
13
|
const { ProjectXService, connections } = require('./services');
|
|
14
|
-
const {
|
|
14
|
+
const { RithmicService } = require('./services/rithmic');
|
|
15
|
+
const { PROPFIRM_CHOICES, getPropFirmsByPlatform, getPropFirm } = require('./config');
|
|
15
16
|
const { getDevice, getSeparator, printLogo, getLogoWidth, drawBoxHeader, drawBoxFooter, centerText, createBoxMenu } = require('./ui');
|
|
16
17
|
const { validateUsername, validatePassword, maskSensitive } = require('./security');
|
|
17
18
|
|
|
@@ -22,6 +23,7 @@ const { algoTradingMenu } = require('./pages/algo');
|
|
|
22
23
|
|
|
23
24
|
// Current service reference
|
|
24
25
|
let currentService = null;
|
|
26
|
+
let currentPlatform = null; // 'projectx' or 'rithmic'
|
|
25
27
|
|
|
26
28
|
/**
|
|
27
29
|
* Displays the application banner with stats if connected
|
|
@@ -257,6 +259,107 @@ const projectXMenu = async () => {
|
|
|
257
259
|
await service.getUser();
|
|
258
260
|
connections.add('projectx', service, service.propfirm.name);
|
|
259
261
|
currentService = service;
|
|
262
|
+
currentPlatform = 'projectx';
|
|
263
|
+
spinner.succeed(`Connected to ${service.propfirm.name}`);
|
|
264
|
+
return service;
|
|
265
|
+
} else {
|
|
266
|
+
spinner.fail(result.error || 'Authentication failed');
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
} catch (error) {
|
|
270
|
+
spinner.fail(error.message);
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Rithmic platform connection menu
|
|
277
|
+
*/
|
|
278
|
+
const rithmicMenu = async () => {
|
|
279
|
+
const propfirms = getPropFirmsByPlatform('Rithmic');
|
|
280
|
+
const boxWidth = getLogoWidth();
|
|
281
|
+
const innerWidth = boxWidth - 2;
|
|
282
|
+
const numCols = 3;
|
|
283
|
+
const colWidth = Math.floor(innerWidth / numCols);
|
|
284
|
+
|
|
285
|
+
// Build numbered list
|
|
286
|
+
const numbered = propfirms.map((pf, i) => ({
|
|
287
|
+
num: i + 1,
|
|
288
|
+
key: pf.key,
|
|
289
|
+
name: pf.displayName,
|
|
290
|
+
systemName: pf.rithmicSystem
|
|
291
|
+
}));
|
|
292
|
+
|
|
293
|
+
// PropFirm selection box
|
|
294
|
+
console.log();
|
|
295
|
+
console.log(chalk.cyan('╔' + '═'.repeat(innerWidth) + '╗'));
|
|
296
|
+
console.log(chalk.cyan('║') + chalk.white.bold(centerText('SELECT PROPFIRM (RITHMIC)', innerWidth)) + chalk.cyan('║'));
|
|
297
|
+
console.log(chalk.cyan('║') + ' '.repeat(innerWidth) + chalk.cyan('║'));
|
|
298
|
+
|
|
299
|
+
// Display in 3 columns with fixed width alignment
|
|
300
|
+
const rows = Math.ceil(numbered.length / numCols);
|
|
301
|
+
|
|
302
|
+
for (let row = 0; row < rows; row++) {
|
|
303
|
+
let line = '';
|
|
304
|
+
for (let col = 0; col < numCols; col++) {
|
|
305
|
+
const idx = row + col * rows;
|
|
306
|
+
if (idx < numbered.length) {
|
|
307
|
+
const item = numbered[idx];
|
|
308
|
+
const numStr = item.num.toString().padStart(2, ' ');
|
|
309
|
+
const coloredText = chalk.cyan(`[${numStr}]`) + ' ' + chalk.white(item.name);
|
|
310
|
+
const textLen = 4 + 1 + item.name.length;
|
|
311
|
+
const padding = colWidth - textLen - 2;
|
|
312
|
+
line += ' ' + coloredText + ' '.repeat(Math.max(0, padding));
|
|
313
|
+
} else {
|
|
314
|
+
line += ' '.repeat(colWidth);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
const lineLen = line.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
318
|
+
const adjust = innerWidth - lineLen;
|
|
319
|
+
console.log(chalk.cyan('║') + line + ' '.repeat(Math.max(0, adjust)) + chalk.cyan('║'));
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
console.log(chalk.cyan('║') + ' '.repeat(innerWidth) + chalk.cyan('║'));
|
|
323
|
+
const backText = ' ' + chalk.red('[X] Back');
|
|
324
|
+
const backLen = '[X] Back'.length + 2;
|
|
325
|
+
console.log(chalk.cyan('║') + backText + ' '.repeat(innerWidth - backLen) + chalk.cyan('║'));
|
|
326
|
+
console.log(chalk.cyan('╚' + '═'.repeat(innerWidth) + '╝'));
|
|
327
|
+
console.log();
|
|
328
|
+
|
|
329
|
+
const validInputs = numbered.map(n => n.num.toString());
|
|
330
|
+
validInputs.push('x', 'X');
|
|
331
|
+
|
|
332
|
+
const { action } = await inquirer.prompt([
|
|
333
|
+
{
|
|
334
|
+
type: 'input',
|
|
335
|
+
name: 'action',
|
|
336
|
+
message: chalk.cyan(`Enter choice (1-${numbered.length}/X):`),
|
|
337
|
+
validate: (input) => {
|
|
338
|
+
if (validInputs.includes(input)) return true;
|
|
339
|
+
return `Please enter 1-${numbered.length} or X`;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
]);
|
|
343
|
+
|
|
344
|
+
if (action.toLowerCase() === 'x') return null;
|
|
345
|
+
|
|
346
|
+
const selectedIdx = parseInt(action) - 1;
|
|
347
|
+
const selectedPropfirm = numbered[selectedIdx];
|
|
348
|
+
|
|
349
|
+
const credentials = await loginPrompt(selectedPropfirm.name);
|
|
350
|
+
const spinner = ora('Connecting to Rithmic...').start();
|
|
351
|
+
|
|
352
|
+
try {
|
|
353
|
+
const service = new RithmicService(selectedPropfirm.key);
|
|
354
|
+
const result = await service.login(credentials.username, credentials.password);
|
|
355
|
+
|
|
356
|
+
if (result.success) {
|
|
357
|
+
spinner.text = 'Fetching accounts...';
|
|
358
|
+
await service.getTradingAccounts();
|
|
359
|
+
|
|
360
|
+
connections.add('rithmic', service, service.propfirm.name);
|
|
361
|
+
currentService = service;
|
|
362
|
+
currentPlatform = 'rithmic';
|
|
260
363
|
spinner.succeed(`Connected to ${service.propfirm.name}`);
|
|
261
364
|
return service;
|
|
262
365
|
} else {
|
|
@@ -294,7 +397,7 @@ const mainMenu = async () => {
|
|
|
294
397
|
console.log(chalk.cyan('║') + leftText + ' '.repeat(Math.max(0, leftPad)) + rightText + ' '.repeat(Math.max(0, rightPad)) + chalk.cyan('║'));
|
|
295
398
|
};
|
|
296
399
|
|
|
297
|
-
menuRow(chalk.cyan('[1] ProjectX'), chalk.
|
|
400
|
+
menuRow(chalk.cyan('[1] ProjectX'), chalk.cyan('[2] Rithmic'));
|
|
298
401
|
menuRow(chalk.gray('[3] Tradovate (Coming Soon)'), chalk.red('[X] Exit'));
|
|
299
402
|
|
|
300
403
|
console.log(chalk.cyan('╚' + '═'.repeat(innerWidth) + '╝'));
|
|
@@ -304,11 +407,11 @@ const mainMenu = async () => {
|
|
|
304
407
|
{
|
|
305
408
|
type: 'input',
|
|
306
409
|
name: 'action',
|
|
307
|
-
message: chalk.cyan('Enter choice (1/X):'),
|
|
410
|
+
message: chalk.cyan('Enter choice (1/2/X):'),
|
|
308
411
|
validate: (input) => {
|
|
309
|
-
const valid = ['1', 'x', 'X'];
|
|
412
|
+
const valid = ['1', '2', 'x', 'X'];
|
|
310
413
|
if (valid.includes(input)) return true;
|
|
311
|
-
return 'Please enter 1 or X';
|
|
414
|
+
return 'Please enter 1, 2 or X';
|
|
312
415
|
}
|
|
313
416
|
}
|
|
314
417
|
]);
|
|
@@ -316,6 +419,7 @@ const mainMenu = async () => {
|
|
|
316
419
|
// Map input to action
|
|
317
420
|
const actionMap = {
|
|
318
421
|
'1': 'projectx',
|
|
422
|
+
'2': 'rithmic',
|
|
319
423
|
'x': 'exit',
|
|
320
424
|
'X': 'exit'
|
|
321
425
|
};
|
|
@@ -512,6 +616,11 @@ const run = async () => {
|
|
|
512
616
|
const service = await projectXMenu();
|
|
513
617
|
if (service) currentService = service;
|
|
514
618
|
}
|
|
619
|
+
|
|
620
|
+
if (choice === 'rithmic') {
|
|
621
|
+
const service = await rithmicMenu();
|
|
622
|
+
if (service) currentService = service;
|
|
623
|
+
}
|
|
515
624
|
} else {
|
|
516
625
|
const action = await dashboardMenu(currentService);
|
|
517
626
|
|
|
@@ -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
|
+
};
|