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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/pages/algo.js +141 -13
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "1.2.56",
3
+ "version": "1.2.57",
4
4
  "description": "Prop Futures Algo Trading CLI - Connect to Topstep, Alpha Futures, and other prop firms",
5
5
  "main": "src/app.js",
6
6
  "bin": {
package/src/pages/algo.js CHANGED
@@ -580,8 +580,52 @@ const copyTradingMenu = async () => {
580
580
  return;
581
581
  }
582
582
 
583
- // Step 1: Select Lead Account
584
- console.log(chalk.cyan.bold(' Step 1: Select Lead Account'));
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 2: Select Follower Account
652
+ // Step 3: Select Follower Account
609
653
  console.log();
610
- console.log(chalk.cyan.bold(' Step 2: Select Follower Account'));
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 3: Select Lead Symbol
680
+ // Step 4: Select Lead Symbol
637
681
  console.log();
638
- console.log(chalk.cyan.bold(' Step 3: Configure Lead Symbol'));
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 4: Select Follower Symbol
716
+ // Step 5: Select Follower Symbol
673
717
  console.log();
674
- console.log(chalk.cyan.bold(' Step 4: Configure Follower Symbol'));
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 = 15;
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
- console.log(chalk.green(' [OK] Copy trading stopped'));
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)}`));