Estrategia Parabolic SAR Stochastic
Implementación de la estrategia Parabolic SAR + Stochastic. Comprar cuando el precio está por encima del SAR y el Stochastic %K está por debajo de 20 (sobrevendido). Vender cuando el precio está por debajo del SAR y el Stochastic %K está por encima de 80 (sobrecomprado).
Las pruebas indican un retorno anual promedio de aproximadamente el 61%. Funciona mejor en el mercado de criptomonedas.
El Parabolic SAR proporciona la tendencia y el Stochastic refina la entrada en los retrocesos. Las señales cambian cuando el SAR cambia de lado.
Una estrategia de tendencia directa con stops SAR integrados. La configuración del ATR gestiona el control de riesgo adicional.
Detalles
- Criterios de entrada:
- Largo:
Close > SAR && StochK < StochOversold - Corto:
Close < SAR && StochK > StochOverbought
- Largo:
- Largo/Corto: Ambos
- Criterios de salida:
- Inversión del Parabolic SAR en dirección opuesta
- Stops: Dinámicos basados en SAR
- Valores predeterminados:
AccelerationFactor= 0.02mMaxAccelerationFactor= 0.2mStochK= 3StochD= 3StochPeriod= 14StochOversold= 20mStochOverbought= 80mCandleType= TimeSpan.FromMinutes(5).TimeFrame()
- Filtros:
- Categoría: Reversión a la media
- Dirección: Ambos
- Indicadores: Parabolic SAR, Parabolic SAR, Stochastic Oscillator
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Medio 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>
/// Strategy combining Parabolic SAR trend direction with Stochastic entry confirmation.
/// </summary>
public class ParabolicSarStochasticStrategy : Strategy
{
private readonly StrategyParam<decimal> _accelerationFactor;
private readonly StrategyParam<decimal> _maxAccelerationFactor;
private readonly StrategyParam<int> _stochK;
private readonly StrategyParam<int> _stochD;
private readonly StrategyParam<int> _stochPeriod;
private readonly StrategyParam<decimal> _stochOversold;
private readonly StrategyParam<decimal> _stochOverbought;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private decimal _sarValue;
private decimal _lastStochK;
private bool _isAboveSar;
private bool _hasTrendState;
private int _cooldown;
/// <summary>
/// Parabolic SAR acceleration factor.
/// </summary>
public decimal AccelerationFactor
{
get => _accelerationFactor.Value;
set => _accelerationFactor.Value = value;
}
/// <summary>
/// Parabolic SAR max acceleration factor.
/// </summary>
public decimal MaxAccelerationFactor
{
get => _maxAccelerationFactor.Value;
set => _maxAccelerationFactor.Value = value;
}
/// <summary>
/// Stochastic K period.
/// </summary>
public int StochK
{
get => _stochK.Value;
set => _stochK.Value = value;
}
/// <summary>
/// Stochastic D period.
/// </summary>
public int StochD
{
get => _stochD.Value;
set => _stochD.Value = value;
}
/// <summary>
/// Stochastic main period.
/// </summary>
public int StochPeriod
{
get => _stochPeriod.Value;
set => _stochPeriod.Value = value;
}
/// <summary>
/// Stochastic oversold level.
/// </summary>
public decimal StochOversold
{
get => _stochOversold.Value;
set => _stochOversold.Value = value;
}
/// <summary>
/// Stochastic overbought level.
/// </summary>
public decimal StochOverbought
{
get => _stochOverbought.Value;
set => _stochOverbought.Value = value;
}
/// <summary>
/// Bars to wait between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Candle type used for strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Strategy constructor.
/// </summary>
public ParabolicSarStochasticStrategy()
{
_accelerationFactor = Param(nameof(AccelerationFactor), 0.02m)
.SetRange(0.01m, 0.2m)
.SetDisplay("Acceleration Factor", "Initial acceleration factor for SAR", "SAR");
_maxAccelerationFactor = Param(nameof(MaxAccelerationFactor), 0.2m)
.SetRange(0.05m, 0.5m)
.SetDisplay("Max Acceleration Factor", "Maximum acceleration factor for SAR", "SAR");
_stochK = Param(nameof(StochK), 3)
.SetRange(1, 10)
.SetDisplay("Stochastic %K", "Stochastic %K smoothing period", "Stochastic");
_stochD = Param(nameof(StochD), 3)
.SetRange(1, 10)
.SetDisplay("Stochastic %D", "Stochastic %D smoothing period", "Stochastic");
_stochPeriod = Param(nameof(StochPeriod), 14)
.SetRange(5, 30)
.SetDisplay("Stochastic Period", "Main stochastic period", "Stochastic");
_stochOversold = Param(nameof(StochOversold), 20m)
.SetDisplay("Oversold Level", "Stochastic oversold level", "Stochastic");
_stochOverbought = Param(nameof(StochOverbought), 80m)
.SetDisplay("Overbought Level", "Stochastic overbought level", "Stochastic");
_cooldownBars = Param(nameof(CooldownBars), 160)
.SetRange(5, 500)
.SetDisplay("Cooldown Bars", "Bars between trades", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle type for strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_sarValue = 0;
_lastStochK = 50m;
_isAboveSar = false;
_hasTrendState = false;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var parabolicSar = new ParabolicSar
{
AccelerationStep = AccelerationFactor,
AccelerationMax = MaxAccelerationFactor
};
var stochastic = new StochasticOscillator
{
K = { Length = StochK },
D = { Length = StochD },
};
var subscription = SubscribeCandles(CandleType);
subscription.BindEx(parabolicSar, OnSar);
subscription
.BindEx(stochastic, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, parabolicSar);
DrawOwnTrades(area);
var stochArea = CreateChartArea();
if (stochArea != null)
DrawIndicator(stochArea, stochastic);
}
}
private void OnSar(ICandleMessage candle, IIndicatorValue sarValue)
{
if (candle is null || sarValue is null)
return;
if (candle.State != CandleStates.Finished || !sarValue.IsFormed)
return;
_sarValue = sarValue.ToDecimal();
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue stochValue)
{
if (candle is null || stochValue is null)
return;
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_sarValue == 0 || !stochValue.IsFormed)
return;
if (stochValue is not IStochasticOscillatorValue stochTyped || stochTyped.K is not decimal stochK)
return;
var close = candle.ClosePrice;
var priceAboveSar = close > _sarValue;
if (!_hasTrendState)
{
_isAboveSar = priceAboveSar;
_hasTrendState = true;
_lastStochK = stochK;
return;
}
var sarSignalChange = priceAboveSar != _isAboveSar;
if (_cooldown > 0)
{
_cooldown--;
_lastStochK = stochK;
_isAboveSar = priceAboveSar;
return;
}
var longEntry = Position == 0
&& priceAboveSar
&& _lastStochK <= StochOversold
&& stochK > _lastStochK;
var shortEntry = Position == 0
&& !priceAboveSar
&& _lastStochK >= StochOverbought
&& stochK < _lastStochK;
if (longEntry)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (shortEntry)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (sarSignalChange && Position > 0 && !priceAboveSar)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (sarSignalChange && Position < 0 && priceAboveSar)
{
BuyMarket();
_cooldown = CooldownBars;
}
_lastStochK = stochK;
_isAboveSar = priceAboveSar;
}
}
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 ParabolicSar, StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class parabolic_sar_stochastic_strategy(Strategy):
"""
Strategy combining Parabolic SAR trend direction with Stochastic entry confirmation.
"""
def __init__(self):
super(parabolic_sar_stochastic_strategy, self).__init__()
self._acceleration_factor = self.Param("AccelerationFactor", 0.02) \
.SetRange(0.01, 0.2) \
.SetDisplay("Acceleration Factor", "Initial acceleration factor for SAR", "SAR")
self._max_acceleration_factor = self.Param("MaxAccelerationFactor", 0.2) \
.SetRange(0.05, 0.5) \
.SetDisplay("Max Acceleration Factor", "Maximum acceleration factor for SAR", "SAR")
self._stoch_k = self.Param("StochK", 3) \
.SetRange(1, 10) \
.SetDisplay("Stochastic %K", "Stochastic %K smoothing period", "Stochastic")
self._stoch_d = self.Param("StochD", 3) \
.SetRange(1, 10) \
.SetDisplay("Stochastic %D", "Stochastic %D smoothing period", "Stochastic")
self._stoch_oversold = self.Param("StochOversold", 20.0) \
.SetDisplay("Oversold Level", "Stochastic oversold level", "Stochastic")
self._stoch_overbought = self.Param("StochOverbought", 80.0) \
.SetDisplay("Overbought Level", "Stochastic overbought level", "Stochastic")
self._cooldown_bars = self.Param("CooldownBars", 160) \
.SetRange(5, 500) \
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Candle type for strategy", "General")
self._sar_value = 0.0
self._last_stoch_k = 50.0
self._is_above_sar = False
self._has_trend_state = False
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(parabolic_sar_stochastic_strategy, self).OnStarted2(time)
self._sar_value = 0.0
self._last_stoch_k = 50.0
self._is_above_sar = False
self._has_trend_state = False
self._cooldown = 0
sar = ParabolicSar()
sar.AccelerationStep = self._acceleration_factor.Value
sar.AccelerationMax = self._max_acceleration_factor.Value
stoch = StochasticOscillator()
stoch.K.Length = self._stoch_k.Value
stoch.D.Length = self._stoch_d.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(sar, self.OnSar)
subscription.BindEx(stoch, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sar)
self.DrawOwnTrades(area)
stoch_area = self.CreateChartArea()
if stoch_area is not None:
self.DrawIndicator(stoch_area, stoch)
def OnSar(self, candle, sar_value):
if candle is None or sar_value is None:
return
if candle.State != CandleStates.Finished or not sar_value.IsFormed:
return
self._sar_value = float(sar_value)
def ProcessCandle(self, candle, stoch_value):
if candle is None or stoch_value is None:
return
if candle.State != CandleStates.Finished:
return
if self._sar_value == 0 or not stoch_value.IsFormed:
return
if not hasattr(stoch_value, 'K') or stoch_value.K is None:
return
stoch_k = float(stoch_value.K)
close = float(candle.ClosePrice)
price_above_sar = close > self._sar_value
if not self._has_trend_state:
self._is_above_sar = price_above_sar
self._has_trend_state = True
self._last_stoch_k = stoch_k
return
sar_signal_change = price_above_sar != self._is_above_sar
if self._cooldown > 0:
self._cooldown -= 1
self._last_stoch_k = stoch_k
self._is_above_sar = price_above_sar
return
cd = self._cooldown_bars.Value
os_level = self._stoch_oversold.Value
ob_level = self._stoch_overbought.Value
long_entry = (self.Position == 0 and price_above_sar
and self._last_stoch_k <= os_level and stoch_k > self._last_stoch_k)
short_entry = (self.Position == 0 and not price_above_sar
and self._last_stoch_k >= ob_level and stoch_k < self._last_stoch_k)
if long_entry:
self.BuyMarket()
self._cooldown = cd
elif short_entry:
self.SellMarket()
self._cooldown = cd
elif sar_signal_change and self.Position > 0 and not price_above_sar:
self.SellMarket()
self._cooldown = cd
elif sar_signal_change and self.Position < 0 and price_above_sar:
self.BuyMarket()
self._cooldown = cd
self._last_stoch_k = stoch_k
self._is_above_sar = price_above_sar
def OnReseted(self):
super(parabolic_sar_stochastic_strategy, self).OnReseted()
self._sar_value = 0.0
self._last_stoch_k = 50.0
self._is_above_sar = False
self._has_trend_state = False
self._cooldown = 0
def CreateClone(self):
return parabolic_sar_stochastic_strategy()