自己相関リバーサル戦略
この戦略は短期的な価格の自己相関を分析して、最近の動きが反転する可能性があるかを評価します。負の自己相関は、連続する価格変動が方向を交互に変える傾向があることを示し、平均回帰的な条件を作り出します。
テストでは年平均リターンは約124%を示しています。外国為替市場で最もよいパフォーマンスを発揮します。
計算された自己相関がしきい値を下回り、価格が移動平均を下回っているとき、システムは反発を期待して買います。自己相関が負で価格が平均を上回っている場合、ショートポジションが建てられます。価格が平均を越えるか、自己相関がしきい値を上回ったときに決済します。
このアプローチは、チャートパターンよりも統計的な優位性を探すトレーダーに適しています。予想される反転に反する持続的なトレンドから保護するために、パーセンテージストップロスが適用されます。
詳細
- エントリー条件:
- ロング: Autocorrelation < Threshold && Close < MA
- ショート: Autocorrelation < Threshold && Close > MA
- ロング/ショート: 両方。
- エグジット条件:
- ロング: Close > MA または autocorrelation > Threshold のときに決済
- ショート: Close < MA または autocorrelation > Threshold のときに決済
- ストップ: あり、パーセンテージストップロス。
- デフォルト値:
AutoCorrPeriod= 20AutoCorrThreshold= -0.3StopLossPercent= 2 自己相関は直近AutoCorrPeriod本の終値変化のラグ1自己相関で、MAは同じAutoCorrPeriod本の単純移動平均です。ストップはエントリー価格の固定StopLossPercentで、ローソク足の間も監視されます。0で無効になります。 保有ポジションと逆方向のエントリーシグナルはドテンになります。CandleType= TimeSpan.FromMinutes(5)
- フィルター:
- カテゴリ: 平均回帰
- 方向: 両方
- インジケーター: Autocorrelation, MA
- ストップ: はい
- 複雑さ: 中級
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Autocorrelation Reversal strategy.
/// The autocorrelation is the lag-one autocorrelation of the close-to-close changes over the last AutoCorrPeriod closes. Below
/// AutoCorrThreshold a close under the AutoCorrPeriod simple moving average goes long and a close above it goes short, reversing an opposite
/// position. A long closes once the close is above the average or the autocorrelation rises above the threshold, a short mirrors it,
/// and a percent stop limits the loss.
/// </summary>
public class AutocorrelationReversionStrategy : Strategy
{
private readonly StrategyParam<int> _autoCorrPeriod;
private readonly StrategyParam<decimal> _autoCorrThreshold;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private readonly Queue<decimal> _closes = [];
/// <summary>
/// Closes the autocorrelation and the average span.
/// </summary>
public int AutoCorrPeriod
{
get => _autoCorrPeriod.Value;
set => _autoCorrPeriod.Value = value;
}
/// <summary>
/// Autocorrelation below which the market reverts.
/// </summary>
public decimal AutoCorrThreshold
{
get => _autoCorrThreshold.Value;
set => _autoCorrThreshold.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 AutocorrelationReversionStrategy()
{
_autoCorrPeriod = Param(nameof(AutoCorrPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Autocorrelation Period", "Closes the autocorrelation and the average span", "Indicators");
_autoCorrThreshold = Param(nameof(AutoCorrThreshold), -0.3m)
.SetDisplay("Autocorrelation Threshold", "Autocorrelation below which the market reverts", "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 OnReseted()
{
base.OnReseted();
_closes.Clear();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_closes.Clear();
var sma = new SimpleMovingAverage { Length = AutoCorrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(sma, 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, sma);
DrawOwnTrades(area);
}
}
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 smaValue)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
_closes.Enqueue(close);
if (_closes.Count > AutoCorrPeriod)
_closes.Dequeue();
if (_closes.Count < AutoCorrPeriod || !smaValue.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var autocorrelation = CalculateAutocorrelation();
var sma = smaValue.GetValue<decimal>();
var reverting = autocorrelation < AutoCorrThreshold;
if (reverting && close < sma && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (reverting && close > sma && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && (close > sma || autocorrelation > AutoCorrThreshold))
SellMarket(Position);
else if (Position < 0 && (close < sma || autocorrelation > AutoCorrThreshold))
BuyMarket(-Position);
}
private decimal CalculateAutocorrelation()
{
var closes = _closes.ToArray();
var changes = new decimal[closes.Length - 1];
for (var i = 1; i < closes.Length; i++)
changes[i - 1] = closes[i] - closes[i - 1];
var mean = changes.Average();
var numerator = 0m;
var denominator = 0m;
for (var i = 0; i < changes.Length; i++)
{
var deviation = changes[i] - mean;
denominator += deviation * deviation;
if (i > 0)
numerator += (changes[i - 1] - mean) * deviation;
}
return denominator == 0 ? 0 : numerator / denominator;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class autocorrelation_reversion_strategy(Strategy):
"""
Autocorrelation Reversal strategy.
The autocorrelation is the lag-one autocorrelation of the close-to-close changes over the last AutoCorrPeriod closes. Below
AutoCorrThreshold a close under the AutoCorrPeriod simple moving average goes long and a close above it goes short, reversing an opposite
position. A long closes once the close is above the average or the autocorrelation rises above the threshold, a short mirrors it,
and a percent stop limits the loss.
"""
def __init__(self):
super(autocorrelation_reversion_strategy, self).__init__()
self._auto_corr_period = self.Param("AutoCorrPeriod", 20).SetGreaterThanZero().SetDisplay("Autocorrelation Period", "Closes the autocorrelation and the average span", "Indicators")
self._auto_corr_threshold = self.Param("AutoCorrThreshold", -0.3).SetDisplay("Autocorrelation Threshold", "Autocorrelation below which the market reverts", "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")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._closes = []
def OnReseted(self):
super(autocorrelation_reversion_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(autocorrelation_reversion_strategy, self).OnStarted2(time)
self._reset_state()
sma = SimpleMovingAverage()
sma.Length = self._auto_corr_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(sma, 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, sma)
self.DrawOwnTrades(area)
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, sma_value):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
period = self._auto_corr_period.Value
self._closes.append(close)
if len(self._closes) > period:
self._closes.pop(0)
if len(self._closes) < period or not sma_value.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
autocorrelation = self._calculate_autocorrelation()
sma = sma_value.GetValue[Decimal](None)
threshold = Decimal(self._auto_corr_threshold.Value)
reverting = autocorrelation < threshold
if reverting and close < sma and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif reverting and close > sma and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and (close > sma or autocorrelation > threshold):
self.SellMarket(self.Position)
elif self.Position < 0 and (close < sma or autocorrelation > threshold):
self.BuyMarket(-self.Position)
def _calculate_autocorrelation(self):
changes = [self._closes[i] - self._closes[i - 1] for i in range(1, len(self._closes))]
total = Decimal(0)
for change in changes:
total += change
mean = total / Decimal(len(changes))
numerator = Decimal(0)
denominator = Decimal(0)
for i in range(len(changes)):
deviation = changes[i] - mean
denominator += deviation * deviation
if i > 0:
numerator += (changes[i - 1] - mean) * deviation
if denominator == 0:
return Decimal(0)
return numerator / denominator
def CreateClone(self):
return autocorrelation_reversion_strategy()