//@version=5
indicator("RSI Trend Signals", 
     overlay=true, max_lines_count=500, max_labels_count=500)

// ═══════════════════════════════════════════════════════════════
// INPUTS
// ═══════════════════════════════════════════════════════════════
rsiPeriod        = input.int(14,  "RSI Period", minval=5, group="RSI")
emaPeriod        = input.int(9,   "EMA Period (color & signals)", minval=1, group="EMA")
filterEmaPeriod  = input.int(100, "EMA Filter Period", minval=1, group="EMA")
useEmaFilter     = input.bool(true, "Enable EMA Filter", group="EMA")

rsiOverbought    = input.int(82, "RSI Overbought", minval=50, group="RSI")
rsiOversold      = input.int(17, "RSI Oversold", minval=10, group="RSI")
rsiBuyThreshold  = input.int(52, "RSI Buy Threshold", minval=50, group="RSI")
rsiSellThreshold = input.int(48, "RSI Sell Threshold", minval=10, group="RSI")

// Time Filter
useTimeFilter    = input.bool(true, "Enable Time Filter", group="Time Filter")
timeFilterStart  = input.int(930, "Start Time (HHMM)", minval=0, maxval=2359, group="Time Filter", tooltip="Example: 930 = 9:30, 1400 = 2:00 PM")
timeFilterEnd    = input.int(1600, "End Time (HHMM)", minval=0, maxval=2359, group="Time Filter", tooltip="Example: 1600 = 4:00 PM")

// Candle Direction Filter
useCandleFilter  = input.bool(true, "Enable Candle Direction Filter", group="Candle Filter")
candlePositionThreshold = input.float(50, "Candle Position Threshold (%)", minval=1, maxval=99, step=1, group="Candle Filter", tooltip="Buy signals require close above this % of real candle range. Sell signals require close below this % of real candle range.")

// MACD Filter
useMacdFilter    = input.bool(false, "Enable MACD Filter", group="MACD Filter")
macdFastLen      = input.int(6, "MACD Fast Length", minval=1, group="MACD Filter")
macdSlowLen      = input.int(13, "MACD Slow Length", minval=1, group="MACD Filter")
macdSignalLen    = input.int(5, "MACD Signal Length", minval=1, group="MACD Filter")
macdTimeframe    = input.string("", "MACD Timeframe", group="MACD Filter", tooltip="Enter timeframe: leave blank for current chart. Examples: 15S, 30S, 45S, 1, 5, 15, 60, 240, D, W, M")

// Exhaustion Filter (after yellow candles)
useExhaustionFilter = input.bool(true, "Enable Exhaustion Filter", group="Exhaustion Filter", tooltip="Filters out signals that occur after a yellow (overbought/oversold) candle")
exhaustionBars     = input.int(1, "Exhaustion Lookback Bars", minval=1, maxval=10, group="Exhaustion Filter", tooltip="Number of bars after a yellow candle to filter signals")

// White Candle Filter (after HA Doji)
useWhiteFilter   = input.bool(true, "Enable White Candle Filter", group="White Candle Filter", tooltip="Blocks signals in the same direction as the trend that printed a white HA Doji (potential reversal warning)")
whiteLookback    = input.int(1, "White Lookback Bars", minval=1, maxval=10, group="White Candle Filter", tooltip="Number of bars after a white candle to block same-direction signals")

// ATR SL / TP
atrLength        = input.int(14, "ATR Length", minval=1, group="Risk Management")
slMultiplier     = input.float(1.0, "Stop Loss ATR Multiplier", minval=0.1, step=0.1, group="Risk Management")
tpMultiplier     = input.float(1.0, "Take Profit ATR Multiplier", minval=0.1, step=0.1, group="Risk Management")
lineLength       = input.int(2, "Lines extend (bars forward)", minval=1, maxval=20, group="Risk Management")

