//@version=5
indicator("Multi-Timeframe Smart Bias & Entry Tool", overlay=true, max_bars_back=500)

// === INPUTS ===
// EMAs
ema_short_len = input.int(9, "EMA Short Length")
ema_mid_len = input.int(21, "EMA Mid Length")
ema_long_len = input.int(50, "EMA Long Length")

// Timeframes
tf1 = input.timeframe("60", "Higher TF for Bias") // H1
tf2 = input.timeframe("15", "Lower TF for Bias") // 15M

// ATR for SL and sweep detection
atr_len = input.int(14, "ATR Length")
atr_mult_sl = input.float(1.5, "SL ATR Multiplier")
atr_mult_pullback = input.float(1.0, "Pullback Threshold (ATR)")

// Risk-Reward Ratio for target line
rr_ratio = input.float(2.0, "Risk Reward Ratio", step=0.1)

// FVG option
enable_fvg = input.bool(true, "Enable Fair Value Gap (FVG)")

// ==== FUNCTIONS ====

// Compute EMAs for a given source and timeframe
f_ema_tf(src, len, timeframe) =>
    request.security(syminfo.tickerid, timeframe, ta.ema(src, len), gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_on)

// Determine bias from 3 EMAs (9,21,50) for given timeframe EMAs
f_check_bias(ema9v, ema21v, ema50v) =>
    bullish = ema9v > ema21v and ema21v > ema50v
    bearish = ema9v < ema21v and ema21v < ema50v
    bullish ? "Bullish" : bearish ? "Bearish" : "Neutral"

// === Multi TF EMAs ===
ema9_h1 = f_ema_tf(close, ema_short_len, tf1)
ema21_h1 = f_ema_tf(close, ema_mid_len, tf1)
ema50_h1 = f_ema_tf(close, ema_long_len, tf1)

ema9_15 = f_ema_tf(close, ema_short_len, tf2)
ema21_15 = f_ema_tf(close, ema_mid_len, tf2)
ema50_15 = f_ema_tf(close, ema_long_len, tf2)

bias_h1 = f_check_bias(ema9_h1, ema21_h1, ema50_h1)
bias_15 = f_check_bias(ema9_15, ema21_15, ema50_15)

// Background color or bias label color
bias_color = bias_h1 == "Bullish" and bias_15 == "Bullish" ? color.new(color.green, 85) :
    bias_h1 == "Bearish" and bias_15 == "Bearish" ? color.new(color.red, 85) :
    color.new(color.gray, 85)

// === Plot bias box top-left with background color

var string bias_text = ""
bias_text := "Bias H1: " + bias_h1 + "\nBias 15M: " + bias_15

var label bias_label = na
if barstate.islast
    if na(bias_label)
        bias_label := label.new(bar_index, high * 1.02, bias_text, xloc=xloc.bar_index, yloc=yloc.price, style=label.style_label_left, color=bias_color, textcolor=color.white, size=size.small)
    else
        label.set_xy(bias_label, bar_index, high * 1.02)
        label.set_text(bias_label, bias_text)
        label.set_color(bias_label, bias_color)
        label.set_textcolor(bias_label, color.white)

// === ATR calculation for SL & Pullback zone
atr = ta.atr(atr_len)

// === Swing High/Low Detection based on pivots (depth=5 recommended)
pivot_len = 5

pivot_high = ta.pivothigh(high, pivot_len, pivot_len)
pivot_low = ta.pivotlow(low, pivot_len, pivot_len)

var float last_pivot_high = na
var float last_pivot_low = na
var int last_pivot_high_idx = na
var int last_pivot_low_idx = na

if not na(pivot_high)
    last_pivot_high := pivot_high
    last_pivot_high_idx := bar_index - pivot_len
if not na(pivot_low)
    last_pivot_low := pivot_low
    last_pivot_low_idx := bar_index - pivot_len

