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);
}
}
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 ExponentialMovingAverage, Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class color_leman_trend_strategy(Strategy):
def __init__(self):
super(color_leman_trend_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for indicator", "General")
self._min_period = self.Param("Min", 13) \
.SetDisplay("Min", "Shortest period", "Indicator")
self._mid_period = self.Param("Midle", 21) \
.SetDisplay("Midle", "Middle period", "Indicator")
self._max_period = self.Param("Max", 34) \
.SetDisplay("Max", "Longest period", "Indicator")
self._ema_period = self.Param("PeriodEma", 3) \
.SetDisplay("EMA Period", "Smoothing length", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 1000.0) \
.SetDisplay("Stop Loss", "Stop loss in points", "Protection")
self._take_profit_points = self.Param("TakeProfitPoints", 2000.0) \
.SetDisplay("Take Profit", "Take profit in points", "Protection")
self._allow_buy = self.Param("AllowBuy", True) \
.SetDisplay("Allow Buy", "Enable long entries", "Trading")
self._allow_sell = self.Param("AllowSell", True) \
.SetDisplay("Allow Sell", "Enable short entries", "Trading")
self._allow_buy_close = self.Param("AllowBuyClose", True) \
.SetDisplay("Allow Buy Close", "Allow closing longs", "Trading")
self._allow_sell_close = self.Param("AllowSellClose", True) \
.SetDisplay("Allow Sell Close", "Allow closing shorts", "Trading")
self._bulls_ema = None
self._bears_ema = None
self._prev_bulls = None
self._prev_bears = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def Min(self):
return self._min_period.Value
@Min.setter
def Min(self, value):
self._min_period.Value = value
@property
def Midle(self):
return self._mid_period.Value
@Midle.setter
def Midle(self, value):
self._mid_period.Value = value
@property
def Max(self):
return self._max_period.Value
@Max.setter
def Max(self, value):
self._max_period.Value = value
@property
def PeriodEma(self):
return self._ema_period.Value
@PeriodEma.setter
def PeriodEma(self, value):
self._ema_period.Value = value
@property
def StopLossPoints(self):
return self._stop_loss_points.Value
@StopLossPoints.setter
def StopLossPoints(self, value):
self._stop_loss_points.Value = value
@property
def TakeProfitPoints(self):
return self._take_profit_points.Value
@TakeProfitPoints.setter
def TakeProfitPoints(self, value):
self._take_profit_points.Value = value
@property
def AllowBuy(self):
return self._allow_buy.Value
@AllowBuy.setter
def AllowBuy(self, value):
self._allow_buy.Value = value
@property
def AllowSell(self):
return self._allow_sell.Value
@AllowSell.setter
def AllowSell(self, value):
self._allow_sell.Value = value
@property
def AllowBuyClose(self):
return self._allow_buy_close.Value
@AllowBuyClose.setter
def AllowBuyClose(self, value):
self._allow_buy_close.Value = value
@property
def AllowSellClose(self):
return self._allow_sell_close.Value
@AllowSellClose.setter
def AllowSellClose(self, value):
self._allow_sell_close.Value = value
def OnStarted2(self, time):
super(color_leman_trend_strategy, self).OnStarted2(time)
self._bulls_ema = ExponentialMovingAverage()
self._bulls_ema.Length = self.PeriodEma
self._bears_ema = ExponentialMovingAverage()
self._bears_ema.Length = self.PeriodEma
highest_min = Highest()
highest_min.Length = self.Min
highest_mid = Highest()
highest_mid.Length = self.Midle
highest_max = Highest()
highest_max.Length = self.Max
lowest_min = Lowest()
lowest_min.Length = self.Min
lowest_mid = Lowest()
lowest_mid.Length = self.Midle
lowest_max = Lowest()
lowest_max.Length = self.Max
self.SubscribeCandles(self.CandleType) \
.Bind(highest_min, highest_mid, highest_max, lowest_min, lowest_mid, lowest_max, self.ProcessCandle) \
.Start()
self.StartProtection(
takeProfit=Unit(self.TakeProfitPoints, UnitTypes.Absolute),
stopLoss=Unit(self.StopLossPoints, UnitTypes.Absolute)
)
def ProcessCandle(self, candle, h_min, h_mid, h_max, l_min, l_mid, l_max):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
hh = 3.0 * high - (float(h_min) + float(h_mid) + float(h_max))
ll = (float(l_min) + float(l_mid) + float(l_max)) - 3.0 * low
t = candle.OpenTime
bulls_result = process_float(self._bulls_ema, hh, t, True)
bears_result = process_float(self._bears_ema, ll, t, True)
if bulls_result.IsEmpty or bears_result.IsEmpty:
return
if not self._bulls_ema.IsFormed or not self._bears_ema.IsFormed:
return
bulls = float(bulls_result)
bears = float(bears_result)
buy_open = False
sell_open = False
buy_close = False
sell_close = False
if self._prev_bulls is not None and self._prev_bears is not None:
if self._prev_bulls > self._prev_bears:
if self.AllowBuy and bulls <= bears:
buy_open = True
if self.AllowSellClose:
sell_close = True
if self._prev_bulls < self._prev_bears:
if self.AllowSell and bulls >= bears:
sell_open = True
if self.AllowBuyClose:
buy_close = True
self._prev_bulls = bulls
self._prev_bears = bears
if sell_close and self.Position < 0:
self.BuyMarket()
if buy_close and self.Position > 0:
self.SellMarket()
if buy_open and self.Position <= 0:
self.BuyMarket()
if sell_open and self.Position >= 0:
self.SellMarket()
def OnReseted(self):
super(color_leman_trend_strategy, self).OnReseted()
self._bulls_ema = None
self._bears_ema = None
self._prev_bulls = None
self._prev_bears = None
def CreateClone(self):
return color_leman_trend_strategy()