Стратегия переносит советник MetaTrader 5 «Divergence EA pip sl tp» в экосистему StockSharp. Алгоритм отслеживает классические дивергенции между ценой и гистограммой MACD и подтверждает сигналы фильтром стохастического осциллятора, прежде чем открыть разворотную позицию.
Логика торговли
Подписка на свечи таймфрейма, заданного параметром CandleType.
На каждой завершённой свече рассчитываются гистограмма MACD (MACD - Signal) и значения стохастика %K/%D.
Хранятся последние два экстремума цены и гистограммы для определения дивергенций.
Медвежья дивергенция — новый максимум цены при меньшем пике гистограммы и %K выше StochasticUpperLevel инициирует короткую позицию или разворот из лонга.
Бычья дивергенция — новый минимум цены при более высокой впадине гистограммы и %K ниже StochasticLowerLevel открывает или переворачивает стратегию в лонг.
Необязательные TakeProfitSteps и StopLossSteps преобразуются в шаги цены и запускают встроенную защиту при старте стратегии.
Особенности реализации
Используется высокоуровневый API StockSharp с одной подпиской на свечи, связанной с индикаторами MovingAverageConvergenceDivergenceSignal и StochasticOscillator.
Состояние дивергенции хранится во внутренних переменных без обращения к GetValue, что соответствует требованиям конвертации.
При наличии области графика отображаются свечи, MACD и стохастик, а также сделки стратегии.
При подтверждённом сигнале стратегия переворачивает позицию, добавляя абсолютное значение текущего объёма к базовому Volume.
Параметры
Параметр
Описание
Значение по умолчанию
CandleType
Таймфрейм для расчёта дивергенций.
Свечи 1 часа
MacdFastLength, MacdSlowLength, MacdSignalLength
Длины EMA индикатора MACD.
12 / 26 / 9
MacdDivergenceThreshold
Минимальная разница гистограммы между экстремумами для подтверждения дивергенции.
0.0005
StochasticLength
Быстрый период %K стохастика.
50
StochasticSlowK, StochasticSlowD
Дополнительные сглаживания %K и %D.
9 / 9
StochasticUpperLevel, StochasticLowerLevel
Уровни перекупленности и перепроданности для фильтрации сигналов.
80 / 20
TakeProfitSteps, StopLossSteps
Необязательные расстояния защитных ордеров в шагах цены (0 отключает уровень).
50
Использование
Привяжите стратегию к подключению StockSharp с инструментом, поддерживающим выбранный таймфрейм.
Настройте торговый объём через свойство Volume и при необходимости скорректируйте параметры индикаторов.
Запустите стратегию — при выполнении условий дивергенции и стохастика ордера будут выставляться автоматически.
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 MACD Stochastic" MetaTrader expert.
/// Uses MACD histogram divergence (price vs histogram) with RSI confirmation.
/// MACD is computed manually from two EMAs.
/// </summary>
public class DivergenceMacdStochasticStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _macdFast;
private readonly StrategyParam<int> _macdSlow;
private readonly StrategyParam<int> _rsiPeriod;
// Manual EMA for MACD
private decimal _fastEma;
private decimal _slowEma;
private bool _emaInitialized;
private int _barCount;
private decimal _fastMultiplier;
private decimal _slowMultiplier;
private readonly decimal[] _macdWindow = new decimal[DivergenceLookback];
private readonly decimal[] _priceWindow = new decimal[DivergenceLookback];
private int _windowCount;
private int _windowIndex;
private const int DivergenceLookback = 10;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int MacdFast
{
get => _macdFast.Value;
set => _macdFast.Value = value;
}
public int MacdSlow
{
get => _macdSlow.Value;
set => _macdSlow.Value = value;
}
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
public DivergenceMacdStochasticStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for divergence detection", "General");
_macdFast = Param(nameof(MacdFast), 20)
.SetGreaterThanZero()
.SetDisplay("MACD Fast", "Fast EMA length", "Indicators");
_macdSlow = Param(nameof(MacdSlow), 50)
.SetGreaterThanZero()
.SetDisplay("MACD Slow", "Slow EMA length", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period for confirmation", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_windowCount = 0;
_windowIndex = 0;
_emaInitialized = false;
_barCount = 0;
_fastMultiplier = 2m / (MacdFast + 1);
_slowMultiplier = 2m / (MacdSlow + 1);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
_barCount++;
// Manual EMA computation
if (!_emaInitialized)
{
_fastEma = close;
_slowEma = close;
_emaInitialized = true;
}
else
{
_fastEma = close * _fastMultiplier + _fastEma * (1 - _fastMultiplier);
_slowEma = close * _slowMultiplier + _slowEma * (1 - _slowMultiplier);
}
if (_barCount < MacdSlow)
return;
var macdLine = _fastEma - _slowEma;
_macdWindow[_windowIndex] = macdLine;
_priceWindow[_windowIndex] = close;
_windowIndex = (_windowIndex + 1) % DivergenceLookback;
if (_windowCount < DivergenceLookback)
_windowCount++;
if (_windowCount < DivergenceLookback)
return;
var volume = Volume;
if (volume <= 0)
volume = 1;
var oldestIndex = _windowIndex;
var newestIndex = (_windowIndex + DivergenceLookback - 1) % DivergenceLookback;
var oldMacd = _macdWindow[oldestIndex];
var newMacd = _macdWindow[newestIndex];
var oldPrice = _priceWindow[oldestIndex];
var newPrice = _priceWindow[newestIndex];
var minPriceMove = oldPrice * 0.005m;
// Bullish divergence: price makes lower low but MACD makes higher low.
var bullishDiv = newPrice < oldPrice - minPriceMove && newMacd > oldMacd;
// Bearish divergence: price makes higher high but MACD makes lower high.
var bearishDiv = newPrice > oldPrice + minPriceMove && newMacd < oldMacd;
if (bullishDiv)
{
if (Position <= 0)
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
}
else if (bearishDiv)
{
if (Position >= 0)
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
}
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fastEma = 0;
_slowEma = 0;
_emaInitialized = false;
_barCount = 0;
_fastMultiplier = 0;
_slowMultiplier = 0;
Array.Clear(_macdWindow, 0, _macdWindow.Length);
Array.Clear(_priceWindow, 0, _priceWindow.Length);
_windowCount = 0;
_windowIndex = 0;
}
}
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.Strategies import Strategy
class divergence_macd_stochastic_strategy(Strategy):
DIVERGENCE_LOOKBACK = 10
def __init__(self):
super(divergence_macd_stochastic_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._macd_fast = self.Param("MacdFast", 20)
self._macd_slow = self.Param("MacdSlow", 50)
self._fast_ema = 0.0
self._slow_ema = 0.0
self._ema_initialized = False
self._bar_count = 0
self._fast_multiplier = 0.0
self._slow_multiplier = 0.0
self._macd_window = []
self._price_window = []
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def MacdFast(self):
return self._macd_fast.Value
@MacdFast.setter
def MacdFast(self, value):
self._macd_fast.Value = value
@property
def MacdSlow(self):
return self._macd_slow.Value
@MacdSlow.setter
def MacdSlow(self, value):
self._macd_slow.Value = value
def OnReseted(self):
super(divergence_macd_stochastic_strategy, self).OnReseted()
self._fast_ema = 0.0
self._slow_ema = 0.0
self._ema_initialized = False
self._bar_count = 0
self._macd_window = []
self._price_window = []
def OnStarted2(self, time):
super(divergence_macd_stochastic_strategy, self).OnStarted2(time)
self._fast_ema = 0.0
self._slow_ema = 0.0
self._ema_initialized = False
self._bar_count = 0
self._fast_multiplier = 2.0 / (self.MacdFast + 1)
self._slow_multiplier = 2.0 / (self.MacdSlow + 1)
self._macd_window = []
self._price_window = []
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
self._bar_count += 1
if not self._ema_initialized:
self._fast_ema = close
self._slow_ema = close
self._ema_initialized = True
else:
self._fast_ema = close * self._fast_multiplier + self._fast_ema * (1 - self._fast_multiplier)
self._slow_ema = close * self._slow_multiplier + self._slow_ema * (1 - self._slow_multiplier)
if self._bar_count < self.MacdSlow:
return
macd_line = self._fast_ema - self._slow_ema
self._macd_window.append(macd_line)
self._price_window.append(close)
while len(self._macd_window) > self.DIVERGENCE_LOOKBACK:
self._macd_window.pop(0)
self._price_window.pop(0)
if len(self._macd_window) < self.DIVERGENCE_LOOKBACK:
return
old_macd = self._macd_window[0]
new_macd = self._macd_window[-1]
old_price = self._price_window[0]
new_price = self._price_window[-1]
min_price_move = old_price * 0.005
# Bullish divergence: price makes lower low but MACD makes higher low
bullish_div = new_price < old_price - min_price_move and new_macd > old_macd
# Bearish divergence: price makes higher high but MACD makes lower high
bearish_div = new_price > old_price + min_price_move and new_macd < old_macd
if bullish_div:
if self.Position <= 0:
self.BuyMarket()
elif bearish_div:
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return divergence_macd_stochastic_strategy()