ホーム
/
戦略のサンプル
GitHub で見る
Hull Ma Volume Strategy
Strategy that uses Hull Moving Average for trend direction and volume confirmation for trade entries.
Testing indicates an average annual return of about 169%. It performs best in the crypto market.
The Hull moving average smooths out noise, and rising volume confirms conviction. Entries occur when price moves with the Hull slope backed by a volume surge.
This method targets traders watching for strong participation on breakouts. ATR-based stops defend against sudden reversals.
Details
Entry Criteria :
Long: HullMA(t) > HullMA(t-1) && Volume > AvgVolume * VolumeMultiplier
Short: HullMA(t) < HullMA(t-1) && Volume > AvgVolume * VolumeMultiplier
Long/Short : Both
Exit Criteria :
Long: HullMA(t) < HullMA(t-1)
Short: HullMA(t) > HullMA(t-1)
Stops : StopLossAtr ATR from entry
Default Values :
HullPeriod = 9
VolumePeriod = 20
VolumeMultiplier = 1.5m
StopLossAtr = 2.0m
AtrPeriod = 14
CandleType = TimeSpan.FromMinutes(5).TimeFrame()
Filters :
Category: Breakout
Direction: Both
Indicators: Hull MA, Moving Average, Volume
Stops: Yes
Complexity: Intermediate
Timeframe: Mid-term
Seasonality: No
Neural Networks: No
Divergence: No
Risk Level: Medium
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>
/// Strategy that uses Hull Moving Average for trend direction.
/// Enters when HMA direction changes.
/// </summary>
public class HullMaVolumeStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _hullPeriod;
private readonly StrategyParam<int> _cooldownBars;
private decimal _prevHullValue;
private int _cooldown;
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Hull Moving Average period.
/// </summary>
public int HullPeriod
{
get => _hullPeriod.Value;
set => _hullPeriod.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Strategy constructor.
/// </summary>
public HullMaVolumeStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_hullPeriod = Param(nameof(HullPeriod), 9)
.SetRange(5, 30)
.SetDisplay("Hull MA Period", "Period of the Hull Moving Average", "Indicators");
_cooldownBars = Param(nameof(CooldownBars), 100)
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
.SetRange(5, 500);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHullValue = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var hullMa = new HullMovingAverage { Length = HullPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(hullMa, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, hullMa);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal hullValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_prevHullValue == 0)
{
_prevHullValue = hullValue;
return;
}
var rising = hullValue > _prevHullValue;
var falling = hullValue < _prevHullValue;
_prevHullValue = hullValue;
if (_cooldown > 0)
{
_cooldown--;
return;
}
// Entry: HMA turning up
if (rising && Position == 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
// Entry: HMA turning down
else if (falling && Position == 0)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit long: HMA turns down
if (Position > 0 && falling)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit short: HMA turns up
else if (Position < 0 && rising)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
}
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
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class hull_ma_volume_strategy(Strategy):
"""
Strategy that uses Hull Moving Average for trend direction.
Enters when HMA direction changes.
"""
def __init__(self):
super(hull_ma_volume_strategy, self).__init__()
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._hull_period = self.Param("HullPeriod", 9) \
.SetRange(5, 30) \
.SetDisplay("Hull MA Period", "Period of the Hull Moving Average", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 100) \
.SetDisplay("Cooldown Bars", "Bars between trades", "General") \
.SetRange(5, 500)
self._prev_hull_value = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
@property
def hull_period(self):
return self._hull_period.Value
@hull_period.setter
def hull_period(self, value):
self._hull_period.Value = value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
@cooldown_bars.setter
def cooldown_bars(self, value):
self._cooldown_bars.Value = value
def OnStarted2(self, time):
super(hull_ma_volume_strategy, self).OnStarted2(time)
self._prev_hull_value = 0.0
self._cooldown = 0
hull_ma = HullMovingAverage()
hull_ma.Length = self.hull_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(hull_ma, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, hull_ma)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, hull_value):
if candle.State != CandleStates.Finished:
return
hv = float(hull_value)
if self._prev_hull_value == 0:
self._prev_hull_value = hv
return
rising = hv > self._prev_hull_value
falling = hv < self._prev_hull_value
self._prev_hull_value = hv
if self._cooldown > 0:
self._cooldown -= 1
return
# Entry: HMA turning up
if rising and self.Position == 0:
self.BuyMarket()
self._cooldown = self.cooldown_bars
# Entry: HMA turning down
elif falling and self.Position == 0:
self.SellMarket()
self._cooldown = self.cooldown_bars
# Exit long: HMA turns down
if self.Position > 0 and falling:
self.SellMarket()
self._cooldown = self.cooldown_bars
# Exit short: HMA turns up
elif self.Position < 0 and rising:
self.BuyMarket()
self._cooldown = self.cooldown_bars
def OnReseted(self):
super(hull_ma_volume_strategy, self).OnReseted()
self._prev_hull_value = 0.0
self._cooldown = 0
def CreateClone(self):
return hull_ma_volume_strategy()