Chiến lược Scalping thoái lui Triple EMA

EMA ATR PULLBACK SCALPING
Ngày tạo: 2025-09-30 13:04:41 sửa đổi lần cuối: 2025-09-30 13:04:41
sao chép: 0 Số nhấp chuột: 444
2
tập trung vào
319
Người theo dõi

Chiến lược Scalping thoái lui Triple EMA Chiến lược Scalping thoái lui Triple EMA

25/50/100 EMA Triple Filter, đây là xu hướng thực sự rút lui

Không còn giao dịch bằng một đường đồng nhất nữa. Chiến lược này xây dựng một hệ thống nhận dạng xu hướng hoàn chỉnh bằng ba EMA 25/50/100, yêu cầu EMA phải được sắp xếp theo thứ tự và nghiêng theo hướng đồng nhất, cộng với yêu cầu khoảng cách tối thiểu là 0.10 lần ATR. Dữ liệu cho thấy rằng cơ chế lọc ba lần này có thể tránh hiệu quả phá vỡ giả của thị trường lắc lư, chỉ hoạt động trong tình huống xu hướng thực sự.

Điều quan trọng là “sắp xếp EMA sạch”: 25> 50> 100 khi nhiều đầu và tất cả lên, 25 < 50 < 100 khi đầu trống và tất cả xuống. Bộ lọc khoảng cách đảm bảo xu hướng đủ mạnh để tránh tín hiệu vô hiệu trong trạng thái bám đồng nhất.

Phản hồi logic thiết kế chính xác, phải hoàn thành xác nhận phản hồi trong 15 chu kỳ

Cốt lõi của chiến lược là cơ chế phát hiện rút lui. Việc rút lui nhiều đầu yêu cầu giá chạm 25 hoặc 50 EMA nhưng vẫn ở trên 100 EMA, và việc rút lui không đầu yêu cầu giá chạm 25 hoặc 50 EMA nhưng vẫn ở dưới 100 EMA. Thiết kế này chính xác hơn so với việc mua lại sau khi phá vỡ hỗ trợ truyền thống.

Cài đặt cửa sổ rút lui 15 chu kỳ là hợp lý. Dữ liệu đánh giá lại cho thấy rằng sự rút lui của xu hướng thực sự thường được hoàn thành trong vòng 10-15 chu kỳ, và việc rút lui vượt quá cửa sổ thời gian này thường có nghĩa là xu hướng có thể thay đổi.

Cơ chế xác nhận nhập cảnh nghiêm ngặt, toàn bộ đường K phải hoàn toàn tách khỏi 25EMA

Điều kiện kích hoạt vào rất nghiêm ngặt: Sau khi xác nhận kết thúc K-line, toàn bộ K-line (khởi động, cao nhất, thấp nhất, kết thúc) phải nằm hoàn toàn ở bên phải của 25EMA. Thiết kế này tránh phá vỡ giả và tiếng ồn trong đĩa, đảm bảo chỉ vào khi xác nhận thực sự quay ngược.

Yêu cầu đầu vào nhiều đầu: mở cửa> 25EMA, tối thiểu> 25EMA, đóng cửa> 25EMA. Yêu cầu đầu vào trống: mở cửa <25EMA, tối đa <25EMA, đóng cửa <25EMA. Phương pháp xác nhận toàn bộ K-line này đã cải thiện đáng kể chất lượng nhập cảnh và giảm giao dịch không hợp lệ.

10% vị trí + 0.05% phí xử lý, phù hợp cho hoạt động cắt bỏ da đầu tần số cao

Chiến lược đặt vị trí 10% mặc định ở mức vừa phải, vừa có đủ lợi nhuận vừa kiểm soát rủi ro đơn lẻ… Phí xử lý 0,05% được đặt gần với chi phí giao dịch thực tế, kết quả kiểm tra lại có giá trị tham khảo hơn…

Lưu ý quan trọng: Chiến lược chỉ bao gồm logic nhập cảnh, không có thiết lập dừng dừng lỗ. Khi sử dụng trên thực tế, phải được kết hợp với quản lý rủi ro nghiêm ngặt, khuyến nghị thiết lập dừng lỗ 2-3 lần ATR và 1,5-2 lần tỷ lệ lợi nhuận rủi ro.

Các trường hợp được áp dụng rõ ràng, thị trường xu hướng hoạt động tốt nhưng thị trường chấn động cần thận trọng

Chiến lược này hoạt động tốt trong thị trường xu hướng rõ ràng, đặc biệt phù hợp với việc mua lại một chiều. Tuy nhiên, trong thị trường biến động ngang, điều kiện xếp hạng EMA khó đáp ứng và cơ hội giao dịch tương đối ít. Đây thực sự là lợi thế của chiến lược, tránh giao dịch quá mức trong môi trường bất lợi.

Lưu ý rủi ro: Đánh giá lịch sử không đại diện cho lợi nhuận trong tương lai, chiến lược có nguy cơ thua lỗ liên tục. Thị trường chấn động có thể xảy ra tình trạng không có tín hiệu lâu dài, cần kiên nhẫn chờ đợi môi trường thị trường thích hợp.

Mã nguồn chiến lược
/*backtest
start: 2025-01-01 00:00:00
end: 2025-09-27 08:00:00
period: 1d
basePeriod: 1d
exchanges: [{"eid":"Futures_Bybit","currency":"ETH_USDT","balance":500000}]
*/

