이 전략은 볼링거 밴드 (Bollinger Band) 와 켈트너 채널 (Keltner Channels) 을 사용하여 가격 채널 거래를 기반으로 합니다. 이 전략은 상부 또는 하부 채널 경계선을 뚫고 넘어선 후 가격 반등의 특성으로 거래 기회를 식별합니다. 이 전략은 자산과 시간 틀에 맞게 정비할 수 있는 여러 가지 사용자 정의 옵션을 제공합니다.
이 전략의 주요 구성 요소는 다음과 같습니다.
채널 표시기: 볼링거 밴드 또는 켈트너 채널
입국 조건: 채널 경계의 가격 파업, 다음 촛불 폐쇄에 내부로 회귀를 요구 옵션
손실 중지: 이전 촛불
이윤 을 취하라: 반대 채널 대역, 채널 중선, ATR 수익
다른 구성: 단장만, 단장만, 동적 취익 조정 등
위의 조합을 통해 전략은 시장 조건에 따라 최적의 진입점과 출구점을 동적으로 결정합니다.
고정 이동 스톱/이익 취득 전략에 비해 이 전략은 다음과 같은 장점을 가지고 있습니다.
가격 변동을 억제하는 채널의 능력을 활용하여 트렌드 반전 지점을 더 정확하게 포착합니다.
다양한 스톱 로스 옵션과 수익 취득 옵션은 다양한 자산과 시간 프레임에 최적의 구성을 허용합니다.
채널 지표에 기반한 브레이크아웃 및 리버션 작은 범위 오스칠레이션을 활용하여 상대의 자금을 흡수할 수 있습니다.
ATR에 의해 계산된 스톱/트랙 이윤 거리는 시장 변동성에 자동으로 조정됩니다.
동적 취득 조정 허용 잠재적인 추가 실행 공간을 완전히 활용합니다.
이 전략의 주요 위험은 다음과 같습니다.
강한 트렌드 시장에서 채널 실패는 스톱 손실을 유발할 수 있습니다. 스톱 손실 거리는 느려질 수 있습니다.
높은 변동성으로 ATR 계산된 스톱/트랙 이윤 거리는 너무 넓어 손실을 증가시킬 수 있습니다. ATR 계수를 줄이는 것을 고려하십시오.
범위 시장에서 채널 경계를 자주 만지는 것은 거래가 과도하게 될 수 있습니다.
극심한 반전은 동적 조정으로 수익을 취하기 전에 손실을 멈추게 될 수 있습니다. 주요 S / R 수준 근처에서 출출하는 것이 좋습니다.
이 전략은 다음으로 더 최적화 될 수 있습니다.
최대 마취량에 대한 영향에 대한 ATR 기간 매개 변수 테스트.
트렌드가 명확하지 않을 때 거래를 중단하기 위해 트렌드 지표를 포함합니다.
주요 S/R 구역 근처의 위치 크기를 줄이기 위해 JOIN 회귀 기술을 테스트합니다.
단일 거래 손실을 제한하기 위해 거래 크기 통제를 추가합니다.
최적의 매개 변수 조합을 찾기 위해 특정 자산에 대한 매개 변수 최적화
요약하자면, 이것은 채널 브레이크아웃 및 리버션 전략입니다. 다양한 시장 환경에 대한 풍부한 구성 옵션을 제공합니다. 장점은 반전 지점과 유연성을 캡처합니다. 그러나 강한 트렌드에서 스톱 로스 무효화 조심하십시오. 트렌드, 리스크 제어 등에 대한 미래 최적화는 실용성을 향상시킬 것입니다.
/*backtest start: 2022-10-10 00:00:00 end: 2023-10-16 00:00:00 period: 1d basePeriod: 1h exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}] */ // Copyright © 2022, José Manuel Gassin Pérez-Traverso, All rights reserved. // © JoseMetal //@version=5 // Ésta estrategia se basa en los rebotes de canales (ya sea Bandas de Bollinger o Keltner Channel) para entrar y salir de posiciones, tiene multitud de opciones para // elegir el tipo de stop loss o take profit, ya sea utilizando mínimos/máximos anteriores, ATR o las propias bandas. // La entrada de posiciones también tiene variedad de opciones, por ejemplo, se puede entrar cuando el precio salga de la banda y vuelva a entrar, o simplemente en cuanto una vela cierre fuera. // De éste modo y con todas éstas opciones se puede realizar un backtest exhaustivo para buscar la mejor combinación según activo y temporalidad. //== Constantes 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) //== Declarar estrategia y período de testeo strategy("Estrategia de Canales [JoseMetal]", shorttitle="Estrategia de Canales [JoseMetal]", overlay=true, initial_capital=10000, pyramiding=0, default_qty_value=10, default_qty_type=strategy.percent_of_equity, commission_type=strategy.commission.percent, commission_value=0.0, max_labels_count=500, max_bars_back=1000) fecha_inicio = input(timestamp("1 Jan 2000"), title="• Fecha de inicio", group="Período de pruebas", inline="periodo_de_pruebas") vela_en_fecha = true posicion_abierta = strategy.position_size != 0 LONG_abierto = strategy.position_size > 0 SHORT_abierto = strategy.position_size < 0 //== Condiciones de entrada y salida de estrategia GRUPO_P = "Posiciones" P_indicador = input.string("Canales de Keltner", "Indicador", ["Bandas de Bollinger", "Canales de Keltner"], "Se puede escoger entre los indicadores de Bandas de Bollinger y Canales de Keltner para las condiciones, éstos se usarán también para el Stop Loss y Take Profit y se escogen para dicha función.", group=GRUPO_P) P_permitir_LONGS = input.bool(title="¿LONGS?", group=GRUPO_P, defval=true) P_permitir_SHORTS = input.bool(title="¿SHORTS?", group=GRUPO_P, defval=true) P_cond_entrada = input.string("Cierre fuera de la banda y luego cierre dentro", "Condición de entrada", ["Mecha fuera de la banda", "Mecha fuera de la banda y luego cierre dentro", "Cierre fuera de la banda", "Cierre fuera de la banda y luego cierre dentro"], "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 y Take Profit" TP_SL_tipo_SL = input.string("Banda extendida", "Tipo de Stop Loss", options=["Mecha anterior", "Banda extendida", "ATR"], group=GRUPO_TPSL) TP_SL_tipo_TP = input.string("Banda contraria", "Tipo de Take Profit", options=["Banda contraria", "Media móvil", "ATR"], group=GRUPO_TPSL) TPSL_SL_ATR_mult = input.float(title="• (Solo ATR) Multiplicador Stop Loss / Take Profit", group=GRUPO_TPSL, defval=1, minval=0.1, step=0.1, inline="tp_sl", tooltip="Éstos son los multiplicadores al ATR para calcular STOP LOSS y TAKE PROFIT en caso de seleccionarse como tales.") TPSL_TP_ATR_mult = input.float(title="", 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="En caso de usar las Bandas de Bollinger como STOP LOSS, éste será el valor de su desviación estándar.") 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="En caso de usar los canales de Keltner como STOP LOSS, éste será el valor de su multiplicador de ATR.") TP_SL_TP_dinamico = input.bool(title="Take Profit dinámico", group=GRUPO_TPSL, defval=false, tooltip="Ésto hará que el Take Profit se ajuste vela a vela en lugar de quedarse fijo en su valor inicial.") //== Inputs de indicadores // ATR GRUPO_ATR = "ATR" ATR_referencia = input.source(title="• Referencia / Longitud", group=GRUPO_ATR, defval=close, inline="atr_calc") // La fuente no se aplica al cálculo del ATR, es para el posicionamiento respecto al precio en el gráfico ATR_length = input.int(title="", 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 // De forma contraria el inferior se puede usar como STOP LOSS o TRAILING STOP ATR_LONG_tp = ATR_referencia + ATR_tp // El ATR sobre las velas se puede usar como TAKE PROFIT ATR_SHORT_sl = ATR_referencia + ATR_sl // "" ATR_SHORT_tp = ATR_referencia - ATR_tp // Para Shorts es al revés GRUPO_BB = "Bollinger Bands" BB_length = input.int(title="• Long. / Desv. ", group=GRUPO_BB, defval=20, minval=1, inline="bb") BB_dev = input.float(title="", 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) GRUPO_KC = "Keltner Channel" KC_length = input.int(title="• Long. / Mult. ", group=GRUPO_KC, defval=35, minval=1, inline="kc") KC_mult = input.float(title="", group=GRUPO_KC, defval=1.5, minval=0.01, step=0.5, 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) //== Cálculo de condiciones // Asignar variables comunes en función del indicador seleccionado 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 == "Canales de Keltner") 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 == "Mecha fuera de la banda") longCondition1 := low < banda_inferior shortCondition1 := high > banda_superior else if (P_cond_entrada == "Mecha fuera de la banda y luego cierre dentro") longCondition1 := low[1] < banda_inferior and close > banda_inferior shortCondition1 := high[1] > banda_superior and close < banda_superior else if (P_cond_entrada == "Cierre fuera de la banda") longCondition1 := close < banda_inferior shortCondition1 := close > banda_superior else // Cierre fuera de la banda y luego cierre dentro 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 not posicion_abierta 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 not posicion_abierta 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) LONG_stop_loss := TP_SL_tipo_SL == "Mecha anterior" ? (P_cond_entrada == "Mecha fuera de la banda" or P_cond_entrada == "Cierre fuera de la banda" ? low[1] : low) : TP_SL_tipo_SL == "Banda extendida" ? banda_extendida_inf_SL : ATR_LONG_sl LONG_take_profit := TP_SL_tipo_TP == "Banda contraria" ? banda_superior : TP_SL_tipo_TP == "Media móvil" ? media_movil : ATR_LONG_tp strategy.entry("Abrir Long", strategy.long) strategy.exit("Cerrar Long", "Abrir Long", limit=LONG_take_profit, stop=LONG_stop_loss) else if (entrar_en_SHORT) SHORT_stop_loss := TP_SL_tipo_SL == "Mecha anterior" ? (P_cond_entrada == "Mecha fuera de la banda" or P_cond_entrada == "Cierre fuera de la banda" ? high[1] : high) : TP_SL_tipo_SL == "Banda extendida" ? banda_extendida_sup_SL : ATR_SHORT_sl SHORT_take_profit := TP_SL_tipo_TP == "Banda contraria" ? banda_inferior : TP_SL_tipo_TP == "Media móvil" ? media_movil : ATR_SHORT_tp strategy.entry("Abrir Short", strategy.short) strategy.exit("Cerrar Short", "Abrir Short", limit=SHORT_take_profit, stop=SHORT_stop_loss) if (posicion_abierta and TP_SL_TP_dinamico) if (LONG_abierto) LONG_take_profit := TP_SL_tipo_TP == "Banda contraria" ? banda_superior : TP_SL_tipo_TP == "Media móvil" ? media_movil : ATR_LONG_tp strategy.exit("Cerrar Long", "Abrir Long", limit=LONG_take_profit, stop=LONG_stop_loss) else SHORT_take_profit := TP_SL_tipo_TP == "Banda contraria" ? banda_inferior : TP_SL_tipo_TP == "Media móvil" ? media_movil : ATR_SHORT_tp strategy.exit("Cerrar Short", "Abrir Short", limit=SHORT_take_profit, stop=SHORT_stop_loss) //== 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, "Media móvil", color.orange) plot(banda_inferior, "Banda inferior", color.aqua) // Bandas extendidas plot(TP_SL_tipo_SL == "Banda extendida" ? banda_extendida_sup_SL : na, "Banda superior extendida (Stop Loss)", color.red, style=plot.style_circles) plot(TP_SL_tipo_SL == "Banda extendida" ? 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))