Chiến lược tái chiếm đường EMA đa khung thời gian ưu tú


Ngày tạo: 2025-12-29 16:40:54 sửa đổi lần cuối: 2025-12-29 16:40:54
sao chép: 9 Số nhấp chuột: 207
2
tập trung vào
413
Người theo dõi

Chiến lược tái chiếm đường EMA đa khung thời gian ưu tú Chiến lược tái chiếm đường EMA đa khung thời gian ưu tú

EMA, MTF, ADX, ATR

Đây không phải là chiến lược EMA thông thường, mà là hệ thống bắn tỉa chính xác trong nhiều khung thời gian.

Lưu ý rằng các đường EMA toàn màn hình không làm cho mắt bạn bị ám ảnh. Logic cốt lõi của chiến lược EMA Reclaim của Elite MTF này đơn giản và thô sơ: chờ đợi giá rút khỏi hệ thống đường EMA, sau đó lấy lại đường trung bình quan trọng để tham gia chính xác. Nhưng quỷ dữ trong các chi tiết đã làm cho nó trở nên dễ dàng hơn bằng cách lọc nhiều khung thời gian, xác nhận cường độ xu hướng của ADX, dừng động ATR.

Dữ liệu phản hồi cho thấy rằng khi hoạt động trên chu kỳ 6 phút, chiến lược này đã lọc hiệu quả một lượng lớn các tín hiệu phá vỡ giả thông qua yêu cầu xếp chồng EMA nghiêm ngặt ((5>10>20>50) và cơ chế xác nhận phản hồi. Điều quan trọng là nó không làm quá nhiều, mà yêu cầu giá phải lùi trở lại đường EMA được chỉ định trước khi lấy lại.

Ba bộ cấu hình mặc định, tối ưu hóa bạo lực cho các thị trường khác nhau

Chiến lược cung cấp ba dự đoán Elite, Balanced và Aggressive, mỗi loại được tối ưu hóa sâu cho bốn thị trường Forex, XAUUSD, Crypto và Indices. Đây không phải là tham số đập đầu, mà là điều chỉnh chính xác dựa trên dữ liệu phản hồi lớn.

Ví dụ như thị trường ngoại hối:

  • Chế độ Elite: EMA20-50 chênh lệch giá tối thiểu là 0.06%, ADX≥14, ATR dừng lỗ 1,8 lần, tỷ lệ lợi nhuận rủi ro là 2:1
  • Chế độ cân bằng: Nới lỏng đến chênh lệch giá 0.045%, ADX≥12, dừng lỗ 1,6 lần, mục tiêu 1.75:1
  • Chế độ tích cực: mở rộng thêm đến 0.03%, ADX ≥ 10, dừng 1.4 lần, mục tiêu là 1.5:1

Các tham số của XAUUSD nghiêm ngặt hơn, mô hình Elite yêu cầu chênh lệch EMA đạt 0.09% và ADX≥16, vì đặc tính biến động của vàng cần xác nhận xu hướng mạnh hơn. Thị trường Crypto tương đối thoải mái, nhưng ATR Stop Loss đã tăng gấp 2.2 lần, thích nghi với môi trường biến động cao của tiền điện tử.

Bộ lọc nhiều khung thời gian là tính năng cạnh tranh cốt lõi của hệ thống

Chiến lược này đồng thời giám sát trạng thái xếp hàng EMA của đường hồng và biểu đồ 1 giờ, chỉ cho phép tín hiệu nhập vào cấp độ 6 phút khi xu hướng khung thời gian cao rõ ràng. Thiết kế này giải quyết trực tiếp các điểm đau lớn nhất của giao dịch chu kỳ nhỏ bị nhiễu bởi tiếng ồn tần số cao.

HTF cung cấp bốn tùy chọn: tắt, chỉ đường mặt trời, chỉ 1 giờ, đường mặt trời + 1 giờ. Trong chiến đấu thực tế, người ta khuyên nên sử dụng phương thức “đường mặt trời + 1 giờ”, mặc dù tần số tín hiệu sẽ giảm khoảng 30%, nhưng lợi nhuận sau khi điều chỉnh tỷ lệ thắng và rủi ro tăng lên rõ rệt.

Thiết kế này hoạt động đặc biệt tốt trong thị trường chấn động. Phản hồi cho thấy, sau khi thêm bộ lọc HTF, sự rút lui tối đa giảm khoảng 25%.

ADX + ATR có bộ lọc kép, từ chối chiến đấu trong bùn

Chiến lược yêu cầu ADX đạt mức giảm giá tối thiểu để cho phép giao dịch, điều này đảm bảo chỉ hoạt động trong môi trường có xu hướng rõ ràng. ATR cũng phải vượt quá một tỷ lệ phần trăm nhất định của giá, tránh tạo ra tín hiệu vô hiệu trong thời gian biến động cực thấp.

Sự kết hợp của hai bộ lọc này có hiệu quả đáng kinh ngạc: chiến lược dừng giao dịch hoàn toàn khi ADX <12 và ATR <0.1%. Dữ liệu lịch sử cho thấy thiết kế “tốt hơn là bỏ lỡ, không làm sai” này đã giúp chiến lược giảm hơn 70% giao dịch không hiệu quả trong thời gian biên soạn ngang.

Thiết kế ba giai đoạn của logic nhập cảnh, mỗi giai đoạn có tiêu chuẩn nghiêm ngặt

Chiến lược này có ba giai đoạn:

  1. Giai đoạn Pullback: Giá phải chạm vào đường EMA được chỉ định ((EMA10 mặc định)
  2. Giai đoạn thu hồi: Giá lấy lại đường EMA, có thể chọn xác nhận đóng cửa hoặc xác nhận đường K tiếp theo
  3. Giai đoạn RetestTrong 18 đường K, giá thử nghiệm đường EMA nhưng không bị phá vỡ.

Sự tinh tế của thiết kế này là nó yêu cầu giá thể hiện một mô hình “tháo gỡ - lấy lại - xác nhận” rõ ràng, chứ không phải là một sự phá vỡ trung bình đơn giản. Theo đánh giá, mặc dù số tín hiệu giảm khoảng 20% sau khi thêm yêu cầu Retest, nhưng lợi nhuận trên mỗi giao dịch trung bình tăng 35%.

ATR: Hệ thống dừng lỗ động để quản lý rủi ro thông minh

Chiến lược sử dụng 1,8 lần ATR làm khoảng cách dừng lỗ (phong cách Elite), điều này thích ứng tốt hơn với sự thay đổi của biến động thị trường so với điểm dừng cố định. Khi ATR mở rộng, khoảng cách dừng lỗ sẽ tự động nới lỏng; khi biến động thu hẹp, dừng lỗ sẽ thắt chặt, tối đa hóa lợi nhuận điều chỉnh rủi ro.

Các tính năng nâng cao bao gồm:

  • Lợi nhuận 1R sau khi di chuyển dừng lỗ đến điểm cân bằng lợi nhuận
  • Tiếp theo, bắt đầu ATR theo dõi dừng lỗ sau 1R lợi nhuận
  • Tỷ lệ lợi nhuận rủi ro động được điều chỉnh từ 1.5:1 đến 2:1

Dữ liệu thực tế cho thấy việc sử dụng ATR để dừng động tốt hơn so với dừng cố định khoảng 15%, đặc biệt là trong môi trường thị trường có biến động lớn.

Dẫn dắt về rủi ro nghiêm ngặt: Đây không phải là thánh cốc và cần phải được xử lý hợp lý

Chiến lược này hoạt động tốt trong thị trường có xu hướng rõ ràng, nhưng có thể gây ra tổn thất liên tục trong tình huống biến động. Theo lịch sử, tổn thất liên tục tối đa có thể đạt 5-7 giao dịch, đòi hỏi các nhà giao dịch phải có đủ khả năng chịu đựng tâm lý và khả năng quản lý tiền.

Chiến lược này hoạt động tốt nhất trong giai đoạn đầu và giữa khi xu hướng bắt đầu, dễ tạo ra tín hiệu giả ở cuối xu hướng và gần điểm biến. Phân tích kỹ thuật trong khung thời gian cao hơn được đề xuất để tránh đi theo tín hiệu một cách mù quáng gần mức hỗ trợ kháng cự rõ ràng.

Các hoạt động phản hồi trong quá khứ không thể đại diện cho lợi nhuận trong tương lai, thay đổi môi trường thị trường có thể ảnh hưởng đến hiệu quả của chiến lược.

Mã nguồn chiến lược
/*backtest
start: 2024-12-29 00:00:00
end: 2025-12-28 00:00:00
period: 1h
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT"}]
*/

// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © sledgeproduuctions

//@version=6
strategy(
     "Elite MTF EMA Reclaim — 6m (1:1 Signals + Full Presets + Global Signal Toggle) [NA-Safe]",
     overlay=true,
     pyramiding=0,
     initial_capital=10000,
     commission_type=strategy.commission.percent,
     commission_value=0.01,
     slippage=1,
     process_orders_on_close=true,
     calc_on_order_fills=true,
     max_labels_count=200,
     max_lines_count=200
)

//──────────────────────────────────────────────────────────────────────────────
// MODE + GLOBAL SIGNAL DISPLAY
//──────────────────────────────────────────────────────────────────────────────
mode = input.string("Strategy (Backtest)", "Mode", options=["Strategy (Backtest)","Indicator (Signals Only)"])
allowOrders = (mode == "Strategy (Backtest)")
showSignals = input.bool(true, "Show Signals (All Modes)")

//──────────────────────────────────────────────────────────────────────────────
// MARKET + PRESET
//──────────────────────────────────────────────────────────────────────────────
market = input.string("Forex", "Market", options=["Forex","XAUUSD","Crypto","Indices"])
preset = input.string("Elite", "Preset", options=["Elite","Balanced","Aggressive"])

// HTF selection (optimized + toggle)
tfH1  = input.string("60", "HTF2 TF (minutes)")
tfD   = input.string("D",  "HTF1 TF")
htfMode = input.string("D + H1", "HTF Alignment Mode", options=["Off","D only","H1 only","D + H1"])

// Base behavior toggles
strictStackIn   = input.bool(true, "Base: Require STRICT EMA stack (5>10>20>50)")
requireRetestIn = input.bool(true, "Base: Require Retest")

// Optional looseners
looserLTF          = input.bool(false, "Looser LTF Mode (more 6m signals)")
allowReclaimNoPull = input.bool(false, "Allow reclaim without prior Pullback state")

// Dynamic default handled via "Preset" option:
reclaimTimingDefault = input.string("Preset", "Reclaim Timing Default",
     options=["Preset","Reclaim close","Next bar confirmation"])

// EMAs
len5  = input.int(5,  "EMA 5",  minval=1)
len10 = input.int(10, "EMA 10", minval=1)
len20 = input.int(20, "EMA 20", minval=1)
len50 = input.int(50, "EMA 50", minval=1)

// Base thresholds (override knobs)
curvMinIn        = input.float(0.0, "Base: Min Curvature Threshold", step=0.00001)
minSpreadIn      = input.float(0.0006, "Base: Min EMA20-50 Spread (% of price)", step=0.0001)
adxLen           = input.int(14, "ADX Length", minval=1)
minAdxIn         = input.float(14.0, "Base: Min ADX", step=0.5)
atrLen           = input.int(14, "ATR Length", minval=1)
minAtrPctIn      = input.float(0.0010, "Base: Min ATR (% of price)", step=0.0001)
crossLookbackIn  = input.int(30, "Base: Block if EMA20/50 crossed within N bars", minval=1)

// Base entry mechanics (override knobs)
pullbackToIn      = input.string("EMA10", "Base: Pullback To", options=["EMA5","EMA10","EMA20"])
reclaimOnIn       = input.string("EMA10", "Base: Reclaim On", options=["EMA5","EMA10","EMA20"])
retestOnIn        = input.string("EMA10", "Base: Retest On", options=["EMA5","EMA10","EMA20"])
maxBarsToRetestIn = input.int(18, "Base: Max bars allowed for retest after reclaim", minval=1)

// Visuals
showEma     = input.bool(true, "Show EMAs")
showBlocks  = input.bool(true, "Show BLOCK markers")
useChopKill = input.bool(true, "Kill Chop")

//──────────────────────────────────────────────────────────────────────────────
// ATR STOP + RR TARGETS
//──────────────────────────────────────────────────────────────────────────────
riskGroup = "Risk (ATR Stops / RR Targets)"
useAtrRisk     = input.bool(true, "Use ATR Stop + RR Target", group=riskGroup)
atrStopMultIn  = input.float(1.8, "ATR Stop Multiplier", step=0.1, group=riskGroup)
rrTargetIn     = input.float(2.0, "RR Target (TP = risk*RR)", step=0.25, group=riskGroup)
useBreakeven   = input.bool(false, "Move stop to breakeven at +1R", group=riskGroup)
useTrailAfterR = input.bool(false, "Trail stop after +1R (ATR)", group=riskGroup)
trailAtrMult   = input.float(1.0, "Trail ATR Multiplier", step=0.1, group=riskGroup)

//──────────────────────────────────────────────────────────────────────────────
// EFFECTIVE PARAMS (start from base, then overwrite by market+preset)
//──────────────────────────────────────────────────────────────────────────────
float minSpread       = minSpreadIn
float minAtrPct       = minAtrPctIn
float minAdx          = minAdxIn
float curvMin         = curvMinIn
int   crossLookback   = crossLookbackIn
int   maxBarsToRetest = maxBarsToRetestIn
bool  strictStack     = strictStackIn
bool  requireRetest   = requireRetestIn
string pullbackTo     = pullbackToIn
string reclaimOn      = reclaimOnIn
string retestOn       = retestOnIn

float atrStopMult = atrStopMultIn
float rrTarget    = rrTargetIn

//──────────────────────────────────────────────────────────────────────────────
// PRESET RECLAIM TIMING (best defaults per market/preset)
//──────────────────────────────────────────────────────────────────────────────
string presetReclaimTiming = "Reclaim close"
if market == "Forex"
    presetReclaimTiming := (preset == "Elite") ? "Next bar confirmation" : "Reclaim close"
else if market == "XAUUSD"
    presetReclaimTiming := (preset == "Aggressive") ? "Reclaim close" : "Next bar confirmation"
else if market == "Crypto"
    presetReclaimTiming := "Reclaim close"
else
    presetReclaimTiming := (preset == "Elite") ? "Next bar confirmation" : "Reclaim close"

string reclaimEntryTiming =
     reclaimTimingDefault == "Preset" ? presetReclaimTiming : reclaimTimingDefault

//──────────────────────────────────────────────────────────────────────────────
// FULL MARKET + PRESET OVERWRITE (matches your indicator presets)
//──────────────────────────────────────────────────────────────────────────────
if market == "Forex"
    if preset == "Elite"
        minSpread := 0.0006
        minAtrPct := 0.0010
        minAdx := 14.0
        curvMin := 0.0
        crossLookback := 30
        maxBarsToRetest := 18
        strictStack := true
        requireRetest := true
        pullbackTo := "EMA10"
        reclaimOn  := "EMA10"
        retestOn   := "EMA10"
        atrStopMult := 1.8
        rrTarget := 2.0
    else if preset == "Balanced"
        minSpread := 0.00045
        minAtrPct := 0.0008
        minAdx := 12.0
        curvMin := 0.0
        crossLookback := 25
        maxBarsToRetest := 20
        strictStack := true
        requireRetest := true
        pullbackTo := "EMA10"
        reclaimOn  := "EMA10"
        retestOn   := "EMA10"
        atrStopMult := 1.6
        rrTarget := 1.75
    else
        minSpread := 0.0003
        minAtrPct := 0.0006
        minAdx := 10.0
        curvMin := 0.0
        crossLookback := 20
        maxBarsToRetest := 24
        strictStack := false
        requireRetest := false
        pullbackTo := "EMA20"
        reclaimOn  := "EMA20"
        retestOn   := "EMA20"
        atrStopMult := 1.4
        rrTarget := 1.5

else if market == "XAUUSD"
    if preset == "Elite"
        minSpread := 0.0009
        minAtrPct := 0.0013
        minAdx := 16.0
        curvMin := 0.0
        crossLookback := 40
        maxBarsToRetest := 18
        strictStack := true
        requireRetest := true
        pullbackTo := "EMA10"
        reclaimOn  := "EMA10"
        retestOn   := "EMA10"
        atrStopMult := 2.0
        rrTarget := 2.0
    else if preset == "Balanced"
        minSpread := 0.0007
        minAtrPct := 0.0011
        minAdx := 14.0
        curvMin := 0.0
        crossLookback := 35
        maxBarsToRetest := 22
        strictStack := true
        requireRetest := true
        pullbackTo := "EMA20"
        reclaimOn  := "EMA10"
        retestOn   := "EMA20"
        atrStopMult := 1.8
        rrTarget := 1.75
    else
        minSpread := 0.0005
        minAtrPct := 0.0009
        minAdx := 12.0
        curvMin := 0.0
        crossLookback := 28
        maxBarsToRetest := 26
        strictStack := false
        requireRetest := false
        pullbackTo := "EMA20"
        reclaimOn  := "EMA20"
        retestOn   := "EMA20"
        atrStopMult := 1.6
        rrTarget := 1.5

else if market == "Crypto"
    if preset == "Elite"
        minSpread := 0.0008
        minAtrPct := 0.0015
        minAdx := 14.0
        curvMin := 0.0
        crossLookback := 28
        maxBarsToRetest := 18
        strictStack := true
        requireRetest := true
        pullbackTo := "EMA20"
        reclaimOn  := "EMA10"
        retestOn   := "EMA20"
        atrStopMult := 2.2
        rrTarget := 2.0
    else if preset == "Balanced"
        minSpread := 0.0006
        minAtrPct := 0.0012
        minAdx := 12.0
        curvMin := 0.0
        crossLookback := 24
        maxBarsToRetest := 22
        strictStack := true
        requireRetest := true
        pullbackTo := "EMA20"
        reclaimOn  := "EMA20"
        retestOn   := "EMA20"
        atrStopMult := 2.0
        rrTarget := 1.75
    else
        minSpread := 0.00045
        minAtrPct := 0.0010
        minAdx := 10.0
        curvMin := 0.0
        crossLookback := 18
        maxBarsToRetest := 26
        strictStack := false
        requireRetest := false
        pullbackTo := "EMA20"
        reclaimOn  := "EMA20"
        retestOn   := "EMA20"
        atrStopMult := 1.8
        rrTarget := 1.5

else
    if preset == "Elite"
        minSpread := 0.0007
        minAtrPct := 0.0010
        minAdx := 14.0
        curvMin := 0.0
        crossLookback := 30
        maxBarsToRetest := 18
        strictStack := true
        requireRetest := true
        pullbackTo := "EMA10"
        reclaimOn  := "EMA10"
        retestOn   := "EMA10"
        atrStopMult := 1.8
        rrTarget := 2.0
    else if preset == "Balanced"
        minSpread := 0.00055
        minAtrPct := 0.00085
        minAdx := 12.0
        curvMin := 0.0
        crossLookback := 26
        maxBarsToRetest := 22
        strictStack := true
        requireRetest := true
        pullbackTo := "EMA20"
        reclaimOn  := "EMA10"
        retestOn   := "EMA20"
        atrStopMult := 1.6
        rrTarget := 1.75
    else
        minSpread := 0.0004
        minAtrPct := 0.0007
        minAdx := 10.0
        curvMin := 0.0
        crossLookback := 20
        maxBarsToRetest := 26
        strictStack := false
        requireRetest := false
        pullbackTo := "EMA20"
        reclaimOn  := "EMA20"
        retestOn   := "EMA20"
        atrStopMult := 1.4
        rrTarget := 1.5

if looserLTF
    strictStack := false
    requireRetest := false
    pullbackTo := "EMA20"
    reclaimOn  := "EMA20"
    retestOn   := "EMA20"

//──────────────────────────────────────────────────────────────────────────────
// WARMUP GATING (NA-safety + reliable backtest on 6m)
//──────────────────────────────────────────────────────────────────────────────
warmupBars = math.max(math.max(len50, atrLen), adxLen) + 10
ready = (bar_index >= warmupBars)

//──────────────────────────────────────────────────────────────────────────────
// HELPERS (NA-safe)
//──────────────────────────────────────────────────────────────────────────────
f_pick(_e5,_e10,_e20,_c)=>
    float o = _e20
    if _c == "EMA5"
        o := _e5
    else if _c == "EMA10"
        o := _e10
    o

f_stackL(_e5,_e10,_e20,_e50,_strict)=>
    _strict ? (_e5 > _e10 and _e10 > _e20 and _e20 > _e50) : (_e20 > _e50)

f_stackS(_e5,_e10,_e20,_e50,_strict)=>
    _strict ? (_e5 < _e10 and _e10 < _e20 and _e20 < _e50) : (_e20 < _e50)

f_curv(_x)=>
    float c = 0.0
    if bar_index >= 2 and not na(_x) and not na(_x[1]) and not na(_x[2])
        float slope0 = _x - _x[1]
        float slope1 = _x[1] - _x[2]
        c := (slope0 - slope1)
    c

f_adx(_len)=>
    float out = na
    if bar_index >= 2
        float upMove   = high - high[1]
        float downMove = low[1] - low
        float plusDM  = (upMove > downMove and upMove > 0) ? upMove : 0.0
        float minusDM = (downMove > upMove and downMove > 0) ? downMove : 0.0

        float tr1 = high - low
        float tr2 = math.abs(high - close[1])
        float tr3 = math.abs(low  - close[1])
        float tr  = math.max(tr1, math.max(tr2, tr3))

        float trur = ta.rma(tr, _len)
        float plusDI  = trur == 0 ? 0.0 : 100.0 * ta.rma(plusDM, _len) / trur
        float minusDI = trur == 0 ? 0.0 : 100.0 * ta.rma(minusDM, _len) / trur

        float denom = plusDI + minusDI
        float dx = denom == 0 ? 0.0 : (100.0 * math.abs(plusDI - minusDI) / denom)
        out := ta.rma(dx, _len)
    out

//──────────────────────────────────────────────────────────────────────────────
// LOCAL TF
//──────────────────────────────────────────────────────────────────────────────
ema5  = ta.ema(close,len5)
ema10 = ta.ema(close,len10)
ema20 = ta.ema(close,len20)
ema50 = ta.ema(close,len50)

s20 = bar_index >= 1 ? (ema20 - ema20[1]) : 0.0
s50 = bar_index >= 1 ? (ema50 - ema50[1]) : 0.0
c20 = f_curv(ema20)
c50 = f_curv(ema50)

atr = ta.atr(atrLen)
adx = f_adx(adxLen)

spreadPct = close != 0 ? math.abs(ema20-ema50)/close : 0.0
atrPct    = close != 0 ? atr/close : 0.0
recentX   = ta.barssince(ta.cross(ema20,ema50))

// Treat "not ready" / "na ADX" as chop (safe, prevents early weirdness)
isChop = useChopKill and (
    (not ready) or
    spreadPct < minSpread or
    (na(adx) or adx < minAdx) or
    atrPct < minAtrPct or
    (recentX >= 0 and recentX < crossLookback)
)

localLongOk  = ready and f_stackL(ema5,ema10,ema20,ema50,strictStack) and (s20 > 0 and s50 > 0) and (c20 > curvMin and c50 > curvMin)
localShortOk = ready and f_stackS(ema5,ema10,ema20,ema50,strictStack) and (s20 < 0 and s50 < 0) and (c20 < -curvMin and c50 < -curvMin)

//──────────────────────────────────────────────────────────────────────────────
// HTF ALIGNMENT
//──────────────────────────────────────────────────────────────────────────────
sec(_tf, _expr)=>
    request.security(syminfo.tickerid, _tf, _expr, barmerge.gaps_off, barmerge.lookahead_off)

d20 = (htfMode == "D only" or htfMode == "D + H1") ? sec(tfD, ta.ema(close,len20)) : na
d50 = (htfMode == "D only" or htfMode == "D + H1") ? sec(tfD, ta.ema(close,len50)) : na
h20 = (htfMode == "H1 only" or htfMode == "D + H1") ? sec(tfH1, ta.ema(close,len20)) : na
h50 = (htfMode == "H1 only" or htfMode == "D + H1") ? sec(tfH1, ta.ema(close,len50)) : na

dOkLong  = (htfMode == "D only" or htfMode == "D + H1") ? (not na(d20) and not na(d50) and d20 > d50) : true
dOkShort = (htfMode == "D only" or htfMode == "D + H1") ? (not na(d20) and not na(d50) and d20 < d50) : true
hOkLong  = (htfMode == "H1 only" or htfMode == "D + H1") ? (not na(h20) and not na(h50) and h20 > h50) : true
hOkShort = (htfMode == "H1 only" or htfMode == "D + H1") ? (not na(h20) and not na(h50) and h20 < h50) : true

htfLong  = (htfMode == "Off") ? true : (dOkLong and hOkLong)
htfShort = (htfMode == "Off") ? true : (dOkShort and hOkShort)

//──────────────────────────────────────────────────────────────────────────────
// ENTRY STATE (Pullback → Reclaim → Retest) — unchanged logic (1:1)
//──────────────────────────────────────────────────────────────────────────────
pullLvl   = f_pick(ema5,ema10,ema20,pullbackTo)
reclLvl   = f_pick(ema5,ema10,ema20,reclaimOn)
retestLvl = f_pick(ema5,ema10,ema20,retestOn)

var int lState=0
var int sState=0
var int lBar=na
var int sBar=na

allow = ready and (not isChop)

lPull = allow and htfLong  and localLongOk  and (low <= pullLvl)  and (close > ema50)
sPull = allow and htfShort and localShortOk and (high >= pullLvl) and (close < ema50)

prevClose = bar_index >= 1 ? close[1] : na
lRecl = allow and htfLong  and localLongOk  and (close > reclLvl) and (not na(prevClose) and prevClose <= reclLvl)
sRecl = allow and htfShort and localShortOk and (close < reclLvl) and (not na(prevClose) and prevClose >= reclLvl)

lRet  = allow and htfLong  and localLongOk  and (low <= retestLvl)  and (close > retestLvl)
sRet  = allow and htfShort and localShortOk and (high >= retestLvl) and (close < retestLvl)

if lState==0 and lPull
    lState:=1
if sState==0 and sPull
    sState:=1

if allowReclaimNoPull
    if lState==0 and lRecl
        lState := 2
        lBar := bar_index
    if sState==0 and sRecl
        sState := 2
        sBar := bar_index

if lState==1 and lRecl
    lState:=2
    lBar:=bar_index
if sState==1 and sRecl
    sState:=2
    sBar:=bar_index

if lState==2 and not na(lBar) and (bar_index - lBar > maxBarsToRetest)
    lState:=0
if sState==2 and not na(sBar) and (bar_index - sBar > maxBarsToRetest)
    sState:=0

bool longReclaimTrigger  = false
bool shortReclaimTrigger = false
if reclaimEntryTiming == "Reclaim close"
    longReclaimTrigger  := lRecl
    shortReclaimTrigger := sRecl
else
    longReclaimTrigger  := (bar_index >= 1 ? lRecl[1] : false) and (close > reclLvl)
    shortReclaimTrigger := (bar_index >= 1 ? sRecl[1] : false) and (close < reclLvl)

bool longEntry  = false
bool shortEntry = false
if barstate.isconfirmed
    if allow and htfLong and localLongOk
        longEntry := requireRetest ? (lState==2 and lRet) : longReclaimTrigger
    if allow and htfShort and localShortOk
        shortEntry := requireRetest ? (sState==2 and sRet) : shortReclaimTrigger

if longEntry
    lState := 0
if shortEntry
    sState := 0

//──────────────────────────────────────────────────────────────────────────────
// ATR RISK ENGINE
//──────────────────────────────────────────────────────────────────────────────
var float longStop = na
var float longTp   = na
var float longR    = na
var float shortStop = na
var float shortTp   = na
var float shortR    = na

if allowOrders and longEntry
    strategy.entry("LONG", strategy.long)
    if useAtrRisk
        float risk = atr * atrStopMult
        longStop := close - risk
        longTp   := close + (risk * rrTarget)
        longR    := risk

if allowOrders and shortEntry
    strategy.entry("SHORT", strategy.short)
    if useAtrRisk
        float risk = atr * atrStopMult
        shortStop := close + risk
        shortTp   := close - (risk * rrTarget)
        shortR    := risk

inLong  = strategy.position_size > 0
inShort = strategy.position_size < 0
avg     = strategy.position_avg_price

if allowOrders and useAtrRisk
    if inLong and not na(longStop) and not na(longTp)
        float stopL = longStop
        if useBreakeven and not na(longR) and close >= avg + longR
            stopL := math.max(stopL, avg)
        if useTrailAfterR and not na(longR) and close >= avg + longR
            stopL := math.max(stopL, close - (atr * trailAtrMult))
        strategy.exit("L-Exit", from_entry="LONG", stop=stopL, limit=longTp)

    if inShort and not na(shortStop) and not na(shortTp)
        float stopS = shortStop
        if useBreakeven and not na(shortR) and close <= avg - shortR
            stopS := math.min(stopS, avg)
        if useTrailAfterR and not na(shortR) and close <= avg - shortR
            stopS := math.min(stopS, close + (atr * trailAtrMult))
        strategy.exit("S-Exit", from_entry="SHORT", stop=stopS, limit=shortTp)

if strategy.position_size == 0
    longStop := na
    longTp := na
    longR := na
    shortStop := na
    shortTp := na
    shortR := na

//──────────────────────────────────────────────────────────────────────────────
// PLOTS + BLOCKS + ALERTS
//──────────────────────────────────────────────────────────────────────────────
plot(ema5,  "EMA 5",  display = showEma ? display.all : display.none)
plot(ema10, "EMA 10", display = showEma ? display.all : display.none)
plot(ema20, "EMA 20", display = showEma ? display.all : display.none)
plot(ema50, "EMA 50", display = showEma ? display.all : display.none)

plotshape(showSignals and longEntry,  title="Long",  style=shape.triangleup,   location=location.belowbar, size=size.tiny, text="LONG")
plotshape(showSignals and shortEntry, title="Short", style=shape.triangledown, location=location.abovebar, size=size.tiny, text="SHORT")

showRiskLines = allowOrders and useAtrRisk
plot(showRiskLines and inLong  ? longStop  : na, "Long Stop",  style=plot.style_linebr)
plot(showRiskLines and inLong  ? longTp    : na, "Long TP",    style=plot.style_linebr)
plot(showRiskLines and inShort ? shortStop : na, "Short Stop", style=plot.style_linebr)
plot(showRiskLines and inShort ? shortTp   : na, "Short TP",   style=plot.style_linebr)

blockChop = showBlocks and isChop
blockHtf  = showBlocks and ready and (not isChop) and (htfMode != "Off") and (not htfLong and not htfShort)

plotshape(showBlocks and blockChop, title="Blocked: Chop", style=shape.circle, location=location.top, size=size.tiny, text="CHOP")
plotshape(showBlocks and blockHtf,  title="Blocked: HTF",  style=shape.circle, location=location.top, size=size.tiny, text="HTF")

alertcondition(longEntry,  "Long Entry",  "Elite EMA Reclaim LONG on {{ticker}}")
alertcondition(shortEntry, "Short Entry", "Elite EMA Reclaim SHORT on {{ticker}}")