hedgequantx 1.7.8 → 1.8.0
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 -1
- package/src/app.js +2 -3
- package/src/menus/dashboard.js +1 -2
- package/src/utils/prompts.js +96 -164
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hedgequantx",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
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": {
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"chalk": "^4.1.2",
|
|
46
46
|
"commander": "^11.1.0",
|
|
47
47
|
"figlet": "^1.7.0",
|
|
48
|
+
"inquirer": "^8.2.7",
|
|
48
49
|
"ora": "^5.4.1",
|
|
49
50
|
"protobufjs": "^8.0.0",
|
|
50
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
|
+
console.log(chalk.cyan('╚' + '═'.repeat(innerWidth) + '╝'));
|
|
137
137
|
};
|
|
138
138
|
|
|
139
139
|
/**
|
|
@@ -152,8 +152,7 @@ const mainMenu = async () => {
|
|
|
152
152
|
console.log(chalk.cyan('║') + leftPadded + rightPadded + chalk.cyan('║'));
|
|
153
153
|
};
|
|
154
154
|
|
|
155
|
-
|
|
156
|
-
console.log(chalk.cyan('╠' + '═'.repeat(innerWidth) + '╣'));
|
|
155
|
+
console.log(chalk.cyan('╔' + '═'.repeat(innerWidth) + '╗'));
|
|
157
156
|
console.log(chalk.cyan('║') + chalk.white.bold(centerText('SELECT PLATFORM', innerWidth)) + chalk.cyan('║'));
|
|
158
157
|
console.log(chalk.cyan('╠' + '═'.repeat(innerWidth) + '╣'));
|
|
159
158
|
|
package/src/menus/dashboard.js
CHANGED
|
@@ -30,8 +30,7 @@ const dashboardMenu = async (service) => {
|
|
|
30
30
|
return chalk.cyan('║') + content + ' '.repeat(Math.max(0, padding)) + chalk.cyan('║');
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
|
|
33
|
+
console.log(chalk.cyan('╔' + '═'.repeat(W) + '╗'));
|
|
35
34
|
console.log(makeLine(chalk.yellow.bold('Welcome, HQX Trader!'), 'center'));
|
|
36
35
|
console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
|
|
37
36
|
|
package/src/utils/prompts.js
CHANGED
|
@@ -1,195 +1,127 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Centralized prompts utility
|
|
3
|
-
* Uses
|
|
2
|
+
* Centralized prompts utility
|
|
3
|
+
* Uses inquirer for reliable stdin handling
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const inquirer = require('inquirer');
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* Ensure stdin is ready
|
|
10
10
|
*/
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
+
};
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
|
-
* Wait for Enter
|
|
19
|
+
* Wait for Enter
|
|
18
20
|
*/
|
|
19
|
-
const waitForEnter = (message = 'Press Enter to continue...') =>
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
21
|
+
const waitForEnter = async (message = 'Press Enter to continue...') => {
|
|
22
|
+
prepareStdin();
|
|
23
|
+
await inquirer.prompt([{ type: 'input', name: '_', message, prefix: '' }]);
|
|
24
|
+
};
|
|
23
25
|
|
|
24
26
|
/**
|
|
25
|
-
*
|
|
27
|
+
* Text input
|
|
26
28
|
*/
|
|
27
|
-
const textInput = (message, defaultVal = '') =>
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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;
|
|
39
|
+
};
|
|
35
40
|
|
|
36
41
|
/**
|
|
37
|
-
* Password input
|
|
42
|
+
* Password input
|
|
38
43
|
*/
|
|
39
|
-
const passwordInput = (message) =>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (char === '\n' || char === '\r') {
|
|
52
|
-
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
53
|
-
process.stdin.removeListener('data', onData);
|
|
54
|
-
console.log();
|
|
55
|
-
rl.close();
|
|
56
|
-
resolve(password);
|
|
57
|
-
} else if (char === '\u0003') { // Ctrl+C
|
|
58
|
-
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
59
|
-
process.stdin.removeListener('data', onData);
|
|
60
|
-
rl.close();
|
|
61
|
-
resolve(null);
|
|
62
|
-
} else if (char === '\u007F' || char === '\b') { // Backspace
|
|
63
|
-
if (password.length > 0) {
|
|
64
|
-
password = password.slice(0, -1);
|
|
65
|
-
process.stdout.write('\b \b');
|
|
66
|
-
}
|
|
67
|
-
} else {
|
|
68
|
-
password += char;
|
|
69
|
-
process.stdout.write('*');
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
process.stdin.on('data', onData);
|
|
74
|
-
process.stdin.resume();
|
|
75
|
-
});
|
|
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;
|
|
54
|
+
};
|
|
76
55
|
|
|
77
56
|
/**
|
|
78
|
-
*
|
|
57
|
+
* Confirm Y/n
|
|
79
58
|
*/
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
let selectedIndex = 0;
|
|
95
|
-
const maxIndex = options.length - 1;
|
|
96
|
-
|
|
97
|
-
const render = () => {
|
|
98
|
-
// Move cursor up and clear lines
|
|
99
|
-
if (selectedIndex > 0 || options.length > 1) {
|
|
100
|
-
process.stdout.write(`\x1B[${options.length}A`);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
options.forEach((opt, i) => {
|
|
104
|
-
const prefix = i === selectedIndex ? '› ' : ' ';
|
|
105
|
-
const style = i === selectedIndex ? '\x1B[36m' : '\x1B[90m'; // cyan : gray
|
|
106
|
-
process.stdout.write(`\x1B[2K${style}${prefix}${opt.label}\x1B[0m\n`);
|
|
107
|
-
});
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
// Initial render
|
|
111
|
-
console.log(`\x1B[36m${message}\x1B[0m`);
|
|
112
|
-
options.forEach((opt, i) => {
|
|
113
|
-
const prefix = i === selectedIndex ? '› ' : ' ';
|
|
114
|
-
const style = i === selectedIndex ? '\x1B[36m' : '\x1B[90m';
|
|
115
|
-
console.log(`${style}${prefix}${opt.label}\x1B[0m`);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
readline.emitKeypressEvents(process.stdin);
|
|
119
|
-
process.stdin.setRawMode(true);
|
|
120
|
-
process.stdin.resume();
|
|
121
|
-
|
|
122
|
-
const onKeypress = (str, key) => {
|
|
123
|
-
if (key.name === 'up' || key.name === 'k') {
|
|
124
|
-
selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : maxIndex;
|
|
125
|
-
render();
|
|
126
|
-
} else if (key.name === 'down' || key.name === 'j') {
|
|
127
|
-
selectedIndex = selectedIndex < maxIndex ? selectedIndex + 1 : 0;
|
|
128
|
-
render();
|
|
129
|
-
} else if (key.name === 'return') {
|
|
130
|
-
cleanup();
|
|
131
|
-
resolve(options[selectedIndex].value);
|
|
132
|
-
} else if (key.name === 'escape' || (key.ctrl && key.name === 'c')) {
|
|
133
|
-
cleanup();
|
|
134
|
-
resolve(null);
|
|
135
|
-
} else if (str >= '1' && str <= '9') {
|
|
136
|
-
const idx = parseInt(str) - 1;
|
|
137
|
-
if (idx <= maxIndex) {
|
|
138
|
-
cleanup();
|
|
139
|
-
resolve(options[idx].value);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
const cleanup = () => {
|
|
145
|
-
process.stdin.removeListener('keypress', onKeypress);
|
|
146
|
-
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
147
|
-
// Clear the menu display
|
|
148
|
-
process.stdout.write(`\x1B[${options.length + 1}A`);
|
|
149
|
-
for (let i = 0; i <= options.length; i++) {
|
|
150
|
-
process.stdout.write('\x1B[2K\n');
|
|
151
|
-
}
|
|
152
|
-
process.stdout.write(`\x1B[${options.length + 1}A`);
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
process.stdin.on('keypress', onKeypress);
|
|
156
|
-
});
|
|
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;
|
|
69
|
+
};
|
|
157
70
|
|
|
158
71
|
/**
|
|
159
|
-
*
|
|
72
|
+
* Number input
|
|
160
73
|
*/
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
74
|
+
const numberInput = async (message, defaultVal = 1, min = 1, max = 1000) => {
|
|
75
|
+
prepareStdin();
|
|
76
|
+
const { value } = await inquirer.prompt([{
|
|
77
|
+
type: 'input',
|
|
78
|
+
name: 'value',
|
|
79
|
+
message,
|
|
80
|
+
default: String(defaultVal),
|
|
81
|
+
prefix: '',
|
|
82
|
+
validate: (v) => {
|
|
83
|
+
const n = parseInt(v);
|
|
84
|
+
if (isNaN(n)) return 'Enter a number';
|
|
85
|
+
if (n < min) return `Min: ${min}`;
|
|
86
|
+
if (n > max) return `Max: ${max}`;
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
}]);
|
|
90
|
+
return parseInt(value) || defaultVal;
|
|
91
|
+
};
|
|
173
92
|
|
|
174
93
|
/**
|
|
175
|
-
*
|
|
94
|
+
* Select - just text input, map to value
|
|
176
95
|
*/
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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;
|
|
117
|
+
};
|
|
187
118
|
|
|
188
119
|
module.exports = {
|
|
120
|
+
prepareStdin,
|
|
189
121
|
waitForEnter,
|
|
190
|
-
selectOption,
|
|
191
122
|
textInput,
|
|
192
123
|
passwordInput,
|
|
193
124
|
confirmPrompt,
|
|
194
|
-
numberInput
|
|
125
|
+
numberInput,
|
|
126
|
+
selectOption
|
|
195
127
|
};
|