Volatility Adjusted Mean Reversion Strategy
此均值回归变体根据ATR与标准差之比调整入场阈值。若相对噪声的波动增大,触发交易所需的距离也随之增加,避免在剧烈波动时过早进场。
测试表明年均收益约为 115%,该策略在股票市场表现最佳。
当价格低于均线超过调整后的阈值时做多;价格高于均线同样距离时做空。价格重新靠近均线时离场。止损等于2倍ATR,在等待回归期间控制风险。
该自适应阈值适用于波动性变化较大的市场。
细节
- 入场条件:
- 多头:
Price < MA - Multiplier * ATR/StdDev - 空头:
Price > MA + Multiplier * ATR/StdDev
- 多头:
- 多/空: 双向
- 离场条件:
- 多头: 收盘价>= MA
- 空头: 收盘价<= MA
- 止损: 基于ATR动态设置
- 默认值:
Period= 20Multiplier= 2.0mCandleType= TimeSpan.FromMinutes(5)
- 过滤器:
- 类别: Mean Reversion
- 方向: 双向
- 指标: ATR, StdDev
- 止损: 是
- 复杂度: 中等
- 时间框架: 日内
- 季节性: 否
- 神经网络: 否
- 背离: 否
- 风险等级: 中等
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>
/// Volatility Adjusted Mean Reversion strategy.
/// Uses ATR and Standard Deviation to create adaptive entry thresholds.
/// </summary>
public class VolatilityAdjustedMeanReversionStrategy : Strategy
{
private readonly StrategyParam<int> _periodParam;
private readonly StrategyParam<decimal> _multiplierParam;
private readonly StrategyParam<DataType> _candleTypeParam;
private SimpleMovingAverage _sma;
private AverageTrueRange _atr;
private StandardDeviation _stdDev;
/// <summary>
/// Period for indicators.
/// </summary>
public int Period
{
get => _periodParam.Value;
set => _periodParam.Value = value;
}
/// <summary>
/// Multiplier for entry threshold.
/// </summary>
public decimal Multiplier
{
get => _multiplierParam.Value;
set => _multiplierParam.Value = value;
}
/// <summary>
/// Candle type for strategy.
/// </summary>
public DataType CandleType
{
get => _candleTypeParam.Value;
set => _candleTypeParam.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public VolatilityAdjustedMeanReversionStrategy()
{
_periodParam = Param(nameof(Period), 20)
.SetGreaterThanZero()
.SetDisplay("Period", "Period for indicators", "Parameters")
.SetOptimize(10, 50, 10);
_multiplierParam = Param(nameof(Multiplier), 2.0m)
.SetRange(0.1m, decimal.MaxValue)
.SetDisplay("Multiplier", "Multiplier for entry threshold", "Parameters")
.SetOptimize(1.0m, 3.0m, 0.5m);
_candleTypeParam = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle type for strategy", "Common");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_sma = null;
_atr = null;
_stdDev = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create indicators
_sma = new SMA { Length = Period };
_atr = new AverageTrueRange { Length = Period };
_stdDev = new StandardDeviation { Length = Period };
// Create subscription and bind indicators
var subscription = SubscribeCandles(CandleType);
// First, bind SMA and ATR
subscription
.Bind(_sma, _atr, _stdDev, ProcessCandle)
.Start();
// Setup chart visualization if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _sma);
DrawIndicator(area, _atr);
DrawOwnTrades(area);
}
// Enable position protection
StartProtection(
takeProfit: new Unit(0, UnitTypes.Absolute), // No take profit
stopLoss: new Unit(2, UnitTypes.Absolute) // Stop loss at 2*ATR
);
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue, decimal atrValue, decimal stdDevValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Skip if standard deviation is too small to avoid division by zero
if (stdDevValue < 0.0001m)
return;
// Calculate volatility ratio
var volatilityRatio = atrValue / stdDevValue;
// Calculate volatility-adjusted thresholds
var threshold = Multiplier * atrValue / volatilityRatio;
var upperThreshold = smaValue + threshold;
var lowerThreshold = smaValue - threshold;
// Long setup - price below lower threshold
if (candle.ClosePrice < lowerThreshold && Position <= 0)
{
// Buy signal - price has deviated too much below average
BuyMarket(Volume + Math.Abs(Position));
}
// Short setup - price above upper threshold
else if (candle.ClosePrice > upperThreshold && Position >= 0)
{
// Sell signal - price has deviated too much above average
SellMarket(Volume + Math.Abs(Position));
}
// Exit long position when price returns to average
else if (Position > 0 && candle.ClosePrice >= smaValue)
{
// Close long position
SellMarket(Position);
}
// Exit short position when price returns to average
else if (Position < 0 && candle.ClosePrice <= smaValue)
{
// Close short position
BuyMarket(Math.Abs(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, Math
from StockSharp.Messages import DataType, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage, AverageTrueRange, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class volatility_adjusted_mean_reversion_strategy(Strategy):
"""
Volatility Adjusted Mean Reversion strategy.
Uses ATR and Standard Deviation to create adaptive entry thresholds.
"""
def __init__(self):
super(volatility_adjusted_mean_reversion_strategy, self).__init__()
# Period for indicators.
self._period = self.Param("Period", 20) \
.SetGreaterThanZero() \
.SetDisplay("Period", "Period for indicators", "Parameters") \
.SetCanOptimize(True) \
.SetOptimize(10, 50, 10)
# Multiplier for entry threshold.
self._multiplier = self.Param("Multiplier", 2.0) \
.SetRange(0.1, 1e6) \
.SetDisplay("Multiplier", "Multiplier for entry threshold", "Parameters") \
.SetCanOptimize(True) \
.SetOptimize(1.0, 3.0, 0.5)
# Candle type for strategy.
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Candle type for strategy", "Common")
# Internal indicators
self._sma = None
self._atr = None
self._std_dev = None
@property
def period(self):
"""Period for indicators."""
return self._period.Value
@period.setter
def period(self, value):
self._period.Value = value
@property
def multiplier(self):
"""Multiplier for entry threshold."""
return self._multiplier.Value
@multiplier.setter
def multiplier(self, value):
self._multiplier.Value = value
@property
def candle_type(self):
"""Candle type for strategy."""
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def GetWorkingSecurities(self):
return [(self.Security, self.candle_type)]
def OnReseted(self):
super(volatility_adjusted_mean_reversion_strategy, self).OnReseted()
self._sma = None
self._atr = None
self._std_dev = None
def OnStarted2(self, time):
super(volatility_adjusted_mean_reversion_strategy, self).OnStarted2(time)
# Create indicators
self._sma = SimpleMovingAverage()
self._sma.Length = self.period
self._atr = AverageTrueRange()
self._atr.Length = self.period
self._std_dev = StandardDeviation()
self._std_dev.Length = self.period
# Create subscription and bind indicators
subscription = self.SubscribeCandles(self.candle_type)
# First, bind SMA and ATR
subscription.Bind(self._sma, self._atr, self._std_dev, self.ProcessCandle).Start()
# Setup chart visualization if available
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._sma)
self.DrawIndicator(area, self._atr)
self.DrawOwnTrades(area)
# Enable position protection
self.StartProtection(
takeProfit=Unit(0, UnitTypes.Absolute),
stopLoss=Unit(2, UnitTypes.Absolute)
)
def ProcessCandle(self, candle, sma_value, atr_value, std_dev_value):
if candle.State != CandleStates.Finished:
return
# Skip if standard deviation is too small to avoid division by zero
if std_dev_value < 0.0001:
return
# Calculate volatility ratio
volatility_ratio = atr_value / std_dev_value
# Calculate volatility-adjusted thresholds
threshold = self.multiplier * atr_value / volatility_ratio
upper_threshold = sma_value + threshold
lower_threshold = sma_value - threshold
# Long setup - price below lower threshold
if candle.ClosePrice < lower_threshold and self.Position <= 0:
# Buy signal - price has deviated too much below average
self.BuyMarket(self.Volume + Math.Abs(self.Position))
# Short setup - price above upper threshold
elif candle.ClosePrice > upper_threshold and self.Position >= 0:
# Sell signal - price has deviated too much above average
self.SellMarket(self.Volume + Math.Abs(self.Position))
# Exit long position when price returns to average
elif self.Position > 0 and candle.ClosePrice >= sma_value:
# Close long position
self.SellMarket(self.Position)
# Exit short position when price returns to average
elif self.Position < 0 and candle.ClosePrice <= sma_value:
# Close short position
self.BuyMarket(Math.Abs(self.Position))
def CreateClone(self):
"""
!! REQUIRED!! Creates a new instance of the strategy.
"""
return volatility_adjusted_mean_reversion_strategy()