hedgequantx 2.9.88 → 2.9.90

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.
@@ -736,6 +736,47 @@ var require_core = __commonJS({
736
736
  message: `[HQX-2B] Reset state for ${contractId}`
737
737
  });
738
738
  }
739
+ /**
740
+ * Preload historical bars to warm up the strategy
741
+ * @param {string} contractId - Contract ID
742
+ * @param {Array} bars - Array of bars {timestamp, open, high, low, close, volume}
743
+ */
744
+ preloadBars(contractId, bars) {
745
+ if (!bars || bars.length === 0) {
746
+ this.emit("log", {
747
+ type: "debug",
748
+ message: `[HQX-2B] No historical bars to preload`
749
+ });
750
+ return;
751
+ }
752
+ if (!this.barHistory.has(contractId)) {
753
+ this.initialize(contractId);
754
+ }
755
+ const sortedBars = [...bars].sort((a, b) => a.timestamp - b.timestamp);
756
+ this.emit("log", {
757
+ type: "info",
758
+ message: `[HQX-2B] Preloading ${sortedBars.length} historical bars...`
759
+ });
760
+ let signalCount = 0;
761
+ for (const bar of sortedBars) {
762
+ const signal = this.processBar(contractId, bar);
763
+ if (signal) signalCount++;
764
+ }
765
+ const history = this.barHistory.get(contractId) || [];
766
+ const swings = this.swingPoints.get(contractId) || [];
767
+ const zones = this.liquidityZones.get(contractId) || [];
768
+ this.emit("log", {
769
+ type: "info",
770
+ message: `[HQX-2B] Preload complete: ${history.length} bars, ${swings.length} swings, ${zones.length} zones`
771
+ });
772
+ if (signalCount > 0) {
773
+ this.emit("log", {
774
+ type: "debug",
775
+ message: `[HQX-2B] ${signalCount} historical signals detected (ignored)`
776
+ });
777
+ }
778
+ this.lastSignalTime = 0;
779
+ }
739
780
  };
740
781
  module2.exports = { HQX2BLiquiditySweep: HQX2BLiquiditySweep2 };
741
782
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "2.9.88",
3
+ "version": "2.9.90",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
package/src/lib/data.js CHANGED
@@ -16,7 +16,7 @@
16
16
  const EventEmitter = require('events');
17
17
  const { RithmicConnection } = require('../services/rithmic/connection');
18
18
  const { proto } = require('../services/rithmic/protobuf');
19
- const { REQ, RES, STREAM, RITHMIC_ENDPOINTS, RITHMIC_SYSTEMS } = require('../services/rithmic/constants');
19
+ const { REQ, RES, STREAM, RITHMIC_ENDPOINTS, RITHMIC_SYSTEMS, INFRA_TYPE } = require('../services/rithmic/constants');
20
20
 
21
21
  // =============================================================================
22
22
  // MARKET DATA FEED CLASS (Rithmic TICKER_PLANT)
@@ -170,6 +170,99 @@ class MarketDataFeed extends EventEmitter {
170
170
  this.emit('disconnected');
171
171
  }
172
172
 