//@version=6
strategy("Clean 25/50/100 EMA Pullback Scalper — Entries Only (Side Select)",
     overlay=true, calc_on_every_tick=true, calc_on_order_fills=true,
     initial_capital=10000, commission_type=strategy.commission.percent, commission_value=0.05,
     pyramiding=0, default_qty_type=strategy.percent_of_equity, default_qty_value=10)

// === Side selector ===
side = input.string("Both", "Trade Side", options=["Both", "Long Only", "Short Only"])
longsEnabled  = side == "Both" or side == "Long Only"
shortsEnabled = side == "Both" or side == "Short Only"

// === Inputs ===
lenFast   = input.int(25,  "Fast EMA (pullback)", minval=1)
lenMid    = input.int(50,  "Mid EMA (filter)",    minval=1)
lenSlow   = input.int(100, "Slow EMA (safety)",   minval=1)

useSlope  = input.bool(true,  "Require EMAs sloping same way?")
useSpread = input.bool(true,  "Require clean spacing (min spread)?")
spreadPct = input.float(0.10, "Min spread vs ATR (0.10 = 0.10×ATR)", step=0.01, minval=0.0)

pullLookback = input.int(15, "Max bars after pullback", minval=1, maxval=100)
showSignals  = input.bool(true, "Show entry markers?")

// === Series ===
ema25  = ta.ema(close, lenFast)
ema50  = ta.ema(close, lenMid)
ema100 = ta.ema(close, lenSlow)
atr    = ta.atr(14)

// === Trend & spacing ===
isUpStack   = ema25 > ema50 and ema50 > ema100
isDownStack = ema25 < ema50 and ema50 < ema100
slopeUp     = ema25 > ema25[1] and ema50 > ema50[1] and ema100 > ema100[1]
slopeDown   = ema25 < ema25[1] and ema50 < ema50[1] and ema100 < ema100[1]

minGap = atr * spreadPct
spreadUpOK   = (ema25 - ema50) > minGap and (ema50 - ema100) > minGap
spreadDownOK = (ema100 - ema50) > minGap and (ema50 - ema25) > minGap

trendLongOK  = isUpStack   and (useSlope ? slopeUp   : true) and (useSpread ? spreadUpOK   : true)
trendShortOK = isDownStack and (useSlope ? slopeDown : true) and (useSpread ? spreadDownOK : true)

// === Pullback detection state ===
var bool  pullArmedLong   = false
var bool  pullArmedShort  = false
var int   pullBarIdxLong  = na
var int   pullBarIdxShort = na
var float pullMinLong     = na
var float pullMaxShort    = na

// Long pullback state
if trendLongOK
    touched25 = low <= ema25
    touched50 = low <= ema50
    stayedAbove100 = low > ema100
    if (touched25 or touched50) and stayedAbove100
        pullArmedLong  := true
        pullBarIdxLong := bar_index
        pullMinLong    := na(pullMinLong) ? low : math.min(pullMinLong, low)
    else if pullArmedLong
        pullMinLong := na(pullMinLong) ? low : math.min(pullMinLong, low)
        if low <= ema100 or (bar_index - pullBarIdxLong > pullLookback)
            pullArmedLong := false
            pullMinLong   := na
else
    pullArmedLong := false
    pullMinLong   := na

// Short pullback state
if trendShortOK
    touched25s = high >= ema25
    touched50s = high >= ema50
    stayedBelow100 = high < ema100
    if (touched25s or touched50s) and stayedBelow100
        pullArmedShort  := true
        pullBarIdxShort := bar_index
        pullMaxShort    := na(pullMaxShort) ? high : math.max(pullMaxShort, high)
    else if pullArmedShort
        pullMaxShort := na(pullMaxShort) ? high : math.max(pullMaxShort, high)
        if high >= ema100 or (bar_index - pullBarIdxShort > pullLookback)
            pullArmedShort := false
            pullMaxShort   := na
else
    pullArmedShort := false
    pullMaxShort   := na

// === Entry triggers (confirmed bar & whole candle outside 25 EMA) ===
longEntryRaw  = pullArmedLong  and barstate.isconfirmed and (open > ema25 and low > ema25 and close > ema25) and (na(pullMinLong)  or pullMinLong  > ema100)
shortEntryRaw = pullArmedShort and barstate.isconfirmed and (open < ema25 and high < ema25 and close < ema25) and (na(pullMaxShort) or pullMaxShort < ema100)

longEntry  = longsEnabled  and longEntryRaw
shortEntry = shortsEnabled and shortEntryRaw

// Disarm after trigger
if longEntry
    pullArmedLong := false
    pullMinLong   := na
if shortEntry
    pullArmedShort := false
    pullMaxShort   := na

// === Orders (entries only; no TP/SL) ===
if longEntry and strategy.position_size <= 0
    strategy.entry("Long", strategy.long)

if shortEntry and strategy.position_size >= 0
    strategy.entry("Short", strategy.short)

// === Plots & visuals ===
plot(ema25,  "EMA 25",  color=color.new(color.teal, 0))
plot(ema50,  "EMA 50",  color=color.new(color.orange, 0))
plot(ema100, "EMA 100", color=color.new(color.purple, 0))

bgcolor(trendLongOK  ? color.new(color.green, 92) : na)
bgcolor(trendShortOK ? color.new(color.red, 92)   : na)

if showSignals and longEntry
    label.new(bar_index, low, "▲ BUY\nFull candle above 25 EMA", style=label.style_label_up, textcolor=color.white, color=color.new(color.green, 0))
if showSignals and shortEntry
    label.new(bar_index, high, "▼ SELL\nFull candle below 25 EMA", style=label.style_label_down, textcolor=color.white, color=color.new(color.red, 0))