hedgequantx 1.2.60 → 1.2.63

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 +76 -93
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "1.2.60",
3
+ "version": "1.2.63",
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
@@ -308,9 +308,6 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
308
308
  let stopReason = null;
309
309
 
310
310
  // Activity logs
311
- const logs = [];
312
- const MAX_LOGS = 10;
313
-
314
311
  // Stats
315
312
  let stats = {
316
313
  trades: 0,
@@ -321,14 +318,41 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
321
318
  winRate: '0.0'
322
319
  };
323
320
 
321
+ // Logs buffer (newest first)
322
+ const logs = [];
323
+ const MAX_LOGS = 20;
324
+
325
+ // Log colors
326
+ const typeColors = {
327
+ info: chalk.cyan,
328
+ success: chalk.green,
329
+ signal: chalk.yellow.bold,
330
+ trade: chalk.green.bold,
331
+ error: chalk.red,
332
+ warning: chalk.yellow
333
+ };
334
+
335
+ const getIcon = (type) => {
336
+ switch(type) {
337
+ case 'signal': return '[*]';
338
+ case 'trade': return '[>]';
339
+ case 'error': return '[X]';
340
+ case 'success': return '[OK]';
341
+ default: return '[.]';
342
+ }
343
+ };
344
+
345
+ // Add log (newest first)
324
346
  const addLog = (type, message) => {
325
347
  const timestamp = new Date().toLocaleTimeString();
326
- logs.push({ timestamp, type, message });
327
- if (logs.length > MAX_LOGS) logs.shift();
348
+ logs.unshift({ timestamp, type, message }); // Add at beginning
349
+ if (logs.length > MAX_LOGS) logs.pop(); // Remove oldest
328
350
  };
329
351
 
330
- const clearScreen = () => {
331
- console.clear();
352
+ // Print log and refresh display
353
+ const printLog = (type, message) => {
354
+ addLog(type, message);
355
+ displayUI();
332
356
  };
333
357
 
334
358
  // Check market hours
@@ -352,72 +376,38 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
352
376
  return { isOpen: true, message: 'Market OPEN' };
353
377
  };
354
378
 
