hedgequantx 1.2.74 → 1.2.76

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/pages/algo.js +87 -35
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "1.2.74",
3
+ "version": "1.2.76",
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": {
package/src/pages/algo.js CHANGED
@@ -309,6 +309,7 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
309
309
  let latency = 0;
310
310
  let spinnerFrame = 0;
311
311
  const spinnerChars = ['\u280B', '\u2819', '\u2839', '\u2838', '\u283C', '\u2834', '\u2826', '\u2827', '\u2807', '\u280F'];
312
+ const sessionStartTime = Date.now();
312
313
 
313
314
  // Stats
314
315
  let stats = {
@@ -418,12 +419,12 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
418
419
 
419
420
  console.log();
420
421
  console.log(chalk.cyan(TOP));
421
- console.log(chalk.cyan(V) + chalk.cyan(' \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 ') + chalk.cyan(V));
422
- console.log(chalk.cyan(V) + chalk.cyan(' \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D ') + chalk.cyan(V));
423
- console.log(chalk.cyan(V) + chalk.cyan(' \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2554\u255D ') + chalk.cyan(V));
424
- console.log(chalk.cyan(V) + chalk.cyan(' \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551\u2584\u2584 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2588\u2588\u2557 ') + chalk.cyan(V));
425
- console.log(chalk.cyan(V) + chalk.cyan(' \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u255D \u2588\u2588\u2557 ') + chalk.cyan(V));
426
- console.log(chalk.cyan(V) + chalk.cyan(' \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D ') + chalk.cyan(V));
422
+ console.log(chalk.cyan(V) + chalk.cyan(' ██╗ ██╗███████╗██████╗ ██████╗ ███████╗ ██████╗ ██╗ ██╗ █████╗ ███╗ ██╗████████╗') + chalk.yellow('██╗ ██╗ ') + chalk.cyan(V));
423
+ console.log(chalk.cyan(V) + chalk.cyan(' ██║ ██║██╔════╝██╔══██╗██╔════╝ ██╔════╝██╔═══██╗██║ ██║██╔══██╗████╗ ██║╚══██╔══╝') + chalk.yellow('╚██╗██╔╝ ') + chalk.cyan(V));
424
+ console.log(chalk.cyan(V) + chalk.cyan(' ███████║█████╗ ██║ ██║██║ ███╗█████╗ ██║ ██║██║ ██║███████║██╔██╗ ██║ ██║ ') + chalk.yellow(' ╚███╔╝ ') + chalk.cyan(V));
425
+ console.log(chalk.cyan(V) + chalk.cyan(' ██╔══██║██╔══╝ ██║ ██║██║ ██║██╔══╝ ██║▄▄ ██║██║ ██║██╔══██║██║╚██╗██║ ██║ ') + chalk.yellow(' ██╔██╗ ') + chalk.cyan(V));
426
+ console.log(chalk.cyan(V) + chalk.cyan(' ██║ ██║███████╗██████╔╝╚██████╔╝███████╗╚██████╔╝╚██████╔╝██║ ██║██║ ╚████║ ██║ ') + chalk.yellow('██╔╝ ██╗ ') + chalk.cyan(V));
427
+ console.log(chalk.cyan(V) + chalk.cyan(' ╚═╝ ╚═╝╚══════╝╚═════╝ ╚═════╝ ╚══════╝ ╚══▀▀═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ') + chalk.yellow('╚═╝ ╚═╝ ') + chalk.cyan(V));
427
428
  console.log(chalk.cyan(MID));
428
429
 
429
430
  // Centered title
@@ -456,6 +457,9 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
456
457
  // Row 1 cells: widths = 36 + 18 + 10 + 14 + 14 = 92 + 4 separators = 96
457
458
  const c1w = 36, c2w = 18, c3w = 10, c4w = 14, c5w = 14;
458
459
 
460
+ // Safe padding function
461
+ const safePad = (len) => ' '.repeat(Math.max(0, len));
462
+
459
463
  const cell1 = ` ${accLabel}: ${chalk.cyan(accVal)}`;
460
464
  const cell1plain = ` ${accLabel}: ${accVal}`;
461
465
  const cell2 = ` ${symLabel}: ${chalk.yellow(symVal)}`;
@@ -467,11 +471,11 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
467
471
  const cell5 = ` ${latLabel}: ${latencyColor(latVal)}`;
468
472
  const cell5plain = ` ${latLabel}: ${latVal}`;
469
473
 
470
- const row1 = cell1 + ' '.repeat(c1w - cell1plain.length) + chalk.cyan(VS) +
471
- cell2 + ' '.repeat(c2w - cell2plain.length) + chalk.cyan(VS) +
472
- cell3 + ' '.repeat(c3w - cell3plain.length) + chalk.cyan(VS) +
473
- cell4 + ' '.repeat(c4w - cell4plain.length) + chalk.cyan(VS) +
474
- cell5 + ' '.repeat(c5w - cell5plain.length);
474
+ const row1 = cell1 + safePad(c1w - cell1plain.length) + chalk.cyan(VS) +
475
+ cell2 + safePad(c2w - cell2plain.length) + chalk.cyan(VS) +
476
+ cell3 + safePad(c3w - cell3plain.length) + chalk.cyan(VS) +
477
+ cell4 + safePad(c4w - cell4plain.length) + chalk.cyan(VS) +
478
+ cell5 + safePad(c5w - cell5plain.length);
475
479
 
476
480
  console.log(chalk.cyan(V) + row1 + chalk.cyan(V));
477
481
 
@@ -502,27 +506,28 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
502
506
  const cell10 = ` ${wlLabel}: ${chalk.green(stats.wins.toString())}/${chalk.red(stats.losses.toString())}`;
503
507
  const cell10plain = ` ${wlLabel}: ${wlVal}`;
504
508
 
505
- const row2 = cell6 + ' '.repeat(c1w - cell6plain.length) + chalk.cyan(VS) +
506
- cell7 + ' '.repeat(c2w - cell7plain.length) + chalk.cyan(VS) +
507
- cell8 + ' '.repeat(c3w - cell8plain.length) + chalk.cyan(VS) +
508
- cell9 + ' '.repeat(c4w - cell9plain.length) + chalk.cyan(VS) +
509
- cell10 + ' '.repeat(c5w - cell10plain.length);
509
+ const row2 = cell6 + safePad(c1w - cell6plain.length) + chalk.cyan(VS) +
510
+ cell7 + safePad(c2w - cell7plain.length) + chalk.cyan(VS) +
511
+ cell8 + safePad(c3w - cell8plain.length) + chalk.cyan(VS) +
512
+ cell9 + safePad(c4w - cell9plain.length) + chalk.cyan(VS) +
513
+ cell10 + safePad(c5w - cell10plain.length);
510
514
 
511
515
  console.log(chalk.cyan(V) + row2 + chalk.cyan(V));
512
516
  console.log(chalk.cyan(MID));
513
517
 
514
518
  // Activity log header with spinner and centered date
515
519
  spinnerFrame = (spinnerFrame + 1) % spinnerChars.length;
516
- const spinner = spinnerChars[spinnerFrame];
517
- const actLeft = ` Activity Log ${chalk.cyan(spinner)}`;
518
- const actLeftPlain = ` Activity Log ${spinner}`;
520
+ const spinnerChar = spinnerChars[spinnerFrame];
521
+ const actLeft = ` Activity Log ${chalk.cyan(spinnerChar)}`;
522
+ const actLeftPlain = ` Activity Log ${spinnerChar}`;
519
523
  const actRight = 'Press X to stop ';
520
524
  const dateCentered = `- ${dateStr} -`;
521
525
  const leftLen = actLeftPlain.length;
522
526
  const rightLen = actRight.length;
523
- const midSpace = W - leftLen - rightLen;
524
- const datePad = Math.floor((midSpace - dateCentered.length) / 2);
525
- const dateSection = ' '.repeat(datePad) + chalk.gray(dateCentered) + ' '.repeat(midSpace - datePad - dateCentered.length);
527
+ const midSpace = Math.max(0, W - leftLen - rightLen);
528
+ const datePad = Math.max(0, Math.floor((midSpace - dateCentered.length) / 2));
529
+ const remainingPad = Math.max(0, midSpace - datePad - dateCentered.length);
530
+ const dateSection = ' '.repeat(datePad) + chalk.gray(dateCentered) + ' '.repeat(remainingPad);
526
531
  console.log(chalk.cyan(V) + chalk.white(actLeft) + dateSection + chalk.yellow(actRight) + chalk.cyan(V));
527
532
  console.log(chalk.cyan(BOT));
528
533
 
@@ -789,19 +794,66 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
789
794
  }
