hedgequantx 1.2.133 → 1.2.135

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": "1.2.133",
3
+ "version": "1.2.135",
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
@@ -1467,7 +1467,10 @@ const launchCopyTrading = async (config) => {
1467
1467
  followerTrades: 0,
1468
1468
  signals: 0,
1469
1469
  errors: 0,
1470
- pnl: 0
1470
+ pnl: 0,
1471
+ trades: 0,
1472
+ wins: 0,
1473
+ losses: 0
1471
1474
  };
1472
1475
 
1473
1476
  // Log colors
@@ -1538,6 +1541,11 @@ const launchCopyTrading = async (config) => {
1538
1541
  const pnlColor = stats.pnl >= 0 ? chalk.green : chalk.red;
1539
1542
  const pnlStr = (stats.pnl >= 0 ? '+$' : '-$') + Math.abs(stats.pnl).toFixed(2);
1540
1543
 
1544
+ // Latency formatting
1545
+ const latencyMs = latency > 0 ? latency : 0;
1546
+ const latencyStr = `${latencyMs}ms`;
1547
+ const latencyColor = latencyMs < 100 ? chalk.green : (latencyMs < 300 ? chalk.yellow : chalk.red);
1548
+
1541
1549
  // Current date
1542
1550
  const now = new Date();
1543
1551
  const dateStr = now.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' });
@@ -1626,6 +1634,12 @@ const launchCopyTrading = async (config) => {
1626
1634
  const r5c2plain = ` Copied: ${stats.copiedTrades} Errors: ${stats.errors}`;
1627
1635
  const r5c2 = r5c2text + safePad(colR - r5c2plain.length);
1628
1636
 
1637
+ // Row 6: Trades + W/L | Latency
1638
+ const r6c1text = ` Trades: ${chalk.cyan(stats.trades || 0)} W/L: ${chalk.green(stats.wins || 0)}/${chalk.red(stats.losses || 0)}`;
1639
+ const r6c1plain = ` Trades: ${stats.trades || 0} W/L: ${stats.wins || 0}/${stats.losses || 0}`;
1640
+ const r6c1 = r6c1text + safePad(colL - r6c1plain.length);
1641
+ const r6c2 = buildCell('Latency', latencyStr, latencyColor, colR);
1642
+
1629
1643
  // Grid separators
1630
1644
  const GRID_TOP = '\u2560' + '\u2550'.repeat(colL) + '\u2564' + '\u2550'.repeat(colR) + '\u2563';
1631
1645
  const GRID_MID = '\u2560' + '\u2550'.repeat(colL) + '\u256A' + '\u2550'.repeat(colR) + '\u2563';
@@ -1642,6 +1656,8 @@ const launchCopyTrading = async (config) => {
1642
1656
  bufferLine(chalk.cyan(V) + r4c1.padded + chalk.cyan(VS) + r4c2.padded + chalk.cyan(V));
1643
1657
  bufferLine(chalk.cyan(GRID_MID));
1644
1658
  bufferLine(chalk.cyan(V) + r5c1 + chalk.cyan(VS) + r5c2 + chalk.cyan(V));
1659
+ bufferLine(chalk.cyan(GRID_MID));
1660
+ bufferLine(chalk.cyan(V) + r6c1 + chalk.cyan(VS) + r6c2.padded + chalk.cyan(V));
1645
1661
  bufferLine(chalk.cyan(GRID_BOT));
1646
1662
 
1647
1663
  // Activity log header with spinner and centered date
@@ -1824,81 +1840,95 @@ const launchCopyTrading = async (config) => {
1824
1840
  }
1825
1841
  };
1826
1842
 
1827
- // Setup HQX Server event handlers
1828
- if (hqxConnected) {
1829
- hqxServer.on('latency', (data) => {
1830
- latency = data.latency || 0;
1831
- });
1843
+ // Setup HQX Server event handlers (attach before connection, check hqxConnected inside)
1844
+ hqxServer.on('latency', (data) => {
1845
+ latency = data.latency || 0;
1846
+ });
1832
1847
 
1833
- hqxServer.on('log', (data) => {
1834
- addLog(data.type || 'info', data.message);
1835
- });
1848
+ hqxServer.on('log', (data) => {
1849
+ addLog(data.type || 'info', data.message);
1850
+ });
1836
1851
 
1837
- hqxServer.on('signal', async (data) => {
1838
- stats.signals = (stats.signals || 0) + 1;
1839
- const side = data.side === 'long' ? 'BUY' : 'SELL';
1840
- addLog('signal', `${side} Signal @ ${data.entry?.toFixed(2) || 'N/A'} | SL: ${data.stop?.toFixed(2) || 'N/A'} | TP: ${data.target?.toFixed(2) || 'N/A'}`);
1841
-
1842
- // Execute on both accounts
1852
+ hqxServer.on('signal', async (data) => {
1853
+ stats.signals = (stats.signals || 0) + 1;
1854
+ const side = data.side === 'long' ? 'BUY' : 'SELL';
1855
+ addLog('signal', `${side} Signal @ ${data.entry?.toFixed(2) || 'N/A'} | SL: ${data.stop?.toFixed(2) || 'N/A'} | TP: ${data.target?.toFixed(2) || 'N/A'}`);
1856
+
1857
+ // Execute on both accounts
1858
+ if (hqxConnected) {
1843
1859
  await executeSignalOnBothAccounts(data);
1844
- displayUI();
1845
- });
1860
+ }
1861
+ displayUI();
1862
+ });
1846
1863
 
1847
- hqxServer.on('trade', async (data) => {
1848
- stats.pnl += data.pnl || 0;
1849
- if (data.pnl > 0) {
1850
- addLog('trade', `Closed +$${data.pnl.toFixed(2)} (${data.reason || 'take_profit'})`);
1851
- } else {
1852
- addLog('loss', `Closed -$${Math.abs(data.pnl).toFixed(2)} (${data.reason || 'stop_loss'})`);
1853
- }
1864
+ hqxServer.on('trade', async (data) => {
1865
+ stats.pnl += data.pnl || 0;
1866
+ if (data.pnl > 0) {
1867
+ stats.wins = (stats.wins || 0) + 1;
1868
+ addLog('trade', `Closed +$${data.pnl.toFixed(2)} (${data.reason || 'take_profit'})`);
1869
+ } else {
1870
+ stats.losses = (stats.losses || 0) + 1;
1871
+ addLog('loss', `Closed -$${Math.abs(data.pnl).toFixed(2)} (${data.reason || 'stop_loss'})`);
1872
+ }
1873
+ stats.trades = (stats.trades || 0) + 1;
1874
+
1875
+ // Print updated stats like One Account
1876
+ const statsType = stats.pnl >= 0 ? 'info' : 'loss';
1877
+ addLog(statsType, `Stats: Trades: ${stats.trades} | Wins: ${stats.wins || 0} | P&L: $${stats.pnl.toFixed(2)}`);
1854
1878
 
1855
- // Check daily target
1856
- if (stats.pnl >= dailyTarget) {
1857
- stopReason = 'target';
1858
- addLog('success', `Daily target reached! +$${stats.pnl.toFixed(2)}`);
1859
- isRunning = false;
1860
- hqxServer.stopAlgo();
1861
- await closePositionsOnBothAccounts('target');
1862
- }
1879
+ // Check daily target
1880
+ if (stats.pnl >= dailyTarget) {
1881
+ stopReason = 'target';
1882
+ addLog('success', `Daily target reached! +$${stats.pnl.toFixed(2)}`);
1883
+ isRunning = false;
1884
+ if (hqxConnected) hqxServer.stopAlgo();
1885
+ await closePositionsOnBothAccounts('target');
1886
+ }
1863
1887
 
1864
- // Check max risk
1865
- if (stats.pnl <= -maxRisk) {
1866
- stopReason = 'risk';
1867
- addLog('error', `Max risk reached! -$${Math.abs(stats.pnl).toFixed(2)}`);
1868
- isRunning = false;
1869
- hqxServer.stopAlgo();
1870
- await closePositionsOnBothAccounts('risk');
1871
- }
1888
+ // Check max risk
1889
+ if (stats.pnl <= -maxRisk) {
1890
+ stopReason = 'risk';
1891
+ addLog('error', `Max risk reached! -$${Math.abs(stats.pnl).toFixed(2)}`);
1892
+ isRunning = false;
1893
+ if (hqxConnected) hqxServer.stopAlgo();
1894
+ await closePositionsOnBothAccounts('risk');
1895
+ }
1872
1896
 
1873
- displayUI();
1874
- });
1897
+ displayUI();
1898
+ });
1875
1899
 
1876
- hqxServer.on('stats', (data) => {
1877
- const realizedPnl = data.pnl || 0;
1878
- const unrealizedPnl = data.position?.pnl || 0;
1879
- stats.pnl = realizedPnl + unrealizedPnl;
1880
- });
1900
+ hqxServer.on('stats', (data) => {
1901
+ const realizedPnl = data.pnl || 0;
1902
+ const unrealizedPnl = data.position?.pnl || 0;
1903
+ stats.pnl = realizedPnl + unrealizedPnl;
1904
+ stats.trades = data.trades || stats.trades;
1905
+ stats.wins = data.wins || stats.wins;
1906
+ stats.losses = data.losses || stats.losses;
1907
+ });
1881
1908
 
1882
- hqxServer.on('error', (data) => {
1883
- const errorMsg = data.message || 'Unknown error';
1884
- addLog('error', errorMsg);
1885
-
1886
- // If algo failed to start, switch to monitor mode
1887
- if (errorMsg.includes('Failed to start') || errorMsg.includes('WebSocket failed') || errorMsg.includes('Échec')) {
1888
- if (hqxConnected) {
1889
- hqxConnected = false;
1890
- addLog('warning', 'Switching to Monitor Mode (watching Lead positions)');
1891
- displayUI();
1892
- }
1909
+ hqxServer.on('error', (data) => {
1910
+ const errorMsg = data.message || 'Unknown error';
1911
+ addLog('error', errorMsg);
1912
+
1913
+ // If algo failed to start, switch to monitor mode
1914
+ if (errorMsg.includes('Failed to start') || errorMsg.includes('WebSocket failed') || errorMsg.includes('Échec')) {
1915
+ if (hqxConnected) {
1916
+ hqxConnected = false;
1917
+ addLog('warning', 'Switching to Monitor Mode (watching Lead positions)');
1918
+ displayUI();
1893
1919
  }
1894
- });
1920
+ }
1921
+ });
1895
1922
 
1896
- hqxServer.on('disconnected', () => {
1897
- hqxConnected = false;
1898
- if (!stopReason) {
1899
- addLog('warning', 'HQX Server disconnected - Switching to Monitor Mode');
1900
- }
1901
- });
1923
+ hqxServer.on('disconnected', () => {
1924
+ hqxConnected = false;
1925
+ if (!stopReason) {
1926
+ addLog('warning', 'HQX Server disconnected - Switching to Monitor Mode');
1927
+ }
1928
+ });
1929
+
1930
+ // Start algo if connected
1931
+ if (hqxConnected) {
1902
1932
 
1903
1933
  // Start the Ultra-Scalping algo
1904
1934
  addLog('info', 'Starting HQX Ultra-Scalping...');
@@ -1906,6 +1936,19 @@ const launchCopyTrading = async (config) => {
1906
1936
 
1907
1937
  const propfirmToken = lead.service.getToken ? lead.service.getToken() : null;
1908
1938
  const propfirmId = lead.service.getPropfirm ? lead.service.getPropfirm() : (lead.account.propfirm || 'topstep');
1939
+
1940
+ // Get Rithmic credentials if this is a Rithmic account
1941
+ let rithmicCredentials = null;
1942
+ if (lead.service.getRithmicCredentials) {
1943
+ rithmicCredentials = lead.service.getRithmicCredentials();
1944
+ } else if (lead.account.rithmicUserId && lead.account.rithmicPassword) {
1945
+ rithmicCredentials = {
1946
+ userId: lead.account.rithmicUserId,
1947
+ password: lead.account.rithmicPassword,
1948
+ systemName: lead.account.rithmicSystem || 'Apex',
1949
+ gateway: lead.account.rithmicGateway || 'wss://rprotocol.rithmic.com:443'
1950
+ };
1951
+ }
1909
1952
 
1910
1953
  hqxServer.startAlgo({
1911
1954
  accountId: lead.account.accountId,
@@ -1916,6 +1959,7 @@ const launchCopyTrading = async (config) => {
1916
1959
  maxRisk: maxRisk,
1917
1960
  propfirm: propfirmId,
1918
1961
  propfirmToken: propfirmToken,
1962
+ rithmicCredentials: rithmicCredentials,
1919
1963
  copyTrading: true, // Flag for copy trading mode
1920
1964
  followerSymbol: follower.symbol.value,
1921
1965
  followerContracts: follower.contracts
@@ -2044,20 +2088,27 @@ const launchCopyTrading = async (config) => {
2044
2088
 
2045
2089
  // Also listen for X key
2046
2090
  if (process.stdin.isTTY) {
2047
- readline.emitKeypressEvents(process.stdin);
2048
- process.stdin.setRawMode(true);
2049
-
2050
- const onKeypress = (str, key) => {
2051
- if (key && (key.name === 'x' || key.name === 'X' || (key.ctrl && key.name === 'c'))) {
2052
- clearInterval(checkInterval);
2053
- clearInterval(monitorInterval);
2054
- process.stdin.setRawMode(false);
2055
- process.stdin.removeListener('keypress', onKeypress);
2056
- resolve();
2057
- }
2058
- };
2059
-
2060
- process.stdin.on('keypress', onKeypress);
2091
+ try {
2092
+ readline.emitKeypressEvents(process.stdin);
2093
+ process.stdin.setRawMode(true);
2094
+ process.stdin.resume();
2095
+
2096
+ const onKeypress = (str, key) => {
2097
+ if (!key) return;
2098
+ const keyName = key.name?.toLowerCase();
2099
+ if (keyName === 'x' || (key.ctrl && keyName === 'c')) {
2100
+ clearInterval(checkInterval);
2101
+ clearInterval(monitorInterval);
2102
+ process.stdin.setRawMode(false);
2103
+ process.stdin.removeListener('keypress', onKeypress);
2104
+ resolve();
2105
+ }
2106
+ };
2107
+
2108
+ process.stdin.on('keypress', onKeypress);
2109
+ } catch (e) {
2110
+ // Fallback: just wait for auto-stop
2111
+ }
2061
2112
  }
2062
2113
  });
2063
2114
 
@@ -275,7 +275,13 @@ class HQXServerService {
275
275
  dailyTarget: config.dailyTarget,
276
276
  maxRisk: config.maxRisk,
277
277
  propfirm: config.propfirm,
278
- propfirmToken: config.propfirmToken
278
+ propfirmToken: config.propfirmToken,
279
+ // Rithmic credentials (for Apex, TopstepTrader Rithmic, etc.)
280
+ rithmicCredentials: config.rithmicCredentials || null,
281
+ // Copy trading mode
282
+ copyTrading: config.copyTrading || false,
283
+ followerSymbol: config.followerSymbol,
284
+ followerContracts: config.followerContracts
279
285
  });
280
286
  }
281
287
 
@@ -625,6 +625,26 @@ class RithmicService extends EventEmitter {
625
625
  return this.loginInfo ? 'connected' : null;
626
626
  }
627
627
 
628
+ /**
629
+ * Get propfirm name
630
+ */
631
+ getPropfirm() {
632
+ return this.propfirmKey || 'apex';
633
+ }
634
+
635
+ /**
636
+ * Get Rithmic credentials for HQX Server
637
+ */
638
+ getRithmicCredentials() {
639
+ if (!this.credentials) return null;
640
+ return {
641
+ userId: this.credentials.username,
642
+ password: this.credentials.password,
643
+ systemName: this.propfirm.systemName,
644
+ gateway: this.propfirm.gateway || 'wss://rprotocol.rithmic.com:443'
645
+ };
646
+ }
647
+
628
648
  /**
629
649
  * Search contracts (stub - would need TICKER_PLANT)
630
650
  */