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. El giro del SAR es la única salida, así que no hace falta otro ajuste de stop.
Detalles
- Criterios de entrada:
- Largo:
Close > SAR && StochK < StochOversold
- Corto:
Close < SAR && StochK > StochOverbought
- 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.02m
MaxAccelerationFactor = 0.2m
StochK = 3
StochPeriod = 14
StochOversold = 20m
StochOverbought = 80m
CandleType = TimeSpan.FromMinutes(5).TimeFrame()
StochK en las reglas es %K: el estocástico de StochPeriod velas suavizado en StochK velas; %D no interviene, por lo que no tiene ajuste. Una señal de entrada contra una posición abierta la invierte.
- 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>
/// Parabolic SAR Stochastic strategy.
/// A close above the Parabolic SAR with %K below StochOversold goes long and a close below the SAR with %K above StochOverbought goes short,
/// reversing an opposite position; %K is the stochastic over StochPeriod candles smoothed over StochK candles. The SAR is the trailing stop:
/// a long closes when the SAR flips above price and a short when it flips below.
/// </summary>
public class ParabolicSarStochasticStrategy : Strategy
{
private readonly StrategyParam<decimal> _accelerationFactor;
private readonly StrategyParam<decimal> _maxAccelerationFactor;
private readonly StrategyParam<int> _stochK;
private readonly StrategyParam<int> _stochPeriod;
private readonly StrategyParam<decimal> _stochOversold;
private readonly StrategyParam<decimal> _stochOverbought;
private readonly StrategyParam<DataType> _candleType;
/// <summary>
/// Initial acceleration factor of the SAR.
/// </summary>
public decimal AccelerationFactor
{
get => _accelerationFactor.Value;
set => _accelerationFactor.Value = value;
}
/// <summary>
/// Maximum acceleration factor of the SAR.
/// </summary>
public decimal MaxAccelerationFactor
{
get => _maxAccelerationFactor.Value;
set => _maxAccelerationFactor.Value = value;
}
/// <summary>
/// Smoothing period of %K.
/// </summary>
public int StochK
{
get => _stochK.Value;
set => _stochK.Value = value;
}
/// <summary>
/// Lookback period of the raw stochastic.
/// </summary>
public int StochPeriod
{
get => _stochPeriod.Value;
set => _stochPeriod.Value = value;
}
/// <summary>
/// %K level for longs.
/// </summary>
public decimal StochOversold
{
get => _stochOversold.Value;
set => _stochOversold.Value = value;
}
/// <summary>
/// %K level for shorts.
/// </summary>
public decimal StochOverbought
{
get => _stochOverbought.Value;
set => _stochOverbought.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public ParabolicSarStochasticStrategy()
{
_accelerationFactor = Param(nameof(AccelerationFactor), 0.02m)
.SetGreaterThanZero()
.SetDisplay("SAR Acceleration", "Initial acceleration factor of the SAR", "SAR");
_maxAccelerationFactor = Param(nameof(MaxAccelerationFactor), 0.2m)
.SetGreaterThanZero()
.SetDisplay("SAR Max Acceleration", "Maximum acceleration factor of the SAR", "SAR");
_stochK = Param(nameof(StochK), 3)
.SetGreaterThanZero()
.SetDisplay("Stochastic %K", "Smoothing period of %K", "Stochastic");
_stochPeriod = Param(nameof(StochPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Stochastic Period", "Lookback period of the raw stochastic", "Stochastic");
_stochOversold = Param(nameof(StochOversold), 20m)
.SetDisplay("Stochastic Oversold", "%K level for longs", "Stochastic");
_stochOverbought = Param(nameof(StochOverbought), 80m)
.SetDisplay("Stochastic Overbought", "%K level for shorts", "Stochastic");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sar = new ParabolicSar
{
Acceleration = AccelerationFactor,
AccelerationMax = MaxAccelerationFactor,
};
// The D line of the core oscillator is the smoothed %K.
var stochastic = new StochasticOscillator
{
K = { Length = StochPeriod },
D = { Length = StochK },
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(sar, stochastic, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sar);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, stochastic);
}
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue sarValue, IIndicatorValue stochasticValue)
{
if (candle.State != CandleStates.Finished)
return;
// The first SAR value is formed but empty.
if (!sarValue.IsFormed || sarValue.IsEmpty || !stochasticValue.IsFormed)
return;
if (stochasticValue is not IStochasticOscillatorValue { D: decimal k })
return;
var sar = sarValue.GetValue<decimal>();
var close = candle.ClosePrice;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (close > sar && k < StochOversold && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (close < sar && k > StochOverbought && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && close < sar)
SellMarket(Position);
else if (Position < 0 && close > sar)
BuyMarket(-Position);
}
}
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 ParabolicSar, StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
class parabolic_sar_stochastic_strategy(Strategy):
"""
Parabolic SAR Stochastic strategy.
A close above the Parabolic SAR with %K below StochOversold goes long and a close below the SAR with %K above StochOverbought goes short,
reversing an opposite position; %K is the stochastic over StochPeriod candles smoothed over StochK candles. The SAR is the trailing stop:
a long closes when the SAR flips above price and a short when it flips below.
"""
def __init__(self):
super(parabolic_sar_stochastic_strategy, self).__init__()
self._acceleration_factor = self.Param("AccelerationFactor", 0.02).SetGreaterThanZero().SetDisplay("SAR Acceleration", "Initial acceleration factor of the SAR", "SAR")
self._max_acceleration_factor = self.Param("MaxAccelerationFactor", 0.2).SetGreaterThanZero().SetDisplay("SAR Max Acceleration", "Maximum acceleration factor of the SAR", "SAR")
self._stoch_k = self.Param("StochK", 3).SetGreaterThanZero().SetDisplay("Stochastic %K", "Smoothing period of %K", "Stochastic")
self._stoch_period = self.Param("StochPeriod", 14).SetGreaterThanZero().SetDisplay("Stochastic Period", "Lookback period of the raw stochastic", "Stochastic")
self._stoch_oversold = self.Param("StochOversold", 20.0).SetDisplay("Stochastic Oversold", "%K level for longs", "Stochastic")
self._stoch_overbought = self.Param("StochOverbought", 80.0).SetDisplay("Stochastic Overbought", "%K level for shorts", "Stochastic")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(parabolic_sar_stochastic_strategy, self).OnStarted2(time)
sar = ParabolicSar()
sar.Acceleration = Decimal(self._acceleration_factor.Value)
sar.AccelerationMax = Decimal(self._max_acceleration_factor.Value)
# The D line of the core oscillator is the smoothed %K.
stochastic = StochasticOscillator()
stochastic.K.Length = self._stoch_period.Value
stochastic.D.Length = self._stoch_k.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(sar, stochastic, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sar)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, stochastic)
def _process_candle(self, candle, sar_value, stochastic_value):
if candle.State != CandleStates.Finished:
return
# The first SAR value is formed but empty.
if not sar_value.IsFormed or sar_value.IsEmpty or not stochastic_value.IsFormed or stochastic_value.D is None:
return
sar = sar_value.GetValue[Decimal](None)
k = stochastic_value.D
close = candle.ClosePrice
if not self.IsFormedAndOnlineAndAllowTrading():
return
if close > sar and k < Decimal(self._stoch_oversold.Value) and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif close < sar and k > Decimal(self._stoch_overbought.Value) and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and close < sar:
self.SellMarket(self.Position)
elif self.Position < 0 and close > sar:
self.BuyMarket(-self.Position)
def CreateClone(self):
return parabolic_sar_stochastic_strategy()