hedgequantx 2.6.163 → 2.7.1

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.
Files changed (146) hide show
  1. package/README.md +15 -88
  2. package/bin/cli.js +0 -11
  3. package/dist/lib/api.jsc +0 -0
  4. package/dist/lib/api2.jsc +0 -0
  5. package/dist/lib/core.jsc +0 -0
  6. package/dist/lib/core2.jsc +0 -0
  7. package/dist/lib/data.js +1 -1
  8. package/dist/lib/data.jsc +0 -0
  9. package/dist/lib/data2.jsc +0 -0
  10. package/dist/lib/decoder.jsc +0 -0
  11. package/dist/lib/m/mod1.jsc +0 -0
  12. package/dist/lib/m/mod2.jsc +0 -0
  13. package/dist/lib/n/r1.jsc +0 -0
  14. package/dist/lib/n/r2.jsc +0 -0
  15. package/dist/lib/n/r3.jsc +0 -0
  16. package/dist/lib/n/r4.jsc +0 -0
  17. package/dist/lib/n/r5.jsc +0 -0
  18. package/dist/lib/n/r6.jsc +0 -0
  19. package/dist/lib/n/r7.jsc +0 -0
  20. package/dist/lib/o/util1.jsc +0 -0
  21. package/dist/lib/o/util2.jsc +0 -0
  22. package/package.json +8 -5
  23. package/src/app.js +40 -162
  24. package/src/config/constants.js +31 -33
  25. package/src/config/propfirms.js +13 -217
  26. package/src/config/settings.js +0 -43
  27. package/src/lib/api.js +198 -0
  28. package/src/lib/api2.js +353 -0
  29. package/src/lib/core.js +539 -0
  30. package/src/lib/core2.js +341 -0
  31. package/src/lib/data.js +555 -0
  32. package/src/lib/data2.js +492 -0
  33. package/src/lib/decoder.js +599 -0
  34. package/src/lib/m/s1.js +804 -0
  35. package/src/lib/m/s2.js +34 -0
  36. package/src/lib/n/r1.js +454 -0
  37. package/src/lib/n/r2.js +514 -0
  38. package/src/lib/n/r3.js +631 -0
  39. package/src/lib/n/r4.js +401 -0
  40. package/src/lib/n/r5.js +335 -0
  41. package/src/lib/n/r6.js +425 -0
  42. package/src/lib/n/r7.js +530 -0
  43. package/src/lib/o/l1.js +44 -0
  44. package/src/lib/o/l2.js +427 -0
  45. package/src/lib/python-bridge.js +206 -0
  46. package/src/menus/connect.js +14 -176
  47. package/src/menus/dashboard.js +65 -110
  48. package/src/pages/accounts.js +18 -18
  49. package/src/pages/algo/copy-trading.js +210 -240
  50. package/src/pages/algo/index.js +41 -104
  51. package/src/pages/algo/one-account.js +386 -33
  52. package/src/pages/algo/ui.js +312 -151
  53. package/src/pages/orders.js +3 -3
  54. package/src/pages/positions.js +3 -3
  55. package/src/pages/stats/chart.js +74 -0
  56. package/src/pages/stats/display.js +228 -0
  57. package/src/pages/stats/index.js +236 -0
  58. package/src/pages/stats/metrics.js +213 -0
  59. package/src/pages/user.js +6 -6
  60. package/src/services/hqx-server/constants.js +55 -0
  61. package/src/services/hqx-server/index.js +401 -0
  62. package/src/services/hqx-server/latency.js +81 -0
  63. package/src/services/index.js +12 -3
  64. package/src/services/rithmic/accounts.js +7 -32
  65. package/src/services/rithmic/connection.js +1 -204
  66. package/src/services/rithmic/contracts.js +116 -99
  67. package/src/services/rithmic/handlers.js +21 -196
  68. package/src/services/rithmic/index.js +63 -120
  69. package/src/services/rithmic/market.js +31 -0
  70. package/src/services/rithmic/orders.js +5 -111
  71. package/src/services/rithmic/protobuf.js +384 -138
  72. package/src/services/session.js +22 -173
  73. package/src/ui/box.js +10 -18
  74. package/src/ui/index.js +1 -3
  75. package/src/ui/menu.js +1 -1
  76. package/src/utils/prompts.js +2 -2
  77. package/dist/lib/m/s1.js +0 -1
  78. package/src/menus/ai-agent-connect.js +0 -181
  79. package/src/menus/ai-agent-models.js +0 -219
  80. package/src/menus/ai-agent-oauth.js +0 -292
  81. package/src/menus/ai-agent-ui.js +0 -141
  82. package/src/menus/ai-agent.js +0 -484
  83. package/src/pages/algo/algo-config.js +0 -195
  84. package/src/pages/algo/algo-multi.js +0 -801
  85. package/src/pages/algo/algo-utils.js +0 -58
  86. package/src/pages/algo/copy-engine.js +0 -449
  87. package/src/pages/algo/custom-strategy.js +0 -459
  88. package/src/pages/algo/logger.js +0 -245
  89. package/src/pages/algo/smart-logs-data.js +0 -218
  90. package/src/pages/algo/smart-logs.js +0 -387
  91. package/src/pages/algo/ui-constants.js +0 -144
  92. package/src/pages/algo/ui-summary.js +0 -184
  93. package/src/pages/stats-calculations.js +0 -191
  94. package/src/pages/stats-ui.js +0 -381
  95. package/src/pages/stats.js +0 -339
  96. package/src/services/ai/client-analysis.js +0 -194
  97. package/src/services/ai/client-models.js +0 -333
  98. package/src/services/ai/client.js +0 -343
  99. package/src/services/ai/index.js +0 -384
  100. package/src/services/ai/oauth-anthropic.js +0 -265
  101. package/src/services/ai/oauth-gemini.js +0 -223
  102. package/src/services/ai/oauth-iflow.js +0 -269
  103. package/src/services/ai/oauth-openai.js +0 -233
  104. package/src/services/ai/oauth-qwen.js +0 -279
  105. package/src/services/ai/providers/direct-providers.js +0 -323
  106. package/src/services/ai/providers/index.js +0 -62
  107. package/src/services/ai/providers/other-providers.js +0 -104
  108. package/src/services/ai/proxy-install.js +0 -249
  109. package/src/services/ai/proxy-manager.js +0 -494
  110. package/src/services/ai/proxy-remote.js +0 -161
  111. package/src/services/ai/strategy-supervisor.js +0 -1312
  112. package/src/services/ai/supervisor-data.js +0 -195
  113. package/src/services/ai/supervisor-optimize.js +0 -215
  114. package/src/services/ai/supervisor-sync.js +0 -178
  115. package/src/services/ai/supervisor-utils.js +0 -158
  116. package/src/services/ai/supervisor.js +0 -484
  117. package/src/services/ai/validation.js +0 -250
  118. package/src/services/hqx-server-events.js +0 -110
  119. package/src/services/hqx-server-handlers.js +0 -217
  120. package/src/services/hqx-server-latency.js +0 -136
  121. package/src/services/hqx-server.js +0 -403
  122. package/src/services/position-constants.js +0 -28
  123. package/src/services/position-exit-logic.js +0 -174
  124. package/src/services/position-manager.js +0 -438
  125. package/src/services/position-momentum.js +0 -206
  126. package/src/services/projectx/accounts.js +0 -142
  127. package/src/services/projectx/index.js +0 -443
  128. package/src/services/projectx/market.js +0 -172
  129. package/src/services/projectx/stats.js +0 -110
  130. package/src/services/projectx/trading.js +0 -180
  131. package/src/services/rithmic/latency-tracker.js +0 -182
  132. package/src/services/rithmic/market-data-decoders.js +0 -229
  133. package/src/services/rithmic/market-data.js +0 -272
  134. package/src/services/rithmic/orders-fast.js +0 -246
  135. package/src/services/rithmic/proto-decoders.js +0 -403
  136. package/src/services/rithmic/specs.js +0 -146
  137. package/src/services/rithmic/trade-history.js +0 -254
  138. package/src/services/session-history.js +0 -475
  139. package/src/services/strategy/hft-signal-calc.js +0 -147
  140. package/src/services/strategy/hft-tick.js +0 -407
  141. package/src/services/strategy/recovery-math.js +0 -402
  142. package/src/services/tradovate/constants.js +0 -109
  143. package/src/services/tradovate/index.js +0 -392
  144. package/src/services/tradovate/market.js +0 -47
  145. package/src/services/tradovate/orders.js +0 -145
  146. package/src/services/tradovate/websocket.js +0 -97
