JS MA SAR Trades — это конвертация советника MetaTrader 5 «JS MA SAR Trades» на высокоуровневый API StockSharp. Стратегия ищет
последовательности более высоких минимумов или более низких максимумов через упрощённый ZigZag, подтверждает импульс двумя
скользящими средними и входит в сделку при пробое уровня Parabolic SAR. Позиции защищаются классическим стоп-лоссом, тейк-профитом,
опциональным трейлинг-стопом и торговой сессией по времени.
Логика работы
Структура свингов. Индикаторы Highest/Lowest с заданной глубиной повторяют поведение ZigZag. Хранятся последние два
минимума и два максимума. Для покупки требуется, чтобы текущий минимум был выше предыдущего. Для продажи — последний максимум
должен быть ниже предыдущего. Порог отклонения (в пунктах) и минимальный backstep (количество баров между пивотами) отсеивают
шумовые развороты.
Подтверждение скользящими средними. Обе средние используют один тип сглаживания и источник цены, совпадающие с оригиналом MT5,
включая положительные сдвиги по барам. Для лонга сдвинутая быстрая средняя должна быть выше медленной; для шорта — наоборот.
Триггер Parabolic SAR. После выполнения условий по свингам и средним сделка открывается только если свеча закрылась за
уровнем SAR (выше — для лонга, ниже — для шорта). Переворот SAR закрывает текущую позицию даже вне торгового окна.
Управление риском. Стоп-лосс и тейк-профит считаются в пунктах (пересчитываются через PriceStep). Трейлинг-стоп повторяет
MT5: стоп переносится лишь после прохождения ценой расстояния TrailingStop + TrailingStep от цены входа.
Временной фильтр. При включении заявки разрешены только между стартовым и конечным часами (включительно). Защитные проверки
(стопы, трейлинг, переворот SAR) выполняются на каждой закрытой свече.
Правила входа и выхода
Вход в лонг: новый минимум выше предыдущего, SAR ниже цены закрытия, быстрая MA (со сдвигом) выше медленной, время внутри
окна. Открывается объём OrderVolume + |Position|, чтобы закрыть шорт и открыть лонг.
Вход в шорт: новый максимум ниже предыдущего, SAR выше закрытия, быстрая MA ниже медленной, временной фильтр выполнен.
Выход из лонга:
Цена закрытия пересекает SAR сверху вниз;
Достигается стоп-лосс, трейлинг-стоп или тейк-профит.
Выход из шорта:
Цена закрытия пересекает SAR снизу вверх;
Срабатывает стоп-лосс, трейлинг-стоп или тейк-профит.
Параметры
Параметр
Значение по умолчанию
Описание
OrderVolume
1
Базовый объём заявки; к нему добавляется абсолютное значение текущей позиции при развороте.
StopLossPips
50
Расстояние от цены входа до стоп-лосса в пунктах (0 — отключить).
TakeProfitPips
50
Расстояние до тейк-профита в пунктах (0 — отключить).
TrailingStopPips
5
Дистанция трейлинг-стопа в пунктах. Работает вместе с TrailingStepPips.
TrailingStepPips
5
Дополнительный путь цены (в пунктах) для подтягивания трейлинг-стопа. Должен быть > 0 при включённом трейлинге.
UseTimeFilter
true
Включить торговое окно по времени.
StartHour
19
Начало торговой сессии (включительно, биржевое время).
EndHour
22
Окончание сессии (включительно).
FastMaPeriod
55
Период быстрой скользящей средней.
FastMaShift
3
Сдвиг (в барах) для быстрой средней.
SlowMaPeriod
120
Период медленной скользящей средней.
SlowMaShift
0
Сдвиг (в барах) для медленной средней.
MaType
Smoothed
Тип сглаживания (Simple, Exponential, Smoothed, Weighted).
AppliedPrice
Median
Источник цены (Close, Open, High, Low, Median, Typical, Weighted).
SarStep
0.02
Начальный коэффициент ускорения Parabolic SAR.
SarMax
0.2
Максимальный коэффициент ускорения Parabolic SAR.
ZigZagDepth
12
Глубина анализа свингов (количество баров).
ZigZagDeviation
5
Минимальная амплитуда разворота в пунктах.
ZigZagBackstep
3
Минимум баров между последовательными пивотами одного типа.
CandleType
H1
Таймфрейм свечей.
Дополнительно
Защитные условия действуют всегда, поэтому позиции закрываются при стопах и развороте SAR даже вне торгового окна.
Логика трейлинг-стопа соответствует версии MT5: после движения на TrailingStop + TrailingStep стоп переносится к Close - TrailingStop
(для шорта зеркально).
Скользящие средние считают выбранный источник цены, а сдвиг воспроизводит горизонтальное смещение индикатора в MT5.
Если у инструмента отсутствует корректный PriceStep, уровни в пунктах не будут рассчитаны.
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>
/// JS MA SAR Trades strategy. Uses EMA crossover (8/21).
/// </summary>
public class JsMaSarTradesStrategy : 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 JsMaSarTradesStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 8).SetGreaterThanZero().SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 21).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 js_ma_sar_trades_strategy(Strategy):
def __init__(self):
super(js_ma_sar_trades_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 EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 21) \
.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(js_ma_sar_trades_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(js_ma_sar_trades_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 js_ma_sar_trades_strategy()