트리플 EMA 되돌림 스캘핑 전략

EMA ATR PULLBACK SCALPING
생성 날짜: 2025-09-30 13:04:41 마지막으로 수정됨: 2025-09-30 13:04:41
복사: 0 클릭수: 444
avatar of ianzeng123 ianzeng123
2
집중하다
319
수행원

트리플 EMA 되돌림 스캘핑 전략 트리플 EMA 되돌림 스캘핑 전략

25/50/100 EMA 트리플 필터링, 이것이 진짜 트렌드 철회 거래입니다.

단일 평준으로 거래하는 것을 그만 두십시오. 이 전략은 25/50/100의 세 개의 EMA로 전체 트렌드 식별 시스템을 구축합니다. 이 전략은 EMA가 순서대로 배열되어 동방향으로 기울어져야하며 0.10배의 ATR의 최소 간격을 요구합니다.

핵심은 “깨끗한 EMA 배열”입니다: 다중 헤드일 경우 25> 50> 100 그리고 모두 위쪽으로 기울어지며, 공백일 경우 25<50<100 그리고 모두 아래쪽으로 기울어집니다. 간격 필터는 트렌드가 충분히 강하도록 하고, 균등한 접착 상태의 유효하지 않은 신호를 피합니다.

철회 논리 설계 정확성 15주기 내에 철회 확인이 완료되어야 합니다.

이 전략의 핵심은 회수 검출 메커니즘이다. 다중 회수는 가격이 25 또는 50EMA를 만지고 100EMA를 유지하도록 요구하며, 공수 회수는 가격이 25 또는 50EMA를 만지고 100EMA를 유지하도록 요구한다. 이 디자인은 전통적인 “지지를 깨고 다시 구매”보다 더 정확하다.

15 주기의 회귀 창을 설정하는 것은 합리적입니다. 재검토 데이터는 실제 트렌드 회귀는 일반적으로 10-15 주기에 걸쳐 반전을 완료한다는 것을 보여줍니다. 이 시간 창을 초과하는 회귀는 트렌드가 변경 될 수 있음을 의미합니다.

입국 확인 메커니즘은 엄격하며, 전체 K 라인은 25EMA에서 완전히 분리되어야 합니다.

진입 트리거 조건은 매우 엄격하다: 확인된 K선 닫기 후, 전체 K선 (열기, 최고, 최저, 닫기) 은 25EMA의 올바른 쪽에 완벽하게 위치해야 한다. 이 디자인은 가짜 돌파구와 디스크 내 소음을 피하고, 진정한 역전 확인 후만 진입한다.

다중 입점 요구 사항: 오픈> 25EMA, 최소> 25EMA, 클로즈> 25EMA. 공백 입점 요구 사항: 오픈<25EMA, 최대<25EMA, 클로즈<25EMA. 이 “전체 K 라인 확인” 방법은 입점 품질을 크게 향상시키고 무효 거래를 줄인다.

10% 포지션 + 0.05% 수수료, 고주파 탈피 작업에 적합

전략 기본 10% 포지션 설정은 적당하며, 충분한 수익을 얻을 수 있으며, 단위 위험을 제어합니다. 0.05%의 수수료 설정은 실제 거래 비용에 가깝고, 재검토 결과는 더 많은 참고 가치를 갖습니다. 다중 공백 양방향 거래를 지원하고, 다양한 시장 환경에 적응하는 한방향 동작도 선택할 수 있습니다.

중요한 경고: 전략은 입시 논리만을 포함하고, 스톱 스톱 손실을 설정하지 않습니다. 실내 사용은 엄격한 위험 관리와 함께해야하며, 2-3 배의 ATR의 손실과 1.5-2 배의 리스크 수익률의 스톱 스톱을 설정하는 것이 좋습니다.

적용 가능한 시나리오가 명확하고, 트렌드 시장은 우수한 성과를 보이고 있지만, 흔들리는 시장은 신중해야 합니다.

전략은 명확한 트렌드 시장에서 훌륭하게 작동하며, 특히 일방적인 상황에서의 회수 구매에 적합합니다. 그러나 수평 변동 시장에서 EMA 배열 조건은 충족하기가 어렵고 거래 기회가 상대적으로 적습니다. 이것은 실제로 전략의 장점이며, 불리한 환경에서 과도한 거래를 피합니다.

위험 팁: 역사적인 회귀는 미래의 수익을 의미하지 않으며, 전략이 연속적인 손실의 위험이 있습니다. 흔들리는 시장은 장기간 신호 없는 상태가 발생할 수 있으며, 적절한 시장 환경이 나타날 때까지 인내해야합니다. 사용하기 전에 충분한 시뮬레이션 거래 검증이 권장됩니다.

전략 소스 코드
/*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))