Hurst Exponentボラティリティフィルター戦略
Hurst Exponentボラティリティフィルター戦略は、Hurst Exponentをボラティリティフィルターと組み合わせて使用します。指定された条件が揃ったときのみトレードに参入します。
テストでは平均年間リターンが約163%であることが示されています。株式市場で最もよいパフォーマンスを発揮します。
シグナルは、インジケーターが閾値を超え、ボラティリティが事前定義された基準を満たすことを必要とします。ポジションはロングまたはショートで、組み込みのストップがあります。
リスク管理を重視するトレーダー向けに設計されており、インジケーターが平均回帰するかボラティリティが変化するとすぐに戦略が退出します。初期設定 HurstPeriod = 100。
詳細
- エントリー条件: インジケーターが平均に向かって戻るようにクロスする。
- ロング/ショート: 両方。
- エグジット条件: インジケーターが平均に回帰する。
- ストップ: はい。
- デフォルト値:
HurstPeriod= 100MAPeriod= 20ATRPeriod= 14StopLoss= 2.0mCandleType= TimeSpan.FromMinutes(5)
- フィルター:
- カテゴリ: 平均回帰
- 方向: 両方
- インジケーター: Hurst
- ストップ: はい
- 複雑さ: 中級
- 時間軸: 短期
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Hurst exponent mean reversion with a volatility filter.
/// Trades back toward the moving average only while the Hurst exponent is below 0.5 (anti-persistent prices)
/// and ATR is below its own average. Exits when price returns to the moving average or volatility expands.
/// </summary>
public class HurstVolatilityFilterStrategy : Strategy
{
private const decimal _randomWalkHurst = 0.5m;
private readonly StrategyParam<int> _hurstPeriod;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _atrAverage;
private HurstExponent _hurst;
private decimal? _prevClose;
/// <summary>
/// Period for the Hurst exponent.
/// </summary>
public int HurstPeriod
{
get => _hurstPeriod.Value;
set => _hurstPeriod.Value = value;
}
/// <summary>
/// Period for the moving average.
/// </summary>
public int MAPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Period for ATR and its average.
/// </summary>
public int ATRPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Stop-loss percentage.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="HurstVolatilityFilterStrategy"/>.
/// </summary>
public HurstVolatilityFilterStrategy()
{
_hurstPeriod = Param(nameof(HurstPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("Hurst Period", "Period for the Hurst exponent", "Indicators");
_maPeriod = Param(nameof(MAPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Period for the moving average", "Indicators");
_atrPeriod = Param(nameof(ATRPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "Period for ATR and its average", "Indicators");
_stopLoss = Param(nameof(StopLoss), 2.0m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop-loss percentage", "Risk Management");
_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();
_atrAverage = null;
_hurst = null;
_prevClose = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sma = new SimpleMovingAverage { Length = MAPeriod };
var atr = new AverageTrueRange { Length = ATRPeriod };
_hurst = new HurstExponent { Length = HurstPeriod };
_atrAverage = new SimpleMovingAverage { Length = ATRPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, atr, ProcessCandle)
.Start();
StartProtection(
takeProfit: null,
stopLoss: StopLoss > 0 ? new Unit(StopLoss, UnitTypes.Percent) : null
);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
var atrAverage = _atrAverage.Process(atrValue, candle.ServerTime, true).ToDecimal();
var prevClose = _prevClose;
_prevClose = candle.ClosePrice;
if (prevClose is not decimal prev)
return;
// Hurst is measured on bar-to-bar price changes, where 0.5 separates trending from mean-reverting behaviour.
var hurstResult = _hurst.Process(candle.ClosePrice - prev, candle.ServerTime, true);
if (!_atrAverage.IsFormed || !_hurst.IsFormed || hurstResult.IsEmpty)
return;
var hurstValue = hurstResult.ToDecimal();
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var isQuiet = atrValue < atrAverage;
var isMeanReverting = hurstValue < _randomWalkHurst;
if (Position > 0 && (close >= smaValue || !isQuiet))
{
SellMarket(Position);
return;
}
if (Position < 0 && (close <= smaValue || !isQuiet))
{
BuyMarket(-Position);
return;
}
if (!isMeanReverting || !isQuiet)
return;
if (close < smaValue && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (close > smaValue && Position >= 0)
SellMarket(Volume + 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
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import SimpleMovingAverage, AverageTrueRange, HurstExponent
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
RANDOM_WALK_HURST = 0.5
class hurst_volatility_filter_strategy(Strategy):
"""
Hurst exponent mean reversion with a volatility filter.
Trades back toward the moving average only while the Hurst exponent is below 0.5 (anti-persistent prices)
and ATR is below its own average. Exits when price returns to the moving average or volatility expands.
"""
def __init__(self):
super(hurst_volatility_filter_strategy, self).__init__()
self._hurst_period = self.Param("HurstPeriod", 100) \
.SetGreaterThanZero() \
.SetDisplay("Hurst Period", "Period for the Hurst exponent", "Indicators")
self._ma_period = self.Param("MAPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("MA Period", "Period for the moving average", "Indicators")
self._atr_period = self.Param("ATRPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("ATR Period", "Period for ATR and its average", "Indicators")
self._stop_loss = self.Param("StopLoss", 2.0) \
.SetNotNegative() \
.SetDisplay("Stop Loss %", "Stop-loss percentage", "Risk Management")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._atr_average = None
self._hurst = None
self._prev_close = None
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnReseted(self):
super(hurst_volatility_filter_strategy, self).OnReseted()
self._atr_average = None
self._hurst = None
self._prev_close = None
def OnStarted2(self, time):
super(hurst_volatility_filter_strategy, self).OnStarted2(time)
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
self._hurst = HurstExponent()
self._hurst.Length = self._hurst_period.Value
self._atr_average = SimpleMovingAverage()
self._atr_average.Length = self._atr_period.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(sma, atr, self._process_candle).Start()
stop = float(self._stop_loss.Value)
self.StartProtection(None, Unit(stop, UnitTypes.Percent) if stop > 0 else None)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, sma_value, atr_value):
if candle.State != CandleStates.Finished:
return
sma_value = float(sma_value)
atr_value = float(atr_value)
close = float(candle.ClosePrice)
atr_average = float(process_float(self._atr_average, atr_value, candle.ServerTime, True))
prev_close = self._prev_close
self._prev_close = close
if prev_close is None:
return
# Hurst is measured on bar-to-bar price changes, where 0.5 separates trending from mean-reverting behaviour.
hurst_result = process_float(self._hurst, close - prev_close, candle.ServerTime, True)
if not self._atr_average.IsFormed or not self._hurst.IsFormed or hurst_result.IsEmpty:
return
hurst_value = float(hurst_result)
if not self.IsFormedAndOnlineAndAllowTrading():
return
is_quiet = atr_value < atr_average
is_mean_reverting = hurst_value < RANDOM_WALK_HURST
if self.Position > 0 and (close >= sma_value or not is_quiet):
self.SellMarket(self.Position)
return
if self.Position < 0 and (close <= sma_value or not is_quiet):
self.BuyMarket(-self.Position)
return
if not is_mean_reverting or not is_quiet:
return
if close < sma_value and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif close > sma_value and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return hurst_volatility_filter_strategy()