hedgequantx 2.6.89 → 2.6.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "2.6.89",
3
+ "version": "2.6.90",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -853,20 +853,32 @@ const launchAlgo = async (service, account, contract, config) => {
853
853
  algoLogger.error(ui, 'CONNECTION ERROR', e.message.substring(0, 50));
854
854
  }
855
855
 
856
- // Poll account P&L and sync with real trades from API (fallback, WebSocket is primary)
856
+ // Poll account P&L and sync with real trades from API
857
+ // For ProjectX: This is the PRIMARY source of P&L data (no WebSocket events)
858
+ // For Rithmic: This is a fallback (WebSocket events are primary)
857
859
  const pollPnL = async () => {
858
860
  try {
859
- // Get account P&L (fallback if WebSocket not updating)
861
+ // Get account P&L
860
862
  const accountResult = await service.getTradingAccounts();
861
863
  if (accountResult.success && accountResult.accounts) {
862
864
  const acc = accountResult.accounts.find(a => a.accountId === account.accountId);
863
- if (acc && acc.profitAndLoss !== undefined && acc.profitAndLoss !== null) {
864
- // Use day P&L directly (same as R Trader)
865
- stats.pnl = acc.profitAndLoss;
865
+ if (acc) {
866
+ // Total day P&L
867
+ if (acc.profitAndLoss !== undefined && acc.profitAndLoss !== null) {
868
+ stats.pnl = acc.profitAndLoss;
869
+ }
870
+ // Closed P&L (realized)
871
+ if (acc.realizedPnl !== undefined) {
872
+ stats.closedPnl = acc.realizedPnl;
873
+ }
874
+ // Balance
875
+ if (acc.balance !== undefined) {
876
+ stats.balance = acc.balance;
877
+ }
866
878
  }
867
879
  }
868
880
 
869
- // Check positions - detect when position closes
881
+ // Check positions - get Open P&L and detect when position closes
870
882
  const posResult = await service.getPositions(account.accountId);
871
883
  if (posResult.success && posResult.positions) {
872
884
  const pos = posResult.positions.find(p => {
@@ -876,6 +888,20 @@ const launchAlgo = async (service, account, contract, config) => {
876
888
 
877
889
  const newPositionQty = pos?.quantity || 0;
878
890
 
891
+ // Update Open P&L from position (unrealized P&L)
892
+ if (pos && pos.unrealizedPnl !== undefined) {
893
+ stats.openPnl = pos.unrealizedPnl;
894
+ } else if (pos && pos.profitAndLoss !== undefined) {
895
+ stats.openPnl = pos.profitAndLoss;
896
+ } else if (newPositionQty === 0) {
897
+ stats.openPnl = 0; // Flat = no open P&L
898
+ }
899
+
900
+ // Recalculate total P&L if we have components
901
+ if (stats.openPnl !== null && stats.closedPnl !== null) {
902
+ stats.pnl = stats.openPnl + stats.closedPnl;
903
+ }
904
+
879
905
  // Position just closed - cancel remaining orders and log result
880
906
  if (lastPositionQty !== 0 && newPositionQty === 0) {
881
907
  // Cancel all open orders to prevent new positions
@@ -244,7 +244,11 @@ function getMarketBiasLog(direction, delta, ofi) {
244
244
  * Get a momentum log
245
245
  */
246
246
  function getMomentumLog(momentum, zscore) {
247
- const isUp = momentum > 0;
247
+ // Handle undefined/NaN values
248
+ const mom = typeof momentum === 'number' && !isNaN(momentum) ? momentum : 0;
249
+ const z = typeof zscore === 'number' && !isNaN(zscore) ? zscore : 0;
250
+
251
+ const isUp = mom > 0;
248
252
  const pool = isUp ? MOMENTUM_UP_MESSAGES : MOMENTUM_DOWN_MESSAGES;
249
253
  const category = isUp ? 'mom_up' : 'mom_down';
250
254
  const message = getVariedMessage(category, pool, isUp ? 'Momentum up' : 'Momentum down');
@@ -252,7 +256,7 @@ function getMomentumLog(momentum, zscore) {
252
256
 
253
257
  return {
254
258
  message: `${arrow} ${message}`,
255
- details: `Mom=${momentum.toFixed(1)} | Z=${zscore.toFixed(2)}`,
259
+ details: `Mom=${mom.toFixed(1)} | Z=${z.toFixed(2)}`,
256
260
  };
257
261
  }
258
262
 
@@ -260,7 +264,10 @@ function getMomentumLog(momentum, zscore) {
260
264
  * Get a mean reversion log
261
265
  */
262
266
  function getMeanReversionLog(zscore) {
263
- const isOverbought = zscore > 0;
267
+ // Handle undefined/NaN values
268
+ const z = typeof zscore === 'number' && !isNaN(zscore) ? zscore : 0;
269
+
270
+ const isOverbought = z > 0;
264
271
  const pool = isOverbought ? OVERBOUGHT_MESSAGES : OVERSOLD_MESSAGES;
265
272
  const category = isOverbought ? 'mr_overbought' : 'mr_oversold';
266
273
  const message = getVariedMessage(category, pool, isOverbought ? 'Overbought' : 'Oversold');
@@ -268,7 +275,7 @@ function getMeanReversionLog(zscore) {
268
275
 
269
276
  return {
270
277
  message: `${arrow} ${message}`,
271
- details: `Z-Score: ${zscore.toFixed(2)} | Looking for ${isOverbought ? 'SHORT' : 'LONG'}`,
278
+ details: `Z-Score: ${z.toFixed(2)} | Looking for ${isOverbought ? 'SHORT' : 'LONG'}`,
272
279
  };
273
280
  }
274
281