Estrategia de Captura de Tendencia
La estrategia Trend Catcher combina el Parabolic SAR con múltiples medias móviles simples para capturar movimientos direccionales. Espera a que el precio cruce el Parabolic SAR en la dirección de las medias rápidas predominantes y luego gestiona la posición mediante reglas dinámicas de stop-loss y trailing.
Se abre una operación cuando la última vela cierra en el lado opuesto del Parabolic SAR respecto a la vela anterior, mientras las medias rápidas confirman el movimiento. El stop-loss inicial se calcula a partir de la distancia al punto SAR y está limitado por valores mínimos y máximos. Los objetivos de beneficio se definen como un múltiplo de la distancia del stop. Cuando el precio avanza una cantidad especificada, el stop se mueve al punto de equilibrio con un pequeño desplazamiento y luego sigue al precio.
Detalles
- Criterios de entrada:
- Largo:
Close[0] > SAR && Close[1] < SAR_prev && FastMA > SlowMA && Close > FastMA2.
- Corto:
Close[0] < SAR && Close[1] > SAR_prev && FastMA < SlowMA && Close < FastMA2.
- Criterios de salida:
- Se alcanzan los niveles de stop-loss o take-profit.
- Trailing stop activado tras el umbral de beneficio.
- Una señal contraria cierra la posición existente.
- Stops: Stop-loss dinámico basado en SAR con ajustes opcionales de punto de equilibrio y trailing.
- Valores predeterminados:
SlowMaPeriod = 200
FastMaPeriod = 50
FastMa2Period = 25
SarStep = 0.004
SarMax = 0.2
SlMultiplier = 1
TpMultiplier = 1
MinStopLoss = 10
MaxStopLoss = 200
ProfitLevel = 500
BreakevenOffset = 1
TrailingThreshold = 500
TrailingDistance = 10
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Ambos
- Indicadores: Parabolic SAR, SMA
- Stops: Sí
- Complejidad: Moderado
- Marco temporal: Corto plazo
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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>
/// Parabolic SAR trend catching strategy.
/// Uses Parabolic SAR flip with MA trend filter for entries.
/// </summary>
public class TrendCatcherStrategy : Strategy
{
private readonly StrategyParam<int> _slowMaPeriod;
private readonly StrategyParam<int> _fastMaPeriod;
private readonly StrategyParam<decimal> _sarStep;
private readonly StrategyParam<decimal> _sarMax;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _slowMa;
private bool _isInitialized;
private bool _isPriceAboveSarPrev;
public int SlowMaPeriod { get => _slowMaPeriod.Value; set => _slowMaPeriod.Value = value; }
public int FastMaPeriod { get => _fastMaPeriod.Value; set => _fastMaPeriod.Value = value; }
public decimal SarStep { get => _sarStep.Value; set => _sarStep.Value = value; }
public decimal SarMax { get => _sarMax.Value; set => _sarMax.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public TrendCatcherStrategy()
{
_slowMaPeriod = Param(nameof(SlowMaPeriod), 200)
.SetGreaterThanZero()
.SetDisplay("Slow MA Period", "Period of the slow moving average", "Moving Averages");
_fastMaPeriod = Param(nameof(FastMaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Fast MA Period", "Period of the fast moving average", "Moving Averages");
_sarStep = Param(nameof(SarStep), 0.004m)
.SetDisplay("SAR Step", "Parabolic SAR acceleration step", "Parabolic SAR");
_sarMax = Param(nameof(SarMax), 0.2m)
.SetDisplay("SAR Max", "Parabolic SAR maximum acceleration", "Parabolic SAR");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_slowMa = default;
_isInitialized = default;
_isPriceAboveSarPrev = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_isInitialized = false;
var sar = new ParabolicSar
{
Acceleration = SarStep,
AccelerationStep = SarStep,
AccelerationMax = SarMax
};
var fastMa = new ExponentialMovingAverage { Length = FastMaPeriod };
_slowMa = new ExponentialMovingAverage { Length = SlowMaPeriod };
Indicators.Add(_slowMa);
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(sar, fastMa, ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(3, UnitTypes.Percent),
stopLoss: new Unit(2, UnitTypes.Percent),
isStopTrailing: true
);
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue sarValue, IIndicatorValue fastMaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!sarValue.IsFormed || !fastMaValue.IsFormed)
return;
var sar = sarValue.ToDecimal();
var fastValue = fastMaValue.ToDecimal();
var slowResult = _slowMa.Process(candle.ClosePrice, candle.OpenTime, true);
if (!slowResult.IsFormed)
return;
var slowValue = slowResult.ToDecimal();
var isPriceAboveSar = candle.ClosePrice > sar;
if (!_isInitialized)
{
_isPriceAboveSarPrev = isPriceAboveSar;
_isInitialized = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Buy: SAR flips below price + fast MA above slow MA
var buySignal = isPriceAboveSar && !_isPriceAboveSarPrev && fastValue > slowValue;
// Sell: SAR flips above price + fast MA below slow MA
var sellSignal = !isPriceAboveSar && _isPriceAboveSarPrev && fastValue < slowValue;
if (buySignal && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (sellSignal && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_isPriceAboveSarPrev = 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, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage, ParabolicSar
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class trend_catcher_strategy(Strategy):
def __init__(self):
super(trend_catcher_strategy, self).__init__()
self._slow_ma_period = self.Param("SlowMaPeriod", 200) \
.SetDisplay("Slow MA Period", "Period of the slow moving average", "Moving Averages")
self._fast_ma_period = self.Param("FastMaPeriod", 50) \
.SetDisplay("Fast MA Period", "Period of the fast moving average", "Moving Averages")
self._sar_step = self.Param("SarStep", 0.004) \
.SetDisplay("SAR Step", "Parabolic SAR acceleration step", "Parabolic SAR")
self._sar_max = self.Param("SarMax", 0.2) \
.SetDisplay("SAR Max", "Parabolic SAR maximum acceleration", "Parabolic SAR")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._slow_ma = None
self._is_initialized = False
self._is_price_above_sar_prev = False
@property
def SlowMaPeriod(self):
return self._slow_ma_period.Value
@SlowMaPeriod.setter
def SlowMaPeriod(self, value):
self._slow_ma_period.Value = value
@property
def FastMaPeriod(self):
return self._fast_ma_period.Value
@FastMaPeriod.setter
def FastMaPeriod(self, value):
self._fast_ma_period.Value = value
@property
def SarStep(self):
return self._sar_step.Value
@SarStep.setter
def SarStep(self, value):
self._sar_step.Value = value
@property
def SarMax(self):
return self._sar_max.Value
@SarMax.setter
def SarMax(self, value):
self._sar_max.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(trend_catcher_strategy, self).OnStarted2(time)
self._is_initialized = False
sar = ParabolicSar()
sar.Acceleration = self.SarStep
sar.AccelerationStep = self.SarStep
sar.AccelerationMax = self.SarMax
fast_ma = ExponentialMovingAverage()
fast_ma.Length = self.FastMaPeriod
self._slow_ma = ExponentialMovingAverage()
self._slow_ma.Length = self.SlowMaPeriod
self.SubscribeCandles(self.CandleType) \
.BindEx(sar, fast_ma, self.ProcessCandle) \
.Start()
self.StartProtection(
takeProfit=Unit(3.0, UnitTypes.Percent),
stopLoss=Unit(2.0, UnitTypes.Percent),
isStopTrailing=True
)
def ProcessCandle(self, candle, sar_value, fast_ma_value):
if candle.State != CandleStates.Finished:
return
if not sar_value.IsFormed or not fast_ma_value.IsFormed:
return
sar = float(sar_value)
fast_val = float(fast_ma_value)
close = float(candle.ClosePrice)
t = candle.OpenTime
slow_result = process_float(self._slow_ma, close, t, True)
if not slow_result.IsFormed:
return
slow_val = float(slow_result)
is_price_above_sar = close > sar
if not self._is_initialized:
self._is_price_above_sar_prev = is_price_above_sar
self._is_initialized = True
return
buy_signal = is_price_above_sar and not self._is_price_above_sar_prev and fast_val > slow_val
sell_signal = not is_price_above_sar and self._is_price_above_sar_prev and fast_val < slow_val
if buy_signal and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif sell_signal and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._is_price_above_sar_prev = is_price_above_sar
def OnReseted(self):
super(trend_catcher_strategy, self).OnReseted()
self._slow_ma = None
self._is_initialized = False
self._is_price_above_sar_prev = False
def CreateClone(self):
return trend_catcher_strategy()