hedgequantx 1.2.41 → 1.2.43
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 +1 -1
- package/src/app.js +16 -7
- package/src/services/rithmic/constants.js +4 -4
- package/src/services/rithmic/index.js +210 -5
- package/src/services/rithmic/protobuf.js +131 -0
package/package.json
CHANGED
package/src/app.js
CHANGED
|
@@ -359,19 +359,24 @@ const rithmicMenu = async () => {
|
|
|
359
359
|
|
|
360
360
|
if (result.success) {
|
|
361
361
|
spinner.text = 'Fetching accounts...';
|
|
362
|
-
await service.getTradingAccounts();
|
|
362
|
+
const accResult = await service.getTradingAccounts();
|
|
363
363
|
|
|
364
364
|
connections.add('rithmic', service, service.propfirm.name);
|
|
365
365
|
currentService = service;
|
|
366
366
|
currentPlatform = 'rithmic';
|
|
367
|
-
spinner.succeed(`Connected to ${service.propfirm.name}`);
|
|
367
|
+
spinner.succeed(`Connected to ${service.propfirm.name} (${accResult.accounts?.length || 0} accounts)`);
|
|
368
|
+
|
|
369
|
+
// Small pause to see the success message
|
|
370
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
368
371
|
return service;
|
|
369
372
|
} else {
|
|
370
373
|
spinner.fail(result.error || 'Authentication failed');
|
|
374
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
371
375
|
return null;
|
|
372
376
|
}
|
|
373
377
|
} catch (error) {
|
|
374
|
-
spinner.fail(error.message);
|
|
378
|
+
spinner.fail(`Connection error: ${error.message}`);
|
|
379
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
375
380
|
return null;
|
|
376
381
|
}
|
|
377
382
|
};
|
|
@@ -583,10 +588,14 @@ const dashboardMenu = async (service) => {
|
|
|
583
588
|
console.log(chalk.cyan('║') + chalk.white.bold(centerText('DASHBOARD', innerWidth)) + chalk.cyan('║'));
|
|
584
589
|
console.log(chalk.cyan('╠' + '═'.repeat(innerWidth) + '╣'));
|
|
585
590
|
|
|
586
|
-
// Connection info
|
|
587
|
-
const
|
|
588
|
-
|
|
589
|
-
|
|
591
|
+
// Connection info - show all active connections
|
|
592
|
+
const allConns = connections.getAll();
|
|
593
|
+
if (allConns.length > 0) {
|
|
594
|
+
const connNames = allConns.map(c => c.propfirm || c.type).join(', ');
|
|
595
|
+
const connText = `Connected to ${connNames}`;
|
|
596
|
+
const connInfo = chalk.green(connText);
|
|
597
|
+
console.log(chalk.cyan('║') + ' ' + connInfo + ' '.repeat(Math.max(0, innerWidth - connText.length - 2)) + chalk.cyan('║'));
|
|
598
|
+
}
|
|
590
599
|
|
|
591
600
|
if (user) {
|
|
592
601
|
const userInfo = 'Welcome, ' + user.userName.toUpperCase() + '!';
|
|
@@ -61,8 +61,8 @@ const REQ = {
|
|
|
61
61
|
BRACKET_ORDER: 330,
|
|
62
62
|
CANCEL_ALL_ORDERS: 346,
|
|
63
63
|
EXIT_POSITION: 3504,
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
PNL_POSITION_UPDATES: 400,
|
|
65
|
+
PNL_POSITION_SNAPSHOT: 402,
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
// Response template IDs
|
|
@@ -87,8 +87,8 @@ const RES = {
|
|
|
87
87
|
BRACKET_ORDER: 331,
|
|
88
88
|
CANCEL_ALL_ORDERS: 347,
|
|
89
89
|
EXIT_POSITION: 3505,
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
PNL_POSITION_UPDATES: 401,
|
|
91
|
+
PNL_POSITION_SNAPSHOT: 403,
|
|
92
92
|
};
|
|
93
93
|
|
|
94
94
|
// Streaming template IDs
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
const EventEmitter = require('events');
|
|
7
7
|
const { RithmicConnection } = require('./connection');
|
|
8
|
-
const { proto, decodeAccountPnL } = require('./protobuf');
|
|
8
|
+
const { proto, decodeAccountPnL, decodeInstrumentPnL } = require('./protobuf');
|
|
9
9
|
const { RITHMIC_ENDPOINTS, RITHMIC_SYSTEMS, REQ, RES, STREAM } = require('./constants');
|
|
10
10
|
|
|
11
11
|
class RithmicService extends EventEmitter {
|
|
@@ -18,7 +18,10 @@ class RithmicService extends EventEmitter {
|
|
|
18
18
|
this.loginInfo = null;
|
|
19
19
|
this.accounts = [];
|
|
20
20
|
this.accountPnL = new Map(); // accountId -> pnl data
|
|
21
|
+
this.positions = new Map(); // symbol -> position data (from InstrumentPnLPositionUpdate)
|
|
22
|
+
this.orders = []; // Active orders
|
|
21
23
|
this.user = null;
|
|
24
|
+
this.credentials = null; // Store for PNL connection
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
/**
|
|
@@ -82,10 +85,37 @@ class RithmicService extends EventEmitter {
|
|
|
82
85
|
try {
|
|
83
86
|
await this.fetchAccounts();
|
|
84
87
|
} catch (e) {
|
|
85
|
-
// Accounts fetch failed,
|
|
86
|
-
console.log('Note: Could not fetch accounts');
|
|
88
|
+
// Accounts fetch failed, ignore
|
|
87
89
|
}
|
|
88
|
-
|
|
90
|
+
|
|
91
|
+
// Create default account if none found
|
|
92
|
+
if (this.accounts.length === 0) {
|
|
93
|
+
this.accounts = [{
|
|
94
|
+
accountId: username,
|
|
95
|
+
accountName: username,
|
|
96
|
+
fcmId: data.fcmId,
|
|
97
|
+
ibId: data.ibId,
|
|
98
|
+
}];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Store credentials for PNL connection
|
|
102
|
+
this.credentials = { username, password };
|
|
103
|
+
|
|
104
|
+
// Format accounts for response
|
|
105
|
+
const formattedAccounts = this.accounts.map(acc => ({
|
|
106
|
+
accountId: acc.accountId,
|
|
107
|
+
accountName: acc.accountName || acc.accountId,
|
|
108
|
+
balance: this.propfirm.defaultBalance,
|
|
109
|
+
startingBalance: this.propfirm.defaultBalance,
|
|
110
|
+
profitAndLoss: 0,
|
|
111
|
+
status: 0
|
|
112
|
+
}));
|
|
113
|
+
|
|
114
|
+
resolve({
|
|
115
|
+
success: true,
|
|
116
|
+
user: this.user,
|
|
117
|
+
accounts: formattedAccounts
|
|
118
|
+
});
|
|
89
119
|
});
|
|
90
120
|
|
|
91
121
|
this.orderConn.once('loginFailed', (data) => {
|
|
@@ -290,6 +320,9 @@ class RithmicService extends EventEmitter {
|
|
|
290
320
|
case RES.TRADE_ROUTES:
|
|
291
321
|
this.onTradeRoutes(data);
|
|
292
322
|
break;
|
|
323
|
+
case RES.SHOW_ORDERS:
|
|
324
|
+
this.onShowOrdersResponse(data);
|
|
325
|
+
break;
|
|
293
326
|
case STREAM.EXCHANGE_NOTIFICATION:
|
|
294
327
|
this.onExchangeNotification(data);
|
|
295
328
|
break;
|
|
@@ -299,6 +332,18 @@ class RithmicService extends EventEmitter {
|
|
|
299
332
|
}
|
|
300
333
|
}
|
|
301
334
|
|
|
335
|
+
onShowOrdersResponse(data) {
|
|
336
|
+
try {
|
|
337
|
+
const res = proto.decode('ResponseShowOrders', data);
|
|
338
|
+
if (res.rpCode?.[0] === '0') {
|
|
339
|
+
// End of orders list
|
|
340
|
+
this.emit('ordersReceived');
|
|
341
|
+
}
|
|
342
|
+
} catch (e) {
|
|
343
|
+
// Ignore
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
302
347
|
/**
|
|
303
348
|
* Handle PNL_PLANT messages
|
|
304
349
|
*/
|
|
@@ -386,7 +431,37 @@ class RithmicService extends EventEmitter {
|
|
|
386
431
|
}
|
|
387
432
|
|
|
388
433
|
onInstrumentPnLUpdate(data) {
|
|
389
|
-
// Handle instrument-level PnL
|
|
434
|
+
// Handle instrument-level PnL - this contains position data
|
|
435
|
+
try {
|
|
436
|
+
const pos = decodeInstrumentPnL(data);
|
|
437
|
+
if (pos.symbol && pos.accountId) {
|
|
438
|
+
const key = `${pos.accountId}:${pos.symbol}:${pos.exchange}`;
|
|
439
|
+
// Net quantity can come from netQuantity field or calculated from buy/sell
|
|
440
|
+
const netQty = pos.netQuantity || pos.openPositionQuantity || ((pos.buyQty || 0) - (pos.sellQty || 0));
|
|
441
|
+
|
|
442
|
+
if (netQty !== 0) {
|
|
443
|
+
// We have an open position
|
|
444
|
+
this.positions.set(key, {
|
|
445
|
+
accountId: pos.accountId,
|
|
446
|
+
symbol: pos.symbol,
|
|
447
|
+
exchange: pos.exchange || 'CME',
|
|
448
|
+
quantity: netQty,
|
|
449
|
+
averagePrice: pos.avgOpenFillPrice || 0,
|
|
450
|
+
openPnl: parseFloat(pos.openPositionPnl || pos.dayOpenPnl || 0),
|
|
451
|
+
closedPnl: parseFloat(pos.closedPositionPnl || pos.dayClosedPnl || 0),
|
|
452
|
+
dayPnl: parseFloat(pos.dayPnl || 0),
|
|
453
|
+
isSnapshot: pos.isSnapshot || false,
|
|
454
|
+
});
|
|
455
|
+
} else {
|
|
456
|
+
// Position closed
|
|
457
|
+
this.positions.delete(key);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
this.emit('positionUpdate', this.positions.get(key));
|
|
461
|
+
}
|
|
462
|
+
} catch (e) {
|
|
463
|
+
// Ignore decode errors
|
|
464
|
+
}
|
|
390
465
|
}
|
|
391
466
|
|
|
392
467
|
onExchangeNotification(data) {
|
|
@@ -417,6 +492,133 @@ class RithmicService extends EventEmitter {
|
|
|
417
492
|
return this.user;
|
|
418
493
|
}
|
|
419
494
|
|
|
495
|
+
/**
|
|
496
|
+
* Get positions via PNL_PLANT
|
|
497
|
+
* Positions are streamed via InstrumentPnLPositionUpdate (template 450)
|
|
498
|
+
*/
|
|
499
|
+
async getPositions() {
|
|
500
|
+
// If PNL connection not established, try to connect
|
|
501
|
+
if (!this.pnlConn && this.credentials) {
|
|
502
|
+
await this.connectPnL(this.credentials.username, this.credentials.password);
|
|
503
|
+
// Request snapshot to populate positions
|
|
504
|
+
await this.requestPnLSnapshot();
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Return cached positions
|
|
508
|
+
const positions = Array.from(this.positions.values()).map(pos => ({
|
|
509
|
+
symbol: pos.symbol,
|
|
510
|
+
exchange: pos.exchange,
|
|
511
|
+
quantity: pos.quantity,
|
|
512
|
+
averagePrice: pos.averagePrice,
|
|
513
|
+
unrealizedPnl: pos.openPnl,
|
|
514
|
+
realizedPnl: pos.closedPnl,
|
|
515
|
+
side: pos.quantity > 0 ? 'LONG' : 'SHORT',
|
|
516
|
+
}));
|
|
517
|
+
|
|
518
|
+
return { success: true, positions };
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Get orders via ORDER_PLANT
|
|
523
|
+
* Uses RequestShowOrders (template 320) -> ResponseShowOrders (template 321)
|
|
524
|
+
*/
|
|
525
|
+
async getOrders() {
|
|
526
|
+
if (!this.orderConn || !this.loginInfo) {
|
|
527
|
+
return { success: true, orders: [] };
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
return new Promise((resolve) => {
|
|
531
|
+
const orders = [];
|
|
532
|
+
const timeout = setTimeout(() => {
|
|
533
|
+
resolve({ success: true, orders });
|
|
534
|
+
}, 3000);
|
|
535
|
+
|
|
536
|
+
// Listen for order notifications
|
|
537
|
+
const orderHandler = (notification) => {
|
|
538
|
+
// RithmicOrderNotification contains order details
|
|
539
|
+
if (notification.orderId) {
|
|
540
|
+
orders.push({
|
|
541
|
+
orderId: notification.orderId,
|
|
542
|
+
symbol: notification.symbol,
|
|
543
|
+
exchange: notification.exchange,
|
|
544
|
+
side: notification.transactionType === 1 ? 'BUY' : 'SELL',
|
|
545
|
+
quantity: notification.quantity,
|
|
546
|
+
filledQuantity: notification.filledQuantity || 0,
|
|
547
|
+
price: notification.price,
|
|
548
|
+
orderType: notification.orderType,
|
|
549
|
+
status: notification.status,
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
this.once('ordersReceived', () => {
|
|
555
|
+
clearTimeout(timeout);
|
|
556
|
+
this.removeListener('orderNotification', orderHandler);
|
|
557
|
+
resolve({ success: true, orders });
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
this.on('orderNotification', orderHandler);
|
|
561
|
+
|
|
562
|
+
// Send request
|
|
563
|
+
try {
|
|
564
|
+
for (const acc of this.accounts) {
|
|
565
|
+
this.orderConn.send('RequestShowOrders', {
|
|
566
|
+
templateId: REQ.SHOW_ORDERS,
|
|
567
|
+
userMsg: ['HQX'],
|
|
568
|
+
fcmId: acc.fcmId || this.loginInfo.fcmId,
|
|
569
|
+
ibId: acc.ibId || this.loginInfo.ibId,
|
|
570
|
+
accountId: acc.accountId,
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
} catch (e) {
|
|
574
|
+
clearTimeout(timeout);
|
|
575
|
+
resolve({ success: false, error: e.message, orders: [] });
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Get order history
|
|
582
|
+
* Uses RequestShowOrderHistorySummary (template 324)
|
|
583
|
+
*/
|
|
584
|
+
async getOrderHistory(date) {
|
|
585
|
+
if (!this.orderConn || !this.loginInfo) {
|
|
586
|
+
return { success: true, orders: [] };
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Default to today
|
|
590
|
+
const dateStr = date || new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
|
591
|
+
|
|
592
|
+
return new Promise((resolve) => {
|
|
593
|
+
const orders = [];
|
|
594
|
+
const timeout = setTimeout(() => {
|
|
595
|
+
resolve({ success: true, orders });
|
|
596
|
+
}, 3000);
|
|
597
|
+
|
|
598
|
+
try {
|
|
599
|
+
for (const acc of this.accounts) {
|
|
600
|
+
this.orderConn.send('RequestShowOrderHistorySummary', {
|
|
601
|
+
templateId: REQ.SHOW_ORDER_HISTORY,
|
|
602
|
+
userMsg: ['HQX'],
|
|
603
|
+
fcmId: acc.fcmId || this.loginInfo.fcmId,
|
|
604
|
+
ibId: acc.ibId || this.loginInfo.ibId,
|
|
605
|
+
accountId: acc.accountId,
|
|
606
|
+
date: dateStr,
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// Wait for response
|
|
611
|
+
setTimeout(() => {
|
|
612
|
+
clearTimeout(timeout);
|
|
613
|
+
resolve({ success: true, orders });
|
|
614
|
+
}, 2000);
|
|
615
|
+
} catch (e) {
|
|
616
|
+
clearTimeout(timeout);
|
|
617
|
+
resolve({ success: false, error: e.message, orders: [] });
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
|
|
420
622
|
/**
|
|
421
623
|
* Check market hours (same as ProjectX)
|
|
422
624
|
*/
|
|
@@ -477,8 +679,11 @@ class RithmicService extends EventEmitter {
|
|
|
477
679
|
}
|
|
478
680
|
this.accounts = [];
|
|
479
681
|
this.accountPnL.clear();
|
|
682
|
+
this.positions.clear();
|
|
683
|
+
this.orders = [];
|
|
480
684
|
this.loginInfo = null;
|
|
481
685
|
this.user = null;
|
|
686
|
+
this.credentials = null;
|
|
482
687
|
}
|
|
483
688
|
}
|
|
484
689
|
|
|
@@ -28,6 +28,36 @@ const PNL_FIELDS = {
|
|
|
28
28
|
USECS: 150101,
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
+
// Instrument PnL Position Update field IDs
|
|
32
|
+
const INSTRUMENT_PNL_FIELDS = {
|
|
33
|
+
TEMPLATE_ID: 154467,
|
|
34
|
+
IS_SNAPSHOT: 110121,
|
|
35
|
+
FCM_ID: 154013,
|
|
36
|
+
IB_ID: 154014,
|
|
37
|
+
ACCOUNT_ID: 154008,
|
|
38
|
+
SYMBOL: 110100,
|
|
39
|
+
EXCHANGE: 110101,
|
|
40
|
+
PRODUCT_CODE: 100749,
|
|
41
|
+
INSTRUMENT_TYPE: 110116,
|
|
42
|
+
FILL_BUY_QTY: 154041,
|
|
43
|
+
FILL_SELL_QTY: 154042,
|
|
44
|
+
ORDER_BUY_QTY: 154037,
|
|
45
|
+
ORDER_SELL_QTY: 154038,
|
|
46
|
+
BUY_QTY: 154260,
|
|
47
|
+
SELL_QTY: 154261,
|
|
48
|
+
AVG_OPEN_FILL_PRICE: 154434,
|
|
49
|
+
DAY_OPEN_PNL: 157954,
|
|
50
|
+
DAY_CLOSED_PNL: 157955,
|
|
51
|
+
DAY_PNL: 157956,
|
|
52
|
+
OPEN_POSITION_PNL: 156961,
|
|
53
|
+
OPEN_POSITION_QUANTITY: 156962,
|
|
54
|
+
CLOSED_POSITION_PNL: 156963,
|
|
55
|
+
CLOSED_POSITION_QUANTITY: 156964,
|
|
56
|
+
NET_QUANTITY: 156967,
|
|
57
|
+
SSBOE: 150100,
|
|
58
|
+
USECS: 150101,
|
|
59
|
+
};
|
|
60
|
+
|
|
31
61
|
/**
|
|
32
62
|
* Read a varint from buffer
|
|
33
63
|
*/
|
|
@@ -158,6 +188,106 @@ function decodeAccountPnL(buffer) {
|
|
|
158
188
|
return result;
|
|
159
189
|
}
|
|
160
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Manually decode InstrumentPnLPositionUpdate from raw bytes
|
|
193
|
+
*/
|
|
194
|
+
function decodeInstrumentPnL(buffer) {
|
|
195
|
+
const result = {};
|
|
196
|
+
let offset = 0;
|
|
197
|
+
|
|
198
|
+
while (offset < buffer.length) {
|
|
199
|
+
try {
|
|
200
|
+
const [tag, tagOffset] = readVarint(buffer, offset);
|
|
201
|
+
const wireType = tag & 0x7;
|
|
202
|
+
const fieldNumber = tag >>> 3;
|
|
203
|
+
offset = tagOffset;
|
|
204
|
+
|
|
205
|
+
switch (fieldNumber) {
|
|
206
|
+
case INSTRUMENT_PNL_FIELDS.TEMPLATE_ID:
|
|
207
|
+
[result.templateId, offset] = readVarint(buffer, offset);
|
|
208
|
+
break;
|
|
209
|
+
case INSTRUMENT_PNL_FIELDS.IS_SNAPSHOT:
|
|
210
|
+
const [isSnap, snapOffset] = readVarint(buffer, offset);
|
|
211
|
+
result.isSnapshot = isSnap !== 0;
|
|
212
|
+
offset = snapOffset;
|
|
213
|
+
break;
|
|
214
|
+
case INSTRUMENT_PNL_FIELDS.FCM_ID:
|
|
215
|
+
[result.fcmId, offset] = readLengthDelimited(buffer, offset);
|
|
216
|
+
break;
|
|
217
|
+
case INSTRUMENT_PNL_FIELDS.IB_ID:
|
|
218
|
+
[result.ibId, offset] = readLengthDelimited(buffer, offset);
|
|
219
|
+
break;
|
|
220
|
+
case INSTRUMENT_PNL_FIELDS.ACCOUNT_ID:
|
|
221
|
+
[result.accountId, offset] = readLengthDelimited(buffer, offset);
|
|
222
|
+
break;
|
|
223
|
+
case INSTRUMENT_PNL_FIELDS.SYMBOL:
|
|
224
|
+
[result.symbol, offset] = readLengthDelimited(buffer, offset);
|
|
225
|
+
break;
|
|
226
|
+
case INSTRUMENT_PNL_FIELDS.EXCHANGE:
|
|
227
|
+
[result.exchange, offset] = readLengthDelimited(buffer, offset);
|
|
228
|
+
break;
|
|
229
|
+
case INSTRUMENT_PNL_FIELDS.PRODUCT_CODE:
|
|
230
|
+
[result.productCode, offset] = readLengthDelimited(buffer, offset);
|
|
231
|
+
break;
|
|
232
|
+
case INSTRUMENT_PNL_FIELDS.BUY_QTY:
|
|
233
|
+
[result.buyQty, offset] = readVarint(buffer, offset);
|
|
234
|
+
break;
|
|
235
|
+
case INSTRUMENT_PNL_FIELDS.SELL_QTY:
|
|
236
|
+
[result.sellQty, offset] = readVarint(buffer, offset);
|
|
237
|
+
break;
|
|
238
|
+
case INSTRUMENT_PNL_FIELDS.FILL_BUY_QTY:
|
|
239
|
+
[result.fillBuyQty, offset] = readVarint(buffer, offset);
|
|
240
|
+
break;
|
|
241
|
+
case INSTRUMENT_PNL_FIELDS.FILL_SELL_QTY:
|
|
242
|
+
[result.fillSellQty, offset] = readVarint(buffer, offset);
|
|
243
|
+
break;
|
|
244
|
+
case INSTRUMENT_PNL_FIELDS.NET_QUANTITY:
|
|
245
|
+
[result.netQuantity, offset] = readVarint(buffer, offset);
|
|
246
|
+
break;
|
|
247
|
+
case INSTRUMENT_PNL_FIELDS.OPEN_POSITION_QUANTITY:
|
|
248
|
+
[result.openPositionQuantity, offset] = readVarint(buffer, offset);
|
|
249
|
+
break;
|
|
250
|
+
case INSTRUMENT_PNL_FIELDS.AVG_OPEN_FILL_PRICE:
|
|
251
|
+
// Double is 64-bit fixed
|
|
252
|
+
if (wireType === 1) {
|
|
253
|
+
result.avgOpenFillPrice = buffer.readDoubleLE(offset);
|
|
254
|
+
offset += 8;
|
|
255
|
+
} else {
|
|
256
|
+
offset = skipField(buffer, offset, wireType);
|
|
257
|
+
}
|
|
258
|
+
break;
|
|
259
|
+
case INSTRUMENT_PNL_FIELDS.OPEN_POSITION_PNL:
|
|
260
|
+
[result.openPositionPnl, offset] = readLengthDelimited(buffer, offset);
|
|
261
|
+
break;
|
|
262
|
+
case INSTRUMENT_PNL_FIELDS.CLOSED_POSITION_PNL:
|
|
263
|
+
[result.closedPositionPnl, offset] = readLengthDelimited(buffer, offset);
|
|
264
|
+
break;
|
|
265
|
+
case INSTRUMENT_PNL_FIELDS.DAY_PNL:
|
|
266
|
+
[result.dayPnl, offset] = readLengthDelimited(buffer, offset);
|
|
267
|
+
break;
|
|
268
|
+
case INSTRUMENT_PNL_FIELDS.DAY_OPEN_PNL:
|
|
269
|
+
[result.dayOpenPnl, offset] = readLengthDelimited(buffer, offset);
|
|
270
|
+
break;
|
|
271
|
+
case INSTRUMENT_PNL_FIELDS.DAY_CLOSED_PNL:
|
|
272
|
+
[result.dayClosedPnl, offset] = readLengthDelimited(buffer, offset);
|
|
273
|
+
break;
|
|
274
|
+
case INSTRUMENT_PNL_FIELDS.SSBOE:
|
|
275
|
+
[result.ssboe, offset] = readVarint(buffer, offset);
|
|
276
|
+
break;
|
|
277
|
+
case INSTRUMENT_PNL_FIELDS.USECS:
|
|
278
|
+
[result.usecs, offset] = readVarint(buffer, offset);
|
|
279
|
+
break;
|
|
280
|
+
default:
|
|
281
|
+
offset = skipField(buffer, offset, wireType);
|
|
282
|
+
}
|
|
283
|
+
} catch (error) {
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return result;
|
|
289
|
+
}
|
|
290
|
+
|
|
161
291
|
/**
|
|
162
292
|
* Protobuf Handler class
|
|
163
293
|
*/
|
|
@@ -253,6 +383,7 @@ const proto = new ProtobufHandler();
|
|
|
253
383
|
module.exports = {
|
|
254
384
|
proto,
|
|
255
385
|
decodeAccountPnL,
|
|
386
|
+
decodeInstrumentPnL,
|
|
256
387
|
readVarint,
|
|
257
388
|
readLengthDelimited,
|
|
258
389
|
skipField,
|