Strategie Keltner Reversion
Strategie, die Mean Reversion mit Keltner-Kanälen handelt
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 130%. Sie funktioniert am besten am Aktienmarkt.
Keltner Reversion handelt gegen Ausbrüche außerhalb des Keltner-Kanals. Einstiege setzen auf eine Rückkehr zur mittleren Bande, wobei Trades geschlossen werden, sobald der Preis wieder in den Kanal eintritt oder der Stop getroffen wird.
Die Kanalbreite dehnt sich mit der Volatilität aus und zusammen, sodass das System extreme Bewegungen erfassen kann, während es den Trades Raum zur Entwicklung lässt. Stops basieren typischerweise auf ATR-Vielfachen.
Details
- Einstiegskriterien: Signale basierend auf RSI, ATR, Keltner.
- Long/Short: Beide Richtungen.
- Ausstiegskriterien: Gegensätzliches Signal oder Stop.
- Stops: Ja.
- Standardwerte:
EmaPeriod= 20AtrPeriod= 14AtrMultiplier= 2.0mStopLossAtrMultiplier= 2.0mCandleType= TimeSpan.FromMinutes(5)
- Filter:
- Kategorie: Mean Reversion
- Richtung: Beide
- Indikatoren: RSI, ATR, Keltner
- Stops: Ja
- Komplexität: Grundlegend
- Zeitrahmen: Intraday (5m)
- 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>
/// Strategy that trades on mean reversion using Keltner Channels.
/// It opens positions when price touches or breaks through the upper or lower Keltner Channel bands
/// and exits when price reverts to the middle band (EMA).
/// </summary>
public class KeltnerReversionStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<decimal> _stopLossAtrMultiplier;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private int _cooldown;
/// <summary>
/// Period for EMA calculation (middle band) (default: 20)
/// </summary>
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
/// <summary>
/// Period for ATR calculation (default: 14)
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// ATR multiplier for Keltner Channel width (default: 2.0)
/// </summary>
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
/// <summary>
/// ATR multiplier for stop-loss calculation (default: 2.0)
/// </summary>
public decimal StopLossAtrMultiplier
{
get => _stopLossAtrMultiplier.Value;
set => _stopLossAtrMultiplier.Value = value;
}
/// <summary>
/// Type of candles used for strategy calculation
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initialize the Keltner Reversion strategy
/// </summary>
public KeltnerReversionStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20)
.SetDisplay("EMA Period", "Period for EMA calculation (middle band)", "Technical Parameters")
.SetOptimize(10, 50, 5);
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetDisplay("ATR Period", "Period for ATR calculation (middle band)", "Technical Parameters")
.SetOptimize(7, 21, 7);
_atrMultiplier = Param(nameof(AtrMultiplier), 2.0m)
.SetDisplay("ATR Multiplier", "ATR multiplier for Keltner Channel width", "Technical Parameters")
.SetOptimize(1.0m, 3.0m, 0.5m);
_stopLossAtrMultiplier = Param(nameof(StopLossAtrMultiplier), 2.0m)
.SetDisplay("ATR Multiplier (Stop Loss)", "ATR multiplier for stop-loss calculation", "Risk Management")
.SetOptimize(1.0m, 3.0m, 0.5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "Technical Parameters");
_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;
// Create indicators
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
// Create subscription and bind indicators
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, atr, ProcessCandle)
.Start();
// Configure chart
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawIndicator(area, atr);
DrawOwnTrades(area);
}
}
/// <summary>
/// Process candle and check for Keltner Channel signals
/// </summary>
private void ProcessCandle(ICandleMessage candle, decimal emaValue, decimal atrValue)
{
// Skip unfinished candles
if (candle.State != CandleStates.Finished)
return;
// Check if strategy is ready to trade
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldown > 0)
{
_cooldown--;
return;
}
// Calculate Keltner Channel bands
decimal upperBand = emaValue + (atrValue * AtrMultiplier);
decimal lowerBand = emaValue - (atrValue * AtrMultiplier);
if (Position == 0)
{
if (candle.ClosePrice < lowerBand)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (candle.ClosePrice > upperBand)
{
SellMarket();
_cooldown = CooldownBars;
}
}
else if (Position > 0)
{
if (candle.ClosePrice > emaValue)
{
SellMarket();
_cooldown = CooldownBars;
}
}
else if (Position < 0)
{
if (candle.ClosePrice < emaValue)
{
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 ExponentialMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class keltner_reversion_strategy(Strategy):
"""
Keltner Channel mean reversion strategy.
Buys below lower band, sells above upper band, exits at EMA.
"""
def __init__(self):
super(keltner_reversion_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20).SetDisplay("EMA Period", "Period for EMA calculation (middle band)", "Technical Parameters")
self._atr_period = self.Param("AtrPeriod", 14).SetDisplay("ATR Period", "Period for ATR calculation", "Technical Parameters")
self._atr_multiplier = self.Param("AtrMultiplier", 2.0).SetDisplay("ATR Multiplier", "ATR multiplier for Keltner Channel width", "Technical Parameters")
self._stop_loss_atr = self.Param("StopLossAtrMultiplier", 2.0).SetDisplay("ATR Multiplier (Stop Loss)", "ATR multiplier for stop-loss calculation", "Risk Management")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "Technical Parameters")
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_reversion_strategy, self).OnReseted()
self._cooldown = 0
def OnStarted2(self, time):
super(keltner_reversion_strategy, self).OnStarted2(time)
self._cooldown = 0
ema = ExponentialMovingAverage()
ema.Length = self._ema_period.Value
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawIndicator(area, atr)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
if self._cooldown > 0:
self._cooldown -= 1
return
ev = float(ema_val)
av = float(atr_val)
mult = float(self._atr_multiplier.Value)
upper = ev + av * mult
lower = ev - av * mult
close = float(candle.ClosePrice)
cd = self._cooldown_bars.Value
if self.Position == 0:
if close < lower:
self.BuyMarket()
self._cooldown = cd
elif close > upper:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0:
if close > ev:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0:
if close < ev:
self.BuyMarket()
self._cooldown = cd
def CreateClone(self):
return keltner_reversion_strategy()