Diese Variante der Mean Reversion skaliert die Einstiegsschwellen durch das Verhältnis von ATR zu Standardabweichung. Wenn die Volatilität im Verhältnis zum typischen Rauschen zunimmt, wächst die Distanz, die benötigt wird, um einen Trade auszulösen, was hilft, vorzeitige Signale während chaotischer Schwankungen zu vermeiden.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 115%. Sie funktioniert am besten am Aktienmarkt.
Eine Long-Position wird eröffnet, wenn der Preis um mehr als die angepasste Schwelle unter den gleitenden Durchschnitt fällt. Eine Short-Position wird eröffnet, wenn der Preis um dasselbe Maß über den Durchschnitt steigt. Positionen werden beendet, sobald der Preis wieder nahe am Durchschnittsniveau schließt.
Die adaptive Schwelle macht diese Strategie geeignet für Märkte mit wechselnden Volatilitätsregimen. Ein Stop-Loss von der zweifachen ATR begrenzt das Risiko, während auf die Umkehr gewartet wird.
Details
Einstiegskriterien:
Long: Schluss < MA - Multiplier * ATR / (ATR/StdDev)
Short: Schluss > MA + Multiplier * ATR / (ATR/StdDev)
Long/Short: Beide Seiten.
Ausstiegskriterien:
Long: Ausstieg, wenn Schluss >= MA
Short: Ausstieg, wenn Schluss <= MA
Stops: Ja, dynamisch basierend auf ATR.
Standardwerte:
Period = 20
Multiplier = 2
MA, ATR und StdDev (die Standardabweichung der Schlusskurse) umfassen Period Kerzen; die Schwelle vereinfacht sich zu Multiplier mal StdDev. Der Stop liegt Multiplier mal die ATR vom Einstiegsschluss entfernt, standardmäßig zwei 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: ATR, StdDev
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>
/// Volatility Adjusted Mean Reversion strategy.
/// The threshold is Multiplier times the ATR divided by the ATR to standard deviation ratio, all over Period candles. A close more than the
/// threshold below the Period 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 VolatilityAdjustedMeanReversionStrategy : Strategy
{
private readonly StrategyParam<int> _period;
private readonly StrategyParam<decimal> _multiplier;
private readonly StrategyParam<DataType> _candleType;
private decimal _stopPrice;
/// <summary>
/// Period of the SMA, ATR and standard deviation.
/// </summary>
public int Period
{
get => _period.Value;
set => _period.Value = value;
}
/// <summary>
/// Multiplier of the threshold 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 VolatilityAdjustedMeanReversionStrategy()
{
_period = Param(nameof(Period), 20)
.SetGreaterThanZero()
.SetDisplay("Period", "Period of the SMA, ATR and standard deviation", "Parameters");
_multiplier = Param(nameof(Multiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("Multiplier", "Multiplier of the threshold 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 = Period };
var atr = new AverageTrueRange { Length = Period };
var stdev = new StandardDeviation { Length = Period };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(sma, atr, stdev, 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, IIndicatorValue stdevValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!smaValue.IsFormed || !atrValue.IsFormed || !stdevValue.IsFormed)
return;
var atr = atrValue.GetValue<decimal>();
var deviation = stdevValue.GetValue<decimal>();
if (atr <= 0 || deviation <= 0)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var sma = smaValue.GetValue<decimal>();
var threshold = Multiplier * atr / (atr / deviation);
var close = candle.ClosePrice;
if (close < sma - threshold && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_stopPrice = close - Multiplier * atr;
}
else if (close > sma + threshold && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_stopPrice = close + Multiplier * atr;
}
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, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
class volatility_adjusted_mean_reversion_strategy(Strategy):
"""
Volatility Adjusted Mean Reversion strategy.
The threshold is Multiplier times the ATR divided by the ATR to standard deviation ratio, all over Period candles. A close more than the
threshold below the Period 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(volatility_adjusted_mean_reversion_strategy, self).__init__()
self._period = self.Param("Period", 20).SetGreaterThanZero().SetDisplay("Period", "Period of the SMA, ATR and standard deviation", "Parameters")
self._multiplier = self.Param("Multiplier", 2.0).SetGreaterThanZero().SetDisplay("Multiplier", "Multiplier of the threshold 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(volatility_adjusted_mean_reversion_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(volatility_adjusted_mean_reversion_strategy, self).OnStarted2(time)
self._reset_state()
sma = SimpleMovingAverage()
sma.Length = self._period.Value
atr = AverageTrueRange()
atr.Length = self._period.Value
stdev = StandardDeviation()
stdev.Length = self._period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(sma, atr, stdev, 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, stdev_value):
if candle.State != CandleStates.Finished:
return
if not sma_value.IsFormed or not atr_value.IsFormed or not stdev_value.IsFormed:
return
atr = atr_value.GetValue[Decimal](None)
deviation = stdev_value.GetValue[Decimal](None)
if atr <= 0 or deviation <= 0:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
multiplier = Decimal(self._multiplier.Value)
sma = sma_value.GetValue[Decimal](None)
threshold = multiplier * atr / (atr / deviation)
close = candle.ClosePrice
if close < sma - threshold and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._stop_price = close - multiplier * atr
elif close > sma + threshold and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._stop_price = close + multiplier * atr
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 volatility_adjusted_mean_reversion_strategy()