Die IStochastic Handelsstrategie ist ein direkter StockSharp-Port des MetaTrader 5 Expert Advisors "IStochastic_Trading". Der Bot verwendet den Stochastischen Oszillator, um überkaufte und überverkaufte Bedingungen zu erkennen und erstellt dann eine Martingale-ähnliche Positionsleiter, während er jeden Einstieg mit Stop-Loss, Take-Profit und einem Trailing-Stop verwaltet. Die Implementierung arbeitet auf abgeschlossenen Kerzen, die über die High-Level-API von StockSharp bezogen werden, und verwendet ausschließlich Market-Orders.
Handelslogik
Einen Stochastischen Oszillator mit konfigurierbarer %K-Länge, %D-Glättung und einem zusätzlichen Verlangsamungsfaktor berechnen.
Wenn keine aktiven Positionen vorhanden sind, die zuletzt abgeschlossene Kerze auswerten:
Eine Long-Position öffnen, wenn %K über %D liegt und %D unter der konfigurierten Kaufzone liegt.
Eine Short-Position öffnen, wenn %K unter %D liegt und %D über der konfigurierten Verkaufszone liegt.
Wenn eine Position besteht, die letzte Füllung in der Leiter überwachen:
Wenn sich der Markt um mindestens den konfigurierten Gap (in Pips) gegen den Trade bewegt, eine neue Position in derselben Richtung mit dem doppelten Vorvolumen öffnen, solange die maximale Anzahl von Positionen nicht überschritten wird.
Für jeden Einstieg Stop-Loss- und Take-Profit-Niveaus pro Trade beibehalten, die aus Pip-Abständen abgeleitet werden, die mit dem PriceStep und der Anzahl der Dezimalstellen des Instruments in Preispunkte umgerechnet werden. Wenn der Schlusskurs den Stop oder das Ziel erreicht, verlässt die Strategie die spezifische Position mit einer Market-Order.
Nach jedem Kerzenschluss einen Trailing-Stop anwenden. Wenn sich der Trade weit genug in die günstige Richtung bewegt, wird der Stop-Preis um den angegebenen Trailing-Schritt gestrafft, was das per-Position-Trailing-Verhalten des Terminals approximiert.
Parameter
Name
Standard
Beschreibung
OrderVolume
0.1
Anfängliche Positionsgröße in Lots. Zusätzliche Einstiege verdoppeln das vorherige Volumen.
TakeProfitPips
50
Take-Profit-Abstand in Pips. Der Wert wird intern in Preispunkte umgerechnet.
StopLossPips
50
Stop-Loss-Abstand in Pips für jede Position.
TrailingStopPips
10
Trailing-Stop-Abstand in Pips. Auf null setzen, um Trailing zu deaktivieren.
TrailingStepPips
5
Minimale günstige Bewegung (in Pips), bevor der Trailing-Stop angepasst wird.
MaxPositions
3
Maximale Anzahl gleichzeitig offener Martingale-Schritte. Ein Wert von 0 entfernt das Limit.
GapPips
7
Preislücke in Pips, die erforderlich ist, bevor in die aktuelle Richtung verdoppelt wird.
KPeriod
5
Anzahl der Kerzen, die zur Erstellung der %K-Linie verwendet werden.
DPeriod
3
Periode des %D-Glättungsdurchschnitts.
Slowing
3
Zusätzliche Glättung, die auf %K angewendet wird.
ZoneBuy
30
%D-Schwellenwert zur Validierung von Long-Einstiegen (überverkaufte Zone).
ZoneSell
70
%D-Schwellenwert zur Validierung von Short-Einstiegen (überkaufte Zone).
CandleType
15-Minuten-Zeitrahmen
Kerzenserie für Berechnungen.
Implementierungshinweise
Pip-Abstände werden mit PriceStep in Preise umgerechnet. Für 3- und 5-stellige Kurse wird ein zusätzlicher Faktor von 10 verwendet, um MetaTraders angepasste Punkt-Logik zu imitieren.
Stop-Loss-, Take-Profit- und Trailing-Stop-Überprüfungen basieren auf geschlossenen Kerzenpreisen, um die Logik im Backtester deterministisch zu halten. Echtzeit-Ausführung kann angepasst werden, wenn Intrabar-Management erforderlich ist.
Die Strategie öffnet nur eine Richtungsleiter gleichzeitig; alle Positionen müssen geschlossen sein, bevor die Richtung gewechselt wird.
Die Python-Implementierung wird wie angefordert absichtlich weggelassen.
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 Oscillator-based strategy with zone filtering.
/// Goes long when %K crosses above %D in the oversold zone, short when %K crosses below %D in overbought zone.
/// </summary>
public class IStochasticTradingStrategy : Strategy
{
private readonly StrategyParam<int> _kPeriod;
private readonly StrategyParam<int> _dPeriod;
private readonly StrategyParam<decimal> _zoneBuy;
private readonly StrategyParam<decimal> _zoneSell;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevK;
private decimal? _prevD;
/// <summary>
/// %K period.
/// </summary>
public int KPeriod
{
get => _kPeriod.Value;
set => _kPeriod.Value = value;
}
/// <summary>
/// %D smoothing period.
/// </summary>
public int DPeriod
{
get => _dPeriod.Value;
set => _dPeriod.Value = value;
}
/// <summary>
/// Buy zone threshold (oversold).
/// </summary>
public decimal ZoneBuy
{
get => _zoneBuy.Value;
set => _zoneBuy.Value = value;
}
/// <summary>
/// Sell zone threshold (overbought).
/// </summary>
public decimal ZoneSell
{
get => _zoneSell.Value;
set => _zoneSell.Value = value;
}
/// <summary>
/// Candle type used for analysis.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public IStochasticTradingStrategy()
{
_kPeriod = Param(nameof(KPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("K Period", "Number of bars for %K", "Indicators");
_dPeriod = Param(nameof(DPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("D Period", "Smoothing period for %D", "Indicators");
_zoneBuy = Param(nameof(ZoneBuy), 30m)
.SetRange(0m, 100m)
.SetDisplay("Buy Zone", "Upper boundary for bullish confirmation", "Signals");
_zoneSell = Param(nameof(ZoneSell), 70m)
.SetRange(0m, 100m)
.SetDisplay("Sell Zone", "Lower boundary for bearish confirmation", "Signals");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe for the strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevK = null;
_prevD = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevK = null;
_prevD = null;
var stochastic = new StochasticOscillator();
stochastic.K.Length = KPeriod;
stochastic.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 stochasticValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!stochasticValue.IsFinal)
return;
var stoch = (StochasticOscillatorValue)stochasticValue;
if (stoch.K is not decimal kValue || stoch.D is not decimal dValue)
return;
if (_prevK is decimal prevK && _prevD is decimal prevD)
{
var crossedUp = prevK <= prevD && kValue > dValue;
var crossedDown = prevK >= prevD && kValue < dValue;
if (crossedUp && dValue < ZoneBuy && Position <= 0)
{
BuyMarket();
}
else if (crossedDown && dValue > ZoneSell && Position >= 0)
{
SellMarket();
}
}
_prevK = kValue;
_prevD = dValue;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import StochasticOscillator
class i_stochastic_trading_strategy(Strategy):
"""Stochastic crossover strategy with oversold/overbought zone filtering."""
def __init__(self):
super(i_stochastic_trading_strategy, self).__init__()
self._k_period = self.Param("KPeriod", 5) \
.SetGreaterThanZero() \
.SetDisplay("K Period", "Number of bars for %K", "Indicators")
self._d_period = self.Param("DPeriod", 3) \
.SetGreaterThanZero() \
.SetDisplay("D Period", "Smoothing period for %D", "Indicators")
self._zone_buy = self.Param("ZoneBuy", 30.0) \
.SetDisplay("Buy Zone", "Upper boundary for bullish confirmation", "Signals")
self._zone_sell = self.Param("ZoneSell", 70.0) \
.SetDisplay("Sell Zone", "Lower boundary for bearish confirmation", "Signals")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Primary timeframe", "General")
self._prev_k = None
self._prev_d = None
@property
def KPeriod(self):
return self._k_period.Value
@property
def DPeriod(self):
return self._d_period.Value
@property
def ZoneBuy(self):
return self._zone_buy.Value
@property
def ZoneSell(self):
return self._zone_sell.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(i_stochastic_trading_strategy, self).OnStarted2(time)
self._prev_k = None
self._prev_d = None
stoch = StochasticOscillator()
stoch.K.Length = self.KPeriod
stoch.D.Length = self.DPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(stoch, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, stoch)
self.DrawOwnTrades(area)
def process_candle(self, candle, stoch_value):
if candle.State != CandleStates.Finished:
return
if not stoch_value.IsFinal:
return
k_val = stoch_value.K
d_val = stoch_value.D
if k_val is None or d_val is None:
return
k = float(k_val)
d = float(d_val)
if self._prev_k is not None and self._prev_d is not None:
crossed_up = self._prev_k <= self._prev_d and k > d
crossed_down = self._prev_k >= self._prev_d and k < d
if crossed_up and d < float(self.ZoneBuy) and self.Position <= 0:
self.BuyMarket()
elif crossed_down and d > float(self.ZoneSell) and self.Position >= 0:
self.SellMarket()
self._prev_k = k
self._prev_d = d
def OnReseted(self):
super(i_stochastic_trading_strategy, self).OnReseted()
self._prev_k = None
self._prev_d = None
def CreateClone(self):
return i_stochastic_trading_strategy()