hedgequantx 2.6.161 → 2.6.163

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/package.json +1 -1
  2. package/src/menus/ai-agent-connect.js +181 -0
  3. package/src/menus/ai-agent-models.js +219 -0
  4. package/src/menus/ai-agent-oauth.js +292 -0
  5. package/src/menus/ai-agent-ui.js +141 -0
  6. package/src/menus/ai-agent.js +88 -1489
  7. package/src/pages/algo/copy-engine.js +449 -0
  8. package/src/pages/algo/copy-trading.js +11 -543
  9. package/src/pages/algo/smart-logs-data.js +218 -0
  10. package/src/pages/algo/smart-logs.js +9 -214
  11. package/src/pages/algo/ui-constants.js +144 -0
  12. package/src/pages/algo/ui-summary.js +184 -0
  13. package/src/pages/algo/ui.js +42 -526
  14. package/src/pages/stats-calculations.js +191 -0
  15. package/src/pages/stats-ui.js +381 -0
  16. package/src/pages/stats.js +14 -507
  17. package/src/services/ai/client-analysis.js +194 -0
  18. package/src/services/ai/client-models.js +333 -0
  19. package/src/services/ai/client.js +6 -489
  20. package/src/services/ai/index.js +2 -257
  21. package/src/services/ai/providers/direct-providers.js +323 -0
  22. package/src/services/ai/providers/index.js +8 -472
  23. package/src/services/ai/providers/other-providers.js +104 -0
  24. package/src/services/ai/proxy-install.js +249 -0
  25. package/src/services/ai/proxy-manager.js +29 -411
  26. package/src/services/ai/proxy-remote.js +161 -0
  27. package/src/services/ai/supervisor-optimize.js +215 -0
  28. package/src/services/ai/supervisor-sync.js +178 -0
  29. package/src/services/ai/supervisor.js +50 -515
  30. package/src/services/ai/validation.js +250 -0
  31. package/src/services/hqx-server-events.js +110 -0
  32. package/src/services/hqx-server-handlers.js +217 -0
  33. package/src/services/hqx-server-latency.js +136 -0
  34. package/src/services/hqx-server.js +51 -403
  35. package/src/services/position-constants.js +28 -0
  36. package/src/services/position-exit-logic.js +174 -0
  37. package/src/services/position-manager.js +90 -629
  38. package/src/services/position-momentum.js +206 -0
  39. package/src/services/projectx/accounts.js +142 -0
  40. package/src/services/projectx/index.js +40 -289
  41. package/src/services/projectx/trading.js +180 -0
  42. package/src/services/rithmic/contracts.js +218 -0
  43. package/src/services/rithmic/handlers.js +2 -208
  44. package/src/services/rithmic/index.js +28 -712
  45. package/src/services/rithmic/latency-tracker.js +182 -0
  46. package/src/services/rithmic/market-data-decoders.js +229 -0
  47. package/src/services/rithmic/market-data.js +1 -278
  48. package/src/services/rithmic/orders-fast.js +246 -0
  49. package/src/services/rithmic/orders.js +1 -251
  50. package/src/services/rithmic/proto-decoders.js +403 -0
  51. package/src/services/rithmic/protobuf.js +7 -443
  52. package/src/services/rithmic/specs.js +146 -0
  53. package/src/services/rithmic/trade-history.js +254 -0
  54. package/src/services/strategy/hft-signal-calc.js +147 -0
  55. package/src/services/strategy/hft-tick.js +33 -133
  56. package/src/services/tradovate/index.js +6 -119
  57. package/src/services/tradovate/orders.js +145 -0
@@ -7,7 +7,12 @@
7
7
  */
8
8
 
9
9
  const { connections } = require('../session');
10
- const { analyzeTrading, analyzePerformance, getMarketAdvice } = require('./client');
10
+ const { analyzeTrading, analyzePerformance, getMarketAdvice: getMarketAdviceClient } = require('./client');
11
+ const { feedTick: feedTickImpl, feedSignal: feedSignalImpl, feedTrade: feedTradeImpl,
12
+ updatePosition: updatePositionImpl, updatePnL: updatePnLImpl,
13
+ checkIntervention: checkInterventionImpl, getSyncStatus: getSyncStatusImpl } = require('./supervisor-sync');
14
+ const { requestOptimization: requestOptimizationImpl, getMarketAdvice: getMarketAdviceImpl,
15
+ applyOptimization: applyOptimizationImpl } = require('./supervisor-optimize');
11
16
 
