Renko Fractals Grid — порт оригинального советника MetaTrader 4 "RENKO FRACTALS GRID". Стратегия торгует пробои последних фракталов Билла Вильямса с подтверждением по волатильности в стиле Ренко, направлением по взвешенным скользящим средним и силой импульса через индикатор Rate of Change. Перенос на StockSharp сохраняет сеточную модель управления позициями исходного робота: мартингейл-подбор объёмов, перевод в безубыток, трейлинг-стопы, защита эквити и опциональный трейлинг плавающей прибыли в денежных единицах.
Логика входов
Пробой фрактала. Лонг открывается, когда последний бычий фрактал пробит закрытием свечи, а минимум одна из трёх предыдущих свечей закрывалась ниже этого уровня. Для шорта условия зеркальные.
Фильтр Ренко. Анализируется диапазон High/Low последних CandlesToRetrace свечей. Сигнал допустим только если текущая цена закрытия находится минимум на один «кирпич» (фиксированное число пунктов или текущее значение ATR) дальше экстремумов.
Фильтр тренда. Быстрая и медленная взвешенные средние должны быть направлены в одну сторону (быстрая выше медленной для покупок и ниже для продаж).
Импульс. Абсолютное отклонение трёх последних значений Rate of Change от 100 должно превышать заданные пороги. Это повторяет фильтр iMomentum из версии MQL4.
MACD. Входы разрешены только при правильном расположении основной линии MACD относительно сигнальной. То же правило используется для досрочного выхода.
Управление риском
Мартингейл-сетка. Каждый дополнительный вход умножает базовый объём на LotExponent, а общее количество одновременно открытых сделок ограничено параметром MaxTrades.
Стоп и тейк. Фиксированные отступы в пунктах рассчитываются от средней цены позиции.
Безубыток. После прохождения цены на BreakEvenTriggerPips стоп переносится на цену входа плюс BreakEvenOffsetPips.
Трейлинг. Свечной трейлинг-стоп отслеживает максимальное отклонение цены от точки входа.
Денежный трейлинг. При активированной опции после достижения MoneyTakeProfit совокупная прибыль фиксируется, если просадка от максимума превышает MoneyStopLoss.
Стоп по эквити. Стратегия запоминает максимум совокупной стоимости портфеля и открытых позиций. При просадке более EquityRiskPercent процентов все позиции закрываются.
Параметры
Параметр
Описание
CandleType
Тип свечей, на которых строятся индикаторы.
FastMaLength / SlowMaLength
Периоды взвешенных скользящих средних.
MomentumLength
Длина окна для индикатора Rate of Change.
MomentumBuyThreshold / MomentumSellThreshold
Минимальное отклонение от 100 для подтверждения сделок.
UseAtrFilter
Использовать ATR вместо фиксированного размера кирпича Ренко.
BoxSizePips
Размер кирпича в пунктах при отключённом ATR-фильтре.
CandlesToRetrace
Количество свечей для поиска экстремумов.
BaseVolume
Базовый объём первой сделки.
LotExponent
Множитель объёма для каждой следующей сделки в сетке.
MaxTrades
Максимальное число одновременно открытых сделок в одном направлении.
StopLossPips / TakeProfitPips
Отступы стоп-лосса и тейк-профита в пунктах.
TrailingStopPips
Дистанция трейлинг-стопа в пунктах (0 — отключено).
UseBreakEven
Включить перевод в безубыток.
BreakEvenTriggerPips / BreakEvenOffsetPips
Порог активации безубытка и добавочный запас в пунктах.
UseMoneyTarget
Включить денежный трейлинг прибыли.
MoneyTakeProfit / MoneyStopLoss
Порог по плавающей прибыли и максимально допустимый откат.
UseEquityStop
Включить глобальный стоп по эквити.
EquityRiskPercent
Допустимая просадка от пика эквити (в процентах).
Особенности реализации
В MQL4 советник считал MACD на месячном таймфрейме. В StockSharp используется тот же набор параметров на рабочем таймфрейме, поскольку дополнительных подписок по умолчанию нет.
Все величины в пунктах пересчитываются через шаг цены инструмента, что корректно работает для пятизнаковых котировок.
Реализован подсчёт реализованной прибыли через события сделок, что достаточно для оценки просадки эквити без данных брокера.
Стратегия использует высокоуровневые подписки на свечи и индикаторы, а комментарии в коде переведены на английский язык, как требуется в проекте.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Renko Fractals Grid strategy. Uses fast/slow WMA crossover with momentum confirmation.
/// </summary>
public class RenkoFractalsGridStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast;
private decimal? _prevSlow;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public RenkoFractalsGridStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 6)
.SetGreaterThanZero()
.SetDisplay("Fast WMA", "Fast WMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 21)
.SetGreaterThanZero()
.SetDisplay("Slow WMA", "Slow WMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null;
_prevSlow = null;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, slow);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevFast = fastVal;
_prevSlow = slowVal;
return;
}
if (_prevFast == null || _prevSlow == null)
{
_prevFast = fastVal;
_prevSlow = slowVal;
return;
}
var prevAbove = _prevFast.Value > _prevSlow.Value;
var currAbove = fastVal > slowVal;
_prevFast = fastVal;
_prevSlow = slowVal;
if (!prevAbove && currAbove && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (prevAbove && !currAbove && 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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class renko_fractals_grid_strategy(Strategy):
def __init__(self):
super(renko_fractals_grid_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 8) \
.SetDisplay("Fast Period", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 21) \
.SetDisplay("Slow Period", "Slow EMA period", "Indicators")
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(renko_fractals_grid_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(renko_fractals_grid_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast = ExponentialMovingAverage()
fast.Length = self.FastPeriod
slow = ExponentialMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fv
self._prev_slow = sv
return
prev_above = self._prev_fast > self._prev_slow
curr_above = fv > sv
self._prev_fast = fv
self._prev_slow = sv
if not prev_above and curr_above:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif prev_above and not curr_above:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return renko_fractals_grid_strategy()