hedgequantx 2.9.249 → 2.9.251

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.249",
3
+ "version": "2.9.251",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -119,30 +119,33 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
119
119
  ui.addLog('system', 'Connecting to market data...');
120
120
 
121
121
  // Listen for position updates from Rithmic (external closes, manual trades)
122
+ // Only if service supports events (RithmicService or DaemonProxyService)
122
123
  const accId = account.rithmicAccountId || account.accountId;
123
- service.on('positionUpdate', (pos) => {
124
- // Match by account and symbol
125
- const posSymbol = pos.contractId || pos.symbol || '';
126
- const matchesSymbol = posSymbol.includes(contract.name) || posSymbol.includes(contractId) ||
127
- posSymbol === symbolCode || contractId.includes(posSymbol);
128
- const matchesAccount = pos.accountId === accId || pos.accountId === account.accountId;
129
-
130
- if (matchesSymbol && matchesAccount) {
131
- const qty = parseInt(pos.quantity) || 0;
132
- if (!isNaN(qty) && Math.abs(qty) < 1000 && qty !== currentPosition) {
133
- const oldPos = currentPosition;
134
- currentPosition = qty;
135
- if (qty === 0 && oldPos !== 0) {
136
- ui.addLog('trade', `Position closed externally (was ${oldPos})`);
137
- sessionLogger.log('POSITION', `External close: ${oldPos} -> 0`);
138
- pendingOrder = false; // Reset pending order flag
139
- } else if (qty !== 0 && oldPos === 0) {
140
- ui.addLog('trade', `Position opened externally: ${qty}`);
141
- sessionLogger.log('POSITION', `External open: 0 -> ${qty}`);
124
+ if (typeof service.on === 'function') {
125
+ service.on('positionUpdate', (pos) => {
126
+ // Match by account and symbol
127
+ const posSymbol = pos.contractId || pos.symbol || '';
128
+ const matchesSymbol = posSymbol.includes(contract.name) || posSymbol.includes(contractId) ||
129
+ posSymbol === symbolCode || contractId.includes(posSymbol);
130
+ const matchesAccount = pos.accountId === accId || pos.accountId === account.accountId;
131
+
132
+ if (matchesSymbol && matchesAccount) {
133
+ const qty = parseInt(pos.quantity) || 0;
134
+ if (!isNaN(qty) && Math.abs(qty) < 1000 && qty !== currentPosition) {
135
+ const oldPos = currentPosition;
136
+ currentPosition = qty;
137
+ if (qty === 0 && oldPos !== 0) {
138
+ ui.addLog('trade', `Position closed externally (was ${oldPos})`);
139
+ sessionLogger.log('POSITION', `External close: ${oldPos} -> 0`);
140
+ pendingOrder = false; // Reset pending order flag
141
+ } else if (qty !== 0 && oldPos === 0) {
142
+ ui.addLog('trade', `Position opened externally: ${qty}`);
143
+ sessionLogger.log('POSITION', `External open: 0 -> ${qty}`);
144
+ }
142
145
  }
143
146
  }
144
- }
145
- });
147
+ });
148
+ }
146
149
 
147
150
  strategy.on('signal', async (signal) => {
148
151
  const dir = signal.direction?.toUpperCase() || 'UNKNOWN';
@@ -221,19 +221,31 @@ const handleNewOrderResponse = (service, data) => {
221
221
  try {
222
222
  const res = proto.decode('ResponseNewOrder', data);
223
223
 
224
+ // Log full response for debugging
225
+ console.log('[Rithmic] ResponseNewOrder:', JSON.stringify(res));
226
+
224
227
  const isAccepted = res.rpCode?.[0] === '0';
225
228
 
226
229
  // Build rejection reason from rpCode array
227
- // rpCode is typically ['code', 'message'] or just ['code']
228
230
  let rejectReason = null;
229
- if (!isAccepted) {
230
- // Try to get the error message from rpCode[1] or rqHandlerRpCode[1]
231
- const rpMsg = res.rpCode?.slice(1).join(' ') || '';
232
- const rqMsg = res.rqHandlerRpCode?.slice(1).join(' ') || '';
233
- rejectReason = rpMsg || rqMsg || null;
231
+ if (!isAccepted && res.rpCode) {
232
+ // rpCode can be ['0'] for success or ['code', 'message', ...] for errors
233
+ // Join all parts after the code for the full message
234
+ const allCodes = Array.isArray(res.rpCode) ? res.rpCode : [res.rpCode];
235
+ const rqCodes = Array.isArray(res.rqHandlerRpCode) ? res.rqHandlerRpCode : [];
236
+
237
+ // Get message from rpCode (skip first element which is the code)
238
+ const rpMsg = allCodes.slice(1).join(' ').trim();
239
+ const rqMsg = rqCodes.slice(1).join(' ').trim();
234
240
 
235
- // If no message, interpret the code
236
- if (!rejectReason && res.rpCode?.[0]) {
241
+ // Use the message if available
242
+ if (rpMsg) {
243
+ rejectReason = rpMsg;
244
+ } else if (rqMsg) {
245
+ rejectReason = rqMsg;
246
+ } else {
247
+ // Map numeric codes to messages
248
+ const code = allCodes[0];
237
249
  const codeMap = {
238
250
  '1': 'Invalid request',
239
251
  '2': 'Invalid account',
@@ -245,17 +257,17 @@ const handleNewOrderResponse = (service, data) => {
245
257
  '8': 'Position limit exceeded',
246
258
  '9': 'Rate limit exceeded',
247
259
  };
248
- rejectReason = codeMap[res.rpCode[0]] || `Gateway rejected (code: ${res.rpCode[0]})`;
260
+ rejectReason = codeMap[code] || `Gateway rejected (code: ${code})`;
249
261
  }
250
262
 
251
- console.log('[Rithmic] Gateway rejection:', rejectReason, '| rpCode:', res.rpCode, '| rqHandlerRpCode:', res.rqHandlerRpCode);
263
+ console.log('[Rithmic] Order REJECTED:', rejectReason);
252
264
  }
253
265
 
254
266
  const order = {
255
267
  basketId: res.basketId || null,
256
268
  symbol: res.symbol,
257
269
  exchange: res.exchange || 'CME',
258
- notifyType: isAccepted ? 1 : 6, // 1=STATUS (accepted), 6=REJECT
270
+ notifyType: isAccepted ? 1 : 6,
259
271
  status: isAccepted ? 2 : 6,
260
272
  text: rejectReason,
261
273
  rpCode: res.rpCode,