Стратегия возврата по показателю Херста
Подход использует показатель Херста, чтобы определить, когда рынок ведёт себя как среднеобращающийся. Значения ниже 0.5 подразумевают склонность цены возвращаться к среднему, что создаёт возможности для контртрендовых сделок.
Тестирование показывает среднегодичную доходность около 121%. Стратегию лучше запускать на крипторынке.
Длинная позиция открывается, когда значение Херста ниже 0.5 и цена закрывается ниже скользящей средней. Короткая позиция — когда Херст ниже 0.5 и закрытие выше средней. Позиции закрываются, когда цена возвращается к средней линии или показатель Херста поднимается выше порога.
Стратегия подходит трейдерам, предпочитающим статистические закономерности сильным трендам. Защитный стоп‑лосс оберегает от затяжных движений без возврата.
Подробности
- Условия входа:
- Long: Hurst < 0.5 && закрытие < MA
- Short: Hurst < 0.5 && закрытие > MA
- Long/Short: обе стороны.
- Условия выхода:
- Long: выход при закрытии >= MA или Hurst > 0.5
- Short: выход при закрытии <= MA или Hurst > 0.5
- Стопы: да, процентный стоп‑лосс.
- Параметры по умолчанию:
HurstPeriod = 100
AveragePeriod = 20
StopLossPercent = 2m
CandleType = TimeSpan.FromMinutes(5)
- Фильтры:
- Категория: Возврат к среднему
- Направление: Обе стороны
- Индикаторы: Показатель Херста, MA
- Стопы: Да
- Сложность: Средняя
- Таймфрейм: Внутридневной
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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>
/// Strategy that trades based on Hurst Exponent mean reversion signals.
/// Buys when Hurst exponent is below 0.5 (indicating mean reversion) and price is below average.
/// Sells when Hurst exponent is below 0.5 and price is above average.
/// </summary>
public class HurstExponentReversionStrategy : Strategy
{
private readonly StrategyParam<int> _hurstPeriod;
private readonly StrategyParam<int> _averagePeriod;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _sma;
private decimal _previousHurstValue;
private decimal _currentPrice;
/// <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 AveragePeriod
{
get => _averagePeriod.Value;
set => _averagePeriod.Value = value;
}
/// <summary>
/// Stop-loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Type of candles to use.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public HurstExponentReversionStrategy()
{
_hurstPeriod = Param(nameof(HurstPeriod), 100)
.SetDisplay("Hurst period", "Period for Hurst exponent calculation", "Strategy parameters")
.SetOptimize(50, 150, 10);
_averagePeriod = Param(nameof(AveragePeriod), 20)
.SetDisplay("Average period", "Period for price average calculation", "Strategy parameters")
.SetOptimize(10, 50, 5);
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetDisplay("Stop-loss %", "Stop-loss as percentage from entry price", "Risk management")
.SetOptimize(1m, 3m, 0.5m);
_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();
_previousHurstValue = default;
_currentPrice = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Initialize the SMA indicator
_sma = new SMA { Length = AveragePeriod };
// Create a subscription to candlesticks
var subscription = SubscribeCandles(CandleType);
// Subscribe to candle processing
subscription
.Bind(_sma, ProcessCandle)
.Start();
// Start position protection
StartProtection(
new Unit(StopLossPercent, UnitTypes.Percent),
new Unit(StopLossPercent * 1.5m, UnitTypes.Percent));
// Setup chart if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
// Skip unfinished candles
if (candle.State != CandleStates.Finished)
return;
// Check if strategy is ready to trade
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Store current price
_currentPrice = candle.ClosePrice;
// Calculate Hurst exponent (simplified approach)
// In a real implementation, you would use a proper Hurst exponent calculation
// This is a placeholder to demonstrate the concept
decimal hurstValue = CalculateSimplifiedHurst(candle);
// Store for logging
_previousHurstValue = hurstValue;
// Mean reversion market condition (Hurst < 0.5)
if (hurstValue < 0.5m)
{
// Price below average - buy signal
if (_currentPrice < smaValue && Position <= 0)
{
BuyMarket(Volume);
LogInfo($"Buy signal: Hurst={hurstValue}, Price={_currentPrice}, SMA={smaValue}");
}
// Price above average - sell signal
else if (_currentPrice > smaValue && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
LogInfo($"Sell signal: Hurst={hurstValue}, Price={_currentPrice}, SMA={smaValue}");
}
}
}
private decimal CalculateSimplifiedHurst(ICandleMessage candle)
{
// This is a simplified placeholder implementation
// A real Hurst exponent would require more complex calculations
// Simplified approach: if volatility is decreasing, return value below 0.5 (mean-reverting)
// If volatility is increasing, return value above 0.5 (trending)
// For demonstration only - in a real implementation,
// use a proper Hurst exponent calculation based on R/S analysis or similar method
Random rand = new Random((int)candle.OpenTime.Ticks);
return 0.3m + (decimal)rand.NextDouble() * 0.4m; // Returns value between 0.3 and 0.7
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Random, Math, Int32
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class hurst_exponent_reversion_strategy(Strategy):
"""
Hurst Exponent Reversion: SMA mean reversion with stop protection.
Price below SMA = buy, price above SMA = sell.
"""
def __init__(self):
super(hurst_exponent_reversion_strategy, self).__init__()
self._ma_period = self.Param("AveragePeriod", 20).SetDisplay("MA Period", "SMA period", "Indicators")
self._sl_pct = self.Param("StopLossPercent", 2.0).SetDisplay("SL %", "Stop loss percent", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(hurst_exponent_reversion_strategy, self).OnReseted()
def OnStarted2(self, time):
super(hurst_exponent_reversion_strategy, self).OnStarted2(time)
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, self._process_candle).Start()
sl_pct = self._sl_pct.Value
self.StartProtection(Unit(sl_pct, UnitTypes.Percent), Unit(sl_pct * 1.5, UnitTypes.Percent))
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_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
close = float(candle.ClosePrice)
sma = float(sma_val)
# Replicate CS CalculateSimplifiedHurst
# C# (int)long truncates to low 32 bits with sign
ticks = int(candle.OpenTime.Ticks)
ticks_masked = ticks & 0xFFFFFFFF
if ticks_masked >= 0x80000000:
ticks_masked -= 0x100000000
rand = Random(Int32(ticks_masked))
hurst_value = 0.3 + rand.NextDouble() * 0.4
if hurst_value < 0.5:
if close < sma and self.Position <= 0:
self.BuyMarket(self.Volume)
elif close > sma and self.Position >= 0:
self.SellMarket(self.Volume + Math.Abs(self.Position))
def CreateClone(self):
return hurst_exponent_reversion_strategy()