Ein plötzlicher Anstieg der Average True Range weist auf eine sich ausdehnende Volatilität hin, die schnell nachlassen kann. Diese Strategie sucht nach ATR-Werten, die eine gleitende Durchschnittslinie um einen konfigurierbaren Multiplikator überschreiten. In Verbindung mit einer Umkehrkerze zielt sie darauf ab, die anschließende Kontraktion zu erfassen.
Tests zeigen eine durchschnittliche Jahresrendite von etwa 139%. Am besten funktioniert die Strategie am Aktienmarkt.
Jeder Balken aktualisiert ATR und seinen eigenen Durchschnitt. Wenn ATR den Durchschnitt um den Multiplikator überschreitet und die Kerze entgegen der vorherigen Bewegung schließt, wird ein Trade eröffnet. Der Stop-Loss verwendet ebenfalls ein ATR-Vielfaches und verankert das Risiko an den aktuellen Volatilitätsniveaus.
Positionen verlassen sich typischerweise auf den Stop für den Ausstieg und suchen eine rasche Gegenbewegung, nachdem der Volatilitätsausbruch abgeklungen ist.
Details
Einstiegskriterien: ATR-Spike über dem Durchschnitt mit Umkehrkerze.
Long/Short: Beide.
Ausstiegskriterien: Stop-Loss.
Stops: Ja, ATR-basiert.
Standardwerte:
AtrPeriod = 14
AtrAvgPeriod = 20
AtrMultiplier = 1.5
MaPeriod = 20
StopLoss = 2%
CandleType = 5 minute
Filter:
Kategorie: Umkehr
Richtung: Beide
Indikatoren: ATR, MA
Stops: Ja
Komplexität: Mittel
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>
/// ATR Exhaustion strategy.
/// Enters when ATR spikes (current ATR significantly higher than previous ATR).
/// ATR spike + bullish candle = buy.
/// ATR spike + bearish candle = sell.
/// Exits on SMA cross.
/// </summary>
public class AtrExhaustionStrategy : Strategy
{
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal _prevAtr;
private int _cooldown;
/// <summary>
/// ATR period.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// MA Period.
/// </summary>
public int MAPeriod
{
get => _maPeriod.Value;
set => _maPeriod.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 AtrExhaustionStrategy()
{
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetRange(7, 21)
.SetDisplay("ATR Period", "Period for ATR", "Indicators");
_maPeriod = Param(nameof(MAPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Period for SMA", "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();
_prevAtr = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevAtr = 0;
_cooldown = 0;
var sma = new SimpleMovingAverage { Length = MAPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawIndicator(area, atr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevAtr = atrValue;
return;
}
if (_prevAtr == 0)
{
_prevAtr = atrValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevAtr = atrValue;
return;
}
// ATR spike: current ATR significantly higher than previous
var atrSpike = _prevAtr > 0 && atrValue > _prevAtr * 1.3m;
var isBullish = candle.ClosePrice > candle.OpenPrice;
var isBearish = candle.ClosePrice < candle.OpenPrice;
if (Position == 0 && atrSpike && isBullish)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (Position == 0 && atrSpike && isBearish)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position > 0 && candle.ClosePrice < smaValue)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && candle.ClosePrice > smaValue)
{
BuyMarket();
_cooldown = CooldownBars;
}
_prevAtr = atrValue;
}
}
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 SimpleMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class atr_exhaustion_strategy(Strategy):
"""
ATR Exhaustion strategy.
Enters when ATR spikes (current ATR significantly higher than previous ATR).
ATR spike + bullish candle = buy.
ATR spike + bearish candle = sell.
Exits on SMA cross.
"""
def __init__(self):
super(atr_exhaustion_strategy, self).__init__()
self._atr_period = self.Param("AtrPeriod", 14).SetDisplay("ATR Period", "Period for ATR", "Indicators")
self._ma_period = self.Param("MAPeriod", 20).SetDisplay("MA Period", "Period for SMA", "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._prev_atr = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(atr_exhaustion_strategy, self).OnReseted()
self._prev_atr = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(atr_exhaustion_strategy, self).OnStarted2(time)
self._prev_atr = 0.0
self._cooldown = 0
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawIndicator(area, atr)
self.DrawOwnTrades(area)
def _process_candle(self, candle, sma_val, atr_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_atr = float(atr_val)
return
av = float(atr_val)
if self._prev_atr == 0:
self._prev_atr = av
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_atr = av
return
# ATR spike: current ATR significantly higher than previous
atr_spike = self._prev_atr > 0 and av > self._prev_atr * 1.3
is_bullish = candle.ClosePrice > candle.OpenPrice
is_bearish = candle.ClosePrice < candle.OpenPrice
sv = float(sma_val)
close = float(candle.ClosePrice)
cd = self._cooldown_bars.Value
if self.Position == 0 and atr_spike and is_bullish:
self.BuyMarket()
self._cooldown = cd
elif self.Position == 0 and atr_spike and is_bearish:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0 and close < sv:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and close > sv:
self.BuyMarket()
self._cooldown = cd
self._prev_atr = av
def CreateClone(self):
return atr_exhaustion_strategy()