hedgequantx 1.8.7 → 1.8.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "1.8.7",
3
+ "version": "1.8.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": {
@@ -133,15 +133,10 @@ const copyTradingMenu = async () => {
133
133
  });
134
134
  };
135
135
 
136
- // Cached contracts from API
137
- let cachedContracts = null;
138
-
139
136
  /**
140
137
  * Get contracts from ProjectX API (shared for all services)
141
138
  */
142
139
  const getContractsFromAPI = async () => {
143
- if (cachedContracts) return cachedContracts;
144
-
145
140
  // Find a ProjectX connection to get contracts from API
146
141
  const allConns = connections.getAll();
147
142
  const projectxConn = allConns.find(c => c.type === 'projectx');
@@ -149,8 +144,12 @@ const getContractsFromAPI = async () => {
149
144
  if (projectxConn && typeof projectxConn.service.getContracts === 'function') {
150
145
  const result = await projectxConn.service.getContracts();
151
146
  if (result.success && result.contracts?.length > 0) {
152
- cachedContracts = result.contracts;
153
- return cachedContracts;
147
+ // Normalize contract structure - API returns { name: "ESH6", description: "E-mini S&P 500..." }
148
+ return result.contracts.map(c => ({
149
+ ...c,
150
+ symbol: c.name || c.symbol,
151
+ name: c.description || c.name || c.symbol
152
+ }));
154
153
  }
155
154
  }
156
155
 
@@ -162,6 +161,8 @@ const getContractsFromAPI = async () => {
162
161
  */
163
162
  const selectSymbol = async (service, label) => {
164
163
  try {
164
+ const spinner = ora({ text: 'Loading symbols...', color: 'yellow' }).start();
165
+
165
166
  // Always use contracts from ProjectX API for consistency
166
167
  let contracts = await getContractsFromAPI();
167
168
 
@@ -174,15 +175,18 @@ const selectSymbol = async (service, label) => {
174
175
  }
175
176
 
176
177
  if (!contracts || contracts.length === 0) {
177
- console.log(chalk.red(' No contracts available'));
178
+ spinner.fail('No contracts available');
178
179
  return null;
179
180
  }
180
181
 
182
+ spinner.succeed(`Found ${contracts.length} contracts`);
183
+
181
184
  const options = contracts.map(c => ({ label: c.name || c.symbol, value: c }));
182
185
  options.push({ label: '< Cancel', value: null });
183
186
 
184
187
  return await prompts.selectOption(`${label} Symbol:`, options);
185
188
  } catch (e) {
189
+ console.log(chalk.red(' Error loading contracts'));
186
190
  return null;
187
191
  }
188
192
  };
@@ -444,29 +444,26 @@ class ProjectXService {
444
444
  // ==================== CONTRACTS ====================
445
445
 
446
446
  /**
447
- * Get popular contracts for trading from API
447
+ * Get all available contracts from API
448
448
  */
449
449
  async getContracts() {
450
450
  try {
451
- // Search for popular futures symbols from API
452
- const symbols = ['ES', 'NQ', 'MES', 'MNQ', 'CL', 'GC', 'RTY', 'YM', 'SI', 'ZB', 'ZN', 'NG', 'MCL', 'MGC'];
453
- const allContracts = [];
451
+ const response = await this._request(
452
+ this.propfirm.gatewayApi, '/api/Contract/available', 'POST',
453
+ { live: false }
454
+ );
454
455
 
455
- for (const sym of symbols) {
456
- const response = await this._request(
457
- this.propfirm.gatewayApi, '/api/Contract/search', 'POST',
458
- { searchText: sym, live: false }
459
- );
460
- if (response.statusCode === 200) {
461
- const contracts = response.data.contracts || response.data || [];
462
- // Take first contract for each symbol (front month)
463
- if (contracts.length > 0) {
464
- allContracts.push(contracts[0]);
465
- }
466
- }
456
+ if (response.statusCode === 200) {
457
+ const contracts = response.data.contracts || response.data || [];
458
+ // Filter only active contracts and sort by description
459
+ const activeContracts = contracts
460
+ .filter(c => c.activeContract === true)
461
+ .sort((a, b) => (a.description || '').localeCompare(b.description || ''));
462
+
463
+ return { success: true, contracts: activeContracts };
467
464
  }
468
465
 
469
- return { success: true, contracts: allContracts };
466
+ return { success: false, contracts: [], error: 'Failed to fetch contracts' };
470
467
  } catch (error) {
471
468
  return { success: false, contracts: [], error: error.message };
472
469
  }
@@ -54,16 +54,21 @@ const passwordInput = async (message) => {
54
54
  };
55
55
 
56
56
  /**
57
- * Confirm Y/n
57
+ * Confirm - arrow keys selection
58
58
  */
59
59
  const confirmPrompt = async (message, defaultVal = true) => {
60
60
  prepareStdin();
61
+ const choices = defaultVal
62
+ ? [{ name: 'Yes', value: true }, { name: 'No', value: false }]
63
+ : [{ name: 'No', value: false }, { name: 'Yes', value: true }];
64
+
61
65
  const { value } = await inquirer.prompt([{
62
- type: 'confirm',
66
+ type: 'list',
63
67
  name: 'value',
64
68
  message,
65
- default: defaultVal,
66
- prefix: ''
69
+ choices,
70
+ prefix: '',
71
+ loop: false
67
72
  }]);
68
73
  return value;
69
74
  };