Volatilitätsbasierte Kanäle können überstreckte Bewegungen hervorheben. Diese Methode handelt gegen den Kurs, wenn er den Keltner Channel verlässt, und erwartet eine Rückkehr zur Mittellinie. Zur Berechnung der Kanalbreite werden ein exponentieller gleitender Durchschnitt und der ATR verwendet.
Tests zeigen eine durchschnittliche Jahresrendite von etwa 106%. Die Strategie eignet sich am besten für den Aktienmarkt.
Mit dem Abschluss jeder Kerze prüft die Strategie, ob der Schlusskurs jenseits der oberen oder unteren Bande liegt und ob die Kerzenrichtung übereinstimmt. Bullische Kerzen, die unterhalb der unteren Bande schließen, lösen Long-Einstiege aus, während bearische Kerzen oberhalb der oberen Bande Short-Positionen veranlassen. Positionen werden geschlossen, sobald der Kurs die Mittelbande kreuzt oder wenn der ATR-basierte Stop erreicht wird.
Indem das System in der entgegengesetzten Richtung kurzfristiger Extreme handelt, sucht es nach schnellen Mean-Reversion-Bewegungen innerhalb einer breiteren Spanne.
Details
Einstiegskriterien: Schlusskurs außerhalb des Keltner Channel in Kerzenrichtung.
Long/Short: Beide.
Ausstiegskriterien: Kurs kreuzt die Mittelbande oder Stop-Loss.
Stops: Ja, ATR-basiert.
Standardwerte:
EmaPeriod = 20
AtrPeriod = 14
AtrMultiplier = 2.0
StopLossAtrMultiplier = 2.0
CandleType = 5 minute
Filter:
Kategorie: Mean Reversion
Richtung: Beide
Indikatoren: Keltner Channel
Stops: Ja
Komplexität: Grundlegend
Zeitrahmen: Intraday
Saisonalität: Nein
Neuronale Netze: Nein
Divergenz: Nein
Risikolevel: Mittel
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>
/// Keltner Channel Reversal strategy.
/// Enters long when price is below lower Keltner Channel with a bullish candle.
/// Enters short when price is above upper Keltner Channel with a bearish candle.
/// Exits at middle band.
/// </summary>
public class KeltnerChannelReversalStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private int _cooldown;
/// <summary>
/// EMA period for Keltner Channel.
/// </summary>
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
/// <summary>
/// ATR multiplier for Keltner Channel.
/// </summary>
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public KeltnerChannelReversalStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "Period for EMA in Keltner Channel", "Indicators");
_atrMultiplier = Param(nameof(AtrMultiplier), 2.0m)
.SetNotNegative()
.SetDisplay("ATR Multiplier", "Multiplier for ATR in Keltner Channel", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 500)
.SetRange(1, 1000)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_cooldown = 0;
var keltner = new KeltnerChannels
{
Length = EmaPeriod,
Multiplier = AtrMultiplier,
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(keltner, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, keltner);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue keltnerValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!keltnerValue.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldown > 0)
{
_cooldown--;
return;
}
var kc = (IKeltnerChannelsValue)keltnerValue;
var upper = kc.Upper;
var lower = kc.Lower;
var middle = kc.Middle;
if (upper == null || lower == null || middle == null)
return;
var isBullish = candle.ClosePrice > candle.OpenPrice;
var isBearish = candle.ClosePrice < candle.OpenPrice;
if (Position == 0 && candle.ClosePrice < lower.Value && isBullish)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (Position == 0 && candle.ClosePrice > upper.Value && isBearish)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position > 0 && candle.ClosePrice > middle.Value)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && candle.ClosePrice < middle.Value)
{
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 KeltnerChannels
from StockSharp.Algo.Strategies import Strategy
class keltner_channel_reversal_strategy(Strategy):
"""
Keltner Channel Reversal strategy.
Enters long when price is below lower Keltner Channel with a bullish candle.
Enters short when price is above upper Keltner Channel with a bearish candle.
Exits at middle band.
"""
def __init__(self):
super(keltner_channel_reversal_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20).SetDisplay("EMA Period", "Period for EMA in Keltner Channel", "Indicators")
self._atr_multiplier = self.Param("AtrMultiplier", 2.0).SetDisplay("ATR Multiplier", "Multiplier for ATR in Keltner Channel", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 500).SetDisplay("Cooldown Bars", "Bars to wait between trades", "General")
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(keltner_channel_reversal_strategy, self).OnReseted()
self._cooldown = 0
def OnStarted2(self, time):
super(keltner_channel_reversal_strategy, self).OnStarted2(time)
self._cooldown = 0
keltner = KeltnerChannels()
keltner.Length = self._ema_period.Value
keltner.Multiplier = self._atr_multiplier.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(keltner, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, keltner)
self.DrawOwnTrades(area)
def _process_candle(self, candle, keltner_value):
if candle.State != CandleStates.Finished:
return
if not keltner_value.IsFormed:
return
if self._cooldown > 0:
self._cooldown -= 1
return
upper = keltner_value.Upper
lower = keltner_value.Lower
middle = keltner_value.Middle
if upper is None or lower is None or middle is None:
return
close = float(candle.ClosePrice)
ub = float(upper)
lb = float(lower)
mb = float(middle)
cd = self._cooldown_bars.Value
is_bullish = candle.ClosePrice > candle.OpenPrice
is_bearish = candle.ClosePrice < candle.OpenPrice
if self.Position == 0 and close < lb and is_bullish:
self.BuyMarket()
self._cooldown = cd
elif self.Position == 0 and close > ub and is_bearish:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0 and close > mb:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and close < mb:
self.BuyMarket()
self._cooldown = cd
def CreateClone(self):
return keltner_channel_reversal_strategy()