This strategy replicates the original MetaTrader "MACD and SAR" expert advisor. It evaluates the relationship between the MACD main and signal lines together with the Parabolic SAR level on every completed candle. Configurable switches allow you to invert each comparison so that the same template can be used for counter-trend or trend-following setups. Multiple entries are allowed as long as the configured maximum number of stacked positions is not exceeded.
When a long signal appears, existing short exposure is flattened and a new long lot is opened (if the cap is not reached). Likewise, a short signal closes longs first and then adds a short lot. There are no additional stop-loss or take-profit orders; trades are closed only when the opposite signal is generated.
Strategy logic
Wait for a finished candle from the configured timeframe.
Read MACD values (main, signal, histogram) and the Parabolic SAR level calculated on close prices.
Evaluate the following comparisons, each of which can be flipped by its corresponding boolean parameter:
MACD main line vs. signal line.
MACD signal line vs. the zero level.
Parabolic SAR vs. closing price.
If all three comparisons for the long side are satisfied and the strategy still has room to stack new positions, buy the specified lot size (including any volume required to close shorts).
If all three comparisons for the short side are satisfied and the stacking limit allows it, sell the specified lot size (including any volume required to close longs).
Parameters
TradeVolume — volume per individual trade (default 0.1).
MaxPositions — maximum number of stacked positions in one direction (default 10).
MacdFastPeriod — fast EMA period for MACD (default 12).
MacdSlowPeriod — slow EMA period for MACD (default 26).
MacdSignalPeriod — signal smoothing period for MACD (default 9).
SarStep — Parabolic SAR acceleration step (default 0.02).
SarMaximum — Parabolic SAR maximum acceleration (default 0.2).
BuyMacdGreaterSignal — if true, require MACD main > signal for longs; otherwise expect the opposite (default true).
BuySignalPositive — if true, require MACD signal > 0 for longs; otherwise expect signal < 0 (default false).
BuySarAbovePrice — if true, require SAR above price for longs; otherwise expect price above SAR (default false).
SellMacdGreaterSignal — if true, require MACD main > signal for shorts; otherwise expect MACD main < signal (default false).
SellSignalPositive — if true, require MACD signal > 0 for shorts; otherwise expect signal < 0 (default true).
SellSarAbovePrice — if true, require SAR above price for shorts; otherwise expect price above SAR (default true).
CandleType — candle type/timeframe used for data processing (default 15 minutes).
Additional notes
The strategy relies solely on indicator crossings; there are no protective stops or profit targets.
Position stacking is implemented by checking the absolute position volume against MaxPositions * TradeVolume with a small tolerance to handle rounding.
All trades are executed with market orders. Make sure the portfolio volume setting matches the instruments you plan to trade.
Add optional portfolio protection rules if you need drawdown limits or trailing stops; none are included by default.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// MACD and Parabolic SAR trend-following strategy (simplified).
/// Uses EMA crossover as MACD proxy and ParabolicSar for trend confirmation.
/// </summary>
public class MacdAndSarStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<DataType> _candleType;
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public MacdAndSarStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow EMA period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candles used for analysis", "General");
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fastEma = new ExponentialMovingAverage { Length = FastPeriod };
var slowEma = new ExponentialMovingAverage { Length = SlowPeriod };
decimal prevFast = 0, prevSlow = 0;
bool hasPrev = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, (ICandleMessage candle, decimal fastValue, decimal slowValue) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!hasPrev)
{
prevFast = fastValue;
prevSlow = slowValue;
hasPrev = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
prevFast = fastValue;
prevSlow = slowValue;
return;
}
// Crossover detection (MACD-like)
if (prevFast <= prevSlow && fastValue > slowValue && Position <= 0)
{
BuyMarket();
}
else if (prevFast >= prevSlow && fastValue < slowValue && Position >= 0)
{
SellMarket();
}
prevFast = fastValue;
prevSlow = slowValue;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastEma);
DrawIndicator(area, slowEma);
DrawOwnTrades(area);
}
}
}
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 macd_and_sar_strategy(Strategy):
def __init__(self):
super(macd_and_sar_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 12) \
.SetDisplay("Fast Period", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 26) \
.SetDisplay("Slow Period", "Slow EMA period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candles used for analysis", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnReseted(self):
super(macd_and_sar_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(macd_and_sar_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.FastPeriod
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(fast_ema, slow_ema, self._on_process) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_ema)
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 not self._has_prev:
self._prev_fast = fv
self._prev_slow = sv
self._has_prev = True
return
if self._prev_fast <= self._prev_slow and fv > sv and self.Position <= 0:
self.BuyMarket()
elif self._prev_fast >= self._prev_slow and fv < sv and self.Position >= 0:
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def CreateClone(self):
return macd_and_sar_strategy()