Стратегия является портом MetaTrader 5 советника «Macd diver and rsi» на платформу StockSharp. Сохранена исходная двухэтапная логика сигналов: RSI определяет области перекупленности или перепроданности, а гистограмма MACD подтверждает разворот импульса. Длинные и короткие сигналы настраиваются раздельно, что позволяет тонко адаптировать поведение под направление тренда.
Работа ведётся по одному потоку свечей (таймфрейм задаётся параметром). Сделки открываются рыночными заявками по инструменту стратегии. Обработка индикаторов выполнена через высокоуровневый API BindEx в соответствии с требованиями репозитория.
Логика торговли
Подготовка индикаторов
Создаются два экземпляра RSI — для длинных и коротких сценариев с собственными периодами и порогами.
Создаются два индикатора MovingAverageConvergenceDivergenceSignal, повторяющие настройки MACD для лонгов и шортов. Для подтверждения используется компонент гистограммы.
Условия входа
Лонг: если значение «длинного» RSI меньше либо равно порогу перепроданности и гистограмма MACD переходит из отрицательной зоны в положительную, открывается покупка. При наличии короткой позиции она закрывается и разворот выполняется той же заявкой.
Шорт: если значение «короткого» RSI больше либо равно порогу перекупленности и гистограмма MACD переходит из положительной зоны в отрицательную, открывается продажа. Активный лонг сначала закрывается.
Управление риском
После входа запоминается цена закрытия сигнальной свечи.
Уровни стоп-лосса и тейк-профита рассчитываются от этой цены по заданным значениям в пунктах для каждого направления.
Пересчёт пунктов выполняется через PriceStep инструмента; для инструментов с 3 или 5 знаками после запятой шаг умножается на 10, что соответствует привычной для MT5 трактовке «пункта».
На каждой завершённой свече проверяется, достигнут ли стоп или тейк. При пробое позиция закрывается рыночной заявкой.
Сопровождение позиции
Состояние позиции очищается, когда объём становится нулевым (по стопу/тейку или при развороте).
Дополнительное сопровождение (частичные фиксации, трейлинг) не используется — только фиксированные уровни.
Параметры
CandleType — таймфрейм свечей, по которым строятся сигналы.
LongRsiPeriod / ShortRsiPeriod — периоды RSI для длинных и коротких сценариев.
LongRsiThreshold / ShortRsiThreshold — пороги RSI, разрешающие вход (перепроданность для лонга, перекупленность для шорта).
LongMacdFastLength, LongMacdSlowLength, LongMacdSignalLength — периоды EMA в MACD для длинной стороны.
ShortMacdFastLength, ShortMacdSlowLength, ShortMacdSignalLength — параметры MACD для короткой стороны.
LongVolume, ShortVolume — объём сделки. При развороте к объёму добавляется абсолютная величина текущей позиции, чтобы одной заявкой закрыть старую и открыть новую.
LongStopLossPips, LongTakeProfitPips, ShortStopLossPips, ShortTakeProfitPips — расстояния до стоп-лосса и тейк-профита в пунктах (0 отключает уровень).
Дополнительные замечания
Для корректной работы нужен ненулевой PriceStep. При отсутствии шага используется запасное значение 0.0001, чтобы избежать деления на ноль.
Раздельные индикаторы для лонга и шорта позволяют использовать асимметричную фильтрацию — например, ужесточить условия для шорта и оставить лонг более чувствительным.
В код добавлены комментарии на английском языке, разъясняющие этапы работы стратегии и соответствующие требованиям проекта.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// MACD Diver And RSI strategy: RSI extremes + MACD histogram crossover.
/// Buys when RSI below 30 and MACD histogram crosses above zero.
/// Sells when RSI above 70 and MACD histogram crosses below zero.
/// </summary>
public class MacdDiverAndRsiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private decimal _prevHistogram;
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 MacdDiverAndRsiStrategy()
{
_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();
_prevHistogram = 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 histogram = macdMain - signal;
var rsi = rsiValue.ToDecimal();
if (_hasPrev)
{
if (_prevHistogram <= 0 && histogram > 0 && _prevRsi < 35 && Position <= 0)
BuyMarket();
else if (_prevHistogram >= 0 && histogram < 0 && _prevRsi > 65 && Position >= 0)
SellMarket();
}
_prevHistogram = histogram;
_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_diver_and_rsi_strategy(Strategy):
def __init__(self):
super(macd_diver_and_rsi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._rsi_period = self.Param("RsiPeriod", 14)
self._prev_histogram = 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_diver_and_rsi_strategy, self).OnReseted()
self._prev_histogram = 0.0
self._prev_rsi = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(macd_diver_and_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)
histogram = macd_main - signal
rsi_val = float(rsi_value)
if self._has_prev:
if self._prev_histogram <= 0 and histogram > 0 and self._prev_rsi < 35 and self.Position <= 0:
self.BuyMarket()
elif self._prev_histogram >= 0 and histogram < 0 and self._prev_rsi > 65 and self.Position >= 0:
self.SellMarket()
self._prev_histogram = histogram
self._prev_rsi = rsi_val
self._has_prev = True
def CreateClone(self):
return macd_diver_and_rsi_strategy()