Mean Reversion 戦略
この統計的アプローチは、最近の平均に対する価格の短期的な極値を探します。この戦略は移動平均を公正価値の定義に使用し、標準偏差の計算によってその平均からの乖離を測定します。
テストでは年間平均リターン約85%を示しています。暗号資産市場で最もパフォーマンスが高いです。
価格が平均から設定された距離を押したときにトレードが開かれます。下部バンドを下回る下落は、平均への反発を期待してロングエントリーを引き起こし、上部バンドを上回るラリーはショートを促します。価格が再び移動平均に触れると、開いているポジションはすべて閉じられます。
この手法は、明確に定義されたエントリーとエグジットゾーンを求める逆張りスタイルのトレーダーに魅力的です。ボラティリティベースのバンドに依存しているため、静かな市場や活発な市場に適応しながら、固定ストップロスによって損失を抑えます。
詳細
- エントリー条件:
- ロング: Price < MA - k*StdDev (below lower band)
- ショート: Price > MA + k*StdDev (above upper band)
- ロング/ショート: 両方。
- エグジット条件:
- ロング: 価格が移動平均を上回ったときに終了
- ショート: 価格が移動平均を下回ったときに終了
- ストップ: はい。
- デフォルト値:
MovingAveragePeriod= 20DeviationMultiplier= 2StopLossPercent= 2 MAは単純移動平均、StdDevは同じMovingAveragePeriod本の終値の標準偏差で、kはDeviationMultiplierです。ストップはエントリー価格の固定StopLossPercentで、ローソク足の間も監視されます。 保有ポジションと逆方向のエントリーシグナルはドテンになります。CandleType= TimeSpan.FromMinutes(5)
- フィルター:
- カテゴリ: 平均回帰
- 方向: 両方
- インジケーター: Mean Reversion
- ストップ: はい
- 複雑さ: 中級
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Mean Reversion strategy.
/// The bands lie DeviationMultiplier standard deviations around the MovingAveragePeriod simple moving average, both measured over the same
/// candles. A close below the lower band goes long and a close above the upper band goes short, reversing an opposite position.
/// A long closes once price closes above the average and a short once it closes below, and a percent stop limits the loss.
/// </summary>
public class MeanReversionStrategy : Strategy
{
private readonly StrategyParam<int> _movingAveragePeriod;
private readonly StrategyParam<decimal> _deviationMultiplier;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
/// <summary>
/// Period of the moving average and the standard deviation.
/// </summary>
public int MovingAveragePeriod
{
get => _movingAveragePeriod.Value;
set => _movingAveragePeriod.Value = value;
}
/// <summary>
/// Standard deviations between the average and a band.
/// </summary>
public decimal DeviationMultiplier
{
get => _deviationMultiplier.Value;
set => _deviationMultiplier.Value = value;
}
/// <summary>
/// Stop loss percentage from entry price.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public MeanReversionStrategy()
{
_movingAveragePeriod = Param(nameof(MovingAveragePeriod), 20)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Period of the moving average and the standard deviation", "Indicators");
_deviationMultiplier = Param(nameof(DeviationMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("Deviation Multiplier", "Standard deviations between the average and a band", "Indicators");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_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 OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ma = new SimpleMovingAverage { Length = MovingAveragePeriod };
var stdev = new StandardDeviation { Length = MovingAveragePeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(ma, stdev, ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
// The stop has to see prices between candles, not only at their close.
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ma);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, stdev);
}
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue maValue, IIndicatorValue stdDevValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!maValue.IsFormed || !stdDevValue.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var ma = maValue.GetValue<decimal>();
var deviation = stdDevValue.GetValue<decimal>() * DeviationMultiplier;
var upper = ma + deviation;
var lower = ma - deviation;
var close = candle.ClosePrice;
if (close < lower && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (close > upper && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && close > ma)
SellMarket(Position);
else if (Position < 0 && close < ma)
BuyMarket(-Position);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Indicators import SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
class mean_reversion_strategy(Strategy):
"""
Mean Reversion strategy.
The bands lie DeviationMultiplier standard deviations around the MovingAveragePeriod simple moving average, both measured over the same
candles. A close below the lower band goes long and a close above the upper band goes short, reversing an opposite position.
A long closes once price closes above the average and a short once it closes below, and a percent stop limits the loss.
"""
def __init__(self):
super(mean_reversion_strategy, self).__init__()
self._moving_average_period = self.Param("MovingAveragePeriod", 20).SetGreaterThanZero().SetDisplay("MA Period", "Period of the moving average and the standard deviation", "Indicators")
self._deviation_multiplier = self.Param("DeviationMultiplier", 2.0).SetGreaterThanZero().SetDisplay("Deviation Multiplier", "Standard deviations between the average and a band", "Indicators")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(mean_reversion_strategy, self).OnStarted2(time)
ma = SimpleMovingAverage()
ma.Length = self._moving_average_period.Value
stdev = StandardDeviation()
stdev.Length = self._moving_average_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(ma, stdev, self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
# The stop has to see prices between candles, not only at their close.
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ma)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, stdev)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _process_candle(self, candle, ma_value, std_dev_value):
if candle.State != CandleStates.Finished:
return
if not ma_value.IsFormed or not std_dev_value.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
ma = ma_value.GetValue[Decimal](None)
deviation = std_dev_value.GetValue[Decimal](None) * Decimal(self._deviation_multiplier.Value)
upper = ma + deviation
lower = ma - deviation
close = candle.ClosePrice
if close < lower and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif close > upper and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and close > ma:
self.SellMarket(self.Position)
elif self.Position < 0 and close < ma:
self.BuyMarket(-self.Position)
def CreateClone(self):
return mean_reversion_strategy()