Ver en GitHub

Estrategia Hull Trend OSMA

Esta estrategia es una conversión del asesor experto MetaTrader "Exp_HullTrendOSMA".

Descripción general

La estrategia utiliza el indicador Hull Trend OSMA, que calcula una Hull Moving Average y una versión suavizada de la misma. El valor del oscilador es la diferencia entre estas dos series. Cuando el oscilador sube durante dos velas completadas consecutivas, la estrategia abre una posición larga. Cuando el oscilador baja durante dos velas completadas consecutivas, la estrategia abre una posición corta. Las posiciones opuestas se cierran en cada señal.

Parámetros

  • Hull Period – período para la Hull Moving Average.
  • Signal Period – período de la media móvil de suavizado aplicada al oscilador.
  • Take Profit – distancia para órdenes de take profit en unidades de precio.
  • Stop Loss – distancia para órdenes de stop loss en unidades de precio.
  • Candle Type – marco temporal de las velas utilizadas para los cálculos (por defecto 8 horas).

Notas

  • Utiliza la API de alto nivel de StockSharp con suscripción automática a velas.
  • Las entradas y salidas se ejecutan con órdenes de mercado.
  • La protección de stop loss y take profit se inicializa una vez al iniciar la estrategia.
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;
	}
}