Stochastic Slope Estrategia de Reversión a la Media
La estrategia Stochastic Slope de Reversión a la Media se centra en lecturas extremas del Stochastic para aprovechar la reversión. Las desviaciones amplias del nivel normal raramente perduran.
Las operaciones se activan cuando el indicador se aleja mucho de su media y luego comienza a revertirse. Tanto las configuraciones largas como las cortas incluyen un stop de protección.
Adecuada para traders de swing que esperan oscilaciones, la estrategia cierra la posición una vez que el Stochastic regresa al equilibrio. Parámetro inicial StochPeriod = 14.
Detalles
- Criterios de entrada: El indicador cruza de vuelta hacia la media.
- Largo/Corto: Ambas direcciones.
- Criterios de salida: El indicador revierte al promedio.
- Stops: Sí.
- Valores predeterminados:
StochPeriod= 14StochKPeriod= 3StochDPeriod= 3SlopeLookback= 20ThresholdMultiplier= 2mStopLossPercent= 2mCandleType= TimeSpan.FromMinutes(5)
- Filtros:
- Categoría: Reversión a la media
- Dirección: Ambos
- Indicadores: Stochastic
- Stops: Sí
- Complejidad: Intermedio
- 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.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Stochastic slope mean reversion strategy.
/// Trades reversions from extreme smoothed stochastic slopes and exits when the slope returns to its recent average.
/// </summary>
public class StochasticSlopeMeanReversionStrategy : Strategy
{
private readonly StrategyParam<int> _stochKPeriod;
private readonly StrategyParam<int> _stochDPeriod;
private readonly StrategyParam<int> _slopeLookback;
private readonly StrategyParam<decimal> _thresholdMultiplier;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<decimal> _longStochLevel;
private readonly StrategyParam<decimal> _shortStochLevel;
private readonly StrategyParam<DataType> _candleType;
private decimal[] _highs;
private decimal[] _lows;
private int _priceIndex;
private int _priceFilled;
private decimal[] _kValues;
private int _kIndex;
private int _kFilled;
private decimal _previousStochK;
private decimal[] _slopeHistory;
private int _slopeIndex;
private int _slopeFilled;
private int _cooldown;
private bool _isInitialized;
public int StochKPeriod
{
get => _stochKPeriod.Value;
set => _stochKPeriod.Value = value;
}
public int StochDPeriod
{
get => _stochDPeriod.Value;
set => _stochDPeriod.Value = value;
}
public int SlopeLookback
{
get => _slopeLookback.Value;
set => _slopeLookback.Value = value;
}
public decimal ThresholdMultiplier
{
get => _thresholdMultiplier.Value;
set => _thresholdMultiplier.Value = value;
}
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
public decimal LongStochLevel
{
get => _longStochLevel.Value;
set => _longStochLevel.Value = value;
}
public decimal ShortStochLevel
{
get => _shortStochLevel.Value;
set => _shortStochLevel.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public StochasticSlopeMeanReversionStrategy()
{
_stochKPeriod = Param(nameof(StochKPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Stoch %K Period", "Stochastic lookback period", "Stochastic");
_stochDPeriod = Param(nameof(StochDPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("Stoch %D Period", "Smoothing period for stochastic %K", "Stochastic");
_slopeLookback = Param(nameof(SlopeLookback), 20)
.SetGreaterThanZero()
.SetDisplay("Slope Lookback", "Period for slope statistics", "Slope");
_thresholdMultiplier = Param(nameof(ThresholdMultiplier), 1.5m)
.SetGreaterThanZero()
.SetDisplay("Threshold Multiplier", "Std dev multiplier for entry", "Slope");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk Management");
_cooldownBars = Param(nameof(CooldownBars), 1200)
.SetRange(1, 5000)
.SetDisplay("Cooldown Bars", "Bars to wait between orders", "Risk Management");
_longStochLevel = Param(nameof(LongStochLevel), 30m)
.SetRange(1m, 100m)
.SetDisplay("Long Stoch Level", "Maximum stochastic level for long entries", "Signal Filters");
_shortStochLevel = Param(nameof(ShortStochLevel), 70m)
.SetRange(1m, 100m)
.SetDisplay("Short Stoch Level", "Minimum stochastic level for short entries", "Signal Filters");
_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();
_highs = new decimal[StochKPeriod];
_lows = new decimal[StochKPeriod];
_priceIndex = default;
_priceFilled = default;
_kValues = new decimal[StochDPeriod];
_kIndex = default;
_kFilled = default;
_previousStochK = default;
_slopeHistory = new decimal[SlopeLookback];
_slopeIndex = default;
_slopeFilled = default;
_cooldown = default;
_isInitialized = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_highs = new decimal[StochKPeriod];
_lows = new decimal[StochKPeriod];
_kValues = new decimal[StochDPeriod];
_slopeHistory = new decimal[SlopeLookback];
_priceIndex = 0;
_priceFilled = 0;
_kIndex = 0;
_kFilled = 0;
_slopeIndex = 0;
_slopeFilled = 0;
_cooldown = 0;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
StartProtection(new(), new Unit(StopLossPercent, UnitTypes.Percent));
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_highs[_priceIndex] = candle.HighPrice;
_lows[_priceIndex] = candle.LowPrice;
_priceIndex = (_priceIndex + 1) % StochKPeriod;
if (_priceFilled < StochKPeriod)
_priceFilled++;
if (_priceFilled < StochKPeriod)
return;
var highest = decimal.MinValue;
var lowest = decimal.MaxValue;
for (var i = 0; i < StochKPeriod; i++)
{
highest = Math.Max(highest, _highs[i]);
lowest = Math.Min(lowest, _lows[i]);
}
var range = highest - lowest;
if (range <= 0)
return;
var rawK = (candle.ClosePrice - lowest) / range * 100m;
_kValues[_kIndex] = rawK;
_kIndex = (_kIndex + 1) % StochDPeriod;
if (_kFilled < StochDPeriod)
_kFilled++;
if (_kFilled < StochDPeriod)
return;
var stochK = 0m;
for (var i = 0; i < StochDPeriod; i++)
stochK += _kValues[i];
stochK /= StochDPeriod;
if (!_isInitialized)
{
_previousStochK = stochK;
_isInitialized = true;
return;
}
var slope = stochK - _previousStochK;
_previousStochK = stochK;
_slopeHistory[_slopeIndex] = slope;
_slopeIndex = (_slopeIndex + 1) % SlopeLookback;
if (_slopeFilled < SlopeLookback)
_slopeFilled++;
if (_slopeFilled < SlopeLookback)
return;
var avgSlope = 0m;
var sumSq = 0m;
for (var i = 0; i < SlopeLookback; i++)
avgSlope += _slopeHistory[i];
avgSlope /= SlopeLookback;
for (var i = 0; i < SlopeLookback; i++)
{
var diff = _slopeHistory[i] - avgSlope;
sumSq += diff * diff;
}
var stdDev = (decimal)Math.Sqrt((double)(sumSq / SlopeLookback));
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldown > 0)
{
_cooldown--;
return;
}
var lowerThreshold = avgSlope - ThresholdMultiplier * stdDev;
var upperThreshold = avgSlope + ThresholdMultiplier * stdDev;
if (Position == 0)
{
if (slope < lowerThreshold && stochK <= LongStochLevel)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (slope > upperThreshold && stochK >= ShortStochLevel)
{
SellMarket();
_cooldown = CooldownBars;
}
}
else if (Position > 0 && slope >= avgSlope)
{
SellMarket(Math.Abs(Position));
_cooldown = CooldownBars;
}
else if (Position < 0 && slope <= avgSlope)
{
BuyMarket(Math.Abs(Position));
_cooldown = CooldownBars;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
import math
from System import TimeSpan, Math
from StockSharp.Messages import DataType, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Strategies import Strategy
class stochastic_slope_mean_reversion_strategy(Strategy):
"""
Stochastic slope mean reversion strategy.
Trades reversions from extreme smoothed stochastic slopes and exits when the slope returns to its recent average.
"""
def __init__(self):
super(stochastic_slope_mean_reversion_strategy, self).__init__()
self._stoch_k_period = self.Param("StochKPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("Stoch %K Period", "Stochastic lookback period", "Stochastic")
self._stoch_d_period = self.Param("StochDPeriod", 3) \
.SetGreaterThanZero() \
.SetDisplay("Stoch %D Period", "Smoothing period for stochastic %K", "Stochastic")
self._slope_lookback = self.Param("SlopeLookback", 20) \
.SetGreaterThanZero() \
.SetDisplay("Slope Lookback", "Period for slope statistics", "Slope")
self._threshold_multiplier = self.Param("ThresholdMultiplier", 1.5) \
.SetGreaterThanZero() \
.SetDisplay("Threshold Multiplier", "Std dev multiplier for entry", "Slope")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk Management")
self._cooldown_bars = self.Param("CooldownBars", 1200) \
.SetDisplay("Cooldown Bars", "Bars to wait between orders", "Risk Management")
self._long_stoch_level = self.Param("LongStochLevel", 30.0) \
.SetDisplay("Long Stoch Level", "Maximum stochastic level for long entries", "Signal Filters")
self._short_stoch_level = self.Param("ShortStochLevel", 70.0) \
.SetDisplay("Short Stoch Level", "Minimum stochastic level for short entries", "Signal Filters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._highs = None
self._lows = None
self._price_index = 0
self._price_filled = 0
self._k_values = None
self._k_index = 0
self._k_filled = 0
self._previous_stoch_k = 0.0
self._slope_history = None
self._slope_index = 0
self._slope_filled = 0
self._cooldown = 0
self._is_initialized = False
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(stochastic_slope_mean_reversion_strategy, self).OnReseted()
kp = int(self._stoch_k_period.Value)
dp = int(self._stoch_d_period.Value)
lb = int(self._slope_lookback.Value)
self._highs = [0.0] * kp
self._lows = [0.0] * kp
self._price_index = 0
self._price_filled = 0
self._k_values = [0.0] * dp
self._k_index = 0
self._k_filled = 0
self._previous_stoch_k = 0.0
self._slope_history = [0.0] * lb
self._slope_index = 0
self._slope_filled = 0
self._cooldown = 0
self._is_initialized = False
def OnStarted2(self, time):
super(stochastic_slope_mean_reversion_strategy, self).OnStarted2(time)
kp = int(self._stoch_k_period.Value)
dp = int(self._stoch_d_period.Value)
lb = int(self._slope_lookback.Value)
self._highs = [0.0] * kp
self._lows = [0.0] * kp
self._k_values = [0.0] * dp
self._slope_history = [0.0] * lb
self._price_index = 0
self._price_filled = 0
self._k_index = 0
self._k_filled = 0
self._slope_index = 0
self._slope_filled = 0
self._cooldown = 0
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
self.StartProtection(Unit(), Unit(self._stop_loss_percent.Value, UnitTypes.Percent))
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
kp = int(self._stoch_k_period.Value)
dp = int(self._stoch_d_period.Value)
lb = int(self._slope_lookback.Value)
high_price = float(candle.HighPrice)
low_price = float(candle.LowPrice)
close_price = float(candle.ClosePrice)
self._highs[self._price_index] = high_price
self._lows[self._price_index] = low_price
self._price_index = (self._price_index + 1) % kp
if self._price_filled < kp:
self._price_filled += 1
if self._price_filled < kp:
return
highest = -1e18
lowest = 1e18
for i in range(kp):
if self._highs[i] > highest:
highest = self._highs[i]
if self._lows[i] < lowest:
lowest = self._lows[i]
rng = highest - lowest
if rng <= 0:
return
raw_k = (close_price - lowest) / rng * 100.0
self._k_values[self._k_index] = raw_k
self._k_index = (self._k_index + 1) % dp
if self._k_filled < dp:
self._k_filled += 1
if self._k_filled < dp:
return
stoch_k = 0.0
for i in range(dp):
stoch_k += self._k_values[i]
stoch_k /= float(dp)
if not self._is_initialized:
self._previous_stoch_k = stoch_k
self._is_initialized = True
return
slope = stoch_k - self._previous_stoch_k
self._previous_stoch_k = stoch_k
self._slope_history[self._slope_index] = slope
self._slope_index = (self._slope_index + 1) % lb
if self._slope_filled < lb:
self._slope_filled += 1
if self._slope_filled < lb:
return
avg_slope = 0.0
for i in range(lb):
avg_slope += self._slope_history[i]
avg_slope /= float(lb)
sum_sq = 0.0
for i in range(lb):
diff = self._slope_history[i] - avg_slope
sum_sq += diff * diff
std_dev = math.sqrt(sum_sq / float(lb))
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._cooldown > 0:
self._cooldown -= 1
return
tm = float(self._threshold_multiplier.Value)
lower_threshold = avg_slope - tm * std_dev
upper_threshold = avg_slope + tm * std_dev
long_level = float(self._long_stoch_level.Value)
short_level = float(self._short_stoch_level.Value)
if self.Position == 0:
if slope < lower_threshold and stoch_k <= long_level:
self.BuyMarket()
self._cooldown = int(self._cooldown_bars.Value)
elif slope > upper_threshold and stoch_k >= short_level:
self.SellMarket()
self._cooldown = int(self._cooldown_bars.Value)
elif self.Position > 0 and slope >= avg_slope:
self.SellMarket(Math.Abs(self.Position))
self._cooldown = int(self._cooldown_bars.Value)
elif self.Position < 0 and slope <= avg_slope:
self.BuyMarket(Math.Abs(self.Position))
self._cooldown = int(self._cooldown_bars.Value)
def CreateClone(self):
return stochastic_slope_mean_reversion_strategy()