@@ -1,1312 +0,0 @@
1
- /**
2
- * AI Strategy Supervisor
3
- *
4
- * Observes, learns from, and optimizes the HQX Ultra Scalping strategy in real-time.
5
- *
6
- * FUNCTIONS:
7
- * 1. OBSERVE - Receive all market data, signals, and trades in real-time
8
- * 2. LEARN - Analyze winning/losing trades to identify patterns
9
- * 3. OPTIMIZE - Suggest and apply parameter improvements
10
- * 4. SUPERVISE - Monitor risk and intervene when necessary
11
- * 5. PERSIST - Save learned patterns and optimizations between sessions
12
- *
13
- * In CONSENSUS mode (2+ agents), ALL agents must agree before applying changes.
14
- */
15
-
16
- const fs = require('fs');
17
- const path = require('path');
18
- const os = require('os');
19
- const { analyzePerformance, getMarketAdvice, callAI } = require('./client');
20
- const {
21
- calculateTrendStrength, calculateRecentRange, calculatePriceVelocity,
22
- getMarketSession, getMinutesSinceOpen, analyzePriceLevelInteraction,
23
- determineLevelType, analyzePriceAction, analyzeVolume, calculateVolatility
24
- } = require('./supervisor-utils');
25
-
26
- // Path for persisted learning data
27
-
28
- const {
29
- DATA_DIR, LEARNING_FILE, loadLearningData, getSymbolData,
30
- recordPriceLevel, analyzeNearbyLevels, saveLearningData,
31
- buildStrategyProfile, mergePatterns, mergeLevels, clearLearningData
32
- } = require('./supervisor-data');
33
-
34
- /**
35
- * Initialize supervisor with strategy and agents
36
- * Loads previous learning data to continue improving
37
- */
38
- const initialize = (strategy, agents, service, accountId) => {
39
- const now = Date.now();
40
-
41
- // Load previously learned patterns and optimizations
42
- const previousLearning = loadLearningData();
43
-
44
- supervisorState = {
45
- ...supervisorState,
46
- active: true,
47
- agents: agents || [],
48
- strategy,
49
- service,
50
- accountId,
51
- ticks: [],
52
- signals: [],
53
- trades: [],
54
- // Restore previous learning
55
- winningPatterns: previousLearning.winningPatterns || [],
56
- losingPatterns: previousLearning.losingPatterns || [],
57
- previousOptimizations: previousLearning.optimizations || [],
58
- lifetimeStats: {
59
- sessions: previousLearning.totalSessions || 0,
60
- trades: previousLearning.totalTrades || 0,
61
- wins: previousLearning.totalWins || 0,
62
- losses: previousLearning.totalLosses || 0,
63
- pnl: previousLearning.lifetimePnL || 0
64
- },
65
- performance: {
66
- trades: 0,
67
- wins: 0,
68
- losses: 0,
69
- totalPnL: 0,
70
- maxDrawdown: 0,
71
- currentDrawdown: 0,
72
- peakPnL: 0,
73
- winStreak: 0,
74
- lossStreak: 0,
75
- maxWinStreak: 0,
76
- maxLossStreak: 0
77
- },
78
- optimizations: [],
79
- lastOptimizationTime: now,
80
- behaviorHistory: [{ timestamp: now, value: 2, action: 'NORMAL' }], // Start with NORMAL
81
- behaviorStartTime: now,
82
- currentAdvice: { action: 'NORMAL', sizeMultiplier: 1.0, reason: 'Starting' }
83
- };
84
-
85
- // Start continuous analysis loop
86
- if (analysisInterval) clearInterval(analysisInterval);
87
- analysisInterval = setInterval(analyzeAndOptimize, supervisorState.optimizationInterval);
88
-
89
- // Also record behavior every 10 seconds to have smooth graph
90
- setInterval(() => {
91
- if (supervisorState.active) {
92
- recordBehavior(supervisorState.currentAdvice.action);
93
- }
94
- }, 10000);
95
-
96
- // Start agent sync interval - ensures new agents are picked up dynamically
97
- if (!supervisorState.agentSyncInterval) {
98
- supervisorState.agentSyncInterval = setInterval(syncAgents, 5000);
99
- }
100
-
101
- return {
102
- success: true,
103
- agents: agents.length,
104
- mode: agents.length >= 2 ? 'CONSENSUS' : 'INDIVIDUAL'
105
- };
106
- };
107
-
108
- /**
109
- * Sync agents with AI service
110
- * Called periodically to pick up newly added/removed agents
111
- * Ensures all agents are always connected to the supervisor
112
- */
113
- const syncAgents = () => {
114
- if (!supervisorState.active) return;
115
-
116
- try {
117
- // Dynamic import to avoid circular dependency
118
- const aiService = require('./index');
119
- const currentAgents = aiService.getAgents();
120
-
121
- if (!currentAgents) return;
122
-
123
- const currentIds = new Set(currentAgents.map(a => a.id));
124
- const supervisorIds = new Set(supervisorState.agents.map(a => a.id));
125
-
126
- // Check for new agents
127
- const newAgents = currentAgents.filter(a => !supervisorIds.has(a.id));
128
-
129
- // Check for removed agents
130
- const removedIds = [...supervisorIds].filter(id => !currentIds.has(id));
131
-
132
- // Add new agents
133
- if (newAgents.length > 0) {
134
- supervisorState.agents = [...supervisorState.agents, ...newAgents];
135
- // Log would go here if we had UI access
136
- }
137
-
138
- // Remove agents that were disconnected
139
- if (removedIds.length > 0) {
140
- supervisorState.agents = supervisorState.agents.filter(a => !removedIds.includes(a.id));
141
- }
142
-
143
- // Update mode based on current agent count
144
- supervisorState.mode = supervisorState.agents.length >= 2 ? 'CONSENSUS' : 'INDIVIDUAL';
145
-
146
- } catch (e) {
147
- // Silent fail - aiService might not be ready
148
- }
149
- };
150
-
151
- /**
152
- * Force refresh agents from AI service
153
- * Call this when you know agents have changed
154
- */
155
- const refreshAgents = () => {
156
- syncAgents();
157
- return {
158
- agents: supervisorState.agents.length,
159
- mode: supervisorState.agents.length >= 2 ? 'CONSENSUS' : 'INDIVIDUAL'
160
- };
161
- };
162
-
163
- /**
164
- * Add a single agent to the supervisor (called when agent is added)
165
- */
166
- const addAgent = (agent) => {
167
- if (!supervisorState.active) return false;
168
-
169
- // Check if already exists
170
- if (supervisorState.agents.some(a => a.id === agent.id)) {
171
- return false;
172
- }
173
-
174
- supervisorState.agents.push(agent);
175
- supervisorState.mode = supervisorState.agents.length >= 2 ? 'CONSENSUS' : 'INDIVIDUAL';
176
-
177
- return true;
178
- };
179
-
180
- /**
181
- * Remove an agent from the supervisor (called when agent is removed)
182
- */
183
- const removeAgent = (agentId) => {
184
- if (!supervisorState.active) return false;
185
-
186
- const index = supervisorState.agents.findIndex(a => a.id === agentId);
187
- if (index === -1) return false;
188
-
189
- supervisorState.agents.splice(index, 1);
190
- supervisorState.mode = supervisorState.agents.length >= 2 ? 'CONSENSUS' : 'INDIVIDUAL';
191
-
192
- return true;
193
- };
194
-
195
- /**
196
- * Get current agent count and mode
197
- */
198
- const getAgentInfo = () => {
199
- return {
200
- count: supervisorState.agents.length,
201
- mode: supervisorState.agents.length >= 2 ? 'CONSENSUS' : 'INDIVIDUAL',
202
- agents: supervisorState.agents.map(a => ({
203
- id: a.id,
204
- name: a.name,
205
- provider: a.providerId
206
- }))
207
- };
208
- };
209
-
210
- /**
211
- * Stop supervisor and save learned data
212
- */
213
- const stop = () => {
214
- if (analysisInterval) {
215
- clearInterval(analysisInterval);
216
- analysisInterval = null;
217
- }
218
-
219
- // Stop agent sync
220
- if (supervisorState.agentSyncInterval) {
221
- clearInterval(supervisorState.agentSyncInterval);
222
- supervisorState.agentSyncInterval = null;
223
- }
224
-
225
- // Save all learned data before stopping
226
- const saved = saveLearningData();
227
-
228
- const summary = {
229
- ...supervisorState.performance,
230
- optimizationsApplied: supervisorState.optimizations.length,
231
- winningPatterns: supervisorState.winningPatterns.length,
232
- losingPatterns: supervisorState.losingPatterns.length,
233
- dataSaved: saved,
234
- lifetimeStats: supervisorState.lifetimeStats
235
- };
236
-
237
- supervisorState.active = false;
238
-
239
- return summary;
240
- };
241
-
242
- /**
243
- * Feed tick data (called on every market tick)
244
- */
245
- const feedTick = (tick) => {
246
- if (!supervisorState.active) return;
247
-
248
- supervisorState.ticks.push({
249
- ...tick,
250
- timestamp: Date.now()
251
- });
252
-
253
- // Keep last 5000 ticks for pattern analysis
254
- if (supervisorState.ticks.length > 5000) {
255
- supervisorState.ticks = supervisorState.ticks.slice(-5000);
256
- }
257
- };
258
-
259
- /**
260
- * Feed signal data (called when strategy generates a signal)
261
- */
262
- const feedSignal = (signal) => {
263
- if (!supervisorState.active) return;
264
-
265
- const signalData = {
266
- ...signal,
267
- timestamp: Date.now(),
268
- ticksContext: supervisorState.ticks.slice(-50) // Last 50 ticks before signal
269
- };
270
-
271
- supervisorState.signals.push(signalData);
272
-
273
- // Keep last 100 signals
274
- if (supervisorState.signals.length > 100) {
275
- supervisorState.signals = supervisorState.signals.slice(-100);
276
- }
277
- };
278
-
279
- /**
280
- * Feed trade result (called when a trade completes)
281
- * This is where LEARNING happens
282
- *
283
- * Captures CONTEXT that API doesn't provide:
284
- * - Market conditions before entry (volatility, trend, volume)
285
- * - Price action patterns
286
- * - Time-based context (hour, day, session)
287
- * - AI state at time of trade
288
- * - Price levels interaction
289
- */
290
- const feedTradeResult = (trade) => {
291
- if (!supervisorState.active) return;
292
-
293
- const now = Date.now();
294
- const currentHour = new Date().getHours();
295
- const currentDay = new Date().getDay();
296
- const dayNames = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
297
-
298
- // Get ticks before trade for context analysis
299
- const ticksBefore = supervisorState.ticks.slice(-100);
300
- const signalUsed = supervisorState.signals[supervisorState.signals.length - 1] || null;
301
-
302
- // ========== CAPTURE MARKET CONTEXT (NOT IN API) ==========
303
- const marketContext = {
304
- // Price action analysis
305
- priceAction: analyzePriceAction(ticksBefore),
306
-
307
- // Volatility at entry
308
- volatility: calculateVolatility(ticksBefore),
309
-
310
- // Volume profile
311
- volumeProfile: analyzeVolume(ticksBefore),
312
-
313
- // Trend strength (using last 50 vs last 20 ticks)
314
- trendStrength: calculateTrendStrength(ticksBefore),
315
-
316
- // Price range in last N ticks
317
- recentRange: calculateRecentRange(ticksBefore, 50),
318
-
319
- // Speed of price movement
320
- priceVelocity: calculatePriceVelocity(ticksBefore, 20)
321
- };
322
-
323
- // ========== CAPTURE TIME CONTEXT (NOT IN API) ==========
324
- const timeContext = {
325
- hour: currentHour,
326
- dayOfWeek: dayNames[currentDay],
327
- dayNumber: currentDay,
328
- // Trading session (US market hours)
329
- session: getMarketSession(currentHour),
330
- // Minutes since session open
331
- minutesSinceOpen: getMinutesSinceOpen(currentHour),
332
- timestamp: now
333
- };
334
-
335
- // ========== CAPTURE AI STATE (NOT IN API) ==========
336
- const aiContext = {
337
- action: supervisorState.currentAdvice.action,
338
- sizeMultiplier: supervisorState.currentAdvice.sizeMultiplier,
339
- reason: supervisorState.currentAdvice.reason,
340
- currentWinStreak: supervisorState.performance.winStreak,
341
- currentLossStreak: supervisorState.performance.lossStreak,
342
- sessionPnL: supervisorState.performance.totalPnL,
343
- sessionTrades: supervisorState.performance.trades
344
- };
345
-
346
- // ========== CAPTURE PRICE LEVEL INTERACTION ==========
347
- const entryPrice = trade.price || signalUsed?.entry;
348
- const levelContext = entryPrice ? analyzePriceLevelInteraction(entryPrice, ticksBefore) : null;
349
-
350
- // Build enriched trade data
351
- const tradeData = {
352
- ...trade,
353
- timestamp: now,
354
-
355
- // Signal that generated this trade
356
- signalUsed: signalUsed ? {
357
- confidence: signalUsed.confidence,
358
- entry: signalUsed.entry,
359
- stopLoss: signalUsed.stopLoss,
360
- takeProfit: signalUsed.takeProfit,
361
- direction: signalUsed.direction
362
- } : null,
363
-
364
- // Context NOT available in API
365
- marketContext,
366
- timeContext,
367
- aiContext,
368
- levelContext,
369
-
370
- // Store symbol for level learning
371
- symbol: trade.symbol || supervisorState.currentSymbol
372
- };
373
-
374
- supervisorState.trades.push(tradeData);
375
-
376
- // Update performance metrics
377
- const perf = supervisorState.performance;
378
- perf.trades++;
379
- perf.totalPnL += trade.pnl || 0;
380
-
381
- // Determine outcome
382
- const outcome = trade.pnl > 0 ? 'win' : trade.pnl < 0 ? 'loss' : 'breakeven';
383
-
384
- if (trade.pnl > 0) {
385
- perf.wins++;
386
- perf.winStreak++;
387
- perf.lossStreak = 0;
388
- perf.maxWinStreak = Math.max(perf.maxWinStreak, perf.winStreak);
389
-
390
- // Learn from winning trade
391
- learnFromTrade(tradeData, 'win');
392
- } else if (trade.pnl < 0) {
393
- perf.losses++;
394
- perf.lossStreak++;
395
- perf.winStreak = 0;
396
- perf.maxLossStreak = Math.max(perf.maxLossStreak, perf.lossStreak);
397
-
398
- // Learn from losing trade
399
- learnFromTrade(tradeData, 'loss');
400
- }
401
-
402
- // Record price level with outcome (for future reference)
403
- if (entryPrice && tradeData.symbol) {
404
- const levelType = determineLevelType(entryPrice, ticksBefore);
405
- recordPriceLevel(tradeData.symbol, entryPrice, levelType, outcome);
406
- }
407
-
408
- // Update hourly performance tracking
409
- const hourKey = String(currentHour);
410
- if (!supervisorState.hourlyPerformance[hourKey]) {
411
- supervisorState.hourlyPerformance[hourKey] = { trades: 0, wins: 0, losses: 0, pnl: 0 };
412
- }
413
- supervisorState.hourlyPerformance[hourKey].trades++;
414
- if (trade.pnl > 0) supervisorState.hourlyPerformance[hourKey].wins++;
415
- if (trade.pnl < 0) supervisorState.hourlyPerformance[hourKey].losses++;
416
- supervisorState.hourlyPerformance[hourKey].pnl += trade.pnl || 0;
417
-
418
- // Update drawdown
419
- if (perf.totalPnL > perf.peakPnL) {
420
- perf.peakPnL = perf.totalPnL;
421
- perf.currentDrawdown = 0;
422
- } else {
423
- perf.currentDrawdown = perf.peakPnL - perf.totalPnL;
424
- perf.maxDrawdown = Math.max(perf.maxDrawdown, perf.currentDrawdown);
425
- }
426
-
427
- // Trigger immediate analysis after losing streaks
428
- if (perf.lossStreak >= 3) {
429
- analyzeAndOptimize();
430
- }
431
- };
432
-
433
- /**
434
- * Learn from a completed trade
435
- * Extracts patterns from winning and losing trades
436
- *
437
- * Stores ENRICHED context that API doesn't provide:
438
- * - Market conditions (volatility, trend, velocity)
439
- * - Time context (hour, session, day)
440
- * - AI state (what action was recommended)
441
- * - Price level interaction
442
- */
443
- const learnFromTrade = (trade, result) => {
444
- // Build comprehensive pattern from trade context
445
- const pattern = {
446
- timestamp: trade.timestamp,
447
- result,
448
- pnl: trade.pnl,
449
- direction: trade.direction || trade.side,
450
- symbol: trade.symbol,
451
-
452
- // ========== MARKET CONTEXT (from feedTradeResult) ==========
453
- marketContext: trade.marketContext || {
454
- priceAction: { trend: 'unknown', strength: 0 },
455
- volatility: 0,
456
- volumeProfile: { trend: 'unknown' },
457
- trendStrength: { strength: 0, direction: 'unknown' },
458
- priceVelocity: { velocity: 0, acceleration: 0 }
459
- },
460
-
461
- // ========== TIME CONTEXT ==========
462
- timeContext: trade.timeContext || {
463
- hour: new Date().getHours(),
464
- dayOfWeek: 'unknown',
465
- session: 'unknown'
466
- },
467
-
468
- // ========== AI STATE AT TRADE ==========
469
- aiContext: trade.aiContext || {
470
- action: 'NORMAL',
471
- sizeMultiplier: 1.0
472
- },
473
-
474
- // ========== PRICE LEVEL ==========
475
- levelContext: trade.levelContext || null,
476
-
477
- // ========== SIGNAL CHARACTERISTICS ==========
478
- signal: trade.signalUsed ? {
479
- confidence: trade.signalUsed.confidence,
480
- entry: trade.signalUsed.entry,
481
- stopLoss: trade.signalUsed.stopLoss,
482
- takeProfit: trade.signalUsed.takeProfit,
483
- direction: trade.signalUsed.direction
484
- } : null,
485
-
486
- // ========== DERIVED METRICS ==========
487
- derived: {
488
- // Was this trade during a "good" hour (based on historical data)?
489
- hourlyWinRate: getHourlyWinRate(trade.timeContext?.hour),
490
- // Was AI cautious or aggressive?
491
- aiWasCautious: trade.aiContext?.action === 'CAUTIOUS' || trade.aiContext?.action === 'PAUSE',
492
- // Was entry near key level?
493
- nearKeyLevel: trade.levelContext?.levelType === 'support' || trade.levelContext?.levelType === 'resistance',
494
- // High volatility trade?
495
- highVolatility: (trade.marketContext?.volatility || 0) > 0.002,
496
- // Strong trend alignment?
497
- trendAligned: trade.marketContext?.trendStrength?.aligned || false
498
- }
499
- };
500
-
501
- if (result === 'win') {
502
- supervisorState.winningPatterns.push(pattern);
503
- // Keep last 100 winning patterns (increased for better learning)
504
- if (supervisorState.winningPatterns.length > 100) {
505
- supervisorState.winningPatterns = supervisorState.winningPatterns.slice(-100);
506
- }
507
- } else {
508
- supervisorState.losingPatterns.push(pattern);
509
- // Keep last 100 losing patterns
510
- if (supervisorState.losingPatterns.length > 100) {
511
- supervisorState.losingPatterns = supervisorState.losingPatterns.slice(-100);
512
- }
513
- }
514
- };
515
-
516
- /**
517
- * Get historical win rate for a specific hour
518
- * Returns null if not enough data
519
- */
520
- const getHourlyWinRate = (hour) => {
521
- if (hour === null || hour === undefined) return null;
522
-
523
- // Check session data first
524
- const hourKey = String(hour);
525
- const sessionHourly = supervisorState.hourlyPerformance[hourKey];
526
- if (sessionHourly && sessionHourly.trades >= 3) {
527
- return sessionHourly.wins / sessionHourly.trades;
528
- }
529
-
530
- // Check historical data
531
- const saved = loadLearningData();
532
- const historicalHourly = saved.hourlyStats?.[hourKey];
533
- if (historicalHourly && historicalHourly.trades >= 5) {
534
- return historicalHourly.wins / historicalHourly.trades;
535
- }
536
-
537
- return null;
538
- };
539
-
540
-
541
- /**
542
- * Main analysis and optimization loop
543
- * Called periodically and after significant events
544
- *
545
- * Uses BOTH session data AND historical data for decisions
546
- */
547
- const analyzeAndOptimize = async () => {
548
- if (!supervisorState.active || supervisorState.agents.length === 0) return;
549
-
550
- const perf = supervisorState.performance;
551
-
552
- // Skip if not enough data
553
- if (perf.trades < 3) return;
554
-
555
- // Load historical data for context
556
- const historicalData = loadLearningData();
557
- const strategyProfile = historicalData.strategyProfile || {};
558
-
559
- // Get current time context
560
- const currentHour = new Date().getHours();
561
- const currentDay = new Date().getDay();
562
- const dayNames = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
563
-
564
- // ========== HISTORICAL INSIGHTS ==========
565
- const historicalInsights = {
566
- // Is current hour historically good or bad?
567
- currentHourStats: historicalData.hourlyStats?.[String(currentHour)] || null,
568
- isGoodHour: (strategyProfile.bestHours || []).includes(currentHour),
569
- isBadHour: (strategyProfile.worstHours || []).includes(currentHour),
570
-
571
- // Is current day historically good or bad?
572
- currentDayStats: historicalData.dayOfWeekStats?.[dayNames[currentDay]] || null,
573
- isGoodDay: (strategyProfile.bestDays || []).includes(dayNames[currentDay]),
574
- isBadDay: (strategyProfile.worstDays || []).includes(dayNames[currentDay]),
575
-
576
- // Historical averages to compare against
577
- avgWinStreak: strategyProfile.avgWinStreak || 0,
578
- avgLossStreak: strategyProfile.avgLossStreak || 0,
579
-
580
- // Lifetime context
581
- lifetimeWinRate: historicalData.totalTrades > 0
582
- ? historicalData.totalWins / historicalData.totalTrades
583
- : null,
584
- lifetimeSessions: historicalData.totalSessions || 0
585
- };
586
-
587
- // ========== PATTERN ANALYSIS ==========
588
- const patternAnalysis = {
589
- // Analyze what conditions led to wins vs losses
590
- winningConditions: analyzeWinningConditions(),
591
- losingConditions: analyzeLosingConditions(),
592
-
593
- // Best/worst time patterns from this session
594
- sessionHourlyPerformance: supervisorState.hourlyPerformance,
595
-
596
- // Price level effectiveness
597
- levelEffectiveness: analyzeLevelEffectiveness()
598
- };
599
-
600
- // Prepare performance data for AI analysis
601
- const performanceData = {
602
- trades: perf.trades,
603
- wins: perf.wins,
604
- losses: perf.losses,
605
- winRate: perf.trades > 0 ? perf.wins / perf.trades : 0,
606
- pnl: perf.totalPnL,
607
- maxDrawdown: perf.maxDrawdown,
608
- currentDrawdown: perf.currentDrawdown,
609
- winStreak: perf.winStreak,
610
- lossStreak: perf.lossStreak,
611
- maxWinStreak: perf.maxWinStreak,
612
- maxLossStreak: perf.maxLossStreak,
613
-
614
- // Calculate averages
615
- avgWin: perf.wins > 0 ?
616
- supervisorState.trades.filter(t => t.pnl > 0).reduce((s, t) => s + t.pnl, 0) / perf.wins : 0,
617
- avgLoss: perf.losses > 0 ?
618
- Math.abs(supervisorState.trades.filter(t => t.pnl < 0).reduce((s, t) => s + t.pnl, 0) / perf.losses) : 0,
619
-
620
- // Recent trades with FULL context
621
- recentTrades: supervisorState.trades.slice(-10).map(t => ({
622
- side: t.direction || t.side,
623
- qty: t.qty,
624
- price: t.price,
625
- pnl: t.pnl,
626
- // Include context that API doesn't have
627
- hour: t.timeContext?.hour,
628
- session: t.timeContext?.session,
629
- volatility: t.marketContext?.volatility,
630
- trendDirection: t.marketContext?.trendStrength?.direction,
631
- aiAction: t.aiContext?.action,
632
- nearLevel: t.levelContext?.levelType
633
- })),
634
-
635
- // Pattern summaries
636
- winningPatternCount: supervisorState.winningPatterns.length,
637
- losingPatternCount: supervisorState.losingPatterns.length,
638
-
639
- // Common characteristics of losing trades
640
- losingTradeAnalysis: analyzeLosingPatterns(),
641
-
642
- // ========== NEW: Historical context for AI ==========
643
- historicalInsights,
644
- patternAnalysis,
645
-
646
- // Current time context
647
- currentHour,
648
- currentSession: getMarketSession(currentHour),
649
- currentDay: dayNames[currentDay]
650
- };
651
-
652
- // Get suggestions from agents and apply
653
- await processAgentSuggestions(performanceData);
654
- };
655
-
656
- /**
657
- * Analyze conditions that led to winning trades
658
- */
659
- const analyzeWinningConditions = () => {
660
- const patterns = supervisorState.winningPatterns;
661
- if (patterns.length < 3) return null;
662
-
663
- // Aggregate conditions
664
- const conditions = {
665
- // Market context
666
- avgVolatility: 0,
667
- trendDirections: { bullish: 0, bearish: 0, neutral: 0 },
668
- trendAligned: 0,
669
-
670
- // Time context
671
- hours: {},
672
- sessions: {},
673
-
674
- // AI context
675
- aiActions: { AGGRESSIVE: 0, NORMAL: 0, CAUTIOUS: 0, PAUSE: 0 },
676
-
677
- // Level context
678
- nearKeyLevel: 0,
679
- levelTypes: {}
680
- };
681
-
682
- for (const p of patterns) {
683
- // Market
684
- conditions.avgVolatility += p.marketContext?.volatility || 0;
685
- const trend = p.marketContext?.trendStrength?.direction || 'unknown';
686
- if (conditions.trendDirections[trend] !== undefined) conditions.trendDirections[trend]++;
687
- if (p.derived?.trendAligned) conditions.trendAligned++;
688
-
689
- // Time
690
- const hour = p.timeContext?.hour;
691
- if (hour !== undefined) conditions.hours[hour] = (conditions.hours[hour] || 0) + 1;
692
- const session = p.timeContext?.session;
693
- if (session) conditions.sessions[session] = (conditions.sessions[session] || 0) + 1;
694
-
695
- // AI
696
- const action = p.aiContext?.action || 'NORMAL';
697
- if (conditions.aiActions[action] !== undefined) conditions.aiActions[action]++;
698
-
699
- // Levels
700
- if (p.derived?.nearKeyLevel) conditions.nearKeyLevel++;
701
- const levelType = p.levelContext?.levelType;
702
- if (levelType) conditions.levelTypes[levelType] = (conditions.levelTypes[levelType] || 0) + 1;
703
- }
704
-
705
- conditions.avgVolatility /= patterns.length;
706
- conditions.trendAlignedPct = (conditions.trendAligned / patterns.length) * 100;
707
- conditions.nearKeyLevelPct = (conditions.nearKeyLevel / patterns.length) * 100;
708
- conditions.count = patterns.length;
709
-
710
- // Find best hour
711
- const bestHour = Object.entries(conditions.hours).sort((a, b) => b[1] - a[1])[0];
712
- conditions.bestHour = bestHour ? parseInt(bestHour[0]) : null;
713
-
714
- // Find best session
715
- const bestSession = Object.entries(conditions.sessions).sort((a, b) => b[1] - a[1])[0];
716
- conditions.bestSession = bestSession ? bestSession[0] : null;
717
-
718
- return conditions;
719
- };
720
-
721
- /**
722
- * Analyze conditions that led to losing trades
723
- */
724
- const analyzeLosingConditions = () => {
725
- const patterns = supervisorState.losingPatterns;
726
- if (patterns.length < 3) return null;
727
-
728
- const conditions = {
729
- avgVolatility: 0,
730
- trendDirections: { bullish: 0, bearish: 0, neutral: 0 },
731
- trendAligned: 0,
732
- hours: {},
733
- sessions: {},
734
- aiActions: { AGGRESSIVE: 0, NORMAL: 0, CAUTIOUS: 0, PAUSE: 0 },
735
- nearKeyLevel: 0,
736
- levelTypes: {},
737
- highVolatility: 0
738
- };
739
-
740
- for (const p of patterns) {
741
- conditions.avgVolatility += p.marketContext?.volatility || 0;
742
- const trend = p.marketContext?.trendStrength?.direction || 'unknown';
743
- if (conditions.trendDirections[trend] !== undefined) conditions.trendDirections[trend]++;
744
- if (p.derived?.trendAligned) conditions.trendAligned++;
745
- if (p.derived?.highVolatility) conditions.highVolatility++;
746
-
747
- const hour = p.timeContext?.hour;
748
- if (hour !== undefined) conditions.hours[hour] = (conditions.hours[hour] || 0) + 1;
749
- const session = p.timeContext?.session;
750
- if (session) conditions.sessions[session] = (conditions.sessions[session] || 0) + 1;
751
-
752
- const action = p.aiContext?.action || 'NORMAL';
753
- if (conditions.aiActions[action] !== undefined) conditions.aiActions[action]++;
754
-
755
- if (p.derived?.nearKeyLevel) conditions.nearKeyLevel++;
756
- const levelType = p.levelContext?.levelType;
757
- if (levelType) conditions.levelTypes[levelType] = (conditions.levelTypes[levelType] || 0) + 1;
758
- }
759
-
760
- conditions.avgVolatility /= patterns.length;
761
- conditions.trendAlignedPct = (conditions.trendAligned / patterns.length) * 100;
762
- conditions.nearKeyLevelPct = (conditions.nearKeyLevel / patterns.length) * 100;
763
- conditions.highVolatilityPct = (conditions.highVolatility / patterns.length) * 100;
764
- conditions.count = patterns.length;
765
-
766
- // Find worst hour
767
- const worstHour = Object.entries(conditions.hours).sort((a, b) => b[1] - a[1])[0];
768
- conditions.worstHour = worstHour ? parseInt(worstHour[0]) : null;
769
-
770
- // Find worst session
771
- const worstSession = Object.entries(conditions.sessions).sort((a, b) => b[1] - a[1])[0];
772
- conditions.worstSession = worstSession ? worstSession[0] : null;
773
-
774
- return conditions;
775
- };
776
-
777
- /**
778
- * Analyze effectiveness of trading at key price levels
779
- */
780
- const analyzeLevelEffectiveness = () => {
781
- const allPatterns = [...supervisorState.winningPatterns, ...supervisorState.losingPatterns];
782
- if (allPatterns.length < 5) return null;
783
-
784
- const levelStats = {};
785
-
786
- for (const p of allPatterns) {
787
- const levelType = p.levelContext?.levelType || 'unknown';
788
- if (!levelStats[levelType]) {
789
- levelStats[levelType] = { wins: 0, losses: 0, total: 0 };
790
- }
791
- levelStats[levelType].total++;
792
- if (p.result === 'win') levelStats[levelType].wins++;
793
- else levelStats[levelType].losses++;
794
- }
795
-
796
- // Calculate win rate per level type
797
- for (const type of Object.keys(levelStats)) {
798
- const stats = levelStats[type];
799
- stats.winRate = stats.total > 0 ? (stats.wins / stats.total) * 100 : 0;
800
- }
801
-
802
- return levelStats;
803
- };
804
-
805
- /**
806
- * Process agent suggestions and apply optimizations
807
- */
808
- const processAgentSuggestions = async (performanceData) => {
809
- // Get optimization suggestions from all agents
810
- const suggestions = [];
811
-
812
- for (const agent of supervisorState.agents) {
813
- try {
814
- const suggestion = await getOptimizationFromAgent(agent, performanceData);
815
- if (suggestion) {
816
- suggestions.push({
817
- agentId: agent.id,
818
- agentName: agent.name,
819
- ...suggestion
820
- });
821
- }
822
- } catch (e) {
823
- // Silent fail for individual agent
824
- }
825
- }
826
-
827
- if (suggestions.length === 0) return;
828
-
829
- // Process suggestions based on mode
830
- const isConsensus = supervisorState.agents.length >= 2;
831
-
832
- if (isConsensus) {
833
- // CONSENSUS MODE: All agents must agree
834
- const consensusResult = buildConsensus(suggestions);
835
-
836
- if (consensusResult.isUnanimous && consensusResult.optimizations.length > 0) {
837
- // Apply unanimous optimizations
838
- for (const opt of consensusResult.optimizations) {
839
- applyOptimization(opt);
840
- }
841
- }
842
-
843
- // Update current advice based on consensus
844
- if (consensusResult.action) {
845
- supervisorState.currentAdvice = {
846
- action: consensusResult.action,
847
- sizeMultiplier: consensusResult.sizeMultiplier || 1.0,
848
- reason: consensusResult.reason || 'Consensus recommendation'
849
- };
850
- recordBehavior(consensusResult.action);
851
- }
852
- } else {
853
- // INDIVIDUAL MODE: Apply single agent's suggestions
854
- const suggestion = suggestions[0];
855
-
856
- if (suggestion.optimizations) {
857
- for (const opt of suggestion.optimizations) {
858
- applyOptimization(opt);
859
- }
860
- }
861
-
862
- if (suggestion.action) {
863
- supervisorState.currentAdvice = {
864
- action: suggestion.action,
865
- sizeMultiplier: suggestion.sizeMultiplier || 1.0,
866
- reason: suggestion.reason || 'Agent recommendation'
867
- };
868
- recordBehavior(suggestion.action);
869
- }
870
- }
871
-
872
- supervisorState.lastOptimizationTime = Date.now();
873
- };
874
-
875
- /**
876
- * Record behavior for graph visualization
877
- * Converts action to numeric value: PAUSE=0, CAUTIOUS=1, NORMAL=2, AGGRESSIVE=3
878
- */
879
- const recordBehavior = (action) => {
880
- const actionToValue = {
881
- 'PAUSE': 0,
882
- 'CAUTIOUS': 1,
883
- 'NORMAL': 2,
884
- 'AGGRESSIVE': 3
885
- };
886
-
887
- const value = actionToValue[action] ?? 2; // Default to NORMAL
888
- const now = Date.now();
889
-
890
- supervisorState.behaviorHistory.push({
891
- timestamp: now,
892
- value,
893
- action
894
- });
895
-
896
- // Keep last 200 data points
897
- if (supervisorState.behaviorHistory.length > 200) {
898
- supervisorState.behaviorHistory = supervisorState.behaviorHistory.slice(-200);
899
- }
900
- };
901
-
902
- /**
903
- * Analyze patterns in losing trades
904
- */
905
- const analyzeLosingPatterns = () => {
906
- const patterns = supervisorState.losingPatterns;
907
- if (patterns.length === 0) return null;
908
-
909
- // Find common characteristics
910
- const trends = patterns.map(p => p.priceAction?.trend).filter(Boolean);
911
- const volatilities = patterns.map(p => p.volatility).filter(Boolean);
912
- const confidences = patterns.map(p => p.signalConfidence).filter(Boolean);
913
-
914
- const trendCounts = {};
915
- for (const t of trends) {
916
- trendCounts[t] = (trendCounts[t] || 0) + 1;
917
- }
918
-
919
- const avgVolatility = volatilities.length > 0 ?
920
- volatilities.reduce((a, b) => a + b, 0) / volatilities.length : 0;
921
-
922
- const avgConfidence = confidences.length > 0 ?
923
- confidences.reduce((a, b) => a + b, 0) / confidences.length : 0;
924
-
925
- return {
926
- commonTrend: Object.entries(trendCounts).sort((a, b) => b[1] - a[1])[0]?.[0] || 'unknown',
927
- avgVolatility,
928
- avgConfidence,
929
- count: patterns.length
930
- };
931
- };
932
-
933
- /**
934
- * Get optimization suggestion from a single agent
935
- * Provides RICH context including historical data
936
- */
937
- const getOptimizationFromAgent = async (agent, performanceData) => {
938
- const systemPrompt = `You are an AI supervisor for HQX Ultra Scalping, a professional futures trading strategy.
939
-
940
- The strategy uses:
941
- - Order flow analysis (delta, absorption, imbalance)
942
- - Statistical models (z-score, standard deviation)
943
- - Dynamic risk management (Kelly criterion)
944
-
945
- You have access to:
946
- 1. CURRENT SESSION data (trades, P&L, streaks)
947
- 2. HISTORICAL data (which hours/days perform best, pattern analysis)
948
- 3. MARKET CONTEXT (volatility, trend, price levels)
949
-
950
- ANALYZE ALL data and provide SPECIFIC, ACTIONABLE recommendations.
951
-
952
- IMPORTANT: Your recommendations affect REAL MONEY. Be conservative when uncertain.
953
-
954
- Respond in JSON:
955
- {
956
- "assessment": "brief assessment of current conditions",
957
- "action": "AGGRESSIVE|NORMAL|CAUTIOUS|PAUSE",
958
- "sizeMultiplier": 0.5-1.5,
959
- "optimizations": [
960
- {"param": "name", "direction": "increase|decrease", "amount": "10%", "reason": "why based on data"}
961
- ],
962
- "learnings": "what patterns we identified from the data",
963
- "timeAdvice": "should we trade now based on historical hour/day performance?",
964
- "riskAdvice": "specific risk management suggestion",
965
- "confidence": 0-100
966
- }`;
967
-
968
- // Build historical context string
969
- const hist = performanceData.historicalInsights || {};
970
- const hourStats = hist.currentHourStats;
971
- const dayStats = hist.currentDayStats;
972
-
973
- let historicalContext = '';
974
- if (hist.lifetimeSessions > 0) {
975
- historicalContext = `
976
- HISTORICAL DATA (${hist.lifetimeSessions} sessions):
977
- - Lifetime Win Rate: ${hist.lifetimeWinRate ? (hist.lifetimeWinRate * 100).toFixed(1) + '%' : 'N/A'}
978
- - Current Hour (${performanceData.currentHour}:00): ${hist.isGoodHour ? 'HISTORICALLY GOOD' : hist.isBadHour ? 'HISTORICALLY BAD' : 'NEUTRAL'}
979
- ${hourStats ? `(${hourStats.trades} trades, ${hourStats.wins}W/${hourStats.losses}L, $${hourStats.pnl?.toFixed(2) || 0})` : ''}
980
- - Current Day (${performanceData.currentDay}): ${hist.isGoodDay ? 'HISTORICALLY GOOD' : hist.isBadDay ? 'HISTORICALLY BAD' : 'NEUTRAL'}
981
- ${dayStats ? `(${dayStats.trades} trades, ${dayStats.wins}W/${dayStats.losses}L, $${dayStats.pnl?.toFixed(2) || 0})` : ''}
982
- - Avg Win Streak: ${hist.avgWinStreak?.toFixed(1) || 'N/A'}, Avg Loss Streak: ${hist.avgLossStreak?.toFixed(1) || 'N/A'}`;
983
- }
984
-
985
- // Build pattern analysis string
986
- const patterns = performanceData.patternAnalysis || {};
987
- let patternContext = '';
988
-
989
- if (patterns.winningConditions) {
990
- const wc = patterns.winningConditions;
991
- patternContext += `
992
- WINNING TRADE PATTERNS (${wc.count} trades):
993
- - Best Hour: ${wc.bestHour !== null ? wc.bestHour + ':00' : 'N/A'}
994
- - Best Session: ${wc.bestSession || 'N/A'}
995
- - Avg Volatility: ${(wc.avgVolatility * 100).toFixed(3)}%
996
- - Trend Aligned: ${wc.trendAlignedPct?.toFixed(0) || 0}%
997
- - Near Key Level: ${wc.nearKeyLevelPct?.toFixed(0) || 0}%`;
998
- }
999
-
1000
- if (patterns.losingConditions) {
1001
- const lc = patterns.losingConditions;
1002
- patternContext += `
1003
-
1004
- LOSING TRADE PATTERNS (${lc.count} trades):
1005
- - Worst Hour: ${lc.worstHour !== null ? lc.worstHour + ':00' : 'N/A'}
1006
- - Worst Session: ${lc.worstSession || 'N/A'}
1007
- - Avg Volatility: ${(lc.avgVolatility * 100).toFixed(3)}%
1008
- - High Volatility: ${lc.highVolatilityPct?.toFixed(0) || 0}%
1009
- - Trend Aligned: ${lc.trendAlignedPct?.toFixed(0) || 0}%`;
1010
- }
1011
-
1012
- if (patterns.levelEffectiveness) {
1013
- const le = patterns.levelEffectiveness;
1014
- const levelSummary = Object.entries(le)
1015
- .filter(([k, v]) => v.total >= 2)
1016
- .map(([k, v]) => `${k}: ${v.winRate.toFixed(0)}% (${v.total} trades)`)
1017
- .join(', ');
1018
- if (levelSummary) {
1019
- patternContext += `
1020
-
1021
- LEVEL EFFECTIVENESS: ${levelSummary}`;
1022
- }
1023
- }
1024
-
1025
- const prompt = `STRATEGY PERFORMANCE ANALYSIS
1026
-
1027
- SESSION STATS:
1028
- - Trades: ${performanceData.trades} (${performanceData.wins}W / ${performanceData.losses}L)
1029
- - Win Rate: ${(performanceData.winRate * 100).toFixed(1)}%
1030
- - P&L: $${performanceData.pnl.toFixed(2)}
1031
- - Max Drawdown: $${performanceData.maxDrawdown.toFixed(2)}
1032
- - Current Streak: ${performanceData.winStreak > 0 ? performanceData.winStreak + ' wins' : performanceData.lossStreak + ' losses'}
1033
- - Current Session: ${performanceData.currentSession} (${performanceData.currentHour}:00)
1034
- ${historicalContext}
1035
- ${patternContext}
1036
-
1037
- RECENT TRADES (with context):
1038
- ${performanceData.recentTrades.map(t =>
1039
- `${t.side} @ ${t.price} → $${t.pnl?.toFixed(2)} | ${t.session || 'N/A'} | vol:${t.volatility ? (t.volatility * 100).toFixed(2) + '%' : 'N/A'} | trend:${t.trendDirection || 'N/A'} | level:${t.nearLevel || 'N/A'}`
1040
- ).join('\n')}
1041
-
1042
- Based on ALL this data:
1043
- 1. What patterns do you see in winning vs losing trades?
1044
- 2. Should we trade now (considering hour/day history)?
1045
- 3. What SPECIFIC optimizations would improve performance?`;
1046
-
1047
- try {
1048
- const response = await callAI(agent, prompt, systemPrompt);
1049
- if (!response) return null;
1050
-
1051
- const jsonMatch = response.match(/\{[\s\S]*\}/);
1052
- if (jsonMatch) {
1053
- return JSON.parse(jsonMatch[0]);
1054
- }
1055
- return null;
1056
- } catch (e) {
1057
- return null;
1058
- }
1059
- };
1060
-
1061
- /**
1062
- * Build consensus from multiple agent suggestions
1063
- */
1064
- const buildConsensus = (suggestions) => {
1065
- if (suggestions.length === 0) return { isUnanimous: false };
1066
-
1067
- // Check action consensus
1068
- const actions = suggestions.map(s => s.action).filter(Boolean);
1069
- const allSameAction = actions.length === suggestions.length &&
1070
- actions.every(a => a === actions[0]);
1071
-
1072
- // Check optimization consensus
1073
- const allOptimizations = suggestions.flatMap(s => s.optimizations || []);
1074
- const paramGroups = {};
1075
-
1076
- for (const opt of allOptimizations) {
1077
- if (!opt.param) continue;
1078
- const key = `${opt.param}_${opt.direction}`;
1079
- if (!paramGroups[key]) {
1080
- paramGroups[key] = { ...opt, count: 0 };
1081
- }
1082
- paramGroups[key].count++;
1083
- }
1084
-
1085
- // Find unanimous optimizations (all agents agree)
1086
- const unanimousOptimizations = Object.values(paramGroups)
1087
- .filter(g => g.count === suggestions.length)
1088
- .map(g => ({
1089
- param: g.param,
1090
- direction: g.direction,
1091
- amount: g.amount,
1092
- reason: `Unanimous (${suggestions.length} agents)`
1093
- }));
1094
-
1095
- // Average size multiplier
1096
- const multipliers = suggestions.map(s => s.sizeMultiplier || 1.0);
1097
- const avgMultiplier = multipliers.reduce((a, b) => a + b, 0) / multipliers.length;
1098
-
1099
- // Average confidence
1100
- const confidences = suggestions.map(s => s.confidence || 50);
1101
- const avgConfidence = Math.round(confidences.reduce((a, b) => a + b, 0) / confidences.length);
1102
-
1103
- return {
1104
- isUnanimous: allSameAction && unanimousOptimizations.length > 0,
1105
- action: allSameAction ? actions[0] : 'CAUTIOUS',
1106
- sizeMultiplier: allSameAction ? avgMultiplier : 0.5,
1107
- optimizations: unanimousOptimizations,
1108
- confidence: avgConfidence,
1109
- reason: allSameAction ?
1110
- `${suggestions.length} agents agree` :
1111
- 'Agents disagree - being cautious',
1112
- votes: actions.reduce((acc, a) => { acc[a] = (acc[a] || 0) + 1; return acc; }, {})
1113
- };
1114
- };
1115
-
1116
- /**
1117
- * Apply an optimization to the strategy
1118
- */
1119
- const applyOptimization = (optimization) => {
1120
- const strategy = supervisorState.strategy;
1121
- if (!strategy) return false;
1122
-
1123
- // Record the optimization
1124
- supervisorState.optimizations.push({
1125
- timestamp: Date.now(),
1126
- ...optimization
1127
- });
1128
-
1129
- // Try to apply to strategy if it supports it
1130
- try {
1131
- if (typeof strategy.applyAIOptimization === 'function') {
1132
- strategy.applyAIOptimization(optimization);
1133
- return true;
1134
- }
1135
-
1136
- if (typeof strategy.setParameter === 'function') {
1137
- strategy.setParameter(optimization.param, optimization.direction, optimization.amount);
1138
- return true;
1139
- }
1140
- } catch (e) {
1141
- // Strategy doesn't support this optimization
1142
- }
1143
-
1144
- return false;
1145
- };
1146
-
1147
- /**
1148
- * Get current advice for the strategy
1149
- * Called before each trade decision
1150
- */
1151
- const getCurrentAdvice = () => {
1152
- if (!supervisorState.active) {
1153
- return { action: 'NORMAL', sizeMultiplier: 1.0, reason: 'No supervision' };
1154
- }
1155
-
1156
- return supervisorState.currentAdvice;
1157
- };
1158
-
1159
- /**
1160
- * Get supervision status
1161
- */
1162
- const getStatus = () => {
1163
- return {
1164
- active: supervisorState.active,
1165
- agents: supervisorState.agents.length,
1166
- mode: supervisorState.agents.length >= 2 ? 'CONSENSUS' : 'INDIVIDUAL',
1167
- performance: supervisorState.performance,
1168
- currentAdvice: supervisorState.currentAdvice,
1169
- optimizationsApplied: supervisorState.optimizations.length,
1170
- patternsLearned: {
1171
- winning: supervisorState.winningPatterns.length,
1172
- losing: supervisorState.losingPatterns.length
1173
- },
1174
- lastOptimization: supervisorState.lastOptimizationTime
1175
- };
1176
- };
1177
-
1178
- /**
1179
- * Check if should proceed with trade based on AI advice
1180
- */
1181
- const shouldTrade = () => {
1182
- if (!supervisorState.active) return { proceed: true, multiplier: 1.0 };
1183
-
1184
- const advice = supervisorState.currentAdvice;
1185
-
1186
- if (advice.action === 'PAUSE') {
1187
- return { proceed: false, reason: advice.reason };
1188
- }
1189
-
1190
- return {
1191
- proceed: true,
1192
- multiplier: advice.sizeMultiplier || 1.0,
1193
- action: advice.action
1194
- };
1195
- };
1196
-
1197
- /**
1198
- * Get behavior history for graph visualization
1199
- * Returns array of numeric values (0-3) representing agent behavior over time
1200
- *
1201
- * @param {number} maxPoints - Maximum data points to return
1202
- * @returns {Object} { values: number[], labels: string[], startTime: number }
1203
- */
1204
- const getBehaviorHistory = (maxPoints = 50) => {
1205
- if (!supervisorState.active || supervisorState.behaviorHistory.length === 0) {
1206
- return { values: [], labels: [], startTime: null };
1207
- }
1208
-
1209
- let history = [...supervisorState.behaviorHistory];
1210
-
1211
- // Downsample if too many points
1212
- if (history.length > maxPoints) {
1213
- const step = Math.ceil(history.length / maxPoints);
1214
- history = history.filter((_, i) => i % step === 0);
1215
- }
1216
-
1217
- // If too few points, interpolate to make smooth curve
1218
- if (history.length < 10 && history.length > 1) {
1219
- const interpolated = [];
1220
- for (let i = 0; i < history.length - 1; i++) {
1221
- interpolated.push(history[i]);
1222
- // Add intermediate points
1223
- const curr = history[i].value;
1224
- const next = history[i + 1].value;
1225
- const mid = (curr + next) / 2;
1226
- interpolated.push({ value: mid, action: 'interpolated' });
1227
- }
1228
- interpolated.push(history[history.length - 1]);
1229
- history = interpolated;
1230
- }
1231
-
1232
- return {
1233
- values: history.map(h => h.value),
1234
- actions: history.map(h => h.action),
1235
- startTime: supervisorState.behaviorStartTime,
1236
- duration: Date.now() - supervisorState.behaviorStartTime
1237
- };
1238
- };
1239
-
1240
- /**
1241
- * Get learning statistics for display
1242
- */
1243
- const getLearningStats = () => {
1244
- return {
1245
- patternsLearned: {
1246
- winning: supervisorState.winningPatterns.length,
1247
- losing: supervisorState.losingPatterns.length,
1248
- total: supervisorState.winningPatterns.length + supervisorState.losingPatterns.length
1249
- },
1250
- optimizations: supervisorState.optimizations.length,
1251
- tradesAnalyzed: supervisorState.trades.length,
1252
- ticksProcessed: supervisorState.ticks.length,
1253
- signalsObserved: supervisorState.signals.length
1254
- };
1255
- };
1256
-
1257
- /**
1258
- * Get lifetime stats across all sessions
1259
- * Shows cumulative learning progress
1260
- */
1261
- const getLifetimeStats = () => {
1262
- const saved = loadLearningData();
1263
-
1264
- return {
1265
- totalSessions: saved.totalSessions,
1266
- totalTrades: saved.totalTrades,
1267
- totalWins: saved.totalWins,
1268
- totalLosses: saved.totalLosses,
1269
- lifetimeWinRate: saved.totalTrades > 0 ?
1270
- ((saved.totalWins / saved.totalTrades) * 100).toFixed(1) + '%' : 'N/A',
1271
- lifetimePnL: saved.lifetimePnL,
1272
- patternsLearned: {
1273
- winning: saved.winningPatterns?.length || 0,
1274
- losing: saved.losingPatterns?.length || 0
1275
- },
1276
- optimizationsApplied: saved.optimizations?.length || 0,
1277
- lastUpdated: saved.lastUpdated
1278
- };
1279
- };
1280
-
1281
- module.exports = {
1282
- // Core lifecycle
1283
- initialize,
1284
- stop,
1285
-
1286
- // Data feeds (from algo)
1287
- feedTick,
1288
- feedSignal,
1289
- feedTradeResult,
1290
-
1291
- // Trading decisions
1292
- getCurrentAdvice,
1293
- shouldTrade,
1294
-
1295
- // Agent management (dynamic sync)
1296
- syncAgents,
1297
- refreshAgents,
1298
- addAgent,
1299
- removeAgent,
1300
- getAgentInfo,
1301
-
1302
- // Status & stats
1303
- getStatus,
1304
- analyzeAndOptimize,
1305
- getBehaviorHistory,
1306
- getLearningStats,
1307
- getLifetimeStats,
1308
-
1309
- // Data management
1310
- clearLearningData,
1311
- loadLearningData
1312
- };