Keltner-Kanalbreiten-Ausbruch-Strategie
Die Keltner-Kanalbreiten-Ausbruch-Strategie beobachtet den Keltner auf schnelle Expansionen. Wenn die Werte über ihren typischen Bereich hinausspringen, beginnt der Kurs oft eine neue Bewegung.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 112%. Am besten funktioniert sie auf dem Forex-Markt.
Eine Position wird eröffnet, sobald der Indikator ein Band durchbricht, das aus aktuellen Daten und einem Abweichungsmultiplikator abgeleitet wird. Long- und Short-Trades sind mit einem Stop möglich.
Dieses System eignet sich für Momentum-Trader, die frühe Ausbrüche suchen. Trades werden geschlossen, wenn der Keltner zur Mitte zurückkehrt. Die Standardwerte beginnen mit EMAPeriod = 20.
Details
- Einstiegskriterien: Indikator überschreitet den Durchschnitt um den Abweichungsmultiplikator.
- Long/Short: Beide Richtungen.
- Ausstiegskriterien: Indikator kehrt zum Durchschnitt zurück.
- Stops: Ja.
- Standardwerte:
EMAPeriod= 20ATRPeriod= 14ATRMultiplier= 2.0mAvgPeriod= 20Multiplier= 2.0mCandleType= TimeSpan.FromMinutes(5)StopMultiplier= 2
- Filter:
- Kategorie: Ausbruch
- Richtung: Beide
- Indikatoren: Keltner
- Stops: Ja
- Komplexität: Mittel
- Zeitrahmen: Kurzfristig
- 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 Keltner Channel width breakouts.
/// When Keltner Channel width increases significantly above its average,
/// it enters position in the direction determined by price movement.
/// </summary>
public class KeltnerWidthBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<decimal> _widthThreshold;
private readonly StrategyParam<DataType> _candleType;
/// <summary>
/// EMA period for Keltner Channel.
/// </summary>
public int EMAPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
/// <summary>
/// ATR period for Keltner Channel.
/// </summary>
public int ATRPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// ATR multiplier for Keltner Channel.
/// </summary>
public decimal ATRMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.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="KeltnerWidthBreakoutStrategy"/>.
/// </summary>
public KeltnerWidthBreakoutStrategy()
{
_emaPeriod = Param(nameof(EMAPeriod), 20)
.SetDisplay("EMA Period", "Period of EMA for Keltner Channel", "Indicators");
_atrPeriod = Param(nameof(ATRPeriod), 14)
.SetDisplay("ATR Period", "Period of ATR for Keltner Channel", "Indicators");
_atrMultiplier = Param(nameof(ATRMultiplier), 2.0m)
.SetDisplay("ATR Multiplier", "Multiplier for ATR in Keltner Channel", "Indicators");
_widthThreshold = Param(nameof(WidthThreshold), 1.2m)
.SetDisplay("Width Threshold", "Threshold multiplier for width breakout detection", "Trading");
_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 OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = EMAPeriod };
var atr = new AverageTrueRange { Length = ATRPeriod };
var widthAverage = new SimpleMovingAverage { Length = Math.Max(5, EMAPeriod / 2) };
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent)
);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, atr, (candle, emaValue, atrValue) =>
{
if (candle.State != CandleStates.Finished || atrValue <= 0)
return;
// Keltner width = (EMA + ATR*k) - (EMA - ATR*k) = 2*ATR*k
var width = 2m * ATRMultiplier * atrValue;
var avgWidthValue = widthAverage.Process(new DecimalIndicatorValue(widthAverage, width, candle.ServerTime) { IsFinal = true });
if (!widthAverage.IsFormed)
return;
var avgWidth = avgWidthValue.ToDecimal();
if (avgWidth <= 0)
return;
// Width breakout detection
if (width > avgWidth * WidthThreshold && Position == 0)
{
// Determine direction based on price relative to EMA
if (candle.ClosePrice > emaValue)
BuyMarket();
else if (candle.ClosePrice < emaValue)
SellMarket();
}
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
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 ExponentialMovingAverage, AverageTrueRange, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class keltner_width_breakout_strategy(Strategy):
"""
Strategy that trades on Keltner Channel width breakouts.
When Keltner Channel width increases significantly above its average,
it enters position in the direction determined by price movement.
"""
def __init__(self):
super(keltner_width_breakout_strategy, self).__init__()
self._emaPeriod = self.Param("EMAPeriod", 20) \
.SetDisplay("EMA Period", "Period of EMA for Keltner Channel", "Indicators")
self._atrPeriod = self.Param("ATRPeriod", 14) \
.SetDisplay("ATR Period", "Period of ATR for Keltner Channel", "Indicators")
self._atrMultiplier = self.Param("ATRMultiplier", 2.0) \
.SetDisplay("ATR Multiplier", "Multiplier for ATR in Keltner Channel", "Indicators")
self._widthThreshold = self.Param("WidthThreshold", 1.2) \
.SetDisplay("Width Threshold", "Threshold multiplier for width breakout detection", "Trading")
self._candleType = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._widthAverage = None
@property
def EMAPeriod(self):
return self._emaPeriod.Value
@EMAPeriod.setter
def EMAPeriod(self, value):
self._emaPeriod.Value = value
@property
def ATRPeriod(self):
return self._atrPeriod.Value
@ATRPeriod.setter
def ATRPeriod(self, value):
self._atrPeriod.Value = value
@property
def ATRMultiplier(self):
return self._atrMultiplier.Value
@ATRMultiplier.setter
def ATRMultiplier(self, value):
self._atrMultiplier.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 GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnReseted(self):
super(keltner_width_breakout_strategy, self).OnReseted()
def OnStarted2(self, time):
super(keltner_width_breakout_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self.EMAPeriod
atr = AverageTrueRange()
atr.Length = self.ATRPeriod
ema_period = self.EMAPeriod
self._widthAverage = SimpleMovingAverage()
self._widthAverage.Length = max(5, ema_period // 2)
self.StartProtection(
takeProfit=Unit(2, UnitTypes.Percent),
stopLoss=Unit(1, UnitTypes.Percent)
)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, atr, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, ema_value, atr_value):
if candle.State != CandleStates.Finished or atr_value <= 0:
return
# Keltner width = 2 * ATR * multiplier
width = 2.0 * self.ATRMultiplier * float(atr_value)
ema_val = float(ema_value)
# Process width through average
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
# Width breakout detection
if width > avg_width * self.WidthThreshold and self.Position == 0:
# Determine direction based on price relative to EMA
if float(candle.ClosePrice) > ema_val:
self.BuyMarket()
elif float(candle.ClosePrice) < ema_val:
self.SellMarket()
def CreateClone(self):
return keltner_width_breakout_strategy()