Port of the MetaTrader 5 MartingailExpert.mq5 expert adviser.
Uses a stochastic oscillator crossover with configurable %K, %D and slowing parameters to open positions.
Implements a martingale-style grid with both averaging and breakout entries that scale volume geometrically.
Designed for netted portfolios – the strategy maintains a single aggregated long or short position.
Trading Logic
Entry Conditions
The strategy processes closed candles of the CandleType timeframe.
Stochastic values are taken from the previous finished candle to mimic the MQL iStochastic(..., 1) call.
A long entry is triggered when:
Previous %K is greater than previous %D.
Previous %D is above BuyLevel.
No open position exists.
A short entry is triggered when:
Previous %K is below previous %D.
Previous %D is below SellLevel.
No open position exists.
All market orders use the normalized Volume value (rounded to the nearest Security.VolumeStep).
Position Scaling
ProfitPips defines the distance (in pips) required to add another base position in the direction of profit.
When long, if the candle high reaches lastEntryPrice + ProfitPips * positionCount, a new order with the base Volume is sent.
When short, if the candle low reaches lastEntryPrice - ProfitPips * positionCount, a new base order is sent.
StepPips defines the averaging distance (in pips) to apply the martingale multiplier.
For longs, if the candle low touches lastEntryPrice - StepPips, the next order volume equals lastVolume * Multiplier.
For shorts, if the candle high touches lastEntryPrice + StepPips, the same martingale sizing is applied.
Every executed trade updates lastEntryPrice, lastVolume, and the internal count of active positions.
Exit Logic
The last executed trade price is stored per direction.
If price reaches lastEntryPrice ± ProfitPips (using candle highs for longs and lows for shorts), all open positions are closed via market order.
Once the aggregated position returns to zero, martingale state variables are reset.
Parameters
Name
Default
Description
Volume
0.03
Base lot size for the initial order and profit-based add-ons.
Multiplier
1.6
Martingale multiplier for averaging entries.
StepPips
25
Pip distance that triggers averaging orders against the trend.
ProfitPips
9
Pip distance used for both profit exits and breakout add-ons.
KPeriod
5
Lookback period of the stochastic %K calculation.
DPeriod
3
Smoothing period for the stochastic %D line.
Slowing
3
Smoothing applied to the %K line (slow stochastic).
BuyLevel
20
Minimum %D value required to allow long entries.
SellLevel
55
Maximum %D value required to allow short entries.
CandleType
5 minute time frame
Timeframe used to build candles and indicators.
Implementation Notes
Pip distance is computed from Security.PriceStep. Instruments with 3 or 5 decimal quotes are automatically adjusted by multiplying the price step by 10 to match the original MQL pip logic.
Volumes are rounded down to the nearest Security.VolumeStep. Values that fall below the minimum tradable step are ignored.
The strategy relies on candle highs and lows to approximate intra-bar triggers because the high-level API operates on finished candles.
OnOwnTradeReceived tracks real execution prices and volumes to faithfully reproduce the martingale escalation sequence.
Usage Tips
Align the CandleType with the timeframe used in the original MetaTrader template (commonly M5) for similar behaviour.
Ensure the security metadata (price step, volume step) is populated; otherwise adjust Volume, StepPips, and ProfitPips manually to match broker specifications.
Consider enabling external risk management (stop losses or capital limits) because the martingale logic intentionally increases exposure during adverse moves.
Differences from the Original Expert Advisor
The StockSharp version processes completed candles instead of every tick; threshold checks use candle highs/lows to approximate intra-bar behaviour.
MetaTrader-specific account margin checks are unavailable in StockSharp high-level strategies; ensure adequate capital is configured externally.
Order execution and position tracking leverage StockSharp’s netting model; hedging mode is not supported.
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>
/// Martingale Expert strategy (simplified). Uses RSI oscillator with
/// martingale-style position scaling and profit targets.
/// </summary>
public class MartingailExpertStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<decimal> _buyLevel;
private readonly StrategyParam<decimal> _sellLevel;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
public decimal BuyLevel
{
get => _buyLevel.Value;
set => _buyLevel.Value = value;
}
public decimal SellLevel
{
get => _sellLevel.Value;
set => _sellLevel.Value = value;
}
public MartingailExpertStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "Indicators");
_buyLevel = Param(nameof(BuyLevel), 35m)
.SetDisplay("Buy Level", "RSI level for longs", "Logic");
_sellLevel = Param(nameof(SellLevel), 65m)
.SetDisplay("Sell Level", "RSI level for shorts", "Logic");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RelativeStrengthIndex { Length = RsiLength };
decimal prevRsi = 50;
var hasPrev = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, (ICandleMessage candle, decimal rsiVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!hasPrev)
{
prevRsi = rsiVal;
hasPrev = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
prevRsi = rsiVal;
return;
}
// RSI crosses up from oversold
if (prevRsi < BuyLevel && rsiVal >= BuyLevel && Position <= 0)
BuyMarket();
// RSI crosses down from overbought
else if (prevRsi > SellLevel && rsiVal <= SellLevel && Position >= 0)
SellMarket();
prevRsi = rsiVal;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var rsiArea = CreateChartArea();
if (rsiArea != null)
DrawIndicator(rsiArea, rsi);
}
}
}
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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class martingail_expert_strategy(Strategy):
def __init__(self):
super(martingail_expert_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candles", "General")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period", "Indicators")
self._buy_level = self.Param("BuyLevel", 35.0) \
.SetDisplay("Buy Level", "RSI level for longs", "Logic")
self._sell_level = self.Param("SellLevel", 65.0) \
.SetDisplay("Sell Level", "RSI level for shorts", "Logic")
self._prev_rsi = 50.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiLength(self):
return self._rsi_length.Value
@property
def BuyLevel(self):
return self._buy_level.Value
@property
def SellLevel(self):
return self._sell_level.Value
def OnReseted(self):
super(martingail_expert_strategy, self).OnReseted()
self._prev_rsi = 50.0
self._has_prev = False
def OnStarted2(self, time):
super(martingail_expert_strategy, self).OnStarted2(time)
self._prev_rsi = 50.0
self._has_prev = False
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_value)
if not self._has_prev:
self._prev_rsi = rv
self._has_prev = True
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_rsi = rv
return
if self._prev_rsi < self.BuyLevel and rv >= self.BuyLevel and self.Position <= 0:
self.BuyMarket()
elif self._prev_rsi > self.SellLevel and rv <= self.SellLevel and self.Position >= 0:
self.SellMarket()
self._prev_rsi = rv
def CreateClone(self):
return martingail_expert_strategy()