다이내믹 채널 브레이크아웃 전략


생성 날짜: 2023-09-16 22:46:42 마지막으로 수정됨: 2023-09-16 22:46:42
복사: 0 클릭수: 648
avatar of ChaoZhang ChaoZhang
1
집중하다
1617
수행원

개요

이 글은 동적 통로를 기반으로 한 브레이크 트레이딩 전략을 소개한다. 이 전략은 K선이나 브린 벨트로 형성된 통로를 통해 트렌드 방향을 판단하고 가격이 통로를 뚫을 때 진입한다.

전략 원칙

이 전략은 다음과 같은 것을 기반으로 합니다.

  1. 동적 통로를 사용하여 트렌드 방향을 판단하십시오. 가격이 상승하면 상승하고, 하락하면 하락합니다.

  2. 진입 시점으로 선 선 선 선 선 돌파를 선택하거나 종합 돌파를 선택한다.

  3. 다중공간을 위한 정지정지 지점을 설정할 수 있다. 예컨대 이전 돌파점, 통로 외장, ATR 등이다.

  4. 거래 시간, ATR 필터링 등의 조건을 추가하여 거래 빈도를 제어할 수 있다.

  5. 시장에서 더 많은 수익을 얻기 위해 역으로 포지션을 개설할 수 있습니다.

우위 분석

이 전략은 다음과 같은 장점을 가지고 있습니다.

  1. 동적인 통로로 트렌드를 판단하고, 쉽게 조작할 수 있다.

  2. 브레이크 포인트 거래와 스톱 스톱 손실 설정이 통일되어 명확합니다.

  3. 필터링 조건을 자유롭게 선택하여 거래 위험을 조절할 수 있습니다.

  4. 역투자는 핫포인트에서 수익을 올릴 수 있다.

  5. 전략적 논리는 간단하고 명확하며 테스트 및 최적화가 쉽다.

위험 분석

이 전략의 주요 위험은 다음과 같습니다.

  1. 시장이 급격하게 변동할 때 통로 판단이 실패할 수 있다.

  2. 가짜 침입은 잘못된 거래로 이어질 수 있다. 침입의 유효성을 평가해야 한다.

  3. 스톱톱 손실 포인트를 적절하게 설정하지 않으면 수익을 잃을 수 있습니다.

  4. 거래 빈도가 너무 높으면 거래 비용과 위험이 증가할 수 있습니다.

  5. 반전 거래의 추가적인 위험은 통제해야 합니다.

요약하다

이 전략은 동적 통로 판단과 획기적인 거래 아이디어를 결합한다. 위험을 통제하는 전제 하에서, 파라미터를 최적화하여 만족스러운 수익을 얻을 수 있다. 그러나 거래자는 여전히 잘못된 트리거의 위험에 주의를 기울이고, 적절한 시기에 전략을 조정해야 한다.

