hedgequantx 2.9.195 → 2.9.196
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
|
@@ -492,48 +492,92 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
|
|
|
492
492
|
clearInterval(pnlInterval);
|
|
493
493
|
clearInterval(liveLogInterval);
|
|
494
494
|
|
|
495
|
-
//
|
|
496
|
-
//
|
|
495
|
+
// ============================================================
|
|
496
|
+
// EMERGENCY FLATTEN - ULTRA SOLID
|
|
497
|
+
// 1. Cancel ALL pending orders
|
|
498
|
+
// 2. Exit position via Rithmic API
|
|
499
|
+
// 3. Fallback: Market order to flatten
|
|
500
|
+
// 4. Verify position is flat
|
|
501
|
+
// ============================================================
|
|
502
|
+
|
|
503
|
+
const accountId = account.rithmicAccountId || account.accountId;
|
|
504
|
+
const exchange = contract.exchange || 'CME';
|
|
505
|
+
|
|
506
|
+
ui.addLog('system', 'Stopping algo - cancelling orders...');
|
|
507
|
+
sessionLogger.log('EXIT', 'Emergency flatten initiated');
|
|
508
|
+
|
|
509
|
+
// STEP 1: Cancel ALL pending orders
|
|
510
|
+
try {
|
|
511
|
+
if (service.cancelAllOrders) {
|
|
512
|
+
await service.cancelAllOrders(accountId);
|
|
513
|
+
ui.addLog('system', 'All pending orders cancelled');
|
|
514
|
+
sessionLogger.log('EXIT', 'All orders cancelled');
|
|
515
|
+
}
|
|
516
|
+
} catch (e) {
|
|
517
|
+
sessionLogger.log('EXIT', `Cancel orders error: ${e.message}`);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Wait for cancellations to process
|
|
521
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
522
|
+
|
|
523
|
+
// STEP 2: Try Rithmic's native ExitPosition first
|
|
524
|
+
try {
|
|
525
|
+
if (service.exitPosition) {
|
|
526
|
+
ui.addLog('system', 'Exiting position via Rithmic...');
|
|
527
|
+
const exitResult = await service.exitPosition(accountId, symbolCode, exchange);
|
|
528
|
+
if (exitResult.success) {
|
|
529
|
+
ui.addLog('system', 'Exit position command sent');
|
|
530
|
+
sessionLogger.log('EXIT', 'ExitPosition sent to Rithmic');
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
} catch (e) {
|
|
534
|
+
sessionLogger.log('EXIT', `ExitPosition error: ${e.message}`);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Wait for exit to process
|
|
538
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
539
|
+
|
|
540
|
+
// STEP 3: Verify position and flatten with market order if needed
|
|
497
541
|
let positionToFlatten = 0;
|
|
498
542
|
try {
|
|
499
|
-
const posResult = await service.getPositions(
|
|
543
|
+
const posResult = await service.getPositions(accountId);
|
|
500
544
|
if (posResult.success && posResult.positions) {
|
|
501
545
|
const pos = posResult.positions.find(p => {
|
|
502
546
|
const sym = p.contractId || p.symbol || '';
|
|
503
|
-
return sym.includes(contract.name) || sym.includes(
|
|
547
|
+
return sym.includes(symbolCode) || sym.includes(contract.name) || sym.includes(contract.baseSymbol);
|
|
504
548
|
});
|
|
505
549
|
if (pos && pos.quantity) {
|
|
506
550
|
const qty = parseInt(pos.quantity);
|
|
507
|
-
// Validate quantity is reasonable (not overflow)
|
|
508
|
-
if (!isNaN(qty) && Math.abs(qty)
|
|
551
|
+
// Validate quantity is reasonable (not overflow - max 100 contracts)
|
|
552
|
+
if (!isNaN(qty) && qty !== 0 && Math.abs(qty) <= 100) {
|
|
509
553
|
positionToFlatten = qty;
|
|
510
554
|
}
|
|
511
555
|
}
|
|
512
556
|
}
|
|
513
557
|
} catch (e) {
|
|
514
|
-
|
|
515
|
-
if (Math.abs(currentPosition) < 1000) {
|
|
516
|
-
positionToFlatten = currentPosition;
|
|
517
|
-
}
|
|
558
|
+
sessionLogger.log('EXIT', `Get positions error: ${e.message}`);
|
|
518
559
|
}
|
|
519
560
|
|
|
561
|
+
// STEP 4: Market order flatten if position still open
|
|
520
562
|
if (positionToFlatten !== 0) {
|
|
521
563
|
const side = positionToFlatten > 0 ? 'LONG' : 'SHORT';
|
|
522
564
|
const size = Math.abs(positionToFlatten);
|
|
523
|
-
ui.addLog('system', `Flattening
|
|
524
|
-
sessionLogger.log('EXIT', `
|
|
565
|
+
ui.addLog('system', `Flattening ${side} ${size} @ market...`);
|
|
566
|
+
sessionLogger.log('EXIT', `Market flatten: ${side} ${size}`);
|
|
567
|
+
|
|
525
568
|
try {
|
|
526
569
|
const flattenResult = await service.placeOrder({
|
|
527
|
-
accountId:
|
|
570
|
+
accountId: accountId,
|
|
528
571
|
symbol: symbolCode,
|
|
529
|
-
exchange:
|
|
530
|
-
type: 2, // Market
|
|
572
|
+
exchange: exchange,
|
|
573
|
+
type: 2, // Market order
|
|
531
574
|
side: positionToFlatten > 0 ? 1 : 0, // Sell if long, Buy if short
|
|
532
575
|
size: size
|
|
533
576
|
});
|
|
577
|
+
|
|
534
578
|
if (flattenResult.success) {
|
|
535
579
|
ui.addLog('fill_' + (positionToFlatten > 0 ? 'sell' : 'buy'), `Position flattened @ market`);
|
|
536
|
-
sessionLogger.log('EXIT',
|
|
580
|
+
sessionLogger.log('EXIT', 'Position flattened successfully');
|
|
537
581
|
} else {
|
|
538
582
|
ui.addLog('error', `Flatten failed: ${flattenResult.error}`);
|
|
539
583
|
sessionLogger.log('EXIT', `Flatten failed: ${flattenResult.error}`);
|
|
@@ -542,8 +586,32 @@ const executeAlgo = async ({ service, account, contract, config, strategy: strat
|
|
|
542
586
|
ui.addLog('error', `Flatten error: ${e.message}`);
|
|
543
587
|
sessionLogger.log('EXIT', `Flatten error: ${e.message}`);
|
|
544
588
|
}
|
|
589
|
+
|
|
545
590
|
// Wait for fill
|
|
546
591
|
await new Promise(r => setTimeout(r, 2000));
|
|
592
|
+
|
|
593
|
+
// STEP 5: Final verification
|
|
594
|
+
try {
|
|
595
|
+
const verifyResult = await service.getPositions(accountId);
|
|
596
|
+
if (verifyResult.success && verifyResult.positions) {
|
|
597
|
+
const pos = verifyResult.positions.find(p => {
|
|
598
|
+
const sym = p.contractId || p.symbol || '';
|
|
599
|
+
return sym.includes(symbolCode);
|
|
600
|
+
});
|
|
601
|
+
if (pos && pos.quantity && Math.abs(parseInt(pos.quantity)) > 0 && Math.abs(parseInt(pos.quantity)) <= 100) {
|
|
602
|
+
ui.addLog('error', `WARNING: Position still open! Qty: ${pos.quantity}`);
|
|
603
|
+
sessionLogger.log('EXIT', `WARNING: Position NOT flat! Qty: ${pos.quantity}`);
|
|
604
|
+
} else {
|
|
605
|
+
ui.addLog('system', 'Position verified flat');
|
|
606
|
+
sessionLogger.log('EXIT', 'Position verified flat');
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
} catch (e) {
|
|
610
|
+
// Ignore verification errors
|
|
611
|
+
}
|
|
612
|
+
} else {
|
|
613
|
+
ui.addLog('system', 'No position to flatten');
|
|
614
|
+
sessionLogger.log('EXIT', 'No position to flatten');
|
|
547
615
|
}
|
|
548
616
|
|
|
549
617
|
await marketFeed.disconnect();
|
|
@@ -17,7 +17,7 @@ const {
|
|
|
17
17
|
getPositions,
|
|
18
18
|
fetchTradeRoutes,
|
|
19
19
|
} = require('./accounts');
|
|
20
|
-
const { placeOrder, cancelOrder, getOrders, getOrderHistory, getOrderHistoryDates, getTradeHistoryFull, closePosition } = require('./orders');
|
|
20
|
+
const { placeOrder, cancelOrder, cancelAllOrders, exitPosition, getOrders, getOrderHistory, getOrderHistoryDates, getTradeHistoryFull, closePosition } = require('./orders');
|
|
21
21
|
const { fillsToRoundTrips, calculateTradeStats } = require('./trades');
|
|
22
22
|
const { getContracts, searchContracts } = require('./contracts');
|
|
23
23
|
const { handleAutoReconnect } = require('./reconnect');
|
|
@@ -320,6 +320,8 @@ class RithmicService extends EventEmitter {
|
|
|
320
320
|
async getTradeHistoryFull(days) { return getTradeHistoryFull(this, days); }
|
|
321
321
|
async placeOrder(orderData) { return placeOrder(this, orderData); }
|
|
322
322
|
async cancelOrder(orderId) { return cancelOrder(this, orderId); }
|
|
323
|
+
async cancelAllOrders(accountId) { return cancelAllOrders(this, accountId); }
|
|
324
|
+
async exitPosition(accountId, symbol, exchange) { return exitPosition(this, accountId, symbol, exchange); }
|
|
323
325
|
async closePosition(accountId, symbol) { return closePosition(this, accountId, symbol); }
|
|
324
326
|
async getContracts() { return getContracts(this); }
|
|
325
327
|
async searchContracts(searchText) { return searchContracts(this, searchText); }
|
|
@@ -473,9 +473,112 @@ const closePosition = async (service, accountId, symbol) => {
|
|
|
473
473
|
});
|
|
474
474
|
};
|
|
475
475
|
|
|
476
|
+
/**
|
|
477
|
+
* Cancel all orders for an account
|
|
478
|
+
* Uses RequestCancelAllOrders (template 346)
|
|
479
|
+
* @param {RithmicService} service - The Rithmic service instance
|
|
480
|
+
* @param {string} accountId - Account ID
|
|
481
|
+
*/
|
|
482
|
+
const cancelAllOrders = async (service, accountId) => {
|
|
483
|
+
if (!service.orderConn || !service.loginInfo) {
|
|
484
|
+
return { success: false, error: 'Not connected' };
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return new Promise((resolve) => {
|
|
488
|
+
const timeout = setTimeout(() => {
|
|
489
|
+
resolve({ success: true, message: 'Cancel all orders sent' });
|
|
490
|
+
}, 3000);
|
|
491
|
+
|
|
492
|
+
try {
|
|
493
|
+
service.orderConn.send('RequestCancelAllOrders', {
|
|
494
|
+
templateId: REQ.CANCEL_ALL_ORDERS,
|
|
495
|
+
userMsg: ['HQX-FLATTEN'],
|
|
496
|
+
fcmId: service.loginInfo.fcmId,
|
|
497
|
+
ibId: service.loginInfo.ibId,
|
|
498
|
+
accountId: accountId,
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
// Listen for response
|
|
502
|
+
const handler = (msg) => {
|
|
503
|
+
if (msg.templateId === 347) { // ResponseCancelAllOrders
|
|
504
|
+
clearTimeout(timeout);
|
|
505
|
+
service.orderConn.removeListener('message', handler);
|
|
506
|
+
resolve({ success: true, message: 'All orders cancelled' });
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
service.orderConn.on('message', handler);
|
|
510
|
+
|
|
511
|
+
} catch (error) {
|
|
512
|
+
clearTimeout(timeout);
|
|
513
|
+
resolve({ success: false, error: error.message });
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Exit position using Rithmic's ExitPosition request
|
|
520
|
+
* Uses RequestExitPosition (template 3504)
|
|
521
|
+
* @param {RithmicService} service - The Rithmic service instance
|
|
522
|
+
* @param {string} accountId - Account ID
|
|
523
|
+
* @param {string} symbol - Symbol to exit
|
|
524
|
+
* @param {string} exchange - Exchange (default CME)
|
|
525
|
+
*/
|
|
526
|
+
const exitPosition = async (service, accountId, symbol, exchange = 'CME') => {
|
|
527
|
+
if (!service.orderConn || !service.loginInfo) {
|
|
528
|
+
return { success: false, error: 'Not connected' };
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Get trade route
|
|
532
|
+
let tradeRoute = null;
|
|
533
|
+
if (service.tradeRoutes && service.tradeRoutes.size > 0) {
|
|
534
|
+
const routeInfo = service.tradeRoutes.get(exchange);
|
|
535
|
+
if (routeInfo) {
|
|
536
|
+
tradeRoute = routeInfo.tradeRoute;
|
|
537
|
+
} else {
|
|
538
|
+
const firstRoute = service.tradeRoutes.values().next().value;
|
|
539
|
+
if (firstRoute) tradeRoute = firstRoute.tradeRoute;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return new Promise((resolve) => {
|
|
544
|
+
const timeout = setTimeout(() => {
|
|
545
|
+
resolve({ success: true, message: 'Exit position sent' });
|
|
546
|
+
}, 5000);
|
|
547
|
+
|
|
548
|
+
try {
|
|
549
|
+
service.orderConn.send('RequestExitPosition', {
|
|
550
|
+
templateId: REQ.EXIT_POSITION,
|
|
551
|
+
userMsg: ['HQX-EXIT'],
|
|
552
|
+
fcmId: service.loginInfo.fcmId,
|
|
553
|
+
ibId: service.loginInfo.ibId,
|
|
554
|
+
accountId: accountId,
|
|
555
|
+
symbol: symbol,
|
|
556
|
+
exchange: exchange,
|
|
557
|
+
tradeRoute: tradeRoute,
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
// Listen for response
|
|
561
|
+
const handler = (msg) => {
|
|
562
|
+
if (msg.templateId === 3505) { // ResponseExitPosition
|
|
563
|
+
clearTimeout(timeout);
|
|
564
|
+
service.orderConn.removeListener('message', handler);
|
|
565
|
+
resolve({ success: true, message: 'Position exit sent' });
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
service.orderConn.on('message', handler);
|
|
569
|
+
|
|
570
|
+
} catch (error) {
|
|
571
|
+
clearTimeout(timeout);
|
|
572
|
+
resolve({ success: false, error: error.message });
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
};
|
|
576
|
+
|
|
476
577
|
module.exports = {
|
|
477
578
|
placeOrder,
|
|
478
579
|
cancelOrder,
|
|
580
|
+
cancelAllOrders,
|
|
581
|
+
exitPosition,
|
|
479
582
|
getOrders,
|
|
480
583
|
getOrderHistory,
|
|
481
584
|
getOrderHistoryDates,
|