JS MA SAR Trades converts the MetaTrader 5 expert "JS MA SAR Trades" into the StockSharp high-level API. The strategy looks for
higher swing lows or lower swing highs detected via a ZigZag-style filter, confirms momentum with two moving averages, and then
enters in the direction of a Parabolic SAR breakout. Positions are protected with classic stops, optional trailing stops and an
explicit trading session filter.
Logic Overview
Swing structure – Highest/Lowest indicators with the configured depth approximate the original ZigZag. The two most recent
swing lows and highs are tracked. A long setup requires the latest low to be higher than the previous one (ascending structure),
while a short setup requires the latest high to be lower than the previous one (descending structure). A deviation filter (in
pips) and a minimum backstep (bars between pivots) prevent noise pivots from being accepted.
Moving average confirmation – Both moving averages use the same smoothing type and applied price as the MT5 version,
including optional positive shifts (bars to the right). A long signal needs the shifted fast MA to stay above the shifted slow
MA; a short signal requires the opposite relation.
Parabolic SAR trigger – Once the swing and moving average conditions are satisfied, the trade is executed only if the candle
closes beyond the Parabolic SAR level: close above SAR for longs and close below for shorts. SAR flips to the other side close
all existing positions even outside the entry window.
Risk management – Stop-loss and take-profit levels are computed in pips (converted through the instrument price step). The
optional trailing stop mimics the MT5 logic: the stop is shifted only after the price has moved by the configured trailing stop
plus trailing step distance from the entry price.
Session filter – When enabled, orders are allowed only between the specified start and end hours (inclusive). Protective
exits (stop/take/trailing and SAR reversal) are still evaluated on every finished candle.
Entry and Exit Rules
Long entry: higher swing low, Parabolic SAR below the close, fast MA (with shift) above slow MA, and close within the trading
window. The strategy buys OrderVolume + |Position| to close shorts and open the long position.
Short entry: lower swing high, Parabolic SAR above the close, fast MA (with shift) below slow MA, and time filter satisfied.
Long exit:
Close price crosses below Parabolic SAR;
Stop-loss, trailing stop, or take-profit level is hit.
Short exit:
Close price crosses above Parabolic SAR;
Stop-loss, trailing stop, or take-profit level is hit.
Parameters
Parameter
Default
Description
OrderVolume
1
Base order size used for new entries; the strategy adds the absolute current position to reverse instantly.
StopLossPips
50
Distance in pips between entry price and stop-loss. Set to 0 to disable.
TakeProfitPips
50
Distance in pips between entry price and take-profit. Set to 0 to disable.
TrailingStopPips
5
Trailing stop distance in pips. Works together with TrailingStepPips.
TrailingStepPips
5
Extra distance the price must travel (in pips) before the trailing stop is tightened. Must be positive when trailing is enabled.
UseTimeFilter
true
Enable the start/end hour filter for new entries.
StartHour
19
Beginning of the trading window (inclusive, exchange time).
EndHour
22
End of the trading window (inclusive).
FastMaPeriod
55
Period of the fast moving average.
FastMaShift
3
Forward shift (in bars) applied to the fast moving average values.
SlowMaPeriod
120
Period of the slow moving average.
SlowMaShift
0
Forward shift (in bars) for the slow moving average.
MaType
Smoothed
Moving average smoothing method (Simple, Exponential, Smoothed, Weighted).
AppliedPrice
Median
Price source for both moving averages (Close, Open, High, Low, Median, Typical, Weighted).
SarStep
0.02
Initial acceleration factor of the Parabolic SAR.
SarMax
0.2
Maximum acceleration factor of the Parabolic SAR.
ZigZagDepth
12
Lookback window (bars) for swing detection.
ZigZagDeviation
5
Minimum swing size measured in pips to accept a new pivot.
ZigZagBackstep
3
Minimum number of bars between consecutive pivots of the same type.
CandleType
H1
Trading timeframe for candle subscription.
Notes
The strategy keeps the protective logic active even outside the entry window, ensuring stops and SAR flips are honoured.
The trailing stop reproduces the MT5 implementation: once price advances by TrailingStop + TrailingStep, the stop is moved to
Close - TrailingStop for longs (mirrored for shorts).
Moving averages are evaluated on the selected applied price; shifting emulates the MT5 indicator offset.
Make sure the instrument has a valid PriceStep, otherwise pip-based distances are skipped.
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()