hedgequantx 1.2.65 → 1.2.66
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.js +90 -77
- package/src/services/hqx-server.js +25 -3
package/package.json
CHANGED
package/src/pages/algo.js
CHANGED
|
@@ -306,8 +306,8 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
|
|
|
306
306
|
let hqxConnected = false;
|
|
307
307
|
let algoRunning = false;
|
|
308
308
|
let stopReason = null;
|
|
309
|
+
let latency = 0;
|
|
309
310
|
|
|
310
|
-
// Activity logs
|
|
311
311
|
// Stats
|
|
312
312
|
let stats = {
|
|
313
313
|
trades: 0,
|
|
@@ -401,7 +401,9 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
|
|
|
401
401
|
// Info Box
|
|
402
402
|
console.log(chalk.cyan(' ╔════════════════════════════════════════════════════════════════════╗'));
|
|
403
403
|
console.log(chalk.cyan(' ║') + chalk.white(` Account: ${chalk.cyan(accountName.padEnd(25))} Symbol: ${chalk.yellow(symbolName.padEnd(10))} Qty: ${chalk.cyan(numContracts.toString().padEnd(3))}`) + chalk.cyan('║'));
|
|
404
|
-
|
|
404
|
+
const latencyStr = hqxConnected ? (latency > 0 ? `${latency}ms` : '---') : '---';
|
|
405
|
+
const latencyColor = latency < 100 ? chalk.green : (latency < 300 ? chalk.yellow : chalk.red);
|
|
406
|
+
console.log(chalk.cyan(' ║') + chalk.white(` Target: ${chalk.green(('$' + dailyTarget.toFixed(2)).padEnd(12))} Risk: ${chalk.red(('$' + maxRisk.toFixed(2)).padEnd(12))} Server: ${hqxConnected ? chalk.green('ON') : chalk.red('OFF')} ${latencyColor(latencyStr.padEnd(6))}`) + chalk.cyan('║'));
|
|
405
407
|
|
|
406
408
|
// Stats line
|
|
407
409
|
const pnlColor = stats.pnl >= 0 ? chalk.green : chalk.red;
|
|
@@ -554,32 +556,45 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
|
|
|
554
556
|
|
|
555
557
|
// Wait for X key OR auto-stop (target/risk reached)
|
|
556
558
|
await new Promise((resolve) => {
|
|
559
|
+
let resolved = false;
|
|
560
|
+
|
|
561
|
+
const cleanup = () => {
|
|
562
|
+
if (resolved) return;
|
|
563
|
+
resolved = true;
|
|
564
|
+
clearInterval(checkInterval);
|
|
565
|
+
if (process.stdin.isTTY) {
|
|
566
|
+
try {
|
|
567
|
+
process.stdin.setRawMode(false);
|
|
568
|
+
process.stdin.removeAllListeners('keypress');
|
|
569
|
+
} catch (e) {}
|
|
570
|
+
}
|
|
571
|
+
resolve();
|
|
572
|
+
};
|
|
573
|
+
|
|
557
574
|
// Check for auto-stop every 500ms
|
|
558
575
|
const checkInterval = setInterval(() => {
|
|
559
576
|
if (!algoRunning || stopReason) {
|
|
560
|
-
|
|
561
|
-
if (process.stdin.isTTY && process.stdin.isRaw) {
|
|
562
|
-
process.stdin.setRawMode(false);
|
|
563
|
-
}
|
|
564
|
-
resolve();
|
|
577
|
+
cleanup();
|
|
565
578
|
}
|
|
566
579
|
}, 500);
|
|
567
580
|
|
|
568
|
-
//
|
|
581
|
+
// Listen for X key
|
|
569
582
|
if (process.stdin.isTTY) {
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
+
try {
|
|
584
|
+
readline.emitKeypressEvents(process.stdin);
|
|
585
|
+
process.stdin.setRawMode(true);
|
|
586
|
+
process.stdin.resume();
|
|
587
|
+
|
|
588
|
+
process.stdin.on('keypress', (str, key) => {
|
|
589
|
+
if (!key) return;
|
|
590
|
+
const keyName = key.name?.toLowerCase();
|
|
591
|
+
if (keyName === 'x' || (key.ctrl && keyName === 'c')) {
|
|
592
|
+
cleanup();
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
} catch (e) {
|
|
596
|
+
// Fallback: just wait for auto-stop
|
|
597
|
+
}
|
|
583
598
|
}
|
|
584
599
|
});
|
|
585
600
|
|
|
@@ -1014,6 +1029,26 @@ const launchCopyTrading = async (config) => {
|
|
|
1014
1029
|
pnl: 0
|
|
1015
1030
|
};
|
|
1016
1031
|
|
|
1032
|
+
// Log colors
|
|
1033
|
+
const typeColors = {
|
|
1034
|
+
info: chalk.cyan,
|
|
1035
|
+
success: chalk.green,
|
|
1036
|
+
trade: chalk.green.bold,
|
|
1037
|
+
copy: chalk.yellow.bold,
|
|
1038
|
+
error: chalk.red,
|
|
1039
|
+
warning: chalk.yellow
|
|
1040
|
+
};
|
|
1041
|
+
|
|
1042
|
+
const getIcon = (type) => {
|
|
1043
|
+
switch(type) {
|
|
1044
|
+
case 'trade': return '[>]';
|
|
1045
|
+
case 'copy': return '[+]';
|
|
1046
|
+
case 'error': return '[X]';
|
|
1047
|
+
case 'success': return '[OK]';
|
|
1048
|
+
default: return '[.]';
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
|
|
1017
1052
|
const addLog = (type, message) => {
|
|
1018
1053
|
const timestamp = new Date().toLocaleTimeString();
|
|
1019
1054
|
logs.push({ timestamp, type, message });
|
|
@@ -1022,74 +1057,52 @@ const launchCopyTrading = async (config) => {
|
|
|
1022
1057
|
|
|
1023
1058
|
const displayUI = () => {
|
|
1024
1059
|
console.clear();
|
|
1060
|
+
|
|
1061
|
+
// Logo
|
|
1062
|
+
const logo = [
|
|
1063
|
+
'██╗ ██╗ ██████╗ ██╗ ██╗',
|
|
1064
|
+
'██║ ██║██╔═══██╗╚██╗██╔╝',
|
|
1065
|
+
'███████║██║ ██║ ╚███╔╝ ',
|
|
1066
|
+
'██╔══██║██║▄▄ ██║ ██╔██╗ ',
|
|
1067
|
+
'██║ ██║╚██████╔╝██╔╝ ██╗',
|
|
1068
|
+
'╚═╝ ╚═╝ ╚══▀▀═╝ ╚═╝ ╚═╝'
|
|
1069
|
+
];
|
|
1070
|
+
|
|
1025
1071
|
console.log();
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
console.log(chalk.
|
|
1030
|
-
console.log(chalk.gray(getSeparator()));
|
|
1031
|
-
console.log();
|
|
1032
|
-
|
|
1033
|
-
// Risk Management
|
|
1034
|
-
console.log(chalk.white.bold(' RISK MANAGEMENT'));
|
|
1035
|
-
const targetProgress = Math.min(100, (stats.pnl / dailyTarget) * 100);
|
|
1036
|
-
const riskProgress = Math.min(100, (Math.abs(Math.min(0, stats.pnl)) / maxRisk) * 100);
|
|
1037
|
-
console.log(chalk.white(` Target: ${chalk.green('$' + dailyTarget.toFixed(2))} | Progress: ${targetProgress >= 100 ? chalk.green.bold(targetProgress.toFixed(1) + '%') : chalk.yellow(targetProgress.toFixed(1) + '%')}`));
|
|
1038
|
-
console.log(chalk.white(` Risk: ${chalk.red('$' + maxRisk.toFixed(2))} | Used: ${riskProgress >= 100 ? chalk.red.bold(riskProgress.toFixed(1) + '%') : chalk.cyan(riskProgress.toFixed(1) + '%')}`));
|
|
1039
|
-
console.log(chalk.white(` P&L: ${stats.pnl >= 0 ? chalk.green('+$' + stats.pnl.toFixed(2)) : chalk.red('-$' + Math.abs(stats.pnl).toFixed(2))}`));
|
|
1040
|
-
console.log();
|
|
1041
|
-
|
|
1042
|
-
// Lead info
|
|
1043
|
-
console.log(chalk.white.bold(' LEAD'));
|
|
1044
|
-
console.log(chalk.white(` ${chalk.cyan(lead.account.accountName)} @ ${chalk.magenta(lead.account.propfirm)}`));
|
|
1045
|
-
console.log(chalk.white(` ${chalk.cyan(lead.symbol.value)} x ${lead.contracts}`));
|
|
1046
|
-
console.log();
|
|
1047
|
-
|
|
1048
|
-
// Follower info
|
|
1049
|
-
console.log(chalk.white.bold(' FOLLOWER'));
|
|
1050
|
-
console.log(chalk.white(` ${chalk.cyan(follower.account.accountName)} @ ${chalk.magenta(follower.account.propfirm)}`));
|
|
1051
|
-
console.log(chalk.white(` ${chalk.cyan(follower.symbol.value)} x ${follower.contracts}`));
|
|
1072
|
+
logo.forEach(line => {
|
|
1073
|
+
console.log(chalk.cyan(' ' + line));
|
|
1074
|
+
});
|
|
1075
|
+
console.log(chalk.gray(' Copy Trading System'));
|
|
1052
1076
|
console.log();
|
|
1053
1077
|
|
|
1054
|
-
//
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
);
|
|
1061
|
-
console.log(chalk.
|
|
1078
|
+
// Info Box
|
|
1079
|
+
const pnlColor = stats.pnl >= 0 ? chalk.green : chalk.red;
|
|
1080
|
+
const pnlStr = (stats.pnl >= 0 ? '+$' : '-$') + Math.abs(stats.pnl).toFixed(2);
|
|
1081
|
+
|
|
1082
|
+
console.log(chalk.cyan(' ╔════════════════════════════════════════════════════════════════════╗'));
|
|
1083
|
+
console.log(chalk.cyan(' ║') + chalk.white(` LEAD: ${chalk.cyan((lead.account.accountName || '').substring(0, 20).padEnd(20))} ${chalk.yellow((lead.symbol.value || '').padEnd(10))} x${lead.contracts}`) + chalk.cyan(' ║'));
|
|
1084
|
+
console.log(chalk.cyan(' ║') + chalk.white(` FOLLOWER: ${chalk.cyan((follower.account.accountName || '').substring(0, 20).padEnd(20))} ${chalk.yellow((follower.symbol.value || '').padEnd(10))} x${follower.contracts}`) + chalk.cyan(' ║'));
|
|
1085
|
+
console.log(chalk.cyan(' ╠════════════════════════════════════════════════════════════════════╣'));
|
|
1086
|
+
console.log(chalk.cyan(' ║') + chalk.white(` Target: ${chalk.green(('$' + dailyTarget.toFixed(2)).padEnd(10))} Risk: ${chalk.red(('$' + maxRisk.toFixed(2)).padEnd(10))} P&L: ${pnlColor(pnlStr.padEnd(12))}`) + chalk.cyan(' ║'));
|
|
1087
|
+
console.log(chalk.cyan(' ║') + chalk.white(` Lead Trades: ${chalk.cyan(stats.leadTrades.toString().padEnd(4))} Copied: ${chalk.green(stats.copiedTrades.toString().padEnd(4))} Errors: ${chalk.red(stats.errors.toString().padEnd(4))}`) + chalk.cyan(' ║'));
|
|
1088
|
+
console.log(chalk.cyan(' ╠════════════════════════════════════════════════════════════════════╣'));
|
|
1089
|
+
console.log(chalk.cyan(' ║') + chalk.white(' Activity Log ') + chalk.yellow('Press X to stop') + chalk.cyan(' ║'));
|
|
1090
|
+
console.log(chalk.cyan(' ╠════════════════════════════════════════════════════════════════════╣'));
|
|
1062
1091
|
|
|
1063
1092
|
// Logs
|
|
1064
|
-
console.log(chalk.white.bold(' Activity Log'));
|
|
1065
|
-
console.log(chalk.gray(getSeparator()));
|
|
1066
|
-
|
|
1067
1093
|
if (logs.length === 0) {
|
|
1068
|
-
console.log(chalk.gray(' Monitoring lead account for trades...'));
|
|
1094
|
+
console.log(chalk.cyan(' ║') + chalk.gray(' Monitoring lead account for trades...'.padEnd(68)) + chalk.cyan('║'));
|
|
1069
1095
|
} else {
|
|
1070
|
-
const typeColors = {
|
|
1071
|
-
info: chalk.cyan,
|
|
1072
|
-
success: chalk.green,
|
|
1073
|
-
trade: chalk.green.bold,
|
|
1074
|
-
copy: chalk.yellow.bold,
|
|
1075
|
-
error: chalk.red,
|
|
1076
|
-
warning: chalk.yellow
|
|
1077
|
-
};
|
|
1078
|
-
|
|
1079
1096
|
logs.forEach(log => {
|
|
1080
1097
|
const color = typeColors[log.type] || chalk.white;
|
|
1081
|
-
const icon = log.type
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
console.log(chalk.gray(` [${log.timestamp}]`) + ' ' + color(`${icon} ${log.message}`));
|
|
1098
|
+
const icon = getIcon(log.type);
|
|
1099
|
+
const logLine = `[${log.timestamp}] ${icon} ${log.message}`;
|
|
1100
|
+
const truncated = logLine.length > 66 ? logLine.substring(0, 63) + '...' : logLine;
|
|
1101
|
+
console.log(chalk.cyan(' ║') + ' ' + color(truncated.padEnd(67)) + chalk.cyan('║'));
|
|
1086
1102
|
});
|
|
1087
1103
|
}
|
|
1088
1104
|
|
|
1089
|
-
console.log(chalk.
|
|
1090
|
-
console.log();
|
|
1091
|
-
console.log(chalk.yellow(' Press X to stop copy trading...'));
|
|
1092
|
-
console.log();
|
|
1105
|
+
console.log(chalk.cyan(' ╚════════════════════════════════════════════════════════════════════╝'));
|
|
1093
1106
|
};
|
|
1094
1107
|
|
|
1095
1108
|
addLog('info', 'Copy trading initialized');
|
|
@@ -30,6 +30,8 @@ class HQXServerService {
|
|
|
30
30
|
this.listeners = new Map();
|
|
31
31
|
this.heartbeatInterval = null;
|
|
32
32
|
this.messageQueue = [];
|
|
33
|
+
this.latency = 0;
|
|
34
|
+
this.lastPingTime = 0;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
/**
|
|
@@ -200,6 +202,14 @@ class HQXServerService {
|
|
|
200
202
|
* Handle incoming messages
|
|
201
203
|
*/
|
|
202
204
|
_handleMessage(message) {
|
|
205
|
+
// Calculate latency from server timestamp
|
|
206
|
+
if (message.timestamp) {
|
|
207
|
+
this.latency = Date.now() - message.timestamp;
|
|
208
|
+
if (this.latency < 0) this.latency = 0; // Handle clock skew
|
|
209
|
+
if (this.latency > 5000) this.latency = 0; // Ignore unrealistic values
|
|
210
|
+
this._emit('latency', { latency: this.latency });
|
|
211
|
+
}
|
|
212
|
+
|
|
203
213
|
switch (message.type) {
|
|
204
214
|
case 'signal':
|
|
205
215
|
this._emit('signal', message.data);
|
|
@@ -217,12 +227,23 @@ class HQXServerService {
|
|
|
217
227
|
this._emit('error', message.data);
|
|
218
228
|
break;
|
|
219
229
|
case 'pong':
|
|
220
|
-
//
|
|
230
|
+
// Calculate ping latency
|
|
231
|
+
if (this.lastPingTime > 0) {
|
|
232
|
+
this.latency = Date.now() - this.lastPingTime;
|
|
233
|
+
this._emit('latency', { latency: this.latency });
|
|
234
|
+
}
|
|
221
235
|
break;
|
|
222
236
|
default:
|
|
223
237
|
this._emit('message', message);
|
|
224
238
|
}
|
|
225
239
|
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Get current latency
|
|
243
|
+
*/
|
|
244
|
+
getLatency() {
|
|
245
|
+
return this.latency;
|
|
246
|
+
}
|
|
226
247
|
|
|
227
248
|
/**
|
|
228
249
|
* Send message to server
|
|
@@ -303,9 +324,10 @@ class HQXServerService {
|
|
|
303
324
|
_startHeartbeat() {
|
|
304
325
|
this.heartbeatInterval = setInterval(() => {
|
|
305
326
|
if (this.connected) {
|
|
306
|
-
this.
|
|
327
|
+
this.lastPingTime = Date.now();
|
|
328
|
+
this.send('ping', { timestamp: this.lastPingTime });
|
|
307
329
|
}
|
|
308
|
-
},
|
|
330
|
+
}, 5000); // Ping every 5 seconds for more accurate latency
|
|
309
331
|
}
|
|
310
332
|
|
|
311
333
|
_stopHeartbeat() {
|