hedgequantx 1.8.25 → 1.8.27
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 +17 -15
- package/src/pages/algo/index.js +2 -2
- package/src/pages/algo/one-account.js +16 -14
- package/src/pages/algo/ui.js +76 -1
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@ const readline = require('readline');
|
|
|
8
8
|
|
|
9
9
|
const { connections } = require('../../services');
|
|
10
10
|
const { HQXServerService } = require('../../services/hqx-server');
|
|
11
|
-
const { AlgoUI } = require('./ui');
|
|
11
|
+
const { AlgoUI, renderSessionSummary } = require('./ui');
|
|
12
12
|
const { logger, prompts } = require('../../utils');
|
|
13
13
|
const { checkMarketHours } = require('../../services/projectx/market');
|
|
14
14
|
|
|
@@ -35,7 +35,7 @@ const copyTradingMenu = async () => {
|
|
|
35
35
|
|
|
36
36
|
if (allConns.length < 2) {
|
|
37
37
|
console.log();
|
|
38
|
-
console.log(chalk.yellow(
|
|
38
|
+
console.log(chalk.yellow(` Copy Trading requires 2 connected accounts (found: ${allConns.length})`));
|
|
39
39
|
console.log(chalk.gray(' Connect to another PropFirm first'));
|
|
40
40
|
console.log();
|
|
41
41
|
await prompts.waitForEnter();
|
|
@@ -43,7 +43,7 @@ const copyTradingMenu = async () => {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
console.log();
|
|
46
|
-
console.log(chalk.
|
|
46
|
+
console.log(chalk.yellow.bold(' Copy Trading Setup'));
|
|
47
47
|
console.log();
|
|
48
48
|
|
|
49
49
|
const spinner = ora({ text: 'Fetching accounts...', color: 'yellow' }).start();
|
|
@@ -192,6 +192,19 @@ const selectSymbol = async (service, label) => {
|
|
|
192
192
|
return null;
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
+
// Sort: Popular indices first (ES, NQ, YM, RTY, MES, MNQ)
|
|
196
|
+
const prioritySymbols = ['ES', 'NQ', 'YM', 'RTY', 'MES', 'MNQ', 'MYM', 'M2K', 'NKD', 'CL', 'GC'];
|
|
197
|
+
contracts.sort((a, b) => {
|
|
198
|
+
const aSymbol = (a.symbol || a.name || '').toUpperCase();
|
|
199
|
+
const bSymbol = (b.symbol || b.name || '').toUpperCase();
|
|
200
|
+
const aIdx = prioritySymbols.findIndex(p => aSymbol.startsWith(p));
|
|
201
|
+
const bIdx = prioritySymbols.findIndex(p => bSymbol.startsWith(p));
|
|
202
|
+
if (aIdx !== -1 && bIdx !== -1) return aIdx - bIdx;
|
|
203
|
+
if (aIdx !== -1) return -1;
|
|
204
|
+
if (bIdx !== -1) return 1;
|
|
205
|
+
return (a.name || '').localeCompare(b.name || '');
|
|
206
|
+
});
|
|
207
|
+
|
|
195
208
|
spinner.succeed(`Found ${contracts.length} contracts`);
|
|
196
209
|
|
|
197
210
|
const options = contracts.map(c => ({ label: c.name || c.symbol, value: c }));
|
|
@@ -335,18 +348,7 @@ const launchCopyTrading = async (config) => {
|
|
|
335
348
|
ui.cleanup();
|
|
336
349
|
|
|
337
350
|
// Summary
|
|
338
|
-
|
|
339
|
-
console.log();
|
|
340
|
-
console.log(chalk.cyan('╔════════════════════════════════════════════════════════════════╗'));
|
|
341
|
-
console.log(chalk.cyan('║') + chalk.white(' Copy Trading Summary ') + chalk.cyan('║'));
|
|
342
|
-
console.log(chalk.cyan('╠════════════════════════════════════════════════════════════════╣'));
|
|
343
|
-
console.log(chalk.cyan('║') + chalk.white(` Stop Reason: ${(stopReason || 'unknown').padEnd(47)}`) + chalk.cyan('║'));
|
|
344
|
-
console.log(chalk.cyan('║') + chalk.white(` Trades: ${String(stats.trades).padEnd(10)} Wins: ${String(stats.wins).padEnd(10)} Losses: ${String(stats.losses).padEnd(10)}`) + chalk.cyan('║'));
|
|
345
|
-
const pnlColor = stats.pnl >= 0 ? chalk.green : chalk.red;
|
|
346
|
-
const pnlStr = `${stats.pnl >= 0 ? '+' : ''}$${stats.pnl.toFixed(2)}`;
|
|
347
|
-
console.log(chalk.cyan('║') + chalk.white(' P&L: ') + pnlColor(pnlStr.padEnd(55)) + chalk.cyan('║'));
|
|
348
|
-
console.log(chalk.cyan('╚════════════════════════════════════════════════════════════════╝'));
|
|
349
|
-
console.log();
|
|
351
|
+
renderSessionSummary(stats, stopReason);
|
|
350
352
|
|
|
351
353
|
await prompts.waitForEnter();
|
|
352
354
|
};
|
package/src/pages/algo/index.js
CHANGED
|
@@ -19,11 +19,11 @@ const algoTradingMenu = async (service) => {
|
|
|
19
19
|
|
|
20
20
|
console.log();
|
|
21
21
|
console.log(chalk.gray(getSeparator()));
|
|
22
|
-
console.log(chalk.
|
|
22
|
+
console.log(chalk.yellow.bold(' Algo-Trading'));
|
|
23
23
|
console.log(chalk.gray(getSeparator()));
|
|
24
24
|
console.log();
|
|
25
25
|
|
|
26
|
-
const action = await prompts.selectOption('Select Mode:', [
|
|
26
|
+
const action = await prompts.selectOption(chalk.yellow('Select Mode:'), [
|
|
27
27
|
{ value: 'one_account', label: 'One Account' },
|
|
28
28
|
{ value: 'copy_trading', label: 'Copy Trading' },
|
|
29
29
|
{ value: 'back', label: '< Back' }
|
|
@@ -8,7 +8,7 @@ const readline = require('readline');
|
|
|
8
8
|
|
|
9
9
|
const { connections } = require('../../services');
|
|
10
10
|
const { HQXServerService } = require('../../services/hqx-server');
|
|
11
|
-
const { AlgoUI } = require('./ui');
|
|
11
|
+
const { AlgoUI, renderSessionSummary } = require('./ui');
|
|
12
12
|
const { prompts } = require('../../utils');
|
|
13
13
|
const { checkMarketHours } = require('../../services/projectx/market');
|
|
14
14
|
|
|
@@ -89,12 +89,25 @@ const selectSymbol = async (service, account) => {
|
|
|
89
89
|
name: c.description || c.name || c.symbol
|
|
90
90
|
}));
|
|
91
91
|
|
|
92
|
+
// Sort: Popular indices first (ES, NQ, YM, RTY, MES, MNQ)
|
|
93
|
+
const prioritySymbols = ['ES', 'NQ', 'YM', 'RTY', 'MES', 'MNQ', 'MYM', 'M2K', 'NKD', 'CL', 'GC'];
|
|
94
|
+
contracts.sort((a, b) => {
|
|
95
|
+
const aSymbol = (a.symbol || '').toUpperCase();
|
|
96
|
+
const bSymbol = (b.symbol || '').toUpperCase();
|
|
97
|
+
const aIdx = prioritySymbols.findIndex(p => aSymbol.startsWith(p));
|
|
98
|
+
const bIdx = prioritySymbols.findIndex(p => bSymbol.startsWith(p));
|
|
99
|
+
if (aIdx !== -1 && bIdx !== -1) return aIdx - bIdx;
|
|
100
|
+
if (aIdx !== -1) return -1;
|
|
101
|
+
if (bIdx !== -1) return 1;
|
|
102
|
+
return (a.name || '').localeCompare(b.name || '');
|
|
103
|
+
});
|
|
104
|
+
|
|
92
105
|
spinner.succeed(`Found ${contracts.length} contracts`);
|
|
93
106
|
|
|
94
107
|
const options = contracts.map(c => ({ label: c.name, value: c }));
|
|
95
108
|
options.push({ label: '< Back', value: 'back' });
|
|
96
109
|
|
|
97
|
-
const contract = await prompts.selectOption('Select Symbol:', options);
|
|
110
|
+
const contract = await prompts.selectOption(chalk.yellow('Select Symbol:'), options);
|
|
98
111
|
return contract === 'back' ? null : contract;
|
|
99
112
|
};
|
|
100
113
|
|
|
@@ -242,18 +255,7 @@ const launchAlgo = async (service, account, contract, config) => {
|
|
|
242
255
|
ui.cleanup();
|
|
243
256
|
|
|
244
257
|
// Summary
|
|
245
|
-
|
|
246
|
-
console.log();
|
|
247
|
-
console.log(chalk.cyan('╔════════════════════════════════════════════════════════════════╗'));
|
|
248
|
-
console.log(chalk.cyan('║') + chalk.white(' Session Summary ') + chalk.cyan('║'));
|
|
249
|
-
console.log(chalk.cyan('╠════════════════════════════════════════════════════════════════╣'));
|
|
250
|
-
console.log(chalk.cyan('║') + chalk.white(` Stop Reason: ${(stopReason || 'unknown').padEnd(47)}`) + chalk.cyan('║'));
|
|
251
|
-
console.log(chalk.cyan('║') + chalk.white(` Trades: ${String(stats.trades).padEnd(10)} Wins: ${String(stats.wins).padEnd(10)} Losses: ${String(stats.losses).padEnd(10)}`) + chalk.cyan('║'));
|
|
252
|
-
const pnlColor = stats.pnl >= 0 ? chalk.green : chalk.red;
|
|
253
|
-
const pnlStr = `${stats.pnl >= 0 ? '+' : ''}$${stats.pnl.toFixed(2)}`;
|
|
254
|
-
console.log(chalk.cyan('║') + chalk.white(' P&L: ') + pnlColor(pnlStr.padEnd(55)) + chalk.cyan('║'));
|
|
255
|
-
console.log(chalk.cyan('╚════════════════════════════════════════════════════════════════╝'));
|
|
256
|
-
console.log();
|
|
258
|
+
renderSessionSummary(stats, stopReason);
|
|
257
259
|
|
|
258
260
|
await prompts.waitForEnter();
|
|
259
261
|
};
|
package/src/pages/algo/ui.js
CHANGED
|
@@ -350,4 +350,79 @@ const checkMarketStatus = () => {
|
|
|
350
350
|
return { isOpen: true, message: 'Market OPEN' };
|
|
351
351
|
};
|
|
352
352
|
|
|
353
|
-
|
|
353
|
+
/**
|
|
354
|
+
* Render Session Summary - Same style as dashboard
|
|
355
|
+
*/
|
|
356
|
+
const renderSessionSummary = (stats, stopReason) => {
|
|
357
|
+
const W = 96; // Same width as dashboard
|
|
358
|
+
const colL = Math.floor(W / 2) - 1;
|
|
359
|
+
const colR = W - colL - 1;
|
|
360
|
+
const version = require('../../../package.json').version;
|
|
361
|
+
|
|
362
|
+
console.clear();
|
|
363
|
+
console.log();
|
|
364
|
+
|
|
365
|
+
// Top border
|
|
366
|
+
console.log(chalk.cyan(BOX.TOP + BOX.H.repeat(W) + BOX.TR));
|
|
367
|
+
|
|
368
|
+
// Logo
|
|
369
|
+
console.log(chalk.cyan(BOX.V) + chalk.cyan(' ██╗ ██╗███████╗██████╗ ██████╗ ███████╗ ██████╗ ██╗ ██╗ █████╗ ███╗ ██╗████████╗') + chalk.yellow('██╗ ██╗') + ' ' + chalk.cyan(BOX.V));
|
|
370
|
+
console.log(chalk.cyan(BOX.V) + chalk.cyan(' ██║ ██║██╔════╝██╔══██╗██╔════╝ ██╔════╝██╔═══██╗██║ ██║██╔══██╗████╗ ██║╚══██╔══╝') + chalk.yellow('╚██╗██╔╝') + ' ' + chalk.cyan(BOX.V));
|
|
371
|
+
console.log(chalk.cyan(BOX.V) + chalk.cyan(' ███████║█████╗ ██║ ██║██║ ███╗█████╗ ██║ ██║██║ ██║███████║██╔██╗ ██║ ██║ ') + chalk.yellow(' ╚███╔╝ ') + ' ' + chalk.cyan(BOX.V));
|
|
372
|
+
console.log(chalk.cyan(BOX.V) + chalk.cyan(' ██╔══██║██╔══╝ ██║ ██║██║ ██║██╔══╝ ██║▄▄ ██║██║ ██║██╔══██║██║╚██╗██║ ██║ ') + chalk.yellow(' ██╔██╗ ') + ' ' + chalk.cyan(BOX.V));
|
|
373
|
+
console.log(chalk.cyan(BOX.V) + chalk.cyan(' ██║ ██║███████╗██████╔╝╚██████╔╝███████╗╚██████╔╝╚██████╔╝██║ ██║██║ ╚████║ ██║ ') + chalk.yellow('██╔╝ ██╗') + ' ' + chalk.cyan(BOX.V));
|
|
374
|
+
console.log(chalk.cyan(BOX.V) + chalk.cyan(' ╚═╝ ╚═╝╚══════╝╚═════╝ ╚═════╝ ╚══════╝ ╚══▀▀═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ') + chalk.yellow('╚═╝ ╚═╝') + ' ' + chalk.cyan(BOX.V));
|
|
375
|
+
|
|
376
|
+
// Separator + title
|
|
377
|
+
console.log(chalk.cyan(BOX.ML + BOX.H.repeat(W) + BOX.MR));
|
|
378
|
+
console.log(chalk.cyan(BOX.V) + chalk.white(center(`Prop Futures Algo Trading v${version}`, W)) + chalk.cyan(BOX.V));
|
|
379
|
+
console.log(chalk.cyan(BOX.ML + BOX.H.repeat(W) + BOX.MR));
|
|
380
|
+
console.log(chalk.cyan(BOX.V) + chalk.yellow(center('Session Summary', W)) + chalk.cyan(BOX.V));
|
|
381
|
+
|
|
382
|
+
// Grid separators
|
|
383
|
+
const GT = BOX.ML + BOX.H.repeat(colL) + BOX.TM + BOX.H.repeat(colR) + BOX.MR;
|
|
384
|
+
const GM = BOX.ML + BOX.H.repeat(colL) + BOX.MM + BOX.H.repeat(colR) + BOX.MR;
|
|
385
|
+
|
|
386
|
+
const row = (label1, value1, color1, label2, value2, color2) => {
|
|
387
|
+
const c1 = ` ${label1}: ${color1(value1)}`;
|
|
388
|
+
const c2 = ` ${label2}: ${color2(value2)}`;
|
|
389
|
+
const p1 = ` ${label1}: ${value1}`;
|
|
390
|
+
const p2 = ` ${label2}: ${value2}`;
|
|
391
|
+
const padded1 = c1 + ' '.repeat(Math.max(0, colL - p1.length));
|
|
392
|
+
const padded2 = c2 + ' '.repeat(Math.max(0, colR - p2.length));
|
|
393
|
+
console.log(chalk.cyan(BOX.V) + padded1 + chalk.cyan(BOX.VS) + padded2 + chalk.cyan(BOX.V));
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
console.log(chalk.cyan(GT));
|
|
397
|
+
|
|
398
|
+
// Row 1: Stop Reason | Duration
|
|
399
|
+
const duration = stats.duration || '--';
|
|
400
|
+
const reasonColor = stopReason === 'target' ? chalk.green : stopReason === 'risk' ? chalk.red : chalk.yellow;
|
|
401
|
+
row('Stop Reason', (stopReason || 'manual').toUpperCase(), reasonColor, 'Duration', duration, chalk.white);
|
|
402
|
+
|
|
403
|
+
console.log(chalk.cyan(GM));
|
|
404
|
+
|
|
405
|
+
// Row 2: Trades | Win Rate
|
|
406
|
+
const winRate = stats.trades > 0 ? ((stats.wins / stats.trades) * 100).toFixed(1) + '%' : '0%';
|
|
407
|
+
row('Trades', String(stats.trades || 0), chalk.white, 'Win Rate', winRate, stats.wins >= stats.losses ? chalk.green : chalk.red);
|
|
408
|
+
|
|
409
|
+
console.log(chalk.cyan(GM));
|
|
410
|
+
|
|
411
|
+
// Row 3: Wins | Losses
|
|
412
|
+
row('Wins', String(stats.wins || 0), chalk.green, 'Losses', String(stats.losses || 0), chalk.red);
|
|
413
|
+
|
|
414
|
+
console.log(chalk.cyan(GM));
|
|
415
|
+
|
|
416
|
+
// Row 4: P&L | Target
|
|
417
|
+
const pnl = stats.pnl || 0;
|
|
418
|
+
const pnlStr = `${pnl >= 0 ? '+' : ''}$${Math.abs(pnl).toFixed(2)}`;
|
|
419
|
+
const pnlColor = pnl >= 0 ? chalk.green : chalk.red;
|
|
420
|
+
const targetStr = `$${(stats.target || 0).toFixed(2)}`;
|
|
421
|
+
row('P&L', pnlStr, pnlColor, 'Target', targetStr, chalk.cyan);
|
|
422
|
+
|
|
423
|
+
// Bottom border
|
|
424
|
+
console.log(chalk.cyan(BOX.BOT + BOX.H.repeat(W) + BOX.BR));
|
|
425
|
+
console.log();
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
module.exports = { AlgoUI, checkMarketStatus, renderSessionSummary, LOG_COLORS, LOG_ICONS, stripAnsi, center, fitToWidth };
|