hedgequantx 2.6.138 → 2.6.140

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": "2.6.138",
3
+ "version": "2.6.140",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -948,8 +948,6 @@ const launchCopyTrading = async (config) => {
948
948
  };
949
949
  }
950
950
 
951
- ui.cleanup();
952
-
953
951
  // Duration
954
952
  const durationMs = Date.now() - stats.startTime;
955
953
  const hours = Math.floor(durationMs / 3600000);
@@ -961,6 +959,11 @@ const launchCopyTrading = async (config) => {
961
959
  ? `${minutes}m ${seconds}s`
962
960
  : `${seconds}s`;
963
961
 
962
+ // Close log file with session summary
963
+ try { ui.closeLog(stats); } catch {}
964
+
965
+ ui.cleanup();
966
+
964
967
  // Summary
965
968
  renderSessionSummary(stats, stopReason);
966
969
  await prompts.waitForEnter();
@@ -1368,6 +1368,9 @@ const launchAlgo = async (service, account, contract, config) => {
1368
1368
  // Cleanup keyboard handler
1369
1369
  try { if (cleanupKeys) cleanupKeys(); } catch {}
1370
1370
 
1371
+ // Close log file with session summary
1372
+ try { ui.closeLog(stats); } catch {}
1373
+
1371
1374
  // Cleanup UI
1372
1375
  try { ui.cleanup(); } catch {}
1373
1376
 
@@ -1416,11 +1419,12 @@ const launchMultiSymbolRithmic = async (service, account, contracts, config) =>
1416
1419
  : 'HQX *****';
1417
1420
  const rithmicAccountId = account.rithmicAccountId || account.accountId;
1418
1421
 
1419
- // Build symbols string for UI (with quantities)
1422
+ // Build symbols string for UI (clean names without X1 suffix, symbol only)
1420
1423
  const symbolsDisplay = contracts.map(c => {
1421
- const name = c.name || c.symbol;
1422
- const qty = c.qty || 1;
1423
- return `${name}x${qty}`;
1424
+ let name = c.name || c.symbol;
1425
+ // Remove X1, X2 suffix (Rithmic internal suffixes)
1426
+ name = name.replace(/X\d+$/, '');
1427
+ return name;
1424
1428
  }).join(', ');
1425
1429
 
1426
1430
  const ui = new AlgoUI({
@@ -1502,6 +1506,22 @@ const launchMultiSymbolRithmic = async (service, account, contracts, config) =>
1502
1506
  };
1503
1507
  });
1504
1508
 
1509
+ // ═══════════════════════════════════════════════════════════════════════════
1510
+ // AI SUPERVISOR INITIALIZATION (same as single-symbol)
1511
+ // ═══════════════════════════════════════════════════════════════════════════
1512
+ let aiAgentCount = 0;
1513
+ if (enableAI) {
1514
+ const aiAgents = aiService.getAgents();
1515
+ aiAgentCount = aiAgents.length;
1516
+ stats.agentCount = aiAgentCount;
1517
+ if (aiAgents.length > 0) {
1518
+ // Use first symbol's strategy for AI supervisor
1519
+ const firstContract = contracts[0];
1520
+ const firstSymbolName = firstContract.name || firstContract.symbol;
1521
+ // Strategy will be created below, so we init supervisor after strategies
1522
+ }
1523
+ }
1524
+
1505
1525
  // ═══════════════════════════════════════════════════════════════════════════
1506
1526
  // POSITION MANAGERS & STRATEGIES - One per symbol
1507
1527
  // ═══════════════════════════════════════════════════════════════════════════
@@ -1675,6 +1695,22 @@ const launchMultiSymbolRithmic = async (service, account, contracts, config) =>
1675
1695
  });
1676
1696
  });
1677
1697
 
1698
+ // ═══════════════════════════════════════════════════════════════════════════
1699
+ // AI SUPERVISOR - Initialize after strategies are created
1700
+ // ═══════════════════════════════════════════════════════════════════════════
1701
+ if (enableAI && aiAgentCount > 0) {
1702
+ const aiAgents = aiService.getAgents();
1703
+ const firstSymbol = Object.keys(strategies)[0];
1704
+ const firstStrategy = strategies[firstSymbol];
1705
+ const supervisorResult = StrategySupervisor.initialize(firstStrategy, aiAgents, service, rithmicAccountId);
1706
+ stats.aiSupervision = supervisorResult.success;
1707
+ stats.aiMode = supervisorResult.mode;
1708
+
1709
+ if (stats.aiSupervision) {
1710
+ algoLogger.info(ui, 'AI SUPERVISION', `${aiAgentCount} agent(s) - ${stats.aiMode} mode - LEARNING ACTIVE`);
1711
+ }
1712
+ }
1713
+
1678
1714
  // ═══════════════════════════════════════════════════════════════════════════
1679
1715
  // MARKET DATA FEED - Single connection, multiple subscriptions
1680
1716
  // ═══════════════════════════════════════════════════════════════════════════
@@ -2044,16 +2080,8 @@ const launchMultiSymbolRithmic = async (service, account, contracts, config) =>
2044
2080
 
2045
2081
  // Cleanup keyboard
2046
2082
  try { if (cleanupKeys) cleanupKeys(); } catch {}
2047
- try { ui.cleanup(); } catch {}
2048
-
2049
- try {
2050
- if (process.stdin.isTTY) process.stdin.setRawMode(false);
2051
- process.stdin.resume();
2052
- } catch {}
2053
2083
 
