hedgequantx 1.2.63 → 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 +122 -92
- 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,
|
|
@@ -342,11 +342,11 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
|
|
|
342
342
|
}
|
|
343
343
|
};
|
|
344
344
|
|
|
345
|
-
// Add log (newest
|
|
345
|
+
// Add log (oldest first, newest at bottom)
|
|
346
346
|
const addLog = (type, message) => {
|
|
347
347
|
const timestamp = new Date().toLocaleTimeString();
|
|
348
|
-
logs.
|
|
349
|
-
if (logs.length > MAX_LOGS) logs.
|
|
348
|
+
logs.push({ timestamp, type, message }); // Add at end
|
|
349
|
+
if (logs.length > MAX_LOGS) logs.shift(); // Remove oldest from top
|
|
350
350
|
};
|
|
351
351
|
|
|
352
352
|
// Print log and refresh display
|
|
@@ -381,33 +381,52 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
|
|
|
381
381
|
console.clear();
|
|
382
382
|
const marketStatus = checkMarketStatus();
|
|
383
383
|
|
|
384
|
-
//
|
|
384
|
+
// Logo
|
|
385
|
+
const logo = [
|
|
386
|
+
'██╗ ██╗ ██████╗ ██╗ ██╗',
|
|
387
|
+
'██║ ██║██╔═══██╗╚██╗██╔╝',
|
|
388
|
+
'███████║██║ ██║ ╚███╔╝ ',
|
|
389
|
+
'██╔══██║██║▄▄ ██║ ██╔██╗ ',
|
|
390
|
+
'██║ ██║╚██████╔╝██╔╝ ██╗',
|
|
391
|
+
'╚═╝ ╚═╝ ╚══▀▀═╝ ╚═╝ ╚═╝'
|
|
392
|
+
];
|
|
393
|
+
|
|
394
|
+
console.log();
|
|
395
|
+
logo.forEach(line => {
|
|
396
|
+
console.log(chalk.cyan(' ' + line));
|
|
397
|
+
});
|
|
398
|
+
console.log(chalk.gray(' Ultra-Scalping Algorithm'));
|
|
385
399
|
console.log();
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
console.log(chalk.
|
|
389
|
-
console.log(chalk.white(`
|
|
390
|
-
|
|
400
|
+
|
|
401
|
+
// Info Box
|
|
402
|
+
console.log(chalk.cyan(' ╔════════════════════════════════════════════════════════════════════╗'));
|
|
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
|
+
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('║'));
|
|
391
407
|
|
|
392
408
|
// Stats line
|
|
393
409
|
const pnlColor = stats.pnl >= 0 ? chalk.green : chalk.red;
|
|
394
|
-
const pnlStr = stats.pnl >= 0 ? '+$'
|
|
395
|
-
console.log(chalk.white(`
|
|
396
|
-
|
|
397
|
-
console.log(chalk.
|
|
410
|
+
const pnlStr = (stats.pnl >= 0 ? '+$' : '-$') + Math.abs(stats.pnl).toFixed(2);
|
|
411
|
+
console.log(chalk.cyan(' ║') + chalk.white(` P&L: ${pnlColor(pnlStr.padEnd(12))} Trades: ${chalk.cyan(stats.trades.toString().padEnd(4))} W: ${chalk.green(stats.wins.toString().padEnd(3))} L: ${chalk.red(stats.losses.toString().padEnd(3))} `) + chalk.cyan('║'));
|
|
412
|
+
console.log(chalk.cyan(' ╠════════════════════════════════════════════════════════════════════╣'));
|
|
413
|
+
console.log(chalk.cyan(' ║') + chalk.white(' Activity Log ') + chalk.yellow('Press X to stop') + chalk.cyan(' ║'));
|
|
414
|
+
console.log(chalk.cyan(' ╠════════════════════════════════════════════════════════════════════╣'));
|
|
398
415
|
|
|
399
416
|
// Logs (newest first - already in correct order)
|
|
400
417
|
if (logs.length === 0) {
|
|
401
|
-
console.log(chalk.gray(' Waiting for activity...'));
|
|
418
|
+
console.log(chalk.cyan(' ║') + chalk.gray(' Waiting for activity...'.padEnd(68)) + chalk.cyan('║'));
|
|
402
419
|
} else {
|
|
403
420
|
logs.forEach(log => {
|
|
404
421
|
const color = typeColors[log.type] || chalk.white;
|
|
405
422
|
const icon = getIcon(log.type);
|
|
406
|
-
|
|
423
|
+
const logLine = `[${log.timestamp}] ${icon} ${log.message}`;
|
|
424
|
+
const truncated = logLine.length > 66 ? logLine.substring(0, 63) + '...' : logLine;
|
|
425
|
+
console.log(chalk.cyan(' ║') + ' ' + color(truncated.padEnd(67)) + chalk.cyan('║'));
|
|
407
426
|
});
|
|
408
427
|
}
|
|
409
428
|
|
|
410
|
-
console.log(chalk.
|
|
429
|
+
console.log(chalk.cyan(' ╚════════════════════════════════════════════════════════════════════╝'));
|
|
411
430
|
};
|
|
412
431
|
|
|
413
432
|
// Connect to HQX Server
|
|
@@ -537,32 +556,45 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
|
|
|
537
556
|
|
|
538
557
|
// Wait for X key OR auto-stop (target/risk reached)
|
|
539
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
|
+
|
|
540
574
|
// Check for auto-stop every 500ms
|
|
541
575
|
const checkInterval = setInterval(() => {
|
|
542
576
|
if (!algoRunning || stopReason) {
|
|
543
|
-
|
|
544
|
-
if (process.stdin.isTTY && process.stdin.isRaw) {
|
|
545
|
-
process.stdin.setRawMode(false);
|
|
546
|
-
}
|
|
547
|
-
resolve();
|
|
577
|
+
cleanup();
|
|
548
578
|
}
|
|
549
579
|
}, 500);
|
|
550
580
|
|
|
551
|
-
//
|
|
581
|
+
// Listen for X key
|
|
552
582
|
if (process.stdin.isTTY) {
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
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
|
+
}
|
|
566
598
|
}
|
|
567
599
|
});
|
|
568
600
|
|
|
@@ -997,6 +1029,26 @@ const launchCopyTrading = async (config) => {
|
|
|
997
1029
|
pnl: 0
|
|
998
1030
|
};
|
|
999
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
|
+
|
|
1000
1052
|
const addLog = (type, message) => {
|
|
1001
1053
|
const timestamp = new Date().toLocaleTimeString();
|
|
1002
1054
|
logs.push({ timestamp, type, message });
|
|
@@ -1005,74 +1057,52 @@ const launchCopyTrading = async (config) => {
|
|
|
1005
1057
|
|
|
1006
1058
|
const displayUI = () => {
|
|
1007
1059
|
console.clear();
|
|
1060
|
+
|
|
1061
|
+
// Logo
|
|
1062
|
+
const logo = [
|
|
1063
|
+
'██╗ ██╗ ██████╗ ██╗ ██╗',
|
|
1064
|
+
'██║ ██║██╔═══██╗╚██╗██╔╝',
|
|
1065
|
+
'███████║██║ ██║ ╚███╔╝ ',
|
|
1066
|
+
'██╔══██║██║▄▄ ██║ ██╔██╗ ',
|
|
1067
|
+
'██║ ██║╚██████╔╝██╔╝ ██╗',
|
|
1068
|
+
'╚═╝ ╚═╝ ╚══▀▀═╝ ╚═╝ ╚═╝'
|
|
1069
|
+
];
|
|
1070
|
+
|
|
1008
1071
|
console.log();
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
console.log(chalk.
|
|
1013
|
-
console.log(chalk.gray(getSeparator()));
|
|
1014
|
-
console.log();
|
|
1015
|
-
|
|
1016
|
-
// Risk Management
|
|
1017
|
-
console.log(chalk.white.bold(' RISK MANAGEMENT'));
|
|
1018
|
-
const targetProgress = Math.min(100, (stats.pnl / dailyTarget) * 100);
|
|
1019
|
-
const riskProgress = Math.min(100, (Math.abs(Math.min(0, stats.pnl)) / maxRisk) * 100);
|
|
1020
|
-
console.log(chalk.white(` Target: ${chalk.green('$' + dailyTarget.toFixed(2))} | Progress: ${targetProgress >= 100 ? chalk.green.bold(targetProgress.toFixed(1) + '%') : chalk.yellow(targetProgress.toFixed(1) + '%')}`));
|
|
1021
|
-
console.log(chalk.white(` Risk: ${chalk.red('$' + maxRisk.toFixed(2))} | Used: ${riskProgress >= 100 ? chalk.red.bold(riskProgress.toFixed(1) + '%') : chalk.cyan(riskProgress.toFixed(1) + '%')}`));
|
|
1022
|
-
console.log(chalk.white(` P&L: ${stats.pnl >= 0 ? chalk.green('+$' + stats.pnl.toFixed(2)) : chalk.red('-$' + Math.abs(stats.pnl).toFixed(2))}`));
|
|
1023
|
-
console.log();
|
|
1024
|
-
|
|
1025
|
-
// Lead info
|
|
1026
|
-
console.log(chalk.white.bold(' LEAD'));
|
|
1027
|
-
console.log(chalk.white(` ${chalk.cyan(lead.account.accountName)} @ ${chalk.magenta(lead.account.propfirm)}`));
|
|
1028
|
-
console.log(chalk.white(` ${chalk.cyan(lead.symbol.value)} x ${lead.contracts}`));
|
|
1029
|
-
console.log();
|
|
1030
|
-
|
|
1031
|
-
// Follower info
|
|
1032
|
-
console.log(chalk.white.bold(' FOLLOWER'));
|
|
1033
|
-
console.log(chalk.white(` ${chalk.cyan(follower.account.accountName)} @ ${chalk.magenta(follower.account.propfirm)}`));
|
|
1034
|
-
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'));
|
|
1035
1076
|
console.log();
|
|
1036
1077
|
|
|
1037
|
-
//
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
);
|
|
1044
|
-
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(' ╠════════════════════════════════════════════════════════════════════╣'));
|
|
1045
1091
|
|
|
1046
1092
|
// Logs
|
|
1047
|
-
console.log(chalk.white.bold(' Activity Log'));
|
|
1048
|
-
console.log(chalk.gray(getSeparator()));
|
|
1049
|
-
|
|
1050
1093
|
if (logs.length === 0) {
|
|
1051
|
-
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('║'));
|
|
1052
1095
|
} else {
|
|
1053
|
-
const typeColors = {
|
|
1054
|
-
info: chalk.cyan,
|
|
1055
|
-
success: chalk.green,
|
|
1056
|
-
trade: chalk.green.bold,
|
|
1057
|
-
copy: chalk.yellow.bold,
|
|
1058
|
-
error: chalk.red,
|
|
1059
|
-
warning: chalk.yellow
|
|
1060
|
-
};
|
|
1061
|
-
|
|
1062
1096
|
logs.forEach(log => {
|
|
1063
1097
|
const color = typeColors[log.type] || chalk.white;
|
|
1064
|
-
const icon = log.type
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
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('║'));
|
|
1069
1102
|
});
|
|
1070
1103
|
}
|
|
1071
1104
|
|
|
1072
|
-
console.log(chalk.
|
|
1073
|
-
console.log();
|
|
1074
|
-
console.log(chalk.yellow(' Press X to stop copy trading...'));
|
|
1075
|
-
console.log();
|
|
1105
|
+
console.log(chalk.cyan(' ╚════════════════════════════════════════════════════════════════════╝'));
|
|
1076
1106
|
};
|
|
1077
1107
|
|
|
1078
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() {
|