hedgequantx 2.5.39 → 2.5.40
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 +1 -1
- package/src/pages/stats.js +10 -3
- package/src/services/ai/strategy-supervisor.js +166 -5
package/package.json
CHANGED
package/src/pages/stats.js
CHANGED
|
@@ -608,12 +608,19 @@ const showStats = async (service) => {
|
|
|
608
608
|
// Empty line
|
|
609
609
|
console.log(chalk.cyan('\u2551') + ' '.repeat(behaviorInnerWidth) + chalk.cyan('\u2551'));
|
|
610
610
|
|
|
611
|
-
//
|
|
612
|
-
const
|
|
613
|
-
const statsLine = ` CURRENT: ${barColors[currentAction](currentAction)} | PATTERNS: ${learningStats.patternsLearned.total} (${learningStats.patternsLearned.winning}W/${learningStats.patternsLearned.losing}L) | OPTIMIZATIONS: ${learningStats.optimizations}`;
|
|
611
|
+
// Current session stats line
|
|
612
|
+
const statsLine = ` CURRENT: ${barColors[currentAction](currentAction)} | SESSION PATTERNS: ${learningStats.patternsLearned.total} (${learningStats.patternsLearned.winning}W/${learningStats.patternsLearned.losing}L) | OPTIMIZATIONS: ${learningStats.optimizations}`;
|
|
614
613
|
const statsLen = statsLine.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
615
614
|
console.log(chalk.cyan('\u2551') + statsLine + ' '.repeat(Math.max(0, behaviorInnerWidth - statsLen)) + chalk.cyan('\u2551'));
|
|
616
615
|
|
|
616
|
+
// Lifetime stats line (memory across sessions)
|
|
617
|
+
const lifetimeStats = StrategySupervisor.getLifetimeStats();
|
|
618
|
+
if (lifetimeStats.totalSessions > 0) {
|
|
619
|
+
const lifetimeLine = ` LIFETIME: ${lifetimeStats.totalSessions} sessions | ${lifetimeStats.totalTrades} trades | WR: ${lifetimeStats.lifetimeWinRate} | P&L: $${lifetimeStats.lifetimePnL.toFixed(2)} | ${lifetimeStats.patternsLearned.winning + lifetimeStats.patternsLearned.losing} patterns learned`;
|
|
620
|
+
const lifetimeLen = lifetimeLine.length;
|
|
621
|
+
console.log(chalk.cyan('\u2551') + chalk.magenta(lifetimeLine) + ' '.repeat(Math.max(0, behaviorInnerWidth - lifetimeLen)) + chalk.cyan('\u2551'));
|
|
622
|
+
}
|
|
623
|
+
|
|
617
624
|
drawBoxFooter(boxWidth);
|
|
618
625
|
}
|
|
619
626
|
|
|
@@ -8,12 +8,114 @@
|
|
|
8
8
|
* 2. LEARN - Analyze winning/losing trades to identify patterns
|
|
9
9
|
* 3. OPTIMIZE - Suggest and apply parameter improvements
|
|
10
10
|
* 4. SUPERVISE - Monitor risk and intervene when necessary
|
|
11
|
+
* 5. PERSIST - Save learned patterns and optimizations between sessions
|
|
11
12
|
*
|
|
12
13
|
* In CONSENSUS mode (2+ agents), ALL agents must agree before applying changes.
|
|
13
14
|
*/
|
|
14
15
|
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const os = require('os');
|
|
15
19
|
const { analyzePerformance, getMarketAdvice, callAI } = require('./client');
|
|
16
20
|
|
|
21
|
+
// Path for persisted learning data
|
|
22
|
+
const DATA_DIR = path.join(os.homedir(), '.hqx');
|
|
23
|
+
const LEARNING_FILE = path.join(DATA_DIR, 'ai-learning.json');
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Load persisted learning data from disk
|
|
27
|
+
* Called on startup to restore previous learnings
|
|
28
|
+
*/
|
|
29
|
+
const loadLearningData = () => {
|
|
30
|
+
try {
|
|
31
|
+
if (!fs.existsSync(DATA_DIR)) {
|
|
32
|
+
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (fs.existsSync(LEARNING_FILE)) {
|
|
36
|
+
const data = JSON.parse(fs.readFileSync(LEARNING_FILE, 'utf8'));
|
|
37
|
+
return {
|
|
38
|
+
winningPatterns: data.winningPatterns || [],
|
|
39
|
+
losingPatterns: data.losingPatterns || [],
|
|
40
|
+
optimizations: data.optimizations || [],
|
|
41
|
+
totalSessions: data.totalSessions || 0,
|
|
42
|
+
totalTrades: data.totalTrades || 0,
|
|
43
|
+
totalWins: data.totalWins || 0,
|
|
44
|
+
totalLosses: data.totalLosses || 0,
|
|
45
|
+
lifetimePnL: data.lifetimePnL || 0,
|
|
46
|
+
lastUpdated: data.lastUpdated || null
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
} catch (e) {
|
|
50
|
+
// Silent fail - start fresh
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
winningPatterns: [],
|
|
55
|
+
losingPatterns: [],
|
|
56
|
+
optimizations: [],
|
|
57
|
+
totalSessions: 0,
|
|
58
|
+
totalTrades: 0,
|
|
59
|
+
totalWins: 0,
|
|
60
|
+
totalLosses: 0,
|
|
61
|
+
lifetimePnL: 0,
|
|
62
|
+
lastUpdated: null
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Save learning data to disk
|
|
68
|
+
* Called after each trade and on session end
|
|
69
|
+
*/
|
|
70
|
+
const saveLearningData = () => {
|
|
71
|
+
try {
|
|
72
|
+
if (!fs.existsSync(DATA_DIR)) {
|
|
73
|
+
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Load existing data first
|
|
77
|
+
const existing = loadLearningData();
|
|
78
|
+
|
|
79
|
+
// Merge with current session data
|
|
80
|
+
const dataToSave = {
|
|
81
|
+
// Patterns - keep last 100 of each type (merge and dedupe by timestamp)
|
|
82
|
+
winningPatterns: mergePatterns(existing.winningPatterns, supervisorState.winningPatterns, 100),
|
|
83
|
+
losingPatterns: mergePatterns(existing.losingPatterns, supervisorState.losingPatterns, 100),
|
|
84
|
+
|
|
85
|
+
// Optimizations history - keep last 50
|
|
86
|
+
optimizations: [...existing.optimizations, ...supervisorState.optimizations].slice(-50),
|
|
87
|
+
|
|
88
|
+
// Lifetime stats
|
|
89
|
+
totalSessions: existing.totalSessions + (supervisorState.active ? 0 : 1),
|
|
90
|
+
totalTrades: existing.totalTrades + supervisorState.performance.trades,
|
|
91
|
+
totalWins: existing.totalWins + supervisorState.performance.wins,
|
|
92
|
+
totalLosses: existing.totalLosses + supervisorState.performance.losses,
|
|
93
|
+
lifetimePnL: existing.lifetimePnL + supervisorState.performance.totalPnL,
|
|
94
|
+
|
|
95
|
+
// Metadata
|
|
96
|
+
lastUpdated: new Date().toISOString()
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
fs.writeFileSync(LEARNING_FILE, JSON.stringify(dataToSave, null, 2));
|
|
100
|
+
return true;
|
|
101
|
+
} catch (e) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Merge pattern arrays, keeping most recent unique entries
|
|
108
|
+
*/
|
|
109
|
+
const mergePatterns = (existing, current, maxCount) => {
|
|
110
|
+
const merged = [...existing, ...current];
|
|
111
|
+
|
|
112
|
+
// Sort by timestamp descending (most recent first)
|
|
113
|
+
merged.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));
|
|
114
|
+
|
|
115
|
+
// Keep only maxCount most recent
|
|
116
|
+
return merged.slice(0, maxCount);
|
|
117
|
+
};
|
|
118
|
+
|
|
17
119
|
// Singleton supervisor state
|
|
18
120
|
let supervisorState = {
|
|
19
121
|
active: false,
|
|
@@ -69,10 +171,14 @@ let analysisInterval = null;
|
|
|
69
171
|
|
|
70
172
|
/**
|
|
71
173
|
* Initialize supervisor with strategy and agents
|
|
174
|
+
* Loads previous learning data to continue improving
|
|
72
175
|
*/
|
|
73
176
|
const initialize = (strategy, agents, service, accountId) => {
|
|
74
177
|
const now = Date.now();
|
|
75
178
|
|
|
179
|
+
// Load previously learned patterns and optimizations
|
|
180
|
+
const previousLearning = loadLearningData();
|
|
181
|
+
|
|
76
182
|
supervisorState = {
|
|
77
183
|
...supervisorState,
|
|
78
184
|
active: true,
|
|
@@ -83,8 +189,17 @@ const initialize = (strategy, agents, service, accountId) => {
|
|
|
83
189
|
ticks: [],
|
|
84
190
|
signals: [],
|
|
85
191
|
trades: [],
|
|
86
|
-
|
|
87
|
-
|
|
192
|
+
// Restore previous learning
|
|
193
|
+
winningPatterns: previousLearning.winningPatterns || [],
|
|
194
|
+
losingPatterns: previousLearning.losingPatterns || [],
|
|
195
|
+
previousOptimizations: previousLearning.optimizations || [],
|
|
196
|
+
lifetimeStats: {
|
|
197
|
+
sessions: previousLearning.totalSessions || 0,
|
|
198
|
+
trades: previousLearning.totalTrades || 0,
|
|
199
|
+
wins: previousLearning.totalWins || 0,
|
|
200
|
+
losses: previousLearning.totalLosses || 0,
|
|
201
|
+
pnl: previousLearning.lifetimePnL || 0
|
|
202
|
+
},
|
|
88
203
|
performance: {
|
|
89
204
|
trades: 0,
|
|
90
205
|
wins: 0,
|
|
@@ -124,7 +239,7 @@ const initialize = (strategy, agents, service, accountId) => {
|
|
|
124
239
|
};
|
|
125
240
|
|
|
126
241
|
/**
|
|
127
|
-
* Stop supervisor
|
|
242
|
+
* Stop supervisor and save learned data
|
|
128
243
|
*/
|
|
129
244
|
const stop = () => {
|
|
130
245
|
if (analysisInterval) {
|
|
@@ -132,11 +247,16 @@ const stop = () => {
|
|
|
132
247
|
analysisInterval = null;
|
|
133
248
|
}
|
|
134
249
|
|
|
250
|
+
// Save all learned data before stopping
|
|
251
|
+
const saved = saveLearningData();
|
|
252
|
+
|
|
135
253
|
const summary = {
|
|
136
254
|
...supervisorState.performance,
|
|
137
255
|
optimizationsApplied: supervisorState.optimizations.length,
|
|
138
256
|
winningPatterns: supervisorState.winningPatterns.length,
|
|
139
|
-
losingPatterns: supervisorState.losingPatterns.length
|
|
257
|
+
losingPatterns: supervisorState.losingPatterns.length,
|
|
258
|
+
dataSaved: saved,
|
|
259
|
+
lifetimeStats: supervisorState.lifetimeStats
|
|
140
260
|
};
|
|
141
261
|
|
|
142
262
|
supervisorState.active = false;
|
|
@@ -768,6 +888,44 @@ const getLearningStats = () => {
|
|
|
768
888
|
};
|
|
769
889
|
};
|
|
770
890
|
|
|
891
|
+
/**
|
|
892
|
+
* Get lifetime stats across all sessions
|
|
893
|
+
* Shows cumulative learning progress
|
|
894
|
+
*/
|
|
895
|
+
const getLifetimeStats = () => {
|
|
896
|
+
const saved = loadLearningData();
|
|
897
|
+
|
|
898
|
+
return {
|
|
899
|
+
totalSessions: saved.totalSessions,
|
|
900
|
+
totalTrades: saved.totalTrades,
|
|
901
|
+
totalWins: saved.totalWins,
|
|
902
|
+
totalLosses: saved.totalLosses,
|
|
903
|
+
lifetimeWinRate: saved.totalTrades > 0 ?
|
|
904
|
+
((saved.totalWins / saved.totalTrades) * 100).toFixed(1) + '%' : 'N/A',
|
|
905
|
+
lifetimePnL: saved.lifetimePnL,
|
|
906
|
+
patternsLearned: {
|
|
907
|
+
winning: saved.winningPatterns?.length || 0,
|
|
908
|
+
losing: saved.losingPatterns?.length || 0
|
|
909
|
+
},
|
|
910
|
+
optimizationsApplied: saved.optimizations?.length || 0,
|
|
911
|
+
lastUpdated: saved.lastUpdated
|
|
912
|
+
};
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Clear all learned data (reset AI memory)
|
|
917
|
+
*/
|
|
918
|
+
const clearLearningData = () => {
|
|
919
|
+
try {
|
|
920
|
+
if (fs.existsSync(LEARNING_FILE)) {
|
|
921
|
+
fs.unlinkSync(LEARNING_FILE);
|
|
922
|
+
}
|
|
923
|
+
return true;
|
|
924
|
+
} catch (e) {
|
|
925
|
+
return false;
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
|
|
771
929
|
module.exports = {
|
|
772
930
|
initialize,
|
|
773
931
|
stop,
|
|
@@ -779,5 +937,8 @@ module.exports = {
|
|
|
779
937
|
getStatus,
|
|
780
938
|
analyzeAndOptimize,
|
|
781
939
|
getBehaviorHistory,
|
|
782
|
-
getLearningStats
|
|
940
|
+
getLearningStats,
|
|
941
|
+
getLifetimeStats,
|
|
942
|
+
clearLearningData,
|
|
943
|
+
loadLearningData
|
|
783
944
|
};
|