Стратегия переносит советник MetaTrader «Divergence + ema + rsi close buy only» на высокоуровневый API StockSharp. Основная торговля ведётся на 5-минутных свечах, а фильтры опираются на данные H1 и D1. Все сделки — только на покупку. Вход формируется при наличии бычьей дивергенции MACD, подтверждённой пересечением стохастика на часовом графике в зоне перепроданности и восходящей структурой EMA на дневном таймфрейме. Выход реализуется через достижение заданного уровня RSI либо за счёт защитных уровней StartProtection.
Логика торговли
Фильтр тренда по дневным EMA
EMA(9) на D1 должна быть выше EMA(20), что подтверждает восходящий тренд.
Текущая закрытая 5-минутная свеча должна находиться ниже EMA(9) на D1 — стратегия ищет вход «из отката».
Подтверждение стохастиком на H1
Последнее завершённое значение %K должно лежать между StochasticLowerBound (по умолчанию 0) и StochasticUpperBound (по умолчанию 40).
На предыдущем часовом баре %K должен пересечь %D снизу вверх (текущее %K > %D при условии, что предыдущее %K ≤ %D).
Триггер дивергенции MACD (M5)
Гистограмма MACD (разность линии MACD и сигнальной линии) должна увеличиться как минимум на MacdThreshold, а закрытие 5-минутной свечи сформировать более низкий минимум относительно предыдущей свечи.
Исполнение входа
При выполнении всех фильтров и отсутствии открытой длинной позиции стратегия выставляет рыночную заявку на покупку. Если неожиданно присутствует короткая позиция, объём заявки увеличивается для её закрытия и открытия лонга.
Правила выхода
Длинная позиция закрывается, когда RSI на M5 поднимается выше RsiExitLevel (77 по умолчанию).
При положительных значениях StopLossPips и TakeProfitPips активируется StartProtection, который переводит значения из пунктов в ценовое расстояние и сопровождает позицию.
Управление ордерами
Перед отправкой нового рыночного ордера снимаются все активные заявки, чтобы избежать дублирования.
Рабочий объём задаётся параметром TradeVolume и может оптимизироваться.
Параметры
Параметр
Описание
Значение по умолчанию
CandleType
Основной таймфрейм для расчётов MACD, RSI и сделок.
Подписка осуществляется на три потока: основной таймфрейм, часовой и дневной. Каждый поток связан со своими индикаторами через 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();
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class divergence_ema_rsi_close_buy_only_strategy(Strategy):
def __init__(self):
super(divergence_ema_rsi_close_buy_only_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30)))
self._ema_period = self.Param("EmaPeriod", 20)
self._rsi_period = self.Param("RsiPeriod", 7)
self._rsi_entry = self.Param("RsiEntry", 35.0)
self._rsi_exit = self.Param("RsiExit", 65.0)
self._prev_rsi = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def RsiEntry(self):
return self._rsi_entry.Value
@RsiEntry.setter
def RsiEntry(self, value):
self._rsi_entry.Value = value
@property
def RsiExit(self):
return self._rsi_exit.Value
@RsiExit.setter
def RsiExit(self, value):
self._rsi_exit.Value = value
def OnReseted(self):
super(divergence_ema_rsi_close_buy_only_strategy, self).OnReseted()
self._prev_rsi = None
def OnStarted2(self, time):
super(divergence_ema_rsi_close_buy_only_strategy, self).OnStarted2(time)
self._prev_rsi = None
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, rsi, self._process_candle).Start()
def _process_candle(self, candle, ema_value, rsi_value):
if candle.State != CandleStates.Finished:
return
ema_val = float(ema_value)
rsi_val = float(rsi_value)
close = float(candle.ClosePrice)
if self._prev_rsi is None:
self._prev_rsi = rsi_val
return
rsi_entry = float(self.RsiEntry)
rsi_exit = float(self.RsiExit)
# Exit: RSI crosses above exit level
if self.Position > 0 and self._prev_rsi < rsi_exit and rsi_val >= rsi_exit:
self.SellMarket()
# Entry: RSI crosses below entry level and price is near/below EMA
if self.Position <= 0 and self._prev_rsi > rsi_entry and rsi_val <= rsi_entry and close <= ema_val * 1.005:
self.BuyMarket()
self._prev_rsi = rsi_val
def CreateClone(self):
return divergence_ema_rsi_close_buy_only_strategy()