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
|
@@ -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 (
|
|
1422
|
+
// Build symbols string for UI (clean names without X1 suffix, symbol only)
|
|
1420
1423
|
const symbolsDisplay = contracts.map(c => {
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
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
|
|
package/src/pages/algo/ui.js
CHANGED
|
@@ -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
|
/**
|