379
+ // Display full UI with logs (newest first)
355
380
  const displayUI = () => {
356
- clearScreen();
381
+ console.clear();
357
382
  const marketStatus = checkMarketStatus();
383
+
384
+ // Header
358
385
  console.log();
359
386
  console.log(chalk.gray(getSeparator()));
360
- console.log(chalk.cyan.bold(' HQX Ultra-Scalping Algo'));
361
- console.log(chalk.gray(getSeparator()));
362
- console.log(chalk.white(` Status: ${algoRunning ? chalk.green('RUNNING') : chalk.yellow('CONNECTING...')}`));
363
- console.log(chalk.white(` Account: ${chalk.cyan(accountName)}`));
364
- console.log(chalk.white(` Symbol: ${chalk.cyan(symbolName)}`));
365
- console.log(chalk.white(` Contracts: ${chalk.cyan(numContracts)}`));
366
- console.log(chalk.white(` Server: ${hqxConnected ? chalk.green('CONNECTED') : chalk.red('DISCONNECTED')}`));
367
- console.log(chalk.white(` Market: ${marketStatus.isOpen ? chalk.green(marketStatus.message) : chalk.red(marketStatus.message)}`));
387
+ console.log(chalk.cyan.bold(' HQX Ultra-Scalping Algo') + chalk.gray(' | ') + chalk.yellow('Press X to stop'));
368
388
  console.log(chalk.gray(getSeparator()));
369
-
370
- // Risk Management
371
- console.log();
372
- const targetProgress = Math.min(100, Math.max(0, (stats.pnl / dailyTarget) * 100));
373
- const riskProgress = Math.min(100, (Math.abs(Math.min(0, stats.pnl)) / maxRisk) * 100);
374
- console.log(chalk.white(' Target: ') + chalk.green('$' + dailyTarget.toFixed(2)) +
375
- chalk.gray(' | Progress: ') + (targetProgress >= 100 ? chalk.green.bold(targetProgress.toFixed(1) + '%') : chalk.yellow(targetProgress.toFixed(1) + '%')));
376
- console.log(chalk.white(' Risk: ') + chalk.red('$' + maxRisk.toFixed(2)) +
377
- chalk.gray(' | Used: ') + (riskProgress >= 100 ? chalk.red.bold(riskProgress.toFixed(1) + '%') : chalk.cyan(riskProgress.toFixed(1) + '%')));
389
+ console.log(chalk.white(` Account: ${chalk.cyan(accountName)} | Symbol: ${chalk.cyan(symbolName)} | Qty: ${chalk.cyan(numContracts)}`));
390
+ console.log(chalk.white(` Target: ${chalk.green('$' + dailyTarget.toFixed(2))} | Risk: ${chalk.red('$' + maxRisk.toFixed(2))} | Server: ${hqxConnected ? chalk.green('ON') : chalk.red('OFF')}`));
378
391
 
379
- // Stats bar
380
- console.log();
381
- console.log(chalk.white(' Stats: ') +
382
- chalk.gray('Trades: ') + chalk.cyan(stats.trades) +
383
- chalk.gray(' | Wins: ') + chalk.green(stats.wins) +
384
- chalk.gray(' | Losses: ') + chalk.red(stats.losses) +
385
- chalk.gray(' | Win Rate: ') + chalk.yellow(stats.winRate + '%') +
386
- chalk.gray(' | P&L: ') + (stats.pnl >= 0 ? chalk.green('+$' + stats.pnl.toFixed(2)) : chalk.red('-$' + Math.abs(stats.pnl).toFixed(2)))
387
- );
388
- console.log();
392
+ // Stats line
393
+ const pnlColor = stats.pnl >= 0 ? chalk.green : chalk.red;
394
+ const pnlStr = stats.pnl >= 0 ? '+$' + stats.pnl.toFixed(2) : '-$' + Math.abs(stats.pnl).toFixed(2);
395
+ console.log(chalk.white(` P&L: ${pnlColor(pnlStr)} | Trades: ${chalk.cyan(stats.trades)} | Wins: ${chalk.green(stats.wins)} | Losses: ${chalk.red(stats.losses)}`));
389
396
 
390
- // Activity logs
391
- console.log(chalk.gray(getSeparator()));
392
- console.log(chalk.white.bold(' Activity Log'));
393
397
  console.log(chalk.gray(getSeparator()));
394
398
 
395
- const typeColors = {
396
- info: chalk.cyan,
397
- success: chalk.green,
398
- signal: chalk.yellow.bold,
399
- trade: chalk.green.bold,
400
- error: chalk.red,
401
- warning: chalk.yellow
402
- };
403
-
399
+ // Logs (newest first - already in correct order)
404
400
  if (logs.length === 0) {
405
401
  console.log(chalk.gray(' Waiting for activity...'));
406
402
  } else {
407
403
  logs.forEach(log => {
408
404
  const color = typeColors[log.type] || chalk.white;
409
- const icon = log.type === 'signal' ? '[*]' :
410
- log.type === 'trade' ? '[>]' :
411
- log.type === 'error' ? '[X]' :
412
- log.type === 'success' ? '[OK]' : '[.]';
405
+ const icon = getIcon(log.type);
413
406
  console.log(chalk.gray(` [${log.timestamp}]`) + ' ' + color(`${icon} ${log.message}`));
414
407
  });
415
408
  }
416
409
 
417
410
  console.log(chalk.gray(getSeparator()));
418
- console.log();
419
- console.log(chalk.yellow(' Press X to stop algo...'));
420
- console.log();
421
411
  };
422
412
 
423
413
  // Connect to HQX Server
@@ -455,17 +445,15 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
455
445
  hqxConnected = false;
456
446
  }
457
447
 
458
- // Setup event handlers
448
+ // Setup event handlers - logs scroll down naturally
459
449
  hqxServer.on('log', (data) => {
460
- addLog(data.type || 'info', data.message);
461
- displayUI();
450
+ printLog(data.type || 'info', data.message);
462
451
  });
463
452
 
