Открыть на GitHub

Стратегия AG Dual MACD

Обзор

Данная стратегия представляет собой порт MetaTrader 4 эксперта AG.mq4 на платформу StockSharp. Алгоритм использует две пары индикаторов MACD с различными параметрами: основная пара формирует торговые сигналы, а вторичная (масштабированная) фильтрует входы и управляет выходами. Обработка ведётся только по закрытым свечам, что полностью повторяет оригинальное поведение советника.

Логика торговли

  • Индикаторы
    • Основной MACD: быстрая EMA = FastEmaLength, медленная EMA = SlowEmaLength, сигнальная SMA = SignalSmaLength.
    • Второй MACD: быстрая EMA = SlowEmaLength * 2, медленная EMA = FastEmaLength * 2, сигнальная SMA = SignalSmaLength * 2.
  • Условия для покупки
    • Главная линия основного MACD находится выше сигнальной.
    • Сигнальная линия основного MACD меньше нуля.
    • Главная линия вторичного MACD выше сигнальной.
    • Сигнальная линия вторичного MACD меньше нуля.
  • Условия для продажи
    • Главная линия основного MACD ниже сигнальной.
    • Сигнальная линия основного MACD больше нуля.
    • Главная линия вторичного MACD ниже сигнальной.
    • Сигнальная линия вторичного MACD больше нуля.
  • Выходы
    • Длинные позиции закрываются, когда вторичный MACD переходит в медвежий режим, а сигнальная линия основного MACD остаётся выше нуля.
    • Короткие позиции закрываются, когда вторичный MACD становится бычьим, а сигнальная линия основного MACD остаётся ниже нуля.
  • Стратегия игнорирует незавершённые свечи, чтобы избежать перерисовки.

Управление позицией

  • Все сделки выполняются рыночными ордерами объёмом OrderVolume.
  • Параметр MaxOpenOrders соответствует входному ORDER в MQL4 и ограничивает суммарное количество активных ордеров и позиций (значение 0 снимает ограничение).
  • В методе OnStarted вызывается StartProtection(), чтобы задействовать модуль защиты StockSharp.

Параметры

Имя Описание
OrderVolume Базовый объём для новых сделок.
FastEmaLength Период быстрой EMA основного MACD.
SlowEmaLength Период медленной EMA основного MACD.
SignalSmaLength Период сигнальной SMA для обеих пар MACD.
MaxOpenOrders Максимальное число активных ордеров и открытых позиций, 0 — без ограничений.
CandleType Тип или таймфрейм свечей, используемых для расчётов.

Примечания

  • Во второй паре MACD сохранён оригинальный порядок «быстрая/медленная» EMA, даже если быстрая становится длиннее медленной, чтобы поведение совпадало с MQL-версией.
  • Стратегия не использует отложенные ордера — вход и выход выполняются по рынку при появлении сигнала.
  • Дополнительные стоп-лоссы и тейк-профиты не устанавливаются, поскольку исходный советник полагался на разворот сигналов.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// AG MACD Dual strategy - MACD histogram crossover with EMA trend filter.
/// Buys when MACD histogram crosses above zero while price is above EMA.
/// Sells when MACD histogram crosses below zero while price is below EMA.
/// </summary>
public class AgMacdDualStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevMacd;
	private decimal _prevSignal;
	private bool _hasPrev;
	private decimal _currentEma;

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

	public AgMacdDualStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 50)
			.SetDisplay("EMA Period", "EMA trend filter", "Indicators");

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

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); _prevMacd = 0m; _prevSignal = 0m; _hasPrev = false; _currentEma = 0m; }

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

		_hasPrev = false;

		var macd = new MovingAverageConvergenceDivergenceSignal();
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(macd, ProcessMacd)
			.Bind(ema, ProcessEma)
			.Start();
	}

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

		_currentEma = ema;
	}

	private void ProcessMacd(ICandleMessage candle, IIndicatorValue value)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!value.IsFinal || value.IsEmpty)
			return;

		var macdVal = value as MovingAverageConvergenceDivergenceSignalValue;
		if (macdVal == null)
			return;

		var macdLine = macdVal.Macd;
		var signalLine = macdVal.Signal;

		if (macdLine == null || signalLine == null)
			return;

		var histogram = macdLine.Value - signalLine.Value;

		if (!_hasPrev)
		{
			_prevMacd = macdLine.Value;
			_prevSignal = signalLine.Value;
			_hasPrev = true;
			return;
		}

		var prevHist = _prevMacd - _prevSignal;
		var close = candle.ClosePrice;

		if (prevHist <= 0 && histogram > 0 && close > _currentEma && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (prevHist >= 0 && histogram < 0 && close < _currentEma && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevMacd = macdLine.Value;
		_prevSignal = signalLine.Value;
	}
}