은행 지수 양방향 모멘텀 전략

KERNEL VWAP CVD RSI ATR
생성 날짜: 2025-10-29 15:57:59 마지막으로 수정됨: 2025-10-29 15:57:59
복사: 0 클릭수: 151
avatar of ianzeng123 ianzeng123
2
집중하다
319
수행원

은행 지수 양방향 모멘텀 전략 은행 지수 양방향 모멘텀 전략

, 이 전략은 뭐하는 거야?

이 전략은 매우 유연한 ‘변형 용 거래자’와 같습니다. 고정된 한방향 전략과는 달리 시장 상황에 따라 언제든지 다방향으로 전환할 수 있습니다.

핵심 기술 지표 포트폴리오

이 전략은 5개의 강력한 지표의 조합을 이용합니다.

  • 커널 복귀GPS 탐색처럼 가격 동향을 가리키고 있습니다.
  • VWAP거래량과 중화평균값이 ‘큰 돈’이 어디에 있는지 알려줍니다.
  • CVD의 누적적 차이는이 사진들은 “상거래의 힘”이라는 제목의 사진입니다.
  • RSI의 비교적 강한 지표이 사진의 제목은 “미국에서 가장 높은 지점에서 추격하는 것을 막기 위한 방법”입니다.
  • ATR 실제 변동 범위시장의 변동에 적응하기 위해 역동적으로 손실을 조정합니다.

다중 인증 메커니즘

