Hurst Exponent トレンド戦略
このシステムはHurst Exponentを使用して、市場がトレンド行動を示しているかどうかを判断します。閾値を超える値は持続性を示し、閾値を下回る値はノイズまたは平均回帰を示唆します。移動平均が追加の方向確認を提供します。
テストによると、平均年間リターンは約40%です。暗号通貨市場で最も高いパフォーマンスを発揮します。
Hurst Exponentが閾値より大きく、価格が移動平均を上回って終値をつけると、戦略は買います。Hurst Exponentが高く、価格が平均を下回って終値をつけると、空売りします。Hurst Exponentが閾値を下回ると、不安定な市場での取引を避けるために既存のポジションが決済されます。
このアプローチは、エントリー前にトレンドが存在するという客観的な確認を求めるトレーダーに機能します。トレンドフィルターとストップロスの組み合わせにより、偽シグナルのリスクを管理するのに役立ちます。
詳細
- エントリー条件:
- ロング: Hurst > 閾値 && 終値 > MA
- ショート: Hurst > 閾値 && 終値 < MA
- ロング/ショート: 両方。
- エグジット条件:
- ロング: 終値 < MA または Hurst < 閾値 の時に決済
- ショート: 終値 > MA または Hurst < 閾値 の時に決済
- ストップ: あり、パーセンテージストップロス。
- デフォルト値:
HurstPeriod= 100MaPeriod= 20HurstThreshold= 0.55StopLossPercent= 2 MAはMaPeriod本の単純移動平均です。ストップはエントリー価格の固定StopLossPercentで、ローソク足の間も監視されます。0で無効になります。 保有ポジションと逆方向のエントリーシグナルはドテンになります。CandleType= TimeSpan.FromMinutes(5)
- フィルター:
- カテゴリ: トレンド
- 方向: 両方
- インジケーター: Hurst Exponent, MA
- ストップ: あり
- 複雑さ: 中級
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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 Trend strategy.
/// A HurstPeriod Hurst exponent above HurstThreshold marks a trending market. Then a close above the MaPeriod simple moving average goes long
/// and a close below it goes short, reversing an opposite position. A long closes once the close is below the average or the exponent
/// drops below the threshold, a short mirrors it, and a percent stop limits the loss.
/// </summary>
public class HurstExponentTrendStrategy : Strategy
{
private readonly StrategyParam<int> _hurstPeriod;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<decimal> _hurstThreshold;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
/// <summary>
/// Period of the Hurst exponent.
/// </summary>
public int HurstPeriod
{
get => _hurstPeriod.Value;
set => _hurstPeriod.Value = value;
}
/// <summary>
/// Period of the simple moving average.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Hurst exponent level of a trending market.
/// </summary>
public decimal HurstThreshold
{
get => _hurstThreshold.Value;
set => _hurstThreshold.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 HurstExponentTrendStrategy()
{
_hurstPeriod = Param(nameof(HurstPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("Hurst Period", "Period of the Hurst exponent", "Indicators");
_maPeriod = Param(nameof(MaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Period of the simple moving average", "Indicators");
_hurstThreshold = Param(nameof(HurstThreshold), 0.55m)
.SetDisplay("Hurst Threshold", "Hurst exponent level of a trending market", "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 hurst = new HurstExponent { Length = HurstPeriod };
var sma = new SimpleMovingAverage { Length = MaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(hurst, 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);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, hurst);
}
}
}
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 hurstValue, IIndicatorValue smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!hurstValue.IsFormed || !smaValue.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var hurst = hurstValue.GetValue<decimal>();
var sma = smaValue.GetValue<decimal>();
var close = candle.ClosePrice;
var trending = hurst > HurstThreshold;
if (trending && close > sma && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (trending && close < sma && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && (close < sma || hurst < HurstThreshold))
SellMarket(Position);
else if (Position < 0 && (close > sma || hurst < HurstThreshold))
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 HurstExponent, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class hurst_exponent_trend_strategy(Strategy):
"""
Hurst Exponent Trend strategy.
A HurstPeriod Hurst exponent above HurstThreshold marks a trending market. Then a close above the MaPeriod simple moving average goes long
and a close below it goes short, reversing an opposite position. A long closes once the close is below the average or the exponent
drops below the threshold, a short mirrors it, and a percent stop limits the loss.
"""
def __init__(self):
super(hurst_exponent_trend_strategy, self).__init__()
self._hurst_period = self.Param("HurstPeriod", 100).SetGreaterThanZero().SetDisplay("Hurst Period", "Period of the Hurst exponent", "Indicators")
self._ma_period = self.Param("MaPeriod", 20).SetGreaterThanZero().SetDisplay("MA Period", "Period of the simple moving average", "Indicators")
self._hurst_threshold = self.Param("HurstThreshold", 0.55).SetDisplay("Hurst Threshold", "Hurst exponent level of a trending market", "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(hurst_exponent_trend_strategy, self).OnStarted2(time)
hurst = HurstExponent()
hurst.Length = self._hurst_period.Value
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(hurst, 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)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, hurst)
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, hurst_value, sma_value):
if candle.State != CandleStates.Finished:
return
if not hurst_value.IsFormed or not sma_value.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
hurst = hurst_value.GetValue[Decimal](None)
sma = sma_value.GetValue[Decimal](None)
close = candle.ClosePrice
threshold = Decimal(self._hurst_threshold.Value)
trending = hurst > threshold
if trending and close > sma and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif trending and close < sma and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and (close < sma or hurst < threshold):
self.SellMarket(self.Position)
elif self.Position < 0 and (close > sma or hurst < threshold):
self.BuyMarket(-self.Position)
def CreateClone(self):
return hurst_exponent_trend_strategy()