hedgequantx 1.2.65 → 1.2.67
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 +95 -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;
|
|
@@ -463,6 +465,11 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
|
|
|
463
465
|
}
|
|
464
466
|
|
|
465
467
|
// Setup event handlers - logs scroll down naturally
|
|
468
|
+
hqxServer.on('latency', (data) => {
|
|
469
|
+
latency = data.latency || 0;
|
|
470
|
+
displayUI(); // Refresh UI with new latency
|
|
471
|
+
});
|
|
472
|
+
|
|
466
473
|
hqxServer.on('log', (data) => {
|
|
467
474
|
printLog(data.type || 'info', data.message);
|
|
468
475
|
});
|
|
@@ -554,32 +561,45 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
|
|
|
554
561
|
|
|
555
562
|
// Wait for X key OR auto-stop (target/risk reached)
|
|
556
563
|
await new Promise((resolve) => {
|
|
564
|
+
let resolved = false;
|
|
565
|
+
|
|
566
|
+
const cleanup = () => {
|
|
567
|
+
if (resolved) return;
|
|
568
|
+
resolved = true;
|
|
569
|
+
clearInterval(checkInterval);
|
|
570
|
+
if (process.stdin.isTTY) {
|
|
571
|
+
try {
|
|
572
|
+
process.stdin.setRawMode(false);
|
|
573
|
+
process.stdin.removeAllListeners('keypress');
|
|
574
|
+
} catch (e) {}
|
|
575
|
+
}
|
|
576
|
+
resolve();
|
|
577
|
+
};
|
|
578
|
+
|
|
557
579
|
// Check for auto-stop every 500ms
|
|
558
580
|
const checkInterval = setInterval(() => {
|
|
559
581
|
if (!algoRunning || stopReason) {
|
|
560
|
-
|
|
561
|
-
if (process.stdin.isTTY && process.stdin.isRaw) {
|
|
562
|
-
process.stdin.setRawMode(false);
|
|
563
|
-
}
|
|
564
|
-
resolve();
|
|
582
|
+
cleanup();
|
|
565
583
|
}
|
|
566
584
|
}, 500);
|
|
567
585
|
|
|
568
|
-
//
|
|
586
|
+
// Listen for X key
|
|
569
587
|
if (process.stdin.isTTY) {
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
588
|
+
try {
|
|
589
|
+
readline.emitKeypressEvents(process.stdin);
|
|
590
|
+
process.stdin.setRawMode(true);
|
|
591
|
+
process.stdin.resume();
|
|
592
|
+
|
|
593
|
+
process.stdin.on('keypress', (str, key) => {
|
|
594
|
+
if (!key) return;
|
|
595
|
+
const keyName = key.name?.toLowerCase();
|
|
596
|
+
if (keyName === 'x' || (key.ctrl && keyName === 'c')) {
|
|
597
|
+
cleanup();
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
} catch (e) {
|
|
601
|
+
// Fallback: just wait for auto-stop
|
|
602
|
+
}
|
|
583
603
|
}
|
|
584
604
|
});
|
|
585
605
|
|
|
@@ -1014,6 +1034,26 @@ const launchCopyTrading = async (config) => {
|
|
|
1014
1034
|
pnl: 0
|
|
1015
1035
|
};
|
|
1016
1036
|
|
|
1037
|
+
// Log colors
|
|
1038
|
+
const typeColors = {
|
|
1039
|
+
info: chalk.cyan,
|
|
1040
|
+
success: chalk.green,
|
|
1041
|
+
trade: chalk.green.bold,
|
|
1042
|
+
copy: chalk.yellow.bold,
|
|
1043
|
+
error: chalk.red,
|
|
1044
|
+
warning: chalk.yellow
|
|
1045
|
+
};
|
|
1046
|
+
|
|
1047
|
+
const getIcon = (type) => {
|
|
1048
|
+
switch(type) {
|
|
1049
|
+
case 'trade': return '[>]';
|
|
1050
|
+
case 'copy': return '[+]';
|
|
1051
|
+
case 'error': return '[X]';
|
|
1052
|
+
case 'success': return '[OK]';
|
|
1053
|
+
default: return '[.]';
|
|
1054
|
+
}
|
|
1055
|
+
};
|
|
1056
|
+
|
|
1017
1057
|
const addLog = (type, message) => {
|
|
1018
1058
|
const timestamp = new Date().toLocaleTimeString();
|
|
1019
1059
|
logs.push({ timestamp, type, message });
|
|
@@ -1022,74 +1062,52 @@ const launchCopyTrading = async (config) => {
|
|
|
1022
1062
|
|
|
1023
1063
|
const displayUI = () => {
|
|
1024
1064
|
console.clear();
|
|
1065
|
+
|
|
1066
|
+
// Logo
|
|
1067
|
+
const logo = [
|
|
1068
|
+
'██╗ ██╗ ██████╗ ██╗ ██╗',
|
|
1069
|
+
'██║ ██║██╔═══██╗╚██╗██╔╝',
|
|
1070
|
+
'███████║██║ ██║ ╚███╔╝ ',
|
|
1071
|
+
'██╔══██║██║▄▄ ██║ ██╔██╗ ',
|
|
1072
|
+
'██║ ██║╚██████╔╝██╔╝ ██╗',
|
|
1073
|
+
'╚═╝ ╚═╝ ╚══▀▀═╝ ╚═╝ ╚═╝'
|
|
1074
|
+
];
|
|
1075
|
+
|
|
1025
1076
|
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}`));
|
|
1077
|
+
logo.forEach(line => {
|
|
1078
|
+
console.log(chalk.cyan(' ' + line));
|
|
1079
|
+
});
|
|
1080
|
+
console.log(chalk.gray(' Copy Trading System'));
|
|
1052
1081
|
console.log();
|
|
1053
1082
|
|
|
1054
|
-
//
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
);
|
|
1061
|
-
console.log(chalk.
|
|
1083
|
+
// Info Box
|
|
1084
|
+
const pnlColor = stats.pnl >= 0 ? chalk.green : chalk.red;
|
|
1085
|
+
const pnlStr = (stats.pnl >= 0 ? '+$' : '-$') + Math.abs(stats.pnl).toFixed(2);
|
|
1086
|
+
|
|
1087
|
+
console.log(chalk.cyan(' ╔════════════════════════════════════════════════════════════════════╗'));
|
|
1088
|
+
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(' ║'));
|
|
1089
|
+
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(' ║'));
|
|
1090
|
+
console.log(chalk.cyan(' ╠════════════════════════════════════════════════════════════════════╣'));
|
|
1091
|
+
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(' ║'));
|
|
1092
|
+
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(' ║'));
|
|
1093
|
+
console.log(chalk.cyan(' ╠════════════════════════════════════════════════════════════════════╣'));
|
|
1094
|
+
console.log(chalk.cyan(' ║') + chalk.white(' Activity Log ') + chalk.yellow('Press X to stop') + chalk.cyan(' ║'));
|
|
1095
|
+
console.log(chalk.cyan(' ╠════════════════════════════════════════════════════════════════════╣'));
|
|
1062
1096
|
|
|
1063
1097
|
// Logs
|
|
1064
|
-
console.log(chalk.white.bold(' Activity Log'));
|
|
1065
|
-
console.log(chalk.gray(getSeparator()));
|
|
1066
|
-
|
|
1067
1098
|
if (logs.length === 0) {
|
|
1068
|
-
console.log(chalk.gray(' Monitoring lead account for trades...'));
|
|
1099
|
+
console.log(chalk.cyan(' ║') + chalk.gray(' Monitoring lead account for trades...'.padEnd(68)) + chalk.cyan('║'));
|
|
1069
1100
|
} 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
1101
|
logs.forEach(log => {
|
|
1080
1102
|
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}`));
|
|
1103
|
+
const icon = getIcon(log.type);
|
|
1104
|
+
const logLine = `[${log.timestamp}] ${icon} ${log.message}`;
|
|
1105
|
+
const truncated = logLine.length > 66 ? logLine.substring(0, 63) + '...' : logLine;
|
|
1106
|
+
console.log(chalk.cyan(' ║') + ' ' + color(truncated.padEnd(67)) + chalk.cyan('║'));
|
|
1086
1107
|
});
|
|
1087
1108
|
}
|
|
1088
1109
|
|
|
1089
|
-
console.log(chalk.
|
|
1090
|
-
console.log();
|
|
1091
|
-
console.log(chalk.yellow(' Press X to stop copy trading...'));
|
|
1092
|
-
console.log();
|
|
1110
|
+
console.log(chalk.cyan(' ╚════════════════════════════════════════════════════════════════════╝'));
|
|
1093
1111
|
};
|
|
1094
1112
|
|
|
1095
1113
|
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() {
|