Estrategia de Tendencia MA PSAR ATR
La estrategia de Tendencia MA PSAR ATR combina un cruce de medias móviles con un filtro Parabolic SAR diario. Las operaciones se realizan solo cuando el precio se alinea por encima o por debajo de ambas medias y el PSAR coincide. Un stop basado en ATR controla el riesgo.
El método es adecuado para traders que buscan seguimiento de tendencia con stops dinámicos. Las señales se activan en velas de 5 minutos por defecto.
Detalles
- Criterios de entrada:
- Largo: MA rápida > MA lenta, Cierre > MA rápida, Mínimo > PSAR diario
- Corto: MA rápida < MA lenta, Cierre < MA rápida, Máximo < PSAR diario
- Largo/Corto: Ambos lados.
- Criterios de salida:
- Largo: La tendencia se vuelve bajista o el precio cae por debajo del stop ATR
- Corto: La tendencia se vuelve alcista o el precio sube por encima del stop ATR
- Stops: Sí, basado en ATR.
- Valores predeterminados:
FastMaPeriod= 40SlowMaPeriod= 160SarStep= 0.02mSarMaxStep= 0.2mAtrPeriod= 14AtrMultiplierLong= 2mAtrMultiplierShort= 2mUsePsarFilter= trueCandleType= TimeSpan.FromMinutes(5)
- Filtros:
- Categoría: Tendencia
- Dirección: Ambos
- Indicadores: MA, Parabolic SAR, ATR
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Intradía
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// MA PSAR ATR Trend Strategy.
/// A long opens when Fast MA > Slow MA, the close is above the fast MA and the low is above the Parabolic SAR of the last
/// finished daily candle; a short mirrors this. UsePsarFilter switches the daily SAR condition off. Each entry gets a stop
/// AtrMultiplierLong or AtrMultiplierShort ATRs away; a position closes when the stop is hit or the fast MA crosses back
/// over the slow MA.
/// </summary>
public class MaPsarAtrTrendStrategy : Strategy
{
private readonly StrategyParam<int> _fastMaPeriod;
private readonly StrategyParam<int> _slowMaPeriod;
private readonly StrategyParam<decimal> _sarStep;
private readonly StrategyParam<decimal> _sarMaxStep;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplierLong;
private readonly StrategyParam<decimal> _atrMultiplierShort;
private readonly StrategyParam<bool> _usePsarFilter;
private readonly StrategyParam<DataType> _candleType;
private decimal? _dailySar;
private decimal? _stopPrice;
/// <summary>
/// Fast MA period.
/// </summary>
public int FastMaPeriod
{
get => _fastMaPeriod.Value;
set => _fastMaPeriod.Value = value;
}
/// <summary>
/// Slow MA period.
/// </summary>
public int SlowMaPeriod
{
get => _slowMaPeriod.Value;
set => _slowMaPeriod.Value = value;
}
/// <summary>
/// Parabolic SAR acceleration step.
/// </summary>
public decimal SarStep
{
get => _sarStep.Value;
set => _sarStep.Value = value;
}
/// <summary>
/// Parabolic SAR maximum acceleration.
/// </summary>
public decimal SarMaxStep
{
get => _sarMaxStep.Value;
set => _sarMaxStep.Value = value;
}
/// <summary>
/// ATR period.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// ATR multiplier of the long stop.
/// </summary>
public decimal AtrMultiplierLong
{
get => _atrMultiplierLong.Value;
set => _atrMultiplierLong.Value = value;
}
/// <summary>
/// ATR multiplier of the short stop.
/// </summary>
public decimal AtrMultiplierShort
{
get => _atrMultiplierShort.Value;
set => _atrMultiplierShort.Value = value;
}
/// <summary>
/// Require the daily Parabolic SAR to agree.
/// </summary>
public bool UsePsarFilter
{
get => _usePsarFilter.Value;
set => _usePsarFilter.Value = value;
}
/// <summary>
/// Candle type of the signals.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public MaPsarAtrTrendStrategy()
{
_fastMaPeriod = Param(nameof(FastMaPeriod), 40)
.SetGreaterThanZero()
.SetDisplay("Fast MA", "Fast MA period", "Indicators");
_slowMaPeriod = Param(nameof(SlowMaPeriod), 160)
.SetGreaterThanZero()
.SetDisplay("Slow MA", "Slow MA period", "Indicators");
_sarStep = Param(nameof(SarStep), 0.02m)
.SetGreaterThanZero()
.SetDisplay("SAR Step", "Parabolic SAR acceleration step", "PSAR");
_sarMaxStep = Param(nameof(SarMaxStep), 0.2m)
.SetGreaterThanZero()
.SetDisplay("SAR Max Step", "Parabolic SAR maximum acceleration", "PSAR");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period", "Risk");
_atrMultiplierLong = Param(nameof(AtrMultiplierLong), 2m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier Long", "ATR multiplier of the long stop", "Risk");
_atrMultiplierShort = Param(nameof(AtrMultiplierShort), 2m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier Short", "ATR multiplier of the short stop", "Risk");
_usePsarFilter = Param(nameof(UsePsarFilter), true)
.SetDisplay("Use PSAR Filter", "Require the daily Parabolic SAR to agree", "PSAR");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle type of the signals", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType), (Security, TimeSpan.FromDays(1).TimeFrame())];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_dailySar = null;
_stopPrice = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_dailySar = null;
_stopPrice = null;
var fastMa = new ExponentialMovingAverage { Length = FastMaPeriod };
var slowMa = new ExponentialMovingAverage { Length = SlowMaPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var psar = new ParabolicSar { AccelerationStep = SarStep, AccelerationMax = SarMaxStep };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(fastMa, slowMa, atr, ProcessCandle)
.Start();
SubscribeCandles(TimeSpan.FromDays(1).TimeFrame())
.BindEx(psar, ProcessDailyCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastMa);
DrawIndicator(area, slowMa);
DrawOwnTrades(area);
}
}
private void ProcessDailyCandle(ICandleMessage candle, IIndicatorValue psarValue)
{
if (candle.State != CandleStates.Finished || !psarValue.IsFormed)
return;
_dailySar = psarValue.GetValue<decimal>();
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue fastValue, IIndicatorValue slowValue, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!fastValue.IsFormed || !slowValue.IsFormed || !atrValue.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var fast = fastValue.GetValue<decimal>();
var slow = slowValue.GetValue<decimal>();
var atr = atrValue.GetValue<decimal>();
var close = candle.ClosePrice;
if (Position > 0)
{
if (fast < slow || (_stopPrice is decimal stop && candle.LowPrice <= stop))
{
SellMarket(Position);
_stopPrice = null;
}
return;
}
if (Position < 0)
{
if (fast > slow || (_stopPrice is decimal stop && candle.HighPrice >= stop))
{
BuyMarket(-Position);
_stopPrice = null;
}
return;
}
if (UsePsarFilter && _dailySar is null)
return;
var longSar = !UsePsarFilter || candle.LowPrice > _dailySar;
var shortSar = !UsePsarFilter || candle.HighPrice < _dailySar;
if (fast > slow && close > fast && longSar)
{
BuyMarket(Volume);
_stopPrice = close - atr * AtrMultiplierLong;
}
else if (fast < slow && close < fast && shortSar)
{
SellMarket(Volume);
_stopPrice = close + atr * AtrMultiplierShort;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, AverageTrueRange, ParabolicSar
from StockSharp.Algo.Strategies import Strategy
class ma_psar_atr_trend_strategy(Strategy):
"""
MA PSAR ATR Trend Strategy.
A long opens when Fast MA > Slow MA, the close is above the fast MA and the low is above the Parabolic SAR of the last
finished daily candle; a short mirrors this. UsePsarFilter switches the daily SAR condition off. Each entry gets a stop
AtrMultiplierLong or AtrMultiplierShort ATRs away; a position closes when the stop is hit or the fast MA crosses back
over the slow MA.
"""
def __init__(self):
super(ma_psar_atr_trend_strategy, self).__init__()
self._fast_ma_period = self.Param("FastMaPeriod", 40).SetGreaterThanZero().SetDisplay("Fast MA", "Fast MA period", "Indicators")
self._slow_ma_period = self.Param("SlowMaPeriod", 160).SetGreaterThanZero().SetDisplay("Slow MA", "Slow MA period", "Indicators")
self._sar_step = self.Param("SarStep", 0.02).SetGreaterThanZero().SetDisplay("SAR Step", "Parabolic SAR acceleration step", "PSAR")
self._sar_max_step = self.Param("SarMaxStep", 0.2).SetGreaterThanZero().SetDisplay("SAR Max Step", "Parabolic SAR maximum acceleration", "PSAR")
self._atr_period = self.Param("AtrPeriod", 14).SetGreaterThanZero().SetDisplay("ATR Period", "ATR period", "Risk")
self._atr_multiplier_long = self.Param("AtrMultiplierLong", 2.0).SetGreaterThanZero().SetDisplay("ATR Multiplier Long", "ATR multiplier of the long stop", "Risk")
self._atr_multiplier_short = self.Param("AtrMultiplierShort", 2.0).SetGreaterThanZero().SetDisplay("ATR Multiplier Short", "ATR multiplier of the short stop", "Risk")
self._use_psar_filter = self.Param("UsePsarFilter", True).SetDisplay("Use PSAR Filter", "Require the daily Parabolic SAR to agree", "PSAR")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candle type of the signals", "General")
self._daily_sar = None
self._stop_price = None
@property
def candle_type(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.candle_type), (self.Security, DataType.TimeFrame(TimeSpan.FromDays(1)))]
def OnReseted(self):
super(ma_psar_atr_trend_strategy, self).OnReseted()
self._daily_sar = None
self._stop_price = None
def OnStarted2(self, time):
super(ma_psar_atr_trend_strategy, self).OnStarted2(time)
self._daily_sar = None
self._stop_price = None
fast_ma = ExponentialMovingAverage()
fast_ma.Length = self._fast_ma_period.Value
slow_ma = ExponentialMovingAverage()
slow_ma.Length = self._slow_ma_period.Value
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
psar = ParabolicSar()
psar.AccelerationStep = Decimal(float(self._sar_step.Value))
psar.AccelerationMax = Decimal(float(self._sar_max_step.Value))
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(fast_ma, slow_ma, atr, self._process_candle).Start()
self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromDays(1))).BindEx(psar, self._process_daily_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ma)
self.DrawIndicator(area, slow_ma)
self.DrawOwnTrades(area)
def _process_daily_candle(self, candle, psar_value):
if candle.State != CandleStates.Finished or not psar_value.IsFormed:
return
self._daily_sar = float(psar_value.GetValue[Decimal](None))
def _process_candle(self, candle, fast_value, slow_value, atr_value):
if candle.State != CandleStates.Finished:
return
if not fast_value.IsFormed or not slow_value.IsFormed or not atr_value.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
fast = float(fast_value.GetValue[Decimal](None))
slow = float(slow_value.GetValue[Decimal](None))
atr = float(atr_value.GetValue[Decimal](None))
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
if self.Position > 0:
if fast < slow or (self._stop_price is not None and low <= self._stop_price):
self.SellMarket(self.Position)
self._stop_price = None
return
if self.Position < 0:
if fast > slow or (self._stop_price is not None and high >= self._stop_price):
self.BuyMarket(-self.Position)
self._stop_price = None
return
use_psar = self._use_psar_filter.Value
if use_psar and self._daily_sar is None:
return
long_sar = not use_psar or low > self._daily_sar
short_sar = not use_psar or high < self._daily_sar
if fast > slow and close > fast and long_sar:
self.BuyMarket(self.Volume)
self._stop_price = close - atr * float(self._atr_multiplier_long.Value)
elif fast < slow and close < fast and short_sar:
self.SellMarket(self.Volume)
self._stop_price = close + atr * float(self._atr_multiplier_short.Value)
def CreateClone(self):
return ma_psar_atr_trend_strategy()