790
795
  console.log();
791
796
 
792
- // Final stats
793
- console.log(chalk.gray(getSeparator()));
794
- console.log(chalk.white.bold(' Session Summary'));
795
- console.log(chalk.gray(getSeparator()));
796
- console.log(chalk.white(` Daily Target: ${chalk.green('$' + dailyTarget.toFixed(2))}`));
797
- console.log(chalk.white(` Max Risk: ${chalk.red('$' + maxRisk.toFixed(2))}`));
798
- console.log(chalk.white(` Final P&L: ${stats.pnl >= 0 ? chalk.green('+$' + stats.pnl.toFixed(2)) : chalk.red('-$' + Math.abs(stats.pnl).toFixed(2))}`));
799
- console.log(chalk.gray(getSeparator()));
800
- console.log(chalk.white(` Total Trades: ${chalk.cyan(stats.trades)}`));
801
- console.log(chalk.white(` Wins: ${chalk.green(stats.wins)}`));
802
- console.log(chalk.white(` Losses: ${chalk.red(stats.losses)}`));
803
- console.log(chalk.white(` Win Rate: ${chalk.yellow(stats.winRate + '%')}`));
804
- console.log(chalk.gray(getSeparator()));
797
+ // Final stats in a grid box
798
+ const V = '\u2551';
799
+ const VS = '\u2502';
800
+ const H = '\u2550';
801
+
802
+ // Calculate session duration
803
+ const sessionDuration = Date.now() - sessionStartTime;
804
+ const durationSec = Math.floor(sessionDuration / 1000);
805
+ const durationMin = Math.floor(durationSec / 60);
806
+ const durationHr = Math.floor(durationMin / 60);
807
+ const durationStr = durationHr > 0
808
+ ? `${durationHr}h ${durationMin % 60}m ${durationSec % 60}s`
809
+ : durationMin > 0
810
+ ? `${durationMin}m ${durationSec % 60}s`
811
+ : `${durationSec}s`;
812
+
813
+ // Cell widths: 4 cells of 24 each = 96
814
+ const cw = 24;
815
+ const W = cw * 4;
816
+
817
+ const TOP = '\u2554' + H.repeat(cw) + '\u2564' + H.repeat(cw) + '\u2564' + H.repeat(cw) + '\u2564' + H.repeat(cw) + '\u2557';
818
+ const MID = '\u2560' + H.repeat(cw) + '\u256A' + H.repeat(cw) + '\u256A' + H.repeat(cw) + '\u256A' + H.repeat(cw) + '\u2563';
819
+ const BOT = '\u255A' + H.repeat(cw) + '\u2567' + H.repeat(cw) + '\u2567' + H.repeat(cw) + '\u2567' + H.repeat(cw) + '\u255D';
820
+
821
+ const cell = (label, value, width) => {
822
+ const text = ` ${label}: ${value}`;
823
+ const stripped = text.replace(/\x1b\[[0-9;]*m/g, '');
824
+ const padding = Math.max(0, width - stripped.length);
825
+ return text + ' '.repeat(padding);
826
+ };
827
+
828
+ const centerTitle = (text, width) => {
829
+ const pad = Math.floor((width - text.length) / 2);
830
+ return ' '.repeat(pad) + text + ' '.repeat(width - pad - text.length);
831
+ };
832
+
833
+ const pnlValue = stats.pnl >= 0 ? chalk.green('+$' + stats.pnl.toFixed(2)) : chalk.red('-$' + Math.abs(stats.pnl).toFixed(2));
834
+
835
+ console.log();
836
+ console.log(chalk.cyan('\u2554' + H.repeat(W) + '\u2557'));
837
+ console.log(chalk.cyan(V) + chalk.white.bold(centerTitle('Session Summary', W)) + chalk.cyan(V));
838
+ console.log(chalk.cyan(TOP.replace('\u2554', '\u2560').replace('\u2557', '\u2563')));
839
+
840
+ // Row 1: Target | Risk | P&L | Win Rate
841
+ const r1c1 = cell('Target', chalk.green('$' + dailyTarget.toFixed(2)), cw);
842
+ const r1c2 = cell('Risk', chalk.red('$' + maxRisk.toFixed(2)), cw);
843
+ const r1c3 = cell('P&L', pnlValue, cw);
844
+ const r1c4 = cell('Win Rate', chalk.yellow(stats.winRate + '%'), cw);
845
+ console.log(chalk.cyan(V) + r1c1 + chalk.cyan(VS) + r1c2 + chalk.cyan(VS) + r1c3 + chalk.cyan(VS) + r1c4 + chalk.cyan(V));
846
+
847
+ console.log(chalk.cyan(MID));
848
+
849
+ // Row 2: Trades | Wins | Losses | Duration
850
+ const r2c1 = cell('Trades', chalk.cyan(stats.trades.toString()), cw);
851
+ const r2c2 = cell('Wins', chalk.green(stats.wins.toString()), cw);
852
+ const r2c3 = cell('Losses', chalk.red(stats.losses.toString()), cw);
853
+ const r2c4 = cell('Duration', chalk.white(durationStr), cw);
854
+ console.log(chalk.cyan(V) + r2c1 + chalk.cyan(VS) + r2c2 + chalk.cyan(VS) + r2c3 + chalk.cyan(VS) + r2c4 + chalk.cyan(V));
855
+
856
+ console.log(chalk.cyan(BOT));
805
857
  console.log();
806
858
 
807
859
  await inquirer.prompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);