// === CHoCH & BOS logic (Change of Character & Break of Structure)
// CHoCH: Break of last swing low (bullish) or last swing high (bearish) in the opposite direction 
// Simplify: CHoCH up -> price closes above previous pivot high
// CHoCH down -> price closes below previous pivot low

var string last_choch = "None"
var int last_choch_bar = na

// Track structure highs and lows for BOS/CHoCH
var float structure_high = na
var float structure_low = na
var int structure_dir = 0 // 1 bullish structure, -1 bearish structure

// On every bar detect CHoCH or BOS
if (not na(last_pivot_high) and close > last_pivot_high)
    last_choch := "Up"
    last_choch_bar := bar_index
    structure_dir := 1
    structure_high := last_pivot_high
if (not na(last_pivot_low) and close < last_pivot_low)
    last_choch := "Down"
    last_choch_bar := bar_index
    structure_dir := -1
    structure_low := last_pivot_low

// BOS labels with arrows
if (not na(last_pivot_high) and close > last_pivot_high)
    label.new(bar_index, high, "BOS ↑", color=color.green, style=label.style_label_up, textcolor=color.white, size=size.tiny)
if (not na(last_pivot_low) and close < last_pivot_low)
    label.new(bar_index, low, "BOS ↓", color=color.red, style=label.style_label_down, textcolor=color.white, size=size.tiny)

// CHoCH labels with arrows
if last_choch_bar == bar_index
    if last_choch == "Up"
        label.new(bar_index, low - atr * 0.5, "CHoCH ↑", color=color.lime, style=label.style_label_left, textcolor=color.black, size=size.small)
    else if last_choch == "Down"
        label.new(bar_index, high + atr * 0.5, "CHoCH ↓", color=color.orange, style=label.style_label_left, textcolor=color.black, size=size.small)

// === Swing markers plot ===
plotshape(not na(pivot_high), style=shape.triangledown, location=location.abovebar, color=color.red, size=size.tiny, title="Swing High")
plotshape(not na(pivot_low), style=shape.triangleup, location=location.belowbar, color=color.green, size=size.tiny, title="Swing Low")

// === Pullback checker: price near EMA21 or EMA50 (within atr_mult_pullback ATR range)
price_near_ema21 = math.abs(close - ema21_15) <= (atr * atr_mult_pullback)
price_near_ema50 = math.abs(close - ema50_15) <= (atr * atr_mult_pullback)
pullback_to_ema = price_near_ema21 or price_near_ema50

// === Price sweeps recent swing
// Sweep low = price breaks below recent swing low (last_pivot_low) before pullback/entry
// Sweep high = price breaks above recent swing high (last_pivot_high)
// We'll check if recent lows/highs are broken in the right direction

sweep_bull = (last_pivot_low != na) and (low < last_pivot_low)
sweep_bear = (last_pivot_high != na) and (high > last_pivot_high)

// === Bias Match
bias_match_bull = (bias_h1 == "Bullish" and bias_15 == "Bullish")
bias_match_bear = (bias_h1 == "Bearish" and bias_15 == "Bearish")
bias_match = bias_match_bull or bias_match_bear

// === Entry signals ===
// Conditions:
// - both biases match (bull or bear)
// - CHoCH in that direction
// - price sweeps recent high (for bear/sell) or low (for bull/buy)
// - pullback to EMA21 or EMA50

// Buy Signal
buy_signal = bias_match_bull and last_choch == "Up" and sweep_bull and pullback_to_ema

// Sell Signal
sell_signal = bias_match_bear and last_choch == "Down" and sweep_bear and pullback_to_ema

// === Plot Buy/Sell arrows ===
plotshape(buy_signal, title="Buy Signal", location=location.belowbar, color=color.green, style=shape.labelup, size=size.large, text="BUY")
plotshape(sell_signal, title="Sell Signal", location=location.abovebar, color=color.red, style=shape.labeldown, size=size.large, text="SELL")

// === Stop Loss Helper Line ===
// Stop loss for buy: below last swing low minus ATR buffer
// Stop loss for sell: above last swing high plus ATR buffer

var line sl_line = na

