Keltner-Kanal-Ausbruch
Strategie basierend auf dem Keltner-Kanal-Ausbruch.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 58%. Die Strategie funktioniert am besten im Aktienmarkt.
Der Keltner-Kanal-Ausbruch verwendet Volatilitätsbänder, die vom ATR abgeleitet sind. Ausbrüche über das obere Band oder unter das untere Band lösen Einstiege aus. Wenn der Preis durch die EMA-Mitte zurückbewegt oder einen Stop trifft, wird die Position geschlossen.
Da sich die Bänder mit der Volatilität ausdehnen und zusammenziehen, zielt diese Ausbruchsmethode darauf ab, die frühen Phasen einer starken Bewegung zu erfassen, während dem Preis noch Raum zum Atmen innerhalb des Kanals gelassen wird.
Details
- Einstiegskriterien: Signale basierend auf ATR, Keltner.
- Long/Short: Beide Richtungen.
- Ausstiegskriterien: Gegensätzliches Signal oder Stop.
- Stops: Ja.
- Standardwerte:
EmaPeriod= 20AtrPeriod= 14AtrMultiplier= 2mCandleType= TimeSpan.FromMinutes(5)
- Filter:
- Kategorie: Ausbruch
- Richtung: Beide
- Indikatoren: ATR, Keltner
- Stops: Ja
- Komplexität: Grundlegend
- Zeitrahmen: Intraday (5m)
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on Keltner Channel breakout.
/// The channel is an EMA with bands AtrMultiplier ATRs away. A close that breaks above the upper band opens a long position,
/// one that breaks below the lower band a short one. The position closes when the close crosses back through the EMA
/// or reaches the stop set AtrMultiplier ATRs from the entry close.
/// </summary>
public class KeltnerChannelBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<DataType> _candleType;
// Close and bands of the previous candle.
private decimal? _prevClose;
private decimal _prevUpper;
private decimal _prevLower;
private decimal _stopPrice;
/// <summary>
/// EMA period of the channel center.
/// </summary>
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
/// <summary>
/// ATR period of the channel width.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Band and stop distance in ATR multiples.
/// </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>
/// Initializes a new instance of the <see cref="KeltnerChannelBreakoutStrategy"/>.
/// </summary>
public KeltnerChannelBreakoutStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "Period for Exponential Moving Average", "Indicators")
.SetOptimize(10, 50, 5);
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "Period for Average True Range", "Indicators")
.SetOptimize(10, 30, 2);
_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "Band and stop distance in ATR multiples", "Indicators")
.SetOptimize(1, 3, 0.5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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();
_prevClose = null;
_prevUpper = default;
_prevLower = default;
_stopPrice = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(ema, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue emaValue, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished || !emaValue.IsFormed || !atrValue.IsFormed)
return;
var center = emaValue.GetValue<decimal>();
var atr = atrValue.GetValue<decimal>();
var close = candle.ClosePrice;
var prevClose = _prevClose;
var prevUpper = _prevUpper;
var prevLower = _prevLower;
_prevClose = close;
_prevUpper = center + AtrMultiplier * atr;
_prevLower = center - AtrMultiplier * atr;
if (prevClose is not decimal lastClose || !IsFormedAndOnlineAndAllowTrading())
return;
// A breakout is the first close beyond the band of the previous candle.
var upperBreakout = close > prevUpper && lastClose <= prevUpper;
var lowerBreakout = close < prevLower && lastClose >= prevLower;
if (upperBreakout && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_stopPrice = close - AtrMultiplier * atr;
}
else if (lowerBreakout && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_stopPrice = close + AtrMultiplier * atr;
}
else if (Position > 0 && (close < center || close <= _stopPrice))
{
SellMarket(Position);
}
else if (Position < 0 && (close > center || close >= _stopPrice))
{
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, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class keltner_channel_breakout_strategy(Strategy):
"""
Strategy based on Keltner Channel breakout.
The channel is an EMA with bands AtrMultiplier ATRs away. A close that breaks above the upper band opens a long position,
one that breaks below the lower band a short one. The position closes when the close crosses back through the EMA
or reaches the stop set AtrMultiplier ATRs from the entry close.
"""
def __init__(self):
super(keltner_channel_breakout_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("EMA Period", "Period for Exponential Moving Average", "Indicators") \
.SetOptimize(10, 50, 5)
self._atr_period = self.Param("AtrPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("ATR Period", "Period for Average True Range", "Indicators") \
.SetOptimize(10, 30, 2)
self._atr_multiplier = self.Param("AtrMultiplier", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("ATR Multiplier", "Band and stop distance in ATR multiples", "Indicators") \
.SetOptimize(1.0, 3.0, 0.5)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
# Close and bands of the previous candle.
self._prev_close = None
self._prev_upper = Decimal(0)
self._prev_lower = Decimal(0)
self._stop_price = Decimal(0)
def OnReseted(self):
super(keltner_channel_breakout_strategy, self).OnReseted()
self._prev_close = None
self._prev_upper = Decimal(0)
self._prev_lower = Decimal(0)
self._stop_price = Decimal(0)
def OnStarted2(self, time):
super(keltner_channel_breakout_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self._ema_period.Value
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
subscription = self.SubscribeCandles(self._candle_type.Value)
subscription.BindEx(ema, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ema_value, atr_value):
if candle.State != CandleStates.Finished or not ema_value.IsFormed or not atr_value.IsFormed:
return
center = ema_value.GetValue[Decimal](None)
offset = Decimal(self._atr_multiplier.Value) * atr_value.GetValue[Decimal](None)
close = candle.ClosePrice
prev_close = self._prev_close
prev_upper = self._prev_upper
prev_lower = self._prev_lower
self._prev_close = close
self._prev_upper = center + offset
self._prev_lower = center - offset
if prev_close is None or not self.IsFormedAndOnlineAndAllowTrading():
return
# A breakout is the first close beyond the band of the previous candle.
upper_breakout = close > prev_upper and prev_close <= prev_upper
lower_breakout = close < prev_lower and prev_close >= prev_lower
position = self.Position
if upper_breakout and position <= 0:
self.BuyMarket(self.Volume + abs(position))
self._stop_price = close - offset
elif lower_breakout and position >= 0:
self.SellMarket(self.Volume + abs(position))
self._stop_price = close + offset
elif position > 0 and (close < center or close <= self._stop_price):
self.SellMarket(position)
elif position < 0 and (close > center or close >= self._stop_price):
self.BuyMarket(-position)
def CreateClone(self):
return keltner_channel_breakout_strategy()