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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "1.2.87",
3
+ "version": "1.2.89",
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": {
@@ -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
- { name: 'NQ - E-mini NASDAQ-100', value: 'NQ', searchText: 'NQ' },
55
- { name: 'MNQ - Micro E-mini NASDAQ-100', value: 'MNQ', searchText: 'MNQ' },
56
- { name: 'ES - E-mini S&P 500', value: 'ES', searchText: 'ES' },
57
- { name: 'MES - Micro E-mini S&P 500', value: 'MES', searchText: 'MES' },
58
- { name: 'YM - E-mini Dow Jones', value: 'YM', searchText: 'YM' },
59
- { name: 'MYM - Micro E-mini Dow Jones', value: 'MYM', searchText: 'MYM' },
60
- { name: 'RTY - E-mini Russell 2000', value: 'RTY', searchText: 'RTY' },
61
- { name: 'M2K - Micro E-mini Russell 2000', value: 'M2K', searchText: 'M2K' },
62
- { name: 'CL - Crude Oil', value: 'CL', searchText: 'CL' },
63
- { name: 'MCL - Micro Crude Oil', value: 'MCL', searchText: 'MCL' },
64
- { name: 'GC - Gold', value: 'GC', searchText: 'GC' },
65
- { name: 'MGC - Micro Gold', value: 'MGC', searchText: 'MGC' }
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
- // Format symbols with aligned columns: "NQ - E-mini NASDAQ-100"
157
- const symbolChoices = FUTURES_SYMBOLS.map(symbol => {
158
- const code = symbol.value.padEnd(4);
159
- const desc = symbol.name.split(' - ')[1] || '';
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.gray(' - ') + chalk.white(desc),
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: 15,
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
- // Search contract via Gateway API
185
- const spinner = ora(`Searching for ${selectedSymbol.value} contract...`).start();
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
- let contract = null;
189
- if (contractResult.success && contractResult.contracts && contractResult.contracts.length > 0) {
190
- contract = contractResult.contracts.find(c => c.activeContract) || contractResult.contracts[0];
191
- spinner.succeed(`Found: ${contract.name || selectedSymbol.value}`);
192
- if (contract.tickSize && contract.tickValue) {
193
- console.log(chalk.gray(` Tick Size: ${contract.tickSize} | Tick Value: $${contract.tickValue}`));
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
- console.clear();
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
- console.log(color(` [${log.timestamp}] ${icon} ${log.message}`));
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