Estrategia Donchian RSI
Estrategia que combina los Canales Donchian y el indicador RSI. Compra en rupturas de Donchian cuando el RSI confirma que la tendencia no está sobreextendida.
Las pruebas indican un retorno anual promedio de aproximadamente el 55%. Funciona mejor en el mercado de acciones.
Los canales Donchian identifican los niveles de ruptura, mientras que el RSI verifica si el momentum respalda el movimiento. Las posiciones se abren cuando una ruptura se alinea con la dirección del RSI.
Ideal para traders que esperan una ruptura sostenida en lugar de una trampa. El riesgo se limita mediante un stop basado en ATR.
Detalles
- Criterios de entrada:
- Largo:
Close > DonchianHigh && RSI < RsiOversoldLevel - Corto:
Close < DonchianLow && RSI > RsiOverboughtLevel
- Largo:
- Largo/Corto: Ambos
- Criterios de salida:
- Fallo de ruptura o señal opuesta
- Stops: Basados en porcentaje usando
StopLossPercent - Valores predeterminados:
DonchianPeriod= 20RsiPeriod= 14RsiOverboughtLevel= 70mRsiOversoldLevel= 30mStopLossPercent= 2.0mCandleType= TimeSpan.FromMinutes(5).TimeFrame()
- Filtros:
- Categoría: Ruptura
- Dirección: Ambos
- Indicadores: Donchian Channel, RSI
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Medio plazo
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy combining manual Donchian Channels with RSI.
/// Buys on upper band breakout when RSI is not overbought.
/// Sells on lower band breakout when RSI is not oversold.
/// </summary>
public class DonchianRsiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _donchianPeriod;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiOverboughtLevel;
private readonly StrategyParam<decimal> _rsiOversoldLevel;
private readonly StrategyParam<int> _cooldownBars;
private readonly List<decimal> _highs = new();
private readonly List<decimal> _lows = new();
private int _cooldown;
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Donchian channel period.
/// </summary>
public int DonchianPeriod
{
get => _donchianPeriod.Value;
set => _donchianPeriod.Value = value;
}
/// <summary>
/// RSI period.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// RSI overbought level.
/// </summary>
public decimal RsiOverboughtLevel
{
get => _rsiOverboughtLevel.Value;
set => _rsiOverboughtLevel.Value = value;
}
/// <summary>
/// RSI oversold level.
/// </summary>
public decimal RsiOversoldLevel
{
get => _rsiOversoldLevel.Value;
set => _rsiOversoldLevel.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initialize strategy.
/// </summary>
public DonchianRsiStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_donchianPeriod = Param(nameof(DonchianPeriod), 20)
.SetRange(10, 50)
.SetDisplay("Donchian Period", "Period for Donchian Channels", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetRange(7, 21)
.SetDisplay("RSI Period", "Period for RSI", "Indicators");
_rsiOverboughtLevel = Param(nameof(RsiOverboughtLevel), 70m)
.SetDisplay("RSI Overbought", "RSI overbought level", "Trading Levels");
_rsiOversoldLevel = Param(nameof(RsiOversoldLevel), 30m)
.SetDisplay("RSI Oversold", "RSI oversold level", "Trading Levels");
_cooldownBars = Param(nameof(CooldownBars), 100)
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
.SetRange(5, 500);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highs.Clear();
_lows.Clear();
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var rsiArea = CreateChartArea();
if (rsiArea != null)
DrawIndicator(rsiArea, rsi);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var high = candle.HighPrice;
var low = candle.LowPrice;
var close = candle.ClosePrice;
_highs.Add(high);
_lows.Add(low);
var period = DonchianPeriod;
if (_highs.Count < period + 1)
{
if (_cooldown > 0) _cooldown--;
return;
}
// Previous Donchian channel (excluding current bar)
decimal prevUpper = decimal.MinValue;
decimal prevLower = decimal.MaxValue;
var count = _highs.Count;
for (int i = count - period - 1; i < count - 1; i++)
{
if (_highs[i] > prevUpper) prevUpper = _highs[i];
if (_lows[i] < prevLower) prevLower = _lows[i];
}
var middleBand = (prevUpper + prevLower) / 2m;
// Trim lists
if (_highs.Count > period * 3)
{
var trim = _highs.Count - period * 2;
_highs.RemoveRange(0, trim);
_lows.RemoveRange(0, trim);
}
if (_cooldown > 0)
{
_cooldown--;
return;
}
// Buy: upper breakout + RSI not overbought
if (close > prevUpper && rsiValue < RsiOverboughtLevel && Position == 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
// Sell: lower breakout + RSI not oversold
else if (close < prevLower && rsiValue > RsiOversoldLevel && Position == 0)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit long: price below middle
if (Position > 0 && close < middleBand)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit short: price above middle
else if (Position < 0 && close > middleBand)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class donchian_rsi_strategy(Strategy):
"""
Strategy combining manual Donchian Channels with RSI.
"""
def __init__(self):
super(donchian_rsi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._donchian_period = self.Param("DonchianPeriod", 20) \
.SetRange(10, 50) \
.SetDisplay("Donchian Period", "Period for Donchian Channels", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetRange(7, 21) \
.SetDisplay("RSI Period", "Period for RSI", "Indicators")
self._rsi_overbought_level = self.Param("RsiOverboughtLevel", 70.0) \
.SetDisplay("RSI Overbought", "RSI overbought level", "Trading Levels")
self._rsi_oversold_level = self.Param("RsiOversoldLevel", 30.0) \
.SetDisplay("RSI Oversold", "RSI oversold level", "Trading Levels")
self._cooldown_bars = self.Param("CooldownBars", 100) \
.SetDisplay("Cooldown Bars", "Bars between trades", "General") \
.SetRange(5, 500)
self._highs = []
self._lows = []
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(donchian_rsi_strategy, self).OnStarted2(time)
self._highs = []
self._lows = []
self._cooldown = 0
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
rsi_area = self.CreateChartArea()
if rsi_area is not None:
self.DrawIndicator(rsi_area, rsi)
def ProcessCandle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
rv = float(rsi_value)
self._highs.append(high)
self._lows.append(low)
period = self._donchian_period.Value
if len(self._highs) < period + 1:
if self._cooldown > 0:
self._cooldown -= 1
return
# Previous Donchian channel (excluding current bar)
count = len(self._highs)
prev_upper = max(self._highs[count - period - 1:count - 1])
prev_lower = min(self._lows[count - period - 1:count - 1])
middle_band = (prev_upper + prev_lower) / 2.0
# Trim lists
if len(self._highs) > period * 3:
trim = len(self._highs) - period * 2
self._highs = self._highs[trim:]
self._lows = self._lows[trim:]
if self._cooldown > 0:
self._cooldown -= 1
return
cd = self._cooldown_bars.Value
ob = self._rsi_overbought_level.Value
os_level = self._rsi_oversold_level.Value
# Buy: upper breakout + RSI not overbought
if close > prev_upper and rv < ob and self.Position == 0:
self.BuyMarket()
self._cooldown = cd
elif close < prev_lower and rv > os_level and self.Position == 0:
self.SellMarket()
self._cooldown = cd
# Exit long: price below middle
if self.Position > 0 and close < middle_band:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and close > middle_band:
self.BuyMarket()
self._cooldown = cd
def OnReseted(self):
super(donchian_rsi_strategy, self).OnReseted()
self._highs = []
self._lows = []
self._cooldown = 0
def CreateClone(self):
return donchian_rsi_strategy()