Стратегия RSI Donchian
Стратегия RSI Donchian ищет экстремумы импульса, совпадающие с пробоем канала Donchian. Индекс относительной силы оценивает перекупленность и перепроданность, тогда как канал определяет недавние максимумы и минимумы цены.
Тестирование показывает среднегодичную доходность около 82%. Стратегию лучше запускать на фондовом рынке.
Сигнал на покупку возникает, когда RSI опускается ниже 30 и цена пробивает верхнюю границу канала. Сигнал на продажу формируется, когда RSI поднимается выше 70 и цена проходит через нижнюю границу. Выходы осуществляются после возврата цены к средней линии канала, что указывает на восстановление баланса.
Метод хорошо подходит активным трейдерам, предпочитающим торговать против истощения импульса, но имеющим чёткие уровни пробоя. Стоп‑лосс помогает ограничить риск, если импульс быстро не разворачивается.
Детали
- Условия входа:
- Лонг: RSI < 30 и закрытие > Donchian High
- Шорт: RSI > 70 и закрытие < Donchian Low
- Лонг/Шорт: обе стороны.
- Условия выхода:
- Лонг: Выход при закрытии < Donchian Middle
- Шорт: Выход при закрытии > Donchian Middle
- Стопы: да, процентный стоп-лосс.
- Значения по умолчанию:
RsiPeriod= 14DonchianPeriod= 20StopLossPercent= 2mCandleType= TimeSpan.FromMinutes(15)
- Фильтры:
- Категория: Смешанная
- Направление: Оба
- Индикаторы: RSI, канал Donchian
- Стопы: Да
- Сложность: Средняя
- Таймфрейм: Внутридневной
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on RSI and Donchian Channel indicators.
/// Enters long when RSI is below 30 (oversold) and price breaks above Donchian high.
/// Enters short when RSI is above 70 (overbought) and price breaks below Donchian low.
/// Uses middle line of Donchian Channel for exit signals.
/// </summary>
public class RsiDonchianStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _donchianPeriod;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private RelativeStrengthIndex _rsi;
private Highest _highestHigh;
private Lowest _lowestLow;
private decimal _previousRsi;
private decimal _donchianHigh;
private decimal _donchianLow;
private decimal _donchianMiddle;
private decimal _currentRsi;
private int _cooldown;
/// <summary>
/// RSI period parameter.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// Donchian Channel period parameter.
/// </summary>
public int DonchianPeriod
{
get => _donchianPeriod.Value;
set => _donchianPeriod.Value = value;
}
/// <summary>
/// Bars to wait between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Stop-loss percentage parameter.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type parameter.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public RsiDonchianStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "Period for RSI calculation", "Indicators")
.SetOptimize(10, 20, 2);
_donchianPeriod = Param(nameof(DonchianPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Donchian Period", "Period for Donchian Channel calculation", "Indicators")
.SetOptimize(10, 30, 5);
_cooldownBars = Param(nameof(CooldownBars), 80)
.SetRange(1, 200)
.SetDisplay("Cooldown Bars", "Bars between trades", "General");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetGreaterThanZero()
.SetDisplay("Stop-loss %", "Stop-loss as percentage of entry price", "Risk Management")
.SetOptimize(1m, 3m, 0.5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_rsi = null;
_highestHigh = null;
_lowestLow = null;
_previousRsi = 0;
_donchianHigh = 0;
_donchianLow = 0;
_donchianMiddle = 0;
_currentRsi = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Initialize indicators
_rsi = new RelativeStrengthIndex
{
Length = RsiPeriod
};
_highestHigh = new Highest
{
Length = DonchianPeriod
};
_lowestLow = new Lowest
{
Length = DonchianPeriod
};
// Create candles subscription
var subscription = SubscribeCandles(CandleType);
// Bind indicators
subscription
.Bind(_rsi, _highestHigh, _lowestLow, ProcessIndicators)
.Start();
// Setup chart if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _rsi);
DrawOwnTrades(area);
}
}
private void ProcessIndicators(ICandleMessage candle, decimal rsiValue, decimal highestValue, decimal lowestValue)
{
// Skip unfinished candles
if (candle.State != CandleStates.Finished)
return;
// Save previous RSI value
_previousRsi = _currentRsi;
// Get current RSI value
_currentRsi = rsiValue;
// Update Donchian high value
_donchianHigh = highestValue;
// Update Donchian low value
_donchianLow = lowestValue;
// Calculate Donchian middle line
_donchianMiddle = (_donchianHigh + _donchianLow) / 2;
// Process trading logic after all indicators are updated
ProcessTradingLogic(candle);
}
private void ProcessTradingLogic(ICandleMessage candle)
{
// Skip if not all indicators are initialized
if (_donchianHigh == 0 || _donchianLow == 0 || _currentRsi == 0)
return;
// Trading signals
bool isRsiOversold = _currentRsi < 30;
bool isRsiOverbought = _currentRsi > 70;
bool isAtLowerBand = candle.ClosePrice <= _donchianLow * 1.001m;
bool isAtUpperBand = candle.ClosePrice >= _donchianHigh * 0.999m;
if (_cooldown > 0)
_cooldown--;
// Long signal: RSI < 30 (oversold) and price near Donchian low
if (_cooldown == 0 && isRsiOversold && isAtLowerBand)
{
if (Position <= 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
// Short signal: RSI > 70 (overbought) and price near Donchian high
else if (_cooldown == 0 && isRsiOverbought && isAtUpperBand)
{
if (Position >= 0)
{
SellMarket();
_cooldown = CooldownBars;
}
}
// Exit signals based on Donchian middle line
else if ((Position > 0 && candle.ClosePrice < _donchianMiddle) ||
(Position < 0 && candle.ClosePrice > _donchianMiddle))
{
if (Position > 0)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0)
{
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, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex, Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class rsi_donchian_strategy(Strategy):
"""
Strategy based on RSI and Donchian Channel indicators.
"""
def __init__(self):
super(rsi_donchian_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("RSI Period", "Period for RSI calculation", "Indicators")
self._donchian_period = self.Param("DonchianPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("Donchian Period", "Period for Donchian Channel calculation", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 80) \
.SetRange(1, 200) \
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Stop-loss %", "Stop-loss as percentage of entry price", "Risk Management")
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._previous_rsi = 0.0
self._donchian_high = 0.0
self._donchian_low = 0.0
self._donchian_middle = 0.0
self._current_rsi = 0.0
self._cooldown = 0
@property
def CandleType(self):
return self._candle_type.Value
def OnReseted(self):
super(rsi_donchian_strategy, self).OnReseted()
self._previous_rsi = 0.0
self._donchian_high = 0.0
self._donchian_low = 0.0
self._donchian_middle = 0.0
self._current_rsi = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(rsi_donchian_strategy, self).OnStarted2(time)
self._previous_rsi = 0.0
self._donchian_high = 0.0
self._donchian_low = 0.0
self._donchian_middle = 0.0
self._current_rsi = 0.0
self._cooldown = 0
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
highest = Highest()
highest.Length = self._donchian_period.Value
lowest = Lowest()
lowest.Length = self._donchian_period.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, highest, lowest, self.ProcessIndicators).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, rsi)
self.DrawOwnTrades(area)
def ProcessIndicators(self, candle, rsi_value, highest_value, lowest_value):
if candle.State != CandleStates.Finished:
return
self._previous_rsi = self._current_rsi
self._current_rsi = float(rsi_value)
self._donchian_high = float(highest_value)
self._donchian_low = float(lowest_value)
self._donchian_middle = (self._donchian_high + self._donchian_low) / 2.0
if self._donchian_high == 0 or self._donchian_low == 0 or self._current_rsi == 0:
return
price = float(candle.ClosePrice)
is_rsi_oversold = self._current_rsi < 30
is_rsi_overbought = self._current_rsi > 70
is_at_lower = price <= self._donchian_low * 1.001
is_at_upper = price >= self._donchian_high * 0.999
if self._cooldown > 0:
self._cooldown -= 1
cooldown_val = int(self._cooldown_bars.Value)
if self._cooldown == 0 and is_rsi_oversold and is_at_lower:
if self.Position <= 0:
self.BuyMarket()
self._cooldown = cooldown_val
elif self._cooldown == 0 and is_rsi_overbought and is_at_upper:
if self.Position >= 0:
self.SellMarket()
self._cooldown = cooldown_val
elif (self.Position > 0 and price < self._donchian_middle) or \
(self.Position < 0 and price > self._donchian_middle):
if self.Position > 0:
self.SellMarket()
self._cooldown = cooldown_val
elif self.Position < 0:
self.BuyMarket()
self._cooldown = cooldown_val
def CreateClone(self):
return rsi_donchian_strategy()