Martingail Expert is a trend-following martingale strategy that relies on the Stochastic Oscillator to time new sequences of trades. Once the indicator generates a direction, the strategy starts a ladder of market orders and manages the exposure using a dynamic profit target and a geometric position sizing scheme.
Trading Logic
Calculate a Stochastic Oscillator on the configured candle series. The most recent final values of %K and %D are cached for decision making.
Start a new long sequence when %K (previous) > %D (previous) and %D (previous) is above the BuyLevel threshold.
Start a new short sequence when %K (previous) < %D (previous) and %D (previous) is below the SellLevel threshold.
After entering a sequence, every favorable price move equal to ProfitFactor × openOrders pips adds a new position with the base volume.
Every adverse move of StepPoints pips multiplies the last filled volume by Multiplier and sends an averaging order in the same direction.
Exit Rules
Close the entire position as soon as the last fill price reaches a dynamic profit target at ProfitFactor × openOrders pips in the favorable direction.
Reset the martingale state whenever the aggregated position size returns to zero.
Risk Management
The martingale progression increases exposure quickly when price moves against the position. Adjust Multiplier, StepPoints, and ProfitFactor carefully to match the account size and instrument volatility.
Parameters
Name
Description
Volume
Base market order volume used for the first trade and every favorable add-on.
Multiplier
Factor applied to the last executed volume when averaging during adverse moves.
StepPoints
Distance in points that triggers a martingale averaging order.
ProfitFactor
Profit target per open order expressed in points. The actual distance is ProfitFactor × number_of_orders.
KPeriod
Lookback length for the %K line.
DPeriod
Smoothing length for the %D line.
Slowing
Additional smoothing applied to %K before comparing with %D.
BuyLevel
Minimum %D value required to allow a new long sequence.
SellLevel
Maximum %D value required to allow a new short sequence.
CandleType
Candle series used for calculations (default: 5-minute timeframe).
Notes
Works best on liquid FX pairs where pip size and volume step allow granular scaling.
Requires sufficient margin to withstand several martingale steps.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Martingale strategy driven by stochastic oscillator crossovers.
/// Buy when K crosses above D, sell when K crosses below D.
/// Doubles down on adverse moves with martingale averaging.
/// </summary>
public class MartingailExpertSequenceStrategy : Strategy
{
private readonly StrategyParam<int> _kPeriod;
private readonly StrategyParam<int> _dPeriod;
private readonly StrategyParam<decimal> _stepPoints;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevK;
private decimal? _prevD;
private decimal _entryPrice;
public int KPeriod { get => _kPeriod.Value; set => _kPeriod.Value = value; }
public int DPeriod { get => _dPeriod.Value; set => _dPeriod.Value = value; }
public decimal StepPoints { get => _stepPoints.Value; set => _stepPoints.Value = value; }
public decimal TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MartingailExpertSequenceStrategy()
{
_kPeriod = Param(nameof(KPeriod), 14)
.SetDisplay("K Period", "Stochastic K period", "Indicators");
_dPeriod = Param(nameof(DPeriod), 3)
.SetDisplay("D Period", "Stochastic D period", "Indicators");
_stepPoints = Param(nameof(StepPoints), 500m)
.SetDisplay("Step Points", "Distance for martingale averaging", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 200m)
.SetDisplay("Take Profit", "Take profit in price steps", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevK = null;
_prevD = null;
_entryPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevK = null;
_prevD = null;
var stoch = new StochasticOscillator
{
K = { Length = KPeriod },
D = { Length = DPeriod }
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(stoch, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue stochValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!stochValue.IsFinal)
return;
if (stochValue is not StochasticOscillatorValue stoch)
return;
if (stoch.K is not decimal k || stoch.D is not decimal d)
return;
var step = Security?.PriceStep ?? 1m;
if (step <= 0) step = 1m;
// Check take profit
if (Position > 0 && candle.ClosePrice >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_prevK = k;
_prevD = d;
return;
}
else if (Position < 0 && candle.ClosePrice <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_prevK = k;
_prevD = d;
return;
}
if (_prevK is not decimal prevK || _prevD is not decimal prevD)
{
_prevK = k;
_prevD = d;
return;
}
// Stochastic crossover signals
var bullCross = prevK <= prevD && k > d;
var bearCross = prevK >= prevD && k < d;
if (Position == 0)
{
if (bullCross)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
}
else if (bearCross)
{
SellMarket();
_entryPrice = candle.ClosePrice;
}
}
else if (Position > 0 && bearCross)
{
SellMarket(); // Close long
SellMarket(); // Open short
_entryPrice = candle.ClosePrice;
}
else if (Position < 0 && bullCross)
{
BuyMarket(); // Close short
BuyMarket(); // Open long
_entryPrice = candle.ClosePrice;
}
_prevK = k;
_prevD = d;
}
}
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 StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
class martingail_expert_sequence_strategy(Strategy):
"""
Martingale Expert Sequence: stochastic K/D crossover with take profit.
"""
def __init__(self):
super(martingail_expert_sequence_strategy, self).__init__()
self._k_period = self.Param("KPeriod", 14).SetDisplay("K Period", "Stochastic K", "Indicators")
self._d_period = self.Param("DPeriod", 3).SetDisplay("D Period", "Stochastic D", "Indicators")
self._take_profit_points = self.Param("TakeProfitPoints", 200.0).SetDisplay("Take Profit", "TP in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Candles", "General")
self._prev_k = None
self._prev_d = None
self._entry_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(martingail_expert_sequence_strategy, self).OnReseted()
self._prev_k = None
self._prev_d = None
self._entry_price = 0.0
def OnStarted2(self, time):
super(martingail_expert_sequence_strategy, self).OnStarted2(time)
stoch = StochasticOscillator()
stoch.K.Length = self._k_period.Value
stoch.D.Length = self._d_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(stoch, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, stoch)
self.DrawOwnTrades(area)
def _process_candle(self, candle, stoch_value):
if candle.State != CandleStates.Finished:
return
k_val = stoch_value.K
d_val = stoch_value.D
if k_val is None or d_val is None:
return
k = float(k_val)
d = float(d_val)
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
tp = float(self._take_profit_points.Value)
if self.Position > 0 and close >= self._entry_price + tp * step:
self.SellMarket()
self._prev_k = k
self._prev_d = d
return
elif self.Position < 0 and close <= self._entry_price - tp * step:
self.BuyMarket()
self._prev_k = k
self._prev_d = d
return
if self._prev_k is None or self._prev_d is None:
self._prev_k = k
self._prev_d = d
return
bull_cross = self._prev_k <= self._prev_d and k > d
bear_cross = self._prev_k >= self._prev_d and k < d
if self.Position == 0:
if bull_cross:
self.BuyMarket()
self._entry_price = close
elif bear_cross:
self.SellMarket()
self._entry_price = close
elif self.Position > 0 and bear_cross:
self.SellMarket()
self.SellMarket()
self._entry_price = close
elif self.Position < 0 and bull_cross:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._prev_k = k
self._prev_d = d
def CreateClone(self):
return martingail_expert_sequence_strategy()