if buy_signal
    sl_price = last_pivot_low != na ? last_pivot_low - atr * atr_mult_sl : na
    if not na(sl_price)
        line.delete(sl_line)
        sl_line := line.new(bar_index, sl_price, bar_index + 10, sl_price, color=color.green, width=2, extend=extend.right, style=line.style_dashed)

if sell_signal
    sl_price = last_pivot_high != na ? last_pivot_high + atr * atr_mult_sl : na
    if not na(sl_price)
        line.delete(sl_line)
        sl_line := line.new(bar_index, sl_price, bar_index + 10, sl_price, color=color.red, width=2, extend=extend.right, style=line.style_dashed)

// === RR Projection Line (Target) ===
// Target for Buy = Entry + (Entry - SL) * RR
// Target for Sell = Entry - (SL - Entry) * RR
var line tp_line = na

if buy_signal and not na(sl_price)
    entry_price = close
    tp_price = entry_price + (entry_price - sl_price) * rr_ratio
    line.delete(tp_line)
    tp_line := line.new(bar_index, tp_price, bar_index + 10, tp_price, color=color.lime, width=2, extend=extend.right, style=line.style_dotted)

if sell_signal and not na(sl_price)
    entry_price = close
    tp_price = entry_price - (sl_price - entry_price) * rr_ratio
    line.delete(tp_line)
    tp_line := line.new(bar_index, tp_price, bar_index + 10, tp_price, color=color.orange, width=2, extend=extend.right, style=line.style_dotted)

// === Fair Value Gap (FVG) Detection ===
// Basic FVG: Look for candles where gap exists between candles' bodies (e.g. bullish gap up: low of current > high of prev one)
// We detect and draw rectangles for FVG zones

var array<int> fvg_bars = array.new_int()
var array<float> fvg_top = array.new_float()
var array<float> fvg_bot = array.new_float()

if enable_fvg
    // Up gap FVG
    cond_bull_gap = low > high[1]
    // Down gap FVG
    cond_bear_gap = high < low[1]

    if cond_bull_gap
        // Plot bullish FVG zone between high[1] and low
        box.new(bar_index-1, high[1], bar_index, low, bgcolor=color.new(color.green, 85), border_color=color.green)
    if cond_bear_gap
        // Plot bearish FVG zone between low[1] and high
        box.new(bar_index-1, low[1], bar_index, high, bgcolor=color.new(color.red, 85), border_color=color.red)

// ==== Entry Panel ====
// Show live data in corner of chart

var label entry_panel = na
panel_text = str.format("Bias H1: {0}\nBias 15M: {1}\nLast CHoCH: {2}\nSignal: {3}\nSL: {4}\nTP: {5}",
     bias_h1, bias_15, last_choch,
     buy_signal ? "BUY triggered" : sell_signal ? "SELL triggered" : "None",
     buy_signal ? str.tostring(last_pivot_low - atr * atr_mult_sl, "#.####") : sell_signal ? str.tostring(last_pivot_high + atr * atr_mult_sl, "#.####") : "N/A",
     buy_signal ? str.tostring(close + (close - (last_pivot_low - atr * atr_mult_sl)) * rr_ratio, "#.####") : sell_signal ? str.tostring(close - ((last_pivot_high + atr * atr_mult_sl) - close) * rr_ratio, "#.####") : "N/A"
)

if barstate.islast
    if na(entry_panel)
        entry_panel := label.new(bar_index, low * 0.98, panel_text, xloc=xloc.bar_index, yloc=yloc.price, style=label.style_label_left, color=color.new(color.blue, 90), textcolor=color.white, size=size.small)
    else
        label.set_xy(entry_panel, bar_index, low * 0.98)
        label.set_text(entry_panel, panel_text)

// === ALERTS ===

alertcondition(buy_signal, "Buy Setup", "Buy setup detected at {{close}} (Bias: Bullish)")
alertcondition(sell_signal, "Sell Setup", "Sell setup detected at {{close}} (Bias: Bearish)")

// === END ===