GitHub で見る

Exp HLRSign Strategy

This strategy implements the HLRSign indicator logic in StockSharp. It opens and closes positions when the High-Low Ratio (HLR) crosses predefined levels.

How It Works

  • Calculates Donchian Channel values over a configurable range.
  • Computes the HLR value as a percentage position of the mid-price within the channel.
  • Generates buy or sell signals when the HLR crosses the upper or lower thresholds depending on the selected mode:
    • ModeIn – buy on crossing above the upper level and sell on crossing below the lower level.
    • ModeOut – sell on crossing below the upper level and buy on crossing above the lower level.
  • Allows enabling or disabling opening and closing of long and short positions separately.

Parameters

Name Description
Mode Indicator operation mode (ModeIn or ModeOut).
Range Lookback period for highest and lowest prices.
UpLevel Upper threshold in percent for HLR.
DnLevel Lower threshold in percent for HLR.
CandleType Timeframe of candles used for calculations.
BuyOpen Allow opening long positions.
SellOpen Allow opening short positions.
BuyClose Allow closing long positions.
SellClose Allow closing short positions.

Notes

  • The strategy uses the high-level API with DonchianChannels indicator.
  • It processes only finished candles and checks position permissions before trading.
  • No stop-loss or take-profit levels are defined; position protection can be added manually.
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;
	}
}