hedgequantx 1.2.57 → 1.2.58
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 +161 -19
package/package.json
CHANGED
package/src/pages/algo.js
CHANGED
|
@@ -215,15 +215,57 @@ const selectSymbolMenu = async (service, account) => {
|
|
|
215
215
|
filter: (input) => parseInt(input)
|
|
216
216
|
}
|
|
217
217
|
]);
|
|
218
|
+
|
|
219
|
+
// Risk Management
|
|
220
|
+
console.log();
|
|
221
|
+
console.log(chalk.cyan.bold(' Risk Management'));
|
|
222
|
+
console.log(chalk.gray(' Set your daily target and maximum risk to auto-stop the algo.'));
|
|
223
|
+
console.log();
|
|
224
|
+
|
|
225
|
+
const { dailyTarget } = await inquirer.prompt([
|
|
226
|
+
{
|
|
227
|
+
type: 'input',
|
|
228
|
+
name: 'dailyTarget',
|
|
229
|
+
message: chalk.white.bold('Daily Target ($):'),
|
|
230
|
+
default: '500',
|
|
231
|
+
validate: (input) => {
|
|
232
|
+
const num = parseFloat(input);
|
|
233
|
+
if (isNaN(num) || num <= 0) {
|
|
234
|
+
return 'Please enter a valid amount greater than 0';
|
|
235
|
+
}
|
|
236
|
+
return true;
|
|
237
|
+
},
|
|
238
|
+
filter: (input) => parseFloat(input)
|
|
239
|
+
}
|
|
240
|
+
]);
|
|
241
|
+
|
|
242
|
+
const { maxRisk } = await inquirer.prompt([
|
|
243
|
+
{
|
|
244
|
+
type: 'input',
|
|
245
|
+
name: 'maxRisk',
|
|
246
|
+
message: chalk.white.bold('Max Risk ($):'),
|
|
247
|
+
default: '200',
|
|
248
|
+
validate: (input) => {
|
|
249
|
+
const num = parseFloat(input);
|
|
250
|
+
if (isNaN(num) || num <= 0) {
|
|
251
|
+
return 'Please enter a valid amount greater than 0';
|
|
252
|
+
}
|
|
253
|
+
return true;
|
|
254
|
+
},
|
|
255
|
+
filter: (input) => parseFloat(input)
|
|
256
|
+
}
|
|
257
|
+
]);
|
|
218
258
|
|
|
219
259
|
// Confirmation
|
|
220
260
|
console.log();
|
|
221
261
|
console.log(chalk.gray(getSeparator()));
|
|
222
262
|
console.log(chalk.white.bold(' Algo Configuration:'));
|
|
223
263
|
console.log(chalk.gray(getSeparator()));
|
|
224
|
-
console.log(chalk.white(` Account:
|
|
225
|
-
console.log(chalk.white(` Symbol:
|
|
226
|
-
console.log(chalk.white(` Contracts:
|
|
264
|
+
console.log(chalk.white(` Account: ${chalk.cyan(accountName)}`));
|
|
265
|
+
console.log(chalk.white(` Symbol: ${chalk.cyan(contract.name || selectedSymbol.value)}`));
|
|
266
|
+
console.log(chalk.white(` Contracts: ${chalk.cyan(contracts)}`));
|
|
267
|
+
console.log(chalk.white(` Daily Target: ${chalk.green('$' + dailyTarget.toFixed(2))}`));
|
|
268
|
+
console.log(chalk.white(` Max Risk: ${chalk.red('$' + maxRisk.toFixed(2))}`));
|
|
227
269
|
console.log(chalk.gray(getSeparator()));
|
|
228
270
|
console.log();
|
|
229
271
|
|
|
@@ -244,13 +286,13 @@ const selectSymbolMenu = async (service, account) => {
|
|
|
244
286
|
return;
|
|
245
287
|
}
|
|
246
288
|
|
|
247
|
-
await launchAlgo(service, account, contract, contracts);
|
|
289
|
+
await launchAlgo(service, account, contract, contracts, dailyTarget, maxRisk);
|
|
248
290
|
};
|
|
249
291
|
|
|
250
292
|
/**
|
|
251
293
|
* Launch Algo with HQX Server Connection
|
|
252
294
|
*/
|
|
253
|
-
const launchAlgo = async (service, account, contract, numContracts) => {
|
|
295
|
+
const launchAlgo = async (service, account, contract, numContracts, dailyTarget, maxRisk) => {
|
|
254
296
|
const accountName = account.accountName || account.name || 'Account #' + account.accountId;
|
|
255
297
|
const symbolName = contract.name || contract.symbol || contract.id;
|
|
256
298
|
const symbol = contract.symbol || contract.id;
|
|
@@ -263,10 +305,11 @@ const launchAlgo = async (service, account, contract, numContracts) => {
|
|
|
263
305
|
const hqxServer = new HQXServerService();
|
|
264
306
|
let hqxConnected = false;
|
|
265
307
|
let algoRunning = false;
|
|
308
|
+
let stopReason = null;
|
|
266
309
|
|
|
267
310
|
// Activity logs
|
|
268
311
|
const logs = [];
|
|
269
|
-
const MAX_LOGS =
|
|
312
|
+
const MAX_LOGS = 10;
|
|
270
313
|
|
|
271
314
|
// Stats
|
|
272
315
|
let stats = {
|
|
@@ -300,6 +343,15 @@ const launchAlgo = async (service, account, contract, numContracts) => {
|
|
|
300
343
|
console.log(chalk.white(` Contracts: ${chalk.cyan(numContracts)}`));
|
|
301
344
|
console.log(chalk.white(` Mode: ${hqxConnected ? chalk.green('LIVE') : chalk.yellow('OFFLINE')}`));
|
|
302
345
|
console.log(chalk.gray(getSeparator()));
|
|
346
|
+
|
|
347
|
+
// Risk Management
|
|
348
|
+
console.log();
|
|
349
|
+
const targetProgress = Math.min(100, Math.max(0, (stats.pnl / dailyTarget) * 100));
|
|
350
|
+
const riskProgress = Math.min(100, (Math.abs(Math.min(0, stats.pnl)) / maxRisk) * 100);
|
|
351
|
+
console.log(chalk.white(' Target: ') + chalk.green('$' + dailyTarget.toFixed(2)) +
|
|
352
|
+
chalk.gray(' | Progress: ') + (targetProgress >= 100 ? chalk.green.bold(targetProgress.toFixed(1) + '%') : chalk.yellow(targetProgress.toFixed(1) + '%')));
|
|
353
|
+
console.log(chalk.white(' Risk: ') + chalk.red('$' + maxRisk.toFixed(2)) +
|
|
354
|
+
chalk.gray(' | Used: ') + (riskProgress >= 100 ? chalk.red.bold(riskProgress.toFixed(1) + '%') : chalk.cyan(riskProgress.toFixed(1) + '%')));
|
|
303
355
|
|
|
304
356
|
// Stats bar
|
|
305
357
|
console.log();
|
|
@@ -308,7 +360,7 @@ const launchAlgo = async (service, account, contract, numContracts) => {
|
|
|
308
360
|
chalk.gray(' | Wins: ') + chalk.green(stats.wins) +
|
|
309
361
|
chalk.gray(' | Losses: ') + chalk.red(stats.losses) +
|
|
310
362
|
chalk.gray(' | Win Rate: ') + chalk.yellow(stats.winRate + '%') +
|
|
311
|
-
chalk.gray(' | P&L: ') + (stats.pnl >= 0 ? chalk.green('
|
|
363
|
+
chalk.gray(' | P&L: ') + (stats.pnl >= 0 ? chalk.green('+$' + stats.pnl.toFixed(2)) : chalk.red('-$' + Math.abs(stats.pnl).toFixed(2)))
|
|
312
364
|
);
|
|
313
365
|
console.log();
|
|
314
366
|
|
|
@@ -409,6 +461,27 @@ const launchAlgo = async (service, account, contract, numContracts) => {
|
|
|
409
461
|
addLog('trade', `Closed -$${Math.abs(data.pnl).toFixed(2)} (${data.reason || 'stop_loss'})`);
|
|
410
462
|
}
|
|
411
463
|
stats.winRate = stats.trades > 0 ? ((stats.wins / stats.trades) * 100).toFixed(1) : '0.0';
|
|
464
|
+
|
|
465
|
+
// Check daily target
|
|
466
|
+
if (stats.pnl >= dailyTarget) {
|
|
467
|
+
stopReason = 'target';
|
|
468
|
+
addLog('success', `Daily target reached! +$${stats.pnl.toFixed(2)}`);
|
|
469
|
+
algoRunning = false;
|
|
470
|
+
if (hqxConnected) {
|
|
471
|
+
hqxServer.stopAlgo();
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Check max risk
|
|
476
|
+
if (stats.pnl <= -maxRisk) {
|
|
477
|
+
stopReason = 'risk';
|
|
478
|
+
addLog('error', `Max risk reached! -$${Math.abs(stats.pnl).toFixed(2)}`);
|
|
479
|
+
algoRunning = false;
|
|
480
|
+
if (hqxConnected) {
|
|
481
|
+
hqxServer.stopAlgo();
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
412
485
|
displayUI();
|
|
413
486
|
});
|
|
414
487
|
|
|
@@ -431,13 +504,14 @@ const launchAlgo = async (service, account, contract, numContracts) => {
|
|
|
431
504
|
// Start algo
|
|
432
505
|
if (hqxConnected) {
|
|
433
506
|
addLog('info', 'Starting HQX Ultra-Scalping...');
|
|
507
|
+
addLog('info', `Target: $${dailyTarget.toFixed(2)} | Risk: $${maxRisk.toFixed(2)}`);
|
|
434
508
|
hqxServer.startAlgo({
|
|
435
509
|
accountId: account.accountId,
|
|
436
510
|
contractId: contract.id || contract.contractId,
|
|
437
511
|
symbol: symbol,
|
|
438
512
|
contracts: numContracts,
|
|
439
|
-
dailyTarget:
|
|
440
|
-
maxRisk:
|
|
513
|
+
dailyTarget: dailyTarget,
|
|
514
|
+
maxRisk: maxRisk,
|
|
441
515
|
propfirm: account.propfirm || 'projectx',
|
|
442
516
|
propfirmToken: service.getToken ? service.getToken() : null
|
|
443
517
|
});
|
|
@@ -450,14 +524,44 @@ const launchAlgo = async (service, account, contract, numContracts) => {
|
|
|
450
524
|
|
|
451
525
|
displayUI();
|
|
452
526
|
|
|
453
|
-
// Wait for X key
|
|
454
|
-
await
|
|
527
|
+
// Wait for X key OR auto-stop (target/risk reached)
|
|
528
|
+
await new Promise((resolve) => {
|
|
529
|
+
// Check for auto-stop every 500ms
|
|
530
|
+
const checkInterval = setInterval(() => {
|
|
531
|
+
if (!algoRunning || stopReason) {
|
|
532
|
+
clearInterval(checkInterval);
|
|
533
|
+
if (process.stdin.isTTY && process.stdin.isRaw) {
|
|
534
|
+
process.stdin.setRawMode(false);
|
|
535
|
+
}
|
|
536
|
+
resolve();
|
|
537
|
+
}
|
|
538
|
+
}, 500);
|
|
539
|
+
|
|
540
|
+
// Also listen for X key
|
|
541
|
+
if (process.stdin.isTTY) {
|
|
542
|
+
readline.emitKeypressEvents(process.stdin);
|
|
543
|
+
process.stdin.setRawMode(true);
|
|
544
|
+
|
|
545
|
+
const onKeypress = (str, key) => {
|
|
546
|
+
if (key && (key.name === 'x' || key.name === 'X' || (key.ctrl && key.name === 'c'))) {
|
|
547
|
+
clearInterval(checkInterval);
|
|
548
|
+
process.stdin.setRawMode(false);
|
|
549
|
+
process.stdin.removeListener('keypress', onKeypress);
|
|
550
|
+
resolve();
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
process.stdin.on('keypress', onKeypress);
|
|
555
|
+
}
|
|
556
|
+
});
|
|
455
557
|
|
|
456
558
|
// Stop algo
|
|
457
|
-
|
|
458
|
-
|
|
559
|
+
if (!stopReason) {
|
|
560
|
+
addLog('warning', 'Stopping algo...');
|
|
561
|
+
displayUI();
|
|
562
|
+
}
|
|
459
563
|
|
|
460
|
-
if (hqxConnected) {
|
|
564
|
+
if (hqxConnected && algoRunning) {
|
|
461
565
|
hqxServer.stopAlgo();
|
|
462
566
|
}
|
|
463
567
|
|
|
@@ -465,18 +569,27 @@ const launchAlgo = async (service, account, contract, numContracts) => {
|
|
|
465
569
|
algoRunning = false;
|
|
466
570
|
|
|
467
571
|
console.log();
|
|
468
|
-
|
|
572
|
+
if (stopReason === 'target') {
|
|
573
|
+
console.log(chalk.green.bold(' [OK] Daily target reached! Algo stopped.'));
|
|
574
|
+
} else if (stopReason === 'risk') {
|
|
575
|
+
console.log(chalk.red.bold(' [X] Max risk reached! Algo stopped.'));
|
|
576
|
+
} else {
|
|
577
|
+
console.log(chalk.yellow(' [OK] Algo stopped by user'));
|
|
578
|
+
}
|
|
469
579
|
console.log();
|
|
470
580
|
|
|
471
581
|
// Final stats
|
|
472
582
|
console.log(chalk.gray(getSeparator()));
|
|
473
583
|
console.log(chalk.white.bold(' Session Summary'));
|
|
474
584
|
console.log(chalk.gray(getSeparator()));
|
|
585
|
+
console.log(chalk.white(` Daily Target: ${chalk.green('$' + dailyTarget.toFixed(2))}`));
|
|
586
|
+
console.log(chalk.white(` Max Risk: ${chalk.red('$' + maxRisk.toFixed(2))}`));
|
|
587
|
+
console.log(chalk.white(` Final P&L: ${stats.pnl >= 0 ? chalk.green('+$' + stats.pnl.toFixed(2)) : chalk.red('-$' + Math.abs(stats.pnl).toFixed(2))}`));
|
|
588
|
+
console.log(chalk.gray(getSeparator()));
|
|
475
589
|
console.log(chalk.white(` Total Trades: ${chalk.cyan(stats.trades)}`));
|
|
476
590
|
console.log(chalk.white(` Wins: ${chalk.green(stats.wins)}`));
|
|
477
591
|
console.log(chalk.white(` Losses: ${chalk.red(stats.losses)}`));
|
|
478
592
|
console.log(chalk.white(` Win Rate: ${chalk.yellow(stats.winRate + '%')}`));
|
|
479
|
-
console.log(chalk.white(` Total P&L: ${stats.pnl >= 0 ? chalk.green('+$' + stats.pnl.toFixed(2)) : chalk.red('-$' + Math.abs(stats.pnl).toFixed(2))}`));
|
|
480
593
|
console.log(chalk.gray(getSeparator()));
|
|
481
594
|
console.log();
|
|
482
595
|
|
|
@@ -1032,11 +1145,40 @@ const launchCopyTrading = async (config) => {
|
|
|
1032
1145
|
}
|
|
1033
1146
|
}, 2000); // Check every 2 seconds
|
|
1034
1147
|
|
|
1035
|
-
// Wait for stop
|
|
1036
|
-
await
|
|
1148
|
+
// Wait for X key OR auto-stop (target/risk reached)
|
|
1149
|
+
await new Promise((resolve) => {
|
|
1150
|
+
// Check for auto-stop every 500ms
|
|
1151
|
+
const checkInterval = setInterval(() => {
|
|
1152
|
+
if (!isRunning || stopReason) {
|
|
1153
|
+
clearInterval(checkInterval);
|
|
1154
|
+
clearInterval(monitorInterval);
|
|
1155
|
+
if (process.stdin.isTTY && process.stdin.isRaw) {
|
|
1156
|
+
process.stdin.setRawMode(false);
|
|
1157
|
+
}
|
|
1158
|
+
resolve();
|
|
1159
|
+
}
|
|
1160
|
+
}, 500);
|
|
1161
|
+
|
|
1162
|
+
// Also listen for X key
|
|
1163
|
+
if (process.stdin.isTTY) {
|
|
1164
|
+
readline.emitKeypressEvents(process.stdin);
|
|
1165
|
+
process.stdin.setRawMode(true);
|
|
1166
|
+
|
|
1167
|
+
const onKeypress = (str, key) => {
|
|
1168
|
+
if (key && (key.name === 'x' || key.name === 'X' || (key.ctrl && key.name === 'c'))) {
|
|
1169
|
+
clearInterval(checkInterval);
|
|
1170
|
+
clearInterval(monitorInterval);
|
|
1171
|
+
process.stdin.setRawMode(false);
|
|
1172
|
+
process.stdin.removeListener('keypress', onKeypress);
|
|
1173
|
+
resolve();
|
|
1174
|
+
}
|
|
1175
|
+
};
|
|
1176
|
+
|
|
1177
|
+
process.stdin.on('keypress', onKeypress);
|
|
1178
|
+
}
|
|
1179
|
+
});
|
|
1037
1180
|
|
|
1038
1181
|
// Cleanup
|
|
1039
|
-
clearInterval(monitorInterval);
|
|
1040
1182
|
isRunning = false;
|
|
1041
1183
|
|
|
1042
1184
|
console.log();
|