Auf GitHub ansehen

IStochastic Handelsstrategie

Übersicht

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

  1. Einen Stochastischen Oszillator mit konfigurierbarer %K-Länge, %D-Glättung und einem zusätzlichen Verlangsamungsfaktor berechnen.
  2. 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.
  3. 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.
  4. 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.
  5. 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;
	}
}