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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "2.6.38",
3
+ "version": "2.6.40",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -449,20 +449,21 @@ const FillInfoPool = {
449
449
  */
450
450
  fill(notif, receiveTime, latency) {
451
451
  const o = this._template;
452
- o.orderTag = notif.userMsg?.[0] || null;
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
- o.fillQuantity = notif.fillQuantity || 0;
460
- o.totalFillQuantity = notif.totalFillQuantity || 0;
461
- o.remainingQuantity = notif.remainingQuantity || 0;
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
- const orderTag = notif.userMsg?.[0] || null;
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
- const fillQty = notif.fillQuantity || notif.totalFillQuantity || 0;
502
- const isFill = fillQty > 0;
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
- console.log(`[FILL] Received: ${orderTag} | ${fillInfo.transactionType === 1 ? 'BUY' : 'SELL'} ${fillQty}x @ ${fillInfo.avgFillPrice} | latency=${roundTripLatency}ms`);
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: fillQty,
532
- avgPrice: fillInfo.avgFillPrice,
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.orderConn.send('RequestSubscribeForOrderUpdates', {
198
- templateId: REQ.ORDER_UPDATES,
199
- userMsg: ['HQX'],
200
- fcmId: data.fcmId,
201
- ibId: data.ibId,
202
- });
203
- log.debug('Subscribed to order updates');
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 || !this.service.tickerConn) {
338
- throw new Error('RithmicService or tickerConn not available');
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.isConnected) {
343
- throw new Error('Ticker connection not established');
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;