hedgequantx 1.7.7 → 1.7.9
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 +2 -2
- package/src/app.js +25 -9
- package/src/menus/dashboard.js +14 -10
- package/src/utils/prompts.js +86 -46
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hedgequantx",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.9",
|
|
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": {
|
|
@@ -41,11 +41,11 @@
|
|
|
41
41
|
"src/"
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@clack/prompts": "^0.11.0",
|
|
45
44
|
"asciichart": "^1.5.25",
|
|
46
45
|
"chalk": "^4.1.2",
|
|
47
46
|
"commander": "^11.1.0",
|
|
48
47
|
"figlet": "^1.7.0",
|
|
48
|
+
"inquirer": "^8.2.7",
|
|
49
49
|
"ora": "^5.4.1",
|
|
50
50
|
"protobufjs": "^8.0.0",
|
|
51
51
|
"ws": "^8.18.3"
|
package/src/app.js
CHANGED
|
@@ -133,7 +133,7 @@ const banner = async () => {
|
|
|
133
133
|
console.log(chalk.cyan('╠' + '═'.repeat(innerWidth) + '╣'));
|
|
134
134
|
const tagline = isMobile ? `HQX v${version}` : `Prop Futures Algo Trading v${version}`;
|
|
135
135
|
console.log(chalk.cyan('║') + chalk.white(centerText(tagline, innerWidth)) + chalk.cyan('║'));
|
|
136
|
-
|
|
136
|
+
// No closing line - dashboard will continue the box
|
|
137
137
|
};
|
|
138
138
|
|
|
139
139
|
/**
|
|
@@ -142,20 +142,36 @@ const banner = async () => {
|
|
|
142
142
|
const mainMenu = async () => {
|
|
143
143
|
const boxWidth = getLogoWidth();
|
|
144
144
|
const innerWidth = boxWidth - 2;
|
|
145
|
+
const col1Width = Math.floor(innerWidth / 2);
|
|
145
146
|
|
|
146
|
-
|
|
147
|
+
const menuRow = (left, right) => {
|
|
148
|
+
const leftPlain = left.replace(/\x1b\[[0-9;]*m/g, '');
|
|
149
|
+
const rightPlain = right ? right.replace(/\x1b\[[0-9;]*m/g, '') : '';
|
|
150
|
+
const leftPadded = ' ' + left + ' '.repeat(Math.max(0, col1Width - leftPlain.length - 2));
|
|
151
|
+
const rightPadded = (right || '') + ' '.repeat(Math.max(0, innerWidth - col1Width - rightPlain.length));
|
|
152
|
+
console.log(chalk.cyan('║') + leftPadded + rightPadded + chalk.cyan('║'));
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// Continue from banner
|
|
156
|
+
console.log(chalk.cyan('╠' + '═'.repeat(innerWidth) + '╣'));
|
|
147
157
|
console.log(chalk.cyan('║') + chalk.white.bold(centerText('SELECT PLATFORM', innerWidth)) + chalk.cyan('║'));
|
|
148
158
|
console.log(chalk.cyan('╠' + '═'.repeat(innerWidth) + '╣'));
|
|
159
|
+
|
|
160
|
+
menuRow(chalk.cyan('[1] ProjectX'), chalk.cyan('[2] Rithmic'));
|
|
161
|
+
menuRow(chalk.cyan('[3] Tradovate'), chalk.red('[X] Exit'));
|
|
162
|
+
|
|
149
163
|
console.log(chalk.cyan('╚' + '═'.repeat(innerWidth) + '╝'));
|
|
150
164
|
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
165
|
+
const input = await prompts.textInput('Select (1/2/3/X)');
|
|
166
|
+
|
|
167
|
+
const actionMap = {
|
|
168
|
+
'1': 'projectx',
|
|
169
|
+
'2': 'rithmic',
|
|
170
|
+
'3': 'tradovate',
|
|
171
|
+
'x': 'exit'
|
|
172
|
+
};
|
|
157
173
|
|
|
158
|
-
return
|
|
174
|
+
return actionMap[(input || '').toLowerCase()] || 'exit';
|
|
159
175
|
};
|
|
160
176
|
|
|
161
177
|
/**
|
package/src/menus/dashboard.js
CHANGED
|
@@ -30,7 +30,8 @@ const dashboardMenu = async (service) => {
|
|
|
30
30
|
return chalk.cyan('║') + content + ' '.repeat(Math.max(0, padding)) + chalk.cyan('║');
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
// Continue from banner (no top border)
|
|
34
|
+
console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
|
|
34
35
|
console.log(makeLine(chalk.yellow.bold('Welcome, HQX Trader!'), 'center'));
|
|
35
36
|
console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
|
|
36
37
|
|
|
@@ -89,16 +90,19 @@ const dashboardMenu = async (service) => {
|
|
|
89
90
|
|
|
90
91
|
console.log(chalk.cyan('╚' + '═'.repeat(W) + '╝'));
|
|
91
92
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
// Simple input - no duplicate menu
|
|
94
|
+
const input = await prompts.textInput('Select (1/2/+/A/U/X)');
|
|
95
|
+
|
|
96
|
+
const actionMap = {
|
|
97
|
+
'1': 'accounts',
|
|
98
|
+
'2': 'stats',
|
|
99
|
+
'+': 'add_prop_account',
|
|
100
|
+
'a': 'algotrading',
|
|
101
|
+
'u': 'update',
|
|
102
|
+
'x': 'disconnect'
|
|
103
|
+
};
|
|
100
104
|
|
|
101
|
-
return
|
|
105
|
+
return actionMap[(input || '').toLowerCase()] || null;
|
|
102
106
|
};
|
|
103
107
|
|
|
104
108
|
/**
|
package/src/utils/prompts.js
CHANGED
|
@@ -1,87 +1,127 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Centralized prompts utility
|
|
3
|
-
*
|
|
2
|
+
* Centralized prompts utility
|
|
3
|
+
* Uses inquirer for reliable stdin handling
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
const readline = require('readline');
|
|
6
|
+
const inquirer = require('inquirer');
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
|
-
*
|
|
9
|
+
* Ensure stdin is ready
|
|
11
10
|
*/
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
const prepareStdin = () => {
|
|
12
|
+
try {
|
|
13
|
+
if (process.stdin.isPaused()) process.stdin.resume();
|
|
14
|
+
if (process.stdin.isTTY && process.stdin.isRaw) process.stdin.setRawMode(false);
|
|
15
|
+
} catch (e) {}
|
|
16
|
+
};
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
|
-
*
|
|
19
|
+
* Wait for Enter
|
|
19
20
|
*/
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return result;
|
|
21
|
+
const waitForEnter = async (message = 'Press Enter to continue...') => {
|
|
22
|
+
prepareStdin();
|
|
23
|
+
await inquirer.prompt([{ type: 'input', name: '_', message, prefix: '' }]);
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Text input
|
|
28
28
|
*/
|
|
29
|
-
const textInput = async (message,
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
const textInput = async (message, defaultVal = '') => {
|
|
30
|
+
prepareStdin();
|
|
31
|
+
const { value } = await inquirer.prompt([{
|
|
32
|
+
type: 'input',
|
|
33
|
+
name: 'value',
|
|
34
|
+
message,
|
|
35
|
+
default: defaultVal,
|
|
36
|
+
prefix: ''
|
|
37
|
+
}]);
|
|
38
|
+
return value;
|
|
37
39
|
};
|
|
38
40
|
|
|
39
41
|
/**
|
|
40
42
|
* Password input
|
|
41
43
|
*/
|
|
42
|
-
const passwordInput = async (message
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
const passwordInput = async (message) => {
|
|
45
|
+
prepareStdin();
|
|
46
|
+
const { value } = await inquirer.prompt([{
|
|
47
|
+
type: 'password',
|
|
48
|
+
name: 'value',
|
|
49
|
+
message,
|
|
50
|
+
mask: '*',
|
|
51
|
+
prefix: ''
|
|
52
|
+
}]);
|
|
53
|
+
return value;
|
|
49
54
|
};
|
|
50
55
|
|
|
51
56
|
/**
|
|
52
|
-
* Confirm
|
|
57
|
+
* Confirm Y/n
|
|
53
58
|
*/
|
|
54
|
-
const confirmPrompt = async (message,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
const confirmPrompt = async (message, defaultVal = true) => {
|
|
60
|
+
prepareStdin();
|
|
61
|
+
const { value } = await inquirer.prompt([{
|
|
62
|
+
type: 'confirm',
|
|
63
|
+
name: 'value',
|
|
64
|
+
message,
|
|
65
|
+
default: defaultVal,
|
|
66
|
+
prefix: ''
|
|
67
|
+
}]);
|
|
68
|
+
return value;
|
|
58
69
|
};
|
|
59
70
|
|
|
60
71
|
/**
|
|
61
|
-
* Number input
|
|
72
|
+
* Number input
|
|
62
73
|
*/
|
|
63
74
|
const numberInput = async (message, defaultVal = 1, min = 1, max = 1000) => {
|
|
64
|
-
|
|
75
|
+
prepareStdin();
|
|
76
|
+
const { value } = await inquirer.prompt([{
|
|
77
|
+
type: 'input',
|
|
78
|
+
name: 'value',
|
|
65
79
|
message,
|
|
66
|
-
|
|
67
|
-
|
|
80
|
+
default: String(defaultVal),
|
|
81
|
+
prefix: '',
|
|
82
|
+
validate: (v) => {
|
|
68
83
|
const n = parseInt(v);
|
|
69
84
|
if (isNaN(n)) return 'Enter a number';
|
|
70
|
-
if (n < min) return `
|
|
71
|
-
if (n > max) return `
|
|
72
|
-
return
|
|
85
|
+
if (n < min) return `Min: ${min}`;
|
|
86
|
+
if (n > max) return `Max: ${max}`;
|
|
87
|
+
return true;
|
|
73
88
|
}
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
|
|
89
|
+
}]);
|
|
90
|
+
return parseInt(value) || defaultVal;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Select - just text input, map to value
|
|
95
|
+
*/
|
|
96
|
+
const selectOption = async (message, options) => {
|
|
97
|
+
prepareStdin();
|
|
98
|
+
const { value } = await inquirer.prompt([{
|
|
99
|
+
type: 'input',
|
|
100
|
+
name: 'value',
|
|
101
|
+
message,
|
|
102
|
+
prefix: ''
|
|
103
|
+
}]);
|
|
104
|
+
|
|
105
|
+
const input = (value || '').toLowerCase().trim();
|
|
106
|
+
|
|
107
|
+
// Find by value
|
|
108
|
+
for (const opt of options) {
|
|
109
|
+
if (String(opt.value).toLowerCase() === input) return opt.value;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Find by index (1-based)
|
|
113
|
+
const idx = parseInt(input) - 1;
|
|
114
|
+
if (idx >= 0 && idx < options.length) return options[idx].value;
|
|
115
|
+
|
|
116
|
+
return null;
|
|
77
117
|
};
|
|
78
118
|
|
|
79
119
|
module.exports = {
|
|
120
|
+
prepareStdin,
|
|
80
121
|
waitForEnter,
|
|
81
|
-
selectOption,
|
|
82
122
|
textInput,
|
|
83
123
|
passwordInput,
|
|
84
124
|
confirmPrompt,
|
|
85
125
|
numberInput,
|
|
86
|
-
|
|
126
|
+
selectOption
|
|
87
127
|
};
|