Hull Candles戦略
Hull Candlesは、平均価格(OHLC4)のHull Moving Averageを使用したシンプルなトレンドフォロー戦略です。HMAが上昇しクローズがSMAを上回る場合にロングポジションを建て、HMAが下落しクローズがSMAを下回る場合にショートポジションを建てます。
詳細
- データ: 価格ローソク足。
- エントリー条件:
- ロング: HMAが上昇し、クローズ > SMA。
- ショート: HMAが下落し、クローズ < SMA。
- エグジット条件: 逆シグナル。
- ストップ: なし。
- デフォルト値:
BodyLength= 10SmaLength= 1
- フィルター:
- カテゴリ: トレンドフォロー
- 方向: ロング/ショート
- インジケーター: HMA, SMA
- 複雑さ: 低
- リスクレベル: 高
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 Candles strategy.
/// A Hull moving average of BodyLength is built on the OHLC4 price and smoothed by an SMA of SmaLength. When the HMA rises and the
/// close is above that SMA the strategy goes long; when the HMA falls and the close is below it the strategy goes short, reversing
/// an opposite position.
/// </summary>
public class HullCandlesStrategy : Strategy
{
private readonly StrategyParam<int> _bodyLength;
private readonly StrategyParam<int> _smaLength;
private readonly StrategyParam<DataType> _candleType;
private HullMovingAverage _hma;
private SimpleMovingAverage _sma;
private decimal? _prevHma;
/// <summary>
/// Hull moving average length.
/// </summary>
public int BodyLength
{
get => _bodyLength.Value;
set => _bodyLength.Value = value;
}
/// <summary>
/// Length of the SMA that smooths the HMA.
/// </summary>
public int SmaLength
{
get => _smaLength.Value;
set => _smaLength.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public HullCandlesStrategy()
{
_bodyLength = Param(nameof(BodyLength), 10)
.SetGreaterThanZero()
.SetDisplay("Body Length", "Hull moving average length", "Indicators");
_smaLength = Param(nameof(SmaLength), 1)
.SetGreaterThanZero()
.SetDisplay("SMA Length", "Length of the SMA that smooths the HMA", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).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();
_prevHma = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHma = null;
_hma = new HullMovingAverage { Length = BodyLength };
_sma = new SimpleMovingAverage { Length = SmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _hma);
DrawIndicator(area, _sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var ohlc4 = (candle.OpenPrice + candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 4m;
var hmaValue = _hma.Process(new DecimalIndicatorValue(_hma, ohlc4, candle.OpenTime) { IsFinal = true });
if (!_hma.IsFormed)
return;
var hma = hmaValue.GetValue<decimal>();
var smaValue = _sma.Process(new DecimalIndicatorValue(_sma, hma, candle.OpenTime) { IsFinal = true });
var prevHma = _prevHma;
_prevHma = hma;
if (!_sma.IsFormed || prevHma is not decimal previous)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var sma = smaValue.GetValue<decimal>();
var close = candle.ClosePrice;
if (hma > previous && close > sma && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (hma < previous && close < sma && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import HullMovingAverage, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class hull_candles_strategy(Strategy):
"""
Hull Candles strategy.
A Hull moving average of BodyLength is built on the OHLC4 price and smoothed by an SMA of SmaLength. When the HMA rises and the
close is above that SMA the strategy goes long; when the HMA falls and the close is below it the strategy goes short, reversing
an opposite position.
"""
def __init__(self):
super(hull_candles_strategy, self).__init__()
self._body_length = self.Param("BodyLength", 10).SetGreaterThanZero().SetDisplay("Body Length", "Hull moving average length", "Indicators")
self._sma_length = self.Param("SmaLength", 1).SetGreaterThanZero().SetDisplay("SMA Length", "Length of the SMA that smooths the HMA", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._hma = None
self._sma = None
self._prev_hma = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(hull_candles_strategy, self).OnReseted()
self._prev_hma = None
def OnStarted2(self, time):
super(hull_candles_strategy, self).OnStarted2(time)
self._prev_hma = None
self._hma = HullMovingAverage()
self._hma.Length = self._body_length.Value
self._sma = SimpleMovingAverage()
self._sma.Length = self._sma_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._hma)
self.DrawIndicator(area, self._sma)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
ohlc4 = (candle.OpenPrice + candle.HighPrice + candle.LowPrice + candle.ClosePrice) / Decimal(4)
hma_value = process_float(self._hma, ohlc4, candle.OpenTime, True)
if not self._hma.IsFormed:
return
hma = float(hma_value)
sma_value = process_float(self._sma, Decimal(hma), candle.OpenTime, True)
prev_hma = self._prev_hma
self._prev_hma = hma
if not self._sma.IsFormed or prev_hma is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
sma = float(sma_value)
close = float(candle.ClosePrice)
if hma > prev_hma and close > sma and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif hma < prev_hma and close < sma and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return hull_candles_strategy()