Donchian Quest Forschungs-Strategie
Diese Strategie verwendet zwei Donchian-Kanäle mit unterschiedlichen Längen. Ein Ausbruch über das obere Band des längeren Kanals eröffnet eine Long-Position, während ein Ausbruch unter das untere Band eine Short-Position eröffnet. Positionen werden geschlossen, wenn der Preis das gegenüberliegende Band des kürzeren Kanals berührt.
Parameter
OpenPeriod= 50ClosePeriod= 50
Indikatoren
- Donchian-Kanal
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>
/// Donchian Quest Research strategy.
/// A close above the upper band of the OpenPeriod Donchian channel goes long and a close below its lower band goes short,
/// reversing an opposite position. A long closes when price touches the lower band of the ClosePeriod channel and a short
/// when price touches its upper band. Both channels are measured on the candles before the current one.
/// </summary>
public class DonchianQuestResearchStrategy : Strategy
{
private readonly StrategyParam<int> _openPeriod;
private readonly StrategyParam<int> _closePeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevOpenUpper;
private decimal? _prevOpenLower;
private decimal? _prevCloseUpper;
private decimal? _prevCloseLower;
/// <summary>
/// Period of the entry channel.
/// </summary>
public int OpenPeriod
{
get => _openPeriod.Value;
set => _openPeriod.Value = value;
}
/// <summary>
/// Period of the exit channel.
/// </summary>
public int ClosePeriod
{
get => _closePeriod.Value;
set => _closePeriod.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public DonchianQuestResearchStrategy()
{
_openPeriod = Param(nameof(OpenPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Open Period", "Period of the entry Donchian channel", "Indicators");
_closePeriod = Param(nameof(ClosePeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Close Period", "Period of the exit Donchian channel", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).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();
ResetState();
}
private void ResetState()
{
_prevOpenUpper = null;
_prevOpenLower = null;
_prevCloseUpper = null;
_prevCloseLower = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var openChannel = new DonchianChannels { Length = OpenPeriod };
var closeChannel = new DonchianChannels { Length = ClosePeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(openChannel, closeChannel, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, openChannel);
DrawIndicator(area, closeChannel);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue openValue, IIndicatorValue closeValue)
{
if (candle.State != CandleStates.Finished)
return;
var openUpper = _prevOpenUpper;
var openLower = _prevOpenLower;
var closeUpper = _prevCloseUpper;
var closeLower = _prevCloseLower;
if (openValue.IsFormed && openValue is IDonchianChannelsValue { UpperBand: decimal ou, LowerBand: decimal ol })
{
_prevOpenUpper = ou;
_prevOpenLower = ol;
}
if (closeValue.IsFormed && closeValue is IDonchianChannelsValue { UpperBand: decimal cu, LowerBand: decimal cl })
{
_prevCloseUpper = cu;
_prevCloseLower = cl;
}
if (openUpper is not decimal entryHigh || openLower is not decimal entryLow || closeUpper is not decimal exitHigh || closeLower is not decimal exitLow)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
if (close > entryHigh && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (close < entryLow && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && candle.LowPrice <= exitLow)
SellMarket(Position);
else if (Position < 0 && candle.HighPrice >= exitHigh)
BuyMarket(-Position);
}
}
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 DonchianChannels
from StockSharp.Algo.Strategies import Strategy
class donchian_quest_research_strategy(Strategy):
"""
Donchian Quest Research strategy.
A close above the upper band of the OpenPeriod Donchian channel goes long and a close below its lower band goes short,
reversing an opposite position. A long closes when price touches the lower band of the ClosePeriod channel and a short
when price touches its upper band. Both channels are measured on the candles before the current one.
"""
def __init__(self):
super(donchian_quest_research_strategy, self).__init__()
self._open_period = self.Param("OpenPeriod", 50).SetGreaterThanZero().SetDisplay("Open Period", "Period of the entry Donchian channel", "Indicators")
self._close_period = self.Param("ClosePeriod", 50).SetGreaterThanZero().SetDisplay("Close Period", "Period of the exit Donchian channel", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_open_upper = None
self._prev_open_lower = None
self._prev_close_upper = None
self._prev_close_lower = None
def OnReseted(self):
super(donchian_quest_research_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(donchian_quest_research_strategy, self).OnStarted2(time)
self._reset_state()
open_channel = DonchianChannels()
open_channel.Length = self._open_period.Value
close_channel = DonchianChannels()
close_channel.Length = self._close_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(open_channel, close_channel, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, open_channel)
self.DrawIndicator(area, close_channel)
self.DrawOwnTrades(area)
def _process_candle(self, candle, open_value, close_value):
if candle.State != CandleStates.Finished:
return
open_upper = self._prev_open_upper
open_lower = self._prev_open_lower
close_upper = self._prev_close_upper
close_lower = self._prev_close_lower
if open_value.IsFormed and open_value.UpperBand is not None and open_value.LowerBand is not None:
self._prev_open_upper = open_value.UpperBand
self._prev_open_lower = open_value.LowerBand
if close_value.IsFormed and close_value.UpperBand is not None and close_value.LowerBand is not None:
self._prev_close_upper = close_value.UpperBand
self._prev_close_lower = close_value.LowerBand
if open_upper is None or open_lower is None or close_upper is None or close_lower is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
close = candle.ClosePrice
if close > open_upper and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif close < open_lower and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and candle.LowPrice <= close_lower:
self.SellMarket(self.Position)
elif self.Position < 0 and candle.HighPrice >= close_upper:
self.BuyMarket(-self.Position)
def CreateClone(self):
return donchian_quest_research_strategy()