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})();