hedgequantx 2.6.84 → 2.6.85
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 +1 -1
- package/src/pages/algo/one-account.js +147 -48
package/package.json
CHANGED
|
@@ -986,17 +986,163 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
986
986
|
const pnlInterval = setInterval(() => { if (running) pollPnL(); }, 10000);
|
|
987
987
|
pollPnL(); // Initial poll
|
|
988
988
|
|
|
989
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
990
|
+
// ULTRA SOLID EMERGENCY STOP FUNCTION
|
|
991
|
+
// Called when X is pressed - MUST cancel all orders and flatten positions
|
|
992
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
993
|
+
const emergencyStopAll = async () => {
|
|
994
|
+
const accountId = account.rithmicAccountId || account.accountId;
|
|
995
|
+
const MAX_RETRIES = 3;
|
|
996
|
+
const TIMEOUT_MS = 5000;
|
|
997
|
+
|
|
998
|
+
ui.addLog('warning', '████ EMERGENCY STOP INITIATED ████');
|
|
999
|
+
ui.render(stats);
|
|
1000
|
+
|
|
1001
|
+
// Helper: run with timeout
|
|
1002
|
+
const withTimeout = (promise, ms) => Promise.race([
|
|
1003
|
+
promise,
|
|
1004
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('TIMEOUT')), ms))
|
|
1005
|
+
]);
|
|
1006
|
+
|
|
1007
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
1008
|
+
// STEP 1: CANCEL ALL ORDERS (with retries)
|
|
1009
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
1010
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
1011
|
+
try {
|
|
1012
|
+
ui.addLog('info', `[${attempt}/${MAX_RETRIES}] Cancelling all orders...`);
|
|
1013
|
+
ui.render(stats);
|
|
1014
|
+
|
|
1015
|
+
if (typeof service.cancelAllOrders === 'function') {
|
|
1016
|
+
await withTimeout(service.cancelAllOrders(accountId), TIMEOUT_MS);
|
|
1017
|
+
ui.addLog('success', 'All orders cancelled');
|
|
1018
|
+
ui.render(stats);
|
|
1019
|
+
break;
|
|
1020
|
+
} else {
|
|
1021
|
+
ui.addLog('info', 'No cancelAllOrders function - skipping');
|
|
1022
|
+
break;
|
|
1023
|
+
}
|
|
1024
|
+
} catch (e) {
|
|
1025
|
+
ui.addLog('error', `Cancel orders attempt ${attempt} failed: ${e.message}`);
|
|
1026
|
+
ui.render(stats);
|
|
1027
|
+
if (attempt === MAX_RETRIES) {
|
|
1028
|
+
ui.addLog('error', 'FAILED TO CANCEL ORDERS AFTER 3 ATTEMPTS');
|
|
1029
|
+
}
|
|
1030
|
+
await new Promise(r => setTimeout(r, 500)); // Wait before retry
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
1035
|
+
// STEP 2: FLATTEN POSITION (with retries)
|
|
1036
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
1037
|
+
const posQty = Math.abs(currentPosition || stats.position || 0);
|
|
1038
|
+
|
|
1039
|
+
if (posQty > 0) {
|
|
1040
|
+
const closeSide = (currentPosition || stats.position) > 0 ? 1 : 0;
|
|
1041
|
+
const sideStr = closeSide === 1 ? 'SELL' : 'BUY';
|
|
1042
|
+
|
|
1043
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
1044
|
+
try {
|
|
1045
|
+
ui.addLog('info', `[${attempt}/${MAX_RETRIES}] Flattening ${posQty}x position (${sideStr})...`);
|
|
1046
|
+
ui.render(stats);
|
|
1047
|
+
|
|
1048
|
+
// Method 1: Rithmic emergencyStop (best)
|
|
1049
|
+
if (typeof service.emergencyStop === 'function') {
|
|
1050
|
+
const result = await withTimeout(service.emergencyStop(accountId), TIMEOUT_MS);
|
|
1051
|
+
if (result.success) {
|
|
1052
|
+
ui.addLog('success', 'Position FLATTENED (Rithmic emergency stop)');
|
|
1053
|
+
ui.render(stats);
|
|
1054
|
+
break;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
// Method 2: Market order to close
|
|
1059
|
+
if (typeof service.placeOrder === 'function') {
|
|
1060
|
+
await withTimeout(service.placeOrder({
|
|
1061
|
+
accountId: account.accountId,
|
|
1062
|
+
contractId: contractId,
|
|
1063
|
+
type: 2, // Market order
|
|
1064
|
+
side: closeSide,
|
|
1065
|
+
size: posQty
|
|
1066
|
+
}), TIMEOUT_MS);
|
|
1067
|
+
ui.addLog('success', `Position FLATTENED (market ${sideStr} ${posQty}x)`);
|
|
1068
|
+
ui.render(stats);
|
|
1069
|
+
break;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
// Method 3: Rithmic fastExit
|
|
1073
|
+
if (typeof service.fastExit === 'function') {
|
|
1074
|
+
await withTimeout(service.fastExit({
|
|
1075
|
+
accountId: accountId,
|
|
1076
|
+
symbol: symbolName,
|
|
1077
|
+
exchange: contract.exchange || 'CME',
|
|
1078
|
+
size: posQty,
|
|
1079
|
+
side: closeSide
|
|
1080
|
+
}), TIMEOUT_MS);
|
|
1081
|
+
ui.addLog('success', `Position FLATTENED (fast exit ${sideStr} ${posQty}x)`);
|
|
1082
|
+
ui.render(stats);
|
|
1083
|
+
break;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
} catch (e) {
|
|
1087
|
+
ui.addLog('error', `Flatten attempt ${attempt} failed: ${e.message}`);
|
|
1088
|
+
ui.render(stats);
|
|
1089
|
+
if (attempt === MAX_RETRIES) {
|
|
1090
|
+
ui.addLog('error', '████ FAILED TO FLATTEN - CHECK MANUALLY! ████');
|
|
1091
|
+
}
|
|
1092
|
+
await new Promise(r => setTimeout(r, 500));
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
} else {
|
|
1096
|
+
ui.addLog('success', 'No position to flatten - clean exit');
|
|
1097
|
+
ui.render(stats);
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
1101
|
+
// STEP 3: VERIFY (optional - check position is actually flat)
|
|
1102
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
1103
|
+
try {
|
|
1104
|
+
if (typeof service.getPositions === 'function') {
|
|
1105
|
+
const posResult = await withTimeout(service.getPositions(account.accountId), 3000);
|
|
1106
|
+
if (posResult.success && posResult.positions) {
|
|
1107
|
+
const stillOpen = posResult.positions.find(p => {
|
|
1108
|
+
const sym = p.contractId || p.symbol || '';
|
|
1109
|
+
return (sym.includes(symbolName) || sym.includes(contractId)) && p.quantity !== 0;
|
|
1110
|
+
});
|
|
1111
|
+
if (stillOpen) {
|
|
1112
|
+
ui.addLog('error', `████ POSITION STILL OPEN: ${stillOpen.quantity}x ████`);
|
|
1113
|
+
ui.addLog('error', 'CLOSE MANUALLY IN R TRADER!');
|
|
1114
|
+
} else {
|
|
1115
|
+
ui.addLog('success', 'VERIFIED: Position is flat');
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
} catch (e) {
|
|
1120
|
+
// Verification failed, but main stop was attempted
|
|
1121
|
+
ui.addLog('warning', 'Could not verify position - check manually');
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
ui.addLog('info', '════════════════════════════════════');
|
|
1125
|
+
ui.render(stats);
|
|
1126
|
+
};
|
|
1127
|
+
|
|
989
1128
|
// Keyboard handler
|
|
1129
|
+
let emergencyStopInProgress = false;
|
|
1130
|
+
|
|
990
1131
|
const setupKeyHandler = () => {
|
|
991
1132
|
if (!process.stdin.isTTY) return;
|
|
992
1133
|
readline.emitKeypressEvents(process.stdin);
|
|
993
1134
|
process.stdin.setRawMode(true);
|
|
994
1135
|
process.stdin.resume();
|
|
995
1136
|
|
|
996
|
-
const onKey = (str, key) => {
|
|
1137
|
+
const onKey = async (str, key) => {
|
|
997
1138
|
if (key && (key.name === 'x' || key.name === 'X' || (key.ctrl && key.name === 'c'))) {
|
|
1139
|
+
if (emergencyStopInProgress) return; // Prevent double-trigger
|
|
1140
|
+
emergencyStopInProgress = true;
|
|
998
1141
|
running = false;
|
|
999
1142
|
stopReason = 'manual';
|
|
1143
|
+
|
|
1144
|
+
// Run emergency stop IMMEDIATELY on keypress (don't wait for main loop)
|
|
1145
|
+
await emergencyStopAll();
|
|
1000
1146
|
}
|
|
1001
1147
|
};
|
|
1002
1148
|
process.stdin.on('keypress', onKey);
|
|
@@ -1022,53 +1168,6 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
1022
1168
|
clearInterval(refreshInterval);
|
|
1023
1169
|
clearInterval(pnlInterval);
|
|
1024
1170
|
|
|
1025
|
-
// EMERGENCY STOP: Cancel all orders and flatten all positions
|
|
1026
|
-
// Works for BOTH Rithmic (fast path) and ProjectX (slow path)
|
|
1027
|
-
ui.addLog('warning', 'STOPPING - Cancelling orders & flattening positions...');
|
|
1028
|
-
ui.render(stats); // Force render to show message
|
|
1029
|
-
|
|
1030
|
-
try {
|
|
1031
|
-
const accountId = account.rithmicAccountId || account.accountId;
|
|
1032
|
-
|
|
1033
|
-
// 1. Cancel ALL open orders first
|
|
1034
|
-
if (typeof service.cancelAllOrders === 'function') {
|
|
1035
|
-
await service.cancelAllOrders(accountId);
|
|
1036
|
-
ui.addLog('info', 'All orders cancelled');
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
// 2. Flatten position if we have one
|
|
1040
|
-
if (currentPosition !== 0 || stats.position !== 0) {
|
|
1041
|
-
const posQty = Math.abs(currentPosition || stats.position);
|
|
1042
|
-
const closeSide = (currentPosition || stats.position) > 0 ? 1 : 0; // 1=Sell to close long, 0=Buy to close short
|
|
1043
|
-
|
|
1044
|
-
// Try emergency stop first (Rithmic)
|
|
1045
|
-
if (typeof service.emergencyStop === 'function') {
|
|
1046
|
-
const stopResult = await service.emergencyStop(accountId);
|
|
1047
|
-
if (stopResult.success) {
|
|
1048
|
-
ui.addLog('success', 'Position flattened (emergency stop)');
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
// Fallback: place market order to close (ProjectX)
|
|
1052
|
-
else if (typeof service.placeOrder === 'function') {
|
|
1053
|
-
await service.placeOrder({
|
|
1054
|
-
accountId: account.accountId,
|
|
1055
|
-
contractId: contractId,
|
|
1056
|
-
type: 2, // Market order
|
|
1057
|
-
side: closeSide,
|
|
1058
|
-
size: posQty
|
|
1059
|
-
});
|
|
1060
|
-
ui.addLog('success', `Position flattened (market order ${posQty}x)`);
|
|
1061
|
-
}
|
|
1062
|
-
} else {
|
|
1063
|
-
ui.addLog('success', 'No open position - clean exit');
|
|
1064
|
-
}
|
|
1065
|
-
} catch (e) {
|
|
1066
|
-
ui.addLog('error', `Emergency stop error: ${e.message}`);
|
|
1067
|
-
ui.addLog('warning', 'CHECK POSITIONS MANUALLY!');
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
ui.render(stats); // Force render to show final status
|
|
1071
|
-
|
|
1072
1171
|
// Stop Position Manager (fast path)
|
|
1073
1172
|
if (positionManager) {
|
|
1074
1173
|
positionManager.stop();
|