Die Strategie reagiert auf extreme Niveaus des Stochastic Oszillators. Wenn die %K-Linie in überverkauftes Terrain abtaucht, erwartet das System eine Erholung, während überkaufte Werte einen Rückgang ankündigen können. Die Methode läuft auf kurzen Intraday-Kerzen, sodass Signale schnell eintreffen.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 73%. Sie funktioniert am besten auf dem Kryptomarkt.
Nach der Anmeldung auf dem ausgewählten Zeitrahmen überwacht sie die %K- und %D-Linien. Ein bullisches Setup entsteht, wenn %K unter 20 fällt und dann beginnt, sich zu erholen. Umgekehrt erscheint ein bärisches Setup, wenn %K über 80 steigt und anfängt, sich nach unten zu drehen. Ein fester prozentualer Stop kontrolliert das Risiko für beide Seiten.
Positionen werden geschlossen, wenn die %K-Linie wieder durch das Niveau 50 kreuzt, was signalisiert, dass sich der Schwung in die entgegengesetzte Richtung verschoben hat. Da die Stops mit dem aktuellen ATR skalieren, passt sich die Handelsgröße an die Volatilität an.
Details
Einstiegskriterien:
Long: %K < 20 mit bullischer Wende.
Short: %K > 80 mit bärischer Wende.
Long/Short: Beide.
Ausstiegskriterien: %K kreuzt Niveau 50 oder Stop-Loss.
Stops: Ja, bei 2% Abstand.
Standardwerte:
StochPeriod = 14
KPeriod = 3
DPeriod = 3
CandleType = 5 minute
Filter:
Kategorie: Oszillator
Richtung: Beide
Indikatoren: Stochastic
Stops: Ja
Komplexität: Grundlegend
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 Overbought/Oversold strategy.
/// Buys when K is oversold, sells when K is overbought.
/// </summary>
public class StochasticOverboughtOversoldStrategy : Strategy
{
private readonly StrategyParam<int> _kPeriod;
private readonly StrategyParam<int> _dPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
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>
/// 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 StochasticOverboughtOversoldStrategy()
{
_kPeriod = Param(nameof(KPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("K Period", "Smoothing period for %K", "Indicators");
_dPeriod = Param(nameof(DPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("D Period", "Smoothing period for %D", "Indicators");
_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();
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_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 stochValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!stochValue.IsFormed)
return;
var stochTyped = (StochasticOscillatorValue)stochValue;
if (stochTyped.K is not decimal kValue)
return;
if (_cooldown > 0)
{
_cooldown--;
return;
}
if (Position == 0 && kValue < 20)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (Position == 0 && kValue > 80)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position > 0 && kValue > 80)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && kValue < 20)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
}
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_overbought_oversold_strategy(Strategy):
"""
Stochastic Overbought/Oversold strategy.
Buys when K is oversold (<20), sells when K is overbought (>80).
"""
def __init__(self):
super(stochastic_overbought_oversold_strategy, self).__init__()
self._k_period = self.Param("KPeriod", 3).SetDisplay("K Period", "Smoothing period for %K", "Indicators")
self._d_period = self.Param("DPeriod", 3).SetDisplay("D Period", "Smoothing period for %D", "Indicators")
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._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(stochastic_overbought_oversold_strategy, self).OnReseted()
self._cooldown = 0
def OnStarted2(self, time):
super(stochastic_overbought_oversold_strategy, self).OnStarted2(time)
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_value):
if candle.State != CandleStates.Finished:
return
if not stoch_value.IsFormed:
return
k_val = stoch_value.K
if k_val is None:
return
kv = float(k_val)
if self._cooldown > 0:
self._cooldown -= 1
return
cd = self._cooldown_bars.Value
if self.Position == 0 and kv < 20:
self.BuyMarket()
self._cooldown = cd
elif self.Position == 0 and kv > 80:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0 and kv > 80:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and kv < 20:
self.BuyMarket()
self._cooldown = cd
def CreateClone(self):
return stochastic_overbought_oversold_strategy()