Ver no GitHub

Estratégia Exp HLRSign

Esta estratégia implementa a lógica do indicador HLRSign no StockSharp. Abre e fecha posições quando a Relação Alto-Baixo (HLR) cruza níveis predefinidos.

Como Funciona

  • Calcula os valores do Canal Donchian sobre um intervalo configurável.
  • Calcula o valor HLR como a posição percentual do preço médio dentro do canal.
  • Gera sinais de compra ou venda quando o HLR cruza os limites superior ou inferior dependendo do modo selecionado:
    • ModeIn – comprar ao cruzar acima do nível superior e vender ao cruzar abaixo do nível inferior.
    • ModeOut – vender ao cruzar abaixo do nível superior e comprar ao cruzar acima do nível inferior.
  • Permite habilitar ou desabilitar a abertura e o fechamento de posições compradas e vendidas separadamente.

Parâmetros

Nome Descrição
Mode Modo de operação do indicador (ModeIn ou ModeOut).
Range Período de retrocesso para preços máximos e mínimos.
UpLevel Limite superior em percentual para o HLR.
DnLevel Limite inferior em percentual para o HLR.
CandleType Período das velas usadas para cálculos.
BuyOpen Permitir abertura de posições compradas.
SellOpen Permitir abertura de posições vendidas.
BuyClose Permitir fechamento de posições compradas.
SellClose Permitir fechamento de posições vendidas.

Notas

  • A estratégia usa a API de alto nível com o indicador DonchianChannels.
  • Processa apenas velas fechadas e verifica permissões de posição antes de negociar.
  • Nenhum nível de stop-loss ou take-profit está definido; a proteção de posição pode ser adicionada manualmente.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;


/// <summary>
/// HLRSign based strategy.
/// Opens and closes positions based on HLR level crossings.
/// </summary>
public class ExpHlrSignStrategy : Strategy
{
	public enum AlgMethods
	{
		ModeIn,
		ModeOut,
	}

	private readonly StrategyParam<AlgMethods> _mode;
	private readonly StrategyParam<int> _range;
	private readonly StrategyParam<decimal> _upLevel;
	private readonly StrategyParam<decimal> _dnLevel;
	private readonly StrategyParam<int> _cooldownBars;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<bool> _buyOpen;
	private readonly StrategyParam<bool> _sellOpen;
	private readonly StrategyParam<bool> _buyClose;
	private readonly StrategyParam<bool> _sellClose;

	private decimal _previousHlr;
	private bool _isFirst = true;
	private int _barsSinceTrade;

	/// <summary>
	/// Indicator mode.
	/// </summary>
	public AlgMethods Mode
	{
		get => _mode.Value;
		set => _mode.Value = value;
	}

	/// <summary>
	/// Lookback range for highest and lowest values.
	/// </summary>
	public int Range
	{
		get => _range.Value;
		set => _range.Value = value;
	}

	/// <summary>
	/// Upper level in percent for HLR crossing.
	/// </summary>
	public decimal UpLevel
	{
		get => _upLevel.Value;
		set => _upLevel.Value = value;
	}

	/// <summary>
	/// Lower level in percent for HLR crossing.
	/// </summary>
	public decimal DnLevel
	{
		get => _dnLevel.Value;
		set => _dnLevel.Value = value;
	}

	/// <summary>
	/// Bars to wait after a completed trade.
	/// </summary>
	public int CooldownBars
	{
		get => _cooldownBars.Value;
		set => _cooldownBars.Value = value;
	}

	/// <summary>
	/// Type of candles to use.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Allow opening long positions.
	/// </summary>
	public bool BuyOpen
	{
		get => _buyOpen.Value;
		set => _buyOpen.Value = value;
	}

	/// <summary>
	/// Allow opening short positions.
	/// </summary>
	public bool SellOpen
	{
		get => _sellOpen.Value;
		set => _sellOpen.Value = value;
	}

	/// <summary>
	/// Allow closing long positions on sell signal.
	/// </summary>
	public bool BuyClose
	{
		get => _buyClose.Value;
		set => _buyClose.Value = value;
	}

	/// <summary>
	/// Allow closing short positions on buy signal.
	/// </summary>
	public bool SellClose
	{
		get => _sellClose.Value;
		set => _sellClose.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="ExpHlrSignStrategy"/>.
	/// </summary>
	public ExpHlrSignStrategy()
	{
		_mode = Param(nameof(Mode), AlgMethods.ModeIn)
			.SetDisplay("Mode", "Indicator operation mode", "General");

		_range = Param(nameof(Range), 40)
			.SetDisplay("Range", "Lookback period for HLR", "Indicator")
			.SetOptimize(20, 80, 10)
			;

		_upLevel = Param(nameof(UpLevel), 80m)
			.SetDisplay("Up Level", "Upper level for HLR", "Indicator")
			.SetOptimize(60m, 90m, 5m)
			;

		_dnLevel = Param(nameof(DnLevel), 20m)
			.SetDisplay("Down Level", "Lower level for HLR", "Indicator")
			.SetOptimize(10m, 40m, 5m)
			;

		_cooldownBars = Param(nameof(CooldownBars), 1)
			.SetDisplay("Cooldown Bars", "Bars to wait after a completed trade", "Trading");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for analysis", "General");

		_buyOpen = Param(nameof(BuyOpen), true)
			.SetDisplay("Buy Open", "Allow opening long positions", "Trading");

		_sellOpen = Param(nameof(SellOpen), true)
			.SetDisplay("Sell Open", "Allow opening short positions", "Trading");

		_buyClose = Param(nameof(BuyClose), true)
			.SetDisplay("Buy Close", "Allow closing long positions", "Trading");

		_sellClose = Param(nameof(SellClose), true)
			.SetDisplay("Sell Close", "Allow closing short positions", "Trading");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_previousHlr = 0;
		_isFirst = true;
		_barsSinceTrade = CooldownBars;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var donchian = new DonchianChannels { Length = Range };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(donchian, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, donchian);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue value)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (_barsSinceTrade < CooldownBars)
			_barsSinceTrade++;

		var donchian = (DonchianChannelsValue)value;
		var upper = donchian.UpperBand;
		var lower = donchian.LowerBand;

		if (upper == null || lower == null)
			return;

		var mid = (candle.HighPrice + candle.LowPrice) / 2m;
		var range = (decimal)(upper - lower);
		var hlr = range != 0m ? 100m * (mid - lower.Value) / range : 0m;

		bool buySignal = false;
		bool sellSignal = false;

		if (_isFirst)
		{
			_previousHlr = hlr;
			_isFirst = false;
			return;
		}

		if (Mode == AlgMethods.ModeIn)
		{
			if (hlr > UpLevel && _previousHlr <= UpLevel)
				buySignal = true;
			if (hlr < DnLevel && _previousHlr >= DnLevel)
				sellSignal = true;
		}
		else
		{
			if (hlr < UpLevel && _previousHlr >= UpLevel)
				sellSignal = true;
			if (hlr > DnLevel && _previousHlr <= DnLevel)
				buySignal = true;
		}

		if (_barsSinceTrade >= CooldownBars && buySignal)
		{
			if (BuyOpen && Position <= 0)
			{
				BuyMarket(Volume + Math.Abs(Position));
				_barsSinceTrade = 0;
			}
		}

		if (_barsSinceTrade >= CooldownBars && sellSignal)
		{
			if (SellOpen && Position >= 0)
			{
				SellMarket(Volume + Math.Abs(Position));
				_barsSinceTrade = 0;
			}
		}

		_previousHlr = hlr;
	}
}