Стратегия повторяет логику советников MetaTrader «M.A break mt5 buy» и «M.A break mt5 sell», объединяя длинные и короткие сигналы в одном алгоритме StockSharp. Она анализирует завершённые свечи выбранного таймфрейма, сравнивает несколько экспоненциальных скользящих средних и проверяет структуру импульсной свечи перед открытием позиции. Управление сделкой осуществляется фиксированными стоп-лоссами и тейк-профитами, задаваемыми в пунктах.
Алгоритм работы
Подтверждение тренда. Две пары EMA должны располагаться в направлении сделки: для покупок обе быстрые средние выше медленных, для продаж — ниже. Дополнительно проверяется, что открытие предыдущей свечи находится по правильную сторону от фильтрующей EMA.
Оценка «тихого» диапазона. Перед импульсом учитывается заданное число предыдущих свечей. Максимальный размах их цен (High-Low) используется как базовый диапазон и сравнивается с минимальным порогом в пунктах.
Проверка импульса. Последняя закрытая свеча должна иметь размер не меньше ImpulseStrength * «тихий» диапазон. При необходимости можно ограничить минимальную и максимальную величину свечи в пунктах.
Шаблон свечи. Импульсная свеча должна соответствовать форме:
Для покупок: свеча бычья, верхняя тень не превышает BullUpperWickPercent диапазона, нижняя тень не меньше BullLowerWickPercent.
Для продаж: свеча медвежья, верхняя тень минимум BearUpperWickPercent, нижняя тень максимум BearLowerWickPercent.
Условие отката. Минимум (для покупок) или максимум (для продаж) импульсной свечи обязан коснуться дополнительной EMA, что подтверждает выход из отката.
Контроль позиции. В системе поддерживается только одна нетто-позиция. При появлении сигнала противоположного направления текущая позиция закрывается перед открытием новой. Стратегия не торгует против фильтров тренда.
Выход из сделки. Стоп и цель рассчитываются от цены входа по заданным расстояниям в пунктах. После закрытия каждой свечи проверяется, достигнута ли защита; при одновременном срабатывании приоритет отдаётся стоп-лоссу.
Параметры
Параметр
Описание
Candle Type
Основная серия свечей, по которой выполняются расчёты.
Fast MA 1 / Slow MA 1
Периоды первой пары EMA для базового трендового фильтра.
Fast MA 2 / Slow MA 2
Периоды второй пары EMA для дополнительного подтверждения тренда.
Open Filter MA
Период EMA, с которой сравнивается открытие предыдущей свечи.
Pullback MA
EMA, которую должна протестировать тень импульсной свечи.
Quiet Bars
Количество свечей в тихом диапазоне перед импульсом.
Quiet Range (pips)
Минимальный размер тихого диапазона в пунктах.
Impulse Multiplier
Минимальное отношение размера импульсной свечи к тихому диапазону.
Min / Max Candle Size (pips)
Ограничения на диапазон импульсной свечи. Ноль отключает проверку.
Bull Upper Wick % / Bull Lower Wick %
Допустимые тени бычьей импульсной свечи в процентах от диапазона.
Bear Upper Wick % / Bear Lower Wick %
Допустимые тени медвежьей импульсной свечи.
Volume
Объём заявки в лотах.
Stop-Loss (pips)
Расстояние до защитного стопа. Ноль — без стопа.
Take-Profit (pips)
Расстояние до тейк-профита. Ноль — без цели.
Enable Long / Enable Short
Включение торговых сигналов в соответствующем направлении.
Рекомендации по использованию
Настройте тип свечей согласно исходным роботам (например, M5 или H1). По умолчанию используется пятиминутный таймфрейм.
В памяти хранится только столько свечей, сколько нужно для расчёта тихого диапазона, чтобы избежать лишнего расхода ресурсов.
Цена входа приближается к закрытию импульсной свечи, что соответствует постановке рыночного ордера на открытии следующей свечи в MetaTrader.
Проверка стопов и целей выполняется после закрытия каждой свечи. Если на одной свече достигаются оба уровня, срабатывает стоп-лосс для консервативного поведения.
Отключение одного из направлений позволяет воспроизвести отдельный советник «buy» или «sell».
Особенности конверсии
Оба MQ5-файла имели кодировку UTF-16 и были построены из блоков FXD. Каждый блок был преобразован в явные условия на C#.
Все проверки используют данные закрытых свечей (Shift = 1), как и в исходных советниках.
Виртуальные стопы и графические подписи, присутствующие в MQ5, не влияют на торговлю и потому опущены.
Тестирование
Скомпилируйте решение AlgoTrading.sln или запустите стратегию в тестере StockSharp. Если у инструмента отсутствует шаг цены, задайте его вручную; в противном случае используется значение по умолчанию 0.0001, подходящее для большинства валютных пар.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// MA Break strategy: EMA breakout with impulse candle confirmation.
/// Buys when close crosses above EMA with strong bullish candle body.
/// Sells when close crosses below EMA with strong bearish candle body.
/// </summary>
public class MaBreakStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<decimal> _bodyRatio;
private decimal _prevClose;
private decimal _prevEma;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public decimal BodyRatio { get => _bodyRatio.Value; set => _bodyRatio.Value = value; }
public MaBreakStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_emaPeriod = Param(nameof(EmaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA period for breakout", "Indicators");
_bodyRatio = Param(nameof(BodyRatio), 0.7m)
.SetDisplay("Body Ratio", "Min body/range ratio for impulse", "Pattern");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0;
_prevEma = 0;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_prevEma = 0;
_hasPrev = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue)
{
if (candle.State != CandleStates.Finished) return;
var range = candle.HighPrice - candle.LowPrice;
var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
if (_hasPrev && range > 0)
{
var isImpulse = body >= range * BodyRatio;
if (_prevClose <= _prevEma && candle.ClosePrice > emaValue
&& candle.ClosePrice > candle.OpenPrice && isImpulse && Position <= 0)
BuyMarket();
else if (_prevClose >= _prevEma && candle.ClosePrice < emaValue
&& candle.ClosePrice < candle.OpenPrice && isImpulse && Position >= 0)
SellMarket();
}
_prevClose = candle.ClosePrice;
_prevEma = emaValue;
_hasPrev = true;
}
}
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 ma_break_strategy(Strategy):
def __init__(self):
super(ma_break_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._ema_period = self.Param("EmaPeriod", 50)
self._body_ratio = self.Param("BodyRatio", 0.7)
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
@property
def BodyRatio(self):
return self._body_ratio.Value
@BodyRatio.setter
def BodyRatio(self, value):
self._body_ratio.Value = value
def OnReseted(self):
super(ma_break_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(ma_break_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, self._process_candle).Start()
def _process_candle(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
ema_val = float(ema_value)
range_val = high - low
body = abs(close - open_price)
if self._has_prev and range_val > 0:
is_impulse = body >= range_val * self.BodyRatio
if (self._prev_close <= self._prev_ema and close > ema_val and
close > open_price and is_impulse and self.Position <= 0):
self.BuyMarket()
elif (self._prev_close >= self._prev_ema and close < ema_val and
close < open_price and is_impulse and self.Position >= 0):
self.SellMarket()
self._prev_close = close
self._prev_ema = ema_val
self._has_prev = True
def CreateClone(self):
return ma_break_strategy()