hedgequantx 1.5.4 → 1.5.6

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.5.4",
3
+ "version": "1.5.6",
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": {
@@ -136,33 +136,41 @@ const copyTradingMenu = async () => {
136
136
  console.log();
137
137
  console.log(chalk.cyan(' Step 5: Configure Parameters'));
138
138
 
139
- const { leadContracts } = await inquirer.prompt([{
140
- type: 'number',
141
- name: 'leadContracts',
139
+ const { leadContractsInput } = await inquirer.prompt([{
140
+ type: 'input',
141
+ name: 'leadContractsInput',
142
142
  message: 'Lead contracts:',
143
- default: 1
143
+ default: '1',
144
+ validate: v => !isNaN(parseInt(v)) && parseInt(v) > 0 ? true : 'Enter a positive number'
144
145
  }]);
146
+ const leadContracts = parseInt(leadContractsInput) || 1;
145
147
 
146
- const { followerContracts } = await inquirer.prompt([{
147
- type: 'number',
148
- name: 'followerContracts',
148
+ const { followerContractsInput } = await inquirer.prompt([{
149
+ type: 'input',
150
+ name: 'followerContractsInput',
149
151
  message: 'Follower contracts:',
150
- default: leadContracts
152
+ default: String(leadContracts),
153
+ validate: v => !isNaN(parseInt(v)) && parseInt(v) > 0 ? true : 'Enter a positive number'
151
154
  }]);
155
+ const followerContracts = parseInt(followerContractsInput) || leadContracts;
152
156
 
153
- const { dailyTarget } = await inquirer.prompt([{
154
- type: 'number',
155
- name: 'dailyTarget',
157
+ const { dailyTargetInput } = await inquirer.prompt([{
158
+ type: 'input',
159
+ name: 'dailyTargetInput',
156
160
  message: 'Daily target ($):',
157
- default: 400
161
+ default: '400',
162
+ validate: v => !isNaN(parseInt(v)) && parseInt(v) > 0 ? true : 'Enter a positive number'
158
163
  }]);
164
+ const dailyTarget = parseInt(dailyTargetInput) || 400;
159
165
 
160
- const { maxRisk } = await inquirer.prompt([{
161
- type: 'number',
162
- name: 'maxRisk',
166
+ const { maxRiskInput } = await inquirer.prompt([{
167
+ type: 'input',
168
+ name: 'maxRiskInput',
163
169
  message: 'Max risk ($):',
164
- default: 200
170
+ default: '200',
171
+ validate: v => !isNaN(parseInt(v)) && parseInt(v) > 0 ? true : 'Enter a positive number'
165
172
  }]);
173
+ const maxRisk = parseInt(maxRiskInput) || 200;
166
174
 
167
175
  // Step 6: Privacy
168
176
  const { showNames } = await inquirer.prompt([{
@@ -146,11 +146,15 @@ class AlgoUI {
146
146
 
147
147
  this._line(chalk.cyan(GT));
148
148
 
149
- // Row 1: Account | Symbol
150
- const r1c1 = buildCell('Account', stats.accountName || 'N/A', chalk.cyan, colL);
151
- const r1c2t = ` Symbol: ${chalk.yellow(stats.symbol || 'N/A')} Qty: ${chalk.cyan(stats.contracts || 1)}`;
152
- const r1c2p = ` Symbol: ${stats.symbol || 'N/A'} Qty: ${stats.contracts || 1}`;
153
- row(r1c1.padded, r1c2t + pad(colR - r1c2p.length));
149
+ // Row 1: Account | Symbol (truncate long values)
150
+ const accName = (stats.accountName || 'N/A').substring(0, 35);
151
+ const symName = (stats.symbol || 'N/A').substring(0, 25);
152
+ const qtyStr = stats.contracts || '1/1';
153
+
154
+ const r1c1 = buildCell('Account', accName, chalk.cyan, colL);
155
+ const r1c2t = ` Symbol: ${chalk.yellow(symName)} Qty: ${chalk.cyan(qtyStr)}`;
156
+ const r1c2p = ` Symbol: ${symName} Qty: ${qtyStr}`;
157
+ row(r1c1.padded, r1c2t + pad(Math.max(0, colR - r1c2p.length)));
154
158
 
155
159
  this._line(chalk.cyan(GM));
156
160
 
@@ -193,9 +197,9 @@ class AlgoUI {
193
197
  this._line(chalk.cyan(BOX.V) + chalk.white(left) + ' '.repeat(midPad) + chalk.cyan(mid) + ' '.repeat(space - midPad - mid.length) + chalk.yellow(right) + chalk.cyan(BOX.V));
194
198
  this._line(chalk.cyan(BOX.ML + BOX.H.repeat(W) + BOX.MR));
195
199
 
196
- // Logs (oldest at top, newest at bottom - like a terminal)
197
- // Take the last maxLogs entries (most recent), keep chronological order
198
- const visible = logs.slice(-maxLogs);
200
+ // Logs: newest at top, oldest at bottom
201
+ // Take the last maxLogs entries and reverse for display
202
+ const visible = logs.slice(-maxLogs).reverse();
199
203
 
200
204
  if (visible.length === 0) {
201
205
  this._line(chalk.cyan(BOX.V) + chalk.gray(fitToWidth(' Waiting for activity...', W)) + chalk.cyan(BOX.V));
@@ -203,17 +207,17 @@ class AlgoUI {
203
207
  this._line(chalk.cyan(BOX.V) + ' '.repeat(W) + chalk.cyan(BOX.V));
204
208
  }
205
209
  } else {
206
- // First draw empty lines for padding (so logs stick to bottom)
207
- for (let i = visible.length; i < maxLogs; i++) {
208
- this._line(chalk.cyan(BOX.V) + ' '.repeat(W) + chalk.cyan(BOX.V));
209
- }
210
- // Then draw logs (oldest first, newest last/at bottom)
210
+ // Draw logs (newest first at top)
211
211
  visible.forEach(log => {
212
212
  const color = LOG_COLORS[log.type] || chalk.white;
213
213
  const icon = LOG_ICONS[log.type] || LOG_ICONS.info;
214
214
  const line = ` [${log.timestamp}] ${icon} ${log.message}`;
215
215
  this._line(chalk.cyan(BOX.V) + color(fitToWidth(line, W)) + chalk.cyan(BOX.V));
216
216
  });
217
+ // Pad remaining lines at bottom
218
+ for (let i = visible.length; i < maxLogs; i++) {
219
+ this._line(chalk.cyan(BOX.V) + ' '.repeat(W) + chalk.cyan(BOX.V));
220
+ }
217
221
  }
218
222
 
219
223
  // Bottom border
@@ -210,15 +210,32 @@ class RithmicService extends EventEmitter {
210
210
  };
211
211
  }
212
212
 
213
- async searchContracts(searchText) {
214
- const contracts = [
215
- { symbol: 'ESH5', name: 'E-mini S&P 500 Mar 2025', exchange: 'CME' },
216
- { symbol: 'NQH5', name: 'E-mini NASDAQ-100 Mar 2025', exchange: 'CME' },
217
- { symbol: 'CLH5', name: 'Crude Oil Mar 2025', exchange: 'NYMEX' },
218
- { symbol: 'GCG5', name: 'Gold Feb 2025', exchange: 'COMEX' },
219
- { symbol: 'MESH5', name: 'Micro E-mini S&P 500 Mar 2025', exchange: 'CME' },
220
- { symbol: 'MNQH5', name: 'Micro E-mini NASDAQ-100 Mar 2025', exchange: 'CME' },
213
+ // All available contracts for Rithmic
214
+ _getAvailableContracts() {
215
+ return [
216
+ { symbol: 'ESH5', name: 'E-mini S&P 500 Mar 2025', exchange: 'CME', group: 'Index' },
217
+ { symbol: 'NQH5', name: 'E-mini NASDAQ-100 Mar 2025', exchange: 'CME', group: 'Index' },
218
+ { symbol: 'MESH5', name: 'Micro E-mini S&P 500 Mar 2025', exchange: 'CME', group: 'Micro' },
219
+ { symbol: 'MNQH5', name: 'Micro E-mini NASDAQ-100 Mar 2025', exchange: 'CME', group: 'Micro' },
220
+ { symbol: 'MCLE5', name: 'Micro Crude Oil Mar 2025', exchange: 'NYMEX', group: 'Micro' },
221
+ { symbol: 'MGCG5', name: 'Micro Gold Feb 2025', exchange: 'COMEX', group: 'Micro' },
222
+ { symbol: 'CLH5', name: 'Crude Oil Mar 2025', exchange: 'NYMEX', group: 'Energy' },
223
+ { symbol: 'GCG5', name: 'Gold Feb 2025', exchange: 'COMEX', group: 'Metals' },
224
+ { symbol: 'SIH5', name: 'Silver Mar 2025', exchange: 'COMEX', group: 'Metals' },
225
+ { symbol: 'RTYH5', name: 'E-mini Russell 2000 Mar 2025', exchange: 'CME', group: 'Index' },
226
+ { symbol: 'YMH5', name: 'E-mini Dow Jones Mar 2025', exchange: 'CBOT', group: 'Index' },
227
+ { symbol: 'ZBH5', name: '30-Year US Treasury Bond Mar 2025', exchange: 'CBOT', group: 'Bonds' },
228
+ { symbol: 'ZNH5', name: '10-Year US Treasury Note Mar 2025', exchange: 'CBOT', group: 'Bonds' },
221
229
  ];
230
+ }
231
+
232
+ async getContracts() {
233
+ return { success: true, contracts: this._getAvailableContracts() };
234
+ }
235
+
236
+ async searchContracts(searchText) {
237
+ const contracts = this._getAvailableContracts();
238
+ if (!searchText) return contracts;
222
239
  const search = searchText.toUpperCase();
223
240
  return contracts.filter(c => c.symbol.includes(search) || c.name.toUpperCase().includes(search));
224
241
  }