Порт советника MetaTrader «Macd diver rsi mt4» на высокоуровневый API StockSharp.
Использует фильтры RSI и поиск дивергенций MACD для работы с разворотами на одном инструменте.
В рынке может находиться только одна позиция; новые сигналы рассматриваются только при нулевой позиции.
Логика сигналов
Каждая закрывшаяся свеча выбранного таймфрейма обновляет четыре связанных индикатора:
Два независимых RelativeStrengthIndex для зон перепроданности и перекупленности, значения берутся с предыдущей свечи.
Два MovingAverageConvergenceDivergence с настраиваемыми периодами EMA и сигнальной линии.
Условия для покупки
RSI предыдущей свечи должен быть ниже заданного порога перепроданности.
Последовательность значений MACD должна сформировать локальную впадину ниже динамического порога (эквивалент трёх пунктов
для текущего инструмента).
История MACD и цен просматривается в поисках предыдущей впадины и соответствующего ценового минимума. Дивергенция
подтверждается, когда MACD растёт, а цена обновляет минимум (обычная дивергенция), либо MACD снижается, а цена формирует
более высокий минимум (скрытая дивергенция), что полностью повторяет алгоритм MQL.
При выполнении условий и отсутствии открытой позиции отправляется рыночная покупка с отдельными параметрами объёма и
риск-менеджмента для лонга.
Условия для продажи зеркальны: используется порог перекупленности RSI и поиск пиков MACD, сравниваются предыдущие и
текущий ценовые максимумы.
После входа расстояния стоп-лосса и тейк-профита, заданные в пунктах, переводятся в абсолютную цену (с учётом правил point
format) и передаются в SetStopLoss / SetTakeProfit.
CandleType – таймфрейм, на основе которого выполняются все расчёты.
Особенности реализации
Порог дивергенции MACD рассчитывается по текущему шагу цены и равен трём пунктам, что соответствует значению 0.0003 в исходном
MQL.
История свечей, MACD и цен хранится в списках ограниченной длины (до 600 элементов), что позволяет повторить окна поиска
дивергенций без лишних выделений памяти.
Подписка SubscribeCandles(...).Bind(...) обновляет все индикаторы в одном обработчике, стратегия работает только с
завершёнными свечами — аналогично блоку Once per bar в оригинале.
Расстояния стоп-лосса и тейк-профита переводятся из пунктов в цену перед вызовами SetStopLoss и SetTakeProfit, тем самым
соблюдаются правила форматирования шага цены из исходного кода MQL.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// MACD Divergence RSI strategy: RSI filter + MACD signal line crossover.
/// Buys when RSI below threshold and MACD crosses above signal.
/// Sells when RSI above threshold and MACD crosses below signal.
/// </summary>
public class MacdDivergenceRsiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private decimal _prevMacd;
private decimal _prevSignal;
private decimal _prevRsi;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public MacdDivergenceRsiStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMacd = 0;
_prevSignal = 0;
_prevRsi = 0;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd = { ShortMa = { Length = 12 }, LongMa = { Length = 26 } },
SignalMa = { Length = 9 }
};
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.BindEx(macd, rsi, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue, IIndicatorValue rsiValue)
{
if (candle.State != CandleStates.Finished) return;
if (!macdValue.IsFinal || !rsiValue.IsFinal) return;
if (macdValue is not MovingAverageConvergenceDivergenceSignalValue typed) return;
if (typed.Macd is not decimal macdMain || typed.Signal is not decimal signal) return;
var rsi = rsiValue.ToDecimal();
if (_hasPrev)
{
if (_prevMacd <= _prevSignal && macdMain > signal && rsi < 40 && Position <= 0)
BuyMarket();
else if (_prevMacd >= _prevSignal && macdMain < signal && rsi > 60 && Position >= 0)
SellMarket();
}
_prevMacd = macdMain;
_prevSignal = signal;
_prevRsi = rsi;
_hasPrev = true;
}
}
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 MovingAverageConvergenceDivergenceSignal, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class macd_divergence_rsi_strategy(Strategy):
def __init__(self):
super(macd_divergence_rsi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._rsi_period = self.Param("RsiPeriod", 14)
self._prev_macd = 0.0
self._prev_signal = 0.0
self._prev_rsi = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
def OnReseted(self):
super(macd_divergence_rsi_strategy, self).OnReseted()
self._prev_macd = 0.0
self._prev_signal = 0.0
self._prev_rsi = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(macd_divergence_rsi_strategy, self).OnStarted2(time)
self._has_prev = False
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = 12
macd.Macd.LongMa.Length = 26
macd.SignalMa.Length = 9
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(macd, rsi, self._process_candle).Start()
def _process_candle(self, candle, macd_value, rsi_value):
if candle.State != CandleStates.Finished:
return
if not macd_value.IsFinal or not rsi_value.IsFinal:
return
if macd_value.Macd is None or macd_value.Signal is None:
return
macd_main = float(macd_value.Macd)
signal = float(macd_value.Signal)
rsi_val = float(rsi_value)
if self._has_prev:
if self._prev_macd <= self._prev_signal and macd_main > signal and rsi_val < 40 and self.Position <= 0:
self.BuyMarket()
elif self._prev_macd >= self._prev_signal and macd_main < signal and rsi_val > 60 and self.Position >= 0:
self.SellMarket()
self._prev_macd = macd_main
self._prev_signal = signal
self._prev_rsi = rsi_val
self._has_prev = True
def CreateClone(self):
return macd_divergence_rsi_strategy()