Strategie Stochastic Hook Reversal
Die Stochastic Hook Reversal-Strategie beobachtet die %K-Linie auf einen Haken aus dem überkauften oder überverkauften Bereich. Nachdem der Oszillator eine Extremzone erreicht hat, dreht er sich häufig zurück, was darauf hinweist, dass der Schwung nachlässt.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 166%. Die Strategie funktioniert am besten am Aktienmarkt.
Das System steigt long ein, wenn %K von unterhalb von zwanzig nach oben dreht, während der Preis ein neues Tief drückt. Es verkauft short, wenn der Oszillator von oberhalb von achtzig nach unten hakt, während eines finalen Aufwärtsschubs.
Positionen verwenden einen kleinen prozentualen Stop und schließen, wenn der Stochastik in die andere Richtung hakt oder der Stop erreicht wird.
Details
- Einstiegskriterien: Indikatorsignal
- Long/Short: Beide
- Ausstiegskriterien: Stop-Loss oder entgegengesetztes Signal
- Stops: Ja, prozentbasiert
- Standardwerte:
CandleType= 15 MinutenStopLoss= 2%
- Filter:
- Kategorie: Umkehr
- Richtung: Beide
- Indikatoren: Stochastic
- Stops: Ja
- Komplexität: Mittel
- Zeitrahmen: Intraday
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
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()