hedgequantx 2.9.252 → 2.9.254
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
|
@@ -60,6 +60,11 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
|
|
|
60
60
|
let running = true, stopReason = null, startingPnL = null;
|
|
61
61
|
let currentPosition = 0, pendingOrder = false, tickCount = 0, lastBias = 'FLAT';
|
|
62
62
|
|
|
63
|
+
// Clear any stale position data at startup
|
|
64
|
+
if (service.positions) {
|
|
65
|
+
service.positions.clear();
|
|
66
|
+
}
|
|
67
|
+
|
|
63
68
|
// HFT: Use CircularBuffer for O(1) tick storage (replaces O(n) shift())
|
|
64
69
|
const recentTicksBuffer = new CircularBuffer(100);
|
|
65
70
|
const recentSignalsBuffer = new CircularBuffer(10);
|
|
@@ -215,40 +215,53 @@ const handleShowOrdersResponse = (service, data) => {
|
|
|
215
215
|
|
|
216
216
|
/**
|
|
217
217
|
* Handle new order response (template 313)
|
|
218
|
-
*
|
|
218
|
+
*
|
|
219
|
+
* Rithmic sends TWO ResponseNewOrder messages:
|
|
220
|
+
* 1. First with basketId + rqHandlerRpCode: ["0"] = request handler accepted
|
|
221
|
+
* 2. Second with rpCode: ["0"] = gateway accepted
|
|
222
|
+
*
|
|
223
|
+
* We only care about messages with rpCode (gateway response)
|
|
224
|
+
* If rpCode is missing, it's just an ACK from request handler - ignore rejection logic
|
|
219
225
|
*/
|
|
220
226
|
const handleNewOrderResponse = (service, data) => {
|
|
221
227
|
try {
|
|
222
228
|
const res = proto.decode('ResponseNewOrder', data);
|
|
223
229
|
|
|
224
|
-
//
|
|
225
|
-
|
|
230
|
+
// If no rpCode, this is just a request handler ACK - emit but don't judge
|
|
231
|
+
if (!res.rpCode || res.rpCode.length === 0) {
|
|
232
|
+
// First message with basketId - just store it
|
|
233
|
+
if (res.basketId) {
|
|
234
|
+
const order = {
|
|
235
|
+
basketId: res.basketId,
|
|
236
|
+
symbol: res.symbol,
|
|
237
|
+
exchange: res.exchange || 'CME',
|
|
238
|
+
notifyType: 1, // STATUS - order acknowledged
|
|
239
|
+
status: 1, // Pending
|
|
240
|
+
text: null,
|
|
241
|
+
rpCode: res.rqHandlerRpCode,
|
|
242
|
+
userMsg: res.userMsg,
|
|
243
|
+
};
|
|
244
|
+
service.emit('orderNotification', order);
|
|
245
|
+
}
|
|
246
|
+
service.emit('newOrderResponse', res);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
226
249
|
|
|
227
|
-
|
|
250
|
+
// Second message with rpCode - this is the real gateway response
|
|
251
|
+
const isAccepted = res.rpCode[0] === '0';
|
|
228
252
|
|
|
229
|
-
// Build rejection reason from rpCode array
|
|
230
253
|
let rejectReason = null;
|
|
231
|
-
if (!isAccepted
|
|
232
|
-
// rpCode can be ['0'] for success or ['code', 'message', ...] for errors
|
|
233
|
-
// Join all parts after the code for the full message
|
|
254
|
+
if (!isAccepted) {
|
|
234
255
|
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
256
|
const rpMsg = allCodes.slice(1).join(' ').trim();
|
|
239
|
-
const rqMsg = rqCodes.slice(1).join(' ').trim();
|
|
240
257
|
|
|
241
|
-
// Use the message if available
|
|
242
258
|
if (rpMsg) {
|
|
243
259
|
rejectReason = rpMsg;
|
|
244
|
-
} else if (rqMsg) {
|
|
245
|
-
rejectReason = rqMsg;
|
|
246
260
|
} else {
|
|
247
|
-
// Map numeric codes to messages
|
|
248
261
|
const code = allCodes[0];
|
|
249
262
|
const codeMap = {
|
|
250
263
|
'1': 'Invalid request',
|
|
251
|
-
'2': 'Invalid account',
|
|
264
|
+
'2': 'Invalid account',
|
|
252
265
|
'3': 'Invalid symbol',
|
|
253
266
|
'4': 'Invalid quantity',
|
|
254
267
|
'5': 'Insufficient buying power',
|
|
@@ -259,8 +272,7 @@ const handleNewOrderResponse = (service, data) => {
|
|
|
259
272
|
};
|
|
260
273
|
rejectReason = codeMap[code] || `Gateway rejected (code: ${code})`;
|
|
261
274
|
}
|
|
262
|
-
|
|
263
|
-
console.log('[Rithmic] Order REJECTED:', rejectReason);
|
|
275
|
+
console.log('[Rithmic] Order REJECTED by gateway:', rejectReason);
|
|
264
276
|
}
|
|
265
277
|
|
|
266
278
|
const order = {
|
|
@@ -125,8 +125,12 @@ const placeOrder = async (service, orderData) => {
|
|
|
125
125
|
}, ORDER_TIMEOUTS.PLACE);
|
|
126
126
|
|
|
127
127
|
const onNotification = (order) => {
|
|
128
|
-
// Match by symbol
|
|
129
|
-
|
|
128
|
+
// Match by orderTag (userMsg) or symbol
|
|
129
|
+
const orderUserMsg = order.userMsg?.[0] || '';
|
|
130
|
+
const matchesTag = orderUserMsg === orderTag;
|
|
131
|
+
const matchesSymbol = order.symbol === orderData.symbol;
|
|
132
|
+
|
|
133
|
+
if (!matchesTag && !matchesSymbol) return;
|
|
130
134
|
|
|
131
135
|
const notifyType = order.notifyType;
|
|
132
136
|
|
|
@@ -158,26 +162,28 @@ const placeOrder = async (service, orderData) => {
|
|
|
158
162
|
return;
|
|
159
163
|
}
|
|
160
164
|
|
|
161
|
-
// STATUS (notifyType 1) with
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
if (orderData.type === 2) {
|
|
166
|
-
// Market order - don't resolve yet, wait for fill
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
165
|
+
// STATUS (notifyType 1) with rpCode=['0'] - Order accepted by gateway
|
|
166
|
+
// For market orders on Apex/Rithmic, the fill notification may not arrive
|
|
167
|
+
// via ORDER connection, so we consider gateway acceptance as success
|
|
168
|
+
if (notifyType === 1 && order.rpCode?.[0] === '0') {
|
|
169
169
|
clearTimeout(timeout);
|
|
170
170
|
service.removeListener('orderNotification', onNotification);
|
|
171
171
|
resolve({
|
|
172
172
|
success: true,
|
|
173
173
|
orderId: order.basketId,
|
|
174
|
-
status: 2,
|
|
175
|
-
fillPrice: orderData.price,
|
|
176
|
-
filledQty: 0,
|
|
174
|
+
status: orderData.type === 2 ? 3 : 2, // 3=filled for market, 2=working for limit
|
|
175
|
+
fillPrice: orderData.price || 0,
|
|
176
|
+
filledQty: orderData.type === 2 ? orderData.size : 0,
|
|
177
177
|
orderTag,
|
|
178
178
|
});
|
|
179
179
|
return;
|
|
180
180
|
}
|
|
181
|
+
|
|
182
|
+
// STATUS (notifyType 1) with basketId but no rpCode - order acknowledged, wait for more
|
|
183
|
+
if (notifyType === 1 && order.basketId && !order.rpCode) {
|
|
184
|
+
// Just an ACK, continue waiting
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
181
187
|
};
|
|
182
188
|
|
|
183
189
|
service.on('orderNotification', onNotification);
|