양방향 이동 평균 반전 거래 전략은 가격 평균 반전 이론에 기반한 양적 거래 전략이다. 이 전략은 여러 이동 평균을 설정하여 가격이 이동 평균에서 크게 벗어나면 시장에 진입하고, 다시 돌아온 경우 시장에서 빠져 나가는 방식으로 가격 반전 기회를 포착합니다.
이 전략의 핵심 아이디어는 가격 평균 회귀 (Price mean reversal) 이며, 이는 가격이 평균 값 주위에서 변동하는 경향이 있으며 평균에서 너무 멀리 벗어날 때 회귀 할 가능성이 높다는 것을 암시합니다. 구체적으로, 이 전략은 세 가지 이동 평균 그룹을 설정합니다. 입시 이동 평균, 출구 이동 평균 및 스톱-손실 이동 평균. 가격이 입시 이동 평균에 도달하면 대응하는 긴 또는 짧은 포지션을 열고, 가격이 출구 이동 평균에 도달하면 포지션을 닫고, 가격이 다시 회귀하지 않고 추세를 계속하는 경우 중단 손실 이동 평균으로 손실을 제어합니다.
코드 논리 관점에서, 두 가지 진입 이동 평균 - 길고 짧은 - 각각 빠른 이동 평균과 느린 이동 평균으로 구성되어 있습니다. 그것들과 가격 사이의 오차는 포지션 크기를 결정합니다. 또한 출구 이동 평균은 포지션을 닫을 때를 신호하는 별도의 이동 평균입니다. 가격이이 선을 칠 때 기존 포지션은 평평화됩니다.
양방향 이동 평균 회전 전략의 주요 장점은 다음과 같습니다.
이 전략은 상대적으로 작은 가격 변동이 있는 낮은 변동성 도구와 잘 작동합니다. 특히 범위 제한 주기에 들어갈 때 효과적으로 일시적인 가격 반전에서 기회를 잡을 수 있습니다. 한편으로, 위험 통제 조치는 상당히 포괄적입니다. 가격이 되돌리지 않더라도 합리적인 범위 내에서 손실을 제한합니다.
이 전략과 관련된 위험도 있습니다.
위 위험 요소 를 줄일 수 있는 몇 가지 방법 은 다음 과 같다.
또한 이 전략을 더 이상 최적화 할 수있는 충분한 공간이 있습니다.
쌍방향 이동 평균 반전 거래 전략은 이동 평균 수준에서 상당한 오차가 발생한 후 가격 반전에서 이익을 얻는 것을 목표로합니다. 적절한 위험 제어 조치가 있으면 매개 변수 조정으로 일관된 이익을 얻을 수 있습니다. 추세 추구하고 과도한 변동성 같은 위험이 여전히 존재하지만, 진입 논리를 개선하고 위치 크기를 줄임으로써 해결할 수 있습니다. 이해하기 쉬운이 전략은 양적 거래자로부터 추가 연구와 최적화를받을 가치가 있습니다.
/*backtest start: 2023-12-15 00:00:00 end: 2024-01-14 00:00:00 period: 1h basePeriod: 15m exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}] */ //@version=5 strategy(title = "hamster-bot MRS 2", overlay = true, default_qty_type = strategy.percent_of_equity, initial_capital = 100, default_qty_value = 30, pyramiding = 1, commission_value = 0.1, backtest_fill_limits_assumption = 1) info_options = "Options" on_close = input(false, title = "Entry on close", inline=info_options, group=info_options) OFFS = input.int(0, minval = 0, maxval = 1, title = "| Offset View", inline=info_options, group=info_options) trade_offset = input.int(0, minval = 0, maxval = 1, title = "Trade", inline=info_options, group=info_options) use_kalman_filter = input.bool(false, title="Use Kalman filter", group=info_options) //MA Opening info_opening = "MA Opening Long" maopeningtyp_l = input.string("SMA", title="Type", options=["SMA", "EMA", "TEMA", "DEMA", "ZLEMA", "WMA", "Hma", "Thma", "Ehma", "H", "L", "DMA"], title = "", inline=info_opening, group=info_opening) maopeningsrc_l = input.source(ohlc4, title = "", inline=info_opening, group=info_opening) maopeninglen_l = input.int(3, minval = 1, title = "", inline=info_opening, group=info_opening) long1on = input(true, title = "", inline = "long1") long1shift = input.float(0.96, step = 0.005, title = "Long", inline = "long1") long1lot = input.int(10, minval = 0, maxval = 10000, step = 10, title = "Lot 1", inline = "long1") info_opening_s = "MA Opening Short" maopeningtyp_s = input.string("SMA", title="Type", options=["SMA", "EMA", "TEMA", "DEMA", "ZLEMA", "WMA", "Hma", "Thma", "Ehma", "H", "L", "DMA"], title = "", inline=info_opening_s, group=info_opening_s) maopeningsrc_s = input.source(ohlc4, title = "", inline=info_opening_s, group=info_opening_s) maopeninglen_s = input.int(3, minval = 1, title = "", inline=info_opening_s, group=info_opening_s) short1on = input(true, title = "", inline = "short1") short1shift = input.float(1.04, step = 0.005, title = "short", inline = "short1") short1lot = input.int(10, minval = 0, maxval = 10000, step = 10, title = "Lot 1", inline = "short1") //MA Closing info_closing = "MA Closing" maclosingtyp = input.string("SMA", title="Type", options=["SMA", "EMA", "TEMA", "DEMA", "ZLEMA", "WMA", "Hma", "Thma", "Ehma", "H", "L", "DMA"], title = "", inline=info_closing, group=info_closing) maclosingsrc = input.source(ohlc4, title = "", inline=info_closing, group=info_closing) maclosinglen = input.int(3, minval = 1, maxval = 200, title = "", inline=info_closing, group=info_closing) maclosingmul = input.float(1, step = 0.005, title = "mul", inline=info_closing, group=info_closing) startTime = input(timestamp("01 Jan 2010 00:00 +0000"), "Start date", inline = "period") finalTime = input(timestamp("31 Dec 2030 23:59 +0000"), "Final date", inline = "period") HMA(_src, _length) => ta.wma(2 * ta.wma(_src, _length / 2) - ta.wma(_src, _length), math.round(math.sqrt(_length))) EHMA(_src, _length) => ta.ema(2 * ta.ema(_src, _length / 2) - ta.ema(_src, _length), math.round(math.sqrt(_length))) THMA(_src, _length) => ta.wma(ta.wma(_src,_length / 3) * 3 - ta.wma(_src, _length / 2) - ta.wma(_src, _length), _length) tema(sec, length)=> tema1= ta.ema(sec, length) tema2= ta.ema(tema1, length) tema3= ta.ema(tema2, length) tema_r = 3*tema1-3*tema2+tema3 donchian(len) => math.avg(ta.lowest(len), ta.highest(len)) ATR_func(_src, _len)=> atrLow = low - ta.atr(_len) trailAtrLow = atrLow trailAtrLow := na(trailAtrLow[1]) ? trailAtrLow : atrLow >= trailAtrLow[1] ? atrLow : trailAtrLow[1] supportHit = _src <= trailAtrLow trailAtrLow := supportHit ? atrLow : trailAtrLow trailAtrLow f_dema(src, len)=> EMA1 = ta.ema(src, len) EMA2 = ta.ema(EMA1, len) DEMA = (2*EMA1)-EMA2 f_zlema(src, period) => lag = math.round((period - 1) / 2) ema_data = src + (src - src[lag]) zl= ta.ema(ema_data, period) f_kalman_filter(src) => float value1= na float value2 = na value1 := 0.2 * (src - src[1]) + 0.8 * nz(value1[1]) value2 := 0.1 * (ta.tr) + 0.8 * nz(value2[1]) lambda = math.abs(value1 / value2) alpha = (-math.pow(lambda, 2) + math.sqrt(math.pow(lambda, 4) + 16 * math.pow(lambda, 2)))/8 value3 = float(na) value3 := alpha * src + (1 - alpha) * nz(value3[1]) //SWITCH ma_func(modeSwitch, src, len, use_k_f=true) => modeSwitch == "SMA" ? use_kalman_filter and use_k_f ? f_kalman_filter(ta.sma(src, len)) : ta.sma(src, len) : modeSwitch == "RMA" ? use_kalman_filter and use_k_f ? f_kalman_filter(ta.rma(src, len)) : ta.rma(src, len) : modeSwitch == "EMA" ? use_kalman_filter and use_k_f ? f_kalman_filter(ta.ema(src, len)) : ta.ema(src, len) : modeSwitch == "TEMA" ? use_kalman_filter and use_k_f ? f_kalman_filter(tema(src, len)) : tema(src, len): modeSwitch == "DEMA" ? use_kalman_filter and use_k_f ? f_kalman_filter(f_dema(src, len)) : f_dema(src, len): modeSwitch == "ZLEMA" ? use_kalman_filter and use_k_f ? f_kalman_filter(f_zlema(src, len)) : f_zlema(src, len): modeSwitch == "WMA" ? use_kalman_filter and use_k_f ? f_kalman_filter(ta.wma(src, len)) : ta.wma(src, len): modeSwitch == "VWMA" ? use_kalman_filter and use_k_f ? f_kalman_filter(ta.vwma(src, len)) : ta.vwma(src, len): modeSwitch == "Hma" ? use_kalman_filter and use_k_f ? f_kalman_filter(HMA(src, len)) : HMA(src, len): modeSwitch == "Ehma" ? use_kalman_filter and use_k_f ? f_kalman_filter(EHMA(src, len)) : EHMA(src, len): modeSwitch == "Thma" ? use_kalman_filter and use_k_f ? f_kalman_filter(THMA(src, len/2)) : THMA(src, len/2): modeSwitch == "ATR" ? use_kalman_filter and use_k_f ? f_kalman_filter(ATR_func(src, len)): ATR_func(src, len) : modeSwitch == "L" ? use_kalman_filter and use_k_f ? f_kalman_filter(ta.lowest(len)): ta.lowest(len) : modeSwitch == "H" ? use_kalman_filter and use_k_f ? f_kalman_filter(ta.highest(len)): ta.highest(len) : modeSwitch == "DMA" ? donchian(len) : na //Var sum = 0.0 maopening_l = 0.0 maopening_s = 0.0 maclosing = 0.0 pos = strategy.position_size p = 0.0 p := pos == 0 ? (strategy.equity / 100) / close : p[1] truetime = true loss = 0.0 maxloss = 0.0 equity = 0.0 //MA Opening maopening_l := ma_func(maopeningtyp_l, maopeningsrc_l, maopeninglen_l) maopening_s := ma_func(maopeningtyp_s, maopeningsrc_s, maopeninglen_s) //MA Closing maclosing := ma_func(maclosingtyp, maclosingsrc, maclosinglen) * maclosingmul long1 = long1on == false ? 0 : long1shift == 0 ? 0 : long1lot == 0 ? 0 : maopening_l == 0 ? 0 : maopening_l * long1shift short1 = short1on == false ? 0 : short1shift == 0 ? 0 : short1lot == 0 ? 0 : maopening_s == 0 ? 0 : maopening_s * short1shift //Colors long1col = long1 == 0 ? na : color.green short1col = short1 == 0 ? na : color.red //Lines // plot(maopening_l, offset = OFFS, color = color.new(color.green, 50)) // plot(maopening_s, offset = OFFS, color = color.new(color.red, 50)) plot(maclosing, offset = OFFS, color = color.fuchsia) long1line = long1 == 0 ? close : long1 short1line = short1 == 0 ? close : short1 plot(long1line, offset = OFFS, color = long1col) plot(short1line, offset = OFFS, color = short1col) //Lots lotlong1 = p * long1lot lotshort1 = p * short1lot //Entry if truetime //Long sum := 0 strategy.entry("L", strategy.long, lotlong1, limit = on_close ? na : long1, when = long1 > 0 and pos <= sum and (on_close ? close <= long1[trade_offset] : true)) sum := lotlong1 //Short sum := 0 pos := -1 * pos strategy.entry("S", strategy.short, lotshort1, limit = on_close ? na : short1, when = short1 > 0 and pos <= sum and (on_close ? close >= short1[trade_offset] : true)) sum := lotshort1 strategy.exit("Exit", na, limit = maclosing) if time > finalTime strategy.close_all()