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;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Mean-reversion strategy that enters only when Hurst indicates anti-persistent behavior and ATR confirms a quiet regime.
/// </summary>
public class HurstVolatilityFilterStrategy : Strategy
{
private readonly StrategyParam<int> _hurstPeriod;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _hurstThreshold;
private readonly StrategyParam<decimal> _deviationAtrMultiplier;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _sma;
private AverageTrueRange _atr;
private HurstExponent _hurstExponent;
private SimpleMovingAverage _atrAverage;
private int _cooldown;
/// <summary>
/// Period for Hurst exponent calculation.
/// </summary>
public int HurstPeriod
{
get => _hurstPeriod.Value;
set => _hurstPeriod.Value = value;
}
/// <summary>
/// Period for moving average calculation.
/// </summary>
public int MAPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Period for ATR calculation.
/// </summary>
public int ATRPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Maximum Hurst value allowed for entries.
/// </summary>
public decimal HurstThreshold
{
get => _hurstThreshold.Value;
set => _hurstThreshold.Value = value;
}
/// <summary>
/// ATR multiple required for deviation from the moving average.
/// </summary>
public decimal DeviationAtrMultiplier
{
get => _deviationAtrMultiplier.Value;
set => _deviationAtrMultiplier.Value = value;
}
/// <summary>
/// Stop loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Bars to wait after each order.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Candle series used for calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public HurstVolatilityFilterStrategy()
{
_hurstPeriod = Param(nameof(HurstPeriod), 80)
.SetRange(20, 200)
.SetDisplay("Hurst Period", "Period for the Hurst exponent", "Indicators");
_maPeriod = Param(nameof(MAPeriod), 20)
.SetRange(5, 100)
.SetDisplay("MA Period", "Period for the moving average", "Indicators");
_atrPeriod = Param(nameof(ATRPeriod), 14)
.SetRange(5, 50)
.SetDisplay("ATR Period", "Period for the ATR", "Indicators");
_hurstThreshold = Param(nameof(HurstThreshold), 0.7m)
.SetRange(-1m, 1m)
.SetDisplay("Hurst Threshold", "Maximum Hurst value allowed for entries", "Signals");
_deviationAtrMultiplier = Param(nameof(DeviationAtrMultiplier), 0.5m)
.SetRange(0.1m, 5m)
.SetDisplay("Deviation ATR", "Minimum ATR multiple required for entry", "Signals");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetRange(0.5m, 10m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_cooldownBars = Param(nameof(CooldownBars), 90)
.SetRange(1, 500)
.SetDisplay("Cooldown Bars", "Bars to wait after each order", "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()
{
if (Security != null)
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_sma = null;
_atr = null;
_hurstExponent = null;
_atrAverage = null;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
if (Security == null)
throw new InvalidOperationException("Security is not specified.");
_sma = new SimpleMovingAverage { Length = MAPeriod };
_atr = new AverageTrueRange { Length = ATRPeriod };
_hurstExponent = new HurstExponent { Length = HurstPeriod };
_atrAverage = new SimpleMovingAverage { Length = Math.Max(ATRPeriod * 2, 10) };
_cooldown = 0;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_sma, _atr, _hurstExponent, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _sma);
DrawIndicator(area, _atr);
DrawIndicator(area, _hurstExponent);
DrawOwnTrades(area);
}
StartProtection(new Unit(0, UnitTypes.Absolute), new Unit(StopLossPercent, UnitTypes.Percent), false);
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue, decimal atrValue, decimal hurstValue)
{
if (candle.State != CandleStates.Finished)
return;
var atrAverageValue = _atrAverage.Process(atrValue, candle.OpenTime, true).ToDecimal();
if (!_sma.IsFormed || !_atr.IsFormed || !_hurstExponent.IsFormed || !_atrAverage.IsFormed)
return;
if (ProcessState != ProcessStates.Started)
return;
if (_cooldown > 0)
{
_cooldown--;
return;
}
var price = candle.ClosePrice;
var deviation = price - smaValue;
var requiredDeviation = atrValue * DeviationAtrMultiplier;
var isMeanReversionRegime = hurstValue <= HurstThreshold;
var isQuietVolatility = atrValue <= atrAverageValue * 1.5m;
if (Position == 0)
{
if (!isMeanReversionRegime || !isQuietVolatility)
return;
if (deviation <= -requiredDeviation)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (deviation >= requiredDeviation)
{
SellMarket();
_cooldown = CooldownBars;
}
return;
}
if (Position > 0 && (price >= smaValue || deviation >= -atrValue * 0.2m || !isMeanReversionRegime))
{
SellMarket(Math.Abs(Position));
_cooldown = CooldownBars;
}
else if (Position < 0 && (price <= smaValue || deviation <= atrValue * 0.2m || !isMeanReversionRegime))
{
BuyMarket(Math.Abs(Position));
_cooldown = CooldownBars;
}
}
}
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, Decimal
from StockSharp.Messages import DataType, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage, AverageTrueRange, HurstExponent
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class hurst_volatility_filter_strategy(Strategy):
"""
Mean-reversion strategy that enters only when Hurst indicates anti-persistent
behavior and ATR confirms a quiet regime.
"""
def __init__(self):
super(hurst_volatility_filter_strategy, self).__init__()
self._hurst_period = self.Param("HurstPeriod", 80) \
.SetDisplay("Hurst Period", "Period for the Hurst exponent", "Indicators")
self._ma_period = self.Param("MAPeriod", 20) \
.SetDisplay("MA Period", "Period for the moving average", "Indicators")
self._atr_period = self.Param("ATRPeriod", 14) \
.SetDisplay("ATR Period", "Period for the ATR", "Indicators")
self._hurst_threshold = self.Param("HurstThreshold", 0.7) \
.SetDisplay("Hurst Threshold", "Maximum Hurst value allowed for entries", "Signals")
self._deviation_atr_multiplier = self.Param("DeviationAtrMultiplier", 0.5) \
.SetDisplay("Deviation ATR", "Minimum ATR multiple required for entry", "Signals")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._cooldown_bars = self.Param("CooldownBars", 90) \
.SetDisplay("Cooldown Bars", "Bars to wait after each order", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._sma = None
self._atr = None
self._hurst_exponent = None
self._atr_average = None
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(hurst_volatility_filter_strategy, self).OnReseted()
self._sma = None
self._atr = None
self._hurst_exponent = None
self._atr_average = None
self._cooldown = 0
def OnStarted2(self, time):
super(hurst_volatility_filter_strategy, self).OnStarted2(time)
atr_period = int(self._atr_period.Value)
self._sma = SimpleMovingAverage()
self._sma.Length = int(self._ma_period.Value)
self._atr = AverageTrueRange()
self._atr.Length = atr_period
self._hurst_exponent = HurstExponent()
self._hurst_exponent.Length = int(self._hurst_period.Value)
self._atr_average = SimpleMovingAverage()
self._atr_average.Length = max(atr_period * 2, 10)
self._cooldown = 0
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._sma, self._atr, self._hurst_exponent, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._sma)
self.DrawIndicator(area, self._atr)
self.DrawIndicator(area, self._hurst_exponent)
self.DrawOwnTrades(area)
self.StartProtection(Unit(0, UnitTypes.Absolute), Unit(self._stop_loss_percent.Value, UnitTypes.Percent), False)
def _process_candle(self, candle, sma_value, atr_value, hurst_value):
if candle.State != CandleStates.Finished:
return
sma_val = float(sma_value)
atr_val = float(atr_value)
hurst_val = float(hurst_value)
atr_average_value = float(process_float(self._atr_average, Decimal(atr_val), candle.OpenTime, True))
if not self._sma.IsFormed or not self._atr.IsFormed or not self._hurst_exponent.IsFormed or not self._atr_average.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._cooldown > 0:
self._cooldown -= 1
return
price = float(candle.ClosePrice)
deviation = price - sma_val
dev_mult = float(self._deviation_atr_multiplier.Value)
required_deviation = atr_val * dev_mult
hurst_thresh = float(self._hurst_threshold.Value)
is_mean_reversion_regime = hurst_val <= hurst_thresh
is_quiet_volatility = atr_val <= atr_average_value * 1.5
cd = int(self._cooldown_bars.Value)
if self.Position == 0:
if not is_mean_reversion_regime or not is_quiet_volatility:
return
if deviation <= -required_deviation:
self.BuyMarket()
self._cooldown = cd
elif deviation >= required_deviation:
self.SellMarket()
self._cooldown = cd
return
if self.Position > 0 and (price >= sma_val or deviation >= -atr_val * 0.2 or not is_mean_reversion_regime):
self.SellMarket(Math.Abs(self.Position))
self._cooldown = cd
elif self.Position < 0 and (price <= sma_val or deviation <= atr_val * 0.2 or not is_mean_reversion_regime):
self.BuyMarket(Math.Abs(self.Position))
self._cooldown = cd
def CreateClone(self):
return hurst_volatility_filter_strategy()