hedgequantx 1.8.30 → 1.8.31

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.30",
3
+ "version": "1.8.31",
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": {
@@ -71,11 +71,11 @@ const projectXMenu = async () => {
71
71
  console.log(chalk.cyan('║') + ' ' + chalk.red('[X] Back') + ' '.repeat(W - 10) + chalk.cyan('║'));
72
72
  console.log(chalk.cyan('╚' + '═'.repeat(W) + '╝'));
73
73
 
74
- const options = numbered.map(pf => ({ value: pf.num, label: `[${pf.num}] ${pf.name}` }));
75
- options.push({ value: -1, label: '[X] Back' });
74
+ const input = await prompts.textInput(chalk.cyan('Select number (or X):'));
75
+ if (!input || input.toLowerCase() === 'x') return null;
76
76
 
77
- const action = await prompts.selectOption('Select:', options);
78
- if (!action || action === -1) return null;
77
+ const action = parseInt(input);
78
+ if (isNaN(action) || action < 1 || action > numbered.length) return null;
79
79
 
80
80
  const selectedPropfirm = numbered[action - 1];
81
81
  const credentials = await loginPrompt(selectedPropfirm.name);
@@ -142,11 +142,11 @@ const rithmicMenu = async () => {
142
142
  console.log(chalk.cyan('║') + ' ' + chalk.red('[X] Back') + ' '.repeat(innerWidth - 10) + chalk.cyan('║'));
143
143
  console.log(chalk.cyan('╚' + '═'.repeat(innerWidth) + '╝'));
144
144
 
145
- const options = numbered.map(pf => ({ value: pf.num, label: `[${pf.num}] ${pf.name}` }));
146
- options.push({ value: -1, label: '[X] Back' });
145
+ const input = await prompts.textInput(chalk.cyan('Select number (or X):'));
146
+ if (!input || input.toLowerCase() === 'x') return null;
147
147
 
148
- const action = await prompts.selectOption('Select:', options);
149
- if (!action || action === -1) return null;
148
+ const action = parseInt(input);
149
+ if (isNaN(action) || action < 1 || action > numbered.length) return null;
150
150
 
151
151
  const selectedPropfirm = numbered[action - 1];
152
152
  const credentials = await loginPrompt(selectedPropfirm.name);
@@ -203,11 +203,11 @@ const tradovateMenu = async () => {
203
203
  console.log(chalk.cyan('║') + ' ' + chalk.red('[X] Back') + ' '.repeat(innerWidth - 10) + chalk.cyan('║'));
204
204
  console.log(chalk.cyan('╚' + '═'.repeat(innerWidth) + '╝'));
205
205
 
206
- const options = numbered.map(pf => ({ value: pf.num, label: `[${pf.num}] ${pf.name}` }));
207
- options.push({ value: -1, label: '[X] Back' });
206
+ const input = await prompts.textInput(chalk.cyan('Select number (or X):'));
207
+ if (!input || input.toLowerCase() === 'x') return null;
208
208
 
209
- const action = await prompts.selectOption('Select:', options);
210
- if (!action || action === -1) return null;
209
+ const action = parseInt(input);
210
+ if (isNaN(action) || action < 1 || action > numbered.length) return null;
211
211
 
212
212
  const selectedPropfirm = numbered[action - 1];
213
213
  const credentials = await loginPrompt(selectedPropfirm.name);
@@ -261,12 +261,14 @@ const addPropAccountMenu = async () => {
261
261
 
262
262
  console.log(chalk.cyan('╚' + '═'.repeat(W) + '╝'));
263
263
 
264
- return await prompts.selectOption('Select:', [
265
- { value: 'projectx', label: '[1] ProjectX' },
266
- { value: 'rithmic', label: '[2] Rithmic' },
267
- { value: 'tradovate', label: '[3] Tradovate' },
268
- { value: null, label: '[X] Back' }
269
- ]);
264
+ const input = await prompts.textInput(chalk.cyan('Select (1/2/3/X):'));
265
+ if (!input || input.toLowerCase() === 'x') return null;
266
+
267
+ const num = parseInt(input);
268
+ if (num === 1) return 'projectx';
269
+ if (num === 2) return 'rithmic';
270
+ if (num === 3) return 'tradovate';
271
+ return null;
270
272
  };
271
273
 
272
274
  module.exports = { loginPrompt, projectXMenu, rithmicMenu, tradovateMenu, addPropAccountMenu };
@@ -216,18 +216,46 @@ class RithmicService extends EventEmitter {
216
216
  * For now, returns common futures contracts that are available on Rithmic
217
217
  */
218
218
  async getContracts() {
219
- // Standard futures contracts - same list as ProjectX for consistency
219
+ // Calculate current front month dynamically
220
+ const now = new Date();
221
+ const month = now.getMonth();
222
+ const year = now.getFullYear() % 100;
223
+
224
+ // Quarterly months for index futures: H=Mar, M=Jun, U=Sep, Z=Dec
225
+ const quarterlyMonths = ['H', 'M', 'U', 'Z'];
226
+ const monthNames = ['Mar', 'Jun', 'Sep', 'Dec'];
227
+ const quarterIndex = Math.floor(month / 3);
228
+
229
+ // If past 15th of expiry month, use next quarter
230
+ let frontMonth = quarterlyMonths[quarterIndex];
231
+ let frontMonthName = monthNames[quarterIndex];
232
+ let frontYear = year;
233
+
234
+ if (now.getDate() > 15 && [2, 5, 8, 11].includes(month)) {
235
+ const nextIdx = (quarterIndex + 1) % 4;
236
+ frontMonth = quarterlyMonths[nextIdx];
237
+ frontMonthName = monthNames[nextIdx];
238
+ if (nextIdx === 0) frontYear++;
239
+ }
240
+
241
+ const y = frontYear; // e.g., 26 for 2026
242
+ const fy = `${frontYear}`; // full year string
243
+
220
244
  const contracts = [
221
- { symbol: 'ESH5', name: 'E-mini S&P 500 (Mar 25)', exchange: 'CME' },
222
- { symbol: 'NQH5', name: 'E-mini NASDAQ-100 (Mar 25)', exchange: 'CME' },
223
- { symbol: 'MESH5', name: 'Micro E-mini S&P 500 (Mar 25)', exchange: 'CME' },
224
- { symbol: 'MNQH5', name: 'Micro E-mini NASDAQ-100 (Mar 25)', exchange: 'CME' },
225
- { symbol: 'CLG5', name: 'Crude Oil (Feb 25)', exchange: 'NYMEX' },
226
- { symbol: 'GCG5', name: 'Gold (Feb 25)', exchange: 'COMEX' },
227
- { symbol: 'RTYH5', name: 'E-mini Russell 2000 (Mar 25)', exchange: 'CME' },
228
- { symbol: 'YMH5', name: 'E-mini Dow Jones (Mar 25)', exchange: 'CBOT' },
229
- { symbol: 'SIH5', name: 'Silver (Mar 25)', exchange: 'COMEX' },
230
- { symbol: 'NGG5', name: 'Natural Gas (Feb 25)', exchange: 'NYMEX' },
245
+ { symbol: `ES${frontMonth}${y}`, name: `E-mini S&P 500 (${frontMonthName} ${fy})`, exchange: 'CME' },
246
+ { symbol: `NQ${frontMonth}${y}`, name: `E-mini NASDAQ-100 (${frontMonthName} ${fy})`, exchange: 'CME' },
247
+ { symbol: `MES${frontMonth}${y}`, name: `Micro E-mini S&P 500 (${frontMonthName} ${fy})`, exchange: 'CME' },
248
+ { symbol: `MNQ${frontMonth}${y}`, name: `Micro E-mini NASDAQ-100 (${frontMonthName} ${fy})`, exchange: 'CME' },
249
+ { symbol: `RTY${frontMonth}${y}`, name: `E-mini Russell 2000 (${frontMonthName} ${fy})`, exchange: 'CME' },
250
+ { symbol: `M2K${frontMonth}${y}`, name: `Micro E-mini Russell 2000 (${frontMonthName} ${fy})`, exchange: 'CME' },
251
+ { symbol: `YM${frontMonth}${y}`, name: `E-mini Dow Jones (${frontMonthName} ${fy})`, exchange: 'CBOT' },
252
+ { symbol: `MYM${frontMonth}${y}`, name: `Micro E-mini Dow Jones (${frontMonthName} ${fy})`, exchange: 'CBOT' },
253
+ { symbol: `CL${frontMonth}${y}`, name: `Crude Oil (${frontMonthName} ${fy})`, exchange: 'NYMEX' },
254
+ { symbol: `MCL${frontMonth}${y}`, name: `Micro Crude Oil (${frontMonthName} ${fy})`, exchange: 'NYMEX' },
255
+ { symbol: `GC${frontMonth}${y}`, name: `Gold (${frontMonthName} ${fy})`, exchange: 'COMEX' },
256
+ { symbol: `MGC${frontMonth}${y}`, name: `Micro Gold (${frontMonthName} ${fy})`, exchange: 'COMEX' },
257
+ { symbol: `SI${frontMonth}${y}`, name: `Silver (${frontMonthName} ${fy})`, exchange: 'COMEX' },
258
+ { symbol: `NG${frontMonth}${y}`, name: `Natural Gas (${frontMonthName} ${fy})`, exchange: 'NYMEX' },
231
259
  ];
232
260
  return { success: true, contracts };
233
261
  }