Открыть на GitHub

MACD Four Colors 2 Martingale

Стратегия переносит советник «MACD Four Colors 2 Martingale» с платформы MetaTrader в StockSharp и сохраняет оригинальную логику, основанную на цветовом анализе MACD и мартингейле при наборе позиции.

Общее описание

Базовый индикатор раскрашивает гистограмму MACD в пять цветов. В стандартной «новой» схеме цвет меняется в зависимости от того, растёт или падает линия MACD и находится ли она выше либо ниже нулевой линии. Советник открывает позицию, когда палитра меняется с серебристой на жёлтую (MACD остаётся ниже нуля и снова начинает падать) или с красной на синюю (MACD выше нуля и разворачивается вниз). Версия для StockSharp восстанавливает эти переходы по значениям MACD и повторяет сигналы.

Одновременно допускается только одна направленная серия сделок. Новый вход разрешён, если цена улучшает среднюю цену набранной серии: ниже для покупок и выше для продаж. Каждый следующий ордер умножает объём предыдущего заполненного ордера на коэффициент LotCoefficient, что реализует мартингейл из оригинального кода.

Торговые правила

  • Логика индикатора: используется MovingAverageConvergenceDivergenceSignal с классическими параметрами 12/26/9.
  • Восстановление цветов: стратегия сравнивает два последних значения MACD. Растущий отрицательный MACD соответствует цвету 1 (серебро), растущий положительный — цвету 2 (красный), падающий положительный — цвету 3 (синий), падающий отрицательный — цвету 4 (жёлтый).
  • Вход в лонг: сигнал формируется, когда цвета переходят с 1 на 4, а MACD на предыдущем баре остаётся ниже нуля. Сделка исполняется только при отсутствии коротких позиций и если цена ниже всех предыдущих входов в лонг.
  • Вход в шорт: сигнал возникает при переходе цветов с 2 на 3 и положительном MACD на предыдущем баре. Сделка разрешена только при отсутствии длинных позиций и если цена выше всех открытых шортов.
  • Управление объёмом: первый ордер использует InitialVolume. Каждый следующий ордер внутри серии умножает объём на LotCoefficient. Если коэффициент ≤ 0, усиление отключается.
  • Контроль прибыли и убытков: плавающая прибыль пересчитывается на каждом закрытом баре. Достижение TargetProfit закрывает все позиции и сбрасывает цикл. Пробой MaxDrawdown (порог убытка) также приводит к полному выходу и перезапуску. Поддерживаются отрицательные пороги по аналогии с MQL-реализацией.
  • Выход из позиции: кроме денежных целей автоматических стопов нет. Позиции удерживаются, пока не сработают ограничители или пока пользователь не вмешается.

Параметры

  • CandleType (DataType, по умолчанию 1h) — таймфрейм для расчёта MACD.
  • InitialVolume (decimal, по умолчанию 1) — объём первого ордера в серии.
  • LotCoefficient (decimal, по умолчанию 2) — множитель объёма для следующего ордера в мартингейле.
  • MaxDrawdown (decimal, по умолчанию 50) — порог плавающего убытка в деньгах, при достижении которого закрываются все позиции. Положительное значение контролирует -MaxDrawdown, отрицательное трактуется напрямую.
  • TargetProfit (decimal, по умолчанию 150) — цель по плавающей прибыли в деньгах. Отрицательное значение инвертирует сравнение, как и в версии MQL.
  • FastEmaPeriod (int, по умолчанию 12) — период быстрой EMA в MACD.
  • SlowEmaPeriod (int, по умолчанию 26) — период медленной EMA в MACD.
  • SignalPeriod (int, по умолчанию 9) — период сигнальной EMA.

Рекомендации по использованию

  • Стратегии требуется инструмент с заданными PriceStep и StepPrice, поскольку не реализован независимый расчёт плавающего PnL.
  • Мартингейл быстро наращивает объём позиции, поэтому заранее проверьте лимиты риска и маржинальные требования брокера.
  • Для визуального контроля используйте создаваемую область графика: на ней отображаются свечи, MACD и сделки стратегии.

Фильтры каталога

  • Категория: тренд / усреднение по импульсу
  • Направление: обе стороны (лонг и шорт)
  • Индикаторы: MACD
  • Стопы: только денежные цели
  • Таймфрейм: настраиваемый (по умолчанию 1h)
  • Сложность: средняя
  • Риск: высокий из-за мартингейла
  • Автоматизация: полная
