Открыть на GitHub

Divergence + EMA + RSI Close Buy Only

Обзор

Стратегия переносит советник MetaTrader «Divergence + ema + rsi close buy only» на высокоуровневый API StockSharp. Основная торговля ведётся на 5-минутных свечах, а фильтры опираются на данные H1 и D1. Все сделки — только на покупку. Вход формируется при наличии бычьей дивергенции MACD, подтверждённой пересечением стохастика на часовом графике в зоне перепроданности и восходящей структурой EMA на дневном таймфрейме. Выход реализуется через достижение заданного уровня RSI либо за счёт защитных уровней StartProtection.

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

  1. Фильтр тренда по дневным EMA

    • EMA(9) на D1 должна быть выше EMA(20), что подтверждает восходящий тренд.
    • Текущая закрытая 5-минутная свеча должна находиться ниже EMA(9) на D1 — стратегия ищет вход «из отката».
  2. Подтверждение стохастиком на H1

    • Последнее завершённое значение %K должно лежать между StochasticLowerBound (по умолчанию 0) и StochasticUpperBound (по умолчанию 40).
    • На предыдущем часовом баре %K должен пересечь %D снизу вверх (текущее %K > %D при условии, что предыдущее %K ≤ %D).
  3. Триггер дивергенции MACD (M5)

    • Гистограмма MACD (разность линии MACD и сигнальной линии) должна увеличиться как минимум на MacdThreshold, а закрытие 5-минутной свечи сформировать более низкий минимум относительно предыдущей свечи.
  4. Исполнение входа

    • При выполнении всех фильтров и отсутствии открытой длинной позиции стратегия выставляет рыночную заявку на покупку. Если неожиданно присутствует короткая позиция, объём заявки увеличивается для её закрытия и открытия лонга.
  5. Правила выхода

    • Длинная позиция закрывается, когда RSI на M5 поднимается выше RsiExitLevel (77 по умолчанию).
    • При положительных значениях StopLossPips и TakeProfitPips активируется StartProtection, который переводит значения из пунктов в ценовое расстояние и сопровождает позицию.
  6. Управление ордерами

    • Перед отправкой нового рыночного ордера снимаются все активные заявки, чтобы избежать дублирования.
    • Рабочий объём задаётся параметром TradeVolume и может оптимизироваться.

Параметры

Параметр Описание Значение по умолчанию
CandleType Основной таймфрейм для расчётов MACD, RSI и сделок. 5 минут
HourTimeFrame Таймфрейм стохастика. 1 час
DayTimeFrame Таймфрейм EMA фильтра тренда. 1 день
MacdFastPeriod / MacdSlowPeriod / MacdSignalPeriod Настройка MACD на M5. 6 / 13 / 5
MacdThreshold Минимальный прирост гистограммы для фиксации дивергенции. 0.0003
DailyFastPeriod / DailySlowPeriod Периоды EMA на D1. 9 / 20
StochasticKPeriod / StochasticDPeriod / StochasticSlowing Конфигурация стохастика на H1. 30 / 5 / 9
StochasticUpperBound / StochasticLowerBound Допустимый диапазон %K. 40 / 0
RsiPeriod Период RSI на M5. 7
RsiExitLevel Значение RSI, при котором позиция закрывается. 77
TradeVolume Базовый объём ордеров. 0.01
StopLossPips Стоп-лосс в пунктах (0 отключает). 100
TakeProfitPips Тейк-профит в пунктах (0 отключает). 200

Примечания

  • Подписка осуществляется на три потока: основной таймфрейм, часовой и дневной. Каждый поток связан со своими индикаторами через Bind/BindEx.
  • Обрабатываются только завершённые свечи, что соответствует использованию смещения (shift) в исходном MQL.
  • Проверка дивергенции MACD повторяет поведение конструктора fxDreema: текущая свеча сравнивается с предыдущей по цене закрытия и значению гистограммы.
  • Управление защитными ордерами делегировано StartProtection, поэтому стратегия корректно работает как в тестах, так и в онлайне без отдельного сопровождения стопов и тейков.
using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Simplified from "Divergence EMA RSI Close Buy Only" MetaTrader expert.
/// Long-only strategy: buys when price pulls back below fast EMA with RSI oversold,
/// exits when RSI reaches overbought level.
/// </summary>
public class DivergenceEmaRsiCloseBuyOnlyStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _rsiEntry;
	private readonly StrategyParam<decimal> _rsiExit;

	private ExponentialMovingAverage _ema;
	private RelativeStrengthIndex _rsi;
	private decimal? _prevRsi;

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

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

	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}

	public decimal RsiEntry
	{
		get => _rsiEntry.Value;
		set => _rsiEntry.Value = value;
	}

	public decimal RsiExit
	{
		get => _rsiExit.Value;
		set => _rsiExit.Value = value;
	}

	public DivergenceEmaRsiCloseBuyOnlyStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for signals", "General");

		_emaPeriod = Param(nameof(EmaPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA period for trend filter", "Indicators");

		_rsiPeriod = Param(nameof(RsiPeriod), 7)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI period for entry/exit", "Indicators");

		_rsiEntry = Param(nameof(RsiEntry), 35m)
			.SetDisplay("RSI Entry", "RSI level to enter long", "Signals");

		_rsiExit = Param(nameof(RsiExit), 65m)
			.SetDisplay("RSI Exit", "RSI level to exit long", "Signals");
	}

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

		_ema = new ExponentialMovingAverage { Length = EmaPeriod };
		_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		_prevRsi = null;

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

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

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

		if (!_ema.IsFormed || !_rsi.IsFormed)
		{
			_prevRsi = rsiValue;
			return;
		}

		if (_prevRsi is null)
		{
			_prevRsi = rsiValue;
			return;
		}

		var volume = Volume;
		if (volume <= 0)
			volume = 1;
		var close = candle.ClosePrice;

		// Exit: RSI crosses above exit level
		if (Position > 0 && _prevRsi.Value < RsiExit && rsiValue >= RsiExit)
		{
			SellMarket(Position);
		}

		// Entry: RSI crosses below entry level and price is near/below EMA (buy the dip)
		if (Position <= 0 && _prevRsi.Value > RsiEntry && rsiValue <= RsiEntry && close <= emaValue * 1.005m)
		{
			if (Position < 0)
				BuyMarket(Math.Abs(Position));

			BuyMarket(volume);
		}

		_prevRsi = rsiValue;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_ema = null;
		_rsi = null;
		_prevRsi = null;

		base.OnReseted();
	}
}