ATR Mean Reversion Strategy
此策略衡量价格偏离移动平均线的距离与近期波动(ATR)的比值。价格偏离越大,ATR阈值越宽,动态适应市场活跃程度。
测试表明年均收益约为 109%,该策略在加密市场表现最佳。
当收盘价低于均线Multiplier*ATR以上时做多;当收盘价高于均线同样距离时做空。价格回到均线时离场。ATR止损保持风险与当前波动相匹配。
适合短线交易者,在过度波动后期望价格回归均值。
细节
- 入场条件:
- 多头:
Close < MA - Multiplier * ATR - 空头:
Close > MA + Multiplier * ATR
- 多头:
- 多/空: 双向
- 离场条件:
- 多头: 收盘价>= MA
- 空头: 收盘价<= MA
- 止损: 默认约
2*ATR - 默认值:
MaPeriod= 20AtrPeriod= 14Multiplier= 2.0mCandleType= TimeSpan.FromMinutes(5)
- 过滤器:
- 类别: Mean Reversion
- 方向: 双向
- 指标: MA, ATR
- 止损: 是
- 复杂度: 中等
- 时间框架: 日内
- 季节性: 否
- 神经网络: 否
- 背离: 否
- 风险等级: 中等
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>
/// ATR Mean Reversion strategy.
/// Trades when price deviates from its average by a multiple of ATR.
/// </summary>
public class AtrMeanReversionStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriodParam;
private readonly StrategyParam<int> _atrPeriodParam;
private readonly StrategyParam<decimal> _multiplierParam;
private readonly StrategyParam<DataType> _candleTypeParam;
private SimpleMovingAverage _sma;
private AverageTrueRange _atr;
/// <summary>
/// Moving average period.
/// </summary>
public int MaPeriod
{
get => _maPeriodParam.Value;
set => _maPeriodParam.Value = value;
}
/// <summary>
/// ATR indicator period.
/// </summary>
public int AtrPeriod
{
get => _atrPeriodParam.Value;
set => _atrPeriodParam.Value = value;
}
/// <summary>
/// ATR 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 AtrMeanReversionStrategy()
{
_maPeriodParam = Param(nameof(MaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Period for Moving Average", "Parameters")
.SetOptimize(10, 50, 10);
_atrPeriodParam = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "Period for ATR indicator", "Parameters")
.SetOptimize(7, 21, 7);
_multiplierParam = Param(nameof(Multiplier), 2.0m)
.SetRange(0.1m, decimal.MaxValue)
.SetDisplay("ATR Multiplier", "ATR 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;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create indicators
_sma = new SMA { Length = MaPeriod };
_atr = new AverageTrueRange { Length = AtrPeriod };
// Create subscription and bind indicators
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_sma, _atr, 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)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Calculate entry thresholds
var upperThreshold = smaValue + Multiplier * atrValue;
var lowerThreshold = smaValue - Multiplier * atrValue;
// 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, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import SimpleMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class atr_mean_reversion_strategy(Strategy):
"""
ATR Mean Reversion strategy.
Trades when price deviates from its average by a multiple of ATR.
"""
def __init__(self):
super(atr_mean_reversion_strategy, self).__init__()
# Initialize strategy parameters
self._maPeriod = self.Param("MaPeriod", 20) \
.SetDisplay("MA Period", "Period for Moving Average", "Parameters") \
.SetCanOptimize(True) \
.SetOptimize(10, 50, 10)
self._atrPeriod = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "Period for ATR indicator", "Parameters") \
.SetCanOptimize(True) \
.SetOptimize(7, 21, 7)
self._multiplier = self.Param("Multiplier", 2.0) \
.SetRange(0.1, float('inf')) \
.SetDisplay("ATR Multiplier", "ATR multiplier for entry threshold", "Parameters") \
.SetCanOptimize(True) \
.SetOptimize(1.0, 3.0, 0.5)
self._candleType = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Candle type for strategy", "Common")
# Indicators
self._sma = None
self._atr = None
@property
def MaPeriod(self):
"""Moving average period."""
return self._maPeriod.Value
@MaPeriod.setter
def MaPeriod(self, value):
self._maPeriod.Value = value
@property
def AtrPeriod(self):
"""ATR indicator period."""
return self._atrPeriod.Value
@AtrPeriod.setter
def AtrPeriod(self, value):
self._atrPeriod.Value = value
@property
def Multiplier(self):
"""ATR multiplier for entry threshold."""
return self._multiplier.Value
@Multiplier.setter
def Multiplier(self, value):
self._multiplier.Value = value
@property
def CandleType(self):
"""Candle type for strategy."""
return self._candleType.Value
@CandleType.setter
def CandleType(self, value):
self._candleType.Value = value
def OnReseted(self):
super(atr_mean_reversion_strategy, self).OnReseted()
self._sma = None
self._atr = None
def OnStarted2(self, time):
super(atr_mean_reversion_strategy, self).OnStarted2(time)
# Create indicators
self._sma = SimpleMovingAverage()
self._sma.Length = self.MaPeriod
self._atr = AverageTrueRange()
self._atr.Length = self.AtrPeriod
# Create subscription and bind indicators
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._sma, self._atr, 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):
if candle.State != CandleStates.Finished:
return
# Calculate entry thresholds
upper_threshold = sma_value + self.Multiplier * atr_value
lower_threshold = sma_value - self.Multiplier * atr_value
# 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 atr_mean_reversion_strategy()