464
453
  hqxServer.on('signal', (data) => {
465
454
  stats.signals++;
466
455
  const side = data.side === 'long' ? 'BUY' : 'SELL';
467
- addLog('signal', `${side} Signal @ ${data.entry?.toFixed(2) || 'N/A'} | SL: ${data.stop?.toFixed(2) || 'N/A'} | TP: ${data.target?.toFixed(2) || 'N/A'}`);
468
- displayUI();
456
+ printLog('signal', `${side} Signal @ ${data.entry?.toFixed(2) || 'N/A'} | SL: ${data.stop?.toFixed(2) || 'N/A'} | TP: ${data.target?.toFixed(2) || 'N/A'}`);
469
457
 
470
458
  // Execute order via PropFirm API if connected
471
459
  if (hqxConnected && service) {
@@ -478,17 +466,20 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
478
466
  stats.pnl += data.pnl || 0;
479
467
  if (data.pnl > 0) {
480
468
  stats.wins++;
481
- addLog('trade', `Closed +$${data.pnl.toFixed(2)} (${data.reason || 'take_profit'})`);
469
+ printLog('trade', `Closed +$${data.pnl.toFixed(2)} (${data.reason || 'take_profit'})`);
482
470
  } else {
483
471
  stats.losses++;
484
- addLog('trade', `Closed -$${Math.abs(data.pnl).toFixed(2)} (${data.reason || 'stop_loss'})`);
472
+ printLog('trade', `Closed -$${Math.abs(data.pnl).toFixed(2)} (${data.reason || 'stop_loss'})`);
485
473
  }
486
474
  stats.winRate = stats.trades > 0 ? ((stats.wins / stats.trades) * 100).toFixed(1) : '0.0';
487
475
 
476
+ // Print updated stats
477
+ printLog('info', `Stats: Trades: ${stats.trades} | Wins: ${stats.wins} | P&L: $${stats.pnl.toFixed(2)}`);
478
+
488
479
  // Check daily target
489
480
  if (stats.pnl >= dailyTarget) {
490
481
  stopReason = 'target';
491
- addLog('success', `Daily target reached! +$${stats.pnl.toFixed(2)}`);
482
+ printLog('success', `Daily target reached! +$${stats.pnl.toFixed(2)}`);
492
483
  algoRunning = false;
493
484
  if (hqxConnected) {
494
485
  hqxServer.stopAlgo();
@@ -498,36 +489,35 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
498
489
  // Check max risk
499
490
  if (stats.pnl <= -maxRisk) {
500
491
  stopReason = 'risk';
501
- addLog('error', `Max risk reached! -$${Math.abs(stats.pnl).toFixed(2)}`);
492
+ printLog('error', `Max risk reached! -$${Math.abs(stats.pnl).toFixed(2)}`);
502
493
  algoRunning = false;
503
494
  if (hqxConnected) {
504
495
  hqxServer.stopAlgo();
505
496
  }
506
497
  }
507
-
508
- displayUI();
509
498
  });
510
499
 
511
500
  hqxServer.on('stats', (data) => {
512
501
  stats = { ...stats, ...data };
513
- displayUI();
502
+ // Don't print stats every time, only on trades
514
503
  });
515
504
 
516
505
  hqxServer.on('error', (data) => {
517
- addLog('error', data.message || 'Unknown error');
518
- displayUI();
506
+ printLog('error', data.message || 'Unknown error');
519
507
  });
520
508
 
521
509
  hqxServer.on('disconnected', () => {
522
510
  hqxConnected = false;
523
- addLog('warning', 'Disconnected from HQX Server');
524
- displayUI();
511
+ printLog('warning', 'Disconnected from HQX Server');
525
512
  });
526
513
 
514
+ // Display header once
515
+ displayUI();
516
+
527
517
  // Start algo
528
518
  if (hqxConnected) {
529
- addLog('info', 'Starting HQX Ultra-Scalping...');
530
- addLog('info', `Target: $${dailyTarget.toFixed(2)} | Risk: $${maxRisk.toFixed(2)}`);
519
+ printLog('info', 'Starting HQX Ultra-Scalping...');
520
+ printLog('info', `Target: $${dailyTarget.toFixed(2)} | Risk: $${maxRisk.toFixed(2)}`);
531
521
  hqxServer.startAlgo({
532
522
  accountId: account.accountId,
533
523
  contractId: contract.id || contract.contractId,
@@ -540,13 +530,11 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
540
530
  });
541
531
  algoRunning = true;
542
532
  } else {
543
- addLog('warning', 'Running in offline demo mode');
544
- addLog('info', 'No real trades will be executed');
533
+ printLog('warning', 'Running in offline demo mode');
534
+ printLog('info', 'No real trades will be executed');
545
535
  algoRunning = true;
546
536
  }
547
537
 
548
- displayUI();
549
-
550
538
  // Wait for X key OR auto-stop (target/risk reached)
551
539
  await new Promise((resolve) => {
552
540
  // Check for auto-stop every 500ms
@@ -579,31 +567,28 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
579
567
  });
580
568
 
581
569
  // Stop algo
570
+ console.log();
582
571
  if (!stopReason) {
583
- addLog('warning', 'Stopping algo...');
572
+ printLog('warning', 'Stopping algo...');
584
573
  }
585
574
 
586
575
  // Cancel all pending orders and close positions
587
- addLog('info', 'Cancelling pending orders...');
588
- displayUI();
576
+ printLog('info', 'Cancelling pending orders...');
589
577
 
590
578
  try {
591
579
  // Cancel all orders
592
580
  const cancelResult = await service.cancelAllOrders(account.accountId);
593
581
  if (cancelResult.success) {
594
- addLog('success', 'All pending orders cancelled');
582
+ printLog('success', 'All pending orders cancelled');
595
583
  } else {
596
- addLog('warning', 'No pending orders to cancel');
584
+ printLog('warning', 'No pending orders to cancel');
597
585
  }
598
586
  } catch (e) {
599
- addLog('warning', 'Could not cancel orders: ' + e.message);
587
+ printLog('warning', 'Could not cancel orders: ' + e.message);
600
588
  }
601
589
 
602
- displayUI();
603
-
604
590
  // Close all positions for this symbol
605
- addLog('info', 'Closing open positions...');
606
- displayUI();
591
+ printLog('info', 'Closing open positions...');
607
592
 
608
593
  try {
609
594
  const positions = await service.getPositions(account.accountId);
@@ -616,20 +601,18 @@ const launchAlgo = async (service, account, contract, numContracts, dailyTarget,
616
601
  if (symbolPos && symbolPos.quantity !== 0) {
617
602
  const closeResult = await service.closePosition(account.accountId, symbolPos.contractId || symbolPos.symbol);
618
603
  if (closeResult.success) {
619
- addLog('success', `Position closed: ${Math.abs(symbolPos.quantity)} ${symbol}`);
604
+ printLog('success', `Position closed: ${Math.abs(symbolPos.quantity)} ${symbol}`);
620
605
  } else {
621
- addLog('error', 'Failed to close position: ' + (closeResult.error || 'Unknown'));
606
+ printLog('error', 'Failed to close position: ' + (closeResult.error || 'Unknown'));
622
607
  }
623
608
  } else {
624
- addLog('info', 'No open position to close');
609
+ printLog('info', 'No open position to close');
625
610
  }
626
611
  }
627
612
  } catch (e) {
628
- addLog('warning', 'Could not close positions: ' + e.message);
613
+ printLog('warning', 'Could not close positions: ' + e.message);
629
614
  }
630
615
 
631
- displayUI();
632
-
633
616
  if (hqxConnected && algoRunning) {
634
617
  hqxServer.stopAlgo();
635
618
  }
@@ -1004,7 +987,7 @@ const launchCopyTrading = async (config) => {
1004
987
  let stopReason = null;
1005
988
  let lastLeadPosition = null;
1006
989
  const logs = [];
1007
- const MAX_LOGS = 12;
990
+ const MAX_LOGS = 25;
1008
991
 
1009
992
  const stats = {
1010
993
  copiedTrades: 0,