GitHub で見る

Color LeMan Trend Strategy

This strategy is a port of the original MQL5 expert advisor ColorLeManTrend. It uses a custom high/low based trend indicator to identify market direction.

Idea

The indicator calculates bullish and bearish lines using extreme high and low values over three different lookback periods. Exponential moving averages smooth these values. Trading decisions are based on crossovers of the bullish and bearish lines:

  • When the previous bullish line is above the bearish line and the current bullish line drops below the bearish line, a buy signal is generated.
  • When the previous bullish line is below the bearish line and the current bullish line rises above the bearish line, a sell signal is generated.
  • Optional flags control whether long or short positions may be opened or closed.

Parameters

  • CandleType – timeframe for indicator calculations.
  • Min – period for the shortest extreme calculation.
  • Midle – period for the medium extreme calculation.
  • Max – period for the longest extreme calculation.
  • PeriodEma – smoothing period for both bullish and bearish lines.
  • StopLossPoints – protective stop in points.
  • TakeProfitPoints – take profit in points.
  • AllowBuy – enable long entries.
  • AllowSell – enable short entries.
  • AllowBuyClose – allow closing long positions.
  • AllowSellClose – allow closing short positions.
  • Volume – trade volume per order.

Notes

The strategy processes only finished candles and uses market orders for all operations. Stop loss and take profit values are applied using built-in position protection.

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>
/// Strategy based on the Color LeMan Trend indicator.
/// </summary>
public class ColorLemanTrendStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _minPeriod;
	private readonly StrategyParam<int> _midPeriod;
	private readonly StrategyParam<int> _maxPeriod;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<decimal> _stopLossPoints;
	private readonly StrategyParam<decimal> _takeProfitPoints;
	private readonly StrategyParam<bool> _allowBuy;
	private readonly StrategyParam<bool> _allowSell;
	private readonly StrategyParam<bool> _allowBuyClose;
	private readonly StrategyParam<bool> _allowSellClose;

	private ExponentialMovingAverage _bullsEma;
	private ExponentialMovingAverage _bearsEma;

	private decimal? _prevBulls;
	private decimal? _prevBears;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int Min { get => _minPeriod.Value; set => _minPeriod.Value = value; }
	public int Midle { get => _midPeriod.Value; set => _midPeriod.Value = value; }
	public int Max { get => _maxPeriod.Value; set => _maxPeriod.Value = value; }
	public int PeriodEma { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public decimal StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public decimal TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
	public bool AllowBuy { get => _allowBuy.Value; set => _allowBuy.Value = value; }
	public bool AllowSell { get => _allowSell.Value; set => _allowSell.Value = value; }
	public bool AllowBuyClose { get => _allowBuyClose.Value; set => _allowBuyClose.Value = value; }
	public bool AllowSellClose { get => _allowSellClose.Value; set => _allowSellClose.Value = value; }

	/// <summary>
	/// Initializes a new instance of <see cref="ColorLemanTrendStrategy"/>.
	/// </summary>
	public ColorLemanTrendStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for indicator", "General");

		_minPeriod = Param(nameof(Min), 13)
			.SetGreaterThanZero()
			.SetDisplay("Min", "Shortest period", "Indicator")
			;

		_midPeriod = Param(nameof(Midle), 21)
			.SetGreaterThanZero()
			.SetDisplay("Midle", "Middle period", "Indicator")
			;

		_maxPeriod = Param(nameof(Max), 34)
			.SetGreaterThanZero()
			.SetDisplay("Max", "Longest period", "Indicator")
			;

		_emaPeriod = Param(nameof(PeriodEma), 3)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "Smoothing length", "Indicator")
			;

		_stopLossPoints = Param(nameof(StopLossPoints), 1000m)
			.SetDisplay("Stop Loss", "Stop loss in points", "Protection")
			;

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 2000m)
			.SetDisplay("Take Profit", "Take profit in points", "Protection")
			;

		_allowBuy = Param(nameof(AllowBuy), true)
			.SetDisplay("Allow Buy", "Enable long entries", "Trading");

		_allowSell = Param(nameof(AllowSell), true)
			.SetDisplay("Allow Sell", "Enable short entries", "Trading");

		_allowBuyClose = Param(nameof(AllowBuyClose), true)
			.SetDisplay("Allow Buy Close", "Allow closing longs", "Trading");

		_allowSellClose = Param(nameof(AllowSellClose), true)
			.SetDisplay("Allow Sell Close", "Allow closing shorts", "Trading");

	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_bullsEma?.Reset();
		_bearsEma?.Reset();
		_bullsEma = null!;
		_bearsEma = null!;
		_prevBulls = null;
		_prevBears = null;
	}

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

		_bullsEma = new EMA { Length = PeriodEma };
		_bearsEma = new EMA { Length = PeriodEma };

		var highestMin = new Highest { Length = Min };
		var highestMid = new Highest { Length = Midle };
		var highestMax = new Highest { Length = Max };
		var lowestMin = new Lowest { Length = Min };
		var lowestMid = new Lowest { Length = Midle };
		var lowestMax = new Lowest { Length = Max };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(highestMin, highestMid, highestMax, lowestMin, lowestMid, lowestMax, ProcessCandle)
			.Start();

		StartProtection(
			new Unit(TakeProfitPoints, UnitTypes.Absolute),
			new Unit(StopLossPoints, UnitTypes.Absolute),
			false);

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

	private void ProcessCandle(ICandleMessage candle,
		decimal highestMin, decimal highestMid, decimal highestMax,
		decimal lowestMin, decimal lowestMid, decimal lowestMax)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var hh = 3m * candle.HighPrice - (highestMin + highestMid + highestMax);
		var ll = (lowestMin + lowestMid + lowestMax) - 3m * candle.LowPrice;

		var bullsValue = _bullsEma.Process(hh, candle.OpenTime, true);
		var bearsValue = _bearsEma.Process(ll, candle.OpenTime, true);

		if (!bullsValue.IsFinal || !bearsValue.IsFinal || bullsValue.IsEmpty || bearsValue.IsEmpty)
			return;
		if (!_bullsEma.IsFormed || !_bearsEma.IsFormed)
			return;

		var bulls = bullsValue.ToDecimal();
		var bears = bearsValue.ToDecimal();

		bool buyOpen = false;
		bool sellOpen = false;
		bool buyClose = false;
		bool sellClose = false;

		if (_prevBulls is decimal prevUp && _prevBears is decimal prevDn)
		{
			if (prevUp > prevDn)
			{
				if (AllowBuy && bulls <= bears)
					buyOpen = true;
				if (AllowSellClose)
					sellClose = true;
			}

			if (prevUp < prevDn)
			{
				if (AllowSell && bulls >= bears)
					sellOpen = true;
				if (AllowBuyClose)
					buyClose = true;
			}
		}

		_prevBulls = bulls;
		_prevBears = bears;

		if (sellClose && Position < 0)
			BuyMarket(Volume);

		if (buyClose && Position > 0)
			SellMarket(Volume);

		if (buyOpen && Position <= 0)
			BuyMarket(Volume);

		if (sellOpen && Position >= 0)
			SellMarket(Volume);
	}
}