hedgequantx 2.6.162 → 2.7.0
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/README.md +15 -88
- package/bin/cli.js +0 -11
- package/dist/lib/api.jsc +0 -0
- package/dist/lib/api2.jsc +0 -0
- package/dist/lib/core.jsc +0 -0
- package/dist/lib/core2.jsc +0 -0
- package/dist/lib/data.js +1 -1
- package/dist/lib/data.jsc +0 -0
- package/dist/lib/data2.jsc +0 -0
- package/dist/lib/decoder.jsc +0 -0
- package/dist/lib/m/mod1.jsc +0 -0
- package/dist/lib/m/mod2.jsc +0 -0
- package/dist/lib/n/r1.jsc +0 -0
- package/dist/lib/n/r2.jsc +0 -0
- package/dist/lib/n/r3.jsc +0 -0
- package/dist/lib/n/r4.jsc +0 -0
- package/dist/lib/n/r5.jsc +0 -0
- package/dist/lib/n/r6.jsc +0 -0
- package/dist/lib/n/r7.jsc +0 -0
- package/dist/lib/o/util1.jsc +0 -0
- package/dist/lib/o/util2.jsc +0 -0
- package/package.json +6 -3
- package/src/app.js +40 -162
- package/src/config/constants.js +31 -33
- package/src/config/propfirms.js +13 -217
- package/src/config/settings.js +0 -43
- package/src/lib/api.js +198 -0
- package/src/lib/api2.js +353 -0
- package/src/lib/core.js +539 -0
- package/src/lib/core2.js +341 -0
- package/src/lib/data.js +555 -0
- package/src/lib/data2.js +492 -0
- package/src/lib/decoder.js +599 -0
- package/src/lib/m/s1.js +804 -0
- package/src/lib/m/s2.js +34 -0
- package/src/lib/n/r1.js +454 -0
- package/src/lib/n/r2.js +514 -0
- package/src/lib/n/r3.js +631 -0
- package/src/lib/n/r4.js +401 -0
- package/src/lib/n/r5.js +335 -0
- package/src/lib/n/r6.js +425 -0
- package/src/lib/n/r7.js +530 -0
- package/src/lib/o/l1.js +44 -0
- package/src/lib/o/l2.js +427 -0
- package/src/lib/python-bridge.js +206 -0
- package/src/menus/connect.js +14 -176
- package/src/menus/dashboard.js +65 -110
- package/src/pages/accounts.js +18 -18
- package/src/pages/algo/copy-trading.js +210 -240
- package/src/pages/algo/index.js +41 -104
- package/src/pages/algo/one-account.js +386 -33
- package/src/pages/algo/ui.js +312 -151
- package/src/pages/orders.js +3 -3
- package/src/pages/positions.js +3 -3
- package/src/pages/stats/chart.js +74 -0
- package/src/pages/stats/display.js +228 -0
- package/src/pages/stats/index.js +236 -0
- package/src/pages/stats/metrics.js +213 -0
- package/src/pages/user.js +6 -6
- package/src/services/hqx-server/constants.js +55 -0
- package/src/services/hqx-server/index.js +401 -0
- package/src/services/hqx-server/latency.js +81 -0
- package/src/services/index.js +12 -3
- package/src/services/rithmic/accounts.js +7 -32
- package/src/services/rithmic/connection.js +1 -204
- package/src/services/rithmic/contracts.js +235 -0
- package/src/services/rithmic/handlers.js +21 -196
- package/src/services/rithmic/index.js +60 -291
- package/src/services/rithmic/market.js +31 -0
- package/src/services/rithmic/orders.js +5 -361
- package/src/services/rithmic/protobuf.js +5 -195
- package/src/services/session.js +22 -173
- package/src/ui/box.js +10 -18
- package/src/ui/index.js +1 -3
- package/src/ui/menu.js +1 -1
- package/src/utils/prompts.js +2 -2
- package/dist/lib/m/s1.js +0 -1
- package/src/menus/ai-agent-connect.js +0 -181
- package/src/menus/ai-agent-models.js +0 -219
- package/src/menus/ai-agent-oauth.js +0 -292
- package/src/menus/ai-agent-ui.js +0 -141
- package/src/menus/ai-agent.js +0 -484
- package/src/pages/algo/algo-config.js +0 -195
- package/src/pages/algo/algo-multi.js +0 -801
- package/src/pages/algo/algo-utils.js +0 -58
- package/src/pages/algo/copy-engine.js +0 -449
- package/src/pages/algo/custom-strategy.js +0 -459
- package/src/pages/algo/logger.js +0 -245
- package/src/pages/algo/smart-logs-data.js +0 -218
- package/src/pages/algo/smart-logs.js +0 -387
- package/src/pages/algo/ui-constants.js +0 -144
- package/src/pages/algo/ui-summary.js +0 -184
- package/src/pages/stats-calculations.js +0 -191
- package/src/pages/stats-ui.js +0 -381
- package/src/pages/stats.js +0 -339
- package/src/services/ai/client-analysis.js +0 -194
- package/src/services/ai/client-models.js +0 -333
- package/src/services/ai/client.js +0 -343
- package/src/services/ai/index.js +0 -384
- package/src/services/ai/oauth-anthropic.js +0 -265
- package/src/services/ai/oauth-gemini.js +0 -223
- package/src/services/ai/oauth-iflow.js +0 -269
- package/src/services/ai/oauth-openai.js +0 -233
- package/src/services/ai/oauth-qwen.js +0 -279
- package/src/services/ai/providers/index.js +0 -526
- package/src/services/ai/proxy-install.js +0 -249
- package/src/services/ai/proxy-manager.js +0 -494
- package/src/services/ai/proxy-remote.js +0 -161
- package/src/services/ai/strategy-supervisor.js +0 -1312
- package/src/services/ai/supervisor-data.js +0 -195
- package/src/services/ai/supervisor-optimize.js +0 -215
- package/src/services/ai/supervisor-sync.js +0 -178
- package/src/services/ai/supervisor-utils.js +0 -158
- package/src/services/ai/supervisor.js +0 -484
- package/src/services/ai/validation.js +0 -250
- package/src/services/hqx-server-events.js +0 -110
- package/src/services/hqx-server-handlers.js +0 -217
- package/src/services/hqx-server-latency.js +0 -136
- package/src/services/hqx-server.js +0 -403
- package/src/services/position-constants.js +0 -28
- package/src/services/position-manager.js +0 -528
- package/src/services/position-momentum.js +0 -206
- package/src/services/projectx/accounts.js +0 -142
- package/src/services/projectx/index.js +0 -443
- package/src/services/projectx/market.js +0 -172
- package/src/services/projectx/stats.js +0 -110
- package/src/services/projectx/trading.js +0 -180
- package/src/services/rithmic/latency-tracker.js +0 -182
- package/src/services/rithmic/market-data.js +0 -549
- package/src/services/rithmic/specs.js +0 -146
- package/src/services/rithmic/trade-history.js +0 -254
- package/src/services/session-history.js +0 -475
- package/src/services/strategy/hft-tick.js +0 -507
- package/src/services/strategy/recovery-math.js +0 -402
- package/src/services/tradovate/constants.js +0 -109
- package/src/services/tradovate/index.js +0 -505
- package/src/services/tradovate/market.js +0 -47
- 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
|
-
};
|