hedgequantx 1.8.13 → 1.8.15
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 +1 -1
- package/src/pages/algo/copy-trading.js +1 -1
- package/src/pages/algo/one-account.js +12 -19
- package/src/pages/algo/ui.js +71 -13
package/package.json
CHANGED
|
@@ -213,7 +213,7 @@ const launchCopyTrading = async (config) => {
|
|
|
213
213
|
const leadName = showNames ? (lead.account.accountName || lead.account.accountId) : 'HQX Lead *****';
|
|
214
214
|
const followerName = showNames ? (follower.account.accountName || follower.account.accountId) : 'HQX Follower *****';
|
|
215
215
|
|
|
216
|
-
const ui = new AlgoUI({ subtitle: 'HQX Copy Trading' });
|
|
216
|
+
const ui = new AlgoUI({ subtitle: 'HQX Copy Trading', mode: 'copy-trading' });
|
|
217
217
|
|
|
218
218
|
const stats = {
|
|
219
219
|
leadName, followerName,
|
|
@@ -71,34 +71,27 @@ const oneAccountMenu = async (service) => {
|
|
|
71
71
|
};
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
|
-
* Symbol selection
|
|
74
|
+
* Symbol selection - same as copy-trading
|
|
75
75
|
*/
|
|
76
76
|
const selectSymbol = async (service, account) => {
|
|
77
|
-
const spinner = ora({ text: 'Loading
|
|
77
|
+
const spinner = ora({ text: 'Loading symbols...', color: 'yellow' }).start();
|
|
78
78
|
|
|
79
79
|
const contractsResult = await service.getContracts();
|
|
80
|
-
if (!contractsResult.success) {
|
|
80
|
+
if (!contractsResult.success || !contractsResult.contracts?.length) {
|
|
81
81
|
spinner.fail('Failed to load contracts');
|
|
82
82
|
return null;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
// Normalize contract structure - API returns { name: "ESH6", description: "E-mini S&P 500..." }
|
|
86
|
+
const contracts = contractsResult.contracts.map(c => ({
|
|
87
|
+
...c,
|
|
88
|
+
symbol: c.name || c.symbol,
|
|
89
|
+
name: c.description || c.name || c.symbol
|
|
90
|
+
}));
|
|
86
91
|
|
|
87
|
-
|
|
88
|
-
const categories = {};
|
|
89
|
-
for (const c of contractsResult.contracts) {
|
|
90
|
-
const cat = c.group || 'Other';
|
|
91
|
-
if (!categories[cat]) categories[cat] = [];
|
|
92
|
-
categories[cat].push(c);
|
|
93
|
-
}
|
|
92
|
+
spinner.succeed(`Found ${contracts.length} contracts`);
|
|
94
93
|
|
|
95
|
-
|
|
96
|
-
const options = [];
|
|
97
|
-
for (const [cat, contracts] of Object.entries(categories)) {
|
|
98
|
-
for (const c of contracts.slice(0, 10)) {
|
|
99
|
-
options.push({ label: `[${cat}] ${c.name || c.symbol}`, value: c });
|
|
100
|
-
}
|
|
101
|
-
}
|
|
94
|
+
const options = contracts.map(c => ({ label: c.name, value: c }));
|
|
102
95
|
options.push({ label: '< Back', value: 'back' });
|
|
103
96
|
|
|
104
97
|
const contract = await prompts.selectOption('Select Symbol:', options);
|
|
@@ -139,7 +132,7 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
139
132
|
const accountName = showName ? (account.accountName || account.accountId) : 'HQX *****';
|
|
140
133
|
const symbolName = contract.name || contract.symbol;
|
|
141
134
|
|
|
142
|
-
const ui = new AlgoUI({ subtitle: 'HQX Ultra-Scalping' });
|
|
135
|
+
const ui = new AlgoUI({ subtitle: 'HQX Ultra-Scalping', mode: 'one-account' });
|
|
143
136
|
|
|
144
137
|
const stats = {
|
|
145
138
|
accountName, symbol: symbolName, contracts,
|
package/src/pages/algo/ui.js
CHANGED
|
@@ -126,20 +126,83 @@ class AlgoUI {
|
|
|
126
126
|
|
|
127
127
|
_drawStats(stats) {
|
|
128
128
|
const { W } = this;
|
|
129
|
-
const
|
|
130
|
-
const pad = (len) => ' '.repeat(Math.max(0, len));
|
|
129
|
+
const isCopyTrading = this.config.mode === 'copy-trading';
|
|
131
130
|
|
|
132
131
|
const pnlColor = stats.pnl >= 0 ? chalk.green : chalk.red;
|
|
133
132
|
const pnlStr = (stats.pnl >= 0 ? '+$' : '-$') + Math.abs(stats.pnl).toFixed(2);
|
|
134
133
|
const latencyColor = stats.latency < 100 ? chalk.green : (stats.latency < 300 ? chalk.yellow : chalk.red);
|
|
135
134
|
const serverColor = stats.connected ? chalk.green : chalk.red;
|
|
136
135
|
|
|
137
|
-
|
|
136
|
+
if (isCopyTrading) {
|
|
137
|
+
this._drawCopyTradingStats(stats, pnlColor, pnlStr, latencyColor, serverColor);
|
|
138
|
+
} else {
|
|
139
|
+
this._drawOneAccountStats(stats, pnlColor, pnlStr, latencyColor, serverColor);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
_drawOneAccountStats(stats, pnlColor, pnlStr, latencyColor, serverColor) {
|
|
144
|
+
const { W } = this;
|
|
145
|
+
const colL = 48, colR = 47;
|
|
146
|
+
const pad = (len) => ' '.repeat(Math.max(0, len));
|
|
147
|
+
|
|
148
|
+
const GT = BOX.ML + BOX.H.repeat(colL) + BOX.TM + BOX.H.repeat(colR) + BOX.MR;
|
|
149
|
+
const GM = BOX.ML + BOX.H.repeat(colL) + BOX.MM + BOX.H.repeat(colR) + BOX.MR;
|
|
150
|
+
const GB = BOX.ML + BOX.H.repeat(colL) + BOX.BM + BOX.H.repeat(colR) + BOX.MR;
|
|
151
|
+
|
|
152
|
+
const row = (c1, c2) => {
|
|
153
|
+
this._line(chalk.cyan(BOX.V) + c1 + chalk.cyan(BOX.VS) + c2 + chalk.cyan(BOX.V));
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
this._line(chalk.cyan(GT));
|
|
157
|
+
|
|
158
|
+
// Row 1: Account | Symbol
|
|
159
|
+
const accountName = (stats.accountName || 'N/A').substring(0, 40);
|
|
160
|
+
const symbol = (stats.symbol || 'N/A').substring(0, 35);
|
|
161
|
+
const r1c1 = buildCell('Account', accountName, chalk.cyan, colL);
|
|
162
|
+
const r1c2 = buildCell('Symbol', symbol, chalk.yellow, colR);
|
|
163
|
+
row(r1c1.padded, r1c2.padded);
|
|
164
|
+
|
|
165
|
+
this._line(chalk.cyan(GM));
|
|
166
|
+
|
|
167
|
+
// Row 2: Qty | P&L
|
|
168
|
+
const r2c1 = buildCell('Qty', (stats.qty || '1').toString(), chalk.cyan, colL);
|
|
169
|
+
const r2c2 = buildCell('P&L', pnlStr, pnlColor, colR);
|
|
170
|
+
row(r2c1.padded, r2c2.padded);
|
|
171
|
+
|
|
172
|
+
this._line(chalk.cyan(GM));
|
|
173
|
+
|
|
174
|
+
// Row 3: Target | Risk
|
|
175
|
+
const r3c1 = buildCell('Target', '$' + (stats.target || 0).toFixed(2), chalk.green, colL);
|
|
176
|
+
const r3c2 = buildCell('Risk', '$' + (stats.risk || 0).toFixed(2), chalk.red, colR);
|
|
177
|
+
row(r3c1.padded, r3c2.padded);
|
|
178
|
+
|
|
179
|
+
this._line(chalk.cyan(GM));
|
|
180
|
+
|
|
181
|
+
// Row 4: Trades | Server
|
|
182
|
+
const r4c1t = ` Trades: ${chalk.cyan(stats.trades || 0)} W/L: ${chalk.green(stats.wins || 0)}/${chalk.red(stats.losses || 0)}`;
|
|
183
|
+
const r4c1p = ` Trades: ${stats.trades || 0} W/L: ${stats.wins || 0}/${stats.losses || 0}`;
|
|
184
|
+
const r4c2 = buildCell('Server', stats.connected ? 'ON' : 'OFF', serverColor, colR);
|
|
185
|
+
row(r4c1t + pad(colL - r4c1p.length), r4c2.padded);
|
|
186
|
+
|
|
187
|
+
this._line(chalk.cyan(GM));
|
|
188
|
+
|
|
189
|
+
// Row 5: Latency | Platform
|
|
190
|
+
const r5c1 = buildCell('Latency', `${stats.latency || 0}ms`, latencyColor, colL);
|
|
191
|
+
const r5c2 = buildCell('Platform', stats.platform || 'N/A', chalk.cyan, colR);
|
|
192
|
+
row(r5c1.padded, r5c2.padded);
|
|
193
|
+
|
|
194
|
+
this._line(chalk.cyan(GB));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
_drawCopyTradingStats(stats, pnlColor, pnlStr, latencyColor, serverColor) {
|
|
198
|
+
const { W } = this;
|
|
199
|
+
const colL = 48, colR = 47;
|
|
200
|
+
const pad = (len) => ' '.repeat(Math.max(0, len));
|
|
201
|
+
|
|
138
202
|
const GT = BOX.ML + BOX.H.repeat(colL) + BOX.TM + BOX.H.repeat(colR) + BOX.MR;
|
|
139
203
|
const GM = BOX.ML + BOX.H.repeat(colL) + BOX.MM + BOX.H.repeat(colR) + BOX.MR;
|
|
140
204
|
const GB = BOX.ML + BOX.H.repeat(colL) + BOX.BM + BOX.H.repeat(colR) + BOX.MR;
|
|
141
205
|
|
|
142
|
-
// Row builders
|
|
143
206
|
const row = (c1, c2) => {
|
|
144
207
|
this._line(chalk.cyan(BOX.V) + c1 + chalk.cyan(BOX.VS) + c2 + chalk.cyan(BOX.V));
|
|
145
208
|
};
|
|
@@ -147,9 +210,8 @@ class AlgoUI {
|
|
|
147
210
|
this._line(chalk.cyan(GT));
|
|
148
211
|
|
|
149
212
|
// Row 1: Lead Account | Follower Account
|
|
150
|
-
const leadName = (stats.leadName ||
|
|
213
|
+
const leadName = (stats.leadName || 'N/A').substring(0, 40);
|
|
151
214
|
const followerName = (stats.followerName || 'N/A').substring(0, 40);
|
|
152
|
-
|
|
153
215
|
const r1c1 = buildCell('Lead', leadName, chalk.cyan, colL);
|
|
154
216
|
const r1c2 = buildCell('Follower', followerName, chalk.magenta, colR);
|
|
155
217
|
row(r1c1.padded, r1c2.padded);
|
|
@@ -158,8 +220,7 @@ class AlgoUI {
|
|
|
158
220
|
|
|
159
221
|
// Row 2: Lead Symbol | Follower Symbol
|
|
160
222
|
const leadSymbol = (stats.leadSymbol || stats.symbol || 'N/A').substring(0, 35);
|
|
161
|
-
const followerSymbol = (stats.followerSymbol || 'N/A').substring(0, 35);
|
|
162
|
-
|
|
223
|
+
const followerSymbol = (stats.followerSymbol || stats.symbol || 'N/A').substring(0, 35);
|
|
163
224
|
const r2c1 = buildCell('Symbol', leadSymbol, chalk.yellow, colL);
|
|
164
225
|
const r2c2 = buildCell('Symbol', followerSymbol, chalk.yellow, colR);
|
|
165
226
|
row(r2c1.padded, r2c2.padded);
|
|
@@ -167,11 +228,8 @@ class AlgoUI {
|
|
|
167
228
|
this._line(chalk.cyan(GM));
|
|
168
229
|
|
|
169
230
|
// Row 3: Lead Qty | Follower Qty
|
|
170
|
-
const
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
const r3c1 = buildCell('Qty', leadQty.toString(), chalk.cyan, colL);
|
|
174
|
-
const r3c2 = buildCell('Qty', followerQty.toString(), chalk.cyan, colR);
|
|
231
|
+
const r3c1 = buildCell('Qty', (stats.leadQty || '1').toString(), chalk.cyan, colL);
|
|
232
|
+
const r3c2 = buildCell('Qty', (stats.followerQty || '1').toString(), chalk.cyan, colR);
|
|
175
233
|
row(r3c1.padded, r3c2.padded);
|
|
176
234
|
|
|
177
235
|
this._line(chalk.cyan(GM));
|