Donchian 通道宽度突破策略
该策略观察 Donchian 通道宽度的扩张情况。当宽度显著超出常态时,波动往往开始增强并可能形成趋势。
测试表明年均收益约为 61%,该策略在加密市场表现最佳。
当宽度突破根据历史数据和倍数设定的阈值时建仓,可多可空,并配合止损。宽度回到均值附近即平仓。
适合动量交易者在突破初期介入。
详细信息
- 入场条件: Indicator exceeds average by deviation multiplier.
- Long/Short: 双向 directions.
- 退出条件: Indicator reverts to average.
- 止损: 是
- 默认值:
DonchianPeriod= 20AvgPeriod= 20Multiplier= 2.0mCandleType= TimeSpan.FromMinutes(5)StopLoss= 2.0m
- 筛选条件:
- 类别: 突破
- 方向: 双向
- 指标: Donchian
- 止损: 是
- 复杂度: 中等
- 时间框架: 短期
- 季节性: 否
- 神经网络: 否
- 背离: 否
- 风险等级: 中等
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Strategy that trades on Donchian Channel width breakouts.
/// When Donchian Channel width increases significantly above its average,
/// it enters position in the direction determined by price movement.
/// </summary>
public class DonchianWidthBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _donchianPeriod;
private readonly StrategyParam<decimal> _widthThreshold;
private readonly StrategyParam<DataType> _candleType;
/// <summary>
/// Donchian Channel period.
/// </summary>
public int DonchianPeriod
{
get => _donchianPeriod.Value;
set => _donchianPeriod.Value = value;
}
/// <summary>
/// Width threshold multiplier for breakout detection.
/// </summary>
public decimal WidthThreshold
{
get => _widthThreshold.Value;
set => _widthThreshold.Value = value;
}
/// <summary>
/// Candle type for strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="DonchianWidthBreakoutStrategy"/>.
/// </summary>
public DonchianWidthBreakoutStrategy()
{
_donchianPeriod = Param(nameof(DonchianPeriod), 20)
.SetDisplay("Donchian Period", "Period for the Donchian Channel", "Indicators");
_widthThreshold = Param(nameof(WidthThreshold), 1.2m)
.SetDisplay("Width Threshold", "Threshold multiplier for width breakout", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var highest = new Highest { Length = DonchianPeriod };
var lowest = new Lowest { Length = DonchianPeriod };
var widthAverage = new SimpleMovingAverage { Length = Math.Max(5, DonchianPeriod / 2) };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, lowest, (candle, highestValue, lowestValue) =>
{
if (candle.State != CandleStates.Finished)
return;
var width = highestValue - lowestValue;
if (width <= 0)
return;
var avgWidthValue = widthAverage.Process(new DecimalIndicatorValue(widthAverage, width, candle.ServerTime) { IsFinal = true });
if (!widthAverage.IsFormed)
return;
var avgWidth = avgWidthValue.ToDecimal();
if (avgWidth <= 0)
return;
var middleChannel = (highestValue + lowestValue) / 2m;
// Width breakout detection
if (width > avgWidth * WidthThreshold && Position == 0)
{
if (candle.ClosePrice > middleChannel)
BuyMarket();
else if (candle.ClosePrice < middleChannel)
SellMarket();
}
})
.Start();
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent)
);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
}
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, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Indicators import Highest, Lowest, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class donchian_width_breakout_strategy(Strategy):
"""
Strategy that trades on Donchian Channel width breakouts.
When Donchian Channel width increases significantly above its average,
it enters position in the direction determined by price movement.
"""
def __init__(self):
super(donchian_width_breakout_strategy, self).__init__()
self._donchianPeriod = self.Param("DonchianPeriod", 20) \
.SetDisplay("Donchian Period", "Period for the Donchian Channel", "Indicators")
self._widthThreshold = self.Param("WidthThreshold", 1.2) \
.SetDisplay("Width Threshold", "Threshold multiplier for width breakout", "Trading")
self._candleType = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._widthAverage = None
@property
def DonchianPeriod(self):
return self._donchianPeriod.Value
@DonchianPeriod.setter
def DonchianPeriod(self, value):
self._donchianPeriod.Value = value
@property
def WidthThreshold(self):
return self._widthThreshold.Value
@WidthThreshold.setter
def WidthThreshold(self, value):
self._widthThreshold.Value = value
@property
def CandleType(self):
return self._candleType.Value
@CandleType.setter
def CandleType(self, value):
self._candleType.Value = value
def OnReseted(self):
super(donchian_width_breakout_strategy, self).OnReseted()
def OnStarted2(self, time):
super(donchian_width_breakout_strategy, self).OnStarted2(time)
highest = Highest()
highest.Length = self.DonchianPeriod
lowest = Lowest()
lowest.Length = self.DonchianPeriod
donchian_period = self.DonchianPeriod
self._widthAverage = SimpleMovingAverage()
self._widthAverage.Length = max(5, donchian_period // 2)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(highest, lowest, self.ProcessCandle).Start()
self.StartProtection(
takeProfit=Unit(2, UnitTypes.Percent),
stopLoss=Unit(1, UnitTypes.Percent)
)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, highest_value, lowest_value):
if candle.State != CandleStates.Finished:
return
width = float(highest_value) - float(lowest_value)
if width <= 0:
return
avg_result = process_float(self._widthAverage, width, candle.ServerTime, True)
if not self._widthAverage.IsFormed:
return
avg_width = float(avg_result)
if avg_width <= 0:
return
middle_channel = (float(highest_value) + float(lowest_value)) / 2.0
# Width breakout detection
if width > avg_width * self.WidthThreshold and self.Position == 0:
if float(candle.ClosePrice) > middle_channel:
self.BuyMarket()
elif float(candle.ClosePrice) < middle_channel:
self.SellMarket()
def CreateClone(self):
return donchian_width_breakout_strategy()