Estrategia de Zonas Dinámicas Stochastic
La estrategia Stochastic Dynamic Zones está construida en torno al Oscilador Stochastic con zonas dinámicas de sobrecompra/sobreventa.
Las pruebas indican un rendimiento anual promedio de aproximadamente 52%. Funciona mejor en el mercado de criptomonedas.
Las señales se activan cuando Stochastic confirma cambios de tendencia en datos intradía (5m). Esto hace que el método sea adecuado para traders activos.
Los stops dependen de múltiplos de ATR y factores como StochPeriod, StochKPeriod. Ajuste estos valores predeterminados para equilibrar el riesgo y la recompensa.
Detalles
- Criterios de entrada: ver implementación para condiciones de indicadores.
- Largo/Corto: Ambos.
- Criterios de salida: señal opuesta o lógica de stops.
- Stops: Sí, usando cálculos basados en indicadores.
- Valores predeterminados:
StochPeriod = 14StochKPeriod = 3StochDPeriod = 3LookbackPeriod = 20StandardDeviationFactor = 2.0mCandleType = TimeSpan.FromMinutes(5).TimeFrame()
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Ambos
- Indicadores: Stochastic
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Intradía (5m)
- 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>
/// Stochastic Oscillator with dynamic overbought and oversold zones.
/// The zones are the average of %K over <see cref="LookbackPeriod"/> bars plus/minus
/// <see cref="StandardDeviationFactor"/> standard deviations. Buys when %K crosses above %D inside the oversold zone
/// and sells when %K crosses below %D inside the overbought zone; the opposite signal reverses the position.
/// </summary>
public class StochasticWithDynamicZonesStrategy : Strategy
{
private readonly StrategyParam<int> _stochPeriod;
private readonly StrategyParam<int> _stochKPeriod;
private readonly StrategyParam<int> _stochDPeriod;
private readonly StrategyParam<int> _lookbackPeriod;
private readonly StrategyParam<decimal> _standardDeviationFactor;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _kSmoothing;
private SimpleMovingAverage _dLine;
private SimpleMovingAverage _zoneAverage;
private StandardDeviation _zoneStdDev;
private decimal? _prevK;
private decimal? _prevD;
/// <summary>
/// Stochastic lookback period.
/// </summary>
public int StochPeriod
{
get => _stochPeriod.Value;
set => _stochPeriod.Value = value;
}
/// <summary>
/// Smoothing period of %K.
/// </summary>
public int StochKPeriod
{
get => _stochKPeriod.Value;
set => _stochKPeriod.Value = value;
}
/// <summary>
/// Period of %D.
/// </summary>
public int StochDPeriod
{
get => _stochDPeriod.Value;
set => _stochDPeriod.Value = value;
}
/// <summary>
/// Lookback period for the dynamic zones.
/// </summary>
public int LookbackPeriod
{
get => _lookbackPeriod.Value;
set => _lookbackPeriod.Value = value;
}
/// <summary>
/// Standard deviation factor for the dynamic zones.
/// </summary>
public decimal StandardDeviationFactor
{
get => _standardDeviationFactor.Value;
set => _standardDeviationFactor.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="StochasticWithDynamicZonesStrategy"/>.
/// </summary>
public StochasticWithDynamicZonesStrategy()
{
_stochPeriod = Param(nameof(StochPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Stochastic Period", "Stochastic lookback period", "Indicators");
_stochKPeriod = Param(nameof(StochKPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("Stoch %K Period", "Smoothing period of %K", "Indicators");
_stochDPeriod = Param(nameof(StochDPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("Stoch %D Period", "Period of %D", "Indicators");
_lookbackPeriod = Param(nameof(LookbackPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Lookback Period", "Period for dynamic zones", "Indicators");
_standardDeviationFactor = Param(nameof(StandardDeviationFactor), 2.0m)
.SetGreaterThanZero()
.SetDisplay("StdDev Factor", "Standard deviation factor for dynamic zones", "Indicators");
_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 OnReseted()
{
base.OnReseted();
_kSmoothing = null;
_dLine = null;
_zoneAverage = null;
_zoneStdDev = null;
_prevK = null;
_prevD = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rawK = new StochasticK { Length = StochPeriod };
_kSmoothing = new SimpleMovingAverage { Length = StochKPeriod };
_dLine = new SimpleMovingAverage { Length = StochDPeriod };
_zoneAverage = new SimpleMovingAverage { Length = LookbackPeriod };
_zoneStdDev = new StandardDeviation { Length = LookbackPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rawK, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rawKValue)
{
if (candle.State != CandleStates.Finished)
return;
var k = _kSmoothing.Process(rawKValue, candle.ServerTime, true).ToDecimal();
if (!_kSmoothing.IsFormed)
return;
var d = _dLine.Process(k, candle.ServerTime, true).ToDecimal();
var average = _zoneAverage.Process(k, candle.ServerTime, true).ToDecimal();
var stdDev = _zoneStdDev.Process(k, candle.ServerTime, true).ToDecimal();
var prevK = _prevK;
var prevD = _prevD;
_prevK = k;
_prevD = d;
if (!_dLine.IsFormed || !_zoneAverage.IsFormed || !_zoneStdDev.IsFormed)
return;
if (prevK is not decimal pk || prevD is not decimal pd)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var oversold = average - StandardDeviationFactor * stdDev;
var overbought = average + StandardDeviationFactor * stdDev;
var crossUp = pk <= pd && k > d;
var crossDown = pk >= pd && k < d;
if (crossUp && k < oversold && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (crossDown && k > overbought && Position >= 0)
SellMarket(Volume + Math.Abs(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
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import StochasticK, SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class stochastic_with_dynamic_zones_strategy(Strategy):
"""
Stochastic Oscillator with dynamic overbought and oversold zones.
The zones are the average of %K over LookbackPeriod bars plus/minus StandardDeviationFactor
standard deviations. Buys when %K crosses above %D inside the oversold zone and sells when %K
crosses below %D inside the overbought zone; the opposite signal reverses the position.
"""
def __init__(self):
super(stochastic_with_dynamic_zones_strategy, self).__init__()
self._stoch_period = self.Param("StochPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("Stochastic Period", "Stochastic lookback period", "Indicators")
self._stoch_k_period = self.Param("StochKPeriod", 3) \
.SetGreaterThanZero() \
.SetDisplay("Stoch %K Period", "Smoothing period of %K", "Indicators")
self._stoch_d_period = self.Param("StochDPeriod", 3) \
.SetGreaterThanZero() \
.SetDisplay("Stoch %D Period", "Period of %D", "Indicators")
self._lookback_period = self.Param("LookbackPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("Lookback Period", "Period for dynamic zones", "Indicators")
self._standard_deviation_factor = self.Param("StandardDeviationFactor", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("StdDev Factor", "Standard deviation factor for dynamic zones", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._k_smoothing = None
self._d_line = None
self._zone_average = None
self._zone_std_dev = None
self._prev_k = None
self._prev_d = None
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnReseted(self):
super(stochastic_with_dynamic_zones_strategy, self).OnReseted()
self._k_smoothing = None
self._d_line = None
self._zone_average = None
self._zone_std_dev = None
self._prev_k = None
self._prev_d = None
def OnStarted2(self, time):
super(stochastic_with_dynamic_zones_strategy, self).OnStarted2(time)
raw_k = StochasticK()
raw_k.Length = self._stoch_period.Value
self._k_smoothing = SimpleMovingAverage()
self._k_smoothing.Length = self._stoch_k_period.Value
self._d_line = SimpleMovingAverage()
self._d_line.Length = self._stoch_d_period.Value
self._zone_average = SimpleMovingAverage()
self._zone_average.Length = self._lookback_period.Value
self._zone_std_dev = StandardDeviation()
self._zone_std_dev.Length = self._lookback_period.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(raw_k, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle, raw_k_value):
if candle.State != CandleStates.Finished:
return
t = candle.ServerTime
k = float(process_float(self._k_smoothing, raw_k_value, t, True))
if not self._k_smoothing.IsFormed:
return
d = float(process_float(self._d_line, k, t, True))
average = float(process_float(self._zone_average, k, t, True))
std_dev = float(process_float(self._zone_std_dev, k, t, True))
prev_k = self._prev_k
prev_d = self._prev_d
self._prev_k = k
self._prev_d = d
if not self._d_line.IsFormed or not self._zone_average.IsFormed or not self._zone_std_dev.IsFormed:
return
if prev_k is None or prev_d is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
factor = float(self._standard_deviation_factor.Value)
oversold = average - factor * std_dev
overbought = average + factor * std_dev
cross_up = prev_k <= prev_d and k > d
cross_down = prev_k >= prev_d and k < d
if cross_up and k < oversold and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif cross_down and k > overbought and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return stochastic_with_dynamic_zones_strategy()