Hull MA 傾斜平均回帰戦略
Hull MA 傾斜平均回帰戦略は、Hull MA インジケーターの極端な読みに注目し、回帰を利用します。通常レベルから大きく乖離した状態が長続きすることはほとんどありません。
インジケーターが平均から大きく乖離した後に反転し始めたときにトレードが発動します。ロングとショートの両方のセットアップに保護的なストップが含まれます。
振動を期待するスイングトレーダーに適しており、Hull MA が均衡に戻ると戦略がポジションを閉じます。開始パラメーター HullPeriod = 9。
詳細
- エントリー条件: インジケーターが平均に向かって戻るクロス。
- ロング/ショート: 両方向。
- エグジット条件: インジケーターが平均に回帰。
- ストップ: はい。
- デフォルト値:
HullPeriod= 9LookbackPeriod= 20DeviationMultiplier= 2.0mAtrPeriod= 14AtrMultiplier= 2.0mCandleType= TimeSpan.FromMinutes(5)
- フィルター:
- カテゴリ: Mean Reversion
- 方向: 両方
- インジケーター: Hull 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>
/// Hull Moving Average slope mean reversion.
/// Buys when the slope is far below its average and starts turning up, sells when it is far above
/// and starts turning down. Exits when the slope returns to its average or the ATR stop is hit.
/// </summary>
public class HullMaSlopeMeanReversionStrategy : Strategy
{
private readonly StrategyParam<int> _hullPeriod;
private readonly StrategyParam<int> _lookbackPeriod;
private readonly StrategyParam<decimal> _deviationMultiplier;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _slopeAverage;
private StandardDeviation _slopeStdDev;
private decimal? _prevHull;
private decimal? _prevSlope;
private decimal _stopPrice;
/// <summary>
/// Hull Moving Average period.
/// </summary>
public int HullPeriod
{
get => _hullPeriod.Value;
set => _hullPeriod.Value = value;
}
/// <summary>
/// Lookback period for slope statistics.
/// </summary>
public int LookbackPeriod
{
get => _lookbackPeriod.Value;
set => _lookbackPeriod.Value = value;
}
/// <summary>
/// Standard deviation multiplier for extreme slope.
/// </summary>
public decimal DeviationMultiplier
{
get => _deviationMultiplier.Value;
set => _deviationMultiplier.Value = value;
}
/// <summary>
/// ATR period for the stop.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance in ATR multiples.
/// </summary>
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="HullMaSlopeMeanReversionStrategy"/>.
/// </summary>
public HullMaSlopeMeanReversionStrategy()
{
_hullPeriod = Param(nameof(HullPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Hull Period", "Period of Hull Moving Average", "Indicators");
_lookbackPeriod = Param(nameof(LookbackPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Lookback Period", "Period for slope statistics", "Strategy");
_deviationMultiplier = Param(nameof(DeviationMultiplier), 2.0m)
.SetGreaterThanZero()
.SetDisplay("Deviation Multiplier", "Standard deviation multiplier for extreme slope", "Strategy");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period for the stop", "Risk Management");
_atrMultiplier = Param(nameof(AtrMultiplier), 2.0m)
.SetNotNegative()
.SetDisplay("ATR Multiplier", "Stop-loss distance in ATR multiples", "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();
_slopeAverage = null;
_slopeStdDev = null;
_prevHull = null;
_prevSlope = null;
_stopPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var hull = new HullMovingAverage { Length = HullPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
_slopeAverage = new SimpleMovingAverage { Length = LookbackPeriod };
_slopeStdDev = new StandardDeviation { Length = LookbackPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(hull, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, hull);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal hullValue, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevHull is not decimal prevHull)
{
_prevHull = hullValue;
return;
}
_prevHull = hullValue;
var slope = hullValue - prevHull;
var avgSlope = _slopeAverage.Process(slope, candle.ServerTime, true).ToDecimal();
var stdSlope = _slopeStdDev.Process(slope, candle.ServerTime, true).ToDecimal();
var prevSlope = _prevSlope;
_prevSlope = slope;
if (!_slopeAverage.IsFormed || !_slopeStdDev.IsFormed || prevSlope is not decimal prev)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (CheckStop(candle))
return;
var close = candle.ClosePrice;
var stopDistance = AtrMultiplier * atrValue;
// Extreme reading that has started to turn back toward the average.
if (slope < avgSlope - DeviationMultiplier * stdSlope && slope > prev && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_stopPrice = stopDistance > 0 ? close - stopDistance : 0m;
}
else if (slope > avgSlope + DeviationMultiplier * stdSlope && slope < prev && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_stopPrice = stopDistance > 0 ? close + stopDistance : 0m;
}
else if ((Position > 0 && slope >= avgSlope) || (Position < 0 && slope <= avgSlope))
{
ExitPosition();
}
}
private bool CheckStop(ICandleMessage candle)
{
if (_stopPrice == 0m)
return false;
if ((Position > 0 && candle.LowPrice <= _stopPrice) || (Position < 0 && candle.HighPrice >= _stopPrice))
{
ExitPosition();
return true;
}
return false;
}
private void ExitPosition()
{
if (Position > 0)
SellMarket(Position);
else if (Position < 0)
BuyMarket(-Position);
_stopPrice = 0m;
}
}
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
from StockSharp.Algo.Indicators import HullMovingAverage, AverageTrueRange, SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class hull_ma_slope_mean_reversion_strategy(Strategy):
"""
Hull Moving Average slope mean reversion.
Buys when the slope is far below its average and starts turning up, sells when it is far above
and starts turning down. Exits when the slope returns to its average or the ATR stop is hit.
"""
def __init__(self):
super(hull_ma_slope_mean_reversion_strategy, self).__init__()
self._hull_period = self.Param("HullPeriod", 9) \
.SetGreaterThanZero() \
.SetDisplay("Hull Period", "Period of Hull Moving Average", "Indicators")
self._lookback_period = self.Param("LookbackPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("Lookback Period", "Period for slope statistics", "Strategy")
self._deviation_multiplier = self.Param("DeviationMultiplier", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Deviation Multiplier", "Standard deviation multiplier for extreme slope", "Strategy")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("ATR Period", "ATR period for the stop", "Risk Management")
self._atr_multiplier = self.Param("AtrMultiplier", 2.0) \
.SetNotNegative() \
.SetDisplay("ATR Multiplier", "Stop-loss distance in ATR multiples", "Risk Management")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._slope_average = None
self._slope_std_dev = None
self._prev_hull = None
self._prev_slope = None
self._stop_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnReseted(self):
super(hull_ma_slope_mean_reversion_strategy, self).OnReseted()
self._slope_average = None
self._slope_std_dev = None
self._prev_hull = None
self._prev_slope = None
self._stop_price = 0.0
def OnStarted2(self, time):
super(hull_ma_slope_mean_reversion_strategy, self).OnStarted2(time)
hull = HullMovingAverage()
hull.Length = self._hull_period.Value
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
self._slope_average = SimpleMovingAverage()
self._slope_average.Length = self._lookback_period.Value
self._slope_std_dev = StandardDeviation()
self._slope_std_dev.Length = self._lookback_period.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(hull, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, hull)
self.DrawOwnTrades(area)
def _process_candle(self, candle, hull_value, atr_value):
if candle.State != CandleStates.Finished:
return
hull_value = float(hull_value)
atr_value = float(atr_value)
if self._prev_hull is None:
self._prev_hull = hull_value
return
slope = hull_value - self._prev_hull
self._prev_hull = hull_value
avg_slope = float(process_float(self._slope_average, slope, candle.ServerTime, True))
std_slope = float(process_float(self._slope_std_dev, slope, candle.ServerTime, True))
prev = self._prev_slope
self._prev_slope = slope
if not self._slope_average.IsFormed or not self._slope_std_dev.IsFormed or prev is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._check_stop(candle):
return
close = float(candle.ClosePrice)
k = float(self._deviation_multiplier.Value)
stop_distance = float(self._atr_multiplier.Value) * atr_value
# Extreme reading that has started to turn back toward the average.
if slope < avg_slope - k * std_slope and slope > prev and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._stop_price = close - stop_distance if stop_distance > 0 else 0.0
elif slope > avg_slope + k * std_slope and slope < prev and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._stop_price = close + stop_distance if stop_distance > 0 else 0.0
elif (self.Position > 0 and slope >= avg_slope) or (self.Position < 0 and slope <= avg_slope):
self._exit_position()
def _check_stop(self, candle):
if self._stop_price == 0.0:
return False
if (self.Position > 0 and float(candle.LowPrice) <= self._stop_price) or \
(self.Position < 0 and float(candle.HighPrice) >= self._stop_price):
self._exit_position()
return True
return False
def _exit_position(self):
if self.Position > 0:
self.SellMarket(self.Position)
elif self.Position < 0:
self.BuyMarket(-self.Position)
self._stop_price = 0.0
def CreateClone(self):
return hull_ma_slope_mean_reversion_strategy()