hedgequantx 2.9.52 → 2.9.54

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.9.52",
3
+ "version": "2.9.54",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
package/src/lib/data.js CHANGED
@@ -209,23 +209,36 @@ class MarketDataFeed extends EventEmitter {
209
209
  _handleLastTrade(data) {
210
210
  try {
211
211
  const trade = proto.decode('LastTrade', data);
212
+ const tradeObj = trade.toJSON ? trade.toJSON() : trade;
213
+
214
+ // protobufjs converts to camelCase and enums to strings
215
+ const price = tradeObj.tradePrice;
216
+ const size = Number(tradeObj.tradeSize) || 1;
217
+ const volume = Number(tradeObj.volume) || size;
218
+
219
+ // aggressor can be 1, 2, "BUY", "SELL"
220
+ const agg = tradeObj.aggressor;
221
+ const side = (agg === 1 || agg === 'BUY') ? 'buy' :
222
+ (agg === 2 || agg === 'SELL') ? 'sell' : 'unknown';
223
+
224
+ if (price === undefined || price === null) return;
212
225
 
213
226
  const tick = {
214
227
  type: 'trade',
215
- symbol: trade.symbol,
216
- exchange: trade.exchange,
217
- price: trade.tradePrice,
218
- size: trade.tradeSize,
219
- volume: trade.volume,
220
- side: trade.aggressor === 1 ? 'buy' : trade.aggressor === 2 ? 'sell' : 'unknown',
228
+ symbol: tradeObj.symbol,
229
+ exchange: tradeObj.exchange,
230
+ price: Number(price),
231
+ size: size,
232
+ volume: volume,
233
+ side: side,
221
234
  timestamp: Date.now(),
222
- ssboe: trade.ssboe,
223
- usecs: trade.usecs,
235
+ ssboe: tradeObj.ssboe,
236
+ usecs: tradeObj.usecs,
224
237
  };
225
238
 
226
239
  this.emit('tick', tick);
227
240
  } catch (e) {
228
- // Ignore decode errors
241
+ // Silent - don't spam errors
229
242
  }
230
243
  }
231
244
 
@@ -235,19 +248,31 @@ class MarketDataFeed extends EventEmitter {
235
248
  _handleBBO(data) {
236
249
  try {
237
250
  const bbo = proto.decode('BestBidOffer', data);
251
+ const bboObj = bbo.toJSON ? bbo.toJSON() : bbo;
252
+
253
+ // protobufjs converts to camelCase
254
+ const bid = bboObj.bidPrice;
255
+ const ask = bboObj.askPrice;
256
+ const bidSize = Number(bboObj.bidSize) || 0;
257
+ const askSize = Number(bboObj.askSize) || 0;
258
+
259
+ // Calculate mid price
260
+ const price = (bid && ask) ? (Number(bid) + Number(ask)) / 2 : (bid || ask || null);
261
+
262
+ if (price === null) return;
238
263
 
239
264
  const tick = {
240
265
  type: 'quote',
241
- symbol: bbo.symbol,
242
- exchange: bbo.exchange,
243
- bid: bbo.bidPrice,
244
- bidSize: bbo.bidSize,
245
- ask: bbo.askPrice,
246
- askSize: bbo.askSize,
247
- price: bbo.bidPrice && bbo.askPrice ? (bbo.bidPrice + bbo.askPrice) / 2 : null,
266
+ symbol: bboObj.symbol,
267
+ exchange: bboObj.exchange,
268
+ bid: bid ? Number(bid) : null,
269
+ bidSize: bidSize,
270
+ ask: ask ? Number(ask) : null,
271
+ askSize: askSize,
272
+ price: Number(price),
248
273
  timestamp: Date.now(),
249
- ssboe: bbo.ssboe,
250
- usecs: bbo.usecs,
274
+ ssboe: bboObj.ssboe,
275
+ usecs: bboObj.usecs,
251
276
  };
252
277
 
253
278
  this.emit('tick', tick);
@@ -234,12 +234,12 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
234
234
  aiContext.recentTicks.push(tick);
235
235
  if (aiContext.recentTicks.length > aiContext.maxTicks) aiContext.recentTicks.shift();
236
236
 
237
- const price = tick.price || tick.tradePrice;
238
- const bid = tick.bid || tick.bidPrice;
239
- const ask = tick.ask || tick.askPrice;
240
- const volume = tick.volume || tick.size || 1;
237
+ const price = Number(tick.price) || Number(tick.tradePrice) || null;
238
+ const bid = Number(tick.bid) || Number(tick.bidPrice) || null;
239
+ const ask = Number(tick.ask) || Number(tick.askPrice) || null;
240
+ const volume = Number(tick.volume) || Number(tick.size) || 1;
241
241
 
242
- // Track buy/sell volume
242
+ // Track buy/sell volume (ensure numeric addition)
243
243
  if (tick.side === 'buy' || tick.aggressor === 1) buyVolume += volume;
244
244
  else if (tick.side === 'sell' || tick.aggressor === 2) sellVolume += volume;
245
245
  else if (price && lastPrice) {
@@ -15,7 +15,7 @@ const BOX = {
15
15
  // Spinner characters
16
16
  const SPINNER = ['\u280B', '\u2819', '\u2839', '\u2838', '\u283C', '\u2834', '\u2826', '\u2827', '\u2807', '\u280F'];
17
17
 
18
- // Log type colors - HF grade
18
+ // Log type colors - HF grade with variety
19
19
  const LOG_COLORS = {
20
20
  // Executions
21
21
  fill_buy: chalk.green.bold,
@@ -23,14 +23,19 @@ const LOG_COLORS = {
23
23
  fill_win: chalk.green.bold,
24
24
  fill_loss: chalk.red.bold,
25
25
  // Status
26
- connected: chalk.green,
26
+ connected: chalk.green.bold,
27
27
  ready: chalk.cyan,
28
28
  // Errors
29
29
  error: chalk.red.bold,
30
30
  reject: chalk.red,
31
- // Info
32
- info: chalk.gray,
33
- system: chalk.blue
31
+ // Info - varied colors
32
+ info: chalk.white,
33
+ signal: chalk.yellow.bold,
34
+ trade: chalk.magenta.bold,
35
+ analysis: chalk.blue,
36
+ risk: chalk.yellow,
37
+ system: chalk.blue,
38
+ debug: chalk.gray
34
39
  };
35
40
 
36
41
  // Log type icons - compact HF style
@@ -44,7 +49,12 @@ const LOG_ICONS = {
44
49
  error: 'ERR ',
45
50
  reject: 'REJ ',
46
51
  info: 'INFO ',
47
- system: 'SYS '
52
+ signal: 'SIG ',
53
+ trade: 'TRADE',
54
+ analysis: 'ANLZ ',
55
+ risk: 'RISK ',
56
+ system: 'SYS ',
57
+ debug: 'DBG '
48
58
  };
49
59
 
50
60
  /**
@@ -129,7 +139,7 @@ class AlgoUI {
129
139
  this._line(chalk.cyan(BOX.ML + BOX.H.repeat(W) + BOX.MR));
130
140
  this._line(chalk.cyan(BOX.V) + chalk.yellow(center(`PROP FUTURES ALGO TRADING V${version}`, W)) + chalk.cyan(BOX.V));
131
141
  this._line(chalk.cyan(BOX.ML + BOX.H.repeat(W) + BOX.MR));
132
- this._line(chalk.cyan(BOX.V) + chalk.yellow(center((this.config.subtitle || 'HQX ALGO TRADING').toUpperCase(), W)) + chalk.cyan(BOX.V));
142
+ this._line(chalk.cyan(BOX.V) + chalk.cyan.bold(center((this.config.subtitle || 'HQX ALGO TRADING').toUpperCase(), W)) + chalk.cyan(BOX.V));
133
143
  }
134
144
 
135
145
  _drawStats(stats) {
@@ -199,7 +209,7 @@ class AlgoUI {
199
209
 
200
210
  // Row 5: Connection | Propfirm
201
211
  const connection = stats.platform || 'Rithmic';
202
- const r5c1 = buildCell('Connection', connection, chalk.white, colL);
212
+ const r5c1 = buildCell('Connection', connection, chalk.cyan, colL);
203
213
  const r5c2 = buildCell('Propfirm', stats.propfirm || 'N/A', chalk.cyan, colR);
204
214
  row(r5c1.padded, r5c2.padded);
205
215
 
@@ -267,9 +277,11 @@ class AlgoUI {
267
277
  _drawLogs() {
268
278
  const { W, logs, maxLogs } = this;
269
279
 
270
- // Activity header - HF style
271
- this.spinnerFrame = (this.spinnerFrame + 1) % SPINNER.length;
272
- const spinner = SPINNER[this.spinnerFrame];
280
+ // Activity header - HF style with animated spinner
281
+ const elapsed = Math.floor((Date.now() - (this.startTime || Date.now())) / 100);
282
+ if (!this.startTime) this.startTime = Date.now();
283
+ const spinnerIdx = elapsed % SPINNER.length;
284
+ const spinner = SPINNER[spinnerIdx];
273
285
  const now = new Date();
274
286
  const timeStr = now.toLocaleTimeString('en-US', { hour12: false });
275
287
  const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });