The strategy reacts to strong bullish and bearish candles and opens market positions in the same direction. It uses an independent martingale sequence for each side: long positions scale the volume with the Bull Multiplier, while short positions use the Bear Multiplier. Protective stop-loss and take-profit distances are also configured separately for each direction, allowing precise control over the asymmetrical behaviour that the original MQL expert advisor exposes.
Trading Logic
Subscribe to the configured candle type (default: 1 minute) and wait for completed candles only.
When there is no open position:
Bullish setup: if Close > Open and the candle body size exceeds the bullish body filter, buy at market.
Bearish setup: if Close < Open and the body size exceeds the bearish body filter, sell at market.
Each entry sets stop-loss and take-profit orders converted from pip distances to the instrument price step.
When a position closes, the realised PnL is compared with the previous baseline:
A negative result multiplies the respective martingale volume.
A positive or break-even result resets that side to the initial volume.
New signals are ignored while a position is open, reproducing the single-trade behaviour of the source EA.
Money Management
Long and short martingale cycles are tracked independently, so a losing long sequence will not affect the next short trade, and vice versa.
Volumes are aligned with the security VolumeStep to avoid rejected orders.
StartProtection(useMarketOrders: true) enables StockSharp protective order handling for the attached stop and take levels.
Parameters
Parameter
Description
Initial Volume
Base volume that starts each martingale cycle for both directions.
Bull Multiplier
Multiplier applied to the next bullish trade after a losing long position.
Bear Multiplier
Multiplier applied to the next bearish trade after a losing short position.
Bull Stop Loss
Stop-loss distance in pips for bullish trades. Converted to price using the instrument step.
Bull Take Profit
Take-profit distance in pips for bullish trades.
Bear Stop Loss
Stop-loss distance in pips for bearish trades.
Bear Take Profit
Take-profit distance in pips for bearish trades.
Bull Body Filter
Minimum bullish candle body in pips required to trigger a buy order.
Bear Body Filter
Minimum bearish candle body in pips required to trigger a sell order.
Candle Type
Time frame used for signal generation (default: 1-minute time frame).
Usage Notes
Ensure that the connected security exposes valid PriceStep and VolumeStep values. The strategy defaults to 0.0001 when PriceStep is not provided.
The martingale logic relies on realised PnL, so manual position closing will still update the sequence correctly.
Optimisation can focus on body filters and multiplier combinations to balance responsiveness versus drawdown.
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Bull/Bear Candle Martingale strategy: Bullish/bearish candle direction + EMA filter.
/// Buys after strong bullish candle when close crosses above EMA.
/// Sells after strong bearish candle when close crosses below EMA.
/// </summary>
public class BullBearCandleMartingaleStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
public BullBearCandleMartingaleStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_emaPeriod = Param(nameof(EmaPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA period", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
decimal? prevClose = null;
decimal? prevEma = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, (candle, emaVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var bullish = close > candle.OpenPrice;
var bearish = close < candle.OpenPrice;
var bodySize = Math.Abs(close - candle.OpenPrice);
var range = candle.HighPrice - candle.LowPrice;
// Require strong candle body (>50% of range)
var strongCandle = range > 0 && bodySize / range > 0.5m;
if (prevClose.HasValue && prevEma.HasValue && strongCandle)
{
var crossUp = prevClose.Value <= prevEma.Value && close > emaVal;
var crossDown = prevClose.Value >= prevEma.Value && close < emaVal;
if (bullish && crossUp && Position <= 0)
BuyMarket();
else if (bearish && crossDown && Position >= 0)
SellMarket();
}
prevClose = close;
prevEma = emaVal;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
}
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 bull_bear_candle_martingale_strategy(Strategy):
def __init__(self):
super(bull_bear_candle_martingale_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 30) \
.SetDisplay("EMA Period", "EMA period", "Indicators")
self._ema = None
self._prev_close = None
self._prev_ema = None
@property
def ema_period(self):
return self._ema_period.Value
def OnReseted(self):
super(bull_bear_candle_martingale_strategy, self).OnReseted()
self._ema = None
self._prev_close = None
self._prev_ema = None
def OnStarted2(self, time):
super(bull_bear_candle_martingale_strategy, self).OnStarted2(time)
self._ema = ExponentialMovingAverage()
self._ema.Length = self.ema_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromHours(1)))
subscription.Bind(self._ema, self._process_candle)
subscription.Start()
def _process_candle(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
if not self._ema.IsFormed:
return
close = float(candle.ClosePrice)
open_p = float(candle.OpenPrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
ema_val = float(ema_value)
bullish = close > open_p
bearish = close < open_p
body_size = abs(close - open_p)
range_size = high - low
strong_candle = range_size > 0 and body_size / range_size > 0.5
if self._prev_close is not None and self._prev_ema is not None and strong_candle:
cross_up = self._prev_close <= self._prev_ema and close > ema_val
cross_down = self._prev_close >= self._prev_ema and close < ema_val
if bullish and cross_up and self.Position <= 0:
self.BuyMarket()
elif bearish and cross_down and self.Position >= 0:
self.SellMarket()
self._prev_close = close
self._prev_ema = ema_val
def CreateClone(self):
return bull_bear_candle_martingale_strategy()