hedgequantx 1.3.6 → 1.3.8

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": "1.3.6",
3
+ "version": "1.3.8",
4
4
  "description": "Prop Futures Algo Trading CLI - Connect to Topstep, Alpha Futures, and other prop firms",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -113,6 +113,10 @@ const dashboardMenu = async (service) => {
113
113
  console.log(chalk.cyan('╚' + '═'.repeat(W) + '╝'));
114
114
  console.log();
115
115
 
116
+ // Small delay to ensure stdin is ready
117
+ await new Promise(resolve => setTimeout(resolve, 50));
118
+ prepareStdin();
119
+
116
120
  const { action } = await inquirer.prompt([
117
121
  {
118
122
  type: 'input',
@@ -95,29 +95,30 @@ const getTradingAccounts = async (service) => {
95
95
  let tradingAccounts = service.accounts.map((acc) => {
96
96
  // Get P&L data from accountPnL map (populated by PNL_PLANT messages)
97
97
  const pnlData = service.accountPnL.get(acc.accountId) || {};
98
- debug(`Account ${acc.accountId} pnlData:`, pnlData);
98
+ debug(`Account ${acc.accountId} pnlData:`, JSON.stringify(pnlData));
99
+ debug(` accountPnL map size:`, service.accountPnL.size);
99
100
 
100
- // Use API values if available, otherwise use defaults
101
+ // Use API values if available
101
102
  const accountBalance = parseFloat(pnlData.accountBalance || 0);
102
103
  const openPnL = parseFloat(pnlData.openPositionPnl || 0);
103
104
  const closedPnL = parseFloat(pnlData.closedPositionPnl || 0);
104
105
  const dayPnL = parseFloat(pnlData.dayPnl || 0);
105
106
 
106
- // Balance: use API value if available, otherwise default
107
- const balance = accountBalance > 0 ? accountBalance : service.propfirm.defaultBalance;
107
+ // Balance: use API value if > 0, otherwise default
108
+ // Most prop firms don't report balance via PnL stream, so we use default
108
109
  const startingBalance = service.propfirm.defaultBalance;
110
+ const balance = accountBalance > 0 ? accountBalance : startingBalance;
109
111
 
110
- // P&L: prefer dayPnl from API, otherwise calculate
112
+ // P&L: prefer dayPnl from API, otherwise calculate from open+closed
111
113
  let profitAndLoss = 0;
112
114
  if (dayPnL !== 0) {
113
115
  profitAndLoss = dayPnL;
114
116
  } else if (openPnL !== 0 || closedPnL !== 0) {
115
117
  profitAndLoss = openPnL + closedPnL;
116
- } else if (accountBalance > 0) {
117
- profitAndLoss = accountBalance - startingBalance;
118
118
  }
119
+ // Don't calculate P&L from balance difference - that's estimation
119
120
 
120
- debug(` balance: ${balance}, P&L: ${profitAndLoss}`);
121
+ debug(` balance: ${balance}, startingBalance: ${startingBalance}, P&L: ${profitAndLoss}`);
121
122
 
122
123
  return {
123
124
  accountId: hashAccountId(acc.accountId),
@@ -6,6 +6,10 @@
6
6
  const { proto, decodeAccountPnL, decodeInstrumentPnL } = require('./protobuf');
7
7
  const { RES, STREAM } = require('./constants');
8
8
 
9
+ // Debug mode
10
+ const DEBUG = process.env.HQX_DEBUG === '1';
11
+ const debug = (...args) => DEBUG && console.log('[Rithmic:Handler]', ...args);
12
+
9
13
  /**
10
14
  * Create ORDER_PLANT message handler
11
15
  * @param {RithmicService} service - The Rithmic service instance
@@ -44,18 +48,24 @@ const createOrderHandler = (service) => {
44
48
  const createPnLHandler = (service) => {
45
49
  return (msg) => {
46
50
  const { templateId, data } = msg;
51
+
52
+ debug('PNL message received, templateId:', templateId);
47
53
 
48
54
  switch (templateId) {
49
55
  case RES.PNL_POSITION_SNAPSHOT:
50
56
  case RES.PNL_POSITION_UPDATES:
51
- // OK response, nothing to do
57
+ debug('PNL snapshot/updates response OK');
52
58
  break;
53
59
  case STREAM.ACCOUNT_PNL_UPDATE:
60
+ debug('Account PNL update received');
54
61
  handleAccountPnLUpdate(service, data);
55
62
  break;
56
63
  case STREAM.INSTRUMENT_PNL_UPDATE:
64
+ debug('Instrument PNL update received');
57
65
  handleInstrumentPnLUpdate(service, data);
58
66
  break;
67
+ default:
68
+ debug('Unknown PNL templateId:', templateId);
59
69
  }
60
70
  };
61
71
  };
@@ -136,19 +146,25 @@ const handleShowOrdersResponse = (service, data) => {
136
146
  const handleAccountPnLUpdate = (service, data) => {
137
147
  try {
138
148
  const pnl = decodeAccountPnL(data);
149
+ debug('Decoded Account PNL:', JSON.stringify(pnl));
150
+
139
151
  if (pnl.accountId) {
140
- service.accountPnL.set(pnl.accountId, {
152
+ const pnlData = {
141
153
  accountBalance: parseFloat(pnl.accountBalance || 0),
142
154
  cashOnHand: parseFloat(pnl.cashOnHand || 0),
143
155
  marginBalance: parseFloat(pnl.marginBalance || 0),
144
156
  openPositionPnl: parseFloat(pnl.openPositionPnl || 0),
145
157
  closedPositionPnl: parseFloat(pnl.closedPositionPnl || 0),
146
158
  dayPnl: parseFloat(pnl.dayPnl || 0),
147
- });
159
+ };
160
+ debug('Storing PNL for account:', pnl.accountId, pnlData);
161
+ service.accountPnL.set(pnl.accountId, pnlData);
148
162
  service.emit('pnlUpdate', pnl);
163
+ } else {
164
+ debug('No accountId in PNL response');
149
165
  }
150
166
  } catch (e) {
151
- // Ignore decode errors
167
+ debug('Error decoding Account PNL:', e.message);
152
168
  }
153
169
  };
154
170
 
@@ -10,6 +10,10 @@ const { createOrderHandler, createPnLHandler } = require('./handlers');
10
10
  const { fetchAccounts, getTradingAccounts, requestPnLSnapshot, subscribePnLUpdates, getPositions, hashAccountId } = require('./accounts');
11
11
  const { placeOrder, cancelOrder, getOrders, getOrderHistory, closePosition } = require('./orders');
12
12
 
13
+ // Debug mode
14
+ const DEBUG = process.env.HQX_DEBUG === '1';
15
+ const debug = (...args) => DEBUG && console.log('[Rithmic:Service]', ...args);
16
+
13
17
  // PropFirm configurations
14
18
  const PROPFIRM_CONFIGS = {
15
19
  'apex': { name: 'Apex Trader Funding', systemName: 'Apex', defaultBalance: 300000, gateway: RITHMIC_ENDPOINTS.CHICAGO },
@@ -94,19 +98,28 @@ class RithmicService extends EventEmitter {
94
98
 
95
99
  this.credentials = { username, password };
96
100
 
101
+ debug('Accounts found:', this.accounts.length);
102
+ debug('Account IDs:', this.accounts.map(a => a.accountId));
103
+
97
104
  // Connect to PNL_PLANT for balance/P&L data
98
105
  try {
99
- await this.connectPnL(username, password);
106
+ debug('Connecting to PNL_PLANT...');
107
+ const pnlConnected = await this.connectPnL(username, password);
108
+ debug('PNL_PLANT connected:', pnlConnected, 'pnlConn:', !!this.pnlConn);
109
+
100
110
  if (this.pnlConn) {
111
+ debug('Requesting P&L snapshot...');
101
112
  await requestPnLSnapshot(this);
113
+ debug('accountPnL map size after snapshot:', this.accountPnL.size);
102
114
  subscribePnLUpdates(this);
103
115
  }
104
116
  } catch (e) {
105
- // PnL connection failed, continue without it
117
+ debug('PnL connection failed:', e.message);
106
118
  }
107
119
 
108
120
  // Get accounts with P&L data (if available)
109
121
  const result = await getTradingAccounts(this);
122
+ debug('Final accounts:', result.accounts.length);
110
123
 
111
124
  resolve({ success: true, user: this.user, accounts: result.accounts });
112
125
  });
package/src/ui/index.js CHANGED
@@ -29,11 +29,32 @@ const { createBoxMenu } = require('./menu');
29
29
  * This fixes input leaking to bash after session restore or algo trading
30
30
  */
31
31
  const prepareStdin = () => {
32
- // Minimal intervention - just ensure stdin is flowing
33
32
  try {
33
+ // Ensure stdin is flowing
34
34
  if (process.stdin.isPaused()) {
35
35
  process.stdin.resume();
36
36
  }
37
+
38
+ // Reset stdin to proper state for inquirer
39
+ if (process.stdin.isTTY) {
40
+ // Disable raw mode if it was left on
41
+ if (process.stdin.isRaw) {
42
+ process.stdin.setRawMode(false);
43
+ }
44
+ }
45
+
46
+ // Clear any buffered input by removing old listeners temporarily
47
+ const oldListeners = process.stdin.listeners('data');
48
+ process.stdin.removeAllListeners('data');
49
+
50
+ // Restore listeners after a tick
51
+ setImmediate(() => {
52
+ oldListeners.forEach(listener => {
53
+ if (!process.stdin.listeners('data').includes(listener)) {
54
+ process.stdin.on('data', listener);
55
+ }
56
+ });
57
+ });
37
58
  } catch (e) {
38
59
  // Ignore errors
39
60
  }