Конвертация из исходного советника MQL PINBAR.mq4 (каталог MQL/22269). Стратегия ищет разворотные свечи типа «пин-бар» на основном таймфрейме и подтверждает их сигналами старшего таймфрейма по индикаторам Momentum и MACD. Реализация выполнена на высокоуровневом API StockSharp.
Логика работы
Основной таймфрейм – настраиваемый тип свечей, на котором ищутся паттерны price action.
Старший таймфрейм – настраиваемые свечи для фильтров Momentum и MACD.
Распознавание пин-баров – тело свечи должно быть небольшим относительно полного диапазона, а одна из тенью должна занимать большую часть диапазона (параметры задаются).
Трендовый фильтр – быстрая EMA должна находиться выше (либо ниже) медленной EMA для подтверждения длинных (либо коротких) сигналов, что повторяет LWMA-фильтр оригинала.
Фильтр Momentum – значение индикатора Momentum на старшем таймфрейме (или одно из двух предыдущих значений) должно превышать порог по модулю в нужную сторону.
Фильтр MACD – значение MACD должно быть выше сигнальной линии для покупок и ниже сигнальной для продаж, что соответствует проверке MACD на месячном графике в MQL-версии.
Подтверждение фракталом – используется скользящее окно из пяти свечей; сделка разрешается только после формирования актуального фрактала и пока цена не пробьёт его уровень.
Управление позицией – реализованы процентный стоп-лосс, тейк-профит, перевод в безубыток и трейлинг-стоп. Позиция закрывается при срабатывании любого из уровней либо при появлении встречного сигнала.
Правила входа
Покупка
Последняя свеча основного таймфрейма – бычий пин-бар (длинная нижняя тень, небольшое тело).
Быстрая EMA выше медленной EMA.
Momentum на старшем таймфрейме (текущее или одно из двух предыдущих значений) превышает заданный порог.
MACD выше сигнальной линии.
Есть актуальный бычий фрактал, уровень которого не пробит ценой.
Позиция отсутствует либо открыта короткая (короткая позиция переворачивается).
Продажа
Последняя свеча основного таймфрейма – медвежий пин-бар (длинная верхняя тень, небольшое тело).
Быстрая EMA ниже медленной EMA.
Momentum на старшем таймфрейме (текущее или одно из двух предыдущих значений) ниже отрицательного порога.
MACD ниже сигнальной линии.
Есть актуальный медвежий фрактал, уровень которого не пробит.
Позиция отсутствует либо открыта длинная (позиция переворачивается).
Правила выхода
Стоп-лосс и тейк-профит задаются в процентах от цены входа.
После достижения порога перевода в безубыток стоп переносится на цену входа с дополнительным смещением.
После выполнения условия активации включается трейлинг-стоп на заданном расстоянии.
Встречный сигнал приводит к немедленному перевороту позиции.
Параметры
Имя
Значение по умолчанию
Описание
CandleType
15-минутные свечи
Основной таймфрейм для поиска паттернов.
TrendCandleType
Часовые свечи
Старший таймфрейм для фильтров Momentum и MACD.
FastMaLength
6
Период быстрой EMA (аналог быстрой LWMA).
SlowMaLength
85
Период медленной EMA (аналог медленной LWMA).
MomentumLength
14
Период Momentum на старшем таймфрейме.
MomentumThreshold
0.1
Минимальное абсолютное значение Momentum для подтверждения.
MacdFastLength
12
Период быстрой EMA для MACD.
MacdSlowLength
26
Период медленной EMA для MACD.
MacdSignalLength
9
Период сигнальной EMA для MACD.
BodyToRangeRatio
0.3
Максимальная доля тела свечи от полного диапазона.
WickRatio
0.6
Минимальная доля доминирующей тени.
StopLossPercent
2
Процент стоп-лосса.
TakeProfitPercent
4
Процент тейк-профита.
BreakEvenTriggerPercent
1.5
Порог для перевода позиции в безубыток.
BreakEvenOffsetPercent
0.2
Дополнительное смещение безубыточного стопа.
TrailingActivationPercent
2.5
Порог включения трейлинг-стопа.
TrailingDistancePercent
1
Расстояние трейлинг-стопа.
Дополнительно
По умолчанию объём позиции равен 1; при необходимости отрегулируйте свойство Volume.
При пробое цены через сохранённый уровень фрактала требуются новые условия для следующей сделки.
Для ключевых параметров заданы диапазоны оптимизации, что упрощает тестирование и подбор настроек в StockSharp Designer.
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 PinbarReversal2Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast, _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 PinbarReversal2Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 6).SetGreaterThanZero().SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 25).SetGreaterThanZero().SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(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 fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) { _prevFast = fast; _prevSlow = slow; return; }
if (_prevFast == null || _prevSlow == null) { _prevFast = fast; _prevSlow = slow; return; }
var prevAbove = _prevFast.Value > _prevSlow.Value;
var currAbove = fast > slow;
_prevFast = fast; _prevSlow = slow;
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 pinbar_reversal2_strategy(Strategy):
def __init__(self):
super(pinbar_reversal2_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", 6) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 25) \
.SetDisplay("Slow EMA", "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(pinbar_reversal2_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(pinbar_reversal2_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 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif prev_above and not curr_above and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return pinbar_reversal2_strategy()