The Demarker Martingale Strategy recreates the MetaTrader expert advisor "Demarker Martingale" using the StockSharp high-level API. The system combines a medium-term DeMarker oscillator signal with a higher timeframe MACD trend filter. Entries are followed by martingale-style position sizing, hard stop-loss and take-profit levels, break-even protection, and a trailing stop that mimics the original expert's money management toolkit.
Core Trading Logic
Data feeds – the strategy subscribes to a user-defined trading timeframe (default 15-minute candles) for signal generation and a higher timeframe series (default monthly candles) to calculate the MACD filter.
DeMarker trigger – when the DeMarker value exceeds the neutral DemarkerThreshold (default 0.5) and the recent price action forms a bullish overlap (Low[2] < High[1]), a long setup is considered. Conversely, a bearish overlap with DeMarker below the threshold prepares a short.
MACD confirmation – the higher timeframe MACD must agree with the direction. A bullish signal requires the MACD main line to be above its signal line, while a bearish signal expects the opposite relationship. This reproduces the MQL expert's monthly MACD filter.
Order execution – valid signals place market orders with the current martingale-adjusted volume. Only one directional position is maintained at a time.
Position monitoring – while a position is open, the strategy evaluates every finished candle to detect stop-loss, take-profit, break-even, or trailing-stop triggers. Breach events close the full position via market orders.
Money Management
Initial sizing – orders start with InitialVolume aligned to the instrument's VolumeStep and bounded by VolumeMin/VolumeMax.
Martingale escalation – after a losing trade the next volume is either multiplied by MartingaleMultiplier (DoubleLotSize = true) or incremented by LotIncrement. Profitable trades reset the ladder to the base volume. The escalation depth is limited by MaxMartingaleSteps to prevent runaway exposure.
Stop-loss & take-profit – distances are expressed in MetaTrader-style pips. The pip size automatically adapts to 3/5-digit Forex quotes, matching the original ticksize logic.
Break-even – once unrealised profit reaches BreakEvenTriggerPips, the stop-loss is shifted to entry plus BreakEvenOffsetPips (long) or minus the offset (short).
Trailing stop – profits beyond TrailingStopPips move an internal trailing threshold that tightens with every candle, replicating the EA's TrailingStop behaviour.
Parameters
Name
Description
CandleType
Trading timeframe used for DeMarker signals.
MacdCandleType
Higher timeframe used to compute the MACD trend filter.
DemarkerPeriod
DeMarker lookback period.
DemarkerThreshold
Neutral boundary between bullish and bearish setups.
MacdFast / MacdSlow / MacdSignal
MACD EMA lengths.
InitialVolume
Base order size before martingale adjustments.
MartingaleMultiplier
Multiplication factor when DoubleLotSize is enabled.
LotIncrement
Additive increase when doubling is disabled.
DoubleLotSize
Toggle between multiplicative and additive martingale.
MaxMartingaleSteps
Maximum number of consecutive escalations.
StopLossPips
Stop-loss distance in pips.
TakeProfitPips
Take-profit distance in pips.
TrailingStopPips
Trailing stop distance in pips.
UseBreakEven
Enable or disable break-even logic.
BreakEvenTriggerPips
Profit threshold (in pips) before shifting to break-even.
BreakEvenOffsetPips
Buffer applied to the break-even stop.
Conversion Notes
The pip conversion mirrors the MQL EA (ticksize == 0.00001 or 0.001 implies a 10x pip scale). This preserves consistent risk distances on 3/5-digit quotes.
The MACD trend filter uses MovingAverageConvergenceDivergenceSignal with the original EMA lengths and processes a separate candle series to emulate the monthly chart logic.
Martingale bookkeeping tracks weighted-average entry prices and realised PnL to decide whether the next trade should escalate or reset.
All protective actions (stop-loss, take-profit, break-even, trailing) execute via market exits because the high-level API discourages direct order modifications under the StartProtection guard.
Usage Tips
Ensure the assigned security exposes PriceStep, VolumeStep, VolumeMin, and VolumeMax to align pip calculations and volume rounding with exchange constraints.
Experiment with MacdCandleType (e.g., weekly candles) to fine-tune the trend filter for faster markets.
When optimising, jointly adjust DemarkerThreshold, TrailingStopPips, and martingale parameters to keep drawdowns in check.
Combine the strategy with portfolio-level risk controls or trading session filters when deploying live, as martingale sequences inherently increase exposure after losses.
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;
public class DemarkerMartingaleStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public DemarkerMartingaleStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class demarker_martingale_strategy(Strategy):
def __init__(self):
super(demarker_martingale_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(demarker_martingale_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(demarker_martingale_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return demarker_martingale_strategy()