이 전략의 가장 똑똑한 부분은 ‘다중 확인’입니다. 마치 집을 구입할 때 지점, 주택 유형, 가격에 대해 확인하는 것과 같습니다. 이 전략은 다음과 같이 요구합니다.

  • 트렌드 방향 일치 ((Kernel + VWAP 이중 확인)
  • 성량 조합 ((양량 또는 연속 증량)
  • K선형 전환 ((개체가 60% 이상, 크로스스타 트랩을 피한다)
  • 시간 창 제한 (오픈 디스크와 클로즈 디스크의 혼란을 피하기 위해 9:15-15:15 사이에 거래만)

️ 지능형 위험 관리

이 전략은 교과서 수준의 위험 관리입니다!

  • 동적 정지ATR, 고정 점수, 흔들리는 고저점 세 가지 모드
  • 의 분배1위 목표: 50%의 지분을 매각하고 수익을 내기
  • 손실 추적이윤이 나간 후 자동으로 상환해 수익을 잠금합니다.
  • 매일 제한“이번 거래는 최대 5건, 2번 연속으로 손실을 입었다”
  • 자금 관리거래 당 1% 이하의 리스크를 제어합니다.

실전 응용 제안

이 전략이 잘 작동하려면 다음의 몇 가지를 기억하세요:

  1. 가장 좋은 주기는5분 차트는 가장 좋은 성능으로, 단기적인 변동도 잘 포착할 수 있고, 너무 자주 나타나지 않습니다.
  2. 적용 가능한 품종: 은행 지수 선물용으로 설계되었지만 다른 지수들도 참고할 수 있습니다.
  3. 거래 시간인도 시간 9시 15분부터 15시 15분까지, 디스크 앞의 두 K선에서 발생하는 소음을 피하십시오.
  4. 위치 제어한 손당 25~75개의 계약, 최대 200개 이상의 계약이 권장됩니다.

이 전략의 가장 큰 특징은 “공격할 수 있고, 후퇴할 수 있다”는 것인데, 이 전략은 황소 시장이 더 많은 것을 할 수 있고, 곰 시장이 빈 것을 할 수 있고, 흔들림 시장도 두려워하지 않는다! 마치 스위스 군 칼처럼, 어떤 상황에도 대처할 수 있다.

전략 소스 코드
/*backtest
start: 2025-01-01 00:00:00
end: 2025-10-28 00:00:00
period: 10m
basePeriod: 10m
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT"}]
*/

//@version=5
strategy("BankNifty LONG & SHORT Multi-Confluence", 
     shorttitle="BN Long-Short",
     overlay=true)

// ═══════════════════════════════════════════════════════════
// 📊 STRATEGY INFO
// ═══════════════════════════════════════════════════════════
// Timeframe: 5-MINUTE CHART (Recommended)
// Direction: LONG & SHORT (Both directions)
// Asset: BankNifty Futures
// Style: Intraday Momentum Trading
// ═══════════════════════════════════════════════════════════

// ═══════════════════════════════════════════════════════════
// ⚙️ INPUT PARAMETERS
// ═══════════════════════════════════════════════════════════

// Trading Direction
tradingDirection = input.string("Both", "Trading Direction", 
     options=["Long Only", "Short Only", "Both"], group="🎯 Strategy Type")

// Trading Hours (IST)
startHour = input.int(9, "Start Hour", minval=0, maxval=23, group="⏰ Trading Session")
startMinute = input.int(15, "Start Minute", minval=0, maxval=59, group="⏰ Trading Session")
endHour = input.int(15, "End Hour", minval=0, maxval=23, group="⏰ Trading Session")
endMinute = input.int(15, "End Minute", minval=0, maxval=59, group="⏰ Trading Session")
avoidFirstCandles = input.int(2, "Skip First N Candles", minval=0, maxval=10, group="⏰ Trading Session")

// Position Sizing
lotSize = input.int(25, "Lot Size", minval=15, maxval=75, group="💰 Position Management")
maxContracts = input.int(75, "Max Contracts", minval=25, maxval=200, step=25, group="💰 Position Management")
riskPercent = input.float(1.0, "Risk % Per Trade", minval=0.5, maxval=3.0, step=0.1, group="💰 Position Management")

// Kernel Regression
kernelLength = input.int(20, "Kernel Length", minval=10, maxval=50, group="📈 Indicators")

// VWAP
vwapSource = input.string("Session", "VWAP Type", options=["Session", "Rolling"], group="📈 Indicators")
vwapRollingLength = input.int(20, "Rolling VWAP Length", minval=10, maxval=100, group="📈 Indicators")

// Volume
volumeLength = input.int(20, "Volume MA Length", minval=10, maxval=50, group="📊 Volume")
volumeMultiplier = input.float(1.5, "Volume Spike", minval=1.1, maxval=3.0, step=0.1, group="📊 Volume")

// CVD
useCVD = input.bool(true, "Use CVD Filter", group="📊 Volume")
cvdLength = input.int(10, "CVD Smoothing", minval=5, maxval=30, group="📊 Volume")

// RSI
useRSI = input.bool(true, "Use RSI Filter", group="🎯 Filters")
rsiLength = input.int(14, "RSI Length", minval=7, maxval=30, group="🎯 Filters")
rsiOverbought = input.int(70, "RSI Overbought", minval=60, maxval=85, group="🎯 Filters")
rsiOversold = input.int(30, "RSI Oversold", minval=15, maxval=40, group="🎯 Filters")

// Price Action
candleBodyPercent = input.float(0.6, "Min Body %", minval=0.4, maxval=0.9, step=0.05, group="🎯 Filters")
minCandlePoints = input.float(20.0, "Min Candle Size", minval=10.0, maxval=100.0, step=5.0, group="🎯 Filters")

// Stop Loss
stopLossType = input.string("ATR", "Stop Loss Type", options=["Fixed", "ATR", "Swing"], group="🛡️ Exits")
atrLength = input.int(14, "ATR Length", minval=7, maxval=30, group="🛡️ Exits")
atrMultiplier = input.float(1.5, "ATR Multiplier", minval=0.5, maxval=3.0, step=0.1, group="🛡️ Exits")
fixedStopLoss = input.float(50.0, "Fixed SL (points)", minval=20.0, maxval=200.0, step=10.0, group="🛡️ Exits")
swingLookback = input.int(10, "Swing Lookback", minval=5, maxval=30, group="🛡️ Exits")

// Targets
rrTarget1 = input.float(1.5, "Target 1 R:R", minval=0.5, maxval=3.0, step=0.1, group="🛡️ Exits")
rrTarget2 = input.float(2.5, "Target 2 R:R", minval=1.0, maxval=5.0, step=0.5, group="🛡️ Exits")
partialExitPercent = input.int(50, "Partial Exit %", minval=25, maxval=75, step=5, group="🛡️ Exits")

// Trailing
useTrailing = input.bool(true, "Trailing Stop", group="🛡️ Exits")
trailActivationRR = input.float(1.2, "Activate at R:R", minval=0.5, maxval=2.0, step=0.1, group="🛡️ Exits")
trailStopRR = input.float(0.8, "Trail Stop R:R", minval=0.3, maxval=1.5, step=0.1, group="🛡️ Exits")

// Risk Management
maxTradesPerDay = input.int(5, "Max Trades/Day", minval=1, maxval=20, group="🚨 Risk")
maxConsecutiveLosses = input.int(2, "Stop After Losses", minval=1, maxval=5, group="🚨 Risk")
dailyLossLimit = input.float(2.5, "Daily Loss %", minval=1.0, maxval=10.0, step=0.5, group="🚨 Risk")
dailyProfitTarget = input.float(5.0, "Daily Target %", minval=2.0, maxval=20.0, step=0.5, group="🚨 Risk")

// ═══════════════════════════════════════════════════════════
// 📊 INDICATORS
// ═══════════════════════════════════════════════════════════

// Kernel Regression
kernel = ta.linreg(close, kernelLength, 0)
kernelSlope = kernel - kernel[1]

// VWAP
vwapValue = vwapSource == "Session" ? ta.vwap(close) : ta.sma(hlc3, vwapRollingLength)
vwapSlope = vwapValue - vwapValue[1]

// Volume
volumeMA = ta.sma(volume, volumeLength)
highVolume = volume > volumeMA * volumeMultiplier
volumeIncreasing = volume > volume[1] and volume[1] > volume[2]

// CVD
buyVolume = close > open ? volume : 0
sellVolume = close < open ? volume : 0
volumeDelta = buyVolume - sellVolume
cvd = ta.cum(volumeDelta)
cvdMA = ta.sma(cvd, cvdLength)

// RSI
rsi = ta.rsi(close, rsiLength)

// ATR
atr = ta.atr(atrLength)

// Price Action
candleRange = high - low
candleBody = math.abs(close - open)
bodyPercent = candleRange > 0 ? candleBody / candleRange : 0

// Swing High/Low
swingHigh = ta.highest(high, swingLookback)
swingLow = ta.lowest(low, swingLookback)

// ═══════════════════════════════════════════════════════════
// ⏰ TIME MANAGEMENT
// ═══════════════════════════════════════════════════════════

currentMinutes = hour * 60 + minute
startMinutes = startHour * 60 + startMinute
endMinutes = endHour * 60 + endMinute
tradingTime = currentMinutes >= startMinutes and currentMinutes <= endMinutes

var int barsToday = 0
newDay = ta.change(dayofweek)
if newDay
    barsToday := 0
if tradingTime
    barsToday += 1

skipInitialBars = barsToday <= avoidFirstCandles
forceExitTime = currentMinutes >= (endMinutes - 5)

// ═══════════════════════════════════════════════════════════
// 💼 POSITION TRACKING
// ═══════════════════════════════════════════════════════════

var int todayTrades = 0
var int consecutiveLosses = 0
var float todayPnL = 0.0
var float entryPrice = na
var int entryBar = 0
var float stopLoss = na
var float target1 = na
var float target2 = na
var float trailStop = na
var bool target1Hit = false
var bool trailActive = false

// Daily Reset
if newDay
    todayTrades := 0
    consecutiveLosses := 0
    todayPnL := 0.0

// Track P&L
if strategy.closedtrades > 0 and strategy.closedtrades != strategy.closedtrades[1]
    lastPnL = strategy.closedtrades.profit(strategy.closedtrades - 1)
    todayPnL += lastPnL
    if lastPnL < 0
        consecutiveLosses += 1
    else
        consecutiveLosses := 0

// Risk Limits
lossLimit = strategy.initial_capital * (dailyLossLimit / 100)
profitTarget = strategy.initial_capital * (dailyProfitTarget / 100)
hitDailyTarget = todayPnL >= profitTarget
hitLossLimit = math.abs(todayPnL) >= lossLimit

canTrade = todayTrades < maxTradesPerDay and
     consecutiveLosses < maxConsecutiveLosses and
     not hitDailyTarget and
     not hitLossLimit and
     tradingTime and
     not skipInitialBars and
     strategy.position_size == 0

// ═══════════════════════════════════════════════════════════
// 🎯 LONG ENTRY CONDITIONS
// ═══════════════════════════════════════════════════════════

// Bullish Trend
longTrend = kernelSlope > 0 and close > kernel
longVWAP = close > vwapValue and vwapSlope > 0
longCVD = useCVD ? cvd > cvdMA and ta.rising(cvd, 2) : true
longRSI = useRSI ? rsi > rsiOversold and rsi < rsiOverbought : true
longVolume = highVolume or volumeIncreasing
longCandle = close > open and bodyPercent >= candleBodyPercent and 
     candleRange >= minCandlePoints and close >= (high - (candleRange * 0.25))

longSignal = longTrend and longVWAP and longCVD and longRSI and longVolume and longCandle and canTrade and
     (tradingDirection == "Long Only" or tradingDirection == "Both")

// ═══════════════════════════════════════════════════════════
// 🎯 SHORT ENTRY CONDITIONS
// ═══════════════════════════════════════════════════════════

// Bearish Trend
shortTrend = kernelSlope < 0 and close < kernel
shortVWAP = close < vwapValue and vwapSlope < 0
shortCVD = useCVD ? cvd < cvdMA and ta.falling(cvd, 2) : true
shortRSI = useRSI ? rsi < rsiOverbought and rsi > rsiOversold : true
shortVolume = highVolume or volumeIncreasing
shortCandle = close < open and bodyPercent >= candleBodyPercent and 
     candleRange >= minCandlePoints and close <= (low + (candleRange * 0.25))

shortSignal = shortTrend and shortVWAP and shortCVD and shortRSI and shortVolume and shortCandle and canTrade and
     (tradingDirection == "Short Only" or tradingDirection == "Both")

// ═══════════════════════════════════════════════════════════
// 💰 POSITION SIZING
// ═══════════════════════════════════════════════════════════

calcPositionSize(stopDistance) =>
    riskAmount = strategy.initial_capital * (riskPercent / 100)
    contracts = math.floor(riskAmount / stopDistance)
    math.min(contracts, maxContracts)

// ═══════════════════════════════════════════════════════════
// 📥 LONG ENTRY
// ═══════════════════════════════════════════════════════════

if longSignal
    entryPrice := close
    entryBar := bar_index
    
    // Calculate Stop Loss
    if stopLossType == "ATR"
        stopLoss := close - (atr * atrMultiplier)
    else if stopLossType == "Swing"
        stopLoss := math.min(swingLow, close - (atr * atrMultiplier))
    else
        stopLoss := close - fixedStopLoss
    
    risk = entryPrice - stopLoss
    target1 := entryPrice + (risk * rrTarget1)
    target2 := entryPrice + (risk * rrTarget2)
    
    qty = calcPositionSize(risk)
    
    trailStop := stopLoss
    target1Hit := false
    trailActive := false
    todayTrades += 1
    
    strategy.entry("LONG", strategy.long, qty=qty, comment="Long-" + str.tostring(todayTrades))

// ═══════════════════════════════════════════════════════════
// 📥 SHORT ENTRY
// ═══════════════════════════════════════════════════════════

if shortSignal
    entryPrice := close
    entryBar := bar_index
    
    // Calculate Stop Loss
    if stopLossType == "ATR"
        stopLoss := close + (atr * atrMultiplier)
    else if stopLossType == "Swing"
        stopLoss := math.max(swingHigh, close + (atr * atrMultiplier))
    else
        stopLoss := close + fixedStopLoss
    
    risk = stopLoss - entryPrice
    target1 := entryPrice - (risk * rrTarget1)
    target2 := entryPrice - (risk * rrTarget2)
    
    qty = calcPositionSize(risk)
    
    trailStop := stopLoss
    target1Hit := false
    trailActive := false
    todayTrades += 1
    
    strategy.entry("SHORT", strategy.short, qty=qty, comment="Short-" + str.tostring(todayTrades))

// ═══════════════════════════════════════════════════════════
// 🏃 TRAILING STOP (LONG)
// ═══════════════════════════════════════════════════════════

if strategy.position_size > 0 and useTrailing
    unrealizedProfit = close - entryPrice
    risk = entryPrice - stopLoss
    
    if unrealizedProfit >= (risk * trailActivationRR) and not trailActive
        trailActive := true
        trailStop := close - (risk * trailStopRR)
    
    if trailActive
        newTrailStop = close - (risk * trailStopRR)
        if newTrailStop > trailStop
            trailStop := newTrailStop

// ═══════════════════════════════════════════════════════════
// 🏃 TRAILING STOP (SHORT)
// ═══════════════════════════════════════════════════════════

if strategy.position_size < 0 and useTrailing
    unrealizedProfit = entryPrice - close
    risk = stopLoss - entryPrice
    
    if unrealizedProfit >= (risk * trailActivationRR) and not trailActive
        trailActive := true
        trailStop := close + (risk * trailStopRR)
    
    if trailActive
        newTrailStop = close + (risk * trailStopRR)
        if newTrailStop < trailStop
            trailStop := newTrailStop

// ═══════════════════════════════════════════════════════════
// 📤 LONG EXIT
// ═══════════════════════════════════════════════════════════

if strategy.position_size > 0
    activeSL = trailActive and useTrailing ? trailStop : stopLoss
    
    // Stop Loss
    if low <= activeSL
        strategy.close("LONG", comment="Long-SL")
    
    // Target 1
    else if high >= target1 and not target1Hit
        exitQty = math.floor(strategy.position_size * (partialExitPercent / 100))
        strategy.close("LONG", qty=exitQty, comment="Long-T1")
        target1Hit := true
        stopLoss := entryPrice
    
    // Target 2
    else if high >= target2
        strategy.close("LONG", comment="Long-T2")
    
    // Force Exit
    else if forceExitTime
        strategy.close("LONG", comment="Long-EOD")

// ═══════════════════════════════════════════════════════════
// 📤 SHORT EXIT
// ═══════════════════════════════════════════════════════════

if strategy.position_size < 0
    activeSL = trailActive and useTrailing ? trailStop : stopLoss
    
    // Stop Loss
    if high >= activeSL
        strategy.close("SHORT", comment="Short-SL")
    
    // Target 1
    else if low <= target1 and not target1Hit
        exitQty = math.floor(math.abs(strategy.position_size) * (partialExitPercent / 100))
        strategy.close("SHORT", qty=exitQty, comment="Short-T1")
        target1Hit := true
        stopLoss := entryPrice
    
    // Target 2
    else if low <= target2
        strategy.close("SHORT", comment="Short-T2")
    
    // Force Exit
    else if forceExitTime
        strategy.close("SHORT", comment="Short-EOD")

// ═══════════════════════════════════════════════════════════
// ⚠️ REVERSAL EXIT
// ═══════════════════════════════════════════════════════════

longReversal = strategy.position_size > 0 and (close < kernel and kernelSlope < 0 or close < vwapValue)
shortReversal = strategy.position_size < 0 and (close > kernel and kernelSlope > 0 or close > vwapValue)

if longReversal and target1Hit
    strategy.close("LONG", comment="Long-Rev")
if shortReversal and target1Hit
    strategy.close("SHORT", comment="Short-Rev")

// ═══════════════════════════════════════════════════════════
// 🎨 VISUALIZATION
// ═══════════════════════════════════════════════════════════

// Indicators
plot(kernel, "Kernel", color=color.new(color.purple, 0), linewidth=2)
plot(vwapValue, "VWAP", color=color.new(color.orange, 0), linewidth=2)

kernelUpper = kernel + atr
kernelLower = kernel - atr
plot(kernelUpper, "K Upper", color=color.new(color.purple, 70), linewidth=1, style=plot.style_circles)
plot(kernelLower, "K Lower", color=color.new(color.purple, 70), linewidth=1, style=plot.style_circles)

// Signals
plotshape(longSignal, "LONG", shape.triangleup, location.belowbar, 
     color=color.new(color.lime, 0), size=size.normal, text="LONG")
plotshape(shortSignal, "SHORT", shape.triangledown, location.abovebar, 
     color=color.new(color.red, 0), size=size.normal, text="SHORT")

// Position Levels
posColor = strategy.position_size > 0 ? color.blue : strategy.position_size < 0 ? color.orange : na
plot(strategy.position_size != 0 ? entryPrice : na, "Entry", 
     color=color.new(posColor, 0), style=plot.style_linebr, linewidth=2)

slColor = strategy.position_size > 0 ? color.red : color.fuchsia
plot(strategy.position_size != 0 ? stopLoss : na, "SL", 
     color=color.new(slColor, 0), style=plot.style_linebr, linewidth=2)

t1Color = strategy.position_size > 0 ? color.green : color.aqua
plot(strategy.position_size != 0 ? target1 : na, "T1", 
     color=color.new(t1Color, 0), style=plot.style_linebr, linewidth=1)
plot(strategy.position_size != 0 ? target2 : na, "T2", 
     color=color.new(t1Color, 0), style=plot.style_linebr, linewidth=2)

plot(strategy.position_size != 0 and trailActive ? trailStop : na, "Trail", 
     color=color.new(color.yellow, 0), style=plot.style_linebr, linewidth=2)

// Volume
barcolor(highVolume ? color.new(color.blue, 70) : na)

// Background
bgcolor(tradingTime ? color.new(color.green, 98) : color.new(color.gray, 95))
bgcolor(strategy.position_size > 0 ? color.new(color.blue, 97) : 
     strategy.position_size < 0 ? color.new(color.orange, 97) : na)
bgcolor(hitDailyTarget ? color.new(color.green, 92) : hitLossLimit ? color.new(color.red, 92) : na)

// ═══════════════════════════════════════════════════════════
// 📊 STATS TABLE
// ═══════════════════════════════════════════════════════════

var table stats = table.new(position.top_right, 2, 11, border_width=1)

if barstate.islast
    // Header
    table.cell(stats, 0, 0, "📊 LONG & SHORT", bgcolor=color.blue, text_color=color.white, text_size=size.small)
    table.cell(stats, 1, 0, "STRATEGY", bgcolor=color.blue, text_color=color.white, text_size=size.small)
    
    // Trades
    table.cell(stats, 0, 1, "Trades", text_size=size.small)
    table.cell(stats, 1, 1, str.tostring(todayTrades) + "/" + str.tostring(maxTradesPerDay), 
         bgcolor=todayTrades >= maxTradesPerDay ? color.red : color.green, text_color=color.white, text_size=size.small)
    
    // Losses
    table.cell(stats, 0, 2, "Losses", text_size=size.small)
    table.cell(stats, 1, 2, str.tostring(consecutiveLosses), 
         bgcolor=consecutiveLosses >= maxConsecutiveLosses ? color.red : color.green, 
         text_color=color.white, text_size=size.small)
    
    // P&L
    pnlPct = (todayPnL / strategy.initial_capital) * 100
    table.cell(stats, 0, 3, "P&L", text_size=size.small)
    table.cell(stats, 1, 3, str.tostring(todayPnL, "#,###") + "\n" + str.tostring(pnlPct, "#.##") + "%", 
         bgcolor=todayPnL > 0 ? color.green : todayPnL < 0 ? color.red : color.gray, 
         text_color=color.white, text_size=size.small)
    
    // Position
    table.cell(stats, 0, 4, "Position", text_size=size.small)
    posText = strategy.position_size > 0 ? "LONG\n" + str.tostring(strategy.position_size) : 
         strategy.position_size < 0 ? "SHORT\n" + str.tostring(math.abs(strategy.position_size)) : "FLAT"
    posBg = strategy.position_size > 0 ? color.blue : strategy.position_size < 0 ? color.orange : color.gray
    table.cell(stats, 1, 4, posText, bgcolor=posBg, text_color=color.white, text_size=size.small)
    
    if strategy.position_size != 0
        // Entry
        table.cell(stats, 0, 5, "Entry", text_size=size.small)
        table.cell(stats, 1, 5, str.tostring(entryPrice, "#.##"), text_size=size.small)
        
        // Live P&L
        livePnL = strategy.position_size > 0 ? (close - entryPrice) * strategy.position_size : 
             (entryPrice - close) * math.abs(strategy.position_size)
        livePct = strategy.position_size > 0 ? ((close - entryPrice) / entryPrice) * 100 : 
             ((entryPrice - close) / entryPrice) * 100
        table.cell(stats, 0, 6, "Live P&L", text_size=size.small)
        table.cell(stats, 1, 6, str.tostring(livePnL, "#,###") + "\n" + str.tostring(livePct, "#.##") + "%", 
             bgcolor=livePnL > 0 ? color.green : color.red, text_color=color.white, text_size=size.small)
        
        // Bars
        bars = bar_index - entryBar
        table.cell(stats, 0, 7, "Bars", text_size=size.small)
        table.cell(stats, 1, 7, str.tostring(bars), text_size=size.small)
        
        // Stop
        table.cell(stats, 0, 8, "Stop", text_size=size.small)
        table.cell(stats, 1, 8, str.tostring(trailActive ? trailStop : stopLoss, "#.##") + 
             (trailActive ? " 🟡" : ""), text_size=size.small)
        
        // T1
        table.cell(stats, 0, 9, "T1", text_size=size.small)
        table.cell(stats, 1, 9, str.tostring(target1, "#.##") + (target1Hit ? " ✓" : ""), text_size=size.small)
        
        // T2
        table.cell(stats, 0, 10, "T2", text_size=size.small)
        table.cell(stats, 1, 10, str.tostring(target2, "#.##"), text_size=size.small)

// ═══════════════════════════════════════════════════════════
// 🔔 ALERTS
// ═══════════════════════════════════════════════════════════

alertcondition(longSignal, "🟢 LONG SIGNAL", "LONG Entry Signal")
alertcondition(shortSignal, "🔴 SHORT SIGNAL", "SHORT Entry Signal")
alertcondition(longReversal or shortReversal, "⚠️ REVERSAL", "Reversal Detected")