Главная
/
Примеры стратегий
Открыть на GitHub
Стратегия Cryptocurrency Divergence
Обзор
Cryptocurrency Divergence Strategy выявляет классические дивергенции между ценой и индексом относительной силы (RSI), одновременно подтверждая направление тренда с помощью скользящих средних и MACD. Оригинальный советник MetaTrader использовал многопериодный анализ импульса, сложное сопровождение позиций и денежные фильтры. Порт на StockSharp сохраняет задумку:
Определяет бычью дивергенцию, когда цена формирует более низкий минимум, а RSI — более высокий минимум.
Определяет медвежью дивергенцию, когда цена обновляет максимум, но RSI показывает более низкий максимум.
Проверяет сигнал фильтром из быстрой/медленной SMA и сравнением линии MACD с сигнальной линией.
Управляет позицией через настраиваемые стоп-лосс, тейк-профит, перевод в безубыток и трейлинг, выраженные в шагах цены.
Стратегия ориентирована на криптовалютные рынки, однако подходит для любого волатильного инструмента с четко выраженными свингами.
Индикаторы
Простая скользящая средняя (SMA) : быстрая и медленная средние формируют фильтр тренда.
RSI : фиксирует значения импульса на экстремумах для оценки дивергенции.
MACD : подтверждает, что импульс поддерживает найденную дивергенцию.
Все индикаторы подключаются через высокоуровневый API, поэтому буферы и ручные расчеты не требуются.
Логика торговли
Подписываемся на выбранный тип свечей и на каждой закрытой свече рассчитываем SMA, RSI и MACD.
Отслеживаем последние экстремумы цены и соответствующие им значения RSI. Данные обновляются только при формировании новых максимумов или минимумов.
Бычья дивергенция появляется, когда минимум цены обновляется, RSI растет, быстрая SMA находится выше медленной, MACD выше сигнальной линии, а RSI остается ниже бычьего порога (по умолчанию 45).
Медвежья дивергенция требует нового максимума цены, падения RSI на экстремуме, расположения быстрой SMA ниже медленной, линии MACD ниже сигнальной и значения RSI выше медвежьего порога (по умолчанию 55).
В рынке одновременно может находиться только одна чистая позиция. При смене сигнала стратегия закрывает текущую позицию и сразу переходит в противоположную.
Управление рисками
Объем сделки : задается пользователем и применяется ко всем рыночным ордерам.
Стоп-лосс и тейк-профит : рассчитываются в шагах цены и выставляются после фактического исполнения.
Перевод в безубыток : при достижении заданной прибыли стоп переносится к цене входа с заданным отступом.
Трейлинг-стоп : следует за ценой на фиксированном расстоянии и имеет приоритет над исходным стопом после активации.
Все защитные механизмы проверяются на закрытии свечи, что обеспечивает соответствие между тестами и реальной торговлей.
Параметры
Имя
Описание
CandleType
Тип свечей для анализа (по умолчанию 15 минут).
TradeVolume
Объем входа в позицию.
FastMaLength / SlowMaLength
Периоды быстрой и медленной SMA.
RsiLength
Период RSI.
RsiBullishLevel / RsiBearishLevel
Пороговые значения RSI для подтверждения бычьей и медвежьей дивергенции.
MacdShortLength / MacdLongLength / MacdSignalLength
Настройки MACD.
StopLossPoints / TakeProfitPoints
Расстояние до стопа и цели в шагах цены.
EnableBreakEven, BreakEvenTrigger, BreakEvenOffset
Управление переводом в безубыток.
EnableTrailing, TrailDistance
Управление трейлинг-стопом.
Параметры объявлены через StrategyParam<T>, что позволяет оптимизировать стратегию в дизайнере StockSharp.
Рекомендации по использованию
Подключите стратегию к выбранному инструменту и убедитесь, что для него заданы PriceStep и Board, иначе расчет защитных уровней невозможен.
Подберите таймфрейм свечей в соответствии с торгуемым рынком (15 минут, час и т.д.), так как распознавание дивергенций зависит от масштаба.
Настройте расстояния стопов и целей под среднюю волатильность инструмента. Для пар с пятью знаками после запятой потребуются увеличенные значения.
Активируйте перевод в безубыток и трейлинг только после проверки на истории, чтобы избежать преждевременного выхода из прибыльных сделок.
Используйте дизайнер StockSharp для визуального контроля индикаторов и исполнения ордеров.
Отличия от версии MQL
Денежные тралы и останов по капиталу заменены на универсальное управление стопами по шагам цены.
Многопериодные проверки импульса заменены на подтверждение по MACD в одном таймфрейме для повышения прозрачности.
Удалены уведомления по почте/пушу, которые могут быть реализованы сторонними сервисами StockSharp.
Несмотря на упрощения, стратегия сохраняет основную идею поиска дивергенций и защиту позиций исходного советника.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class CryptocurrencyDivergenceStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public CryptocurrencyDivergenceStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class cryptocurrency_divergence_strategy(Strategy):
def __init__(self):
super(cryptocurrency_divergence_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(cryptocurrency_divergence_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(cryptocurrency_divergence_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return cryptocurrency_divergence_strategy()