12
17
  let aiService = null;
13
18
 
@@ -68,10 +73,9 @@ class AISupervisor {
68
73
  const currentSessionCount = supervisionSessions.size;
69
74
 
70
75
  if (currentSessionCount === 0) {
71
- // First agent - start individual supervision
72
76
  session.interval = setInterval(() => {
73
77
  this.supervise(agentId);
74
- }, 10000); // Every 10 seconds
78
+ }, 10000);
75
79
 
76
80
  supervisionSessions.set(agentId, session);
77
81
 
@@ -82,7 +86,6 @@ class AISupervisor {
82
86
  };
83
87
 
84
88
  } else {
85
- // Additional agent - switch to consensus mode
86
89
  this.switchToConsensusMode();
87
90
  session.interval = null;
88
91
  supervisionSessions.set(agentId, session);
@@ -100,7 +103,6 @@ class AISupervisor {
100
103
  * Switch to consensus mode when multiple agents
101
104
  */
102
105
  static switchToConsensusMode() {
103
- // Clear all individual intervals
104
106
  for (const [agentId, session] of supervisionSessions.entries()) {
105
107
  if (session.interval) {
106
108
  clearInterval(session.interval);
@@ -108,7 +110,6 @@ class AISupervisor {
108
110
  }
109
111
  }
110
112
 
111
- // Start single consensus loop
112
113
  if (!consensusInterval) {
113
114
  consensusInterval = setInterval(() => {
114
115
  this.superviseConsensus();
@@ -132,7 +133,6 @@ class AISupervisor {
132
133
  const duration = Date.now() - session.startTime;
133
134
  supervisionSessions.delete(agentId);
134
135
 
135
- // Check if we need to switch back to single agent mode
136
136
  const remainingSessions = supervisionSessions.size;
137
137
  if (remainingSessions === 1 && consensusInterval) {
138
138
  clearInterval(consensusInterval);
@@ -158,155 +158,71 @@ class AISupervisor {
158
158
  }
159
159
 
160
160
  /**
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)
161
+ * Process AI decision and update session metrics
166
162
  */
163
+ static processDecision(session, aiDecision, data, agentId = null) {
164
+ const decision = {
165
+ timestamp: Date.now(),
166
+ action: aiDecision.action || null,
167
+ confidence: aiDecision.confidence || null,
168
+ reason: aiDecision.reason || null,
169
+ dataSnapshot: {
170
+ balance: data.account?.balance ?? null,
171
+ pnl: data.account?.profitAndLoss ?? null,
172
+ positions: data.positions?.length ?? 0,
173
+ orders: data.orders?.length ?? 0
174
+ }
175
+ };
176
+ if (agentId) { decision.agentId = agentId; decision.agentName = session.agent.name; }
177
+ session.decisions.push(decision);
178
+ session.metrics.totalDecisions++;
179
+ if (aiDecision.action === 'REDUCE_SIZE' || aiDecision.action === 'PAUSE') session.metrics.interventions++;
180
+ else if (aiDecision.action === 'CONTINUE') session.metrics.optimizations++;
181
+ if (aiDecision.confidence !== null && aiDecision.confidence < 50) session.metrics.riskWarnings++;
182
+ if (session.decisions.length > 100) session.decisions = session.decisions.slice(-100);
183
+ return decision;
184
+ }
185
+
186
+ /** Main supervision loop - single agent */
167
187
  static async supervise(agentId) {
168
188
  const session = supervisionSessions.get(agentId);
169
189
  if (!session) return;
170
-
171
190
  try {
172
191
  session.lastCheck = Date.now();
173
-
174
- // Get REAL data from API
175
192
  const data = await this.fetchRealData(session.service, session.accountId);
176
193
  if (!data) return;
177
-
178
194
  session.lastData = data;
179
195
  session.metrics.lastFetch = Date.now();
180
-
181
- // Call AI for analysis (real API call, returns null if fails)
182
196
  const aiDecision = await analyzeTrading(session.agent, data);
183
-
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
-
199
- session.decisions.push(decision);
200
- session.metrics.totalDecisions++;
201
-
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);
217
- }
218
- }
219
-
220
- } catch (error) {
221
- // Silent fail - don't spam logs
222
- }
197
+ if (aiDecision) this.processDecision(session, aiDecision, data);
198
+ } catch (error) { /* Silent fail */ }
223
199
  }
224
200
 
225
- /**
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
- */
201
+ /** Multi-agent consensus supervision loop */
232
202
  static async superviseConsensus() {
233
203
  const allSessions = Array.from(supervisionSessions.entries());
234
204
  if (allSessions.length === 0) return;
235
-
236
205
  try {
237
206
  const decisions = [];
238
-
239
- // Fetch data and get AI analysis for each session
240
207
  for (const [agentId, session] of allSessions) {
241
208
  const data = await this.fetchRealData(session.service, session.accountId);
242
209
  if (!data) continue;
243
-
244
210
  session.lastData = data;
245
211
  session.lastCheck = Date.now();
246
212
  session.metrics.lastFetch = Date.now();
247
-
248
- // Call AI for analysis (real API call)
249
213
  const aiDecision = await analyzeTrading(session.agent, data);
250
-
251
- if (aiDecision) {
252
- const decision = {
253
- timestamp: Date.now(),
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
- };
266
-
267
- session.decisions.push(decision);
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
- }
277
-
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);
285
- }
286
- }
287
- }
288
-
289
- // Calculate consensus if multiple decisions
290
- if (decisions.length > 1) {
291
- this.calculateConsensus(decisions);
214
+ if (aiDecision) decisions.push(this.processDecision(session, aiDecision, data, agentId));
292
215
  }
293
-
294
- } catch (error) {
295
- // Silent fail
296
- }
216
+ if (decisions.length > 1) this.calculateConsensus(decisions);
217
+ } catch (error) { /* Silent fail */ }
297
218
  }
298
219
 
299
220
  /**
300
221
  * Calculate consensus from multiple agent decisions
301
- * RULE: ALL agents must agree (100% unanimity) before taking action
302
- *
303
- * @param {Array} decisions - Array of agent decisions
304
- * @returns {Object|null} Consensus result
305
222
  */
306
223
  static calculateConsensus(decisions) {
307
224
  if (!decisions || decisions.length === 0) return null;
308
225
 
309
- // Count votes for each action
310
226
  const votes = {};
311
227
  let totalConfidence = 0;
312
228
  let validConfidenceCount = 0;
@@ -321,26 +237,21 @@ class AISupervisor {
321
237
  }
322
238
  }
323
239
 
324
- // Check for UNANIMITY - ALL agents must agree
325
240
  let unanimousAction = null;
326
241
  let isUnanimous = false;
327
242
 
328
243
  for (const [action, count] of Object.entries(votes)) {
329
244
  if (count === decisions.length) {
330
- // All agents voted for this action
331
245
  unanimousAction = action;
332
246
  isUnanimous = true;
333
247
  break;
334
248
  }
335
249
  }
336
250
 
337
- // Calculate average confidence
338
251
  const avgConfidence = validConfidenceCount > 0
339
252
  ? Math.round(totalConfidence / validConfidenceCount)
340
253
  : null;
341
254
 
342
- // Store consensus result
343
- // If not unanimous, action is HOLD (no action taken)
344
255
  const consensus = {
345
256
  timestamp: Date.now(),
346
257
  action: isUnanimous ? unanimousAction : 'HOLD',
@@ -351,7 +262,6 @@ class AISupervisor {
351
262
  agreement: isUnanimous ? 1.0 : 0
352
263
  };
353
264
 
354
- // Store consensus in first session for retrieval
355
265
  const firstSession = supervisionSessions.values().next().value;
356
266
  if (firstSession) {
357
267
  firstSession.lastConsensus = consensus;
@@ -362,7 +272,6 @@ class AISupervisor {
362
272
 
363
273
  /**
364
274
  * Fetch REAL data from trading API
365
- * NO MOCK, NO SIMULATION
366
275
  */
367
276
  static async fetchRealData(service, accountId) {
368
277
  if (!service || !accountId) return null;
@@ -376,7 +285,6 @@ class AISupervisor {
376
285
  };
377
286
 
378
287
  try {
379
- // Get account with P&L
380
288
  const accountResult = await service.getTradingAccounts();
381
289
  if (accountResult.success && accountResult.accounts) {
382
290
  data.account = accountResult.accounts.find(a =>
@@ -386,19 +294,16 @@ class AISupervisor {
386
294
  );
387
295
  }
388
296
 
389
- // Get open positions
390
297
  const posResult = await service.getPositions(accountId);
391
298
  if (posResult.success && posResult.positions) {
392
299
  data.positions = posResult.positions;
393
300
  }
394
301
 
395
- // Get open orders
396
302
  const orderResult = await service.getOrders(accountId);
397
303
  if (orderResult.success && orderResult.orders) {
398
304
  data.orders = orderResult.orders;
399
305
  }
400
306
 
401
- // Get today's trades (if available)
402
307
  if (typeof service.getTrades === 'function') {
403
308
  const tradesResult = await service.getTrades(accountId);
404
309
  if (tradesResult.success && tradesResult.trades) {
@@ -461,7 +366,6 @@ class AISupervisor {
461
366
 
462
367
  /**
463
368
  * Get aggregated data from all supervised accounts
464
- * Returns REAL data only
465
369
  */
466
370
  static getAggregatedData() {
467
371
  const result = {
@@ -502,8 +406,6 @@ class AISupervisor {
502
406
 
503
407
  /**
504
408
  * Get latest AI decision for an agent
505
- * @param {string} agentId - Agent ID
506
- * @returns {Object|null} Latest decision or null
507
409
  */
508
410
  static getLatestDecision(agentId) {
509
411
  const session = supervisionSessions.get(agentId);
@@ -515,9 +417,6 @@ class AISupervisor {
515
417
 
516
418
  /**
517
419
  * Get all decisions for an agent
518
- * @param {string} agentId - Agent ID
519
- * @param {number} limit - Max decisions to return (default 10)
520
- * @returns {Array} Array of decisions
521
420
  */
522
421
  static getDecisions(agentId, limit = 10) {
523
422
  const session = supervisionSessions.get(agentId);
@@ -529,7 +428,6 @@ class AISupervisor {
529
428
 
530
429
  /**
531
430
  * Get latest consensus (multi-agent mode only)
532
- * @returns {Object|null} Latest consensus or null
533
431
  */
534
432
  static getConsensus() {
535
433
  if (supervisionSessions.size <= 1) {
@@ -568,382 +466,19 @@ class AISupervisor {
568
466
  return supervisionSessions.size;
569
467
  }
570
468
 
571
- /**
572
- * Feed market tick to all agents (sync with strategy)
573
- * Agents receive the same data as the strategy in real-time
574
- *
575
- * @param {Object} tick - Market tick data { price, bid, ask, volume, timestamp }
576
- */
577
- static feedTick(tick) {
578
- if (supervisionSessions.size === 0) return;
579
-
580
- // Store latest tick in all sessions
581
- for (const [agentId, session] of supervisionSessions.entries()) {
582
- if (!session.marketData) {
583
- session.marketData = { ticks: [], lastTick: null };
584
- }
585
- session.marketData.lastTick = tick;
586
- session.marketData.ticks.push(tick);
587
-
588
- // Keep only last 1000 ticks to prevent memory bloat
589
- if (session.marketData.ticks.length > 1000) {
590
- session.marketData.ticks = session.marketData.ticks.slice(-1000);
591
- }
592
- }
593
- }
594
-
595
- /**
596
- * Feed strategy signal to all agents (sync with strategy)
597
- * Agents see every signal the strategy generates
598
- *
599
- * @param {Object} signal - Strategy signal { direction, entry, stopLoss, takeProfit, confidence }
600
- */
601
- static feedSignal(signal) {
602
- if (supervisionSessions.size === 0) return;
603
-
604
- const signalData = {
605
- timestamp: Date.now(),
606
- direction: signal.direction,
607
- entry: signal.entry,
608
- stopLoss: signal.stopLoss,
609
- takeProfit: signal.takeProfit,
610
- confidence: signal.confidence
611
- };
612
-
613
- // Store signal in all sessions
614
- for (const [agentId, session] of supervisionSessions.entries()) {
615
- if (!session.signals) {
616
- session.signals = [];
617
- }
618
- session.signals.push(signalData);
619
-
620
- // Keep only last 100 signals
621
- if (session.signals.length > 100) {
622
- session.signals = session.signals.slice(-100);
623
- }
624
- }
625
- }
626
-
627
- /**
628
- * Feed trade execution to all agents (sync with strategy)
629
- * Agents see every trade executed
630
- *
631
- * @param {Object} trade - Trade data { side, qty, price, pnl, symbol }
632
- */
633
- static feedTrade(trade) {
634
- if (supervisionSessions.size === 0) return;
635
-
636
- const tradeData = {
637
- timestamp: Date.now(),
638
- side: trade.side,
639
- qty: trade.qty,
640
- price: trade.price,
641
- pnl: trade.pnl,
642
- symbol: trade.symbol
643
- };
644
-
645
- // Store trade in all sessions
646
- for (const [agentId, session] of supervisionSessions.entries()) {
647
- if (!session.trades) {
648
- session.trades = [];
649
- }
650
- session.trades.push(tradeData);
651
- }
652
- }
653
-
654
- /**
655
- * Update current position for all agents
656
- *
657
- * @param {Object} position - Position data { qty, side, entryPrice, pnl }
658
- */
659
- static updatePosition(position) {
660
- if (supervisionSessions.size === 0) return;
661
-
662
- for (const [agentId, session] of supervisionSessions.entries()) {
663
- session.currentPosition = {
664
- timestamp: Date.now(),
665
- qty: position.qty,
666
- side: position.side,
667
- entryPrice: position.entryPrice,
668
- pnl: position.pnl
669
- };
670
- }
671
- }
672
-
673
- /**
674
- * Update P&L for all agents
675
- *
676
- * @param {number} pnl - Current session P&L
677
- * @param {number} balance - Account balance
678
- */
679
- static updatePnL(pnl, balance) {
680
- if (supervisionSessions.size === 0) return;
681
-
682
- for (const [agentId, session] of supervisionSessions.entries()) {
683
- session.currentPnL = pnl;
684
- session.currentBalance = balance;
685
- }
686
- }
469
+ // Strategy sync methods (delegated to supervisor-sync.js)
470
+ static feedTick(tick) { feedTickImpl(supervisionSessions, tick); }
471
+ static feedSignal(signal) { feedSignalImpl(supervisionSessions, signal); }
472
+ static feedTrade(trade) { feedTradeImpl(supervisionSessions, trade); }
473
+ static updatePosition(position) { updatePositionImpl(supervisionSessions, position); }
474
+ static updatePnL(pnl, balance) { updatePnLImpl(supervisionSessions, pnl, balance); }
475
+ static checkIntervention() { return checkInterventionImpl(supervisionSessions, () => this.getConsensus()); }
476
+ static getSyncStatus() { return getSyncStatusImpl(supervisionSessions); }
687
477
 
688
- /**
689
- * Check if agents recommend intervention (PAUSE, REDUCE_SIZE, etc.)
690
- * In CONSENSUS mode, ALL agents must agree to continue trading
691
- *
692
- * @returns {Object} { shouldContinue: boolean, action: string, reason: string }
693
- */
694
- static checkIntervention() {
695
- if (supervisionSessions.size === 0) {
696
- return { shouldContinue: true, action: 'CONTINUE', reason: 'No AI supervision active' };
697
- }
698
-
699
- // Get last consensus or individual decision
700
- const consensus = this.getConsensus();
701
-
702
- if (consensus && consensus.isUnanimous) {
703
- if (consensus.action === 'PAUSE' || consensus.action === 'STOP') {
704
- return { shouldContinue: false, action: consensus.action, reason: 'AI agents recommend pause' };
705
- }
706
- if (consensus.action === 'REDUCE_SIZE') {
707
- return { shouldContinue: true, action: 'REDUCE_SIZE', reason: 'AI agents recommend reducing size' };
708
- }
709
- } else if (consensus && !consensus.isUnanimous) {
710
- // Agents disagree - be conservative, don't take new trades
711
- return { shouldContinue: false, action: 'HOLD', reason: 'AI agents disagree - waiting for consensus' };
712
- }
713
-
714
- return { shouldContinue: true, action: 'CONTINUE', reason: 'AI supervision active' };
715
- }
716
-
717
- /**
718
- * Get real-time sync status for display
719
- * Shows what data the agents are receiving
720
- *
721
- * @returns {Object} Sync status
722
- */
723
- static getSyncStatus() {
724
- if (supervisionSessions.size === 0) {
725
- return { synced: false, agents: 0 };
726
- }
727
-
728
- const firstSession = supervisionSessions.values().next().value;
729
-
730
- return {
731
- synced: true,
732
- agents: supervisionSessions.size,
733
- lastTick: firstSession?.marketData?.lastTick?.timestamp || null,
734
- tickCount: firstSession?.marketData?.ticks?.length || 0,
735
- signalCount: firstSession?.signals?.length || 0,
736
- tradeCount: firstSession?.trades?.length || 0,
737
- currentPnL: firstSession?.currentPnL || 0,
738
- currentPosition: firstSession?.currentPosition || null
739
- };
740
- }
741
-
742
- /**
743
- * Request strategy optimization from all agents
744
- * Agents analyze performance data and suggest improvements
745
- * In CONSENSUS mode, only unanimous suggestions are applied
746
- *
747
- * @param {Object} performanceData - Strategy performance data
748
- * @returns {Promise<Object|null>} Optimization suggestions (consensus)
749
- */
750
- static async requestOptimization(performanceData) {
751
- if (supervisionSessions.size === 0) return null;
752
-
753
- const allSessions = Array.from(supervisionSessions.values());
754
- const suggestions = [];
755
-
756
- // Get optimization suggestions from each agent
757
- for (const session of allSessions) {
758
- try {
759
- const suggestion = await analyzePerformance(session.agent, performanceData);
760
- if (suggestion) {
761
- suggestions.push({
762
- agentId: session.agentId,
763
- agentName: session.agent.name,
764
- ...suggestion
765
- });
766
- }
767
- } catch (e) {
768
- // Silent fail for individual agent
769
- }
770
- }
771
-
772
- if (suggestions.length === 0) return null;
773
-
774
- // If single agent, return its suggestion
775
- if (suggestions.length === 1) {
776
- return {
777
- mode: 'INDIVIDUAL',
778
- ...suggestions[0]
779
- };
780
- }
781
-
782
- // CONSENSUS MODE: Find common optimizations
783
- const consensusOptimizations = [];
784
- const allOptimizations = suggestions.flatMap(s => s.optimizations || []);
785
-
786
- // Group by parameter name
787
- const paramGroups = {};
788
- for (const opt of allOptimizations) {
789
- if (!opt.param) continue;
790
- if (!paramGroups[opt.param]) {
791
- paramGroups[opt.param] = [];
792
- }
793
- paramGroups[opt.param].push(opt);
794
- }
795
-
796
- // Find unanimous suggestions (all agents agree on direction)
797
- for (const [param, opts] of Object.entries(paramGroups)) {
798
- if (opts.length === suggestions.length) {
799
- // All agents suggested this param - check if they agree on direction
800
- const directions = opts.map(o => {
801
- const current = parseFloat(o.current) || 0;
802
- const suggested = parseFloat(o.suggested) || 0;
803
- return suggested > current ? 'increase' : suggested < current ? 'decrease' : 'same';
804
- });
805
-
806
- const allSame = directions.every(d => d === directions[0]);
807
- if (allSame && directions[0] !== 'same') {
808
- // Unanimous - use average of suggested values
809
- const avgSuggested = opts.reduce((sum, o) => sum + (parseFloat(o.suggested) || 0), 0) / opts.length;
810
- consensusOptimizations.push({
811
- param,
812
- current: opts[0].current,
813
- suggested: avgSuggested.toFixed(2),
814
- reason: `Unanimous (${suggestions.length} agents agree)`,
815
- direction: directions[0]
816
- });
817
- }
818
- }
819
- }
820
-
821
- // Calculate average confidence
822
- const avgConfidence = Math.round(
823
- suggestions.reduce((sum, s) => sum + (s.confidence || 0), 0) / suggestions.length
824
- );
825
-
826
- // Determine consensus market condition
827
- const conditions = suggestions.map(s => s.marketCondition).filter(Boolean);
828
- const conditionCounts = {};
829
- for (const c of conditions) {
830
- conditionCounts[c] = (conditionCounts[c] || 0) + 1;
831
- }
832
- const consensusCondition = Object.entries(conditionCounts)
833
- .sort((a, b) => b[1] - a[1])[0]?.[0] || 'unknown';
834
-
835
- return {
836
- mode: 'CONSENSUS',
837
- agentCount: suggestions.length,
838
- isUnanimous: consensusOptimizations.length > 0,
839
- optimizations: consensusOptimizations,
840
- marketCondition: consensusCondition,
841
- confidence: avgConfidence,
842
- individualSuggestions: suggestions
843
- };
844
- }
845
-
846
- /**
847
- * Get real-time market advice from all agents
848
- * Used for dynamic position sizing and risk adjustment
849
- *
850
- * @param {Object} marketData - Current market data
851
- * @returns {Promise<Object|null>} Market advice (consensus)
852
- */
853
- static async getMarketAdvice(marketData) {
854
- if (supervisionSessions.size === 0) return null;
855
-
856
- const allSessions = Array.from(supervisionSessions.values());
857
- const advices = [];
858
-
859
- // Get advice from each agent
860
- for (const session of allSessions) {
861
- try {
862
- const advice = await getMarketAdvice(session.agent, marketData);
863
- if (advice) {
864
- advices.push({
865
- agentId: session.agentId,
866
- agentName: session.agent.name,
867
- ...advice
868
- });
869
- }
870
- } catch (e) {
871
- // Silent fail
872
- }
873
- }
874
-
875
- if (advices.length === 0) return null;
876
-
877
- // Single agent
878
- if (advices.length === 1) {
879
- return {
880
- mode: 'INDIVIDUAL',
881
- ...advices[0]
882
- };
883
- }
884
-
885
- // CONSENSUS: All agents must agree on action
886
- const actions = advices.map(a => a.action);
887
- const allSameAction = actions.every(a => a === actions[0]);
888
-
889
- if (allSameAction) {
890
- // Unanimous action - average the size multiplier
891
- const avgMultiplier = advices.reduce((sum, a) => sum + (a.sizeMultiplier || 1), 0) / advices.length;
892
- const avgConfidence = Math.round(advices.reduce((sum, a) => sum + (a.confidence || 0), 0) / advices.length);
893
-
894
- return {
895
- mode: 'CONSENSUS',
896
- isUnanimous: true,
897
- action: actions[0],
898
- sizeMultiplier: Math.round(avgMultiplier * 100) / 100,
899
- confidence: avgConfidence,
900
- reason: `${advices.length} agents unanimous`,
901
- agentCount: advices.length
902
- };
903
- } else {
904
- // Agents disagree - be conservative
905
- return {
906
- mode: 'CONSENSUS',
907
- isUnanimous: false,
908
- action: 'CAUTIOUS',
909
- sizeMultiplier: 0.5,
910
- confidence: 0,
911
- reason: 'Agents disagree - reducing exposure',
912
- agentCount: advices.length,
913
- votes: actions.reduce((acc, a) => { acc[a] = (acc[a] || 0) + 1; return acc; }, {})
914
- };
915
- }
916
- }
917
-
918
- /**
919
- * Apply optimization to strategy
920
- * Called when agents have consensus on improvements
921
- *
922
- * @param {Object} strategy - Strategy instance (M1)
923
- * @param {Object} optimization - Optimization to apply
924
- * @returns {boolean} Success
925
- */
926
- static applyOptimization(strategy, optimization) {
927
- if (!strategy || !optimization) return false;
928
-
929
- try {
930
- // Check if strategy has optimization method
931
- if (typeof strategy.applyOptimization === 'function') {
932
- strategy.applyOptimization(optimization);
933
- return true;
934
- }
935
-
936
- // Fallback: try to set individual parameters
937
- if (typeof strategy.setParameter === 'function' && optimization.param) {
938
- strategy.setParameter(optimization.param, optimization.suggested);
939
- return true;
940
- }
941
-
942
- return false;
943
- } catch (e) {
944
- return false;
945
- }
946
- }
478
+ // Optimization methods (delegated to supervisor-optimize.js)
479
+ static async requestOptimization(performanceData) { return requestOptimizationImpl(supervisionSessions, performanceData); }
480
+ static async getMarketAdvice(marketData) { return getMarketAdviceImpl(supervisionSessions, marketData); }
481
+ static applyOptimization(strategy, optimization) { return applyOptimizationImpl(strategy, optimization); }
947
482
  }
948
483
 
949
484
  module.exports = AISupervisor;