Auf GitHub ansehen

Stoch Sell-Strategie

Überblick

Diese Strategie reproduziert das Verhalten des ursprünglichen MetaTrader-Experten stochSell. Sie hört auf einen einzelnen Kerzenstream und wartet auf eine dreifache stochastische Bestätigung kombiniert mit einem Volatilitätsfilter, bevor eine anfängliche Market-Sell-Order gesendet wird. Unmittelbar nach dem Short-Einstieg wird eine Leiter ausstehender Sell-Stops eingesetzt, um die Bewegung zu skalieren, wenn der Preis weiter fällt.

Handelslogik

  • Volatilitätsfilter – eine Average True Range (ATR) mit konfigurierbarer Länge muss unter dem angegebenen Schwellenwert bleiben.
  • Langsame stochastische Bestätigung – der längste stochastische Oszillator muss unter dem langfristigen Überverkauf-Niveau bleiben, bevor Trades erlaubt sind.
  • Kreuzungsbestätigung – sowohl der mittlere als auch der schnelle stochastische Oszillator müssen während derselben abgeschlossenen Kerze durch den Überverkauf-Trigger nach unten kreuzen.
  • Positionsprüfung – neue Einstiege werden nur dann platziert, wenn die Strategie keine aktiven Orders hat und die Position flat ist.

Sobald alle Bedingungen erfüllt sind, sendet die Strategie eine Market-Sell-Order mit dem konfigurierten Volumen und plant sofort eine Reihe von Sell-Stop-Orders gemäß den Grid-Einstellungen. Ausstehende Orders sind optional und können deaktiviert werden, indem der Grid-Order-Zähler auf null gesetzt wird.

Ausstiegsregeln

  • Gewinnziel – wenn der Short-Korb den gewünschten Gewinn in Pips akkumuliert (berechnet aus dem volumengewichteten Einstiegspreis), kauft die Strategie die gesamte Position zurück und entfernt alle verbleibenden ausstehenden Orders.
  • Manueller Stop – Grid-Orders respektieren eine konfigurierbare Lebensdauer. Wenn eine Stop-Order ohne Ausführung abläuft, wird sie automatisch storniert.
  • Vollständiges Schließen – jeder Kauftrade, der die Position auf null zurückbringt, löscht die internen Einstiegsstatistiken und storniert das ausstehende Grid.

Grid-Verwaltung

  • Ausstehende Orders werden unterhalb des Referenzpreises mit dem Start-Offset und Schritt in Pips platziert.
  • Jede ausstehende Order verwendet den Grid-Volumen-Multiplikator, wodurch die Korbgröße vom anfänglichen Market-Einstieg abweichen kann.
  • Die Ablaufzeit (in Minuten) wird auf jede ausstehende Order angewendet; null deaktiviert das Timeout.

Parameter

Name Beschreibung
CandleType Primärer Zeitrahmen für jeden Indikator und jede Handelsentscheidung.
AtrPeriod / AtrThreshold Volatilitätsfilter, der steuert, wann die Strategie handeln darf.
FastKPeriod, FastDPeriod, FastSlowing Konfiguration des schnellen stochastischen Oszillators.
MediumKPeriod, MediumDPeriod, MediumSlowing Konfiguration des mittleren stochastischen Oszillators.
SlowKPeriod, SlowDPeriod, SlowSlowing Konfiguration des langsamen stochastischen Oszillators.
OversoldLevel Niveau, durch das die schnellen und mittleren stochastischen Werte nach unten kreuzen müssen.
LongTermOversoldLevel Obere Grenze für den langsamen Stochastik beim Einstieg.
ProfitTargetPips Nettogewinn in Pips zum Schließen des Short-Korbs.
GridOrdersCount Anzahl der nach dem Einstieg erstellten ausstehenden Sell-Stops.
GridStartOffsetPips Offset in Pips zwischen Einstiegspreis und erster ausstehender Order.
GridStepPips Abstand in Pips zwischen aufeinanderfolgenden ausstehenden Orders.
GridVolume Volumen für jede ausstehende Order.
GridExpirationMinutes Lebensdauer ausstehender Orders in Minuten.
MarketVolume Volumen für den anfänglichen Market-Verkauf.

Hinweise

  • Indikatorwerte werden über die High-Level-BindEx-API verarbeitet und nur fertige Kerzen lösen Handelsentscheidungen aus.
  • Die Positionsverfolgungslogik hält einen volumengewichteten Einstiegspreis, um das Rohgewinnziel in Pips zu übersetzen.
  • Um die Skalierung zu deaktivieren, setzen Sie den Grid-Order-Zähler einfach auf null; die Strategie verlässt sich dann immer noch auf die stochastische Bestätigung und den ATR-Filter für einzelne Trades.
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;

public class StochSellStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public StochSellStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}