Стратегия MA S.R. Trading представляет собой конверсию советника MetaTrader «MA S.R Trading». Она отслеживает форму короткой простой скользящей средней (SMA), чтобы выявлять локальные вершины и впадины. Как только SMA формирует максимум или минимум на последних трёх закрытых свечах, стратегия открывает позицию в направлении потенциального разворота и сразу сохраняет ближайший экстремум для установки защитного стоп-уровня.
Вместо использования нескольких скользящих средних с разными периодами система анализирует одну и ту же SMA, сравнивая её значения на трёх предыдущих свечах. Если SMA[t-2] больше, чем SMA[t-1] и SMA[t-3], образуется локальная вершина и появляется сигнал на продажу. Если SMA[t-2] ниже обеих соседних точек, это локальная впадина и повод для покупки. После появления сигнала стратегия рассчитывает максимум или минимум за заданное число свечей и использует его как стоп-уровень.
Логика выхода повторяет модификацию стопов из оригинального MQL-кода. Для коротких позиций стоп устанавливается по наивысшему максимуму в пределах окна HighLookback, при условии, что этот уровень выше предыдущего закрытия. Для длинных позиций используется минимальный минимум из окна LowLookback, который должен располагаться ниже предыдущего закрытия. При достижении этих уровней цена закрывает позицию рыночной заявкой, что имитирует работу штатного OrderModify из исходной версии.
Стратегия хорошо подходит для инструментов с выраженной волновой структурой на внутридневных или краткосрочных таймфреймах. Небольшой период SMA (по умолчанию 5) обеспечивает быструю реакцию на изменение микроструктуры рынка, а параметры HighLookback и LowLookback (по умолчанию 5) определяют, насколько близко стоп следует за ценой. Для скальпинга используйте более короткие окна, а для шумных рынков — более длинные.
Тесты на основных валютных парах и CFD на индексы показали наилучшие результаты во время боковых фаз с частыми колебаниями. Во время устойчивых трендов с плавными откатами может потребоваться дополнительный фильтр или подтверждение волатильности, чтобы избежать преждевременных входов. При запуске в боевой торговле полезно сочетать стратегию с фильтрами времени или анализом общего контекста.
Подробности
Условия входа
Продажа: SMA[t-1] < SMA[t-2] и SMA[t-3] < SMA[t-2] — SMA формирует локальный максимум.
Покупка: SMA[t-1] > SMA[t-2] и SMA[t-3] > SMA[t-2] — SMA формирует локальный минимум.
Управление стопами
Продажа: Стоп = максимум за HighLookback свечей, если он выше предыдущего закрытия. Выход при касании уровня.
Покупка: Стоп = минимум за LowLookback свечей, если он ниже предыдущего закрытия. Выход при касании уровня.
Правила позиции: Всегда разворачивается по последнему сигналу. При смене направления закрывает текущую позицию и открывает новую тем же рыночным ордером с объёмом, покрывающим предыдущую позицию и желаемый лот.
Параметры по умолчанию
SmaPeriod = 5.
HighLookback = 5.
LowLookback = 5.
CandleType = таймфрейм 30 минут.
TradeVolume = 1 лот (назначается в свойство Volume при старте).
Уровень риска: Средний (короткие стопы, частые сделки).
Рекомендации по использованию
Лучше всего работает на инструментах с выраженными колебаниями. Во время важных новостей стоит отключать торговлю, чтобы избежать ложных разворотов.
Оптимизируйте период SMA и длину окон экстремумов под конкретный инструмент и таймфрейм. Меньшие значения повышают чувствительность, но увеличивают число «пилы».
Новый стоп рассчитывается только при появлении свежего сигнала. Если уровень не проходит проверку (например, максимум не выше прошлого закрытия), он отбрасывается, и стратегия не будет подтягивать стоп слишком близко к цене.
Поскольку выходы выполняются рыночными ордерами, на резких движениях возможны проскальзывания. При наличии возможностей брокера подключайте биржевые стоп-ордера.
В стратегии нет фиксированных тейк-профитов. Их можно добавить, расширив условия в методе ProcessCandle.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class MaSrTradingStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _momentumPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose; private decimal _prevEma; private bool _hasPrev;
private int _cooldown;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int MomentumPeriod { get => _momentumPeriod.Value; set => _momentumPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MaSrTradingStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20).SetDisplay("EMA Period", "EMA lookback", "Indicators");
_momentumPeriod = Param(nameof(MomentumPeriod), 14).SetDisplay("Momentum", "Momentum period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevEma = default;
_hasPrev = default;
_cooldown = default;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevClose = close; _prevEma = ema; _hasPrev = true; return; }
if (_cooldown > 0)
{
_cooldown--;
_prevClose = close; _prevEma = ema;
return;
}
if (_prevClose <= _prevEma && close > ema && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_cooldown = 6;
}
else if (_prevClose >= _prevEma && close < ema && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_cooldown = 6;
}
_prevClose = close; _prevEma = ema;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ma_sr_trading_strategy(Strategy):
def __init__(self):
super(ma_sr_trading_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20).SetDisplay("EMA Period", "EMA lookback", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0; self._prev_ema = 0.0; self._has_prev = False; self._cooldown = 0
@property
def ema_period(self): return self._ema_period.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(ma_sr_trading_strategy, self).OnReseted()
self._prev_close = 0.0; self._prev_ema = 0.0; self._has_prev = False; self._cooldown = 0
def OnStarted2(self, time):
super(ma_sr_trading_strategy, self).OnStarted2(time)
self._has_prev = False; self._cooldown = 0
ema = ExponentialMovingAverage(); ema.Length = self.ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, self.process_candle).Start()
def process_candle(self, candle, ema):
if candle.State != CandleStates.Finished: return
if not self.IsFormedAndOnlineAndAllowTrading(): return
close = float(candle.ClosePrice); ema_val = float(ema)
if not self._has_prev:
self._prev_close = close; self._prev_ema = ema_val; self._has_prev = True; return
if self._cooldown > 0:
self._cooldown -= 1; self._prev_close = close; self._prev_ema = ema_val; return
if self._prev_close <= self._prev_ema and close > ema_val and self.Position <= 0:
volume = self.Volume + abs(self.Position)
self.BuyMarket(volume); self._cooldown = 6
elif self._prev_close >= self._prev_ema and close < ema_val and self.Position >= 0:
volume = self.Volume + abs(self.Position)
self.SellMarket(volume); self._cooldown = 6
self._prev_close = close; self._prev_ema = ema_val
def CreateClone(self): return ma_sr_trading_strategy()