// Real Price Dots
plotRealDots     = input.bool(true, "Show real close dots on Entry", group="Real Price Dots")
realDotColor     = input.color(color.new(color.white, 0), "Real close dot color", group="Real Price Dots")
dotSize          = input.string("Tiny", options=["Auto", "Tiny", "Small"], title="Dot size", group="Real Price Dots")

// HA Doji
showHaDoji       = input.bool(true, "Show HA Doji (White)", group="HA Doji")
haDojiThreshold  = input.float(0.1, "HA Doji Body Threshold (% of range)", minval=0.01, maxval=0.5, step=0.01, group="HA Doji")

// Labels
labelSizeInput   = input.string("Small", title="Label Size", options=["Tiny", "Small", "Normal", "Large", "Huge"], group="Labels")
labelOffsetMult  = input.float(0.4, "Label Offset (× ATR)", minval=0.0, step=0.1, group="Labels", tooltip="How far to push labels away from the candle. Higher = further away.")

// Convert string to size.*
labelSize = switch labelSizeInput
    "Tiny"   => size.tiny
    "Small"  => size.small
    "Normal" => size.normal
    "Large"  => size.large
    "Huge"   => size.huge
    => size.small

// ═══════════════════════════════════════════════════════════════
// CALCULATIONS
// ═══════════════════════════════════════════════════════════════
ema9      = ta.ema(close, emaPeriod)
emaFilter = ta.ema(close, filterEmaPeriod)
rsiValue  = ta.rsi(close, rsiPeriod)
atrValue  = ta.atr(atrLength)

// MACD Calculation with timeframe option
macdTimeframeRes = macdTimeframe == "" ? timeframe.period : macdTimeframe

macdClose = request.security(syminfo.tickerid, macdTimeframeRes, close, gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)
[macdLine, signalLine, histLine] = ta.macd(macdClose, macdFastLen, macdSlowLen, macdSignalLen)
macdBullish = macdLine > signalLine
macdBearish = macdLine < signalLine

// Real price data
real_price = ticker.new(prefix=syminfo.prefix, ticker=syminfo.ticker)
real_open = request.security(symbol=real_price, timeframe="", expression=open, 
     gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)
real_high = request.security(symbol=real_price, timeframe="", expression=high, 
     gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)
real_low = request.security(symbol=real_price, timeframe="", expression=low, 
     gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)
real_close = request.security(symbol=real_price, timeframe="", expression=close, 
     gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)

// ═══════════════════════════════════════════════════════════════
// HEIKIN ASHI CALCULATION
// ═══════════════════════════════════════════════════════════════
haClose = (open + high + low + close) / 4
var float haOpen = na
haOpen := na(haOpen[1]) ? (open + close) / 2 : (haOpen[1] + haClose[1]) / 2
haHigh  = math.max(high, math.max(haOpen, haClose))
haLow   = math.min(low,  math.min(haOpen, haClose))

haBody  = math.abs(haClose - haOpen)
haRange = haHigh - haLow
isHaDoji = haRange > 0 and (haBody / haRange) <= haDojiThreshold

// ═══════════════════════════════════════════════════════════════
// CANDLE COLOR (based on HA candles)
// ═══════════════════════════════════════════════════════════════
var color candleColor = color.gray

if rsiValue > rsiOverbought or rsiValue < rsiOversold
    candleColor := color.yellow
else if rsiValue >= rsiBuyThreshold and close > ema9
    candleColor := #07830b          // Green
else if rsiValue <= rsiSellThreshold and close < ema9
    candleColor := #9c0707          // Red
else
    candleColor := color.rgb(92, 93, 97)  // Gray

// Optional HA Doji override → White when coming from a trend candle
prevWasTrend = candleColor[1] == #07830b or candleColor[1] == #9c0707
if showHaDoji and isHaDoji and prevWasTrend
    candleColor := color.white

plotcandle(open, high, low, close, color=candleColor, wickcolor=candleColor, bordercolor=candleColor)

