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.
@@ -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, targetAlgo) {
36
+ static start(agentId, service, accountId) {
30
37
  if (supervisionSessions.has(agentId)) {
31
- console.log(`Supervision already active for agent ${agentId}`);
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
- console.log(`Agent ${agentId} not found`);
38
- return false;
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
- algo: targetAlgo,
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
- }, 5000);
74
+ }, 10000); // Every 10 seconds
65
75
 
66
76
  supervisionSessions.set(agentId, session);
67
- console.log(`AI supervision started: ${agentInfo.name} monitoring HQX algo`);
68
- console.log(` Mode: Single agent supervision`);
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 (!this.consensusInterval) {
101
- this.consensusInterval = setInterval(() => {
112
+ if (!consensusInterval) {
113
+ consensusInterval = setInterval(() => {
102
114
  this.superviseConsensus();
103
- }, 5000);
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
- }, 5000);
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 true;
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 algo status
207
- const algoStatus = this.getAlgoStatus(session.algo);
208
- const marketData = this.getMarketData();
209
- const riskMetrics = this.getRiskMetrics(session.algo);
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
- // Make AI decision
212
- const decision = await this.makeAIDecision(session.agent, algoStatus, marketData, riskMetrics);
181
+ // Call AI for analysis (real API call, returns null if fails)
182
+ const aiDecision = await analyzeTrading(session.agent, data);
213
183
 
214
- if (decision) {
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
- // Execute decision if confidence is high enough
219
- if (decision.confidence > 75) {
220
- await this.executeDecision(session, decision);
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
- console.log(`Supervision error for agent ${agentId}: ${error.message}`);
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
- // Get all active agents
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
- // Make consensus decision
251
- const consensusDecision = await this.makeConsensusDecision(allAgents, algoStatus, marketData, riskMetrics);
252
-
253
- if (consensusDecision) {
254
- // Update all sessions with the consensus decision
255
- for (const [agentId, session] of allSessions) {
256
- session.decisions.push({
257
- ...consensusDecision,
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
- isConsensus: true,
260
- participantAgents: consensusDecision.agentNames
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
- // Execute decision if confidence is high enough (lower threshold for consensus)
266
- if (consensusDecision.confidence > 70) {
267
- await this.executeConsensusDecision(session, consensusDecision);
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
- console.log(`Consensus supervision error: ${error.message}`);
295
+ // Silent fail
274
296
  }
275
297
  }
276
-
298
+
277
299
  /**
278
- * Execute multi-agent consensus decision
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 async executeConsensusDecision(session, decision) {
281
- const { agent, metrics } = session;
304
+ static calculateConsensus(decisions) {
305
+ if (!decisions || decisions.length === 0) return null;
282
306
 
283
- console.log(`\nšŸŽÆ MULTI-AGENT CONSENSUS ACTION:`);
284
- console.log(` Agents: ${decision.agentNames.join(', ')}`);
285
- console.log(` Type: ${decision.type}`);
286
- console.log(` Confidence: ${decision.confidence}%`);
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
- // Update metrics
290
- switch (decision.type) {
291
- case 'ADJUST_SIZE':
292
- case 'ADJUST_PARAMETERS':
293
- case 'ADJUST_STRATEGY':
294
- metrics.optimizations++;
295
- console.log(` Action: Consensus optimization - ${decision.agentNames.length} agents agree`);
296
- break;
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
- // In real implementation, this would actually modify the algo
309
- console.log(` Status: Consensus decision executed across all agents\n`);
310
-
311
- return decision;
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
- switch (agent.providerId) {
378
- case 'anthropic':
379
- // Claude: Technical analysis expert
380
- if (marketData.volatility > 0.025 && algoStatus.parameters.tradeSize > 1) {
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 decisions.length > 0 ? decisions[0] : null;
353
+ return consensus;
475
354
  }
476
355
 
477
356
  /**
478
- * Make multi-agent consensus decision
357
+ * Fetch REAL data from trading API
358
+ * NO MOCK, NO SIMULATION
479
359
  */
480
- static async makeConsensusDecision(allAgents, algoStatus, marketData, riskMetrics) {
481
- if (!allAgents || allAgents.length === 0) return null;
482
-
483
- if (allAgents.length === 1) {
484
- // Single agent - use normal decision
485
- return await this.makeAIDecision(allAgents[0], algoStatus, marketData, riskMetrics);
486
- }
487
-
488
- console.log(`šŸ¤ CONSENSUS: Getting decisions from ${allAgents.length} agents...`);
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
- // Calculate weighted consensus for each decision type
534
- const consensusResults = [];
535
-
536
- for (const [decisionKey, group] of Object.entries(decisionGroups)) {
537
- let totalWeight = 0;
538
- let weightedConfidence = 0;
539
- let agentNames = [];
540
-
541
- for (const decision of group) {
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
- const consensusConfidence = weightedConfidence / totalWeight;
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
- consensusResults.push({
551
- type: group[0].type,
552
- action: group[0].action,
553
- parameter: group[0].parameter,
554
- currentSize: group[0].currentSize,
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
- console.log(`šŸŽÆ CONSENSUS DECISION:`);
575
- console.log(` Type: ${bestDecision.type}`);
576
- console.log(` Confidence: ${bestDecision.confidence}%`);
577
- console.log(` Agents: ${bestDecision.agentNames.join(', ')}`);
578
- console.log(` Reason: ${bestDecision.reason}`);
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
- return bestDecision;
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 `Consensus from ${agentNames.join(', ')} - ${expertise}`;
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
- duration: Math.round(duration / 1000),
670
- decisions: session.metrics.totalDecisions,
671
- interventions: session.metrics.interventions,
672
- optimizations: session.metrics.optimizations,
673
- lastDecision: session.decisions.length > 0 ? session.decisions[session.decisions.length - 1] : null
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 result = [];
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
- result.push({
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
- sessions: result,
697
- mode: isConsensusMode ? 'consensus' : 'single',
698
- totalAgents: supervisionSessions.size
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;