Ver no GitHub

Pure Martingale Strategy

This strategy implements a basic martingale system. It opens trades in a random direction and doubles the position size and the stop/take distance after each losing trade. After a winning trade it resets to the initial volume and distance.

The approach assumes that price will eventually return to profitability, but risk grows exponentially. Use only on liquid instruments with tight spreads.

Details

  • Entry Criteria:
    • No open position: randomly buy or sell on candle close.
  • Long/Short: Both.
  • Exit Criteria:
    • Close when price moves in favor or against the position by the configured distance.
  • Stops: Virtual stop loss and take profit managed by the strategy.
  • Filters:
    • None.
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>
/// Momentum strategy with EMA trend filter.
/// </summary>
public class PureMartingaleStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevClose;
	private decimal _prevPrevClose;
	private int _barCount;

	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public PureMartingaleStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA trend period", "Indicators");
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candles for trade timing", "General");
	}

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

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClose = 0;
		_prevPrevClose = 0;
		_barCount = 0;
	}

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

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		SubscribeCandles(CandleType).Bind(ema, ProcessCandle).Start();
	}

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

		var close = candle.ClosePrice;
		_barCount++;

		if (_barCount >= 3)
		{
			// Two consecutive rising closes above EMA => buy
			if (close > _prevClose && _prevClose > _prevPrevClose && close > emaValue && Position <= 0)
			{
				if (Position < 0) BuyMarket();
				BuyMarket();
			}
			// Two consecutive falling closes below EMA => sell
			else if (close < _prevClose && _prevClose < _prevPrevClose && close < emaValue && Position >= 0)
			{
				if (Position > 0) SellMarket();
				SellMarket();
			}
		}

		_prevPrevClose = _prevClose;
		_prevClose = close;
	}
}