Стратегия Donchian Volume
Donchian Volume использует пробои канала Дончиана, подтверждённые ростом объёма, для открытия сделок. Выход цены за пределы канала на высоком объёме указывает на зарождение нового тренда.
Тестирование показывает среднегодичную доходность около 160%. Стратегию лучше запускать на рынке Форекс.
Стратегия открывает позицию в направлении пробоя и закрывает её, когда цена возвращается внутрь канала или объём снижается.
Стопы устанавливаются на небольшом расстоянии внутри канала, чтобы защититься от ложных движений.
Подробности
- Условия входа: сигналы индикаторов
- Длинные/короткие: обе стороны
- Условия выхода: стоп-лосс или противоположный сигнал
- Стопы: да, основанные на проценте
- Значения по умолчанию:
CandleType= 15 минутStopLoss= 2%
- Фильтры:
- Категория: Пробой
- Направление: Оба
- Индикаторы: Канал Дончиана, Объём
- Стопы: Да
- Сложность: Средняя
- Таймфрейм: Внутридневной
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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 that uses Donchian Channels for breakout detection.
/// Enters when price breaks above/below the channel.
/// </summary>
public class DonchianVolumeStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _donchianPeriod;
private readonly StrategyParam<int> _cooldownBars;
private int _cooldown;
private readonly List<decimal> _highs = new();
private readonly List<decimal> _lows = new();
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Donchian Channels period.
/// </summary>
public int DonchianPeriod
{
get => _donchianPeriod.Value;
set => _donchianPeriod.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Strategy constructor.
/// </summary>
public DonchianVolumeStrategy()
{
_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 of the Donchian Channel", "Indicators");
_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();
_cooldown = 0;
_highs.Clear();
_lows.Clear();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Use a simple SMA as a binding indicator (we use it for middle line reference)
var sma = new SimpleMovingAverage { Length = DonchianPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
_highs.Add(candle.HighPrice);
_lows.Add(candle.LowPrice);
var maxBuf = DonchianPeriod * 2;
if (_highs.Count > maxBuf)
{
_highs.RemoveRange(0, _highs.Count - maxBuf);
_lows.RemoveRange(0, _lows.Count - maxBuf);
}
if (_highs.Count < DonchianPeriod)
return;
// Calculate Donchian Channel
var start = _highs.Count - DonchianPeriod;
var highestHigh = decimal.MinValue;
var lowestLow = decimal.MaxValue;
for (var i = start; i < _highs.Count; i++)
{
if (_highs[i] > highestHigh) highestHigh = _highs[i];
if (_lows[i] < lowestLow) lowestLow = _lows[i];
}
var middleLine = (highestHigh + lowestLow) / 2;
var close = candle.ClosePrice;
if (_cooldown > 0)
{
_cooldown--;
return;
}
// Long entry: price breaks above channel
if (close >= highestHigh && Position == 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
// Short entry: price breaks below channel
else if (close <= lowestLow && Position == 0)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit long: price crosses below middle line
if (Position > 0 && close < middleLine)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit short: price crosses above middle line
else if (Position < 0 && close > middleLine)
{
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class donchian_volume_strategy(Strategy):
"""
Donchian Volume strategy.
Uses manual Donchian Channels for breakout detection.
Enters when price breaks above/below the channel.
"""
def __init__(self):
super(donchian_volume_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._donchian_period = self.Param("DonchianPeriod", 20).SetDisplay("Donchian Period", "Period of the Donchian Channel", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 100).SetDisplay("Cooldown Bars", "Bars between trades", "General")
self._cooldown = 0
self._highs = []
self._lows = []
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(donchian_volume_strategy, self).OnReseted()
self._cooldown = 0
self._highs = []
self._lows = []
def OnStarted2(self, time):
super(donchian_volume_strategy, self).OnStarted2(time)
self._cooldown = 0
self._highs = []
self._lows = []
sma = SimpleMovingAverage()
sma.Length = self._donchian_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, sma_val):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
cd = self._cooldown_bars.Value
period = self._donchian_period.Value
self._highs.append(high)
self._lows.append(low)
max_buf = period * 2
if len(self._highs) > max_buf:
self._highs = self._highs[-max_buf:]
self._lows = self._lows[-max_buf:]
if len(self._highs) < period:
return
# Calculate Donchian Channel
recent_h = self._highs[-period:]
recent_l = self._lows[-period:]
highest_high = max(recent_h)
lowest_low = min(recent_l)
middle_line = (highest_high + lowest_low) / 2.0
if self._cooldown > 0:
self._cooldown -= 1
return
# Long entry: price breaks above channel
if close >= highest_high and self.Position == 0:
self.BuyMarket()
self._cooldown = cd
# Short entry: price breaks below channel
elif close <= lowest_low and self.Position == 0:
self.SellMarket()
self._cooldown = cd
# Exit long: price crosses below middle line
if self.Position > 0 and close < middle_line:
self.SellMarket()
self._cooldown = cd
# Exit short: price crosses above middle line
elif self.Position < 0 and close > middle_line:
self.BuyMarket()
self._cooldown = cd
def CreateClone(self):
return donchian_volume_strategy()