RSI & CCI Divergence — адаптация советника RSI&CCI_DIVERGENCE.mq4 (MQL ID 22266) под инфраструктуру StockSharp. Стратегия ищет бычьи и медвежьи расхождения между экстремумами цены и осцилляторов CCI/RSI, подтверждает тренд линейно-взвешенными скользящими средними, затем проверяет согласованность MACD на трёх таймфреймах и оценивает силу импульса с помощью индикатора Momentum на старшем горизонте. Дополнительно предусмотрены опциональные абсолютные уровни стоп-лосса и тейк-профита.
Реализация построена на высокоуровневом API StockSharp: индикаторы привязываются непосредственно к подпискам на свечи, что избавляет от ручного доступа к их значениям и обеспечивает работу в режиме потоковой обработки данных.
Логика работы
Фильтр тренда — две LWMA на основном таймфрейме задают направление (бычий режим, если быстрая LWMA выше медленной; медвежий — наоборот).
Выявление расхождений — последняя закрытая свеча сравнивается с CandlesToRetrace предыдущими свечами; фиксируются ситуации, когда индикатор делает более высокий минимум (или более низкий максимум), а цена — противоположное движение.
Подтверждение MACD — индикатор MACD (12/26/9 по умолчанию) рассчитывается на основном, дополнительном и макро таймфреймах; для открытия позиции необходимо, чтобы на всех горизонтах MACD находился по одну сторону от сигнальной линии.
Проверка импульса — на старшем таймфрейме рассчитывается Momentum. Абсолютное отклонение нескольких последних значений от уровня 100 должно превышать заданные пороги MomentumBuyThreshold/MomentumSellThreshold.
Ценовые условия — чтобы повторить оригинальный советник, дополнительно проверяются отношения последних максимумов/минимумов (аналог условий Low[2] < High[1] и Low[1] < High[2]).
Исполнение — при выполнении всех фильтров стратегия отправляет рыночный ордер, увеличивая объём на величину текущей позиции, что позволяет мгновенно переворачиваться.
Риск-менеджмент — если заданы значения StopLoss или TakeProfit, при достижении уровня формируется встречный рыночный ордер на полный объём позиции.
Параметры
Параметр
Значение по умолчанию
Описание
FastMaLength
6
Период быстрой LWMA.
SlowMaLength
85
Период медленной LWMA.
CciLength
14
Период осциллятора CCI.
RsiLength
14
Период осциллятора RSI.
CandlesToRetrace
10
Количество завершённых свечей для поиска расхождений.
MacdFastPeriod
12
Быстрый период MACD.
MacdSlowPeriod
26
Медленный период MACD.
MacdSignalPeriod
9
Период сигнальной линии MACD.
MomentumLength
14
Период индикатора Momentum.
MomentumBuyThreshold
0.3
Минимальное отклонение от 100 для подтверждения бычьего импульса.
MomentumSellThreshold
0.3
Минимальное отклонение от 100 для подтверждения медвежьего импульса.
StopLoss
0
Абсолютный стоп-лосс (0 — выключен).
TakeProfit
0
Абсолютный тейк-профит (0 — выключен).
CandleType
15 минут
Основной таймфрейм для анализа.
MomentumCandleType
1 час
Таймфрейм для расчёта Momentum.
HigherMacdCandleType
1 час
Дополнительный таймфрейм MACD.
MacroMacdCandleType
30 дней
Макро таймфрейм MACD (при необходимости измените под доступные данные).
Рекомендации по использованию
Убедитесь, что поставщик данных предоставляет все запрошенные таймфреймы; при отсутствии свечей смените тип данных в параметрах.
Значения стоп-лосса/тейк-профита по умолчанию равны нулю, чтобы повторить поведение оригинального советника, использовавшего тралы и контроль по эквити. При желании задайте положительные значения.
Momentum проверяет отклонение относительно уровня 100. Если используется другая методика нормализации, скорректируйте пороги MomentumBuyThreshold и MomentumSellThreshold.
Стратегия работает исключительно с рыночными ордерами (как при входе, так и при выходе).
Особенности портирования
Использована привязка индикаторов через SubscribeCandles().Bind(...); никаких вызовов GetValue().
Функционал управления капиталом (equity stop, trailing), уведомления и почтовые рассылки из 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;
public class RsiCciDivergenceStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _cciPeriod;
private decimal? _prevRsi, _prevCci;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
public RsiCciDivergenceStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14).SetGreaterThanZero().SetDisplay("RSI Period", "RSI lookback", "Indicators");
_cciPeriod = Param(nameof(CciPeriod), 14).SetGreaterThanZero().SetDisplay("CCI Period", "CCI lookback", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = null;
_prevCci = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = null; _prevCci = null;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var cci = new CommodityChannelIndex { Length = CciPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, cci, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal rsiVal, decimal cciVal)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) { _prevRsi = rsiVal; _prevCci = cciVal; return; }
if (_prevRsi == null || _prevCci == null) { _prevRsi = rsiVal; _prevCci = cciVal; return; }
var buySignal = (_prevRsi.Value < 30m && rsiVal >= 30m) || (_prevCci.Value < -100m && cciVal >= -100m);
var sellSignal = (_prevRsi.Value > 70m && rsiVal <= 70m) || (_prevCci.Value > 100m && cciVal <= 100m);
_prevRsi = rsiVal; _prevCci = cciVal;
if (buySignal && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
else if (sellSignal && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
}
}
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 RelativeStrengthIndex, CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class rsi_cci_divergence_strategy(Strategy):
def __init__(self):
super(rsi_cci_divergence_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI lookback", "Indicators")
self._cci_period = self.Param("CciPeriod", 14) \
.SetDisplay("CCI Period", "CCI lookback", "Indicators")
self._prev_rsi = None
self._prev_cci = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@property
def CciPeriod(self):
return self._cci_period.Value
def OnReseted(self):
super(rsi_cci_divergence_strategy, self).OnReseted()
self._prev_rsi = None
self._prev_cci = None
def OnStarted2(self, time):
super(rsi_cci_divergence_strategy, self).OnStarted2(time)
self._prev_rsi = None
self._prev_cci = None
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
cci = CommodityChannelIndex()
cci.Length = self.CciPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, cci, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle, rsi_value, cci_value):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_value)
cv = float(cci_value)
if self._prev_rsi is None or self._prev_cci is None:
self._prev_rsi = rv
self._prev_cci = cv
return
buy_signal = (self._prev_rsi < 30.0 and rv >= 30.0) or (self._prev_cci < -100.0 and cv >= -100.0)
sell_signal = (self._prev_rsi > 70.0 and rv <= 70.0) or (self._prev_cci > 100.0 and cv <= 100.0)
self._prev_rsi = rv
self._prev_cci = cv
if buy_signal and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif sell_signal and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return rsi_cci_divergence_strategy()