에르고틱 듀얼 레일 리버스 MACD 양적 거래 전략

저자:차오장, 날짜: 2023-12-21 11:07:51
태그:

img

전반적인 설명

이 전략은 듀얼 레일 리버스 MACD 양적 거래 전략이다. 윌리엄 블라우가 그의 책?? 모멘텀, 디렉션 앤 디버전스?? 에서 기술한 기술 지표에 기반을 두고 확장된다. 이 전략은 또한 백테스팅 기능을 가지고 있으며 알림, 필터, 트레일링 스톱 로스 등 추가 기능을 포함할 수 있다.

원칙

이 전략의 핵심 지표는 MACD입니다. 빠른 이동 평균 EMA®와 느린 이동 평균 EMA ((slowMALen) 을 계산하고, 그 다음 차이 xmacd를 계산합니다. 또한 xmacd의 EMA ((신호 길이) 를 계산하여 xMA_MACD를 얻습니다. xmacd가 xMA_MACD 위에 넘어가면 긴 신호가 트리거되며, 아래의 크로스에서 짧은 신호가 트리거됩니다. 이 전략의 핵심 측면은 역 거래 신호입니다. 즉, xmacd와 xMA_MACD 사이의 관계는 기존 MACD 지표와 반대입니다.

또한, 전략은 트렌드 필터를 포함합니다. 긴 신호가 발사되면, 상승 트렌드 필터가 구성되면 가격이 증가하는지 확인할 수 있습니다. 마찬가지로, 짧은 신호는 하락 가격 추세를 검사합니다. RSI 및 MFI 지표도 신호를 필터링하는 데 사용할 수 있습니다. 한계 이상의 손실을 방지하기 위해 스톱 로스 메커니즘이 포함되어 있습니다.

이점 분석

이 전략의 가장 큰 장점은 강력한 백테스팅 기능이다. 당신은 다른 거래 도구를 선택하고, 백테스트 시간 프레임을 설정하고, 특정 도구 데이터를 기반으로 전략 매개 변수를 최적화할 수 있다. 간단한 MACD 전략과 비교하면, 트렌드와 과잉 구매/ 과잉 판매 분석을 통합하여 몇 가지 동일한 신호를 필터링한다. 듀얼 레일 역 MACD는 전통적인 MACD와 다르며, 전통적인 MACD가 놓칠 수 있는 몇 가지 기회를 활용할 수 있다.

위험 분석

이 전략의 주요 위험은 역상거래 논리에서 비롯된다. 역상거래 신호는 전통적인 신호에 의해 놓친 몇 가지 기회를 포착 할 수 있지만, 또한 일부 전통적인 MACD 입구 지점을 잃는 것을 의미하며, 신중한 평가를 필요로 한다. 또한, MACD 자체는 잘못된 상승 신호를 생성하는 경향이 있다. 전략은 불안하고 방향 없는 시장에서 과도한 거래와 비용이 증가할 수 있다.

위험을 완화하기 위해 매개 변수를 최적화 할 수 있습니다. 이동 평균 길이를 조정; 트렌드와 지표 필터를 결합하여 불안정한 시장에서 신호를 피합니다.

최적화 방향

이 전략은 몇 가지 측면에서 개선될 수 있습니다.

  1. 빠른 및 느린 철도 매개 변수를 조정, 이동 평균 길이를 최적화, 특정 도구에 대한 최적의 매개 변수 세트를 찾기 위해 백테스트
  2. 트렌드 필터를 추가하거나 조정하여 수익률을 향상시키는지 백테스트 결과로 판단합니다.
  3. 더 나은 성능을 결정하기 위해 고정 또는 후속, 다른 중지 손실 메커니즘을 테스트
  4. 추가 필터 조건을 설정하고 신호 품질을 보장하기 위해 KD, 볼링거 밴드와 같은 다른 지표를 결합 시도

요약

듀얼 레일 역 MACD 양적 전략은 확장 및 개선으로 고전적인 MACD 지표에 기반을 둔다. 유연한 매개 변수 구성, 풍부한 필터 선택 및 강력한 백테스팅 기능으로 다른 거래 도구에 맞게 조정할 수 있다. 따라서 추가 탐구에 가치가 있는 흥미롭고 유망한 양적 거래 전략이다.


/*backtest
start: 2023-11-20 00:00:00
end: 2023-12-20 00:00:00
period: 1h
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

//@version = 3
////////////////////////////////////////////////////////////
//  Copyright by HPotter v1.0 09/12/2016
// This is one of the techniques described by William Blau in his book
// "Momentum, Direction and Divergence" (1995). If you like to learn more,
// we advise you to read this book. His book focuses on three key aspects
// of trading: momentum, direction and divergence. Blau, who was an electrical
// engineer before becoming a trader, thoroughly examines the relationship 
// between price and momentum in step-by-step examples. From this grounding,
// he then looks at the deficiencies in other oscillators and introduces some
// innovative techniques, including a fresh twist on Stochastics. On directional 
// issues, he analyzes the intricacies of ADX and offers a unique approach to help 
// define trending and non-trending periods.
// Blau`s indicator is like usual MACD, but it plots opposite of meaningof
// stndard MACD indicator. 
//
// You can change long to short in the Input Settings
// Please, use it only for learning or paper trading. Do not for real trading.
//
//
// 2018-09 forked by Khalid Salomão
// - Backtesting
// - Added filters: RSI, MFI, Price trend
// - Trailing Stop Loss
// - Other minor adjustments
//
////////////////////////////////////////////////////////////
strategy(title="Ergotic MACD Backtester [forked from HPotter]", shorttitle="Ergotic MACD Backtester", overlay=true, pyramiding=0, default_qty_type=strategy.cash, default_qty_value=25000, initial_capital=50000, commission_type=strategy.commission.percent, commission_value=0.15, slippage=3)


// === BACKTESTING: INPUT BACKTEST RANGE ===
source = input(close)
strategyType = input(defval="Long Only", options=["Long & Short", "Long Only", "Short Only"])

FromMonth = input(defval = 7, title = "From Month", minval = 1, maxval = 12)
FromDay   = input(defval = 1, title = "From Day", minval = 1, maxval = 31)
FromYear  = input(defval = 2018, title = "From Year", minval = 2017)
ToMonth   = input(defval = 12, title = "To Month", minval = 1, maxval = 12)
ToDay     = input(defval = 1, title = "To Day", minval = 1, maxval = 31)
ToYear    = input(defval = 2030, title = "To Year", minval = 2017)

start     = timestamp(FromYear, FromMonth, FromDay, 00, 00)  
finish    = timestamp(ToYear, ToMonth, ToDay, 23, 59)        
window()  => true // window of time verification

// === STRATEGY ===

r = input(144, minval=1, title="R (32,55,89,100,144,200)") // default 32
slowMALen = input(6, minval=1) // default 32
signalLength = input(6, minval=1)
reverse = input(false, title="Trade reverse (long/short switch)")

//hline(0, color=blue, linestyle=line)

fastMA = ema(source, r)
slowMA = ema(source, slowMALen)
xmacd = fastMA - slowMA
xMA_MACD = ema(xmacd, signalLength)

pos = 0
pos := iff(xmacd < xMA_MACD, 1,
	   iff(xmacd > xMA_MACD, -1, nz(pos[1], 0))) 
possig = 0
possig := iff(reverse and pos == 1, -1,
          iff(reverse and pos == -1, 1, pos))

// === FILTER: price trend ====
trending_price_long = input(true, title="Long only if price has increased" )
trending_price_short = input(false, title="Short only if price has decreased" )
trending_price_length = input( 2, minval=1 )
trending_price_with_ema = input( false )
trending_price_ema = input( 3, minval=1 )
price_trend = trending_price_with_ema ? ema(source, trending_price_ema) : source
priceLongTrend() => (trending_price_long ? rising(price_trend, trending_price_length) : true)
priceShortTrend() => (trending_price_short ? falling(price_trend, trending_price_length) : true)

// === FILTER: RSI ===
rsi_length = input( 14, minval=1 )
rsi_overSold = input( 14, minval=0, title="RSI Sell Cutoff (Sell only if >= #)" )
rsi_overBought = input( 82, minval=0, title="RSI Buy Cutoff (Buy only if <= #)" )

vrsi = rsi(source, rsi_length)
rsiOverbought() => vrsi > rsi_overBought
rsiOversold() => vrsi < rsi_overSold

trending_rsi_long = input(false, title="Long only if RSI has increased" )
trending_rsi_length = input( 2 )
rsiLongTrend() => trending_rsi_long ? rising(vrsi, trending_rsi_length) : true

// === FILTER: MFI ===
mfi_length = input(14, minval=1)
mfi_lower = input(14, minval=0, maxval=50)
mfi_upper = input(82, minval=50, maxval=100)
upper_s = sum(volume * (change(source) <= 0 ? 0 : source), mfi_length)
lower_s = sum(volume * (change(source) >= 0 ? 0 : source), mfi_length)
mf = rsi(upper_s, lower_s)

mfiOverbought() => (mf > mfi_upper)
mfiOversold() => (mf < mfi_lower)

trending_mfi_long = input(false, title="Long only if MFI has increased" )
trending_mfi_length = input( 2 )
mfiLongTrend() => trending_mfi_long ? rising(mf, trending_mfi_length) : true

// === SIGNAL CALCULATION ===
long  = window() and possig == 1 and rsiLongTrend() and mfiLongTrend() and not rsiOverbought() and not mfiOverbought() and priceLongTrend()
short = window() and possig == -1 and not rsiOversold() and not mfiOversold() and priceShortTrend()

// === trailing stop
tslSource=input(hlc3,title="TSL source")
//suseCurrentRes = input(true, title="Use current chart resolution for stop trigger?")
tslResolution = input(title="Use different timeframe for stop trigger? Uncheck box above.", defval="5")
tslTrigger = input(3.0) / 100
tslStop = input(0.6) / 100

currentPrice = request.security(syminfo.tickerid, tslResolution, tslSource, barmerge.gaps_off, barmerge.lookahead_off)

isLongOpen = false
isLongOpen := nz(isLongOpen[1], false)
entryPrice=0.0
entryPrice:= nz(entryPrice[1], 0.0)
trailPrice=0.0
trailPrice:=nz(trailPrice[1], 0.0)

// update TSL high mark
if (isLongOpen )
    if (not trailPrice and currentPrice >= entryPrice * (1 + tslTrigger))
        trailPrice := currentPrice
    else 
        if (trailPrice and currentPrice > trailPrice)
            trailPrice := currentPrice

if (trailPrice and currentPrice <= trailPrice * (1 - tslStop))
    // FIRE TSL SIGNAL
    short:=true // <===
    long := false

// if short clean up
if (short)
    isLongOpen := false
    entryPrice := 0.0
    trailPrice := 0.0

if (long)
    isLongOpen := true
    if (not entryPrice)
        entryPrice := currentPrice

// === BACKTESTING: ENTRIES ===
if long
    if (strategyType == "Short Only")
        strategy.close("Short")
    else
        strategy.entry("Long", strategy.long, comment="Long")

if short
    if (strategyType == "Long Only")
        strategy.close("Long")
    else
        strategy.entry("Short", strategy.short, comment="Short")	  
    
//barcolor(possig == -1 ? red: possig == 1 ? green : blue )
//plot(xmacd, color=green, title="Ergotic MACD")
//plot(xMA_MACD, color=red, title="SigLin")

plotshape(trailPrice ? trailPrice : na, style=shape.circle, location=location.absolute, color=blue, size=size.tiny)

plotshape(long, style=shape.triangleup, location=location.belowbar, color=green, size=size.tiny)
plotshape(short, style=shape.triangledown, location=location.abovebar, color=red, size=size.tiny)

// === Strategy Alert ===
alertcondition(long, title='BUY - Ergotic MACD Long Entry', message='Go Long!')
alertcondition(short, title='SELL - Ergotic MACD Long Entry', message='Go Short!')

// === BACKTESTING: EXIT strategy ===
sl_inp = input(7, title='Stop Loss %', type=float)/100
tp_inp = input(1.8, title='Take Profit %', type=float)/100

stop_level = strategy.position_avg_price * (1 - sl_inp)
take_level = strategy.position_avg_price * (1 + tp_inp)

strategy.exit("Stop Loss/Profit", "Long", stop=stop_level, limit=take_level)

더 많은