전략 소스 코드
/*backtest
start: 2023-09-12 04:00:00
end: 2023-09-15 00:00:00
period: 1m
basePeriod: 1m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

// Based on Channels Strategy [JoseMetal]
// Edited by Dimkud
//@version=5

// strategy("Channels Strategy [Dimkud - JoseMetal]", overlay=true, calc_on_order_fills=true, use_bar_magnifier=true, pyramiding=0, commission_type=strategy.commission.percent, commission_value=0.04, max_labels_count=500, default_qty_type=strategy.cash, default_qty_value=340, initial_capital=1000 )



//======Dimak Delay ======================================================================

i_qtyTimeUnits  = - input.float(1, "Delay between orders:", inline = "1", minval = 0, tooltip = "Use 0 for no delay", group="Time_Delay")
i_timeUnits     = input.string("minutes", "", inline = "1", options = ["seconds", "minutes", "hours", "days", "chart"], group="Time_Delay")
useDelay   = input.bool(false, "UseDelay", group="Time_Delay") 

// ————— Converts current chart timeframe into a float minutes value.
f_tfInMinutes() => 
    _tfInMinutes = timeframe.multiplier * (
      timeframe.isseconds ? 1. / 60             :
      timeframe.isminutes ? 1.                  :
      timeframe.isdaily   ? 60. * 24            :
      timeframe.isweekly  ? 60. * 24 * 7        :
      timeframe.ismonthly ? 60. * 24 * 30.4375  : na)

f_timeFrom(_from, _qty, _units) =>
    int _timeFrom = na
    // Remove any "s" letter in the _units argument, so we don't need to compare singular and plural unit names.
    _unit = str.replace_all(_units, "s", "")
    // Determine if we will calculate offset from the bar's time or from current time.
    _t = _from == "bar" ? time : _from == "close" ? time_close : timenow
    // Calculate time at offset.
    if _units == "chart"
        // Offset in chart res multiples.
        _timeFrom := int(_t + (f_tfInMinutes() * 60 * 1000 * _qty))
    else
        // Add the required _qty of time _units to the _from starting time.
        _year   = year(_t)       + (_unit == "year"   ? int(_qty) : 0)
        _month  = month(_t)      + (_unit == "month"  ? int(_qty) : 0)
        _day    = dayofmonth(_t) + (_unit == "day"    ? int(_qty) : 0)
        _hour   = hour(_t)       + (_unit == "hour"   ? int(_qty) : 0)
        _minute = minute(_t)     + (_unit == "minute" ? int(_qty) : 0)
        _second = second(_t)     + (_unit == "econd"  ? int(_qty) : 0)
        // Return the resulting time in ms Unix time format.
        _timeFrom := timestamp(_year, _month, _day, _hour, _minute, _second)

// Time delay filter
var float lastTradeTime = na

if nz(ta.change(strategy.position_size), time) != 0

    // An order has been executed; save the bar's time.
    lastTradeTime := time
// If user has chosen to do so, wait `i_qtyTimeUnits` `i_timeUnits` between orders

delayElapsed = useDelay ? f_timeFrom("close", i_qtyTimeUnits, i_timeUnits) >= lastTradeTime : true

// ==== Dimak Delay End ======================================================================

// ==== Dimak ATRfilterOk  ======================================================================

useAtrDelay = input.bool(false, "useAtrDelay", group="ATR Filter") 
longdAtr1  = input.int(2, "Fast Atr1", inline = "1", step=1, minval = 0, group="ATR Filter")
longdAtr2  = input.int(14, "Slow Atr2", inline = "1", step=1, minval = 0, group="ATR Filter")
dAtrFilter  = input.float(3, "Results: Atr1/Atr2", step=0.1, minval = 0, tooltip = "Short ATR too Big ?", group="ATR Filter")


dAtr1 = ta.atr(longdAtr1)
dAtr2 = ta.atr(longdAtr2)
//ATRfilterOk = true
ATRfilterOk = useAtrDelay ? (dAtr1/dAtr2) < dAtrFilter : true
// ==== Dimak ATRfilterOk End ======================================================================



c_verde_radiactivo = color.rgb(0, 255, 0, 0)
c_verde            = color.rgb(0, 128, 0, 0)
c_verde_oscuro     = color.rgb(0, 80, 0, 0)
c_rojo_radiactivo  = color.rgb(255, 0, 0, 0)
c_rojo             = color.rgb(128, 0, 0, 0)
c_rojo_oscuro      = color.rgb(80, 0, 0, 0)
noneColor          = color.new(color.white, 100)




GRUPO_per_pruebas = "Tests period"
fecha_inicio     = input(0, "Start date", group=GRUPO_per_pruebas) 
fecha_fin_usar   = input.bool(false, "Finish date", group=GRUPO_per_pruebas, inline="fecha_finalizacion") 
fecha_fin        = input(timestamp("1 Jan 2022"), "", group=GRUPO_per_pruebas, inline="fecha_finalizacion")
vela_en_fecha    = true
posicion_abierta = strategy.position_size != 0
LONG_abierto     = strategy.position_size > 0
SHORT_abierto    = strategy.position_size < 0


// DIMAK  Static SL/TP - Begin
GRUPO_statSLTP = "Static SL/TP"

// Make inputs that set the take profit % (optional)
var longProfitPerc = input.float(defval=2.6, title="Take Profit (%)", group=GRUPO_statSLTP, minval=0.0, step=0.1) / 100
var shortProfitPerc = longProfitPerc

var longSLPerc = input.float(defval=1.3, title="Stop Loss (%)", group=GRUPO_statSLTP, minval=0.0, step=0.1) / 100
var shortSLPerc = longSLPerc

// Figure out take profit price  (placed after strategy.entry("Abrir Long", strategy.long) )
longExitPrice  = strategy.position_avg_price * (1 + longProfitPerc)
shortExitPrice = strategy.position_avg_price * (1 - shortProfitPerc)

longSLExitPrice  = strategy.position_avg_price * (1 - longSLPerc)
shortSLExitPrice = strategy.position_avg_price * (1 + shortSLPerc)

// use tak: limit=shortExitPrice, stop=shortSLExitPrice

// DIMAK  Static SL/TP - END



//== Strategy entry and exit conditions
GRUPO_P           = "Posiciones"

P_indicador       = input.string("Keltner Channel", "Indicator", ["Bollinger Bands", "Keltner Channel"], group=GRUPO_P)
P_permitir_LONGS  = input.bool(title="Use LONGS ?", group=GRUPO_P, defval=true)
P_permitir_SHORTS = input.bool(title="Use SHORTS ?", group=GRUPO_P, defval=false)

P_cond_entrada    = input.string("Wick out of band", "Enter Condition", ["Wick out of band", "Wick out of the band then close in", "Out-of-band closure", "Close out of the band then close in"], "Se puede escoger (en orden) que el precio haya salido de la banda, que además la siguiente vela cierre de nuevo dentro de la misma, que el precio tenga que cerrar fuera, y que además la siguiente vela tenga que cerrar dentro de nuevo.", group=GRUPO_P)


GRUPO_TPSL       = "Stop Loss and Take Profit"

TP_SL_tipo_SL    = input.string("useStaticSLTP", "Stop Loss Type", options=["Previous Wick", "Extended Band", "ATR", "useStaticSLTP"], group=GRUPO_TPSL)
TP_SL_tipo_TP    = input.string("useStaticSLTP",  "Take Profit Type", options=["Opposite Band", "Moving Average", "ATR", "useStaticSLTP"], group=GRUPO_TPSL)
TPSL_SL_ATR_mult = input.float(title="• (Solo ATR) Multiplicador Stop Loss", group=GRUPO_TPSL, defval=1, minval=0.1, step=0.1, inline="tp_sl", tooltip="These are the ATR multipliers to calculate STOP LOSS and TAKE PROFIT if selected as such.")
TPSL_TP_ATR_mult = input.float(title="• (Solo ATR) Multiplicador Take Profit", group=GRUPO_TPSL, defval=1.8, minval=0.1, step=0.1, inline="tp_sl")
TPSL_SL_BB_dev   = input.float(title="• (Solo STOP LOSS con BB) Desviación estándar", group=GRUPO_TPSL, defval=4.0, minval=0.01, step=0.5, tooltip="In case of using Bollinger Bands as STOP LOSS, this will be the value of its standard deviation.")
TPSL_SL_KC_mult  = input.float(title="• (Solo STOP LOSS con KC) Multiplicador", group=GRUPO_TPSL, defval=3, minval=0.01, step=0.5, tooltip="In case of using Keltner channels as STOP LOSS, this will be the value of your ATR multiplier.")
TP_SL_TP_dinamico = input.bool(title="Take Profit dinámico", group=GRUPO_TPSL, defval=false, tooltip="This will make the Take Profit adjust bar by bar instead of staying fixed at its initial value.")

GRUPO_KC  = "Keltner Channel"
KC_length = input.int(title="Keltner Long.", group=GRUPO_KC, defval=14, minval=1, inline="kc")
KC_mult   = input.float(title="Keltner Mult.", group=GRUPO_KC, defval=1.5, minval=0.01, step=0.05, inline="kc")
[KC_mid, KC_upper, KC_lower]  = ta.kc(close, KC_length, KC_mult, true)
[_, KC_upper_SL, KC_lower_SL] = ta.kc(close, KC_length, TPSL_SL_KC_mult, true)





// dimak KC timeframe

GRUPO_KC_TF  = "Keltner Channel - Multi TimeFrame"
KC_TF_length = input.int(title="Keltner TF Long:", group=GRUPO_KC_TF, defval=20, minval=1, inline="kc")
KC_TF_mult   = input.float(title="Keltner TF Mult:", group=GRUPO_KC_TF, defval=2, minval=0.01, step=0.05, inline="kc")
tf_indicator = input.timeframe(title="TimeFrame:", defval = '', group=GRUPO_KC_TF, inline = "03")

EMAi   = ta.ema(close, KC_TF_length)
[Keltmiddle, Keltupper, Keltlower] = ta.kc(close, KC_TF_length, KC_TF_mult)

KC_TF_upper  = request.security(syminfo.tickerid, tf_indicator, Keltupper, gaps=barmerge.gaps_off)
KC_TF_lower  = request.security(syminfo.tickerid, tf_indicator, Keltlower, gaps=barmerge.gaps_off)
KC_TF_mid = request.security(syminfo.tickerid, tf_indicator, EMAi, gaps=barmerge.gaps_off)

// dimak KC timeframe  END





//== Inputs de indicadores
// ATR
GRUPO_ATR      = "ATR"
ATR_referencia = input.source(title="ATR Reference", group=GRUPO_ATR, defval=close, inline="atr_calc") // The font is not applied to the ATR calculation, it is for the positioning with respect to the price on the chart
ATR_length     = input.int(title="ATR Length", group=GRUPO_ATR, defval=7, minval=1, inline="atr_calc")
ATR            = ta.atr(ATR_length)
ATR_sl         = ATR * TPSL_SL_ATR_mult
ATR_tp         = ATR * TPSL_TP_ATR_mult
ATR_LONG_sl    = ATR_referencia - ATR_sl // Conversely, the lower one can be used as STOP LOSS or TRAILING STOP
ATR_LONG_tp    = ATR_referencia + ATR_tp // The ATR on the candles can be used as TAKE PROFIT
ATR_SHORT_sl   = ATR_referencia + ATR_sl // ""
ATR_SHORT_tp   = ATR_referencia - ATR_tp // For Shorts it's the other way around

GRUPO_BB  = "Bollinger Bands"
BB_length = input.int(title="BB Long. ", group=GRUPO_BB, defval=20, minval=1, inline="bb")
BB_dev   = input.float(title="BB Deviation (Desv.)", group=GRUPO_BB, defval=2.0, minval=0.01, step=0.5, inline="bb")
[BB_mid, BB_upper, BB_lower]  = ta.bb(close, BB_length, BB_dev)
[_, BB_upper_SL, BB_lower_SL] = ta.bb(close, BB_length, TPSL_SL_BB_dev)



//== Calculation of conditions
// Assign common variables based on the selected indicator
banda_superior = BB_upper
media_movil    = BB_mid
banda_inferior = BB_lower
banda_extendida_sup_SL = BB_upper_SL
banda_extendida_inf_SL = BB_lower_SL

if (P_indicador == "Keltner Channel")
    banda_superior := KC_upper
    media_movil    := KC_mid
    banda_inferior := KC_lower
    banda_extendida_sup_SL := KC_upper_SL
    banda_extendida_inf_SL := KC_lower_SL

// Calcular condiciones de entrada
longCondition1  = false
shortCondition1 = false

if (P_cond_entrada == "Wick out of band")
    longCondition1  := low < banda_inferior
    shortCondition1 := high > banda_superior
else if (P_cond_entrada == "Wick out of the band then close in")
    longCondition1  := low[1] < banda_inferior and close > banda_inferior
    shortCondition1 := high[1] > banda_superior and close < banda_superior
else if (P_cond_entrada == "Out-of-band closure")
    longCondition1  := close < banda_inferior
    shortCondition1 := close > banda_superior
else // Close out of the band then close in
    longCondition1  := close[1] < banda_inferior and close > banda_inferior
    shortCondition1 := close[1] > banda_superior and close < banda_superior



//== Entrada (deben cumplirse todas para entrar)
longCondition2  = true
longCondition3  = true
long_conditions = longCondition1 and longCondition2 and longCondition3
entrar_en_LONG  = P_permitir_LONGS and long_conditions and vela_en_fecha and ATR > 0.0                            // Lo del ATR > 0.0 es por seguridad ya que puede darse una entrada donde aún no es calculable el ATR porque no existan velas y nunca cerrar posición pues no se creó correctamente // Solo permitir 1 posición al mismo tiempo

shortCondition2  = true
shortCondition3  = true
short_conditions = shortCondition1 and shortCondition2 and shortCondition3
entrar_en_SHORT  = P_permitir_SHORTS and short_conditions and vela_en_fecha and ATR > 0.0 // Lo del ATR > 0.0 es por seguridad ya que puede darse una entrada donde aún no es calculable el ATR porque no existan velas y nunca cerrar posición pues no se creó correctamente // Solo permitir 1 posición al mismo tiempo

var LONG_take_profit  = 0.0
var LONG_stop_loss    = 0.0
var SHORT_take_profit = 0.0
var SHORT_stop_loss   = 0.0


if (entrar_en_LONG and not posicion_abierta and delayElapsed and ATRfilterOk)
    strategy.entry("L", strategy.long, alert_message="enter_cycle")
 

else if (entrar_en_SHORT and not posicion_abierta and delayElapsed and ATRfilterOk)
    strategy.entry("S", strategy.short, alert_message="enter_cycle")
    
   
SHORT_stop_loss   := TP_SL_tipo_SL == "useStaticSLTP" ? shortSLExitPrice : TP_SL_tipo_SL == "Previous Wick" ? (P_cond_entrada == "Wick out of band" or P_cond_entrada == "Out-of-band closure" ? high[1] : high) : TP_SL_tipo_SL == "Extended Band" ? banda_extendida_sup_SL : ATR_SHORT_sl
SHORT_take_profit := TP_SL_tipo_TP == "useStaticSLTP" ? shortExitPrice : TP_SL_tipo_TP == "Opposite Band" ? banda_inferior : TP_SL_tipo_TP == "Moving Average" ? media_movil : ATR_SHORT_tp
    
LONG_stop_loss   := TP_SL_tipo_SL == "useStaticSLTP" ? longSLExitPrice : TP_SL_tipo_SL == "Previous Wick" ? (P_cond_entrada == "Wick out of band" or P_cond_entrada == "Out-of-band closure" ? low[1] : low) : TP_SL_tipo_SL == "Extended Band" ? banda_extendida_inf_SL : ATR_LONG_sl
LONG_take_profit := TP_SL_tipo_TP == "useStaticSLTP" ? longExitPrice : TP_SL_tipo_TP == "Opposite Band" ? banda_superior : TP_SL_tipo_TP == "Moving Average" ? media_movil : ATR_LONG_tp
   
strategy.exit("CL", "L", limit=LONG_take_profit, stop=LONG_stop_loss, alert_message="stoploss")
strategy.exit("CS", "S", limit=SHORT_take_profit, stop=SHORT_stop_loss, alert_message="stoploss")

// use {{strategy.order.alert_message}} in Alerts


// Not using by Dimak
if (posicion_abierta and TP_SL_TP_dinamico)
    if (LONG_abierto)
        LONG_take_profit := TP_SL_tipo_TP == "Opposite Band" ? banda_superior : TP_SL_tipo_TP == "Moving Average" ? media_movil : ATR_LONG_tp
        strategy.exit("Cerrar Long", "Abrir Long", limit=LONG_take_profit, stop=LONG_stop_loss, alert_message="stoploss")
    else
        SHORT_take_profit := TP_SL_tipo_TP == "Opposite Band" ? banda_inferior : TP_SL_tipo_TP == "Moving Average" ? media_movil : ATR_SHORT_tp
        strategy.exit("Cerrar Short", "Abrir Short", limit=SHORT_take_profit, stop=SHORT_stop_loss, alert_message="stoploss")



//== Ploteo en pantalla
bgcolor(entrar_en_LONG ? color.new(color.green, 90) : entrar_en_SHORT ? color.new(color.red, 90) : noneColor)

// ATR
plot(TP_SL_tipo_TP == "ATR" ? ATR_LONG_tp : na, style=plot.style_stepline, color=color.new(color.green, 80), linewidth=1)
plot(TP_SL_tipo_SL == "ATR" ? ATR_LONG_sl : na, style=plot.style_stepline, color=color.new(color.red, 80), linewidth=1)
plot(TP_SL_tipo_TP == "ATR" ? ATR_SHORT_tp : na, style=plot.style_stepline, color=color.new(color.green, 80), linewidth=1)
plot(TP_SL_tipo_SL == "ATR" ? ATR_SHORT_sl : na, style=plot.style_stepline, color=color.new(color.red, 80), linewidth=1)

// Canal y media
plot(banda_superior, "Banda superior", color.aqua)
plot(media_movil, "Moving Average", color.orange)
plot(banda_inferior, "Banda inferior", color.aqua)

// Bandas extendidas
plot(TP_SL_tipo_SL == "Extended Band" ? banda_extendida_sup_SL : na, "Banda superior extendida (Stop Loss)", color.red, style=plot.style_circles)
plot(TP_SL_tipo_SL == "Extended Band" ? banda_extendida_inf_SL : na, "Banda inferior extendida (Stop Loss)", color.red, style=plot.style_circles)

// Precio de compra, Take Profit, Stop Loss y relleno
avg_position_price_plot = plot(series=posicion_abierta ? strategy.position_avg_price : na, color=color.new(color.white, 25), style=plot.style_linebr, linewidth=2, title="Precio Entrada")

LONG_tp_plot            = plot(LONG_abierto and LONG_take_profit > 0.0 ? LONG_take_profit : na, color=color.new(color.lime, 25), style=plot.style_linebr, linewidth=3, title="LONG Take Profit")
LONG_sl_plot            = plot(LONG_abierto and LONG_stop_loss > 0.0? LONG_stop_loss : na, color=color.new(color.red, 25), style=plot.style_linebr, linewidth=3, title="Long Stop Loss")
fill(avg_position_price_plot, LONG_tp_plot, color=color.new(color.olive, 85))
fill(avg_position_price_plot, LONG_sl_plot, color=color.new(color.maroon, 85))

SHORT_tp_plot            = plot(SHORT_abierto and SHORT_take_profit > 0.0 ? SHORT_take_profit : na, color=color.new(color.lime, 25), style=plot.style_linebr, linewidth=3, title="SHORT Take Profit")
SHORT_sl_plot            = plot(SHORT_abierto and SHORT_stop_loss > 0.0 ? SHORT_stop_loss : na, color=color.new(color.red, 25), style=plot.style_linebr, linewidth=3, title="Short Stop Loss")
fill(avg_position_price_plot, SHORT_tp_plot, color=color.new(color.olive, 85))
fill(avg_position_price_plot, SHORT_sl_plot, color=color.new(color.maroon, 85))