Главная
/
Примеры стратегий
Открыть на GitHub
Стратегия Exp Adaptive Renko MMRec Duplex
Стратегия переносит советник MetaTrader 5 Exp_AdaptiveRenko_MMRec_Duplex.mq5 на высокоуровневый API StockSharp. Две независимые ветки Adaptive Renko — одна для покупок, другая для продаж — отслеживают, как кирпичные каналы меняют статус между поддержкой и сопротивлением. Когда «длинный» канал формирует свежую поддержку, а «короткий» канал теряет сопротивление (или наоборот), стратегия открывает соответствующую рыночную позицию. C# версия полностью воспроизводит модуль перерасчёта лота (MM Recounter), уменьшающий объём после серии убыточных сделок и возвращающий его к нормальному значению после восстановления.
Основные этапы работы
Подписки на данные . Каждая сторона подписывается на свой тип свечей (таймфрейм) и привязывает к нему индикатор волатильности (ATR или стандартное отклонение) через SubscribeCandles().BindEx(...). Индикатор управляет высотой адаптивных кирпичей.
Обработка Adaptive Renko . Вспомогательный класс AdaptiveRenkoProcessor воссоздаёт логику индикатора из MQL и возвращает снимок с текущим трендом, поддержкой и сопротивлением. Сигналы учитываются только на закрытых свечах.
Логика входа . Когда длинный поток фиксирует бычий разворот (поддержка на сигнальной свече), открывается длинная позиция. Для короткой позиции требуется медвежий сигнал из короткого потока.
Логика выхода . Обратные события Renko закрывают активную позицию. Дополнительно контролируются уровни стоп-лосса и тейк-профита в шагах цены.
MMRec управление капиталом . Для каждой стороны ведётся очередь последних результатов сделок. Если число убыточных исходов в заданном окне достигает порога LossTrigger, следующая сделка исполняется с уменьшенным значением манименеджмента (LongSmallMoneyManagement / ShortSmallMoneyManagement). В остальных случаях используется базовое значение (LongMoneyManagement / ShortMoneyManagement). Перечисление MarginModeOption повторяет режимы расчёта из оригинального советника (лоты, доля баланса, доля убытка и т.д.).
Учёт сделок . При каждом выходе вызывается RegisterTradeResult, который пополняет MMRec очереди. Обрезка очередей полностью соответствует функциям BuyTradeMMRecounterS и SellTradeMMRecounterS без обращения к истории терминала.
Группы параметров
Группа
Ключевые параметры
Описание
Лонг
LongCandleType, LongVolatilityMode, LongVolatilityPeriod, LongSensitivity, LongPriceMode, LongMinimumBrickPoints, LongSignalBarOffset
Управляют Adaptive Renko потоком, генерирующим длинные сигналы.
Шорт
ShortCandleType, ShortVolatilityMode, ShortVolatilityPeriod, ShortSensitivity, ShortPriceMode, ShortMinimumBrickPoints, ShortSignalBarOffset
Аналогичные настройки для короткой ветки.
MMRec
LongTotalTrigger, LongLossTrigger, LongSmallMoneyManagement, LongMoneyManagement, LongMarginMode, ShortTotalTrigger, ShortLossTrigger, ShortSmallMoneyManagement, ShortMoneyManagement, ShortMarginMode
Настройки модуля восстановления объёма. TotalTrigger задаёт длину окна, LossTrigger — количество убытков для переключения на уменьшенный объём.
Риск
LongStopLossPoints, LongTakeProfitPoints, ShortStopLossPoints, ShortTakeProfitPoints, LongDeviationSteps, ShortDeviationSteps
Параметры защитных уровней и информативного слиппеджа в шагах цены.
Поведенческие особенности
Стратегия рассчитана на неттинговый режим: перед открытием новой длинной позиции закрывается существующий шорт и наоборот.
Объём рассчитывается функцией CalculateVolume, поддерживающей все режимы оригинального манименеджмента, включая схемы с учётом стоп-лосса.
Вся обработка индикаторов выполняется только на завершённых свечах, как и в исходном советнике.
В журнал пишется выбранный множитель манименеджмента и ожидаемый слиппедж в шагах цены для удобства анализа.
Состав репозитория
CS/ExpAdaptiveRenkoMmrecDuplexStrategy.cs — реализация стратегии с адаптивным Renko и блоком MMRec.
README.md — документация на английском языке.
README_ru.md — эта страница.
README_zh.md — документация на китайском языке.
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 ExpAdaptiveRenkoMmrecDuplexStrategy : 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 ExpAdaptiveRenkoMmrecDuplexStrategy()
{
_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 exp_adaptive_renko_mmrec_duplex_strategy(Strategy):
def __init__(self):
super(exp_adaptive_renko_mmrec_duplex_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(exp_adaptive_renko_mmrec_duplex_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(exp_adaptive_renko_mmrec_duplex_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 exp_adaptive_renko_mmrec_duplex_strategy()