plot(ema9,      "EMA (color)", color=color.blue,   linewidth=2)
plot(emaFilter, "EMA Filter",  color=color.orange, linewidth=2)

// ═══════════════════════════════════════════════════════════════
// REAL CANDLE POSITION
// ═══════════════════════════════════════════════════════════════
realCandleRange = real_high - real_low
realCandlePositionPercent = realCandleRange > 0 ? ((real_close - real_low) / realCandleRange) * 100 : 50

isRealCloseAboveThreshold = realCandlePositionPercent >= candlePositionThreshold
isRealCloseBelowThreshold = realCandlePositionPercent <= (100 - candlePositionThreshold)

// ═══════════════════════════════════════════════════════════════
// EXHAUSTION FILTER - Yellow candles
// ═══════════════════════════════════════════════════════════════
var int barsSinceYellow = 999
if candleColor == color.yellow
    barsSinceYellow := 0
else
    barsSinceYellow := barsSinceYellow + 1

yellowExhaustionActive = barsSinceYellow <= exhaustionBars and barsSinceYellow > 0

// ═══════════════════════════════════════════════════════════════
// WHITE CANDLE FILTER - HA Doji after trend
// ═══════════════════════════════════════════════════════════════
var int barsSinceWhite = 999
var int whiteDirection = 0   // 1 = after green (block buys), -1 = after red (block sells)

if candleColor == color.white
    barsSinceWhite := 0
    // Record the direction of the trend that produced this white candle
    // (only update when the white candle first appears)
    if candleColor[1] != color.white
        if candleColor[1] == #07830b
            whiteDirection := 1      // came after green → block subsequent buys
        else if candleColor[1] == #9c0707
            whiteDirection := -1     // came after red → block subsequent sells
else
    barsSinceWhite := barsSinceWhite + 1

whiteFilterActive = barsSinceWhite <= whiteLookback and barsSinceWhite > 0

// ═══════════════════════════════════════════════════════════════
// TICK CALCULATION FUNCTION
// ═══════════════════════════════════════════════════════════════
tickSize = syminfo.mintick
ticksToTarget(price1, price2) =>
    diff = math.abs(price1 - price2)
    ticks = diff / tickSize
    math.round(ticks)

// ═══════════════════════════════════════════════════════════════
// TIME FILTER
// ═══════════════════════════════════════════════════════════════
timeToSeconds(hhmm) =>
    hours = math.floor(hhmm / 100)
    minutes = hhmm - (hours * 100)
    seconds = (hours * 3600) + (minutes * 60)
    seconds

startSeconds = timeToSeconds(timeFilterStart)
endSeconds = timeToSeconds(timeFilterEnd)

currentTime = hour * 3600 + minute * 60 + second

inSession = not useTimeFilter or (startSeconds <= endSeconds ? (currentTime >= startSeconds and currentTime < endSeconds) : (currentTime >= startSeconds or currentTime < endSeconds))

// ═══════════════════════════════════════════════════════════════
// RESOLUTION FILTER + 1-CANDLE DELAY
// ═══════════════════════════════════════════════════════════════
var bool  tradeActive  = false
var bool  isLongTrade  = false
var float activeEntry  = na
var float activeSL     = na
var float activeTP     = na
var int   resolveBar   = na

if tradeActive
    bool hit = false
    if isLongTrade
        hit := high >= activeTP or low <= activeSL
    else
        hit := low <= activeTP or high >= activeSL

    if hit
        tradeActive := false
        resolveBar  := bar_index

canSignal = not tradeActive and (na(resolveBar) or bar_index > resolveBar)

// ═══════════════════════════════════════════════════════════════
// SIGNAL LOGIC
// ═══════════════════════════════════════════════════════════════
isGreen = candleColor == #07830b
isRed   = candleColor == #9c0707

buySignalRaw  = isGreen and not isGreen[1]
sellSignalRaw = isRed   and not isRed[1]

