View on GitHub

Simple Martingale Template Strategy

Overview

This strategy replicates the original MetaTrader "Simple Martingale Template" idea in StockSharp. It analyzes finished candles of a configurable timeframe using a pair of simple moving averages (SMA). A breakout filter checks whether the previous candle's close breaks the high or low of an even earlier candle to confirm direction. The position size follows a martingale sequence: after each losing cycle the next trade volume is multiplied, while profitable cycles reset the volume to the configured base size.

Trading Logic

  1. Subscribe to candles of the CandleType timeframe. Only finished candles participate in signal generation.
  2. Calculate a fast SMA and a slow SMA on the candle close.
  3. Generate a buy signal when:
    • the last finished candle close is above the fast SMA,
    • the fast SMA is above the slow SMA,
    • on the previous candle the fast SMA was below the slow SMA, and
    • the last finished candle close is above the high of the candle two bars ago.
  4. Generate a sell signal when the symmetric conditions occur to the downside, including the close being below the low of the candle two bars ago.
  5. When a signal fires and there are no open positions or active orders, send a market order using the currently calculated martingale volume.
  6. Attach synthetic stop-loss and take-profit levels by monitoring future candles. When price touches either level, close the open position.
  7. After a position closes and the portfolio balance updates:
    • if the balance increased, reset volume to the BaseVolume value;
    • if the balance decreased, multiply the last trade volume by Multiplier and align it to the instrument volume step.

Parameters

Name Description
StopLossPoints Distance from entry to the protective stop in price points.
TakeProfitPoints Distance from entry to the profit target in price points.
BaseVolume Initial lot size for the martingale cycle.
Multiplier Factor applied to the previous lot size after a loss.
FastPeriod Length of the fast SMA used for directional bias.
SlowPeriod Length of the slow SMA for trend confirmation.
CandleType Timeframe of candles processed by the strategy.

Money Management

  • The martingale ladder reacts strictly to realized balance changes. Small fluctuations (±0.01 monetary units) are ignored to avoid noise.
  • Volumes are aligned to the instrument VolumeStep, MinVolume, and MaxVolume to ensure valid order sizes.
  • Stop-loss and take-profit levels are monitored on candle extremes (high/low) instead of placing exchange orders, mirroring the original MQL implementation that used market exits.

Usage Notes

  • Choose a timeframe and symbol combination that produces enough historical candles for both SMAs to form before enabling trading.
  • Adjust StopLossPoints and TakeProfitPoints to match the symbol's tick size; they represent point counts, not price units.
  • Consider testing different multipliers and base volumes to control capital requirements because martingale sequences grow quickly.
  • The strategy calls StartProtection() on start to integrate with StockSharp's standard risk management features.
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Simple Martingale Template strategy: SMA crossover with momentum confirmation.
/// Buys on fast SMA crossing above slow SMA, sells on crossing below.
/// </summary>
public class SimpleMartingaleTemplateStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }

	public SimpleMartingaleTemplateStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_fastPeriod = Param(nameof(FastPeriod), 5)
			.SetGreaterThanZero()
			.SetDisplay("Fast SMA", "Fast SMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 15)
			.SetGreaterThanZero()
			.SetDisplay("Slow SMA", "Slow SMA period", "Indicators");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0;
		_prevSlow = 0;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_hasPrev = false;
		var fast = new SimpleMovingAverage { Length = FastPeriod };
		var slow = new SimpleMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;

		if (_hasPrev)
		{
			if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
				BuyMarket();
			else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
				SellMarket();
		}
		else
		{
			if (fastValue > slowValue && Position <= 0)
				BuyMarket();
			else if (fastValue < slowValue && Position >= 0)
				SellMarket();
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
		_hasPrev = true;
	}
}