Ver en GitHub

Reversing Martingale Strategy

Overview

The Reversing Martingale Strategy is a direct C# port of the MetaTrader expert advisor “Reversing Martingale EA”. It continuously maintains a single market position and alternates the trade direction after each closed deal. Losing trades trigger a martingale volume progression, while profitable trades reset the cycle back to the initial lot size. All positions are protected by symmetric stop-loss and take-profit levels expressed in price points.

The strategy does not rely on indicators or market structure. It simply reacts to completed positions and keeps capital exposure active at all times (unless trading is disabled).

Core Logic

  1. Initial setup
    • When the strategy starts it immediately submits a market order using the Start Volume parameter and the configured First Trade Side.
    • Protective stop-loss and take-profit orders are attached using the distance specified in Target (points).
  2. Position management
    • Only one position can be open at a time. The strategy waits until the current position is fully closed by its protective orders or by external actions.
    • After each exit the strategy flips the trade direction (buy → sell or sell → buy).
    • If the last trade realized a loss, the next order volume equals the previous position size multiplied by Lot Multiplier. Otherwise, the volume resets to Start Volume.
  3. Cycle continuation
    • Once the new volume and direction are determined, the next market order is submitted immediately, keeping the alternating martingale cycle running.

Parameters

Name Description
Start Volume Initial trade volume used at the beginning of every winning cycle.
Lot Multiplier Volume multiplier applied after a losing trade. Must be greater than 1.
First Trade Side Direction of the very first trade when the strategy session starts.
Target (points) Distance in price steps used for both stop-loss and take-profit orders.
Order Comment Optional text tag assigned to each generated market order.

Additional Notes

  • The price step distance is converted into UnitTypes.Step and passed to StartProtection, so both stop-loss and take-profit are always active.
  • Volume adjustments respect the security volume step, minimum, and maximum bounds through the NormalizeVolume helper.
  • The strategy expects trade execution events from the connector; if trading is paused or the connector is offline, the martingale cycle will resume once trading is allowed again.
using System;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Reversing Martingale strategy: WMA crossover.
/// Buys when fast WMA crosses above slow WMA. Sells on cross below.
/// </summary>
public class ReversingMartingaleStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	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 ReversingMartingaleStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");

		_fastPeriod = Param(nameof(FastPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Fast WMA", "Fast WMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow WMA", "Slow WMA period", "Indicators");
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var fast = new WeightedMovingAverage { Length = FastPeriod };
		var slow = new WeightedMovingAverage { Length = SlowPeriod };

		decimal? prevFast = null;
		decimal? prevSlow = null;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, (candle, fastVal, slowVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				if (prevFast.HasValue && prevSlow.HasValue)
				{
					var crossUp = prevFast.Value <= prevSlow.Value && fastVal > slowVal;
					var crossDown = prevFast.Value >= prevSlow.Value && fastVal < slowVal;

					if (crossUp && Position <= 0)
						BuyMarket();
					else if (crossDown && Position >= 0)
						SellMarket();
				}

				prevFast = fastVal;
				prevSlow = slowVal;
			})
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fast);
			DrawIndicator(area, slow);
			DrawOwnTrades(area);
		}
	}
}