hedgequantx 1.2.87 → 1.2.89
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/config/constants.js +38 -13
- package/src/pages/algo.js +104 -25
package/package.json
CHANGED
package/src/config/constants.js
CHANGED
|
@@ -49,20 +49,45 @@ const ORDER_SIDE = {
|
|
|
49
49
|
1: { text: 'Sell', color: 'red' }
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
-
// Popular Futures Symbols
|
|
52
|
+
// Popular Futures Symbols - Complete list
|
|
53
53
|
const FUTURES_SYMBOLS = [
|
|
54
|
-
|
|
55
|
-
{ name: '
|
|
56
|
-
{ name: '
|
|
57
|
-
{ name: '
|
|
58
|
-
{ name: '
|
|
59
|
-
{ name: '
|
|
60
|
-
{ name: '
|
|
61
|
-
{ name: '
|
|
62
|
-
{ name: '
|
|
63
|
-
|
|
64
|
-
{ name: '
|
|
65
|
-
{ name: '
|
|
54
|
+
// Index Futures
|
|
55
|
+
{ name: 'NQ - E-mini NASDAQ-100', value: 'NQ', searchText: 'NQ', category: 'index' },
|
|
56
|
+
{ name: 'MNQ - Micro E-mini NASDAQ-100', value: 'MNQ', searchText: 'MNQ', category: 'index' },
|
|
57
|
+
{ name: 'ES - E-mini S&P 500', value: 'ES', searchText: 'ES', category: 'index' },
|
|
58
|
+
{ name: 'MES - Micro E-mini S&P 500', value: 'MES', searchText: 'MES', category: 'index' },
|
|
59
|
+
{ name: 'YM - E-mini Dow Jones', value: 'YM', searchText: 'YM', category: 'index' },
|
|
60
|
+
{ name: 'MYM - Micro E-mini Dow Jones', value: 'MYM', searchText: 'MYM', category: 'index' },
|
|
61
|
+
{ name: 'RTY - E-mini Russell 2000', value: 'RTY', searchText: 'RTY', category: 'index' },
|
|
62
|
+
{ name: 'M2K - Micro E-mini Russell 2000', value: 'M2K', searchText: 'M2K', category: 'index' },
|
|
63
|
+
// Energy
|
|
64
|
+
{ name: 'CL - Crude Oil WTI', value: 'CL', searchText: 'CL', category: 'energy' },
|
|
65
|
+
{ name: 'MCL - Micro Crude Oil', value: 'MCL', searchText: 'MCL', category: 'energy' },
|
|
66
|
+
{ name: 'NG - Natural Gas', value: 'NG', searchText: 'NG', category: 'energy' },
|
|
67
|
+
{ name: 'QG - E-mini Natural Gas', value: 'QG', searchText: 'QG', category: 'energy' },
|
|
68
|
+
// Metals
|
|
69
|
+
{ name: 'GC - Gold', value: 'GC', searchText: 'GC', category: 'metals' },
|
|
70
|
+
{ name: 'MGC - Micro Gold', value: 'MGC', searchText: 'MGC', category: 'metals' },
|
|
71
|
+
{ name: 'SI - Silver', value: 'SI', searchText: 'SI', category: 'metals' },
|
|
72
|
+
{ name: 'SIL - Micro Silver', value: 'SIL', searchText: 'SIL', category: 'metals' },
|
|
73
|
+
{ name: 'HG - Copper', value: 'HG', searchText: 'HG', category: 'metals' },
|
|
74
|
+
{ name: 'PL - Platinum', value: 'PL', searchText: 'PL', category: 'metals' },
|
|
75
|
+
// Currencies
|
|
76
|
+
{ name: '6E - Euro FX', value: '6E', searchText: '6E', category: 'currency' },
|
|
77
|
+
{ name: 'M6E - Micro Euro FX', value: 'M6E', searchText: 'M6E', category: 'currency' },
|
|
78
|
+
{ name: '6B - British Pound', value: '6B', searchText: '6B', category: 'currency' },
|
|
79
|
+
{ name: '6J - Japanese Yen', value: '6J', searchText: '6J', category: 'currency' },
|
|
80
|
+
{ name: '6A - Australian Dollar', value: '6A', searchText: '6A', category: 'currency' },
|
|
81
|
+
{ name: '6C - Canadian Dollar', value: '6C', searchText: '6C', category: 'currency' },
|
|
82
|
+
// Bonds
|
|
83
|
+
{ name: 'ZB - 30-Year T-Bond', value: 'ZB', searchText: 'ZB', category: 'bonds' },
|
|
84
|
+
{ name: 'ZN - 10-Year T-Note', value: 'ZN', searchText: 'ZN', category: 'bonds' },
|
|
85
|
+
{ name: 'ZF - 5-Year T-Note', value: 'ZF', searchText: 'ZF', category: 'bonds' },
|
|
86
|
+
{ name: 'ZT - 2-Year T-Note', value: 'ZT', searchText: 'ZT', category: 'bonds' },
|
|
87
|
+
// Agriculture
|
|
88
|
+
{ name: 'ZC - Corn', value: 'ZC', searchText: 'ZC', category: 'agriculture' },
|
|
89
|
+
{ name: 'ZS - Soybeans', value: 'ZS', searchText: 'ZS', category: 'agriculture' },
|
|
90
|
+
{ name: 'ZW - Wheat', value: 'ZW', searchText: 'ZW', category: 'agriculture' }
|
|
66
91
|
];
|
|
67
92
|
|
|
68
93
|
module.exports = {
|
package/src/pages/algo.js
CHANGED
|
@@ -146,6 +146,7 @@ const oneAccountMenu = async (service) => {
|
|
|
146
146
|
const selectSymbolMenu = async (service, account) => {
|
|
147
147
|
const device = getDevice();
|
|
148
148
|
const accountName = account.accountName || account.name || 'Account #' + account.accountId;
|
|
149
|
+
const propfirm = account.propfirm || 'projectx';
|
|
149
150
|
|
|
150
151
|
console.log();
|
|
151
152
|
console.log(chalk.gray(getSeparator()));
|
|
@@ -153,12 +154,69 @@ const selectSymbolMenu = async (service, account) => {
|
|
|
153
154
|
console.log(chalk.gray(getSeparator()));
|
|
154
155
|
console.log();
|
|
155
156
|
|
|
156
|
-
//
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
// Fetch available symbols from API
|
|
158
|
+
const spinner = ora('Loading available symbols...').start();
|
|
159
|
+
|
|
160
|
+
let availableSymbols = [];
|
|
161
|
+
|
|
162
|
+
// Search for common symbols to get available contracts
|
|
163
|
+
const commonSearches = ['NQ', 'ES', 'YM', 'RTY', 'CL', 'GC', 'SI', '6E', 'ZB', 'NG'];
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
for (const search of commonSearches) {
|
|
167
|
+
const result = await service.searchContracts(search, false);
|
|
168
|
+
if (result.success && result.contracts && result.contracts.length > 0) {
|
|
169
|
+
for (const contract of result.contracts) {
|
|
170
|
+
// Only add active/front-month contracts
|
|
171
|
+
if (contract.activeContract || contract.name) {
|
|
172
|
+
const existing = availableSymbols.find(s => s.id === contract.id);
|
|
173
|
+
if (!existing) {
|
|
174
|
+
availableSymbols.push({
|
|
175
|
+
id: contract.id || contract.contractId,
|
|
176
|
+
name: contract.name || contract.symbol,
|
|
177
|
+
symbol: contract.symbol || search,
|
|
178
|
+
tickSize: contract.tickSize,
|
|
179
|
+
tickValue: contract.tickValue,
|
|
180
|
+
exchange: contract.exchange || 'CME'
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
} catch (e) {
|
|
188
|
+
// Fallback to static list
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// If no symbols found from API, use static list
|
|
192
|
+
if (availableSymbols.length === 0) {
|
|
193
|
+
spinner.warn('Using default symbol list');
|
|
194
|
+
availableSymbols = FUTURES_SYMBOLS.map(s => ({
|
|
195
|
+
id: s.value,
|
|
196
|
+
name: s.name,
|
|
197
|
+
symbol: s.value,
|
|
198
|
+
searchText: s.searchText
|
|
199
|
+
}));
|
|
200
|
+
} else {
|
|
201
|
+
spinner.succeed(`Found ${availableSymbols.length} available contracts`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
console.log();
|
|
205
|
+
|
|
206
|
+
// Format symbols for display
|
|
207
|
+
const symbolChoices = availableSymbols.map(symbol => {
|
|
208
|
+
// Extract short code and description
|
|
209
|
+
const name = symbol.name || symbol.symbol || symbol.id;
|
|
210
|
+
// Parse name like "E-mini NASDAQ-100 Mar26" or "NQH6"
|
|
211
|
+
let displayName = name;
|
|
212
|
+
let code = symbol.symbol || '';
|
|
213
|
+
|
|
214
|
+
// Try to extract month/year from name
|
|
215
|
+
const monthMatch = name.match(/(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)(\d{2})/i);
|
|
216
|
+
const monthYear = monthMatch ? `${monthMatch[1]}${monthMatch[2]}` : '';
|
|
217
|
+
|
|
160
218
|
return {
|
|
161
|
-
name: chalk.yellow(code) + chalk.
|
|
219
|
+
name: chalk.yellow(code.padEnd(5)) + chalk.cyan((monthYear || '').padEnd(7)) + chalk.white(displayName),
|
|
162
220
|
value: symbol
|
|
163
221
|
};
|
|
164
222
|
});
|
|
@@ -172,7 +230,7 @@ const selectSymbolMenu = async (service, account) => {
|
|
|
172
230
|
name: 'selectedSymbol',
|
|
173
231
|
message: chalk.white.bold('Select Symbol:'),
|
|
174
232
|
choices: symbolChoices,
|
|
175
|
-
pageSize:
|
|
233
|
+
pageSize: 20,
|
|
176
234
|
loop: false
|
|
177
235
|
}
|
|
178
236
|
]);
|
|
@@ -181,24 +239,32 @@ const selectSymbolMenu = async (service, account) => {
|
|
|
181
239
|
return;
|
|
182
240
|
}
|
|
183
241
|
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
const contractResult = await service.searchContracts(selectedSymbol.searchText, false);
|
|
242
|
+
// Use the selected contract directly (already fetched from API)
|
|
243
|
+
let contract = selectedSymbol;
|
|
187
244
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
245
|
+
console.log();
|
|
246
|
+
console.log(chalk.green(` [OK] Selected: ${contract.name || contract.symbol}`));
|
|
247
|
+
if (contract.tickSize && contract.tickValue) {
|
|
248
|
+
console.log(chalk.gray(` Tick Size: ${contract.tickSize} | Tick Value: $${contract.tickValue}`));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// If contract doesn't have full details, search again
|
|
252
|
+
if (!contract.id || !contract.tickSize) {
|
|
253
|
+
const searchSpinner = ora(`Getting contract details...`).start();
|
|
254
|
+
const contractResult = await service.searchContracts(contract.symbol || contract.searchText, false);
|
|
255
|
+
|
|
256
|
+
if (contractResult.success && contractResult.contracts && contractResult.contracts.length > 0) {
|
|
257
|
+
const found = contractResult.contracts.find(c => c.activeContract) || contractResult.contracts[0];
|
|
258
|
+
contract = { ...contract, ...found };
|
|
259
|
+
searchSpinner.succeed(`Contract: ${contract.name || contract.symbol}`);
|
|
260
|
+
} else {
|
|
261
|
+
searchSpinner.warn('Using basic contract info');
|
|
262
|
+
contract = {
|
|
263
|
+
id: contract.symbol || contract.id,
|
|
264
|
+
name: contract.name || contract.symbol,
|
|
265
|
+
symbol: contract.symbol || contract.id
|
|
266
|
+
};
|
|
194
267
|
}
|
|
195
|
-
} else {
|
|
196
|
-
spinner.warn(`Using ${selectedSymbol.value} (contract details unavailable)`);
|
|
197
|
-
contract = {
|
|
198
|
-
id: selectedSymbol.value,
|
|
199
|
-
name: selectedSymbol.name,
|
|
200
|
-
symbol: selectedSymbol.value
|
|
201
|
-
};
|
|
202
268
|
}
|
|
203
269
|
|
|
204
270
|
console.log();
|
|
@@ -389,8 +455,17 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
|
|
|
389
455
|
};
|
|
390
456
|
|
|
391
457
|
// Display full UI with logs (newest first at top)
|
|
458
|
+
// Use ANSI escape codes to avoid flickering
|
|
459
|
+
let firstDraw = true;
|
|
392
460
|
const displayUI = () => {
|
|
393
|
-
|
|
461
|
+
// First time: clear screen, after: just move cursor to top
|
|
462
|
+
if (firstDraw) {
|
|
463
|
+
console.clear();
|
|
464
|
+
firstDraw = false;
|
|
465
|
+
} else {
|
|
466
|
+
// Move cursor to top-left without clearing (avoids flicker)
|
|
467
|
+
process.stdout.write('\x1B[H');
|
|
468
|
+
}
|
|
394
469
|
|
|
395
470
|
// Stats
|
|
396
471
|
const pnlColor = stats.pnl >= 0 ? chalk.green : chalk.red;
|
|
@@ -518,16 +593,20 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
|
|
|
518
593
|
// Logs (without borders) - newest first
|
|
519
594
|
console.log();
|
|
520
595
|
if (logs.length === 0) {
|
|
521
|
-
console.log(chalk.gray(' Waiting for activity...'));
|
|
596
|
+
console.log(chalk.gray(' Waiting for activity...') + '\x1B[K'); // Clear to end of line
|
|
522
597
|
} else {
|
|
523
598
|
// Reverse to show newest first
|
|
524
599
|
const reversedLogs = [...logs].reverse();
|
|
525
600
|
reversedLogs.forEach(log => {
|
|
526
601
|
const color = typeColors[log.type] || chalk.white;
|
|
527
602
|
const icon = getIcon(log.type);
|
|
528
|
-
|
|
603
|
+
// Pad message and clear to end of line to avoid artifacts
|
|
604
|
+
const logLine = ` [${log.timestamp}] ${icon} ${log.message}`;
|
|
605
|
+
console.log(color(logLine) + '\x1B[K');
|
|
529
606
|
});
|
|
530
607
|
}
|
|
608
|
+
// Clear remaining lines below logs
|
|
609
|
+
process.stdout.write('\x1B[J');
|
|
531
610
|
};
|
|
532
611
|
|
|
533
612
|
// Spinner interval to refresh UI
|