hedgequantx 2.5.35 → 2.5.37

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.
@@ -0,0 +1,675 @@
1
+ /**
2
+ * AI Strategy Supervisor
3
+ *
4
+ * Observes, learns from, and optimizes the HQX Ultra Scalping strategy in real-time.
5
+ *
6
+ * FUNCTIONS:
7
+ * 1. OBSERVE - Receive all market data, signals, and trades in real-time
8
+ * 2. LEARN - Analyze winning/losing trades to identify patterns
9
+ * 3. OPTIMIZE - Suggest and apply parameter improvements
10
+ * 4. SUPERVISE - Monitor risk and intervene when necessary
11
+ *
12
+ * In CONSENSUS mode (2+ agents), ALL agents must agree before applying changes.
13
+ */
14
+
15
+ const { analyzePerformance, getMarketAdvice, callAI } = require('./client');
16
+
17
+ // Singleton supervisor state
18
+ let supervisorState = {
19
+ active: false,
20
+ agents: [],
21
+ strategy: null,
22
+ service: null,
23
+ accountId: null,
24
+
25
+ // Real-time data (synced with strategy)
26
+ ticks: [],
27
+ signals: [],
28
+ trades: [],
29
+
30
+ // Learning data
31
+ winningPatterns: [],
32
+ losingPatterns: [],
33
+
34
+ // Performance tracking
35
+ performance: {
36
+ trades: 0,
37
+ wins: 0,
38
+ losses: 0,
39
+ totalPnL: 0,
40
+ maxDrawdown: 0,
41
+ currentDrawdown: 0,
42
+ peakPnL: 0,
43
+ winStreak: 0,
44
+ lossStreak: 0,
45
+ maxWinStreak: 0,
46
+ maxLossStreak: 0
47
+ },
48
+
49
+ // Optimization state
50
+ optimizations: [],
51
+ lastOptimizationTime: 0,
52
+ optimizationInterval: 60000, // Analyze every 60 seconds
53
+
54
+ // Current recommendations
55
+ currentAdvice: {
56
+ action: 'NORMAL',
57
+ sizeMultiplier: 1.0,
58
+ reason: 'Starting'
59
+ }
60
+ };
61
+
62
+ // Analysis interval
63
+ let analysisInterval = null;
64
+
65
+ /**
66
+ * Initialize supervisor with strategy and agents
67
+ */
68
+ const initialize = (strategy, agents, service, accountId) => {
69
+ supervisorState = {
70
+ ...supervisorState,
71
+ active: true,
72
+ agents: agents || [],
73
+ strategy,
74
+ service,
75
+ accountId,
76
+ ticks: [],
77
+ signals: [],
78
+ trades: [],
79
+ winningPatterns: [],
80
+ losingPatterns: [],
81
+ performance: {
82
+ trades: 0,
83
+ wins: 0,
84
+ losses: 0,
85
+ totalPnL: 0,
86
+ maxDrawdown: 0,
87
+ currentDrawdown: 0,
88
+ peakPnL: 0,
89
+ winStreak: 0,
90
+ lossStreak: 0,
91
+ maxWinStreak: 0,
92
+ maxLossStreak: 0
93
+ },
94
+ optimizations: [],
95
+ lastOptimizationTime: Date.now()
96
+ };
97
+
98
+ // Start continuous analysis loop
99
+ if (analysisInterval) clearInterval(analysisInterval);
100
+ analysisInterval = setInterval(analyzeAndOptimize, supervisorState.optimizationInterval);
101
+
102
+ return {
103
+ success: true,
104
+ agents: agents.length,
105
+ mode: agents.length >= 2 ? 'CONSENSUS' : 'INDIVIDUAL'
106
+ };
107
+ };
108
+
109
+ /**
110
+ * Stop supervisor
111
+ */
112
+ const stop = () => {
113
+ if (analysisInterval) {
114
+ clearInterval(analysisInterval);
115
+ analysisInterval = null;
116
+ }
117
+
118
+ const summary = {
119
+ ...supervisorState.performance,
120
+ optimizationsApplied: supervisorState.optimizations.length,
121
+ winningPatterns: supervisorState.winningPatterns.length,
122
+ losingPatterns: supervisorState.losingPatterns.length
123
+ };
124
+
125
+ supervisorState.active = false;
126
+
127
+ return summary;
128
+ };
129
+
130
+ /**
131
+ * Feed tick data (called on every market tick)
132
+ */
133
+ const feedTick = (tick) => {
134
+ if (!supervisorState.active) return;
135
+
136
+ supervisorState.ticks.push({
137
+ ...tick,
138
+ timestamp: Date.now()
139
+ });
140
+
141
+ // Keep last 5000 ticks for pattern analysis
142
+ if (supervisorState.ticks.length > 5000) {
143
+ supervisorState.ticks = supervisorState.ticks.slice(-5000);
144
+ }
145
+ };
146
+
147
+ /**
148
+ * Feed signal data (called when strategy generates a signal)
149
+ */
150
+ const feedSignal = (signal) => {
151
+ if (!supervisorState.active) return;
152
+
153
+ const signalData = {
154
+ ...signal,
155
+ timestamp: Date.now(),
156
+ ticksContext: supervisorState.ticks.slice(-50) // Last 50 ticks before signal
157
+ };
158
+
159
+ supervisorState.signals.push(signalData);
160
+
161
+ // Keep last 100 signals
162
+ if (supervisorState.signals.length > 100) {
163
+ supervisorState.signals = supervisorState.signals.slice(-100);
164
+ }
165
+ };
166
+
167
+ /**
168
+ * Feed trade result (called when a trade completes)
169
+ * This is where LEARNING happens
170
+ */
171
+ const feedTradeResult = (trade) => {
172
+ if (!supervisorState.active) return;
173
+
174
+ const tradeData = {
175
+ ...trade,
176
+ timestamp: Date.now(),
177
+ // Capture context at time of trade
178
+ ticksBefore: supervisorState.ticks.slice(-100),
179
+ signalUsed: supervisorState.signals[supervisorState.signals.length - 1] || null
180
+ };
181
+
182
+ supervisorState.trades.push(tradeData);
183
+
184
+ // Update performance metrics
185
+ const perf = supervisorState.performance;
186
+ perf.trades++;
187
+ perf.totalPnL += trade.pnl || 0;
188
+
189
+ if (trade.pnl > 0) {
190
+ perf.wins++;
191
+ perf.winStreak++;
192
+ perf.lossStreak = 0;
193
+ perf.maxWinStreak = Math.max(perf.maxWinStreak, perf.winStreak);
194
+
195
+ // Learn from winning trade
196
+ learnFromTrade(tradeData, 'win');
197
+ } else if (trade.pnl < 0) {
198
+ perf.losses++;
199
+ perf.lossStreak++;
200
+ perf.winStreak = 0;
201
+ perf.maxLossStreak = Math.max(perf.maxLossStreak, perf.lossStreak);
202
+
203
+ // Learn from losing trade
204
+ learnFromTrade(tradeData, 'loss');
205
+ }
206
+
207
+ // Update drawdown
208
+ if (perf.totalPnL > perf.peakPnL) {
209
+ perf.peakPnL = perf.totalPnL;
210
+ perf.currentDrawdown = 0;
211
+ } else {
212
+ perf.currentDrawdown = perf.peakPnL - perf.totalPnL;
213
+ perf.maxDrawdown = Math.max(perf.maxDrawdown, perf.currentDrawdown);
214
+ }
215
+
216
+ // Trigger immediate analysis after losing streaks
217
+ if (perf.lossStreak >= 3) {
218
+ analyzeAndOptimize();
219
+ }
220
+ };
221
+
222
+ /**
223
+ * Learn from a completed trade
224
+ * Extracts patterns from winning and losing trades
225
+ */
226
+ const learnFromTrade = (trade, result) => {
227
+ const pattern = {
228
+ timestamp: trade.timestamp,
229
+ result,
230
+ pnl: trade.pnl,
231
+ direction: trade.direction || trade.side,
232
+
233
+ // Market context before trade
234
+ priceAction: analyzePriceAction(trade.ticksBefore),
235
+ volumeProfile: analyzeVolume(trade.ticksBefore),
236
+ volatility: calculateVolatility(trade.ticksBefore),
237
+
238
+ // Signal characteristics
239
+ signalConfidence: trade.signalUsed?.confidence || null,
240
+ entryPrice: trade.price || trade.signalUsed?.entry,
241
+ stopLoss: trade.signalUsed?.stopLoss,
242
+ takeProfit: trade.signalUsed?.takeProfit
243
+ };
244
+
245
+ if (result === 'win') {
246
+ supervisorState.winningPatterns.push(pattern);
247
+ // Keep last 50 winning patterns
248
+ if (supervisorState.winningPatterns.length > 50) {
249
+ supervisorState.winningPatterns = supervisorState.winningPatterns.slice(-50);
250
+ }
251
+ } else {
252
+ supervisorState.losingPatterns.push(pattern);
253
+ // Keep last 50 losing patterns
254
+ if (supervisorState.losingPatterns.length > 50) {
255
+ supervisorState.losingPatterns = supervisorState.losingPatterns.slice(-50);
256
+ }
257
+ }
258
+ };
259
+
260
+ /**
261
+ * Analyze price action from ticks
262
+ */
263
+ const analyzePriceAction = (ticks) => {
264
+ if (!ticks || ticks.length < 2) return { trend: 'unknown', strength: 0 };
265
+
266
+ const prices = ticks.map(t => t.price).filter(Boolean);
267
+ if (prices.length < 2) return { trend: 'unknown', strength: 0 };
268
+
269
+ const first = prices[0];
270
+ const last = prices[prices.length - 1];
271
+ const change = last - first;
272
+ const range = Math.max(...prices) - Math.min(...prices);
273
+
274
+ return {
275
+ trend: change > 0 ? 'up' : change < 0 ? 'down' : 'flat',
276
+ strength: range > 0 ? Math.abs(change) / range : 0,
277
+ range,
278
+ change
279
+ };
280
+ };
281
+
282
+ /**
283
+ * Analyze volume from ticks
284
+ */
285
+ const analyzeVolume = (ticks) => {
286
+ if (!ticks || ticks.length === 0) return { total: 0, avg: 0, trend: 'unknown' };
287
+
288
+ const volumes = ticks.map(t => t.volume || 0);
289
+ const total = volumes.reduce((a, b) => a + b, 0);
290
+ const avg = total / volumes.length;
291
+
292
+ // Compare first half vs second half
293
+ const mid = Math.floor(volumes.length / 2);
294
+ const firstHalf = volumes.slice(0, mid).reduce((a, b) => a + b, 0);
295
+ const secondHalf = volumes.slice(mid).reduce((a, b) => a + b, 0);
296
+
297
+ return {
298
+ total,
299
+ avg,
300
+ trend: secondHalf > firstHalf * 1.2 ? 'increasing' : secondHalf < firstHalf * 0.8 ? 'decreasing' : 'stable'
301
+ };
302
+ };
303
+
304
+ /**
305
+ * Calculate volatility from ticks
306
+ */
307
+ const calculateVolatility = (ticks) => {
308
+ if (!ticks || ticks.length < 2) return 0;
309
+
310
+ const prices = ticks.map(t => t.price).filter(Boolean);
311
+ if (prices.length < 2) return 0;
312
+
313
+ const returns = [];
314
+ for (let i = 1; i < prices.length; i++) {
315
+ returns.push((prices[i] - prices[i-1]) / prices[i-1]);
316
+ }
317
+
318
+ const mean = returns.reduce((a, b) => a + b, 0) / returns.length;
319
+ const variance = returns.reduce((sum, r) => sum + Math.pow(r - mean, 2), 0) / returns.length;
320
+
321
+ return Math.sqrt(variance);
322
+ };
323
+
324
+ /**
325
+ * Main analysis and optimization loop
326
+ * Called periodically and after significant events
327
+ */
328
+ const analyzeAndOptimize = async () => {
329
+ if (!supervisorState.active || supervisorState.agents.length === 0) return;
330
+
331
+ const perf = supervisorState.performance;
332
+
333
+ // Skip if not enough data
334
+ if (perf.trades < 3) return;
335
+
336
+ // Prepare performance data for AI analysis
337
+ const performanceData = {
338
+ trades: perf.trades,
339
+ wins: perf.wins,
340
+ losses: perf.losses,
341
+ winRate: perf.trades > 0 ? perf.wins / perf.trades : 0,
342
+ pnl: perf.totalPnL,
343
+ maxDrawdown: perf.maxDrawdown,
344
+ currentDrawdown: perf.currentDrawdown,
345
+ winStreak: perf.winStreak,
346
+ lossStreak: perf.lossStreak,
347
+ maxWinStreak: perf.maxWinStreak,
348
+ maxLossStreak: perf.maxLossStreak,
349
+
350
+ // Calculate averages
351
+ avgWin: perf.wins > 0 ?
352
+ supervisorState.trades.filter(t => t.pnl > 0).reduce((s, t) => s + t.pnl, 0) / perf.wins : 0,
353
+ avgLoss: perf.losses > 0 ?
354
+ Math.abs(supervisorState.trades.filter(t => t.pnl < 0).reduce((s, t) => s + t.pnl, 0) / perf.losses) : 0,
355
+
356
+ // Recent trades for context
357
+ recentTrades: supervisorState.trades.slice(-10).map(t => ({
358
+ side: t.direction || t.side,
359
+ qty: t.qty,
360
+ price: t.price,
361
+ pnl: t.pnl
362
+ })),
363
+
364
+ // Pattern summaries
365
+ winningPatternCount: supervisorState.winningPatterns.length,
366
+ losingPatternCount: supervisorState.losingPatterns.length,
367
+
368
+ // Common characteristics of losing trades
369
+ losingTradeAnalysis: analyzeLosingPatterns()
370
+ };
371
+
372
+ // Get optimization suggestions from all agents
373
+ const suggestions = [];
374
+
375
+ for (const agent of supervisorState.agents) {
376
+ try {
377
+ const suggestion = await getOptimizationFromAgent(agent, performanceData);
378
+ if (suggestion) {
379
+ suggestions.push({
380
+ agentId: agent.id,
381
+ agentName: agent.name,
382
+ ...suggestion
383
+ });
384
+ }
385
+ } catch (e) {
386
+ // Silent fail for individual agent
387
+ }
388
+ }
389
+
390
+ if (suggestions.length === 0) return;
391
+
392
+ // Process suggestions based on mode
393
+ const isConsensus = supervisorState.agents.length >= 2;
394
+
395
+ if (isConsensus) {
396
+ // CONSENSUS MODE: All agents must agree
397
+ const consensusResult = buildConsensus(suggestions);
398
+
399
+ if (consensusResult.isUnanimous && consensusResult.optimizations.length > 0) {
400
+ // Apply unanimous optimizations
401
+ for (const opt of consensusResult.optimizations) {
402
+ applyOptimization(opt);
403
+ }
404
+ }
405
+
406
+ // Update current advice based on consensus
407
+ if (consensusResult.action) {
408
+ supervisorState.currentAdvice = {
409
+ action: consensusResult.action,
410
+ sizeMultiplier: consensusResult.sizeMultiplier || 1.0,
411
+ reason: consensusResult.reason || 'Consensus recommendation'
412
+ };
413
+ }
414
+ } else {
415
+ // INDIVIDUAL MODE: Apply single agent's suggestions
416
+ const suggestion = suggestions[0];
417
+
418
+ if (suggestion.optimizations) {
419
+ for (const opt of suggestion.optimizations) {
420
+ applyOptimization(opt);
421
+ }
422
+ }
423
+
424
+ if (suggestion.action) {
425
+ supervisorState.currentAdvice = {
426
+ action: suggestion.action,
427
+ sizeMultiplier: suggestion.sizeMultiplier || 1.0,
428
+ reason: suggestion.reason || 'Agent recommendation'
429
+ };
430
+ }
431
+ }
432
+
433
+ supervisorState.lastOptimizationTime = Date.now();
434
+ };
435
+
436
+ /**
437
+ * Analyze patterns in losing trades
438
+ */
439
+ const analyzeLosingPatterns = () => {
440
+ const patterns = supervisorState.losingPatterns;
441
+ if (patterns.length === 0) return null;
442
+
443
+ // Find common characteristics
444
+ const trends = patterns.map(p => p.priceAction?.trend).filter(Boolean);
445
+ const volatilities = patterns.map(p => p.volatility).filter(Boolean);
446
+ const confidences = patterns.map(p => p.signalConfidence).filter(Boolean);
447
+
448
+ const trendCounts = {};
449
+ for (const t of trends) {
450
+ trendCounts[t] = (trendCounts[t] || 0) + 1;
451
+ }
452
+
453
+ const avgVolatility = volatilities.length > 0 ?
454
+ volatilities.reduce((a, b) => a + b, 0) / volatilities.length : 0;
455
+
456
+ const avgConfidence = confidences.length > 0 ?
457
+ confidences.reduce((a, b) => a + b, 0) / confidences.length : 0;
458
+
459
+ return {
460
+ commonTrend: Object.entries(trendCounts).sort((a, b) => b[1] - a[1])[0]?.[0] || 'unknown',
461
+ avgVolatility,
462
+ avgConfidence,
463
+ count: patterns.length
464
+ };
465
+ };
466
+
467
+ /**
468
+ * Get optimization suggestion from a single agent
469
+ */
470
+ const getOptimizationFromAgent = async (agent, performanceData) => {
471
+ const systemPrompt = `You are an AI supervisor for HQX Ultra Scalping, a professional futures trading strategy.
472
+
473
+ The strategy uses:
474
+ - Order flow analysis (delta, absorption, imbalance)
475
+ - Statistical models (z-score, standard deviation)
476
+ - Dynamic risk management (Kelly criterion)
477
+
478
+ ANALYZE the performance data and LEARN from the losing trades.
479
+ Suggest SPECIFIC optimizations to improve win rate and reduce losses.
480
+
481
+ Respond in JSON:
482
+ {
483
+ "assessment": "brief assessment",
484
+ "action": "AGGRESSIVE|NORMAL|CAUTIOUS|PAUSE",
485
+ "sizeMultiplier": 0.5-1.5,
486
+ "optimizations": [
487
+ {"param": "name", "direction": "increase|decrease", "amount": "10%", "reason": "why"}
488
+ ],
489
+ "learnings": "what we learned from losing trades",
490
+ "confidence": 0-100
491
+ }`;
492
+
493
+ const prompt = `STRATEGY PERFORMANCE ANALYSIS
494
+
495
+ Stats:
496
+ - Trades: ${performanceData.trades} (${performanceData.wins}W / ${performanceData.losses}L)
497
+ - Win Rate: ${(performanceData.winRate * 100).toFixed(1)}%
498
+ - P&L: $${performanceData.pnl.toFixed(2)}
499
+ - Max Drawdown: $${performanceData.maxDrawdown.toFixed(2)}
500
+ - Current Streak: ${performanceData.winStreak > 0 ? performanceData.winStreak + ' wins' : performanceData.lossStreak + ' losses'}
501
+
502
+ Losing Trade Analysis:
503
+ ${performanceData.losingTradeAnalysis ? `
504
+ - Common trend at entry: ${performanceData.losingTradeAnalysis.commonTrend}
505
+ - Avg volatility: ${(performanceData.losingTradeAnalysis.avgVolatility * 100).toFixed(3)}%
506
+ - Avg signal confidence: ${(performanceData.losingTradeAnalysis.avgConfidence * 100).toFixed(1)}%
507
+ - Total losing patterns: ${performanceData.losingTradeAnalysis.count}
508
+ ` : 'Not enough data'}
509
+
510
+ Recent Trades:
511
+ ${performanceData.recentTrades.map(t => `${t.side} @ ${t.price} → $${t.pnl?.toFixed(2)}`).join('\n')}
512
+
513
+ What should we LEARN and OPTIMIZE?`;
514
+
515
+ try {
516
+ const response = await callAI(agent, prompt, systemPrompt);
517
+ if (!response) return null;
518
+
519
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
520
+ if (jsonMatch) {
521
+ return JSON.parse(jsonMatch[0]);
522
+ }
523
+ return null;
524
+ } catch (e) {
525
+ return null;
526
+ }
527
+ };
528
+
529
+ /**
530
+ * Build consensus from multiple agent suggestions
531
+ */
532
+ const buildConsensus = (suggestions) => {
533
+ if (suggestions.length === 0) return { isUnanimous: false };
534
+
535
+ // Check action consensus
536
+ const actions = suggestions.map(s => s.action).filter(Boolean);
537
+ const allSameAction = actions.length === suggestions.length &&
538
+ actions.every(a => a === actions[0]);
539
+
540
+ // Check optimization consensus
541
+ const allOptimizations = suggestions.flatMap(s => s.optimizations || []);
542
+ const paramGroups = {};
543
+
544
+ for (const opt of allOptimizations) {
545
+ if (!opt.param) continue;
546
+ const key = `${opt.param}_${opt.direction}`;
547
+ if (!paramGroups[key]) {
548
+ paramGroups[key] = { ...opt, count: 0 };
549
+ }
550
+ paramGroups[key].count++;
551
+ }
552
+
553
+ // Find unanimous optimizations (all agents agree)
554
+ const unanimousOptimizations = Object.values(paramGroups)
555
+ .filter(g => g.count === suggestions.length)
556
+ .map(g => ({
557
+ param: g.param,
558
+ direction: g.direction,
559
+ amount: g.amount,
560
+ reason: `Unanimous (${suggestions.length} agents)`
561
+ }));
562
+
563
+ // Average size multiplier
564
+ const multipliers = suggestions.map(s => s.sizeMultiplier || 1.0);
565
+ const avgMultiplier = multipliers.reduce((a, b) => a + b, 0) / multipliers.length;
566
+
567
+ // Average confidence
568
+ const confidences = suggestions.map(s => s.confidence || 50);
569
+ const avgConfidence = Math.round(confidences.reduce((a, b) => a + b, 0) / confidences.length);
570
+
571
+ return {
572
+ isUnanimous: allSameAction && unanimousOptimizations.length > 0,
573
+ action: allSameAction ? actions[0] : 'CAUTIOUS',
574
+ sizeMultiplier: allSameAction ? avgMultiplier : 0.5,
575
+ optimizations: unanimousOptimizations,
576
+ confidence: avgConfidence,
577
+ reason: allSameAction ?
578
+ `${suggestions.length} agents agree` :
579
+ 'Agents disagree - being cautious',
580
+ votes: actions.reduce((acc, a) => { acc[a] = (acc[a] || 0) + 1; return acc; }, {})
581
+ };
582
+ };
583
+
584
+ /**
585
+ * Apply an optimization to the strategy
586
+ */
587
+ const applyOptimization = (optimization) => {
588
+ const strategy = supervisorState.strategy;
589
+ if (!strategy) return false;
590
+
591
+ // Record the optimization
592
+ supervisorState.optimizations.push({
593
+ timestamp: Date.now(),
594
+ ...optimization
595
+ });
596
+
597
+ // Try to apply to strategy if it supports it
598
+ try {
599
+ if (typeof strategy.applyAIOptimization === 'function') {
600
+ strategy.applyAIOptimization(optimization);
601
+ return true;
602
+ }
603
+
604
+ if (typeof strategy.setParameter === 'function') {
605
+ strategy.setParameter(optimization.param, optimization.direction, optimization.amount);
606
+ return true;
607
+ }
608
+ } catch (e) {
609
+ // Strategy doesn't support this optimization
610
+ }
611
+
612
+ return false;
613
+ };
614
+
615
+ /**
616
+ * Get current advice for the strategy
617
+ * Called before each trade decision
618
+ */
619
+ const getCurrentAdvice = () => {
620
+ if (!supervisorState.active) {
621
+ return { action: 'NORMAL', sizeMultiplier: 1.0, reason: 'No supervision' };
622
+ }
623
+
624
+ return supervisorState.currentAdvice;
625
+ };
626
+
627
+ /**
628
+ * Get supervision status
629
+ */
630
+ const getStatus = () => {
631
+ return {
632
+ active: supervisorState.active,
633
+ agents: supervisorState.agents.length,
634
+ mode: supervisorState.agents.length >= 2 ? 'CONSENSUS' : 'INDIVIDUAL',
635
+ performance: supervisorState.performance,
636
+ currentAdvice: supervisorState.currentAdvice,
637
+ optimizationsApplied: supervisorState.optimizations.length,
638
+ patternsLearned: {
639
+ winning: supervisorState.winningPatterns.length,
640
+ losing: supervisorState.losingPatterns.length
641
+ },
642
+ lastOptimization: supervisorState.lastOptimizationTime
643
+ };
644
+ };
645
+
646
+ /**
647
+ * Check if should proceed with trade based on AI advice
648
+ */
649
+ const shouldTrade = () => {
650
+ if (!supervisorState.active) return { proceed: true, multiplier: 1.0 };
651
+
652
+ const advice = supervisorState.currentAdvice;
653
+
654
+ if (advice.action === 'PAUSE') {
655
+ return { proceed: false, reason: advice.reason };
656
+ }
657
+
658
+ return {
659
+ proceed: true,
660
+ multiplier: advice.sizeMultiplier || 1.0,
661
+ action: advice.action
662
+ };
663
+ };
664
+
665
+ module.exports = {
666
+ initialize,
667
+ stop,
668
+ feedTick,
669
+ feedSignal,
670
+ feedTradeResult,
671
+ getCurrentAdvice,
672
+ shouldTrade,
673
+ getStatus,
674
+ analyzeAndOptimize
675
+ };