hedgequantx 1.2.143 → 1.2.145

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/app.js +159 -82
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "1.2.143",
3
+ "version": "1.2.145",
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/app.js CHANGED
@@ -46,6 +46,27 @@ const restoreTerminal = () => {
46
46
  }
47
47
  };
48
48
 
49
+ /**
50
+ * Ensure stdin is ready for inquirer prompts
51
+ * This fixes input leaking to bash after session restore
52
+ */
53
+ const prepareStdin = () => {
54
+ try {
55
+ // Remove any raw mode that might be left from previous operations
56
+ if (process.stdin.isTTY && process.stdin.isRaw) {
57
+ process.stdin.setRawMode(false);
58
+ }
59
+ // Remove any lingering keypress listeners
60
+ process.stdin.removeAllListeners('keypress');
61
+ process.stdin.removeAllListeners('data');
62
+ // Pause stdin so inquirer can take control
63
+ process.stdin.pause();
64
+ // Small delay to let event loop settle
65
+ } catch (e) {
66
+ // Ignore errors
67
+ }
68
+ };
69
+
49
70
  // Register global handlers to restore terminal on exit/crash
50
71
  process.on('exit', restoreTerminal);
51
72
  process.on('SIGINT', () => { restoreTerminal(); process.exit(0); });
@@ -668,13 +689,56 @@ const dashboardMenu = async (service) => {
668
689
  console.log(chalk.cyan('║') + chalk.yellow.bold(centerLine('Welcome, HQX Trader!', W)) + chalk.cyan('║'));
669
690
  console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
670
691
 
671
- // Connection info - show all active connections
692
+ // Connection info - show all active connections in boxes (max 3 per row)
672
693
  const allConns = connections.getAll();
673
694
  if (allConns.length > 0) {
674
- allConns.forEach(c => {
675
- const connText = c.propfirm || c.type || 'Connected';
676
- console.log(chalk.cyan('║') + chalk.green(padLine(` ${connText}`, W)) + chalk.cyan('║'));
677
- });
695
+ const maxPerRow = 3;
696
+ const boxPadding = 2; // padding inside each mini-box
697
+ const gap = 2; // gap between boxes
698
+
699
+ // Calculate box width based on number of connections (max 3)
700
+ const numBoxes = Math.min(allConns.length, maxPerRow);
701
+ const totalGaps = (numBoxes - 1) * gap;
702
+ const boxWidth = Math.floor((W - totalGaps - 2) / numBoxes); // -2 for outer padding
703
+
704
+ // Process connections in rows of 3
705
+ for (let rowStart = 0; rowStart < allConns.length; rowStart += maxPerRow) {
706
+ const rowConns = allConns.slice(rowStart, rowStart + maxPerRow);
707
+ const numInRow = rowConns.length;
708
+ const rowBoxWidth = Math.floor((W - (numInRow - 1) * gap - 2) / numInRow);
709
+
710
+ // Top border of boxes
711
+ let topLine = ' ';
712
+ for (let i = 0; i < numInRow; i++) {
713
+ topLine += '┌' + '─'.repeat(rowBoxWidth - 2) + '┐';
714
+ if (i < numInRow - 1) topLine += ' '.repeat(gap);
715
+ }
716
+ const topPad = W - topLine.length;
717
+ console.log(chalk.cyan('║') + chalk.green(topLine) + ' '.repeat(Math.max(0, topPad)) + chalk.cyan('║'));
718
+
719
+ // Content of boxes
720
+ let contentLine = ' ';
721
+ for (let i = 0; i < numInRow; i++) {
722
+ const connText = rowConns[i].propfirm || rowConns[i].type || 'Connected';
723
+ const truncated = connText.length > rowBoxWidth - 4 ? connText.slice(0, rowBoxWidth - 7) + '...' : connText;
724
+ const innerWidth = rowBoxWidth - 4; // -2 for borders, -2 for padding
725
+ const textPad = Math.floor((innerWidth - truncated.length) / 2);
726
+ const textPadRight = innerWidth - truncated.length - textPad;
727
+ contentLine += '│ ' + ' '.repeat(textPad) + truncated + ' '.repeat(textPadRight) + ' │';
728
+ if (i < numInRow - 1) contentLine += ' '.repeat(gap);
729
+ }
730
+ const contentPad = W - contentLine.length;
731
+ console.log(chalk.cyan('║') + chalk.green(contentLine) + ' '.repeat(Math.max(0, contentPad)) + chalk.cyan('║'));
732
+
733
+ // Bottom border of boxes
734
+ let bottomLine = ' ';
735
+ for (let i = 0; i < numInRow; i++) {
736
+ bottomLine += '└' + '─'.repeat(rowBoxWidth - 2) + '┘';
737
+ if (i < numInRow - 1) bottomLine += ' '.repeat(gap);
738
+ }
739
+ const bottomPad = W - bottomLine.length;
740
+ console.log(chalk.cyan('║') + chalk.green(bottomLine) + ' '.repeat(Math.max(0, bottomPad)) + chalk.cyan('║'));
741
+ }
678
742
  }
679
743
 
680
744
  console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
@@ -802,89 +866,102 @@ const handleUpdate = async () => {
802
866
  * Main application loop
803
867
  */
804
868
  const run = async () => {
805
- await banner();
806
-
807
- // Try to restore session
808
- const spinner = ora('Restoring session...').start();
809
- const restored = await connections.restoreFromStorage();
810
-
811
- if (restored) {
812
- spinner.succeed('Session restored');
813
- currentService = connections.getAll()[0].service;
814
- } else {
815
- spinner.info('No active session');
816
- }
817
-
818
- // Main loop
819
- while (true) {
820
- // Refresh banner with stats
869
+ try {
821
870
  await banner();
822
871
 
823
- if (!connections.isConnected()) {
824
- const choice = await mainMenu();
825
-
826
- if (choice === 'exit') {
827
- console.log(chalk.gray('Goodbye!'));
828
- process.exit(0);
829
- }
830
-
831
- if (choice === 'projectx') {
832
- const service = await projectXMenu();
833
- if (service) currentService = service;
834
- }
835
-
836
- if (choice === 'rithmic') {
837
- const service = await rithmicMenu();
838
- if (service) currentService = service;
839
- }
840
-
841
- if (choice === 'tradovate') {
842
- const service = await tradovateMenu();
843
- if (service) currentService = service;
844
- }
872
+ // Try to restore session
873
+ const spinner = ora('Restoring session...').start();
874
+ const restored = await connections.restoreFromStorage();
875
+
876
+ if (restored) {
877
+ spinner.succeed('Session restored');
878
+ currentService = connections.getAll()[0].service;
845
879
  } else {
846
- const action = await dashboardMenu(currentService);
847
-
848
- switch (action) {
849
- case 'accounts':
850
- await showAccounts(currentService);
851
- break;
852
-
853
- case 'stats':
854
- await showStats(currentService);
855
- break;
856
- case 'add_prop_account':
857
- // Show platform selection menu
858
- const platformChoice = await addPropAccountMenu();
859
- if (platformChoice === 'projectx') {
860
- const newService = await projectXMenu();
861
- if (newService) currentService = newService;
862
- } else if (platformChoice === 'rithmic') {
863
- const newService = await rithmicMenu();
864
- if (newService) currentService = newService;
865
- } else if (platformChoice === 'tradovate') {
866
- const newService = await tradovateMenu();
867
- if (newService) currentService = newService;
880
+ spinner.info('No active session');
881
+ }
882
+
883
+ // Main loop
884
+ while (true) {
885
+ try {
886
+ // Ensure stdin is ready for prompts (fixes input leaking to bash)
887
+ prepareStdin();
888
+
889
+ // Refresh banner with stats
890
+ await banner();
891
+
892
+ if (!connections.isConnected()) {
893
+ const choice = await mainMenu();
894
+
895
+ if (choice === 'exit') {
896
+ console.log(chalk.gray('Goodbye!'));
897
+ process.exit(0);
898
+ }
899
+
900
+ if (choice === 'projectx') {
901
+ const service = await projectXMenu();
902
+ if (service) currentService = service;
868
903
  }
869
- break;
870
- case 'algotrading':
871
- await algoTradingMenu(currentService);
872
- break;
873
- case 'update':
874
- const updateResult = await handleUpdate();
875
- if (updateResult === 'restart') return; // Stop loop, new process spawned
876
- break;
877
- case 'disconnect':
878
- const connCount = connections.count();
879
- connections.disconnectAll();
880
- currentService = null;
881
- console.log(chalk.yellow(`Disconnected ${connCount} connection${connCount > 1 ? 's' : ''}`));
882
- break;
883
- case 'exit':
884
- console.log(chalk.gray('Goodbye!'));
885
- process.exit(0);
904
+
905
+ if (choice === 'rithmic') {
906
+ const service = await rithmicMenu();
907
+ if (service) currentService = service;
908
+ }
909
+
910
+ if (choice === 'tradovate') {
911
+ const service = await tradovateMenu();
912
+ if (service) currentService = service;
913
+ }
914
+ } else {
915
+ const action = await dashboardMenu(currentService);
916
+
917
+ switch (action) {
918
+ case 'accounts':
919
+ await showAccounts(currentService);
920
+ break;
921
+
922
+ case 'stats':
923
+ await showStats(currentService);
924
+ break;
925
+ case 'add_prop_account':
926
+ // Show platform selection menu
927
+ const platformChoice = await addPropAccountMenu();
928
+ if (platformChoice === 'projectx') {
929
+ const newService = await projectXMenu();
930
+ if (newService) currentService = newService;
931
+ } else if (platformChoice === 'rithmic') {
932
+ const newService = await rithmicMenu();
933
+ if (newService) currentService = newService;
934
+ } else if (platformChoice === 'tradovate') {
935
+ const newService = await tradovateMenu();
936
+ if (newService) currentService = newService;
937
+ }
938
+ break;
939
+ case 'algotrading':
940
+ await algoTradingMenu(currentService);
941
+ break;
942
+ case 'update':
943
+ const updateResult = await handleUpdate();
944
+ if (updateResult === 'restart') return; // Stop loop, new process spawned
945
+ break;
946
+ case 'disconnect':
947
+ const connCount = connections.count();
948
+ connections.disconnectAll();
949
+ currentService = null;
950
+ console.log(chalk.yellow(`Disconnected ${connCount} connection${connCount > 1 ? 's' : ''}`));
951
+ break;
952
+ case 'exit':
953
+ console.log(chalk.gray('Goodbye!'));
954
+ process.exit(0);
955
+ }
956
+ }
957
+ } catch (loopError) {
958
+ console.error(chalk.red('Error in main loop:'), loopError.message);
959
+ // Continue the loop
886
960
  }
887
961
  }
962
+ } catch (error) {
963
+ console.error(chalk.red('Fatal error:'), error.message);
964
+ process.exit(1);
888
965
  }
889
966
  };
890
967