Auf GitHub ansehen

Martingale MACD Strategy

This strategy replicates the original MQL "MartGreg_1" expert in the StockSharp framework. It uses two Moving Average Convergence Divergence (MACD) indicators to search for reversals and applies a martingale rule for position sizing.

How it works

  • The first MACD searches for local extremes on the last three finished candles.
  • The second MACD compares the last two values to determine momentum direction.
  • A long position is opened when the first MACD forms a valley and the second MACD decreases.
  • A short position is opened when the first MACD forms a peak and the second MACD increases.
  • After every losing trade the next order size is doubled up to the configured limit.
  • Stop loss and take profit are set in absolute price points.

Parameters

  • Shape – divider for calculating initial volume from account balance.
  • Doubling Count – maximum number of consecutive doublings after losses.
  • Stop Loss – protective stop in points.
  • Take Profit – profit target in points.
  • MACD1 Fast/Slow – periods for the first MACD.
  • MACD2 Fast/Slow – periods for the second MACD.
  • Candle Type – timeframe for analysis.
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>
/// Martingale strategy based on dual MACD indicators.
/// Buys when MACD1 turns up and MACD2 turns down (divergence).
/// Sells on the opposite condition.
/// </summary>
public class MartingaleMacdStrategy : Strategy
{
	private readonly StrategyParam<int> _macd1Fast;
	private readonly StrategyParam<int> _macd1Slow;
	private readonly StrategyParam<int> _macd2Fast;
	private readonly StrategyParam<int> _macd2Slow;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _macd1Prev1;
	private decimal? _macd1Prev2;
	private decimal? _macd2Prev;

	public int Macd1Fast { get => _macd1Fast.Value; set => _macd1Fast.Value = value; }
	public int Macd1Slow { get => _macd1Slow.Value; set => _macd1Slow.Value = value; }
	public int Macd2Fast { get => _macd2Fast.Value; set => _macd2Fast.Value = value; }
	public int Macd2Slow { get => _macd2Slow.Value; set => _macd2Slow.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public MartingaleMacdStrategy()
	{
		_macd1Fast = Param(nameof(Macd1Fast), 5)
			.SetDisplay("MACD1 Fast", "Fast EMA for first MACD", "Indicators");

		_macd1Slow = Param(nameof(Macd1Slow), 20)
			.SetDisplay("MACD1 Slow", "Slow EMA for first MACD", "Indicators");

		_macd2Fast = Param(nameof(Macd2Fast), 10)
			.SetDisplay("MACD2 Fast", "Fast EMA for second MACD", "Indicators");

		_macd2Slow = Param(nameof(Macd2Slow), 15)
			.SetDisplay("MACD2 Slow", "Slow EMA for second MACD", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for candles", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_macd1Prev1 = null;
		_macd1Prev2 = null;
		_macd2Prev = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_macd1Prev1 = null;
		_macd1Prev2 = null;
		_macd2Prev = null;

		var macd1 = new MovingAverageConvergenceDivergence
		{
			ShortMa = { Length = Macd1Fast },
			LongMa = { Length = Macd1Slow }
		};
		var macd2 = new MovingAverageConvergenceDivergence
		{
			ShortMa = { Length = Macd2Fast },
			LongMa = { Length = Macd2Slow }
		};

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(macd1, macd2, ProcessCandle)
			.Start();

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (_macd1Prev1.HasValue && _macd1Prev2.HasValue && _macd2Prev.HasValue)
		{
			var t0 = macd1Val;
			var t1 = _macd1Prev1.Value;
			var t2 = _macd1Prev2.Value;
			var k0 = macd2Val;
			var k1 = _macd2Prev.Value;

			// MACD1 turns up (V-shape) and MACD2 turns down => buy
			if (t0 > t1 && t1 < t2 && k1 > k0 && Position <= 0)
				BuyMarket();
			// MACD1 turns down (inverted V) and MACD2 turns up => sell
			else if (t0 < t1 && t1 > t2 && k1 < k0 && Position >= 0)
				SellMarket();
		}

		_macd1Prev2 = _macd1Prev1;
		_macd1Prev1 = macd1Val;
		_macd2Prev = macd2Val;
	}
}