// Apply real candle position filter
if useCandleFilter
    buySignalRaw  := buySignalRaw  and isRealCloseAboveThreshold
    sellSignalRaw := sellSignalRaw and isRealCloseBelowThreshold

// Apply MACD filter
if useMacdFilter
    buySignalRaw  := buySignalRaw  and macdBullish
    sellSignalRaw := sellSignalRaw and macdBearish

// Apply Exhaustion Filter (yellow)
if useExhaustionFilter
    if yellowExhaustionActive
        buySignalRaw := false
        sellSignalRaw := false

// Apply White Candle Filter (directional)
if useWhiteFilter and whiteFilterActive
    if whiteDirection == 1      // white came after green → block buys
        buySignalRaw := false
    else if whiteDirection == -1 // white came after red → block sells
        sellSignalRaw := false

buySignal  = buySignalRaw  and inSession and canSignal
sellSignal = sellSignalRaw and inSession and canSignal

if useEmaFilter
    buySignal  := buySignal  and close > emaFilter
    sellSignal := sellSignal and close < emaFilter

// ═══════════════════════════════════════════════════════════════
// ACTIVATE NEW TRADE + DRAW LINES
// ═══════════════════════════════════════════════════════════════
if buySignal or sellSignal
    entryPrice = real_close
    atrDist    = atrValue

    slPrice = buySignal ? entryPrice - atrDist * slMultiplier : entryPrice + atrDist * slMultiplier
    tpPrice = buySignal ? entryPrice + atrDist * tpMultiplier : entryPrice - atrDist * tpMultiplier

    tradeActive := true
    isLongTrade := buySignal
    activeEntry := entryPrice
    activeSL    := slPrice
    activeTP    := tpPrice

    line.new(bar_index, entryPrice, bar_index + lineLength, entryPrice, color=color.white, width=1, style=line.style_solid)
    line.new(bar_index, slPrice,    bar_index + lineLength, slPrice,    color=color.red,   width=1, style=line.style_dashed)
    line.new(bar_index, tpPrice,    bar_index + lineLength, tpPrice,    color=color.green, width=1, style=line.style_dashed)

    ticksToTargetValue = ticksToTarget(entryPrice, tpPrice)
    
    offset = atrValue * labelOffsetMult
    
    if buySignal
        labelText = "BUY " + str.tostring(ticksToTargetValue)
        label.new(bar_index, low - offset, labelText, style=label.style_label_up, color=color.green, textcolor=color.white, size=labelSize)
    else
        labelText = "SELL " + str.tostring(ticksToTargetValue)
        label.new(bar_index, high + offset, labelText, style=label.style_label_down, color=color.red, textcolor=color.white, size=labelSize)

// ═══════════════════════════════════════════════════════════════
// REAL PRICE DOTS ON ENTRY
// ═══════════════════════════════════════════════════════════════
showTiny  = plotRealDots and dotSize == "Tiny"  ? display.all : display.none
showAuto  = plotRealDots and dotSize == "Auto"  ? display.all : display.none
showSmall = plotRealDots and dotSize == "Small" ? display.all : display.none

entryDot = (buySignal or sellSignal) ? real_close : na

plotchar(entryDot, "Entry Real Close (Tiny)",  location=location.absolute, color=realDotColor, char="•", size=size.tiny,  display=showTiny)
plotchar(entryDot, "Entry Real Close (Auto)",  location=location.absolute, color=realDotColor, char="•", size=size.auto,  display=showAuto)
plotshape(entryDot, "Entry Real Close (Small)", location=location.absolute, color=realDotColor, style=shape.circle, size=size.small, display=showSmall)

// ═══════════════════════════════════════════════════════════════
// ALERTS
// ═══════════════════════════════════════════════════════════════
alertcondition(buySignal,  "Buy Signal",  "Buy Signal – New trade opened")
alertcondition(sellSignal, "Sell Signal", "Sell Signal – New trade opened")