Parabolic SAR Hurst Filter戦略
Parabolic SAR Hurst Filter戦略はParabolic SAR Hurst Filterを中心に構築されています。
テストでは年平均リターン約82%が示されています。株式市場で最もよいパフォーマンスを発揮します。
ParabolicがイントラデイデータでフィルタリングされたエントリーConfirmするとシグナルが発生します (5m)。この手法はアクティブなトレーダーに適しています。
ストップはATRの倍数とSarAccelerationFactor、SarMaxAccelerationFactorなどのパラメーターに依存します。リスクとリワードのバランスを取るためにこれらのデフォルト値を調整してください。
詳細
- エントリー条件: インジケーター条件の実装を参照。
- ロング/ショート: 両方向。
- エグジット条件: 逆シグナルまたはストップロジック。
- ストップ: はい、インジケーターベースの計算を使用。
- デフォルト値:
SarAccelerationFactor = 0.02mSarMaxAccelerationFactor = 0.2mHurstPeriod = 100CandleType = TimeSpan.FromMinutes(5).TimeFrame()
- フィルター:
- カテゴリ: トレンドフォロー
- 方向: 両方
- インジケーター: Parabolic, Hurst
- ストップ: はい
- 複雑さ: 中級
- 時間軸: イントラデイ (5m)
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Parabolic SAR with Hurst Filter Strategy.
/// Enters a position when price crosses SAR and Hurst exponent indicates a persistent trend.
/// </summary>
public class ParabolicSarHurstStrategy : Strategy
{
private readonly StrategyParam<decimal> _sarAccelerationFactor;
private readonly StrategyParam<decimal> _sarMaxAccelerationFactor;
private readonly StrategyParam<int> _hurstPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _signalCooldownBars;
private ParabolicSar _parabolicSar = null!;
private HurstExponent _hurstIndicator = null!;
private decimal _prevSarValue;
private decimal _hurstValue;
private bool? _prevPriceAboveSar;
private int _cooldownRemaining;
/// <summary>
/// Parabolic SAR acceleration factor.
/// </summary>
public decimal SarAccelerationFactor
{
get => _sarAccelerationFactor.Value;
set => _sarAccelerationFactor.Value = value;
}
/// <summary>
/// Parabolic SAR maximum acceleration factor.
/// </summary>
public decimal SarMaxAccelerationFactor
{
get => _sarMaxAccelerationFactor.Value;
set => _sarMaxAccelerationFactor.Value = value;
}
/// <summary>
/// Hurst exponent calculation period.
/// </summary>
public int HurstPeriod
{
get => _hurstPeriod.Value;
set => _hurstPeriod.Value = value;
}
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Number of closed candles to wait before a new SAR crossover entry.
/// </summary>
public int SignalCooldownBars
{
get => _signalCooldownBars.Value;
set => _signalCooldownBars.Value = value;
}
/// <summary>
/// Initialize strategy.
/// </summary>
public ParabolicSarHurstStrategy()
{
_sarAccelerationFactor = Param(nameof(SarAccelerationFactor), 0.02m)
.SetRange(0.01m, 0.2m)
.SetDisplay("SAR Acceleration Factor", "Initial acceleration factor for Parabolic SAR", "SAR Settings")
.SetOptimize(0.01m, 0.1m, 0.01m);
_sarMaxAccelerationFactor = Param(nameof(SarMaxAccelerationFactor), 0.2m)
.SetRange(0.05m, 0.5m)
.SetDisplay("SAR Max Acceleration Factor", "Maximum acceleration factor for Parabolic SAR", "SAR Settings")
.SetOptimize(0.1m, 0.3m, 0.05m);
_hurstPeriod = Param(nameof(HurstPeriod), 100)
.SetRange(20, 200)
.SetDisplay("Hurst Period", "Period for Hurst exponent calculation", "Hurst Settings")
.SetOptimize(50, 150, 25);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_signalCooldownBars = Param(nameof(SignalCooldownBars), 4)
.SetNotNegative()
.SetDisplay("Signal Cooldown Bars", "Closed candles to wait before a new SAR crossover entry", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_parabolicSar = null!;
_hurstIndicator = null!;
_prevSarValue = 0;
_hurstValue = 0.5m;
_prevPriceAboveSar = null;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create indicators
_parabolicSar = new ParabolicSar
{
Acceleration = SarAccelerationFactor,
AccelerationMax = SarMaxAccelerationFactor
};
_hurstIndicator = new HurstExponent
{
Length = HurstPeriod
};
// Create subscription for candles
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
// Start position protection
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent)
);
// Setup chart visualization if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _parabolicSar);
DrawIndicator(area, _hurstIndicator);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
var sarValue = _parabolicSar.Process(new CandleIndicatorValue(_parabolicSar, candle));
var hurstValue = _hurstIndicator.Process(new CandleIndicatorValue(_hurstIndicator, candle));
if (!_parabolicSar.IsFormed || !_hurstIndicator.IsFormed || sarValue.IsEmpty || hurstValue.IsEmpty)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var sarPrice = sarValue.ToDecimal();
_hurstValue = hurstValue.ToDecimal();
var currentSarValue = sarPrice;
var priceAboveSar = candle.ClosePrice > sarPrice;
if (_prevPriceAboveSar is null || _prevSarValue == 0)
{
_prevSarValue = currentSarValue;
_prevPriceAboveSar = priceAboveSar;
return;
}
if (_hurstValue > 0.55m)
{
var bullishCross = !_prevPriceAboveSar.Value && priceAboveSar;
var bearishCross = _prevPriceAboveSar.Value && !priceAboveSar;
if (_cooldownRemaining == 0 && bullishCross && Position <= 0)
{
BuyMarket(Volume + (Position < 0 ? Math.Abs(Position) : 0m));
_cooldownRemaining = SignalCooldownBars;
}
else if (_cooldownRemaining == 0 && bearishCross && Position >= 0)
{
SellMarket(Volume + (Position > 0 ? Math.Abs(Position) : 0m));
_cooldownRemaining = SignalCooldownBars;
}
}
else
{
if (Position > 0)
SellMarket(Position);
else if (Position < 0)
BuyMarket(Math.Abs(Position));
}
_prevSarValue = currentSarValue;
_prevPriceAboveSar = priceAboveSar;
}
}
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, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import ParabolicSar, HurstExponent, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
class parabolic_sar_hurst_strategy(Strategy):
"""
Parabolic SAR with Hurst Filter Strategy.
Enters a position when price crosses SAR and Hurst exponent indicates a persistent trend.
"""
def __init__(self):
super(parabolic_sar_hurst_strategy, self).__init__()
self._sar_acceleration_factor = self.Param("SarAccelerationFactor", 0.02) \
.SetRange(0.01, 0.2) \
.SetDisplay("SAR Acceleration Factor", "Initial acceleration factor for Parabolic SAR", "SAR Settings") \
.SetCanOptimize(True) \
.SetOptimize(0.01, 0.1, 0.01)
self._sar_max_acceleration_factor = self.Param("SarMaxAccelerationFactor", 0.2) \
.SetRange(0.05, 0.5) \
.SetDisplay("SAR Max Acceleration Factor", "Maximum acceleration factor for Parabolic SAR", "SAR Settings") \
.SetCanOptimize(True) \
.SetOptimize(0.1, 0.3, 0.05)
self._hurst_period = self.Param("HurstPeriod", 100) \
.SetRange(20, 200) \
.SetDisplay("Hurst Period", "Period for Hurst exponent calculation", "Hurst Settings") \
.SetCanOptimize(True) \
.SetOptimize(50, 150, 25)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._signal_cooldown_bars = self.Param("SignalCooldownBars", 4) \
.SetNotNegative() \
.SetDisplay("Signal Cooldown Bars", "Closed candles to wait before a new SAR crossover entry", "General")
self._parabolic_sar = None
self._hurst_indicator = None
self._prev_sar_value = 0.0
self._hurst_value = 0.5
self._prev_price_above_sar = None
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.candle_type)]
def OnReseted(self):
super(parabolic_sar_hurst_strategy, self).OnReseted()
self._parabolic_sar = None
self._hurst_indicator = None
self._prev_sar_value = 0.0
self._hurst_value = 0.5
self._prev_price_above_sar = None
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(parabolic_sar_hurst_strategy, self).OnStarted2(time)
self._parabolic_sar = ParabolicSar()
self._parabolic_sar.Acceleration = Decimal(self._sar_acceleration_factor.Value)
self._parabolic_sar.AccelerationMax = Decimal(self._sar_max_acceleration_factor.Value)
self._hurst_indicator = HurstExponent()
self._hurst_indicator.Length = int(self._hurst_period.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.ProcessCandle).Start()
self.StartProtection(
takeProfit=Unit(2, UnitTypes.Percent),
stopLoss=Unit(1, UnitTypes.Percent)
)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._parabolic_sar)
self.DrawIndicator(area, self._hurst_indicator)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
sar_val = self._parabolic_sar.Process(CandleIndicatorValue(self._parabolic_sar, candle))
hurst_val = self._hurst_indicator.Process(CandleIndicatorValue(self._hurst_indicator, candle))
if not self._parabolic_sar.IsFormed or not self._hurst_indicator.IsFormed or sar_val.IsEmpty or hurst_val.IsEmpty:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
sar_price = float(sar_val)
self._hurst_value = float(hurst_val)
current_sar_value = sar_price
price_above_sar = float(candle.ClosePrice) > sar_price
if self._prev_price_above_sar is None or self._prev_sar_value == 0.0:
self._prev_sar_value = current_sar_value
self._prev_price_above_sar = price_above_sar
return
cooldown_bars = int(self._signal_cooldown_bars.Value)
if self._hurst_value > 0.55:
bullish_cross = not self._prev_price_above_sar and price_above_sar
bearish_cross = self._prev_price_above_sar and not price_above_sar
if self._cooldown_remaining == 0 and bullish_cross and self.Position <= 0:
vol = self.Volume
if self.Position < 0:
vol = self.Volume + Math.Abs(self.Position)
self.BuyMarket(vol)
self._cooldown_remaining = cooldown_bars
elif self._cooldown_remaining == 0 and bearish_cross and self.Position >= 0:
vol = self.Volume
if self.Position > 0:
vol = self.Volume + Math.Abs(self.Position)
self.SellMarket(vol)
self._cooldown_remaining = cooldown_bars
else:
if self.Position > 0:
self.SellMarket(self.Position)
elif self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self._prev_sar_value = current_sar_value
self._prev_price_above_sar = price_above_sar
def CreateClone(self):
return parabolic_sar_hurst_strategy()