Die ATR-Mean-Reversion-Strategie misst, wie weit sich der Preis von einem gleitenden Durchschnitt relativ zur jüngsten Volatilität entfernt. Der ATR bietet eine adaptive Messgröße, sodass sich die Schwellen in aktiven Perioden ausdehnen und sich zusammenziehen, wenn die Märkte ruhig werden.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 109%. Sie funktioniert am besten auf dem Kryptomarkt.
Eine Long-Konfiguration entsteht, wenn der Preis um mehr als Multiplier mal den ATR unter dem gleitenden Durchschnitt schließt. Eine Short-Konfiguration erscheint, wenn der Preis um denselben Abstand über dem gleitenden Durchschnitt schließt. Positionen werden beendet, sobald der Preis zum gleitenden Durchschnitt zurückkehrt.
Diese Technik ist für kurzfristige Trader gedacht, die erwarten, dass die Preise nach übermäßigen Bewegungen umkehren. Der ATR-basierte Stop hält das Risiko proportional zu den aktuellen Marktbedingungen.
Details
Einstiegskriterien:
Long: Schluss < MA - Multiplier * ATR
Short: Schluss > MA + Multiplier * ATR
Long/Short: Beide Seiten.
Ausstiegskriterien:
Long: Ausstieg, wenn Schluss >= MA
Short: Ausstieg, wenn Schluss <= MA
Stops: Ja, Stop-Loss standardmäßig bei etwa 2*ATR.
Standardwerte:
MaPeriod = 20
AtrPeriod = 14
Multiplier = 2
MA ist der einfache gleitende Durchschnitt über MaPeriod und ATR die Average True Range über AtrPeriod. Der Stop liegt Multiplier mal die ATR vom Einstiegsschluss entfernt, standardmäßig 2 ATR, und wird bei Kerzenschluss geprüft. Ein Einstiegssignal gegen eine offene Position dreht sie.
CandleType = TimeSpan.FromMinutes(5)
Filter:
Kategorie: Mean Reversion
Richtung: Beide
Indikatoren: MA, ATR
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 Mean Reversion strategy.
/// A close more than Multiplier times the AtrPeriod ATR below the MaPeriod simple moving average goes long and one that far above it goes
/// short, reversing an opposite position. A long closes once the close is back at or above the average and a short once it is back at or
/// below it. The stop lies Multiplier ATR from the entry close and is checked on candle closes.
/// </summary>
public class AtrMeanReversionStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _multiplier;
private readonly StrategyParam<DataType> _candleType;
private decimal _stopPrice;
/// <summary>
/// Period of the simple moving average.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Period of the ATR.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// ATR multiplier for the entry distance and the stop.
/// </summary>
public decimal Multiplier
{
get => _multiplier.Value;
set => _multiplier.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public AtrMeanReversionStrategy()
{
_maPeriod = Param(nameof(MaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Period of the simple moving average", "Parameters");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "Period of the ATR", "Parameters");
_multiplier = Param(nameof(Multiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("Multiplier", "ATR multiplier for the entry distance and the stop", "Parameters");
_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();
_stopPrice = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_stopPrice = default;
var sma = new SimpleMovingAverage { Length = MaPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(sma, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, atr);
}
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue smaValue, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!smaValue.IsFormed || !atrValue.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var sma = smaValue.GetValue<decimal>();
var distance = Multiplier * atrValue.GetValue<decimal>();
var close = candle.ClosePrice;
if (close < sma - distance && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_stopPrice = close - distance;
}
else if (close > sma + distance && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_stopPrice = close + distance;
}
else if (Position > 0 && (close >= sma || close <= _stopPrice))
{
SellMarket(Position);
}
else if (Position < 0 && (close <= sma || 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 SimpleMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class atr_mean_reversion_strategy(Strategy):
"""
ATR Mean Reversion strategy.
A close more than Multiplier times the AtrPeriod ATR below the MaPeriod simple moving average goes long and one that far above it goes
short, reversing an opposite position. A long closes once the close is back at or above the average and a short once it is back at or
below it. The stop lies Multiplier ATR from the entry close and is checked on candle closes.
"""
def __init__(self):
super(atr_mean_reversion_strategy, self).__init__()
self._ma_period = self.Param("MaPeriod", 20).SetGreaterThanZero().SetDisplay("MA Period", "Period of the simple moving average", "Parameters")
self._atr_period = self.Param("AtrPeriod", 14).SetGreaterThanZero().SetDisplay("ATR Period", "Period of the ATR", "Parameters")
self._multiplier = self.Param("Multiplier", 2.0).SetGreaterThanZero().SetDisplay("Multiplier", "ATR multiplier for the entry distance and the stop", "Parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._stop_price = Decimal(0)
def OnReseted(self):
super(atr_mean_reversion_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(atr_mean_reversion_strategy, self).OnStarted2(time)
self._reset_state()
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(sma, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, atr)
def _process_candle(self, candle, sma_value, atr_value):
if candle.State != CandleStates.Finished:
return
if not sma_value.IsFormed or not atr_value.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
sma = sma_value.GetValue[Decimal](None)
distance = Decimal(self._multiplier.Value) * atr_value.GetValue[Decimal](None)
close = candle.ClosePrice
if close < sma - distance and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._stop_price = close - distance
elif close > sma + distance and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._stop_price = close + distance
elif self.Position > 0 and (close >= sma or close <= self._stop_price):
self.SellMarket(self.Position)
elif self.Position < 0 and (close <= sma or close >= self._stop_price):
self.BuyMarket(-self.Position)
def CreateClone(self):
return atr_mean_reversion_strategy()