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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "2.5.39",
3
+ "version": "2.5.40",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -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
- // Stats line
612
- const durationMin = behaviorData.duration ? Math.floor(behaviorData.duration / 60000) : 0;
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
- winningPatterns: [],
87
- losingPatterns: [],
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
  };