ColorMetro DeMarker Strategy
The ColorMetro DeMarker Strategy is a StockSharp implementation of the MQL5 expert advisor Exp_ColorMETRO_DeMarker.
It uses the DeMarker indicator combined with step levels to generate trading signals.
Parameters
- DeMarker Period – period of the DeMarker indicator.
- Fast Step – step size used to build the fast level (MPlus).
- Slow Step – step size used to build the slow level (MMinus).
- Candle Type – time frame of candles for analysis.
- Enable Buy Open – allow opening long positions.
- Enable Sell Open – allow opening short positions.
- Enable Buy Close – allow closing long positions.
- Enable Sell Close – allow closing short positions.
Trading Logic
- The DeMarker value is scaled to 0–100 and two dynamic levels (MPlus and MMinus) are calculated using fast and slow step sizes.
- When the previous fast level is above the previous slow level and the current fast level crosses below the slow level, the strategy buys and optionally closes short positions.
- When the previous fast level is below the previous slow level and the current fast level crosses above the slow level, the strategy sells and optionally closes long positions.
- All calculations use completed candles only.
This approach allows following trend shifts indicated by the stepped DeMarker levels.
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>
/// ColorMetro DeMarker strategy using step levels around the DeMarker indicator.
/// Long positions are opened when the fast level crosses below the slow level.
/// Short positions are opened when the fast level crosses above the slow level.
/// </summary>
public class ColorMetroDeMarkerStrategy : Strategy
{
private readonly StrategyParam<int> _deMarkerPeriod;
private readonly StrategyParam<decimal> _stepSizeFast;
private readonly StrategyParam<decimal> _stepSizeSlow;
private readonly StrategyParam<DataType> _candleType;
private decimal _fmin;
private decimal _fmax;
private decimal _smin;
private decimal _smax;
private int _ftrend;
private int _strend;
private decimal _prevMPlus;
private decimal _prevMMinus;
private bool _isFirst;
public int DeMarkerPeriod { get => _deMarkerPeriod.Value; set => _deMarkerPeriod.Value = value; }
public decimal StepSizeFast { get => _stepSizeFast.Value; set => _stepSizeFast.Value = value; }
public decimal StepSizeSlow { get => _stepSizeSlow.Value; set => _stepSizeSlow.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ColorMetroDeMarkerStrategy()
{
_deMarkerPeriod = Param(nameof(DeMarkerPeriod), 7)
.SetGreaterThanZero()
.SetDisplay("DeMarker Period", "Period of the DeMarker indicator", "Indicator");
_stepSizeFast = Param(nameof(StepSizeFast), 5m)
.SetGreaterThanZero()
.SetDisplay("Fast Step", "Fast step size for MPlus line", "Indicator");
_stepSizeSlow = Param(nameof(StepSizeSlow), 15m)
.SetGreaterThanZero()
.SetDisplay("Slow Step", "Slow step size for MMinus line", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Time frame for candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fmin = 999999m;
_fmax = -999999m;
_smin = 999999m;
_smax = -999999m;
_ftrend = 0;
_strend = 0;
_prevMPlus = 0m;
_prevMMinus = 0m;
_isFirst = true;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fmin = 999999m;
_fmax = -999999m;
_smin = 999999m;
_smax = -999999m;
_ftrend = 0;
_strend = 0;
_prevMPlus = 0m;
_prevMMinus = 0m;
_isFirst = true;
var deMarker = new DeMarker { Length = DeMarkerPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(deMarker, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, deMarker);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal deMarker)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var dm = deMarker * 100m;
var fmax0 = dm + 2m * StepSizeFast;
var fmin0 = dm - 2m * StepSizeFast;
if (dm > _fmax)
_ftrend = 1;
if (dm < _fmin)
_ftrend = -1;
if (_ftrend > 0 && fmin0 < _fmin)
fmin0 = _fmin;
if (_ftrend < 0 && fmax0 > _fmax)
fmax0 = _fmax;
var smax0 = dm + 2m * StepSizeSlow;
var smin0 = dm - 2m * StepSizeSlow;
if (dm > _smax)
_strend = 1;
if (dm < _smin)
_strend = -1;
if (_strend > 0 && smin0 < _smin)
smin0 = _smin;
if (_strend < 0 && smax0 > _smax)
smax0 = _smax;
var mPlus = _ftrend > 0 ? fmin0 + StepSizeFast : fmax0 - StepSizeFast;
var mMinus = _strend > 0 ? smin0 + StepSizeSlow : smax0 - StepSizeSlow;
if (!_isFirst)
{
if (_prevMPlus > _prevMMinus && mPlus <= mMinus)
{
// Fast crossed below slow - buy signal
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else if (_prevMPlus < _prevMMinus && mPlus >= mMinus)
{
// Fast crossed above slow - sell signal
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
}
_prevMPlus = mPlus;
_prevMMinus = mMinus;
_fmin = fmin0;
_fmax = fmax0;
_smin = smin0;
_smax = smax0;
_isFirst = false;
}
}
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
from StockSharp.Algo.Indicators import DeMarker
from StockSharp.Algo.Strategies import Strategy
class color_metro_de_marker_strategy(Strategy):
def __init__(self):
super(color_metro_de_marker_strategy, self).__init__()
self._de_marker_period = self.Param("DeMarkerPeriod", 7) \
.SetDisplay("DeMarker Period", "Period of the DeMarker indicator", "Indicator")
self._step_size_fast = self.Param("StepSizeFast", 5.0) \
.SetDisplay("Fast Step", "Fast step size for MPlus line", "Indicator")
self._step_size_slow = self.Param("StepSizeSlow", 15.0) \
.SetDisplay("Slow Step", "Slow step size for MMinus line", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Time frame for candles", "General")
self._fmin = 999999.0
self._fmax = -999999.0
self._smin = 999999.0
self._smax = -999999.0
self._ftrend = 0
self._strend = 0
self._prev_m_plus = 0.0
self._prev_m_minus = 0.0
self._is_first = True
@property
def de_marker_period(self):
return self._de_marker_period.Value
@property
def step_size_fast(self):
return self._step_size_fast.Value
@property
def step_size_slow(self):
return self._step_size_slow.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(color_metro_de_marker_strategy, self).OnReseted()
self._fmin = 999999.0
self._fmax = -999999.0
self._smin = 999999.0
self._smax = -999999.0
self._ftrend = 0
self._strend = 0
self._prev_m_plus = 0.0
self._prev_m_minus = 0.0
self._is_first = True
def OnStarted2(self, time):
super(color_metro_de_marker_strategy, self).OnStarted2(time)
self._fmin = 999999.0
self._fmax = -999999.0
self._smin = 999999.0
self._smax = -999999.0
self._ftrend = 0
self._strend = 0
self._prev_m_plus = 0.0
self._prev_m_minus = 0.0
self._is_first = True
de_marker = DeMarker()
de_marker.Length = self.de_marker_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(de_marker, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, de_marker)
self.DrawOwnTrades(area)
def process_candle(self, candle, de_marker_val):
if candle.State != CandleStates.Finished:
return
dm = float(de_marker_val) * 100.0
step_fast = float(self.step_size_fast)
step_slow = float(self.step_size_slow)
fmax0 = dm + 2.0 * step_fast
fmin0 = dm - 2.0 * step_fast
if dm > self._fmax:
self._ftrend = 1
if dm < self._fmin:
self._ftrend = -1
if self._ftrend > 0 and fmin0 < self._fmin:
fmin0 = self._fmin
if self._ftrend < 0 and fmax0 > self._fmax:
fmax0 = self._fmax
smax0 = dm + 2.0 * step_slow
smin0 = dm - 2.0 * step_slow
if dm > self._smax:
self._strend = 1
if dm < self._smin:
self._strend = -1
if self._strend > 0 and smin0 < self._smin:
smin0 = self._smin
if self._strend < 0 and smax0 > self._smax:
smax0 = self._smax
m_plus = fmin0 + step_fast if self._ftrend > 0 else fmax0 - step_fast
m_minus = smin0 + step_slow if self._strend > 0 else smax0 - step_slow
if not self._is_first:
if self._prev_m_plus > self._prev_m_minus and m_plus <= m_minus:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif self._prev_m_plus < self._prev_m_minus and m_plus >= m_minus:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._prev_m_plus = m_plus
self._prev_m_minus = m_minus
self._fmin = fmin0
self._fmax = fmax0
self._smin = smin0
self._smax = smax0
self._is_first = False
def CreateClone(self):
return color_metro_de_marker_strategy()