MA Parabolic SAR Strategy
A estratégia MA Parabolic SAR tenta capturar tendências sustentadas usando uma média móvel simples para determinar a direção predominante e os pontos Parabolic SAR para o timing de entrada e posicionamento do stop. Quando ambos os indicadores se alinham, o sistema assume que o momentum é forte o suficiente para seguir.
Os testes indicam um retorno anual médio de aproximadamente 76%. Funciona melhor no mercado de câmbio.
Uma posição comprada é aberta quando o preço de fechamento está acima da média móvel e os pontos Parabolic SAR viram abaixo do mercado. Uma posição vendida é tomada quando o preço está abaixo da média e os pontos SAR viram acima do preço, sinalizando pressão de baixa. A estratégia sai assim que o preço cruza o SAR na direção oposta, assegurando lucros ou limitando perdas.
Esta abordagem é mais adequada para traders que preferem seguimento de tendência sistemático com stops claros e mecânicos. O Parabolic SAR se ajusta continuamente à medida que a volatilidade muda, mantendo a exposição em linha com as condições de mercado enquanto a média móvel previne trades contra a tendência mais ampla.
Detalhes
- Critérios de entrada:
- Comprado: Price > MA && Price > Parabolic SAR
- Vendido: Price < MA && Price < Parabolic SAR
- Comprado/Vendido: Ambos os lados.
- Critérios de saída:
- Comprado: Sair quando o preço cair abaixo do Parabolic SAR
- Vendido: Sair quando o preço subir acima do Parabolic SAR
- Stops: Sim, dinâmico via Parabolic SAR e stop fixo opcional.
- Valores padrão:
MaPeriod = 20
SarStep = 0.02m
SarMaxStep = 0.2m
CandleType = TimeSpan.FromMinutes(5)
TakeValue = new Unit(0, UnitTypes.Absolute)
StopValue = new Unit(2, UnitTypes.Percent)
- Filtros:
- Categoria: Tendência
- Direção: Ambos
- Indicadores: MA, Parabolic SAR
- Stops: Sim
- Complexidade: Intermediário
- Período: Intradiário
- Sazonalidade: Não
- Redes neurais: Não
- Divergência: Não
- Nível de risco: Médio
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on Moving Average and Parabolic SAR indicators.
/// Enters long when price is above MA and above SAR.
/// Enters short when price is below MA and below SAR.
/// Uses Parabolic SAR as dynamic stop-loss.
/// </summary>
public class MaParabolicSarStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<decimal> _sarStep;
private readonly StrategyParam<decimal> _sarMaxStep;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<Unit> _takeValue;
private readonly StrategyParam<Unit> _stopValue;
private SimpleMovingAverage _ma;
private ExponentialMovingAverage _sarProxy;
private decimal _lastSarValue;
private bool _hasPrevState;
private bool _prevAboveMa;
private bool _prevAboveSar;
private int _cooldown;
/// <summary>
/// Moving Average period.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Parabolic SAR acceleration factor.
/// </summary>
public decimal SarStep
{
get => _sarStep.Value;
set => _sarStep.Value = value;
}
/// <summary>
/// Parabolic SAR maximum acceleration factor.
/// </summary>
public decimal SarMaxStep
{
get => _sarMaxStep.Value;
set => _sarMaxStep.Value = value;
}
/// <summary>
/// Bars to wait between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Candle type parameter.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Take profit value.
/// </summary>
public Unit TakeValue
{
get => _takeValue.Value;
set => _takeValue.Value = value;
}
/// <summary>
/// Stop loss value.
/// </summary>
public Unit StopValue
{
get => _stopValue.Value;
set => _stopValue.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public MaParabolicSarStrategy()
{
_maPeriod = Param(nameof(MaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Period for Moving Average calculation", "Indicators")
.SetOptimize(10, 50, 5);
_sarStep = Param(nameof(SarStep), 0.02m)
.SetGreaterThanZero()
.SetDisplay("SAR Step", "Acceleration factor for Parabolic SAR", "Indicators")
.SetOptimize(0.01m, 0.05m, 0.01m);
_sarMaxStep = Param(nameof(SarMaxStep), 0.2m)
.SetGreaterThanZero()
.SetDisplay("SAR Max Step", "Maximum acceleration factor for Parabolic SAR", "Indicators")
.SetOptimize(0.1m, 0.3m, 0.05m);
_cooldownBars = Param(nameof(CooldownBars), 20)
.SetRange(1, 200)
.SetDisplay("Cooldown Bars", "Bars between trades", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_takeValue = Param(nameof(TakeValue), new Unit(0, UnitTypes.Absolute))
.SetDisplay("Take Profit", "Take profit value", "Protection");
_stopValue = Param(nameof(StopValue), new Unit(2, UnitTypes.Percent))
.SetDisplay("Stop Loss", "Stop loss value", "Protection");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ma = null;
_sarProxy = null;
_lastSarValue = default;
_hasPrevState = false;
_prevAboveMa = false;
_prevAboveSar = false;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Initialize indicators
_ma = new() { Length = MaPeriod };
_sarProxy = new ExponentialMovingAverage
{
Length = Math.Max(2, MaPeriod / 2)
};
// Create candles subscription
var subscription = SubscribeCandles(CandleType);
// Bind indicators to subscription
subscription
.Bind(_ma, _sarProxy, ProcessCandle)
.Start();
// Setup chart if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ma);
DrawIndicator(area, _sarProxy);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal maValue, decimal sarValue)
{
// Skip unfinished candles
if (candle.State != CandleStates.Finished)
return;
// Store current SAR value for stop-loss
_lastSarValue = sarValue;
// Trading logic
bool isPriceAboveMA = candle.ClosePrice > maValue;
bool isPriceAboveSAR = candle.ClosePrice > sarValue;
if (!_hasPrevState)
{
_hasPrevState = true;
_prevAboveMa = isPriceAboveMA;
_prevAboveSar = isPriceAboveSAR;
return;
}
var turnedBull = !_prevAboveSar && isPriceAboveSAR && isPriceAboveMA;
var turnedBear = _prevAboveSar && !isPriceAboveSAR && !isPriceAboveMA;
var sarFlipDown = _prevAboveSar && !isPriceAboveSAR;
var sarFlipUp = !_prevAboveSar && isPriceAboveSAR;
if (_cooldown > 0)
_cooldown--;
// Long signal: Price above MA and above SAR
if (_cooldown == 0 && turnedBull)
{
if (Position <= 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
// Short signal: Price below MA and below SAR
else if (_cooldown == 0 && turnedBear)
{
if (Position >= 0)
{
SellMarket();
_cooldown = CooldownBars;
}
}
// Exit long position: Price falls below SAR
else if (Position > 0 && sarFlipDown)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && sarFlipUp)
{
BuyMarket();
_cooldown = CooldownBars;
}
_prevAboveMa = isPriceAboveMA;
_prevAboveSar = isPriceAboveSAR;
}
}
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 SimpleMovingAverage, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ma_parabolic_sar_strategy(Strategy):
"""
MA + SAR proxy (EMA). Enters on combined MA/SAR flip signals.
"""
def __init__(self):
super(ma_parabolic_sar_strategy, self).__init__()
self._ma_period = self.Param("MaPeriod", 20).SetDisplay("MA Period", "SMA period", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 20).SetDisplay("Cooldown", "Bars between trades", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
self._has_prev = False
self._prev_above_ma = False
self._prev_above_sar = False
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ma_parabolic_sar_strategy, self).OnReseted()
self._has_prev = False
self._prev_above_ma = False
self._prev_above_sar = False
self._cooldown = 0
def OnStarted2(self, time):
super(ma_parabolic_sar_strategy, self).OnStarted2(time)
ma = SimpleMovingAverage()
ma.Length = self._ma_period.Value
sar_proxy = ExponentialMovingAverage()
sar_proxy.Length = max(2, self._ma_period.Value // 2)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ma, sar_proxy, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ma)
self.DrawIndicator(area, sar_proxy)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ma_val, sar_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ma = float(ma_val)
sar = float(sar_val)
above_ma = close > ma
above_sar = close > sar
if not self._has_prev:
self._has_prev = True
self._prev_above_ma = above_ma
self._prev_above_sar = above_sar
return
turned_bull = not self._prev_above_sar and above_sar and above_ma
turned_bear = self._prev_above_sar and not above_sar and not above_ma
sar_flip_down = self._prev_above_sar and not above_sar
sar_flip_up = not self._prev_above_sar and above_sar
if self._cooldown > 0:
self._cooldown -= 1
if self._cooldown == 0 and turned_bull:
if self.Position <= 0:
self.BuyMarket()
self._cooldown = self._cooldown_bars.Value
elif self._cooldown == 0 and turned_bear:
if self.Position >= 0:
self.SellMarket()
self._cooldown = self._cooldown_bars.Value
elif self.Position > 0 and sar_flip_down:
self.SellMarket()
self._cooldown = self._cooldown_bars.Value
elif self.Position < 0 and sar_flip_up:
self.BuyMarket()
self._cooldown = self._cooldown_bars.Value
self._prev_above_ma = above_ma
self._prev_above_sar = above_sar
def CreateClone(self):
return ma_parabolic_sar_strategy()