173
+ /**
174
+ * Get historical bars from HISTORY_PLANT
175
+ * @param {string} symbol - Symbol (e.g., 'MNQH5')
176
+ * @param {string} exchange - Exchange (default: 'CME')
177
+ * @param {number} barCount - Number of bars to fetch (default: 30)
178
+ * @returns {Promise<Array>} Array of bar objects {timestamp, open, high, low, close, volume}
179
+ */
180
+ async getHistoricalBars(symbol, exchange = 'CME', barCount = 30) {
181
+ if (!this.credentials) {
182
+ throw new Error('Not connected - call connect() first');
183
+ }
184
+
185
+ // Create a separate connection to HISTORY_PLANT
186
+ const historyConn = new RithmicConnection();
187
+ const historyConfig = {
188
+ ...this.config,
189
+ infraType: INFRA_TYPE.HISTORY_PLANT,
190
+ };
191
+
192
+ try {
193
+ await historyConn.connect(historyConfig);
194
+
195
+ return new Promise((resolve, reject) => {
196
+ const bars = [];
197
+ const timeout = setTimeout(() => {
198
+ historyConn.disconnect();
199
+ if (bars.length > 0) {
200
+ resolve(bars);
201
+ } else {
202
+ reject(new Error('History request timeout'));
203
+ }
204
+ }, 10000);
205
+
206
+ // Listen for bar data
207
+ historyConn.on('message', (msg) => {
208
+ if (msg.templateId === RES.TICK_BAR_REPLAY) {
209
+ try {
210
+ const data = proto.decode('ResponseTickBarReplay', msg.data);
211
+ const barObj = data.toJSON ? data.toJSON() : data;
212
+
213
+ if (barObj.openPrice !== undefined) {
214
+ bars.push({
215
+ timestamp: barObj.dataBarSsboe ? barObj.dataBarSsboe[0] * 1000 : Date.now(),
216
+ open: Number(barObj.openPrice),
217
+ high: Number(barObj.highPrice),
218
+ low: Number(barObj.lowPrice),
219
+ close: Number(barObj.closePrice),
220
+ volume: Number(barObj.volume) || 0,
221
+ });
222
+ }
223
+
224
+ // Check if this is the last bar (rpCode contains "end" or empty)
225
+ if (barObj.rpCode && barObj.rpCode.includes('0')) {
226
+ clearTimeout(timeout);
227
+ historyConn.disconnect();
228
+ resolve(bars);
229
+ }
230
+ } catch (e) {
231
+ this.emit('debug', `History decode error: ${e.message}`);
232
+ }
233
+ }
234
+ });
235
+
236
+ // Login to HISTORY_PLANT
237
+ historyConn.once('loggedIn', () => {
238
+ // Request tick bar replay (1-minute bars)
239
+ historyConn.send('RequestTickBarReplay', {
240
+ templateId: REQ.TICK_BAR_REPLAY,
241
+ symbol: symbol,
242
+ exchange: exchange,
243
+ barType: 1, // TICK_BAR
244
+ barSubType: 1, // REGULAR
245
+ barTypeSpecifier: '1', // 1-minute
246
+ userMaxCount: barCount,
247
+ direction: 2, // LAST (most recent)
248
+ timeOrder: 2, // BACKWARDS
249
+ });
250
+ });
251
+
252
+ historyConn.once('loginFailed', (data) => {
253
+ clearTimeout(timeout);
254
+ historyConn.disconnect();
255
+ reject(new Error(`HISTORY_PLANT login failed: ${data.message}`));
256
+ });
257
+
258
+ historyConn.login('HISTORY_PLANT');
259
+ });
260
+ } catch (e) {
261
+ historyConn.disconnect();
262
+ throw e;
263
+ }
264
+ }
265
+
173
266
  /**
174
267
  * Handle incoming messages from TICKER_PLANT
175
268
  */
@@ -410,6 +410,23 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
410
410
  }
411
411
  await marketFeed.connect(rithmicCredentials);
412
412
  await marketFeed.subscribe(symbolCode, contract.exchange || 'CME');
413
+
414
+ // Preload historical bars for HQX-2B strategy only (bar-based strategy)
415
+ if (strategyId === 'hqx-2b' && strategy.preloadBars) {
416
+ ui.addLog('system', 'Loading historical bars...');
417
+ try {
418
+ const historicalBars = await marketFeed.getHistoricalBars(symbolCode, contract.exchange || 'CME', 30);
419
+ if (historicalBars && historicalBars.length > 0) {
420
+ strategy.preloadBars(contractId, historicalBars);
421
+ ui.addLog('system', `Loaded ${historicalBars.length} bars - strategy ready!`);
422
+ } else {
423
+ ui.addLog('system', 'No historical bars - collecting live data...');
424
+ }
425
+ } catch (histErr) {
426
+ ui.addLog('debug', `Historical data unavailable: ${histErr.message}`);
427
+ ui.addLog('system', 'Collecting live data...');
428
+ }
429
+ }
413
430
  } catch (e) {
414
431
  ui.addLog('error', `Failed to connect: ${e.message}`);
415
432
  }
@@ -60,6 +60,7 @@ const REQ = {
60
60
  MARKET_DATA: 100,
61
61
  PRODUCT_CODES: 111,
62
62
  FRONT_MONTH_CONTRACT: 113,
63
+ TICK_BAR_REPLAY: 200, // History plant - request bar data
63
64
  LOGIN_INFO: 300,
64
65
  ACCOUNT_LIST: 302,
65
66
  ACCOUNT_RMS: 304,
@@ -88,6 +89,7 @@ const RES = {
88
89
  MARKET_DATA: 101,
89
90
  PRODUCT_CODES: 112,
90
91
  FRONT_MONTH_CONTRACT: 114,
92
+ TICK_BAR_REPLAY: 201, // History plant - bar data response
91
93
  LOGIN_INFO: 301,
92
94
  ACCOUNT_LIST: 303,
93
95
  ACCOUNT_RMS: 305,
@@ -134,6 +136,8 @@ const PROTO_FILES = [
134
136
  'response_login_info.proto',
135
137
  'request_account_list.proto',
136
138
  'response_account_list.proto',
139
+ 'request_tick_bar_replay.proto',
140
+ 'response_tick_bar_replay.proto',
137
141
  'request_trade_routes.proto',
138
142
  'response_trade_routes.proto',
139
143
  'request_subscribe_for_order_updates.proto',