hedgequantx 1.2.56 → 1.2.57
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 +141 -13
package/package.json
CHANGED
package/src/pages/algo.js
CHANGED
|
@@ -580,8 +580,52 @@ const copyTradingMenu = async () => {
|
|
|
580
580
|
return;
|
|
581
581
|
}
|
|
582
582
|
|
|
583
|
-
// Step 1:
|
|
584
|
-
console.log(chalk.cyan.bold(' Step 1:
|
|
583
|
+
// Step 1: Risk Management Settings
|
|
584
|
+
console.log(chalk.cyan.bold(' Step 1: Risk Management'));
|
|
585
|
+
console.log(chalk.gray(' Set your daily target and maximum risk to auto-stop copy trading.'));
|
|
586
|
+
console.log();
|
|
587
|
+
|
|
588
|
+
const { dailyTarget } = await inquirer.prompt([
|
|
589
|
+
{
|
|
590
|
+
type: 'input',
|
|
591
|
+
name: 'dailyTarget',
|
|
592
|
+
message: chalk.white.bold('Daily Target ($):'),
|
|
593
|
+
default: '500',
|
|
594
|
+
validate: (input) => {
|
|
595
|
+
const num = parseFloat(input);
|
|
596
|
+
if (isNaN(num) || num <= 0) {
|
|
597
|
+
return 'Please enter a valid amount greater than 0';
|
|
598
|
+
}
|
|
599
|
+
return true;
|
|
600
|
+
},
|
|
601
|
+
filter: (input) => parseFloat(input)
|
|
602
|
+
}
|
|
603
|
+
]);
|
|
604
|
+
|
|
605
|
+
const { maxRisk } = await inquirer.prompt([
|
|
606
|
+
{
|
|
607
|
+
type: 'input',
|
|
608
|
+
name: 'maxRisk',
|
|
609
|
+
message: chalk.white.bold('Max Risk ($):'),
|
|
610
|
+
default: '200',
|
|
611
|
+
validate: (input) => {
|
|
612
|
+
const num = parseFloat(input);
|
|
613
|
+
if (isNaN(num) || num <= 0) {
|
|
614
|
+
return 'Please enter a valid amount greater than 0';
|
|
615
|
+
}
|
|
616
|
+
return true;
|
|
617
|
+
},
|
|
618
|
+
filter: (input) => parseFloat(input)
|
|
619
|
+
}
|
|
620
|
+
]);
|
|
621
|
+
|
|
622
|
+
console.log();
|
|
623
|
+
console.log(chalk.gray(' Daily Target: ') + chalk.green('$' + dailyTarget.toFixed(2)));
|
|
624
|
+
console.log(chalk.gray(' Max Risk: ') + chalk.red('$' + maxRisk.toFixed(2)));
|
|
625
|
+
console.log();
|
|
626
|
+
|
|
627
|
+
// Step 2: Select Lead Account
|
|
628
|
+
console.log(chalk.cyan.bold(' Step 2: Select Lead Account'));
|
|
585
629
|
console.log(chalk.gray(' The lead account is the master account whose trades will be copied.'));
|
|
586
630
|
console.log();
|
|
587
631
|
|
|
@@ -605,9 +649,9 @@ const copyTradingMenu = async () => {
|
|
|
605
649
|
|
|
606
650
|
if (leadAccount === 'back') return;
|
|
607
651
|
|
|
608
|
-
// Step
|
|
652
|
+
// Step 3: Select Follower Account
|
|
609
653
|
console.log();
|
|
610
|
-
console.log(chalk.cyan.bold(' Step
|
|
654
|
+
console.log(chalk.cyan.bold(' Step 3: Select Follower Account'));
|
|
611
655
|
console.log(chalk.gray(' The follower account will copy trades from the lead account.'));
|
|
612
656
|
console.log();
|
|
613
657
|
|
|
@@ -633,9 +677,9 @@ const copyTradingMenu = async () => {
|
|
|
633
677
|
|
|
634
678
|
if (followerAccount === 'back') return;
|
|
635
679
|
|
|
636
|
-
// Step
|
|
680
|
+
// Step 4: Select Lead Symbol
|
|
637
681
|
console.log();
|
|
638
|
-
console.log(chalk.cyan.bold(' Step
|
|
682
|
+
console.log(chalk.cyan.bold(' Step 4: Configure Lead Symbol'));
|
|
639
683
|
console.log();
|
|
640
684
|
|
|
641
685
|
const { leadSymbol } = await inquirer.prompt([
|
|
@@ -669,9 +713,9 @@ const copyTradingMenu = async () => {
|
|
|
669
713
|
}
|
|
670
714
|
]);
|
|
671
715
|
|
|
672
|
-
// Step
|
|
716
|
+
// Step 5: Select Follower Symbol
|
|
673
717
|
console.log();
|
|
674
|
-
console.log(chalk.cyan.bold(' Step
|
|
718
|
+
console.log(chalk.cyan.bold(' Step 5: Configure Follower Symbol'));
|
|
675
719
|
console.log();
|
|
676
720
|
|
|
677
721
|
const { followerSymbol } = await inquirer.prompt([
|
|
@@ -711,6 +755,10 @@ const copyTradingMenu = async () => {
|
|
|
711
755
|
console.log(chalk.white.bold(' Copy Trading Configuration'));
|
|
712
756
|
console.log(chalk.gray(getSeparator()));
|
|
713
757
|
console.log();
|
|
758
|
+
console.log(chalk.white(' RISK MANAGEMENT'));
|
|
759
|
+
console.log(chalk.white(` Daily Target: ${chalk.green('$' + dailyTarget.toFixed(2))}`));
|
|
760
|
+
console.log(chalk.white(` Max Risk: ${chalk.red('$' + maxRisk.toFixed(2))}`));
|
|
761
|
+
console.log();
|
|
714
762
|
console.log(chalk.white(' LEAD ACCOUNT'));
|
|
715
763
|
console.log(chalk.white(` Account: ${chalk.cyan(leadAccount.accountName || leadAccount.name)}`));
|
|
716
764
|
console.log(chalk.white(` PropFirm: ${chalk.magenta(leadAccount.propfirm)}`));
|
|
@@ -743,6 +791,8 @@ const copyTradingMenu = async () => {
|
|
|
743
791
|
|
|
744
792
|
// Launch Copy Trading
|
|
745
793
|
await launchCopyTrading({
|
|
794
|
+
dailyTarget,
|
|
795
|
+
maxRisk,
|
|
746
796
|
lead: {
|
|
747
797
|
account: leadAccount,
|
|
748
798
|
symbol: leadSymbol,
|
|
@@ -762,22 +812,24 @@ const copyTradingMenu = async () => {
|
|
|
762
812
|
* Launch Copy Trading
|
|
763
813
|
*/
|
|
764
814
|
const launchCopyTrading = async (config) => {
|
|
765
|
-
const { lead, follower } = config;
|
|
815
|
+
const { lead, follower, dailyTarget, maxRisk } = config;
|
|
766
816
|
|
|
767
817
|
console.log();
|
|
768
818
|
console.log(chalk.green.bold(' [>] Launching Copy Trading...'));
|
|
769
819
|
console.log();
|
|
770
820
|
|
|
771
821
|
let isRunning = true;
|
|
822
|
+
let stopReason = null;
|
|
772
823
|
let lastLeadPosition = null;
|
|
773
824
|
const logs = [];
|
|
774
|
-
const MAX_LOGS =
|
|
825
|
+
const MAX_LOGS = 12;
|
|
775
826
|
|
|
776
827
|
const stats = {
|
|
777
828
|
copiedTrades: 0,
|
|
778
829
|
leadTrades: 0,
|
|
779
830
|
followerTrades: 0,
|
|
780
|
-
errors: 0
|
|
831
|
+
errors: 0,
|
|
832
|
+
pnl: 0
|
|
781
833
|
};
|
|
782
834
|
|
|
783
835
|
const addLog = (type, message) => {
|
|
@@ -796,6 +848,15 @@ const launchCopyTrading = async (config) => {
|
|
|
796
848
|
console.log(chalk.gray(getSeparator()));
|
|
797
849
|
console.log();
|
|
798
850
|
|
|
851
|
+
// Risk Management
|
|
852
|
+
console.log(chalk.white.bold(' RISK MANAGEMENT'));
|
|
853
|
+
const targetProgress = Math.min(100, (stats.pnl / dailyTarget) * 100);
|
|
854
|
+
const riskProgress = Math.min(100, (Math.abs(Math.min(0, stats.pnl)) / maxRisk) * 100);
|
|
855
|
+
console.log(chalk.white(` Target: ${chalk.green('$' + dailyTarget.toFixed(2))} | Progress: ${targetProgress >= 100 ? chalk.green.bold(targetProgress.toFixed(1) + '%') : chalk.yellow(targetProgress.toFixed(1) + '%')}`));
|
|
856
|
+
console.log(chalk.white(` Risk: ${chalk.red('$' + maxRisk.toFixed(2))} | Used: ${riskProgress >= 100 ? chalk.red.bold(riskProgress.toFixed(1) + '%') : chalk.cyan(riskProgress.toFixed(1) + '%')}`));
|
|
857
|
+
console.log(chalk.white(` P&L: ${stats.pnl >= 0 ? chalk.green('+$' + stats.pnl.toFixed(2)) : chalk.red('-$' + Math.abs(stats.pnl).toFixed(2))}`));
|
|
858
|
+
console.log();
|
|
859
|
+
|
|
799
860
|
// Lead info
|
|
800
861
|
console.log(chalk.white.bold(' LEAD'));
|
|
801
862
|
console.log(chalk.white(` ${chalk.cyan(lead.account.accountName)} @ ${chalk.magenta(lead.account.propfirm)}`));
|
|
@@ -858,6 +919,63 @@ const launchCopyTrading = async (config) => {
|
|
|
858
919
|
if (!isRunning) return;
|
|
859
920
|
|
|
860
921
|
try {
|
|
922
|
+
// Get follower positions for P&L tracking
|
|
923
|
+
const followerPositions = await follower.service.getPositions(follower.account.rithmicAccountId || follower.account.accountId);
|
|
924
|
+
|
|
925
|
+
if (followerPositions.success && followerPositions.positions) {
|
|
926
|
+
const followerPos = followerPositions.positions.find(p =>
|
|
927
|
+
p.symbol === follower.symbol.value ||
|
|
928
|
+
p.symbol?.includes(follower.symbol.searchText)
|
|
929
|
+
);
|
|
930
|
+
|
|
931
|
+
// Update P&L from follower position
|
|
932
|
+
if (followerPos && typeof followerPos.unrealizedPnl === 'number') {
|
|
933
|
+
stats.pnl = followerPos.unrealizedPnl;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// Check if daily target reached
|
|
938
|
+
if (stats.pnl >= dailyTarget) {
|
|
939
|
+
isRunning = false;
|
|
940
|
+
stopReason = 'target';
|
|
941
|
+
addLog('success', `Daily target reached! +$${stats.pnl.toFixed(2)}`);
|
|
942
|
+
|
|
943
|
+
// Close follower position
|
|
944
|
+
try {
|
|
945
|
+
await follower.service.closePosition(
|
|
946
|
+
follower.account.rithmicAccountId || follower.account.accountId,
|
|
947
|
+
follower.symbol.value
|
|
948
|
+
);
|
|
949
|
+
addLog('info', 'Follower position closed');
|
|
950
|
+
} catch (e) {
|
|
951
|
+
// Position may already be closed
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
displayUI();
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
// Check if max risk reached
|
|
959
|
+
if (stats.pnl <= -maxRisk) {
|
|
960
|
+
isRunning = false;
|
|
961
|
+
stopReason = 'risk';
|
|
962
|
+
addLog('error', `Max risk reached! -$${Math.abs(stats.pnl).toFixed(2)}`);
|
|
963
|
+
|
|
964
|
+
// Close follower position
|
|
965
|
+
try {
|
|
966
|
+
await follower.service.closePosition(
|
|
967
|
+
follower.account.rithmicAccountId || follower.account.accountId,
|
|
968
|
+
follower.symbol.value
|
|
969
|
+
);
|
|
970
|
+
addLog('info', 'Follower position closed');
|
|
971
|
+
} catch (e) {
|
|
972
|
+
// Position may already be closed
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
displayUI();
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
|
|
861
979
|
// Get lead positions
|
|
862
980
|
const leadPositions = await lead.service.getPositions(lead.account.rithmicAccountId || lead.account.accountId);
|
|
863
981
|
|
|
@@ -918,15 +1036,25 @@ const launchCopyTrading = async (config) => {
|
|
|
918
1036
|
await waitForStopKey();
|
|
919
1037
|
|
|
920
1038
|
// Cleanup
|
|
921
|
-
isRunning = false;
|
|
922
1039
|
clearInterval(monitorInterval);
|
|
1040
|
+
isRunning = false;
|
|
923
1041
|
|
|
924
1042
|
console.log();
|
|
925
|
-
|
|
1043
|
+
if (stopReason === 'target') {
|
|
1044
|
+
console.log(chalk.green.bold(' [OK] Daily target reached! Copy trading stopped.'));
|
|
1045
|
+
} else if (stopReason === 'risk') {
|
|
1046
|
+
console.log(chalk.red.bold(' [X] Max risk reached! Copy trading stopped.'));
|
|
1047
|
+
} else {
|
|
1048
|
+
console.log(chalk.yellow(' [OK] Copy trading stopped by user'));
|
|
1049
|
+
}
|
|
926
1050
|
console.log();
|
|
927
1051
|
console.log(chalk.gray(getSeparator()));
|
|
928
1052
|
console.log(chalk.white.bold(' Session Summary'));
|
|
929
1053
|
console.log(chalk.gray(getSeparator()));
|
|
1054
|
+
console.log(chalk.white(` Daily Target: ${chalk.green('$' + dailyTarget.toFixed(2))}`));
|
|
1055
|
+
console.log(chalk.white(` Max Risk: ${chalk.red('$' + maxRisk.toFixed(2))}`));
|
|
1056
|
+
console.log(chalk.white(` Final P&L: ${stats.pnl >= 0 ? chalk.green('+$' + stats.pnl.toFixed(2)) : chalk.red('-$' + Math.abs(stats.pnl).toFixed(2))}`));
|
|
1057
|
+
console.log(chalk.gray(getSeparator()));
|
|
930
1058
|
console.log(chalk.white(` Lead Trades: ${chalk.cyan(stats.leadTrades)}`));
|
|
931
1059
|
console.log(chalk.white(` Copied Trades: ${chalk.green(stats.copiedTrades)}`));
|
|
932
1060
|
console.log(chalk.white(` Errors: ${chalk.red(stats.errors)}`));
|