hedgequantx 2.6.38 → 2.6.40
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
CHANGED
|
@@ -449,20 +449,21 @@ const FillInfoPool = {
|
|
|
449
449
|
*/
|
|
450
450
|
fill(notif, receiveTime, latency) {
|
|
451
451
|
const o = this._template;
|
|
452
|
-
o.orderTag = notif.
|
|
452
|
+
o.orderTag = notif.userTag || null; // userTag contains our order tag
|
|
453
453
|
o.basketId = notif.basketId;
|
|
454
|
-
o.orderId = notif.orderId;
|
|
454
|
+
o.orderId = notif.exchangeOrderId || notif.orderId;
|
|
455
455
|
o.status = notif.status;
|
|
456
456
|
o.symbol = notif.symbol;
|
|
457
457
|
o.exchange = notif.exchange;
|
|
458
458
|
o.accountId = notif.accountId;
|
|
459
|
-
|
|
460
|
-
o.
|
|
461
|
-
o.
|
|
459
|
+
// Proto uses totalFillSize, not fillQuantity
|
|
460
|
+
o.fillQuantity = notif.totalFillSize || notif.fillQuantity || 0;
|
|
461
|
+
o.totalFillQuantity = notif.totalFillSize || notif.totalFillQuantity || 0;
|
|
462
|
+
o.remainingQuantity = notif.totalUnfilledSize || notif.remainingQuantity || 0;
|
|
462
463
|
o.avgFillPrice = parseFloat(notif.avgFillPrice || 0);
|
|
463
|
-
o.lastFillPrice = parseFloat(notif.fillPrice || 0);
|
|
464
|
+
o.lastFillPrice = parseFloat(notif.price || notif.fillPrice || 0);
|
|
464
465
|
o.transactionType = notif.transactionType;
|
|
465
|
-
o.orderType = notif.orderType;
|
|
466
|
+
o.orderType = notif.priceType || notif.orderType;
|
|
466
467
|
o.quantity = notif.quantity;
|
|
467
468
|
o.ssboe = notif.ssboe;
|
|
468
469
|
o.usecs = notif.usecs;
|
|
@@ -495,11 +496,13 @@ const handleOrderNotification = (service, data) => {
|
|
|
495
496
|
|
|
496
497
|
try {
|
|
497
498
|
const notif = proto.decode('RithmicOrderNotification', data);
|
|
498
|
-
|
|
499
|
+
// userTag contains our order tag (userMsg is not in this proto)
|
|
500
|
+
const orderTag = notif.userTag || null;
|
|
499
501
|
|
|
500
502
|
// FAST PATH: Check for fill immediately
|
|
501
|
-
|
|
502
|
-
const
|
|
503
|
+
// Proto uses total_fill_size (camelCase: totalFillSize), not fillQuantity
|
|
504
|
+
const fillQty = notif.totalFillSize || notif.totalFillQuantity || notif.fillQuantity || 0;
|
|
505
|
+
const isFill = fillQty > 0 || notif.status === 'complete';
|
|
503
506
|
|
|
504
507
|
// Calculate round-trip latency if this is a fill we're tracking
|
|
505
508
|
let roundTripLatency = null;
|
|
@@ -521,15 +524,18 @@ const handleOrderNotification = (service, data) => {
|
|
|
521
524
|
// Emit raw notification
|
|
522
525
|
service.emit('orderNotification', fillInfo);
|
|
523
526
|
|
|
524
|
-
// Emit fill event if this is a fill
|
|
527
|
+
// Emit fill event if this is a fill (status=complete means order is fully filled)
|
|
525
528
|
if (isFill) {
|
|
526
|
-
|
|
529
|
+
const actualFillQty = fillInfo.totalFillQuantity || fillInfo.fillQuantity || notif.quantity || 0;
|
|
530
|
+
const fillPrice = fillInfo.avgFillPrice || fillInfo.lastFillPrice || 0;
|
|
531
|
+
|
|
532
|
+
console.log(`[FILL] Received: ${orderTag} | ${fillInfo.transactionType === 1 ? 'BUY' : 'SELL'} ${actualFillQty}x @ ${fillPrice} | latency=${roundTripLatency}ms`);
|
|
527
533
|
|
|
528
534
|
debug('ORDER FILLED:', {
|
|
529
535
|
orderTag,
|
|
530
536
|
side: fillInfo.transactionType === 1 ? 'BUY' : 'SELL',
|
|
531
|
-
qty:
|
|
532
|
-
avgPrice:
|
|
537
|
+
qty: actualFillQty,
|
|
538
|
+
avgPrice: fillPrice,
|
|
533
539
|
latencyMs: roundTripLatency,
|
|
534
540
|
});
|
|
535
541
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const EventEmitter = require('events');
|
|
9
9
|
const { RithmicConnection } = require('./connection');
|
|
10
|
-
const { RITHMIC_ENDPOINTS, RITHMIC_SYSTEMS } = require('./constants');
|
|
10
|
+
const { RITHMIC_ENDPOINTS, RITHMIC_SYSTEMS, REQ } = require('./constants');
|
|
11
11
|
const { createOrderHandler, createPnLHandler, LatencyTracker } = require('./handlers');
|
|
12
12
|
const {
|
|
13
13
|
fetchAccounts,
|
|
@@ -192,15 +192,18 @@ class RithmicService extends EventEmitter {
|
|
|
192
192
|
log.warn('Failed to fetch accounts', { error: err.message });
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
// Subscribe to order updates (required to receive fill notifications)
|
|
195
|
+
// Subscribe to order updates for each account (required to receive fill notifications)
|
|
196
196
|
try {
|
|
197
|
-
this.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
197
|
+
for (const acc of this.accounts) {
|
|
198
|
+
this.orderConn.send('RequestSubscribeForOrderUpdates', {
|
|
199
|
+
templateId: REQ.ORDER_UPDATES,
|
|
200
|
+
userMsg: ['HQX'],
|
|
201
|
+
fcmId: acc.fcmId || data.fcmId,
|
|
202
|
+
ibId: acc.ibId || data.ibId,
|
|
203
|
+
accountId: acc.accountId,
|
|
204
|
+
});
|
|
205
|
+
log.debug('Subscribed to order updates for account', { accountId: acc.accountId });
|
|
206
|
+
}
|
|
204
207
|
} catch (err) {
|
|
205
208
|
log.warn('Failed to subscribe to order updates', { error: err.message });
|
|
206
209
|
}
|
|
@@ -331,16 +331,37 @@ class RithmicMarketDataFeed extends EventEmitter {
|
|
|
331
331
|
|
|
332
332
|
/**
|
|
333
333
|
* Connect to market data (uses existing tickerConn from RithmicService)
|
|
334
|
+
* Will attempt to reconnect if tickerConn is not connected
|
|
334
335
|
* @returns {Promise<boolean>}
|
|
335
336
|
*/
|
|
336
337
|
async connect() {
|
|
337
|
-
if (!this.service
|
|
338
|
-
throw new Error('RithmicService
|
|
338
|
+
if (!this.service) {
|
|
339
|
+
throw new Error('RithmicService not available');
|
|
339
340
|
}
|
|
340
341
|
|
|
341
|
-
// Check if ticker connection is ready
|
|
342
|
-
if (!this.service.tickerConn
|
|
343
|
-
|
|
342
|
+
// Check if ticker connection is ready, reconnect if needed
|
|
343
|
+
if (!this.service.tickerConn?.isConnected) {
|
|
344
|
+
log.info('Ticker connection not ready, attempting to reconnect...');
|
|
345
|
+
|
|
346
|
+
// Try to reconnect using stored credentials
|
|
347
|
+
if (this.service.credentials) {
|
|
348
|
+
try {
|
|
349
|
+
const connected = await this.service.connectTicker(
|
|
350
|
+
this.service.credentials.username,
|
|
351
|
+
this.service.credentials.password
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
if (!connected || !this.service.tickerConn?.isConnected) {
|
|
355
|
+
throw new Error('Failed to reconnect to TICKER_PLANT');
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
log.info('Ticker connection re-established');
|
|
359
|
+
} catch (err) {
|
|
360
|
+
throw new Error(`Ticker reconnection failed: ${err.message}`);
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
throw new Error('Ticker connection not established and no credentials available');
|
|
364
|
+
}
|
|
344
365
|
}
|
|
345
366
|
|
|
346
367
|
// Setup message handler
|
|
@@ -40,6 +40,7 @@ const OrderPool = {
|
|
|
40
40
|
_template: {
|
|
41
41
|
templateId: REQ.NEW_ORDER,
|
|
42
42
|
userMsg: [''],
|
|
43
|
+
userTag: '', // Our order tag - returned in RithmicOrderNotification
|
|
43
44
|
fcmId: '',
|
|
44
45
|
ibId: '',
|
|
45
46
|
accountId: '',
|
|
@@ -63,6 +64,7 @@ const OrderPool = {
|
|
|
63
64
|
fill(orderTag, loginInfo, orderData) {
|
|
64
65
|
const o = this._template;
|
|
65
66
|
o.userMsg[0] = orderTag;
|
|
67
|
+
o.userTag = orderTag; // Set userTag for notification tracking
|
|
66
68
|
o.fcmId = loginInfo.fcmId;
|
|
67
69
|
o.ibId = loginInfo.ibId;
|
|
68
70
|
o.accountId = orderData.accountId;
|