2054
- // ═══════════════════════════════════════════════════════════════════════════
2055
- // SESSION SUMMARY
2056
- // ═══════════════════════════════════════════════════════════════════════════
2084
+ // Calculate duration before closeLog
2057
2085
  const durationMs = Date.now() - stats.startTime;
2058
2086
  const hours = Math.floor(durationMs / 3600000);
2059
2087
  const minutes = Math.floor((durationMs % 3600000) / 60000);
@@ -2064,6 +2092,19 @@ const launchMultiSymbolRithmic = async (service, account, contracts, config) =>
2064
2092
  ? `${minutes}m ${seconds}s`
2065
2093
  : `${seconds}s`;
2066
2094
 
2095
+ // Close log file with session summary
2096
+ try { ui.closeLog(stats); } catch {}
2097
+
2098
+ try { ui.cleanup(); } catch {}
2099
+
2100
+ try {
2101
+ if (process.stdin.isTTY) process.stdin.setRawMode(false);
2102
+ process.stdin.resume();
2103
+ } catch {}
2104
+
2105
+ // ═══════════════════════════════════════════════════════════════════════════
2106
+ // SESSION SUMMARY (duration already calculated above)
2107
+ // ═══════════════════════════════════════════════════════════════════════════
2067
2108
  // Render multi-symbol summary with same style as single-symbol
2068
2109
  renderMultiSymbolSummary(stats, stopReason, stats.symbolStats);
2069
2110
 
@@ -4,6 +4,9 @@
4
4
  */
5
5
 
6
6
  const chalk = require('chalk');
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const os = require('os');
7
10
 
8
11
  // Box drawing characters
9
12
  const BOX = {
@@ -146,12 +149,54 @@ class AlgoUI {
146
149
  this.lastStatsHash = ''; // Track stats changes
147
150
  this.lastLogsHash = ''; // Track logs changes
148
151
  this.lastSpinnerUpdate = 0; // Rate limit spinner updates
152
+
153
+ // Session log file
154
+ this.logFile = null;
155
+ this.logStream = null;
156
+ this._initLogFile();
157
+ }
158
+
159
+ _initLogFile() {
160
+ try {
161
+ // Create logs directory in user home
162
+ const logsDir = path.join(os.homedir(), '.hqx', 'logs');
163
+ if (!fs.existsSync(logsDir)) {
164
+ fs.mkdirSync(logsDir, { recursive: true });
165
+ }
166
+
167
+ // Create session log file with timestamp
168
+ const now = new Date();
169
+ const dateStr = now.toISOString().split('T')[0]; // YYYY-MM-DD
170
+ const timeStr = now.toTimeString().split(' ')[0].replace(/:/g, '-'); // HH-MM-SS
171
+ const mode = this.config.mode || 'algo';
172
+ this.logFile = path.join(logsDir, `session_${mode}_${dateStr}_${timeStr}.log`);
173
+
174
+ // Open write stream
175
+ this.logStream = fs.createWriteStream(this.logFile, { flags: 'a' });
176
+
177
+ // Write session header
178
+ this.logStream.write(`\n${'='.repeat(80)}\n`);
179
+ this.logStream.write(`HQX ALGO SESSION LOG\n`);
180
+ this.logStream.write(`Mode: ${this.config.mode || 'unknown'}\n`);
181
+ this.logStream.write(`Started: ${now.toISOString()}\n`);
182
+ this.logStream.write(`${'='.repeat(80)}\n\n`);
183
+ } catch (e) {
184
+ // Silent fail - don't break UI if logging fails
185
+ this.logStream = null;
186
+ }
149
187
  }
150
188
 
151
189
  addLog(type, message) {
152
190
  const timestamp = new Date().toLocaleTimeString();
153
191
  this.logs.push({ timestamp, type, message });
154
192
  if (this.logs.length > this.maxLogs) this.logs.shift();
193
+
194
+ // Write to log file
195
+ if (this.logStream) {
196
+ const isoTime = new Date().toISOString();
197
+ const logLine = `[${isoTime}] [${type.toUpperCase().padEnd(8)}] ${message}\n`;
198
+ this.logStream.write(logLine);
199
+ }
155
200
  }
156
201
 
157
202
  _line(text) {
@@ -443,6 +488,38 @@ class AlgoUI {
443
488
  process.stdout.write('\x1B[?25h');
444
489
  console.clear();
445
490
  }
491
+
492
+ /**
493
+ * Close log file with session summary
494
+ */
495
+ closeLog(stats) {
496
+ if (this.logStream) {
497
+ try {
498
+ const now = new Date();
499
+ this.logStream.write(`\n${'='.repeat(80)}\n`);
500
+ this.logStream.write(`SESSION ENDED: ${now.toISOString()}\n`);
501
+ if (stats) {
502
+ this.logStream.write(`SUMMARY:\n`);
503
+ this.logStream.write(` Trades: ${stats.trades || 0}\n`);
504
+ this.logStream.write(` Wins: ${stats.wins || 0}\n`);
505
+ this.logStream.write(` Losses: ${stats.losses || 0}\n`);
506
+ this.logStream.write(` Session P&L: $${(stats.sessionPnl || 0).toFixed(2)}\n`);
507
+ this.logStream.write(` Duration: ${stats.duration || 'N/A'}\n`);
508
+ }
509
+ this.logStream.write(`${'='.repeat(80)}\n`);
510
+ this.logStream.end();
511
+ } catch (e) {
512
+ // Silent fail
513
+ }
514
+ }
515
+ }
516
+
517
+ /**
518
+ * Get log file path
519
+ */
520
+ getLogFile() {
521
+ return this.logFile;
522
+ }
446
523
  }
447
524
 
448
525
  /**