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.
- package/package.json +1 -1
- package/src/app.js +159 -82
package/package.json
CHANGED
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
|
-
|
|
675
|
-
|
|
676
|
-
|
|
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
|
-
|
|
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
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
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
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
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
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
if (
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
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
|
|