using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// MACD histogram crossover with martingale volume scaling.
/// Based on the MACD Four Colors 2 Martingale expert advisor.
/// </summary>
public class MacdFourColors2MartingaleStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastEmaPeriod;
	private readonly StrategyParam<int> _slowEmaPeriod;
	private readonly StrategyParam<int> _signalPeriod;
	private readonly StrategyParam<decimal> _lotCoefficient;
	private readonly StrategyParam<int> _maxMartingale;

	private MovingAverageConvergenceDivergence _macd;
	private readonly System.Collections.Generic.Queue<decimal> _macdHistory = new();
	private decimal? _prevHistogram;
	private decimal _currentVolume;
	private int _consecutiveLosses;
	private decimal _entryPrice;

	/// <summary>
	/// Type of candles used for MACD analysis.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Period of the fast EMA inside MACD.
	/// </summary>
	public int FastEmaPeriod
	{
		get => _fastEmaPeriod.Value;
		set => _fastEmaPeriod.Value = value;
	}

	/// <summary>
	/// Period of the slow EMA inside MACD.
	/// </summary>
	public int SlowEmaPeriod
	{
		get => _slowEmaPeriod.Value;
		set => _slowEmaPeriod.Value = value;
	}

	/// <summary>
	/// Period of the signal line.
	/// </summary>
	public int SignalPeriod
	{
		get => _signalPeriod.Value;
		set => _signalPeriod.Value = value;
	}

	/// <summary>
	/// Multiplier applied after a losing trade.
	/// </summary>
	public decimal LotCoefficient
	{
		get => _lotCoefficient.Value;
		set => _lotCoefficient.Value = value;
	}

	/// <summary>
	/// Maximum number of martingale steps before resetting.
	/// </summary>
	public int MaxMartingale
	{
		get => _maxMartingale.Value;
		set => _maxMartingale.Value = value;
	}

	/// <summary>
	/// Initialize strategy parameters.
	/// </summary>
	public MacdFourColors2MartingaleStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles for MACD analysis", "General");

		_fastEmaPeriod = Param(nameof(FastEmaPeriod), 20)
			.SetDisplay("Fast EMA", "Fast EMA period for MACD", "Indicators")
			.SetGreaterThanZero();

		_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 50)
			.SetDisplay("Slow EMA", "Slow EMA period for MACD", "Indicators")
			.SetGreaterThanZero();

		_signalPeriod = Param(nameof(SignalPeriod), 12)
			.SetDisplay("Signal Period", "Signal line smoothing period", "Indicators")
			.SetGreaterThanZero();

		_lotCoefficient = Param(nameof(LotCoefficient), 1.5m)
			.SetDisplay("Lot Coefficient", "Multiplier after a loss", "Money Management")
			.SetGreaterThanZero();

		_maxMartingale = Param(nameof(MaxMartingale), 5)
			.SetDisplay("Max Martingale", "Maximum consecutive doublings", "Money Management")
			.SetGreaterThanZero();
	}

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

		_prevHistogram = null;
		_currentVolume = Volume > 0 ? Volume : 1;
		_consecutiveLosses = 0;
		_entryPrice = 0;
		_macdHistory.Clear();

		_macd = new MovingAverageConvergenceDivergence
		{
			ShortMa = { Length = FastEmaPeriod },
			LongMa = { Length = SlowEmaPeriod }
		};

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

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

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

		_macdHistory.Enqueue(macdValue);
		while (_macdHistory.Count > SignalPeriod)
			_macdHistory.Dequeue();

		if (!_macd.IsFormed || _macdHistory.Count < SignalPeriod)
		{
			_prevHistogram = null;
			return;
		}

		// Calculate signal line (SMA of MACD)
		var sum = 0m;
		var history = _macdHistory.ToArray();
		foreach (var v in history)
			sum += v;
		var signalValue = sum / history.Length;

		var histogram = macdValue - signalValue;

		if (_prevHistogram is null)
		{
			_prevHistogram = histogram;
			return;
		}

		var crossUp = _prevHistogram < 0 && histogram >= 0;
		var crossDown = _prevHistogram >= 0 && histogram < 0;

		if (crossUp)
		{
			// Check for loss on closing short
			if (Position < 0)
			{
				var pnl = _entryPrice - candle.ClosePrice;
				if (pnl < 0)
				{
					_consecutiveLosses++;
					if (_consecutiveLosses <= MaxMartingale)
						_currentVolume *= LotCoefficient;
				}
				else
				{
					_consecutiveLosses = 0;
					_currentVolume = Volume > 0 ? Volume : 1;
				}

				BuyMarket(Math.Abs(Position) + _currentVolume);
				_entryPrice = candle.ClosePrice;
			}
			else if (Position == 0)
			{
				BuyMarket(_currentVolume);
				_entryPrice = candle.ClosePrice;
			}
		}
		else if (crossDown)
		{
			// Check for loss on closing long
			if (Position > 0)
			{
				var pnl = candle.ClosePrice - _entryPrice;
				if (pnl < 0)
				{
					_consecutiveLosses++;
					if (_consecutiveLosses <= MaxMartingale)
						_currentVolume *= LotCoefficient;
				}
				else
				{
					_consecutiveLosses = 0;
					_currentVolume = Volume > 0 ? Volume : 1;
				}

				SellMarket(Math.Abs(Position) + _currentVolume);
				_entryPrice = candle.ClosePrice;
			}
			else if (Position == 0)
			{
				SellMarket(_currentVolume);
				_entryPrice = candle.ClosePrice;
			}
		}

		_prevHistogram = histogram;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_macd = null;
		_prevHistogram = null;
		_currentVolume = 0;
		_consecutiveLosses = 0;
		_entryPrice = 0;
		_macdHistory.Clear();

		base.OnReseted();
	}
}