Hull Trend OSMA 策略
该策略是 MetaTrader "Exp_HullTrendOSMA" 专家的转换版本。
概述
策略使用 Hull Trend OSMA 指标。该指标计算 Hull 移动平均线以及其平滑版本,两者的差值即为振荡器。当振荡器在连续两个已完成的 K 线中上升时,策略开多仓;当振荡器在连续两个已完成的 K 线中下降时,策略开空仓。每次信号出现时都会平掉反向仓位。
参数
- Hull Period – Hull 移动平均线的周期。
- Signal Period – 应用于振荡器的平滑移动平均线周期。
- Take Profit – 以价格单位表示的止盈距离。
- Stop Loss – 以价格单位表示的止损距离。
- Candle Type – 计算所使用的 K 线时间框架(默认 8 小时)。
说明
- 使用 StockSharp 高级 API,自动订阅 K 线。
- 入场和平仓均使用市价单执行。
- 止损和止盈保护在策略启动时初始化一次。
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 based on Hull Trend OSMA indicator.
/// Opens long when oscillator rises twice in a row, short when it falls twice in a row.
/// </summary>
public class HullTrendOsmaStrategy : Strategy
{
private readonly StrategyParam<int> _hullPeriod;
private readonly StrategyParam<int> _signalPeriod;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prev1;
private decimal? _prev2;
public int HullPeriod { get => _hullPeriod.Value; set => _hullPeriod.Value = value; }
public int SignalPeriod { get => _signalPeriod.Value; set => _signalPeriod.Value = value; }
public decimal TakeProfitPct { get => _takeProfitPct.Value; set => _takeProfitPct.Value = value; }
public decimal StopLossPct { get => _stopLossPct.Value; set => _stopLossPct.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public HullTrendOsmaStrategy()
{
_hullPeriod = Param(nameof(HullPeriod), 20)
.SetDisplay("Hull Period", "Period for Hull Moving Average", "Indicators");
_signalPeriod = Param(nameof(SignalPeriod), 5)
.SetDisplay("Signal Period", "Period for signal SMA", "Indicators");
_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).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();
_prev1 = null;
_prev2 = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var hma = new HullMovingAverage { Length = HullPeriod };
var signal = new ExponentialMovingAverage { Length = SignalPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(hma, signal, ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(TakeProfitPct, UnitTypes.Percent),
stopLoss: new Unit(StopLossPct, UnitTypes.Percent),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, hma);
DrawIndicator(area, signal);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal hmaValue, decimal signalValue)
{
if (candle.State != CandleStates.Finished)
return;
var osma = hmaValue - signalValue;
if (_prev1 is null || _prev2 is null)
{
_prev2 = _prev1;
_prev1 = osma;
return;
}
var prev = _prev1.Value;
var prevPrev = _prev2.Value;
var isRising = prev > prevPrev && osma >= prev;
var isFalling = prev < prevPrev && osma <= prev;
if (isRising && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (isFalling && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_prev2 = _prev1;
_prev1 = osma;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import HullMovingAverage, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class hull_trend_osma_strategy(Strategy):
def __init__(self):
super(hull_trend_osma_strategy, self).__init__()
self._hull_period = self.Param("HullPeriod", 20) \
.SetDisplay("Hull Period", "Period for Hull Moving Average", "Indicators")
self._signal_period = self.Param("SignalPeriod", 5) \
.SetDisplay("Signal Period", "Period for signal SMA", "Indicators")
self._take_profit_pct = self.Param("TakeProfitPct", 3.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._stop_loss_pct = self.Param("StopLossPct", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev1 = None
self._prev2 = None
@property
def HullPeriod(self):
return self._hull_period.Value
@HullPeriod.setter
def HullPeriod(self, value):
self._hull_period.Value = value
@property
def SignalPeriod(self):
return self._signal_period.Value
@SignalPeriod.setter
def SignalPeriod(self, value):
self._signal_period.Value = value
@property
def TakeProfitPct(self):
return self._take_profit_pct.Value
@TakeProfitPct.setter
def TakeProfitPct(self, value):
self._take_profit_pct.Value = value
@property
def StopLossPct(self):
return self._stop_loss_pct.Value
@StopLossPct.setter
def StopLossPct(self, value):
self._stop_loss_pct.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(hull_trend_osma_strategy, self).OnStarted2(time)
self._prev1 = None
self._prev2 = None
hma = HullMovingAverage()
hma.Length = self.HullPeriod
signal = ExponentialMovingAverage()
signal.Length = self.SignalPeriod
self.SubscribeCandles(self.CandleType) \
.Bind(hma, signal, self.ProcessCandle) \
.Start()
self.StartProtection(
takeProfit=Unit(float(self.TakeProfitPct), UnitTypes.Percent),
stopLoss=Unit(float(self.StopLossPct), UnitTypes.Percent),
useMarketOrders=True
)
def ProcessCandle(self, candle, hma_val, signal_val):
if candle.State != CandleStates.Finished:
return
hma_f = float(hma_val)
signal_f = float(signal_val)
osma = hma_f - signal_f
if self._prev1 is None or self._prev2 is None:
self._prev2 = self._prev1
self._prev1 = osma
return
prev = self._prev1
prev_prev = self._prev2
is_rising = prev > prev_prev and osma >= prev
is_falling = prev < prev_prev and osma <= prev
if is_rising and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif is_falling and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev2 = self._prev1
self._prev1 = osma
def OnReseted(self):
super(hull_trend_osma_strategy, self).OnReseted()
self._prev1 = None
self._prev2 = None
def CreateClone(self):
return hull_trend_osma_strategy()