View on GitHub

Martini Martingale Strategy

This strategy implements a hedged martingale grid. It starts by placing stop orders on both sides of the current price and doubles position size in the opposite direction whenever the market moves against the current exposure by a specified step. All trades are closed once accumulated profit exceeds the target.

Details

  • Entry Criteria:
    • Place a buy stop above and a sell stop below the market at distance Step.
    • When an order triggers, cancel the opposite stop.
  • Position Management:
    • Track the price of the last executed order.
    • If price moves against the open position by Step * orderCount, send a market order in the opposite direction with double the previous volume.
  • Exit Criteria:
    • Close all positions when unrealized profit reaches ProfitClose.
  • Long/Short: Both.
  • Stops: Uses stop orders for initial entries; no stop-loss.
  • Indicators: None.
  • Filters: None.

Parameters

  • Step – price step in absolute units.
  • ProfitClose – profit threshold to close all trades.
  • InitialVolume – starting volume for the first order.
  • CandleType – candle series used for price updates.
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>
/// Mean reversion strategy inspired by martingale.
/// Enters on RSI extremes, exits when price returns to SMA.
/// </summary>
public class MartiniMartingaleStrategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _smaPeriod;
	private readonly StrategyParam<DataType> _candleType;

	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public int SmaPeriod { get => _smaPeriod.Value; set => _smaPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public MartiniMartingaleStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 7)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI period", "Indicators");
		_smaPeriod = Param(nameof(SmaPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("SMA Period", "SMA for mean reversion target", "Indicators");
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

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

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var sma = new SimpleMovingAverage { Length = SmaPeriod };

		SubscribeCandles(CandleType).Bind(rsi, sma, ProcessCandle).Start();
	}

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

		var close = candle.ClosePrice;

		// RSI oversold => buy
		if (rsi < 30 && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
		}
		// RSI overbought => sell
		else if (rsi > 70 && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
		}
		// Exit long at SMA
		else if (Position > 0 && close >= sma && rsi > 50)
		{
			SellMarket();
		}
		// Exit short at SMA
		else if (Position < 0 && close <= sma && rsi < 50)
		{
			BuyMarket();
		}
	}
}