Smart Market Reversal Detector

AI-Powered Reversal Detector: Multi-timeframe analysis with machine learning patterns for high-probability trades

Size

51.9 KB

Version

2.1.3

Created

Nov 8, 2025

Updated

about 1 month ago

1// ==UserScript==
2// @name		Smart Market Reversal Detector
3// @description		AI-Powered Reversal Detector: Multi-timeframe analysis with machine learning patterns for high-probability trades
4// @version		2.1.3
5// @match		https://*.tradingview.com/*
6// @icon		https://static.tradingview.com/static/images/favicon.ico
7// @grant		GM.getValue
8// @grant		GM.setValue
9// ==/UserScript==
10(function() {
11    'use strict';
12
13    console.log('Smart Market Reversal Detector initialized');
14
15    // Configuration
16    const CONFIG = {
17        updateInterval: 2000,
18        lookbackPeriod: 20, // Reduced from 50 to 20 for faster real-time data
19        premiumThreshold: 0.7,
20        discountThreshold: 0.3,
21        volumeMultiplier: 1.5,
22        rsiOverbought: 70,
23        rsiOversold: 30,
24        orderBlockStrength: 3, // Minimum candles for valid order block
25        fvgMinSize: 0.001, // Minimum Fair Value Gap size (0.1%)
26        liquidityZoneSize: 0.02 // 2% zone for liquidity sweeps
27    };
28
29    // State management
30    let lastAnalysis = null;
31    let uiPanel = null;
32    let chartOverlay = null;
33    let lastSignal = null;
34    let priceHistory = [];
35    let volumeHistory = [];
36    let lastStrongSignal = null;
37    let ohlcHistory = []; // Store OHLC candles for advanced analysis
38    let lastSignalTime = 0; // Track when last signal was detected
39    let signalCooldown = 600000; // 10 minutes cooldown between signal switches
40    let signalHistory = []; // Track signal accuracy
41    let trendDirection = 'neutral'; // Current trend direction
42    let trendStrength = 0; // Trend strength (0-1)
43
44    // ==================== UTILITY FUNCTIONS ====================
45    
46    function calculateRSI(prices, period = 14) {
47        if (prices.length < period + 1) return null;
48        let gains = 0, losses = 0;
49        for (let i = prices.length - period; i < prices.length; i++) {
50            const change = prices[i] - prices[i - 1];
51            if (change > 0) gains += change;
52            else losses += Math.abs(change);
53        }
54        const avgGain = gains / period;
55        const avgLoss = losses / period;
56        if (avgLoss === 0) return 100;
57        const rs = avgGain / avgLoss;
58        return 100 - (100 / (1 + rs));
59    }
60
61    function calculateStochasticRSI(prices, period = 14, smoothK = 3, smoothD = 3) {
62        if (prices.length < period + smoothK + smoothD) return null;
63        
64        const rsiValues = [];
65        for (let i = period; i < prices.length; i++) {
66            const rsi = calculateRSI(prices.slice(0, i + 1), period);
67            if (rsi !== null) rsiValues.push(rsi);
68        }
69        
70        if (rsiValues.length < 14) return null;
71        
72        const recentRSI = rsiValues.slice(-14);
73        const minRSI = Math.min(...recentRSI);
74        const maxRSI = Math.max(...recentRSI);
75        const range = maxRSI - minRSI;
76        
77        if (range === 0) return { k: 50, d: 50 };
78        
79        const k = ((recentRSI[recentRSI.length - 1] - minRSI) / range) * 100;
80        const d = k; // Simplified
81        
82        return { k, d };
83    }
84
85    function calculateMACD(prices, fastPeriod = 12, slowPeriod = 26, signalPeriod = 9) {
86        if (prices.length < slowPeriod + signalPeriod) return null;
87        
88        const emaFast = calculateEMA(prices, fastPeriod);
89        const emaSlow = calculateEMA(prices, slowPeriod);
90        
91        if (!emaFast || !emaSlow) return null;
92        
93        const macdLine = emaFast - emaSlow;
94        return { macd: macdLine, signal: 0, histogram: macdLine };
95    }
96
97    function calculateEMA(prices, period) {
98        if (prices.length < period) return null;
99        const multiplier = 2 / (period + 1);
100        let ema = prices.slice(0, period).reduce((a, b) => a + b, 0) / period;
101        for (let i = period; i < prices.length; i++) {
102            ema = (prices[i] - ema) * multiplier + ema;
103        }
104        return ema;
105    }
106
107    function calculateMA(prices, period) {
108        if (prices.length < period) return null;
109        const slice = prices.slice(-period);
110        return slice.reduce((a, b) => a + b, 0) / period;
111    }
112
113    // ==================== SMART MONEY CONCEPTS ====================
114
115    // Detect Price Action Patterns (Reversal Candlestick Patterns)
116    function detectPriceActionPatterns(ohlcData) {
117        if (ohlcData.length < 3) return { bullish: [], bearish: [] };
118        
119        const bullishPatterns = [];
120        const bearishPatterns = [];
121        const recent = ohlcData.slice(-3);
122        const current = recent[recent.length - 1];
123        const prev = recent[recent.length - 2];
124        
125        // Bullish Engulfing: Strong reversal pattern
126        const isBullishEngulfing = prev.close < prev.open && // Previous candle bearish
127                                    current.close > current.open && // Current candle bullish
128                                    current.open < prev.close && // Opens below previous close
129                                    current.close > prev.open; // Closes above previous open
130        
131        if (isBullishEngulfing) {
132            bullishPatterns.push({ type: 'engulfing', strength: 5 });
133        }
134        
135        // Bearish Engulfing: Strong reversal pattern
136        const isBearishEngulfing = prev.close > prev.open && // Previous candle bullish
137                                    current.close < current.open && // Current candle bearish
138                                    current.open > prev.close && // Opens above previous close
139                                    current.close < prev.open; // Closes below previous open
140        
141        if (isBearishEngulfing) {
142            bearishPatterns.push({ type: 'engulfing', strength: 5 });
143        }
144        
145        // Hammer (Bullish): Long lower wick, small body at top
146        const bodySize = Math.abs(current.close - current.open);
147        const lowerWick = Math.min(current.open, current.close) - current.low;
148        const upperWick = current.high - Math.max(current.open, current.close);
149        const totalRange = current.high - current.low;
150        
151        const isHammer = lowerWick > bodySize * 2 && upperWick < bodySize && totalRange > 0;
152        if (isHammer && current.close > current.open) {
153            bullishPatterns.push({ type: 'hammer', strength: 4 });
154        }
155        
156        // Shooting Star (Bearish): Long upper wick, small body at bottom
157        const isShootingStar = upperWick > bodySize * 2 && lowerWick < bodySize && totalRange > 0;
158        if (isShootingStar && current.close < current.open) {
159            bearishPatterns.push({ type: 'shooting_star', strength: 4 });
160        }
161        
162        // Pin Bar Reversal
163        const isPinBarBullish = lowerWick > totalRange * 0.6 && bodySize < totalRange * 0.3;
164        const isPinBarBearish = upperWick > totalRange * 0.6 && bodySize < totalRange * 0.3;
165        
166        if (isPinBarBullish) {
167            bullishPatterns.push({ type: 'pin_bar', strength: 3 });
168        }
169        if (isPinBarBearish) {
170            bearishPatterns.push({ type: 'pin_bar', strength: 3 });
171        }
172        
173        return { bullish: bullishPatterns, bearish: bearishPatterns };
174    }
175    
176    // Detect Long-Term Trend Direction
177    function detectTrend(prices, ohlcData) {
178        if (prices.length < 20) return { direction: 'neutral', strength: 0 };
179        
180        const ma20 = calculateMA(prices, 20);
181        const ma50 = calculateMA(prices, Math.min(50, prices.length));
182        const currentPrice = prices[prices.length - 1];
183        
184        // Count higher highs and higher lows (uptrend)
185        let higherHighs = 0;
186        let higherLows = 0;
187        let lowerHighs = 0;
188        let lowerLows = 0;
189        
190        for (let i = 5; i < ohlcData.length; i++) {
191            if (ohlcData[i].high > ohlcData[i-5].high) higherHighs++;
192            else lowerHighs++;
193            
194            if (ohlcData[i].low > ohlcData[i-5].low) higherLows++;
195            else lowerLows++;
196        }
197        
198        const uptrendScore = (higherHighs + higherLows) / (ohlcData.length - 5);
199        const downtrendScore = (lowerHighs + lowerLows) / (ohlcData.length - 5);
200        
201        let direction = 'neutral';
202        let strength = 0;
203        
204        // Strong uptrend
205        if (ma20 && ma50 && ma20 > ma50 && currentPrice > ma20 && uptrendScore > 0.6) {
206            direction = 'uptrend';
207            strength = uptrendScore;
208        }
209        // Strong downtrend
210        else if (ma20 && ma50 && ma20 < ma50 && currentPrice < ma20 && downtrendScore > 0.6) {
211            direction = 'downtrend';
212            strength = downtrendScore;
213        }
214        // Weak trend or ranging
215        else {
216            direction = 'neutral';
217            strength = 0.5;
218        }
219        
220        return { direction, strength };
221    }
222
223    // Detect Order Blocks (last candle before strong move)
224    function detectOrderBlocks(ohlcData) {
225        if (ohlcData.length < 10) return { bullish: [], bearish: [] };
226        
227        const bullishOB = [];
228        const bearishOB = [];
229        
230        for (let i = 3; i < ohlcData.length - 3; i++) {
231            const candle = ohlcData[i];
232            const nextCandles = ohlcData.slice(i + 1, i + 4);
233            
234            // Bullish Order Block: Last down candle before strong up move
235            // This is where INSTITUTIONS BOUGHT (accumulated)
236            const isBearishCandle = candle.close < candle.open;
237            const strongUpMove = nextCandles.every(c => c.close > c.open) && 
238                                 nextCandles[2].close > candle.high * 1.005;
239            
240            if (isBearishCandle && strongUpMove) {
241                const strength = (nextCandles[2].close - candle.low) / candle.low;
242                bullishOB.push({
243                    index: i,
244                    high: candle.high,
245                    low: candle.low,
246                    strength: strength,
247                    isStrong: strength > 0.01 // 1% move = strong institutional activity
248                });
249            }
250            
251            // Bearish Order Block: Last up candle before strong down move
252            // This is where INSTITUTIONS SOLD (distributed)
253            const isBullishCandle = candle.close > candle.open;
254            const strongDownMove = nextCandles.every(c => c.close < c.open) && 
255                                   nextCandles[2].close < candle.low * 0.995;
256            
257            if (isBullishCandle && strongDownMove) {
258                const strength = (candle.high - nextCandles[2].close) / candle.high;
259                bearishOB.push({
260                    index: i,
261                    high: candle.high,
262                    low: candle.low,
263                    strength: strength,
264                    isStrong: strength > 0.01
265                });
266            }
267        }
268        
269        return { bullish: bullishOB, bearish: bearishOB };
270    }
271
272    // Detect Fair Value Gaps (imbalances in price)
273    function detectFairValueGaps(ohlcData) {
274        if (ohlcData.length < 3) return { bullish: [], bearish: [] };
275        
276        const bullishFVG = [];
277        const bearishFVG = [];
278        
279        for (let i = 1; i < ohlcData.length - 1; i++) {
280            const prev = ohlcData[i - 1];
281            const curr = ohlcData[i];
282            const next = ohlcData[i + 1];
283            
284            // Bullish FVG: Gap between prev high and next low
285            if (next.low > prev.high) {
286                const gapSize = (next.low - prev.high) / prev.high;
287                if (gapSize >= CONFIG.fvgMinSize) {
288                    bullishFVG.push({
289                        index: i,
290                        top: next.low,
291                        bottom: prev.high,
292                        size: gapSize
293                    });
294                }
295            }
296            
297            // Bearish FVG: Gap between prev low and next high
298            if (next.high < prev.low) {
299                const gapSize = (prev.low - next.high) / prev.low;
300                if (gapSize >= CONFIG.fvgMinSize) {
301                    bearishFVG.push({
302                        index: i,
303                        top: prev.low,
304                        bottom: next.high,
305                        size: gapSize
306                    });
307                }
308            }
309        }
310        
311        return { bullish: bullishFVG, bearish: bearishFVG };
312    }
313
314    // Detect Liquidity Zones (areas where stops are likely clustered)
315    function detectLiquidityZones(ohlcData) {
316        if (ohlcData.length < 20) return { highs: [], lows: [] };
317        
318        const liquidityHighs = [];
319        const liquidityLows = [];
320        
321        // Find swing highs and lows (potential liquidity zones)
322        for (let i = 5; i < ohlcData.length - 5; i++) {
323            const candle = ohlcData[i];
324            const leftCandles = ohlcData.slice(i - 5, i);
325            const rightCandles = ohlcData.slice(i + 1, i + 6);
326            
327            // Swing high (liquidity above)
328            const isSwingHigh = leftCandles.every(c => c.high < candle.high) &&
329                                rightCandles.every(c => c.high < candle.high);
330            
331            if (isSwingHigh) {
332                liquidityHighs.push({
333                    index: i,
334                    price: candle.high,
335                    zone: [candle.high * 0.998, candle.high * 1.002]
336                });
337            }
338            
339            // Swing low (liquidity below)
340            const isSwingLow = leftCandles.every(c => c.low > candle.low) &&
341                               rightCandles.every(c => c.low > candle.low);
342            
343            if (isSwingLow) {
344                liquidityLows.push({
345                    index: i,
346                    price: candle.low,
347                    zone: [candle.low * 0.998, candle.low * 1.002]
348                });
349            }
350        }
351        
352        return { highs: liquidityHighs, lows: liquidityLows };
353    }
354
355    // Detect Break of Structure (BOS)
356    function detectBreakOfStructure(ohlcData) {
357        if (ohlcData.length < 15) return null;
358        
359        const recent = ohlcData.slice(-15);
360        const currentPrice = recent[recent.length - 1].close;
361        
362        // Find recent swing high
363        let lastSwingHigh = 0;
364        for (let i = 2; i < recent.length - 2; i++) {
365            if (recent[i].high > recent[i-1].high && recent[i].high > recent[i-2].high &&
366                recent[i].high > recent[i+1].high && recent[i].high > recent[i+2].high) {
367                lastSwingHigh = Math.max(lastSwingHigh, recent[i].high);
368            }
369        }
370        
371        // Find recent swing low
372        let lastSwingLow = Infinity;
373        for (let i = 2; i < recent.length - 2; i++) {
374            if (recent[i].low < recent[i-1].low && recent[i].low < recent[i-2].low &&
375                recent[i].low < recent[i+1].low && recent[i].low < recent[i+2].low) {
376                lastSwingLow = Math.min(lastSwingLow, recent[i].low);
377            }
378        }
379        
380        // Check for BOS
381        if (lastSwingHigh > 0 && currentPrice > lastSwingHigh) {
382            return { type: 'bullish_bos', level: lastSwingHigh, strength: (currentPrice - lastSwingHigh) / lastSwingHigh };
383        }
384        
385        if (lastSwingLow < Infinity && currentPrice < lastSwingLow) {
386            return { type: 'bearish_bos', level: lastSwingLow, strength: (lastSwingLow - currentPrice) / lastSwingLow };
387        }
388        
389        return null;
390    }
391
392    // ==================== ADVANCED REVERSAL DETECTION ====================
393
394    function detectReversal(prices, volumes, ohlcData) {
395        if (prices.length < CONFIG.lookbackPeriod) {
396            return { signal: 'insufficient_data', confidence: 0, signals: [], bullishScore: 0, bearishScore: 0 };
397        }
398        
399        const currentPrice = prices[prices.length - 1];
400        
401        // Calculate all indicators
402        const rsi = calculateRSI(prices);
403        const stochRSI = calculateStochasticRSI(prices);
404        const macd = calculateMACD(prices);
405        const ma20 = calculateMA(prices, 20);
406        const ma50 = calculateMA(prices, 50);
407        const ma200 = calculateMA(prices, Math.min(200, prices.length));
408        
409        // Smart Money Concepts
410        const orderBlocks = detectOrderBlocks(ohlcData);
411        const fvg = detectFairValueGaps(ohlcData);
412        const liquidityZones = detectLiquidityZones(ohlcData);
413        const bos = detectBreakOfStructure(ohlcData);
414        
415        // NEW: Price Action Patterns & Trend Detection
416        const priceActionPatterns = detectPriceActionPatterns(ohlcData);
417        const trend = detectTrend(prices, ohlcData);
418        
419        // Update global trend state
420        trendDirection = trend.direction;
421        trendStrength = trend.strength;
422        
423        // Premium/Discount analysis
424        const recentPrices = prices.slice(-CONFIG.lookbackPeriod);
425        const high = Math.max(...recentPrices);
426        const low = Math.min(...recentPrices);
427        const range = high - low;
428        const pricePosition = (currentPrice - low) / range;
429        
430        let zone = 'equilibrium';
431        if (pricePosition >= CONFIG.premiumThreshold) zone = 'premium';
432        else if (pricePosition <= CONFIG.discountThreshold) zone = 'discount';
433        
434        const premiumDiscount = { zone, position: pricePosition, high, low, range, currentPrice };
435        
436        // Volume analysis
437        let volumeSpike = false;
438        let volumeStrength = 0;
439        if (volumes && volumes.length >= 20) {
440            const avgVolume = calculateMA(volumes, 20);
441            const currentVolume = volumes[volumes.length - 1];
442            volumeStrength = currentVolume / avgVolume;
443            volumeSpike = volumeStrength > CONFIG.volumeMultiplier;
444        }
445        
446        // ==================== SCORING SYSTEM ====================
447        let bullishScore = 0;
448        let bearishScore = 0;
449        const signals = [];
450        
451        // 1. RSI Signals (Weight: 2)
452        if (rsi !== null) {
453            if (rsi < CONFIG.rsiOversold) {
454                bullishScore += 2;
455                signals.push(`RSI Oversold (${rsi.toFixed(1)})`);
456            }
457            if (rsi > CONFIG.rsiOverbought) {
458                bearishScore += 2;
459                signals.push(`RSI Overbought (${rsi.toFixed(1)})`);
460            }
461        }
462        
463        // 2. Stochastic RSI (Weight: 2)
464        if (stochRSI) {
465            if (stochRSI.k < 20) {
466                bullishScore += 2;
467                signals.push('Stoch RSI Oversold');
468            }
469            if (stochRSI.k > 80) {
470                bearishScore += 2;
471                signals.push('Stoch RSI Overbought');
472            }
473        }
474        
475        // 3. MACD Momentum (Weight: 2)
476        if (macd) {
477            if (macd.histogram > 0 && macd.macd < 0) {
478                bullishScore += 2;
479                signals.push('MACD Bullish Crossover');
480            }
481            if (macd.histogram < 0 && macd.macd > 0) {
482                bearishScore += 2;
483                signals.push('MACD Bearish Crossover');
484            }
485        }
486        
487        // 4. Premium/Discount Zones (Weight: 3) - CRITICAL
488        if (zone === 'discount') {
489            bullishScore += 3;
490            signals.push('Price in Discount Zone (Buy Zone)');
491        } else if (zone === 'premium') {
492            bearishScore += 3;
493            signals.push('Price in Premium Zone (Sell Zone)');
494        }
495        
496        // 5. Order Blocks (Weight: 4) - VERY IMPORTANT
497        if (orderBlocks.bullish.length > 0) {
498            const latestOB = orderBlocks.bullish[orderBlocks.bullish.length - 1];
499            if (currentPrice >= latestOB.low && currentPrice <= latestOB.high * 1.01) {
500                bullishScore += 4;
501                signals.push('Bullish Order Block Detected');
502            }
503        }
504        if (orderBlocks.bearish.length > 0) {
505            const latestOB = orderBlocks.bearish[orderBlocks.bearish.length - 1];
506            if (currentPrice <= latestOB.high && currentPrice >= latestOB.low * 0.99) {
507                bearishScore += 4;
508                signals.push('Bearish Order Block Detected');
509            }
510        }
511        
512        // 6. Fair Value Gaps (Weight: 3)
513        if (fvg.bullish.length > 0) {
514            const latestFVG = fvg.bullish[fvg.bullish.length - 1];
515            if (currentPrice >= latestFVG.bottom && currentPrice <= latestFVG.top) {
516                bullishScore += 3;
517                signals.push('Bullish Fair Value Gap');
518            }
519        }
520        if (fvg.bearish.length > 0) {
521            const latestFVG = fvg.bearish[fvg.bearish.length - 1];
522            if (currentPrice >= latestFVG.bottom && currentPrice <= latestFVG.top) {
523                bearishScore += 3;
524                signals.push('Bearish Fair Value Gap');
525            }
526        }
527        
528        // 7. Break of Structure (Weight: 3)
529        if (bos) {
530            if (bos.type === 'bullish_bos' && bos.strength > 0.005) {
531                bullishScore += 3;
532                signals.push('Bullish Break of Structure');
533            } else if (bos.type === 'bearish_bos' && bos.strength > 0.005) {
534                bearishScore += 3;
535                signals.push('Bearish Break of Structure');
536            }
537        }
538        
539        // 8. Liquidity Sweep Detection (Weight: 3)
540        if (liquidityZones.lows.length > 0) {
541            const latestLow = liquidityZones.lows[liquidityZones.lows.length - 1];
542            const swept = ohlcData.slice(-3).some(c => c.low <= latestLow.price && c.close > latestLow.price);
543            if (swept) {
544                bullishScore += 3;
545                signals.push('Liquidity Sweep Below (Reversal Up)');
546            }
547        }
548        if (liquidityZones.highs.length > 0) {
549            const latestHigh = liquidityZones.highs[liquidityZones.highs.length - 1];
550            const swept = ohlcData.slice(-3).some(c => c.high >= latestHigh.price && c.close < latestHigh.price);
551            if (swept) {
552                bearishScore += 3;
553                signals.push('Liquidity Sweep Above (Reversal Down)');
554            }
555        }
556        
557        // 9. Moving Average Confluence (Weight: 2)
558        if (ma20 && ma50) {
559            if (currentPrice < ma20 && ma20 < ma50 && currentPrice < ma50 * 0.98) {
560                bullishScore += 2;
561                signals.push('Price Below Key MAs (Oversold)');
562            } else if (currentPrice > ma20 && ma20 > ma50 && currentPrice > ma50 * 1.02) {
563                bearishScore += 2;
564                signals.push('Price Above Key MAs (Overbought)');
565            }
566        }
567        
568        // 10. Volume Confirmation (Weight: 2)
569        if (volumeSpike && volumeStrength > 2) {
570            if (bullishScore > bearishScore) {
571                bullishScore += 2;
572                signals.push(`High Volume Confirmation (${volumeStrength.toFixed(1)}x)`);
573            } else if (bearishScore > bullishScore) {
574                bearishScore += 2;
575                signals.push(`High Volume Confirmation (${volumeStrength.toFixed(1)}x)`);
576            }
577        }
578        
579        // 11. Price Action Patterns (Weight: 5) - VERY STRONG REVERSAL SIGNAL
580        if (priceActionPatterns.bullish.length > 0) {
581            const strongestPattern = priceActionPatterns.bullish.reduce((max, p) => p.strength > max.strength ? p : max);
582            bullishScore += strongestPattern.strength;
583            signals.push(`Bullish ${strongestPattern.type.replace('_', ' ').toUpperCase()} Pattern`);
584        }
585        if (priceActionPatterns.bearish.length > 0) {
586            const strongestPattern = priceActionPatterns.bearish.reduce((max, p) => p.strength > max.strength ? p : max);
587            bearishScore += strongestPattern.strength;
588            signals.push(`Bearish ${strongestPattern.type.replace('_', ' ').toUpperCase()} Pattern`);
589        }
590        
591        // 12. Trend Alignment (Weight: 3) - CRITICAL FOR LONG-TERM SIGNALS
592        // Only take reversals that align with higher timeframe trend
593        if (trend.direction === 'uptrend' && trend.strength > 0.6) {
594            // In uptrend, favor bullish reversals (buy the dip)
595            if (inDiscountZone) {
596                bullishScore += 3;
597                signals.push('Strong Uptrend - Buy Dip Opportunity');
598            }
599        } else if (trend.direction === 'downtrend' && trend.strength > 0.6) {
600            // In downtrend, favor bearish reversals (sell the rip)
601            if (inPremiumZone) {
602                bearishScore += 3;
603                signals.push('Strong Downtrend - Sell Rally Opportunity');
604            }
605        }
606        
607        // ==================== SIGNAL DETERMINATION ====================
608        let signal = 'neutral';
609        let confidence = 0;
610        
611        // CONFIRMED ENTRY SYSTEM - Requires multiple institutional confirmations
612        // For BULLISH: Need Order Block OR Liquidity Sweep + Discount Zone + RSI/Stoch oversold
613        // For BEARISH: Need Order Block OR Liquidity Sweep + Premium Zone + RSI/Stoch overbought
614        
615        const hasBullishOrderBlock = signals.some(s => s.includes('Bullish Order Block'));
616        const hasBearishOrderBlock = signals.some(s => s.includes('Bearish Order Block'));
617        const hasLiquiditySweepUp = signals.some(s => s.includes('Liquidity Sweep Below'));
618        const hasLiquiditySweepDown = signals.some(s => s.includes('Liquidity Sweep Above'));
619        const inDiscountZone = zone === 'discount';
620        const inPremiumZone = zone === 'premium';
621        const hasRSIOversold = rsi !== null && rsi < CONFIG.rsiOversold;
622        const hasRSIOverbought = rsi !== null && rsi > CONFIG.rsiOverbought;
623        const hasStochOversold = stochRSI && stochRSI.k < 20;
624        const hasStochOverbought = stochRSI && stochRSI.k > 80;
625        
626        // BULLISH CONFIRMED ENTRY: Institutions are BUYING
627        const bullishConfirmed = (hasBullishOrderBlock || hasLiquiditySweepUp) && 
628                                 inDiscountZone && 
629                                 (hasRSIOversold || hasStochOversold) &&
630                                 bullishScore >= 12; // Increased from 9 to 12
631        
632        // BEARISH CONFIRMED ENTRY: Institutions are SELLING
633        const bearishConfirmed = (hasBearishOrderBlock || hasLiquiditySweepDown) && 
634                                 inPremiumZone && 
635                                 (hasRSIOverbought || hasStochOverbought) &&
636                                 bearishScore >= 12; // Increased from 9 to 12
637        
638        // RELAXED ENTRIES: Strong score even without all confirmations
639        const strongBullish = bullishScore >= 10 && (inDiscountZone || hasRSIOversold) && bullishScore > bearishScore * 2;
640        const strongBearish = bearishScore >= 10 && (inPremiumZone || hasRSIOverbought) && bearishScore > bullishScore * 2;
641        
642        if (bullishConfirmed || strongBullish) {
643            signal = 'bullish_reversal';
644            confidence = bullishConfirmed ? 95 : 80;
645        } else if (bearishConfirmed || strongBearish) {
646            signal = 'bearish_reversal';
647            confidence = bearishConfirmed ? 95 : 80;
648        } else {
649            // NO WEAK SIGNALS - Only show strong reversals
650            signal = 'neutral';
651            confidence = 0;
652        }
653        
654        return {
655            signal,
656            confidence,
657            rsi,
658            stochRSI,
659            macd,
660            premiumDiscount,
661            orderBlocks,
662            fvg,
663            liquidityZones,
664            bos,
665            signals,
666            bullishScore,
667            bearishScore,
668            currentPrice,
669            ma20,
670            ma50,
671            ma200
672        };
673    }
674
675    // ==================== DATA EXTRACTION ====================
676
677    function extractPriceData() {
678        try {
679            const ohlcElements = document.querySelectorAll('[data-test-id-value-title]');
680            let ohlc = { open: null, high: null, low: null, close: null };
681            
682            console.log('🔍 Searching for price data... Found', ohlcElements.length, 'elements');
683            
684            ohlcElements.forEach(el => {
685                const title = el.getAttribute('data-test-id-value-title');
686                const valueEl = el.querySelector('.valueValue-l31H9iuA');
687                if (valueEl) {
688                    const priceText = valueEl.textContent.trim().replace(/[^0-9.]/g, '');
689                    const price = parseFloat(priceText);
690                    
691                    if (title === 'O') ohlc.open = price;
692                    else if (title === 'H') ohlc.high = price;
693                    else if (title === 'L') ohlc.low = price;
694                    else if (title === 'C') ohlc.close = price;
695                    
696                    console.log('Found', title, '=', price);
697                }
698            });
699            
700            console.log('📊 OHLC Data:', ohlc);
701            
702            // If we got valid OHLC data, add it to history
703            if (ohlc.close && !isNaN(ohlc.close) && ohlc.close > 0) {
704                priceHistory.push(ohlc.close);
705                
706                if (ohlc.open && ohlc.high && ohlc.low) {
707                    ohlcHistory.push(ohlc);
708                    if (ohlcHistory.length > 100) ohlcHistory = ohlcHistory.slice(-100);
709                }
710                
711                if (priceHistory.length > 100) priceHistory = priceHistory.slice(-100);
712                
713                console.log('✅ Extracted real price:', ohlc.close, '| History:', priceHistory.length, '/', CONFIG.lookbackPeriod);
714                
715                if (priceHistory.length >= CONFIG.lookbackPeriod) {
716                    console.log('🎯 Using REAL Bitcoin data for analysis!');
717                    return { 
718                        prices: priceHistory.slice(-CONFIG.lookbackPeriod), 
719                        volumes: volumeHistory.slice(-CONFIG.lookbackPeriod),
720                        ohlc: ohlcHistory.slice(-CONFIG.lookbackPeriod)
721                    };
722                } else {
723                    console.log('⏳ Building price history... (' + priceHistory.length + '/' + CONFIG.lookbackPeriod + ') - Need', (CONFIG.lookbackPeriod - priceHistory.length), 'more');
724                }
725            } else {
726                console.log('❌ Invalid OHLC data:', ohlc);
727            }
728            
729            console.log('⚠️ Using simulated data (not enough real data yet)');
730            return generateSimulatedData();
731            
732        } catch (error) {
733            console.error('❌ Error extracting price data:', error);
734            return generateSimulatedData();
735        }
736    }
737
738    function generateSimulatedData() {
739        const prices = [];
740        const volumes = [];
741        const ohlc = [];
742        let basePrice = 100;
743        
744        for (let i = 0; i < CONFIG.lookbackPeriod; i++) {
745            const change = i < 30 ? (Math.random() - 0.7) * 3 : (Math.random() - 0.3) * 2;
746            basePrice += change;
747            
748            const open = basePrice;
749            const close = basePrice + (Math.random() - 0.5) * 2;
750            const high = Math.max(open, close) + Math.random() * 1;
751            const low = Math.min(open, close) - Math.random() * 1;
752            
753            prices.push(close);
754            volumes.push((Math.random() * 1000000 + 500000) * (i > 40 ? 1.8 : 1.0));
755            ohlc.push({ open, high, low, close });
756        }
757        
758        console.log('Generated simulated data - Range:', Math.min(...prices).toFixed(2), 'to', Math.max(...prices).toFixed(2));
759        return { prices, volumes, ohlc };
760    }
761
762    // ==================== UI FUNCTIONS ====================
763
764    function createUIPanel() {
765        const panel = document.createElement('div');
766        panel.id = 'reversal-detector-panel';
767        panel.style.cssText = `
768            position: fixed;
769            top: 80px;
770            right: 20px;
771            width: 320px;
772            background: linear-gradient(135deg, #1e1e1e 0%, #2d2d2d 100%);
773            border: 2px solid #3d3d3d;
774            border-radius: 12px;
775            padding: 16px;
776            z-index: 10000;
777            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
778            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
779            color: #ffffff;
780            cursor: move;
781        `;
782        
783        panel.innerHTML = `
784            <div id="panel-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; cursor: move;">
785                <h3 style="margin: 0; font-size: 16px; font-weight: 600; color: #ffffff;">🎯 Smart Reversal Detector</h3>
786                <button id="close-panel" style="background: none; border: none; color: #888; cursor: pointer; font-size: 20px; padding: 0; width: 24px; height: 24px;">×</button>
787            </div>
788            <div id="reversal-content" style="font-size: 13px; line-height: 1.6;">
789                <div style="text-align: center; padding: 20px; color: #888;">
790                    Analyzing market data...
791                </div>
792            </div>
793        `;
794        
795        document.body.appendChild(panel);
796        
797        // Make panel draggable
798        let isDragging = false;
799        let currentX, currentY, initialX, initialY;
800        const header = document.getElementById('panel-header');
801        
802        header.addEventListener('mousedown', (e) => {
803            isDragging = true;
804            initialX = e.clientX - panel.offsetLeft;
805            initialY = e.clientY - panel.offsetTop;
806        });
807        
808        document.addEventListener('mousemove', (e) => {
809            if (isDragging) {
810                e.preventDefault();
811                currentX = e.clientX - initialX;
812                currentY = e.clientY - initialY;
813                panel.style.left = currentX + 'px';
814                panel.style.top = currentY + 'px';
815                panel.style.right = 'auto';
816            }
817        });
818        
819        document.addEventListener('mouseup', () => { isDragging = false; });
820        
821        document.getElementById('close-panel').addEventListener('click', () => {
822            panel.style.display = 'none';
823        });
824        
825        return panel;
826    }
827
828    function createChartOverlay() {
829        const existing = document.getElementById('reversal-chart-overlay');
830        if (existing) existing.remove();
831        
832        const overlay = document.createElement('div');
833        overlay.id = 'reversal-chart-overlay';
834        overlay.style.cssText = `
835            position: fixed;
836            top: 0;
837            left: 0;
838            width: 100%;
839            height: 100%;
840            pointer-events: none;
841            z-index: 9999;
842        `;
843        
844        document.body.appendChild(overlay);
845        return overlay;
846    }
847
848    function drawPremiumDiscountZones(analysis) {
849        if (!chartOverlay || !analysis.premiumDiscount) return;
850        
851        const existingZones = chartOverlay.querySelectorAll('.price-zone');
852        existingZones.forEach(zone => zone.remove());
853        
854        const { high, low, zone } = analysis.premiumDiscount;
855        const premiumLevel = low + (high - low) * CONFIG.premiumThreshold;
856        const discountLevel = low + (high - low) * CONFIG.discountThreshold;
857        
858        const premiumZone = document.createElement('div');
859        premiumZone.className = 'price-zone premium-zone';
860        premiumZone.style.cssText = `
861            position: fixed;
862            right: 400px;
863            top: 150px;
864            padding: 8px 12px;
865            background: rgba(255, 107, 107, ${zone === 'premium' ? '0.3' : '0.15'});
866            border: ${zone === 'premium' ? '3px' : '2px'} solid rgba(255, 107, 107, 0.5);
867            border-radius: 6px;
868            color: #ff6b6b;
869            font-size: 12px;
870            font-weight: 600;
871            pointer-events: none;
872        `;
873        premiumZone.textContent = `🔴 Premium: ${premiumLevel.toFixed(2)}`;
874        chartOverlay.appendChild(premiumZone);
875        
876        const discountZone = document.createElement('div');
877        discountZone.className = 'price-zone discount-zone';
878        discountZone.style.cssText = `
879            position: fixed;
880            right: 400px;
881            bottom: 150px;
882            padding: 8px 12px;
883            background: rgba(81, 207, 102, ${zone === 'discount' ? '0.3' : '0.15'});
884            border: ${zone === 'discount' ? '3px' : '2px'} solid rgba(81, 207, 102, 0.5);
885            border-radius: 6px;
886            color: #51cf66;
887            font-size: 12px;
888            font-weight: 600;
889            pointer-events: none;
890        `;
891        discountZone.textContent = `🟢 Discount: ${discountLevel.toFixed(2)}`;
892        chartOverlay.appendChild(discountZone);
893    }
894
895    function drawEntryExitMarkers(analysis) {
896        if (!chartOverlay) return;
897        
898        const existingMarkers = chartOverlay.querySelectorAll('.entry-exit-marker');
899        existingMarkers.forEach(marker => marker.remove());
900        
901        if (analysis.signal !== 'bullish_reversal' && analysis.signal !== 'bearish_reversal') return;
902        if (lastSignal === analysis.signal) return;
903        
904        lastSignal = analysis.signal;
905        const currentPrice = analysis.currentPrice;
906        const isBullish = analysis.signal === 'bullish_reversal';
907        
908        const entryPrice = currentPrice;
909        const stopLoss = isBullish ? currentPrice * 0.98 : currentPrice * 1.02;
910        const takeProfit1 = isBullish ? currentPrice * 1.03 : currentPrice * 0.97;
911        const takeProfit2 = isBullish ? currentPrice * 1.05 : currentPrice * 0.95;
912        
913        const entryMarker = document.createElement('div');
914        entryMarker.className = 'entry-exit-marker entry-marker';
915        entryMarker.style.cssText = `
916            position: fixed;
917            left: 50%;
918            top: 50%;
919            transform: translate(-50%, -50%);
920            padding: 20px 28px;
921            background: ${isBullish ? 'rgba(0, 255, 136, 0.95)' : 'rgba(255, 68, 68, 0.95)'};
922            border: 4px solid ${isBullish ? '#00ff88' : '#ff4444'};
923            border-radius: 16px;
924            color: #000;
925            font-size: 18px;
926            font-weight: 700;
927            pointer-events: none;
928            box-shadow: 0 12px 48px rgba(0,0,0,0.6);
929            animation: pulse 2s ease-in-out infinite;
930        `;
931        entryMarker.innerHTML = `
932            <div style="text-align: center;">
933                <div style="font-size: 40px; margin-bottom: 8px;">${isBullish ? '📈' : '📉'}</div>
934                <div style="font-size: 24px; margin-bottom: 12px; text-shadow: 0 2px 4px rgba(0,0,0,0.3);">${isBullish ? 'BULLISH' : 'BEARISH'} ENTRY</div>
935                <div style="font-size: 15px; opacity: 0.95; line-height: 1.6;">
936                    Entry: <strong>${entryPrice.toFixed(2)}</strong><br>
937                    Stop Loss: <strong>${stopLoss.toFixed(2)}</strong><br>
938                    TP1: <strong>${takeProfit1.toFixed(2)}</strong><br>
939                    TP2: <strong>${takeProfit2.toFixed(2)}</strong>
940                </div>
941            </div>
942        `;
943        chartOverlay.appendChild(entryMarker);
944        
945        const style = document.createElement('style');
946        style.textContent = `
947            @keyframes pulse {
948                0%, 100% { transform: translate(-50%, -50%) scale(1); }
949                50% { transform: translate(-50%, -50%) scale(1.05); }
950            }
951        `;
952        document.head.appendChild(style);
953        
954        setTimeout(() => {
955            entryMarker.style.transition = 'opacity 1s';
956            entryMarker.style.opacity = '0';
957            setTimeout(() => entryMarker.remove(), 1000);
958        }, 10000);
959        
960        const levels = [
961            { label: 'Entry', price: entryPrice, color: isBullish ? '#00ff88' : '#ff4444' },
962            { label: 'Stop Loss', price: stopLoss, color: '#ff4444' },
963            { label: 'TP1', price: takeProfit1, color: '#ffd700' },
964            { label: 'TP2', price: takeProfit2, color: '#00ff88' }
965        ];
966        
967        levels.forEach((level, index) => {
968            const levelMarker = document.createElement('div');
969            levelMarker.className = 'entry-exit-marker level-marker';
970            levelMarker.style.cssText = `
971                position: fixed;
972                right: 20px;
973                top: ${200 + index * 60}px;
974                padding: 8px 14px;
975                background: rgba(0, 0, 0, 0.85);
976                border-left: 4px solid ${level.color};
977                border-radius: 6px;
978                color: ${level.color};
979                font-size: 12px;
980                font-weight: 600;
981                pointer-events: none;
982                box-shadow: 0 4px 12px rgba(0,0,0,0.3);
983            `;
984            levelMarker.textContent = `${level.label}: ${level.price.toFixed(2)}`;
985            chartOverlay.appendChild(levelMarker);
986            
987            setTimeout(() => {
988                levelMarker.style.transition = 'opacity 1s';
989                levelMarker.style.opacity = '0';
990                setTimeout(() => levelMarker.remove(), 1000);
991            }, 30000);
992        });
993    }
994
995    function drawSupportResistance(analysis) {
996        if (!chartOverlay || !analysis.premiumDiscount) return;
997        
998        const existingSR = chartOverlay.querySelectorAll('.sr-level');
999        existingSR.forEach(sr => sr.remove());
1000        
1001        const { high, low } = analysis.premiumDiscount;
1002        
1003        const resistance = document.createElement('div');
1004        resistance.className = 'sr-level resistance';
1005        resistance.style.cssText = `
1006            position: fixed;
1007            right: 400px;
1008            top: 120px;
1009            padding: 6px 10px;
1010            background: rgba(255, 68, 68, 0.2);
1011            border: 2px dashed rgba(255, 68, 68, 0.6);
1012            border-radius: 4px;
1013            color: #ff4444;
1014            font-size: 11px;
1015            font-weight: 600;
1016            pointer-events: none;
1017        `;
1018        resistance.textContent = `Resistance: ${high.toFixed(2)}`;
1019        chartOverlay.appendChild(resistance);
1020        
1021        const support = document.createElement('div');
1022        support.className = 'sr-level support';
1023        support.style.cssText = `
1024            position: fixed;
1025            right: 400px;
1026            bottom: 120px;
1027            padding: 6px 10px;
1028            background: rgba(0, 255, 136, 0.2);
1029            border: 2px dashed rgba(0, 255, 136, 0.6);
1030            border-radius: 4px;
1031            color: #00ff88;
1032            font-size: 11px;
1033            font-weight: 600;
1034            pointer-events: none;
1035        `;
1036        support.textContent = `Support: ${low.toFixed(2)}`;
1037        chartOverlay.appendChild(support);
1038    }
1039
1040    function updateUI(analysis) {
1041        if (!uiPanel) return;
1042        
1043        const content = document.getElementById('reversal-content');
1044        if (!content) return;
1045        
1046        let signalColor = '#888';
1047        let signalEmoji = '⚪';
1048        let signalText = 'Neutral';
1049        
1050        if (analysis.signal === 'bullish_reversal') {
1051            signalColor = '#00ff88';
1052            signalEmoji = '🟢';
1053            signalText = 'BULLISH REVERSAL';
1054        } else if (analysis.signal === 'bearish_reversal') {
1055            signalColor = '#ff4444';
1056            signalEmoji = '🔴';
1057            signalText = 'BEARISH REVERSAL';
1058        } else if (analysis.signal === 'weak_bullish') {
1059            signalColor = '#88ff88';
1060            signalEmoji = '🟡';
1061            signalText = 'Weak Bullish';
1062        } else if (analysis.signal === 'weak_bearish') {
1063            signalColor = '#ff8888';
1064            signalEmoji = '🟡';
1065            signalText = 'Weak Bearish';
1066        }
1067        
1068        let zoneColor = '#888';
1069        let zoneText = 'Equilibrium';
1070        if (analysis.premiumDiscount) {
1071            if (analysis.premiumDiscount.zone === 'premium') {
1072                zoneColor = '#ff6b6b';
1073                zoneText = 'Premium Zone (Sell)';
1074            } else if (analysis.premiumDiscount.zone === 'discount') {
1075                zoneColor = '#51cf66';
1076                zoneText = 'Discount Zone (Buy)';
1077            }
1078        }
1079        
1080        const signalsHTML = analysis.signals.map(s => 
1081            `<div style="padding: 6px 10px; background: rgba(255,255,255,0.05); border-radius: 6px; margin: 4px 0; font-size: 11px;">• ${s}</div>`
1082        ).join('');
1083        
1084        // Smart Money Concepts summary
1085        let smcSummary = '';
1086        if (analysis.orderBlocks) {
1087            const bullishOB = analysis.orderBlocks.bullish.length;
1088            const bearishOB = analysis.orderBlocks.bearish.length;
1089            if (bullishOB > 0 || bearishOB > 0) {
1090                smcSummary += `<div style="font-size: 11px; margin-top: 4px;">Order Blocks: 🟢${bullishOB} 🔴${bearishOB}</div>`;
1091            }
1092        }
1093        if (analysis.fvg) {
1094            const bullishFVG = analysis.fvg.bullish.length;
1095            const bearishFVG = analysis.fvg.bearish.length;
1096            if (bullishFVG > 0 || bearishFVG > 0) {
1097                smcSummary += `<div style="font-size: 11px; margin-top: 4px;">Fair Value Gaps: 🟢${bullishFVG} 🔴${bearishFVG}</div>`;
1098            }
1099        }
1100        
1101        // Determine institutional activity
1102        let institutionalActivity = '⚪ Neutral';
1103        let activityColor = '#888';
1104        if (analysis.bullishScore > analysis.bearishScore + 3) {
1105            institutionalActivity = '🏦 Institutions BUYING';
1106            activityColor = '#00ff88';
1107        } else if (analysis.bearishScore > analysis.bullishScore + 3) {
1108            institutionalActivity = '🏦 Institutions SELLING';
1109            activityColor = '#ff4444';
1110        }
1111        
1112        content.innerHTML = `
1113            <div style="background: rgba(255,255,255,0.05); border-radius: 8px; padding: 12px; margin-bottom: 12px;">
1114                <div style="font-size: 28px; text-align: center; margin-bottom: 8px;">${signalEmoji}</div>
1115                <div style="font-size: 17px; font-weight: 700; text-align: center; color: ${signalColor}; margin-bottom: 8px;">
1116                    ${signalText}
1117                </div>
1118                <div style="text-align: center; font-size: 13px; color: #888;">
1119                    Confidence: <strong style="color: ${signalColor};">${analysis.confidence.toFixed(0)}%</strong>
1120                </div>
1121                <div style="text-align: center; font-size: 12px; margin-top: 8px; padding: 6px; background: rgba(0,0,0,0.3); border-radius: 6px; color: ${activityColor}; font-weight: 600;">
1122                    ${institutionalActivity}
1123                </div>
1124            </div>
1125            
1126            ${(analysis.signal === 'bullish_reversal' || analysis.signal === 'bearish_reversal') ? `
1127                <div style="background: ${analysis.signal === 'bullish_reversal' ? 'rgba(0, 255, 136, 0.1)' : 'rgba(255, 68, 68, 0.1)'}; border: 2px solid ${analysis.signal === 'bullish_reversal' ? '#00ff88' : '#ff4444'}; border-radius: 8px; padding: 12px; margin-bottom: 12px;">
1128                    <div style="font-size: 13px; font-weight: 700; color: ${signalColor}; margin-bottom: 8px; text-align: center;">
1129                        ⚡ SMART MONEY ENTRY
1130                    </div>
1131                    <div style="font-size: 12px; line-height: 1.8; color: #fff;">
1132                        ${analysis.signal === 'bullish_reversal' ? 
1133        '<strong>🟢 BUY Signal:</strong><br>• Institutions are ACCUMULATING<br>• Enter LONG position NOW<br>• Stop Loss: 2% below entry<br>• Target: +3% to +5% profit' : 
1134        '<strong>🔴 SELL Signal:</strong><br>• Institutions are DISTRIBUTING<br>• Enter SHORT position NOW<br>• Stop Loss: 2% above entry<br>• Target: -3% to -5% profit'}
1135                    </div>
1136                </div>
1137            ` : ''}
1138            
1139            <div style="background: rgba(255,255,255,0.05); border-radius: 8px; padding: 10px; margin-bottom: 12px;">
1140                <div style="font-size: 12px; color: #888; margin-bottom: 6px;">Market Position</div>
1141                <div style="font-weight: 600; color: ${zoneColor};">${zoneText}</div>
1142                ${analysis.premiumDiscount ? `
1143                    <div style="font-size: 11px; color: #888; margin-top: 4px;">
1144                        Current: ${analysis.premiumDiscount.currentPrice.toFixed(2)}<br>
1145                        Range: ${analysis.premiumDiscount.low.toFixed(2)} - ${analysis.premiumDiscount.high.toFixed(2)}<br>
1146                        Position: ${(analysis.premiumDiscount.position * 100).toFixed(1)}% of range
1147                    </div>
1148                ` : ''}
1149                ${smcSummary}
1150            </div>
1151            
1152            <div style="background: rgba(255,255,255,0.05); border-radius: 8px; padding: 10px; margin-bottom: 12px;">
1153                <div style="font-size: 12px; color: #888; margin-bottom: 6px;">Technical Indicators</div>
1154                <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; font-size: 11px;">
1155                    <div>RSI: <span style="color: #fff; font-weight: 600;">${analysis.rsi ? analysis.rsi.toFixed(1) : 'N/A'}</span></div>
1156                    <div>StochRSI: <span style="color: #fff; font-weight: 600;">${analysis.stochRSI ? analysis.stochRSI.k.toFixed(1) : 'N/A'}</span></div>
1157                    <div>Score: <span style="color: ${signalColor}; font-weight: 700;">${analysis.bullishScore}${analysis.bearishScore}</span></div>
1158                    <div>Price: <span style="color: #fff; font-weight: 600;">${analysis.currentPrice ? analysis.currentPrice.toFixed(2) : 'N/A'}</span></div>
1159                </div>
1160            </div>
1161            
1162            ${analysis.signals.length > 0 ? `
1163                <div style="background: rgba(255,255,255,0.05); border-radius: 8px; padding: 10px;">
1164                    <div style="font-size: 12px; color: #888; margin-bottom: 6px;">Active Signals (${analysis.signals.length})</div>
1165                    ${signalsHTML}
1166                </div>
1167            ` : ''}
1168        `;
1169    }
1170
1171    function runAnalysis() {
1172        if (!uiPanel) {
1173            console.error('UI Panel not initialized');
1174            return;
1175        }
1176        
1177        const data = extractPriceData();
1178        
1179        if (!data || !data.prices || data.prices.length === 0) {
1180            console.error('No price data available');
1181            return;
1182        }
1183        
1184        const analysis = detectReversal(data.prices, data.volumes, data.ohlc);
1185        
1186        if (!analysis) {
1187            console.error('Analysis failed');
1188            return;
1189        }
1190        
1191        const currentTime = Date.now();
1192        
1193        // Store and persist strong signals with cooldown
1194        if (analysis.signal === 'bullish_reversal' || analysis.signal === 'bearish_reversal') {
1195            // Check if enough time has passed since last signal change
1196            const timeSinceLastSignal = currentTime - lastSignalTime;
1197            
1198            // Only update if it's a different signal AND cooldown has passed
1199            if (!lastStrongSignal || 
1200                (lastStrongSignal.signal !== analysis.signal && timeSinceLastSignal > signalCooldown)) {
1201                lastStrongSignal = analysis;
1202                lastSignalTime = currentTime;
1203                console.log('🎯 NEW SIGNAL:', analysis.signal, 'Confidence:', analysis.confidence + '%', 'Score:', analysis.bullishScore, '/', analysis.bearishScore);
1204            } else if (lastStrongSignal.signal === analysis.signal) {
1205                // Same signal, just update the data
1206                lastStrongSignal = analysis;
1207            }
1208        }
1209        
1210        // Use stored signal if current is weak
1211        const displayAnalysis = (analysis.signal === 'neutral' || analysis.signal === 'weak_bullish' || analysis.signal === 'weak_bearish') && lastStrongSignal
1212            ? lastStrongSignal
1213            : analysis;
1214        
1215        console.log('Display:', displayAnalysis.signal, 'Conf:', displayAnalysis.confidence, 'Signals:', displayAnalysis.signals.length);
1216        
1217        lastAnalysis = displayAnalysis;
1218        updateUI(displayAnalysis);
1219        
1220        if (chartOverlay) {
1221            drawPremiumDiscountZones(displayAnalysis);
1222            drawSupportResistance(displayAnalysis);
1223            drawEntryExitMarkers(displayAnalysis);
1224        }
1225    }
1226
1227    async function init() {
1228        console.log('Initializing Smart Market Reversal Detector...');
1229        
1230        if (document.readyState === 'loading') {
1231            document.addEventListener('DOMContentLoaded', init);
1232            return;
1233        }
1234        
1235        setTimeout(() => {
1236            uiPanel = createUIPanel();
1237            chartOverlay = createChartOverlay();
1238            
1239            runAnalysis();
1240            setInterval(runAnalysis, CONFIG.updateInterval);
1241            
1242            console.log('✅ Reversal Detector Active - Using Smart Money Concepts!');
1243        }, 2000);
1244    }
1245
1246    init();
1247
1248})();
Smart Market Reversal Detector | Robomonkey