hedgequantx 2.7.92 → 2.7.94

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": "2.7.92",
3
+ "version": "2.7.94",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
package/src/app.js CHANGED
@@ -199,9 +199,9 @@ const run = async () => {
199
199
 
200
200
  // Find max name length for alignment
201
201
  const maxNameLen = Math.max(...numbered.map(n => n.name.length));
202
- const colWidth = 4 + 1 + maxNameLen + 2; // [##] + space + name + gap
203
- const totalContentWidth = numCols * colWidth;
204
- const leftMargin = Math.max(2, Math.floor((innerWidth - totalContentWidth) / 2));
202
+ const itemWidth = 4 + 1 + maxNameLen; // [##] + space + name
203
+ const gap = 3; // gap between columns
204
+ const totalContentWidth = (itemWidth * numCols) + (gap * (numCols - 1));
205
205
 
206
206
  // New rectangle (banner is always closed)
207
207
  console.log(chalk.cyan('╔' + '═'.repeat(innerWidth) + '╗'));
@@ -223,19 +223,22 @@ const run = async () => {
223
223
  }
224
224
  }
225
225
 
226
- let line = ' '.repeat(leftMargin);
226
+ // Build line content
227
+ let content = '';
227
228
  for (let i = 0; i < lineParts.length; i++) {
228
229
  if (lineParts[i]) {
229
- line += chalk.cyan(lineParts[i].num) + ' ' + chalk.white(lineParts[i].name);
230
+ content += chalk.cyan(lineParts[i].num) + ' ' + chalk.white(lineParts[i].name);
230
231
  } else {
231
- line += ' '.repeat(4 + 1 + maxNameLen);
232
+ content += ' '.repeat(itemWidth);
232
233
  }
233
- if (i < lineParts.length - 1) line += ' ';
234
+ if (i < lineParts.length - 1) content += ' '.repeat(gap);
234
235
  }
235
236
 
