hedgequantx 2.5.14 ā 2.5.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/menus/dashboard.js +3 -0
- package/src/pages/algo/copy-trading.js +54 -2
- package/src/pages/algo/index.js +1 -1
- package/src/pages/stats.js +52 -0
- package/src/services/ai/client.js +281 -0
- package/src/services/ai/index.js +2 -29
- package/src/services/ai/supervisor.js +337 -485
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AI Supervisor
|
|
3
3
|
* Manages AI supervision of algo trading
|
|
4
|
+
*
|
|
5
|
+
* STRICT RULE: NO MOCK DATA, NO SIMULATION, NO ESTIMATION
|
|
6
|
+
* All data comes from real APIs (ProjectX, Rithmic, Tradovate)
|
|
4
7
|
*/
|
|
5
8
|
|
|
9
|
+
const { connections } = require('../session');
|
|
10
|
+
const { analyzeTrading } = require('./client');
|
|
11
|
+
|
|
6
12
|
let aiService = null;
|
|
7
13
|
|
|
8
14
|
// Lazy load to avoid circular dependency
|
|
@@ -21,29 +27,34 @@ let consensusInterval = null;
|
|
|
21
27
|
|
|
22
28
|
/**
|
|
23
29
|
* AI Supervisor Class
|
|
30
|
+
* Uses REAL data from connected trading APIs
|
|
24
31
|
*/
|
|
25
32
|
class AISupervisor {
|
|
26
33
|
/**
|
|
27
34
|
* Start supervision for an agent
|
|
28
35
|
*/
|
|
29
|
-
static start(agentId,
|
|
36
|
+
static start(agentId, service, accountId) {
|
|
30
37
|
if (supervisionSessions.has(agentId)) {
|
|
31
|
-
|
|
32
|
-
return false;
|
|
38
|
+
return { success: false, error: 'Supervision already active' };
|
|
33
39
|
}
|
|
34
40
|
|
|
35
41
|
const agentInfo = getAIService().getAgent(agentId);
|
|
36
42
|
if (!agentInfo) {
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
return { success: false, error: 'Agent not found' };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!service || !accountId) {
|
|
47
|
+
return { success: false, error: 'Service and accountId required' };
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
const session = {
|
|
42
51
|
agentId,
|
|
43
52
|
agent: agentInfo,
|
|
44
|
-
|
|
53
|
+
service,
|
|
54
|
+
accountId,
|
|
45
55
|
startTime: Date.now(),
|
|
46
56
|
lastCheck: Date.now(),
|
|
57
|
+
lastData: null,
|
|
47
58
|
decisions: [],
|
|
48
59
|
metrics: {
|
|
49
60
|
totalDecisions: 0,
|
|
@@ -54,40 +65,41 @@ class AISupervisor {
|
|
|
54
65
|
interval: null
|
|
55
66
|
};
|
|
56
67
|
|
|
57
|
-
// Check if this is the first agent or if we need consensus mode
|
|
58
68
|
const currentSessionCount = supervisionSessions.size;
|
|
59
69
|
|
|
60
70
|
if (currentSessionCount === 0) {
|
|
61
71
|
// First agent - start individual supervision
|
|
62
72
|
session.interval = setInterval(() => {
|
|
63
73
|
this.supervise(agentId);
|
|
64
|
-
},
|
|
74
|
+
}, 10000); // Every 10 seconds
|
|
65
75
|
|
|
66
76
|
supervisionSessions.set(agentId, session);
|
|
67
|
-
|
|
68
|
-
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
success: true,
|
|
80
|
+
message: `AI supervision started: ${agentInfo.name}`,
|
|
81
|
+
mode: 'single'
|
|
82
|
+
};
|
|
69
83
|
|
|
70
84
|
} else {
|
|
71
85
|
// Additional agent - switch to consensus mode
|
|
72
|
-
console.log(`Adding ${agentInfo.name} to supervision`);
|
|
73
|
-
console.log(` Total agents: ${currentSessionCount + 1}`);
|
|
74
|
-
|
|
75
|
-
// Stop individual loops and start consensus loop
|
|
76
86
|
this.switchToConsensusMode();
|
|
77
|
-
|
|
78
|
-
session.interval = null; // Will use consensus loop
|
|
87
|
+
session.interval = null;
|
|
79
88
|
supervisionSessions.set(agentId, session);
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
success: true,
|
|
92
|
+
message: `Added ${agentInfo.name} to supervision`,
|
|
93
|
+
mode: 'consensus',
|
|
94
|
+
totalAgents: currentSessionCount + 1
|
|
95
|
+
};
|
|
80
96
|
}
|
|
81
|
-
|
|
82
|
-
return true;
|
|
83
97
|
}
|
|
84
98
|
|
|
85
99
|
/**
|
|
86
100
|
* Switch to consensus mode when multiple agents
|
|
87
101
|
*/
|
|
88
102
|
static switchToConsensusMode() {
|
|
89
|
-
console.log(`\nš¤ SWITCHING TO MULTI-AGENT CONSENSUS MODE`);
|
|
90
|
-
|
|
91
103
|
// Clear all individual intervals
|
|
92
104
|
for (const [agentId, session] of supervisionSessions.entries()) {
|
|
93
105
|
if (session.interval) {
|
|
@@ -97,64 +109,20 @@ class AISupervisor {
|
|
|
97
109
|
}
|
|
98
110
|
|
|
99
111
|
// Start single consensus loop
|
|
100
|
-
if (!
|
|
101
|
-
|
|
112
|
+
if (!consensusInterval) {
|
|
113
|
+
consensusInterval = setInterval(() => {
|
|
102
114
|
this.superviseConsensus();
|
|
103
|
-
},
|
|
104
|
-
|
|
105
|
-
console.log(` Consensus loop started - all agents will vote on decisions`);
|
|
115
|
+
}, 10000);
|
|
106
116
|
}
|
|
107
117
|
}
|
|
108
118
|
|
|
109
|
-
/**
|
|
110
|
-
* Start supervision for an agent
|
|
111
|
-
*/
|
|
112
|
-
static start(agentId, targetAlgo) {
|
|
113
|
-
if (supervisionSessions.has(agentId)) {
|
|
114
|
-
console.log(`Supervision already active for agent ${agentId}`);
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const agent = getAIService().getAgent(agentId);
|
|
119
|
-
if (!agent) {
|
|
120
|
-
console.log(`Agent ${agentId} not found`);
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const session = {
|
|
125
|
-
agentId,
|
|
126
|
-
agent: agent,
|
|
127
|
-
algo: targetAlgo,
|
|
128
|
-
startTime: Date.now(),
|
|
129
|
-
lastCheck: Date.now(),
|
|
130
|
-
decisions: [],
|
|
131
|
-
metrics: {
|
|
132
|
-
totalDecisions: 0,
|
|
133
|
-
interventions: 0,
|
|
134
|
-
optimizations: 0,
|
|
135
|
-
riskWarnings: 0
|
|
136
|
-
},
|
|
137
|
-
interval: null
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
// Start supervision loop (every 5 seconds)
|
|
141
|
-
session.interval = setInterval(() => {
|
|
142
|
-
this.supervise(agentId);
|
|
143
|
-
}, 5000);
|
|
144
|
-
|
|
145
|
-
supervisionSessions.set(agentId, session);
|
|
146
|
-
console.log(`AI supervision started: ${agent.name} monitoring HQX algo`);
|
|
147
|
-
|
|
148
|
-
return true;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
119
|
/**
|
|
152
120
|
* Stop supervision for an agent
|
|
153
121
|
*/
|
|
154
122
|
static stop(agentId) {
|
|
155
123
|
const session = supervisionSessions.get(agentId);
|
|
156
124
|
if (!session) {
|
|
157
|
-
return false;
|
|
125
|
+
return { success: false, error: 'Session not found' };
|
|
158
126
|
}
|
|
159
127
|
|
|
160
128
|
if (session.interval) {
|
|
@@ -164,14 +132,9 @@ class AISupervisor {
|
|
|
164
132
|
const duration = Date.now() - session.startTime;
|
|
165
133
|
supervisionSessions.delete(agentId);
|
|
166
134
|
|
|
167
|
-
console.log(`AI supervision stopped for ${session.agent.name}`);
|
|
168
|
-
console.log(`Session duration: ${Math.round(duration / 1000)}s`);
|
|
169
|
-
console.log(`Decisions: ${session.metrics.totalDecisions}, Interventions: ${session.metrics.interventions}`);
|
|
170
|
-
|
|
171
135
|
// Check if we need to switch back to single agent mode
|
|
172
136
|
const remainingSessions = supervisionSessions.size;
|
|
173
137
|
if (remainingSessions === 1 && consensusInterval) {
|
|
174
|
-
// Last agent - switch back to single mode
|
|
175
138
|
clearInterval(consensusInterval);
|
|
176
139
|
consensusInterval = null;
|
|
177
140
|
|
|
@@ -180,21 +143,26 @@ class AISupervisor {
|
|
|
180
143
|
const remainingAgentId = remainingSession.agentId;
|
|
181
144
|
remainingSession.interval = setInterval(() => {
|
|
182
145
|
this.supervise(remainingAgentId);
|
|
183
|
-
},
|
|
184
|
-
|
|
185
|
-
console.log(`Switched back to single agent supervision for ${remainingSession.agent.name}`);
|
|
146
|
+
}, 10000);
|
|
186
147
|
}
|
|
187
148
|
} else if (remainingSessions === 0 && consensusInterval) {
|
|
188
|
-
// No agents left
|
|
189
149
|
clearInterval(consensusInterval);
|
|
190
150
|
consensusInterval = null;
|
|
191
151
|
}
|
|
192
152
|
|
|
193
|
-
return
|
|
153
|
+
return {
|
|
154
|
+
success: true,
|
|
155
|
+
duration: Math.round(duration / 1000),
|
|
156
|
+
metrics: session.metrics
|
|
157
|
+
};
|
|
194
158
|
}
|
|
195
159
|
|
|
196
160
|
/**
|
|
197
161
|
* Main supervision loop - single agent
|
|
162
|
+
* Fetches REAL data from APIs and analyzes with AI
|
|
163
|
+
*
|
|
164
|
+
* Data source: Trading APIs (ProjectX, Rithmic, Tradovate)
|
|
165
|
+
* AI source: Configured AI provider (real API call)
|
|
198
166
|
*/
|
|
199
167
|
static async supervise(agentId) {
|
|
200
168
|
const session = supervisionSessions.get(agentId);
|
|
@@ -203,456 +171,241 @@ class AISupervisor {
|
|
|
203
171
|
try {
|
|
204
172
|
session.lastCheck = Date.now();
|
|
205
173
|
|
|
206
|
-
// Get
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
174
|
+
// Get REAL data from API
|
|
175
|
+
const data = await this.fetchRealData(session.service, session.accountId);
|
|
176
|
+
if (!data) return;
|
|
177
|
+
|
|
178
|
+
session.lastData = data;
|
|
179
|
+
session.metrics.lastFetch = Date.now();
|
|
210
180
|
|
|
211
|
-
//
|
|
212
|
-
const
|
|
181
|
+
// Call AI for analysis (real API call, returns null if fails)
|
|
182
|
+
const aiDecision = await analyzeTrading(session.agent, data);
|
|
213
183
|
|
|
214
|
-
if (
|
|
184
|
+
if (aiDecision) {
|
|
185
|
+
// Store decision with timestamp
|
|
186
|
+
const decision = {
|
|
187
|
+
timestamp: Date.now(),
|
|
188
|
+
action: aiDecision.action || null,
|
|
189
|
+
confidence: aiDecision.confidence || null,
|
|
190
|
+
reason: aiDecision.reason || null,
|
|
191
|
+
dataSnapshot: {
|
|
192
|
+
balance: data.account?.balance ?? null,
|
|
193
|
+
pnl: data.account?.profitAndLoss ?? null,
|
|
194
|
+
positions: data.positions?.length ?? 0,
|
|
195
|
+
orders: data.orders?.length ?? 0
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
215
199
|
session.decisions.push(decision);
|
|
216
200
|
session.metrics.totalDecisions++;
|
|
217
201
|
|
|
218
|
-
//
|
|
219
|
-
if (
|
|
220
|
-
|
|
202
|
+
// Track decision types
|
|
203
|
+
if (aiDecision.action === 'REDUCE_SIZE' || aiDecision.action === 'PAUSE') {
|
|
204
|
+
session.metrics.interventions++;
|
|
205
|
+
} else if (aiDecision.action === 'CONTINUE') {
|
|
206
|
+
session.metrics.optimizations++;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Check for risk warnings
|
|
210
|
+
if (aiDecision.confidence !== null && aiDecision.confidence < 50) {
|
|
211
|
+
session.metrics.riskWarnings++;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Keep only last 100 decisions to prevent memory bloat
|
|
215
|
+
if (session.decisions.length > 100) {
|
|
216
|
+
session.decisions = session.decisions.slice(-100);
|
|
221
217
|
}
|
|
222
218
|
}
|
|
223
219
|
|
|
224
220
|
} catch (error) {
|
|
225
|
-
|
|
221
|
+
// Silent fail - don't spam logs
|
|
226
222
|
}
|
|
227
223
|
}
|
|
228
224
|
|
|
229
225
|
/**
|
|
230
226
|
* Multi-agent consensus supervision loop
|
|
227
|
+
* Each agent analyzes data independently, then consensus is calculated
|
|
228
|
+
*
|
|
229
|
+
* Data source: Trading APIs (ProjectX, Rithmic, Tradovate)
|
|
230
|
+
* AI source: Each agent's configured AI provider (real API calls)
|
|
231
231
|
*/
|
|
232
232
|
static async superviseConsensus() {
|
|
233
233
|
const allSessions = Array.from(supervisionSessions.entries());
|
|
234
234
|
if (allSessions.length === 0) return;
|
|
235
235
|
|
|
236
236
|
try {
|
|
237
|
-
|
|
238
|
-
const allAgents = allSessions.map(([id, session]) => session.agent).filter(Boolean);
|
|
239
|
-
|
|
240
|
-
if (allAgents.length === 0) return;
|
|
241
|
-
|
|
242
|
-
console.log(`š¤ MULTI-AGENT CONSENSUS: ${allAgents.length} agents participating`);
|
|
243
|
-
|
|
244
|
-
// Get algo status (use same algo for all agents)
|
|
245
|
-
const mockAlgo = { name: 'HQX Ultra Scalping' }; // Would be real algo
|
|
246
|
-
const algoStatus = this.getAlgoStatus(mockAlgo);
|
|
247
|
-
const marketData = this.getMarketData();
|
|
248
|
-
const riskMetrics = this.getRiskMetrics(mockAlgo);
|
|
237
|
+
const decisions = [];
|
|
249
238
|
|
|
250
|
-
//
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
239
|
+
// Fetch data and get AI analysis for each session
|
|
240
|
+
for (const [agentId, session] of allSessions) {
|
|
241
|
+
const data = await this.fetchRealData(session.service, session.accountId);
|
|
242
|
+
if (!data) continue;
|
|
243
|
+
|
|
244
|
+
session.lastData = data;
|
|
245
|
+
session.lastCheck = Date.now();
|
|
246
|
+
session.metrics.lastFetch = Date.now();
|
|
247
|
+
|
|
248
|
+
// Call AI for analysis (real API call)
|
|
249
|
+
const aiDecision = await analyzeTrading(session.agent, data);
|
|
250
|
+
|
|
251
|
+
if (aiDecision) {
|
|
252
|
+
const decision = {
|
|
258
253
|
timestamp: Date.now(),
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
254
|
+
agentId,
|
|
255
|
+
agentName: session.agent.name,
|
|
256
|
+
action: aiDecision.action || null,
|
|
257
|
+
confidence: aiDecision.confidence || null,
|
|
258
|
+
reason: aiDecision.reason || null,
|
|
259
|
+
dataSnapshot: {
|
|
260
|
+
balance: data.account?.balance ?? null,
|
|
261
|
+
pnl: data.account?.profitAndLoss ?? null,
|
|
262
|
+
positions: data.positions?.length ?? 0,
|
|
263
|
+
orders: data.orders?.length ?? 0
|
|
264
|
+
}
|
|
265
|
+
};
|
|
262
266
|
|
|
267
|
+
session.decisions.push(decision);
|
|
263
268
|
session.metrics.totalDecisions++;
|
|
269
|
+
decisions.push(decision);
|
|
270
|
+
|
|
271
|
+
// Track decision types
|
|
272
|
+
if (aiDecision.action === 'REDUCE_SIZE' || aiDecision.action === 'PAUSE') {
|
|
273
|
+
session.metrics.interventions++;
|
|
274
|
+
} else if (aiDecision.action === 'CONTINUE') {
|
|
275
|
+
session.metrics.optimizations++;
|
|
276
|
+
}
|
|
264
277
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
278
|
+
if (aiDecision.confidence !== null && aiDecision.confidence < 50) {
|
|
279
|
+
session.metrics.riskWarnings++;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Keep only last 100 decisions
|
|
283
|
+
if (session.decisions.length > 100) {
|
|
284
|
+
session.decisions = session.decisions.slice(-100);
|
|
268
285
|
}
|
|
269
286
|
}
|
|
270
287
|
}
|
|
271
288
|
|
|
289
|
+
// Calculate consensus if multiple decisions
|
|
290
|
+
if (decisions.length > 1) {
|
|
291
|
+
this.calculateConsensus(decisions);
|
|
292
|
+
}
|
|
293
|
+
|
|
272
294
|
} catch (error) {
|
|
273
|
-
|
|
295
|
+
// Silent fail
|
|
274
296
|
}
|
|
275
297
|
}
|
|
276
|
-
|
|
298
|
+
|
|
277
299
|
/**
|
|
278
|
-
*
|
|
300
|
+
* Calculate consensus from multiple agent decisions
|
|
301
|
+
* @param {Array} decisions - Array of agent decisions
|
|
302
|
+
* @returns {Object|null} Consensus result
|
|
279
303
|
*/
|
|
280
|
-
static
|
|
281
|
-
|
|
304
|
+
static calculateConsensus(decisions) {
|
|
305
|
+
if (!decisions || decisions.length === 0) return null;
|
|
282
306
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
console.log(` Reason: ${decision.reason}`);
|
|
307
|
+
// Count votes for each action
|
|
308
|
+
const votes = {};
|
|
309
|
+
let totalConfidence = 0;
|
|
310
|
+
let validConfidenceCount = 0;
|
|
288
311
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
case 'PAUSE_TRADING':
|
|
298
|
-
case 'RISK_ADJUSTMENT':
|
|
299
|
-
metrics.interventions++;
|
|
300
|
-
console.log(` Action: Consensus risk intervention - ${decision.agentNames.length} agents agree`);
|
|
301
|
-
break;
|
|
302
|
-
case 'ADJUST_FREQUENCY':
|
|
303
|
-
metrics.interventions++;
|
|
304
|
-
console.log(` Action: Consensus frequency adjustment - ${decision.agentNames.length} agents agree`);
|
|
305
|
-
break;
|
|
312
|
+
for (const decision of decisions) {
|
|
313
|
+
if (decision.action) {
|
|
314
|
+
votes[decision.action] = (votes[decision.action] || 0) + 1;
|
|
315
|
+
}
|
|
316
|
+
if (decision.confidence !== null) {
|
|
317
|
+
totalConfidence += decision.confidence;
|
|
318
|
+
validConfidenceCount++;
|
|
319
|
+
}
|
|
306
320
|
}
|
|
307
321
|
|
|
308
|
-
//
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
* Get algo status (placeholder for real implementation)
|
|
316
|
-
*/
|
|
317
|
-
static getAlgoStatus(algo) {
|
|
318
|
-
// This would interface with HQX Ultra Scalping
|
|
319
|
-
// For now, return mock data
|
|
320
|
-
return {
|
|
321
|
-
active: true,
|
|
322
|
-
positions: 1,
|
|
323
|
-
currentPnL: 145.50,
|
|
324
|
-
dayTrades: 8,
|
|
325
|
-
winRate: 0.75,
|
|
326
|
-
avgWin: 25.30,
|
|
327
|
-
avgLoss: 8.20,
|
|
328
|
-
lastTradeTime: Date.now() - 120000, // 2 minutes ago
|
|
329
|
-
currentStrategy: 'momentum_scalping',
|
|
330
|
-
parameters: {
|
|
331
|
-
tradeSize: 2,
|
|
332
|
-
stopLoss: 3,
|
|
333
|
-
takeProfit: 6,
|
|
334
|
-
maxPositions: 3
|
|
322
|
+
// Find majority action
|
|
323
|
+
let majorityAction = null;
|
|
324
|
+
let maxVotes = 0;
|
|
325
|
+
for (const [action, count] of Object.entries(votes)) {
|
|
326
|
+
if (count > maxVotes) {
|
|
327
|
+
maxVotes = count;
|
|
328
|
+
majorityAction = action;
|
|
335
329
|
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Calculate average confidence
|
|
333
|
+
const avgConfidence = validConfidenceCount > 0
|
|
334
|
+
? Math.round(totalConfidence / validConfidenceCount)
|
|
335
|
+
: null;
|
|
336
|
+
|
|
337
|
+
// Store consensus result
|
|
338
|
+
const consensus = {
|
|
339
|
+
timestamp: Date.now(),
|
|
340
|
+
action: majorityAction,
|
|
341
|
+
confidence: avgConfidence,
|
|
342
|
+
votes,
|
|
343
|
+
agentCount: decisions.length,
|
|
344
|
+
agreement: maxVotes / decisions.length
|
|
336
345
|
};
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* Get market data (placeholder)
|
|
341
|
-
*/
|
|
342
|
-
static getMarketData() {
|
|
343
|
-
// This would get real market data
|
|
344
|
-
return {
|
|
345
|
-
symbol: 'ES',
|
|
346
|
-
price: 4502.25,
|
|
347
|
-
change: 12.50,
|
|
348
|
-
volume: 1250000,
|
|
349
|
-
volatility: 0.018,
|
|
350
|
-
trend: 'bullish',
|
|
351
|
-
momentum: 'strong',
|
|
352
|
-
timeframe: '1m'
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
/**
|
|
357
|
-
* Get risk metrics (placeholder)
|
|
358
|
-
*/
|
|
359
|
-
static getRiskMetrics(algo) {
|
|
360
|
-
return {
|
|
361
|
-
maxDrawdown: 0.025, // 2.5%
|
|
362
|
-
currentDrawdown: 0.008, // 0.8%
|
|
363
|
-
exposure: 0.65, // 65% of max
|
|
364
|
-
dailyLoss: -45.30,
|
|
365
|
-
sharpeRatio: 1.8,
|
|
366
|
-
var95: 125.50 // Value at Risk
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* Make AI decision for single agent
|
|
372
|
-
*/
|
|
373
|
-
static async makeAIDecision(agent, algoStatus, marketData, riskMetrics) {
|
|
374
|
-
// Simulate AI decision making based on provider
|
|
375
|
-
const decisions = [];
|
|
376
346
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
decisions.push({
|
|
382
|
-
type: 'ADJUST_SIZE',
|
|
383
|
-
action: 'REDUCE',
|
|
384
|
-
currentSize: algoStatus.parameters.tradeSize,
|
|
385
|
-
suggestedSize: Math.max(1, Math.floor(algoStatus.parameters.tradeSize * 0.7)),
|
|
386
|
-
reason: 'High volatility detected - reducing position size',
|
|
387
|
-
confidence: 85,
|
|
388
|
-
agentType: 'technical',
|
|
389
|
-
urgency: 'medium'
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
if (riskMetrics.currentDrawdown > 0.02) {
|
|
394
|
-
decisions.push({
|
|
395
|
-
type: 'PAUSE_TRADING',
|
|
396
|
-
reason: 'Drawdown exceeding 2% - pausing to preserve capital',
|
|
397
|
-
confidence: 92,
|
|
398
|
-
agentType: 'technical',
|
|
399
|
-
urgency: 'high'
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
|
-
break;
|
|
403
|
-
|
|
404
|
-
case 'openai':
|
|
405
|
-
// OpenAI: Parameter optimization expert
|
|
406
|
-
if (marketData.trend === 'bullish' && marketData.momentum === 'strong') {
|
|
407
|
-
decisions.push({
|
|
408
|
-
type: 'ADJUST_PARAMETERS',
|
|
409
|
-
parameter: 'takeProfit',
|
|
410
|
-
current: algoStatus.parameters.takeProfit,
|
|
411
|
-
suggested: algoStatus.parameters.takeProfit * 1.25,
|
|
412
|
-
reason: 'Strong bullish trend - increasing take profit target',
|
|
413
|
-
confidence: 78,
|
|
414
|
-
agentType: 'optimization',
|
|
415
|
-
urgency: 'low'
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// OpenAI suggests scaling in consolidation
|
|
420
|
-
if (marketData.volatility < 0.015) {
|
|
421
|
-
decisions.push({
|
|
422
|
-
type: 'ADJUST_STRATEGY',
|
|
423
|
-
suggestedStrategy: 'scaling',
|
|
424
|
-
reason: 'Low volatility - switching to scaling strategy',
|
|
425
|
-
confidence: 72,
|
|
426
|
-
agentType: 'optimization',
|
|
427
|
-
urgency: 'medium'
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
break;
|
|
431
|
-
|
|
432
|
-
case 'deepseek':
|
|
433
|
-
// DeepSeek: Trading and prop firm expert
|
|
434
|
-
if (algoStatus.dayTrades > 8) {
|
|
435
|
-
decisions.push({
|
|
436
|
-
type: 'ADJUST_FREQUENCY',
|
|
437
|
-
action: 'REDUCE',
|
|
438
|
-
reason: 'Overtrading detected - reducing trade frequency',
|
|
439
|
-
confidence: 88,
|
|
440
|
-
agentType: 'trading',
|
|
441
|
-
urgency: 'high'
|
|
442
|
-
});
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// DeepSeek focuses on prop firm rules
|
|
446
|
-
if (riskMetrics.dailyLoss < -100) {
|
|
447
|
-
decisions.push({
|
|
448
|
-
type: 'RISK_ADJUSTMENT',
|
|
449
|
-
adjustment: 'HALF_SIZE',
|
|
450
|
-
reason: 'Daily loss approaching limit - halving position size',
|
|
451
|
-
confidence: 95,
|
|
452
|
-
agentType: 'trading',
|
|
453
|
-
urgency: 'critical'
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
break;
|
|
457
|
-
|
|
458
|
-
default:
|
|
459
|
-
// Generic decision for other providers
|
|
460
|
-
if (marketData.volatility > 0.03) {
|
|
461
|
-
decisions.push({
|
|
462
|
-
type: 'ADJUST_SIZE',
|
|
463
|
-
action: 'REDUCE',
|
|
464
|
-
suggestedSize: Math.max(1, Math.floor(algoStatus.parameters.tradeSize * 0.6)),
|
|
465
|
-
reason: 'Very high volatility - significant size reduction',
|
|
466
|
-
confidence: 80,
|
|
467
|
-
agentType: 'generic',
|
|
468
|
-
urgency: 'medium'
|
|
469
|
-
});
|
|
470
|
-
}
|
|
471
|
-
break;
|
|
347
|
+
// Store consensus in first session for retrieval
|
|
348
|
+
const firstSession = supervisionSessions.values().next().value;
|
|
349
|
+
if (firstSession) {
|
|
350
|
+
firstSession.lastConsensus = consensus;
|
|
472
351
|
}
|
|
473
352
|
|
|
474
|
-
return
|
|
353
|
+
return consensus;
|
|
475
354
|
}
|
|
476
355
|
|
|
477
356
|
/**
|
|
478
|
-
*
|
|
357
|
+
* Fetch REAL data from trading API
|
|
358
|
+
* NO MOCK, NO SIMULATION
|
|
479
359
|
*/
|
|
480
|
-
static async
|
|
481
|
-
if (!
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
// Get decision from each agent
|
|
491
|
-
const agentDecisions = [];
|
|
492
|
-
|
|
493
|
-
for (const agent of allAgents) {
|
|
494
|
-
const decision = await this.makeAIDecision(agent, algoStatus, marketData, riskMetrics);
|
|
495
|
-
|
|
496
|
-
if (decision) {
|
|
497
|
-
agentDecisions.push({
|
|
498
|
-
agentId: agent.id,
|
|
499
|
-
agentName: agent.name,
|
|
500
|
-
providerId: agent.providerId,
|
|
501
|
-
agentType: decision.agentType,
|
|
502
|
-
...decision
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
console.log(` ${agent.name} (${agent.providerId}): ${decision.type} (${decision.confidence}% confidence)`);
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
if (agentDecisions.length === 0) {
|
|
510
|
-
console.log(' No consensus - all agents agree current parameters are optimal');
|
|
511
|
-
return null;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
// Group decisions by type
|
|
515
|
-
const decisionGroups = {};
|
|
516
|
-
for (const decision of agentDecisions) {
|
|
517
|
-
const key = `${decision.type}_${decision.action || ''}_${decision.parameter || ''}`;
|
|
518
|
-
if (!decisionGroups[key]) {
|
|
519
|
-
decisionGroups[key] = [];
|
|
520
|
-
}
|
|
521
|
-
decisionGroups[key].push(decision);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
// Weight agents by their expertise
|
|
525
|
-
const agentWeights = {
|
|
526
|
-
'anthropic': { technical: 1.2, optimization: 1.0, trading: 1.0, generic: 1.1 },
|
|
527
|
-
'openai': { technical: 1.0, optimization: 1.3, trading: 0.9, generic: 1.2 },
|
|
528
|
-
'deepseek': { technical: 0.9, optimization: 1.0, trading: 1.4, generic: 1.1 },
|
|
529
|
-
'groq': { technical: 1.1, optimization: 1.0, trading: 1.2, generic: 1.0 },
|
|
530
|
-
'gemini': { technical: 1.0, optimization: 1.1, trading: 1.0, generic: 1.2 }
|
|
360
|
+
static async fetchRealData(service, accountId) {
|
|
361
|
+
if (!service || !accountId) return null;
|
|
362
|
+
|
|
363
|
+
const data = {
|
|
364
|
+
timestamp: Date.now(),
|
|
365
|
+
account: null,
|
|
366
|
+
positions: [],
|
|
367
|
+
orders: [],
|
|
368
|
+
trades: []
|
|
531
369
|
};
|
|
532
370
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
const weight = agentWeights[decision.providerId]?.[decision.agentType] || 1.0;
|
|
543
|
-
totalWeight += weight;
|
|
544
|
-
weightedConfidence += decision.confidence * weight;
|
|
545
|
-
agentNames.push(decision.agentName);
|
|
371
|
+
try {
|
|
372
|
+
// Get account with P&L
|
|
373
|
+
const accountResult = await service.getTradingAccounts();
|
|
374
|
+
if (accountResult.success && accountResult.accounts) {
|
|
375
|
+
data.account = accountResult.accounts.find(a =>
|
|
376
|
+
a.accountId === accountId ||
|
|
377
|
+
a.rithmicAccountId === accountId ||
|
|
378
|
+
String(a.accountId) === String(accountId)
|
|
379
|
+
);
|
|
546
380
|
}
|
|
547
381
|
|
|
548
|
-
|
|
382
|
+
// Get open positions
|
|
383
|
+
const posResult = await service.getPositions(accountId);
|
|
384
|
+
if (posResult.success && posResult.positions) {
|
|
385
|
+
data.positions = posResult.positions;
|
|
386
|
+
}
|
|
549
387
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
suggestedSize: group[0].suggestedSize,
|
|
556
|
-
current: group[0].current,
|
|
557
|
-
suggested: group[0].suggested,
|
|
558
|
-
adjustment: group[0].adjustment,
|
|
559
|
-
suggestedStrategy: group[0].suggestedStrategy,
|
|
560
|
-
reason: this.buildConsensusReason(group, decisionKey),
|
|
561
|
-
confidence: Math.round(consensusConfidence),
|
|
562
|
-
agentCount: group.length,
|
|
563
|
-
agentNames: [...new Set(agentNames)], // Unique names
|
|
564
|
-
agentTypes: [...new Set(group.map(d => d.agentType))],
|
|
565
|
-
urgency: this.calculateUrgency(group),
|
|
566
|
-
consensusStrength: totalWeight
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
// Return highest confidence consensus decision
|
|
571
|
-
if (consensusResults.length > 0) {
|
|
572
|
-
const bestDecision = consensusResults.sort((a, b) => b.confidence - a.confidence)[0];
|
|
388
|
+
// Get open orders
|
|
389
|
+
const orderResult = await service.getOrders(accountId);
|
|
390
|
+
if (orderResult.success && orderResult.orders) {
|
|
391
|
+
data.orders = orderResult.orders;
|
|
392
|
+
}
|
|
573
393
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
394
|
+
// Get today's trades (if available)
|
|
395
|
+
if (typeof service.getTrades === 'function') {
|
|
396
|
+
const tradesResult = await service.getTrades(accountId);
|
|
397
|
+
if (tradesResult.success && tradesResult.trades) {
|
|
398
|
+
data.trades = tradesResult.trades;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
579
401
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
return null;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
/**
|
|
587
|
-
* Build consensus reason
|
|
588
|
-
*/
|
|
589
|
-
static buildConsensusReason(group, decisionKey) {
|
|
590
|
-
const agentNames = group.map(d => d.agentName);
|
|
591
|
-
const agentTypes = [...new Set(group.map(d => d.agentType))];
|
|
592
|
-
|
|
593
|
-
let expertise = '';
|
|
594
|
-
if (agentTypes.length === 1) {
|
|
595
|
-
const typeMap = {
|
|
596
|
-
'technical': 'technical analysis',
|
|
597
|
-
'optimization': 'parameter optimization',
|
|
598
|
-
'trading': 'trading strategy',
|
|
599
|
-
'generic': 'general analysis'
|
|
600
|
-
};
|
|
601
|
-
expertise = `based on ${typeMap[agentTypes[0]]}`;
|
|
602
|
-
} else {
|
|
603
|
-
expertise = `based on multiple expertises (${agentTypes.join(', ')})`;
|
|
402
|
+
} catch (error) {
|
|
403
|
+
return null;
|
|
604
404
|
}
|
|
605
405
|
|
|
606
|
-
return
|
|
406
|
+
return data;
|
|
607
407
|
}
|
|
608
408
|
|
|
609
|
-
/**
|
|
610
|
-
* Calculate urgency from consensus
|
|
611
|
-
*/
|
|
612
|
-
static calculateUrgency(group) {
|
|
613
|
-
const urgencyLevels = { 'low': 1, 'medium': 2, 'high': 3, 'critical': 4 };
|
|
614
|
-
const maxUrgency = Math.max(...group.map(d => urgencyLevels[d.urgency] || 1));
|
|
615
|
-
|
|
616
|
-
const urgencyMap = { 1: 'low', 2: 'medium', 3: 'high', 4: 'critical' };
|
|
617
|
-
return urgencyMap[maxUrgency];
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
/**
|
|
621
|
-
* Execute AI decision
|
|
622
|
-
*/
|
|
623
|
-
static async executeDecision(session, decision) {
|
|
624
|
-
const { agent, algo, metrics } = session;
|
|
625
|
-
|
|
626
|
-
console.log(`\nš¤ ${agent.name} Decision:`);
|
|
627
|
-
console.log(` Type: ${decision.type}`);
|
|
628
|
-
console.log(` Reason: ${decision.reason}`);
|
|
629
|
-
console.log(` Confidence: ${decision.confidence}%`);
|
|
630
|
-
|
|
631
|
-
// Update metrics
|
|
632
|
-
switch (decision.type) {
|
|
633
|
-
case 'ADJUST_SIZE':
|
|
634
|
-
case 'ADJUST_PARAMETERS':
|
|
635
|
-
metrics.optimizations++;
|
|
636
|
-
console.log(` Action: Optimizing algo parameters`);
|
|
637
|
-
break;
|
|
638
|
-
case 'PAUSE_TRADING':
|
|
639
|
-
case 'REDUCE_EXPOSURE':
|
|
640
|
-
metrics.interventions++;
|
|
641
|
-
console.log(` Action: Risk intervention - ${decision.reason.toLowerCase()}`);
|
|
642
|
-
break;
|
|
643
|
-
case 'RISK_WARNING':
|
|
644
|
-
metrics.riskWarnings++;
|
|
645
|
-
console.log(` Action: Risk warning issued`);
|
|
646
|
-
break;
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
// In real implementation, this would actually modify the algo
|
|
650
|
-
// For now, just log the decision
|
|
651
|
-
console.log(` Status: Decision logged for manual review\n`);
|
|
652
|
-
|
|
653
|
-
return decision;
|
|
654
|
-
}
|
|
655
|
-
|
|
656
409
|
/**
|
|
657
410
|
* Get supervision status for an agent
|
|
658
411
|
*/
|
|
@@ -665,12 +418,14 @@ class AISupervisor {
|
|
|
665
418
|
const duration = Date.now() - session.startTime;
|
|
666
419
|
return {
|
|
667
420
|
active: true,
|
|
421
|
+
agentId: session.agentId,
|
|
668
422
|
agentName: session.agent.name,
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
423
|
+
accountId: session.accountId,
|
|
424
|
+
duration,
|
|
425
|
+
lastCheck: session.lastCheck,
|
|
426
|
+
lastData: session.lastData,
|
|
427
|
+
metrics: session.metrics,
|
|
428
|
+
decisionsCount: session.decisions.length
|
|
674
429
|
};
|
|
675
430
|
}
|
|
676
431
|
|
|
@@ -678,36 +433,133 @@ class AISupervisor {
|
|
|
678
433
|
* Get all active supervision sessions
|
|
679
434
|
*/
|
|
680
435
|
static getAllStatus() {
|
|
681
|
-
const
|
|
682
|
-
const isConsensusMode = supervisionSessions.size > 1 && consensusInterval;
|
|
436
|
+
const sessions = [];
|
|
437
|
+
const isConsensusMode = supervisionSessions.size > 1 && consensusInterval !== null;
|
|
683
438
|
|
|
684
439
|
for (const [agentId, session] of supervisionSessions.entries()) {
|
|
685
|
-
|
|
440
|
+
sessions.push({
|
|
441
|
+
active: true,
|
|
686
442
|
agentId,
|
|
687
443
|
agentName: session.agent.name,
|
|
444
|
+
accountId: session.accountId,
|
|
688
445
|
duration: Date.now() - session.startTime,
|
|
446
|
+
lastCheck: session.lastCheck,
|
|
689
447
|
metrics: session.metrics,
|
|
690
|
-
lastDecision: session.decisions.length > 0 ? session.decisions[session.decisions.length - 1] : null,
|
|
691
448
|
mode: isConsensusMode ? 'consensus' : 'single'
|
|
692
449
|
});
|
|
693
450
|
}
|
|
694
451
|
|
|
695
|
-
return
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
452
|
+
return sessions;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Get aggregated data from all supervised accounts
|
|
457
|
+
* Returns REAL data only
|
|
458
|
+
*/
|
|
459
|
+
static getAggregatedData() {
|
|
460
|
+
const result = {
|
|
461
|
+
totalAccounts: 0,
|
|
462
|
+
totalBalance: 0,
|
|
463
|
+
totalPnL: 0,
|
|
464
|
+
totalPositions: 0,
|
|
465
|
+
totalOrders: 0,
|
|
466
|
+
totalTrades: 0,
|
|
467
|
+
accounts: []
|
|
699
468
|
};
|
|
469
|
+
|
|
470
|
+
for (const [agentId, session] of supervisionSessions.entries()) {
|
|
471
|
+
if (session.lastData) {
|
|
472
|
+
const data = session.lastData;
|
|
473
|
+
|
|
474
|
+
if (data.account) {
|
|
475
|
+
result.totalAccounts++;
|
|
476
|
+
result.totalBalance += data.account.balance || 0;
|
|
477
|
+
result.totalPnL += data.account.profitAndLoss || 0;
|
|
478
|
+
result.accounts.push({
|
|
479
|
+
accountId: data.account.accountId,
|
|
480
|
+
accountName: data.account.accountName,
|
|
481
|
+
balance: data.account.balance,
|
|
482
|
+
pnl: data.account.profitAndLoss,
|
|
483
|
+
platform: data.account.platform
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
result.totalPositions += data.positions?.length || 0;
|
|
488
|
+
result.totalOrders += data.orders?.length || 0;
|
|
489
|
+
result.totalTrades += data.trades?.length || 0;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return result;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Get latest AI decision for an agent
|
|
498
|
+
* @param {string} agentId - Agent ID
|
|
499
|
+
* @returns {Object|null} Latest decision or null
|
|
500
|
+
*/
|
|
501
|
+
static getLatestDecision(agentId) {
|
|
502
|
+
const session = supervisionSessions.get(agentId);
|
|
503
|
+
if (!session || session.decisions.length === 0) {
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
506
|
+
return session.decisions[session.decisions.length - 1];
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Get all decisions for an agent
|
|
511
|
+
* @param {string} agentId - Agent ID
|
|
512
|
+
* @param {number} limit - Max decisions to return (default 10)
|
|
513
|
+
* @returns {Array} Array of decisions
|
|
514
|
+
*/
|
|
515
|
+
static getDecisions(agentId, limit = 10) {
|
|
516
|
+
const session = supervisionSessions.get(agentId);
|
|
517
|
+
if (!session) {
|
|
518
|
+
return [];
|
|
519
|
+
}
|
|
520
|
+
return session.decisions.slice(-limit);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Get latest consensus (multi-agent mode only)
|
|
525
|
+
* @returns {Object|null} Latest consensus or null
|
|
526
|
+
*/
|
|
527
|
+
static getConsensus() {
|
|
528
|
+
if (supervisionSessions.size <= 1) {
|
|
529
|
+
return null;
|
|
530
|
+
}
|
|
531
|
+
const firstSession = supervisionSessions.values().next().value;
|
|
532
|
+
return firstSession?.lastConsensus || null;
|
|
700
533
|
}
|
|
701
534
|
|
|
702
535
|
/**
|
|
703
536
|
* Stop all supervision sessions
|
|
704
537
|
*/
|
|
705
538
|
static stopAll() {
|
|
539
|
+
const results = [];
|
|
706
540
|
const agentIds = Array.from(supervisionSessions.keys());
|
|
541
|
+
|
|
707
542
|
for (const agentId of agentIds) {
|
|
708
|
-
this.stop(agentId);
|
|
543
|
+
const result = this.stop(agentId);
|
|
544
|
+
results.push({ agentId, ...result });
|
|
709
545
|
}
|
|
546
|
+
|
|
547
|
+
return results;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Check if any supervision is active
|
|
552
|
+
*/
|
|
553
|
+
static isActive() {
|
|
554
|
+
return supervisionSessions.size > 0;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Get session count
|
|
559
|
+
*/
|
|
560
|
+
static getSessionCount() {
|
|
561
|
+
return supervisionSessions.size;
|
|
710
562
|
}
|
|
711
563
|
}
|
|
712
564
|
|
|
713
|
-
module.exports = AISupervisor;
|
|
565
|
+
module.exports = AISupervisor;
|