Estrategia Stochastic Hook Reversal
La estrategia Stochastic Hook Reversal observa la línea %K en busca de un gancho fuera del territorio de sobrecompra o sobreventa. Después de extenderse a un extremo, el oscilador frecuentemente se curva de regreso, indicando que el impulso está disminuyendo.
Las pruebas indican un rendimiento anual promedio de aproximadamente 166%. Funciona mejor en el mercado de acciones.
El sistema entra largo cuando %K gira al alza desde por debajo de veinte mientras el precio presiona un nuevo mínimo. Vende en corto cuando el oscilador engancha hacia abajo desde por encima de ochenta durante un empuje final hacia arriba.
Las posiciones utilizan un pequeño stop porcentual y se cierran cuando el estocástico engancha en la otra dirección o se alcanza el stop.
Detalles
- Criterios de entrada: señal del indicador
- Largo/Corto: Ambos
- Criterios de salida: stop-loss o señal opuesta
- Stops: Sí, basado en porcentaje
- Valores predeterminados:
CandleType= 15 minutosStopLoss= 2%
- Filtros:
- Categoría: Reversión
- Dirección: Ambos
- Indicadores: Stochastic
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Intradía
- 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>
/// Stochastic Hook Reversal strategy.
/// Enters long when %K hooks up from oversold zone.
/// Enters short when %K hooks down from overbought zone.
/// Exits when %K reaches neutral zone.
/// Uses cooldown to control trade frequency.
/// </summary>
public class StochasticHookReversalStrategy : Strategy
{
private readonly StrategyParam<int> _kPeriod;
private readonly StrategyParam<int> _dPeriod;
private readonly StrategyParam<int> _oversoldLevel;
private readonly StrategyParam<int> _overboughtLevel;
private readonly StrategyParam<int> _exitLevel;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal? _prevK;
private int _cooldown;
/// <summary>
/// %K period.
/// </summary>
public int KPeriod
{
get => _kPeriod.Value;
set => _kPeriod.Value = value;
}
/// <summary>
/// %D period.
/// </summary>
public int DPeriod
{
get => _dPeriod.Value;
set => _dPeriod.Value = value;
}
/// <summary>
/// Oversold level.
/// </summary>
public int OversoldLevel
{
get => _oversoldLevel.Value;
set => _oversoldLevel.Value = value;
}
/// <summary>
/// Overbought level.
/// </summary>
public int OverboughtLevel
{
get => _overboughtLevel.Value;
set => _overboughtLevel.Value = value;
}
/// <summary>
/// Exit level (neutral zone).
/// </summary>
public int ExitLevel
{
get => _exitLevel.Value;
set => _exitLevel.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public StochasticHookReversalStrategy()
{
_kPeriod = Param(nameof(KPeriod), 14)
.SetRange(7, 21)
.SetDisplay("K Period", "%K period", "Stochastic");
_dPeriod = Param(nameof(DPeriod), 3)
.SetRange(1, 5)
.SetDisplay("D Period", "%D period", "Stochastic");
_oversoldLevel = Param(nameof(OversoldLevel), 20)
.SetRange(10, 30)
.SetDisplay("Oversold", "Oversold level", "Stochastic");
_overboughtLevel = Param(nameof(OverboughtLevel), 80)
.SetRange(70, 90)
.SetDisplay("Overbought", "Overbought level", "Stochastic");
_exitLevel = Param(nameof(ExitLevel), 50)
.SetRange(45, 55)
.SetDisplay("Exit Level", "Neutral exit zone", "Stochastic");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 500)
.SetRange(1, 1000)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevK = null;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevK = null;
_cooldown = 0;
var stochastic = new StochasticOscillator
{
K = { Length = KPeriod },
D = { Length = DPeriod },
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(stochastic, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, stochastic);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue stochIv)
{
if (candle.State != CandleStates.Finished)
return;
if (!stochIv.IsFormed)
return;
var sv = (IStochasticOscillatorValue)stochIv;
if (sv.K is not decimal kValue)
return;
if (_prevK == null)
{
_prevK = kValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevK = kValue;
return;
}
// Hook up from oversold
var oversoldHookUp = _prevK < OversoldLevel && kValue > _prevK;
// Hook down from overbought
var overboughtHookDown = _prevK > OverboughtLevel && kValue < _prevK;
if (Position == 0 && oversoldHookUp)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (Position == 0 && overboughtHookDown)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position > 0 && kValue < ExitLevel)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && kValue > ExitLevel)
{
BuyMarket();
_cooldown = CooldownBars;
}
_prevK = kValue;
}
}
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 StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
class stochastic_hook_reversal_strategy(Strategy):
"""
Stochastic Hook Reversal strategy.
Enters long when %K hooks up from oversold zone.
Enters short when %K hooks down from overbought zone.
Exits when %K reaches neutral zone.
"""
def __init__(self):
super(stochastic_hook_reversal_strategy, self).__init__()
self._k_period = self.Param("KPeriod", 14).SetDisplay("K Period", "%K period", "Stochastic")
self._d_period = self.Param("DPeriod", 3).SetDisplay("D Period", "%D period", "Stochastic")
self._oversold_level = self.Param("OversoldLevel", 20).SetDisplay("Oversold", "Oversold level", "Stochastic")
self._overbought_level = self.Param("OverboughtLevel", 80).SetDisplay("Overbought", "Overbought level", "Stochastic")
self._exit_level = self.Param("ExitLevel", 50).SetDisplay("Exit Level", "Neutral exit zone", "Stochastic")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 500).SetDisplay("Cooldown Bars", "Bars to wait between trades", "General")
self._prev_k = None
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(stochastic_hook_reversal_strategy, self).OnReseted()
self._prev_k = None
self._cooldown = 0
def OnStarted2(self, time):
super(stochastic_hook_reversal_strategy, self).OnStarted2(time)
self._prev_k = None
self._cooldown = 0
stochastic = StochasticOscillator()
stochastic.K.Length = self._k_period.Value
stochastic.D.Length = self._d_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(stochastic, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, stochastic)
self.DrawOwnTrades(area)
def _process_candle(self, candle, stoch_iv):
if candle.State != CandleStates.Finished:
return
if not stoch_iv.IsFormed:
return
k_val = stoch_iv.K
if k_val is None:
return
kv = float(k_val)
if self._prev_k is None:
self._prev_k = kv
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_k = kv
return
cd = self._cooldown_bars.Value
oversold = self._oversold_level.Value
overbought = self._overbought_level.Value
exit_lvl = self._exit_level.Value
# Hook up from oversold
oversold_hook_up = self._prev_k < oversold and kv > self._prev_k
# Hook down from overbought
overbought_hook_down = self._prev_k > overbought and kv < self._prev_k
if self.Position == 0 and oversold_hook_up:
self.BuyMarket()
self._cooldown = cd
elif self.Position == 0 and overbought_hook_down:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0 and kv < exit_lvl:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and kv > exit_lvl:
self.BuyMarket()
self._cooldown = cd
self._prev_k = kv
def CreateClone(self):
return stochastic_hook_reversal_strategy()