236
- const lineLen = line.replace(/\x1b\[[0-9;]*m/g, '').length;
237
- const rightPad = Math.max(0, innerWidth - lineLen);
238
- console.log(chalk.cyan('║') + line + ' '.repeat(rightPad) + chalk.cyan('║'));
237
+ // Center the content
238
+ const contentLen = content.replace(/\x1b\[[0-9;]*m/g, '').length;
239
+ const leftPad = Math.floor((innerWidth - contentLen) / 2);
240
+ const rightPad = innerWidth - contentLen - leftPad;
241
+ console.log(chalk.cyan('║') + ' '.repeat(leftPad) + content + ' '.repeat(rightPad) + chalk.cyan('║'));
239
242
  }
240
243
 
241
244
  console.log(chalk.cyan('╠' + '─'.repeat(innerWidth) + '╣'));
@@ -97,12 +97,20 @@ const showAccounts = async (service) => {
97
97
  const name1 = String(acc1.accountName || acc1.rithmicAccountId || acc1.accountId || `Account #${i + 1}`);
98
98
  const name2 = acc2 ? String(acc2.accountName || acc2.rithmicAccountId || acc2.accountId || `Account #${i + 2}`) : '';
99
99
 
100
- draw2ColHeader(name1.substring(0, col1 - 4), name2 ? name2.substring(0, col2 - 4) : '', boxWidth);
100
+ // For single account, use full width; for pairs, use 2-column layout
101
+ const sep = acc2 ? '│' : '║';
102
+ const rightCol = acc2 ? col2 : col2;
103
+
104
+ // Header row with account name(s)
105
+ const h1 = centerText(name1.substring(0, col1 - 4), col1);
106
+ const h2 = acc2 ? centerText(name2.substring(0, col2 - 4), col2) : ' '.repeat(col2);
107
+ console.log(chalk.cyan('║') + chalk.cyan.bold(h1) + chalk.cyan(sep) + chalk.cyan.bold(h2) + chalk.cyan('║'));
108
+ console.log(chalk.cyan('╠') + chalk.cyan('─'.repeat(col1)) + chalk.cyan(acc2 ? '┼' : '┼') + chalk.cyan('─'.repeat(col2)) + chalk.cyan('╣'));
101
109
 
102
110
  // PropFirm
103
111
  const pf1 = chalk.magenta(acc1.propfirm || 'Unknown');
104
112
  const pf2 = acc2 ? chalk.magenta(acc2.propfirm || 'Unknown') : '';
105
- console.log(chalk.cyan('║') + fmtRow('PropFirm:', pf1, col1) + chalk.cyan('│') + (acc2 ? fmtRow('PropFirm:', pf2, col2) : ' '.repeat(col2)) + chalk.cyan('║'));
113
+ console.log(chalk.cyan('║') + fmtRow('PropFirm:', pf1, col1) + chalk.cyan(sep) + (acc2 ? fmtRow('PropFirm:', pf2, col2) : ' '.repeat(col2)) + chalk.cyan('║'));
106
114
 
107
115
  // Balance
108
116
  const bal1 = acc1.balance;
@@ -111,7 +119,7 @@ const showAccounts = async (service) => {
111
119
  const balStr2 = bal2 !== null && bal2 !== undefined ? '$' + Number(bal2).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) : '--';
112
120
  const balColor1 = bal1 === null || bal1 === undefined ? chalk.gray : (bal1 >= 0 ? chalk.green : chalk.red);
113
121
  const balColor2 = bal2 === null || bal2 === undefined ? chalk.gray : (bal2 >= 0 ? chalk.green : chalk.red);
114
- console.log(chalk.cyan('║') + fmtRow('Balance:', balColor1(balStr1), col1) + chalk.cyan('│') + (acc2 ? fmtRow('Balance:', balColor2(balStr2), col2) : ' '.repeat(col2)) + chalk.cyan('║'));
122
+ console.log(chalk.cyan('║') + fmtRow('Balance:', balColor1(balStr1), col1) + chalk.cyan(sep) + (acc2 ? fmtRow('Balance:', balColor2(balStr2), col2) : ' '.repeat(col2)) + chalk.cyan('║'));
115
123
 
116
124
  // P&L
117
125
  const pnl1 = acc1.profitAndLoss;
@@ -120,32 +128,33 @@ const showAccounts = async (service) => {
120
128
  const pnlStr2 = pnl2 !== null && pnl2 !== undefined ? (pnl2 >= 0 ? '+' : '') + '$' + Number(pnl2).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) : '--';
121
129
  const pnlColor1 = pnl1 === null || pnl1 === undefined ? chalk.gray : (pnl1 >= 0 ? chalk.green : chalk.red);
122
130
  const pnlColor2 = pnl2 === null || pnl2 === undefined ? chalk.gray : (pnl2 >= 0 ? chalk.green : chalk.red);
123
- console.log(chalk.cyan('║') + fmtRow('P&L:', pnlColor1(pnlStr1), col1) + chalk.cyan('│') + (acc2 ? fmtRow('P&L:', pnlColor2(pnlStr2), col2) : ' '.repeat(col2)) + chalk.cyan('║'));
131
+ console.log(chalk.cyan('║') + fmtRow('P&L:', pnlColor1(pnlStr1), col1) + chalk.cyan(sep) + (acc2 ? fmtRow('P&L:', pnlColor2(pnlStr2), col2) : ' '.repeat(col2)) + chalk.cyan('║'));
124
132
 
125
- // Status - handle both string from API and numeric lookup
133
+ // Status - numeric 0 = Active for Rithmic
126
134
  const getStatusDisplay = (status) => {
127
- if (!status && status !== 0) return { text: '--', color: 'gray' };
135
+ if (status === null || status === undefined) return { text: '--', color: 'gray' };
136
+ if (status === 0) return { text: 'Active', color: 'green' };
137
+ if (typeof status === 'number') {
138
+ return ACCOUNT_STATUS[status] || { text: `Status ${status}`, color: 'yellow' };
139
+ }
128
140
  if (typeof status === 'string') {
129
- // Direct string from Rithmic API (e.g., "Active", "Disabled")
130
141
  const lowerStatus = status.toLowerCase();
131
142
  if (lowerStatus.includes('active') || lowerStatus.includes('open')) return { text: status, color: 'green' };
132
143
  if (lowerStatus.includes('disabled') || lowerStatus.includes('closed')) return { text: status, color: 'red' };
133
144
  if (lowerStatus.includes('halt')) return { text: status, color: 'red' };
134
145
  return { text: status, color: 'yellow' };
135
146
  }
136
- return ACCOUNT_STATUS[status] || { text: 'Unknown', color: 'gray' };
147
+ return { text: '--', color: 'gray' };
137
148
  };
138
149
  const status1 = getStatusDisplay(acc1.status);
139
150
  const status2 = acc2 ? getStatusDisplay(acc2.status) : null;
140
- console.log(chalk.cyan('║') + fmtRow('Status:', chalk[status1.color](status1.text), col1) + chalk.cyan('│') + (acc2 ? fmtRow('Status:', chalk[status2.color](status2.text), col2) : ' '.repeat(col2)) + chalk.cyan('║'));
151
+ console.log(chalk.cyan('║') + fmtRow('Status:', chalk[status1.color](status1.text), col1) + chalk.cyan(sep) + (acc2 ? fmtRow('Status:', chalk[status2.color](status2.text), col2) : ' '.repeat(col2)) + chalk.cyan('║'));
141
152
 
142
- // Type/Algorithm - handle both string from API and numeric lookup
143
- const getTypeDisplay = (type, algorithm) => {
144
- // Prefer algorithm from RMS info if available
145
- const value = algorithm || type;
146
- if (!value && value !== 0) return { text: '--', color: 'gray' };
153
+ // Type - from accountType or algorithm field
154
+ const getTypeDisplay = (acc) => {
155
+ const value = acc.algorithm || acc.accountType || acc.type;
156
+ if (value === null || value === undefined) return { text: 'Live', color: 'green' }; // Default for Rithmic
147
157
  if (typeof value === 'string') {
148
- // Direct string from Rithmic API
149
158
  const lowerValue = value.toLowerCase();
150
159
  if (lowerValue.includes('eval')) return { text: value, color: 'yellow' };
151
160
  if (lowerValue.includes('live') || lowerValue.includes('funded')) return { text: value, color: 'green' };
@@ -153,11 +162,14 @@ const showAccounts = async (service) => {
153
162
  if (lowerValue.includes('express')) return { text: value, color: 'magenta' };
154
163
  return { text: value, color: 'cyan' };
155
164
  }
156
- return ACCOUNT_TYPE[value] || { text: 'Unknown', color: 'white' };
165
+ if (typeof value === 'number') {
166
+ return ACCOUNT_TYPE[value] || { text: `Type ${value}`, color: 'white' };
167
+ }
168
+ return { text: 'Live', color: 'green' };
157
169
  };
158
- const type1 = getTypeDisplay(acc1.type, acc1.algorithm);
159
- const type2 = acc2 ? getTypeDisplay(acc2.type, acc2.algorithm) : null;
160
- console.log(chalk.cyan('║') + fmtRow('Type:', chalk[type1.color](type1.text), col1) + chalk.cyan('│') + (acc2 ? fmtRow('Type:', chalk[type2.color](type2.text), col2) : ' '.repeat(col2)) + chalk.cyan('║'));
170
+ const type1 = getTypeDisplay(acc1);
171
+ const type2 = acc2 ? getTypeDisplay(acc2) : null;
172
+ console.log(chalk.cyan('║') + fmtRow('Type:', chalk[type1.color](type1.text), col1) + chalk.cyan(sep) + (acc2 ? fmtRow('Type:', chalk[type2.color](type2.text), col2) : ' '.repeat(col2)) + chalk.cyan('║'));
161
173
 
162
174
  if (i + 2 < allAccounts.length) {
163
175
  console.log(chalk.cyan('╠') + chalk.cyan('═'.repeat(col1)) + chalk.cyan('╪') + chalk.cyan('═'.repeat(col2)) + chalk.cyan('╣'));