Auf GitHub ansehen

Strategie Killer Sell 2.0 (C#)

Überblick

Killer Sell 2.0 ist ein reiner Short-MetaTrader-4-Expertenberater, der Einstiege nach ausgedehnten überkauften Readings timed und Gewinne sichert, wenn der Momentum in überverkauftes Gebiet schwenkt. Dieser Port schreibt die ursprüngliche Logik auf Basis der StockSharp High-Level-Strategie-API um. Die gesamte Indikatorverarbeitung ist ereignisgesteuert über SubscribeCandles().BindEx(...), und Money-Management-Regeln sind innerhalb der Strategieklasse gekapselt.

Handelslogik

Die konvertierte Logik folgt der ursprünglichen Signalkette und verwendet das Nettopositionsmodell von StockSharp. Jede abgeschlossene Kerze des konfigurierten Zeitrahmens führt folgende Schritte aus:

  1. Datenvorbereitung. Die Strategie aktualisiert einen MACD (12/120/9), Williams %R (Periode 350 für beide Filter) und zwei Stochastik-Oszillatoren (10/1/3 für Einstieg, 90/7/1 für Ausstiege). Indikatorwerte werden nur konsumiert, wenn der neue Balken fertig und die Eingaben vollständig gebildet sind.
  2. Einstiegsfilter. Ein Short-Setup ist gültig, wenn alle folgenden Bedingungen erfüllt sind:
    • Williams %R steigt über −10, was einen überkauften Markt signalisiert.
    • Die MACD-Hauptlinie ist größer als 0.0014.
    • Der Einstiegs-Stochastik %K kreuzt unter das konfigurierbare Einstiegsniveau (Standard 90). Die Kreuzungserkennung erfolgt auf aufeinanderfolgenden %K-Lesungen.
  3. Orderplatzierung. Sobald die Filter übereinstimmen, sendet die Strategie einen Marktverkauf mit der aktuellen Martingale-Lot-Größe. Orders erhalten einen Take-Profit N Pips entfernt (Standard 100 Pips) über StartProtection.
  4. Ausstiegsverwaltung. Während ein Short-Exposure besteht, berechnet die Strategie das arithmetische Mittel des Gewinns offener Tickets in Pips. Abhängig vom Momentum:
    • Wenn der durchschnittliche Gewinn unter 10 Pips liegt und Williams %R unter −80 fällt, werden alle Shorts sofort geschlossen.
    • Wenn der durchschnittliche Gewinn über 15 Pips liegt und der Exit-Stochastik %K unter 12 fällt, wird die Position zur Gewinnmitnahme geschlossen.

Money Management

Killer Sell 2.0 verwendet eine Martingale-Leiter ähnlich dem originalen EA. Die StockSharp-Implementierung führt eine interne Liste offener Short-Lots, um die Per-Ticket-Berechnungen von MetaTrader nachzuahmen:

  • Der erste Trade verwendet InitialVolume (Standard 0.05 Lots).
  • Nach einem profitablen oder Breakeven-Zyklus wird das Volumen auf die anfängliche Lot-Größe zurückgesetzt.
  • Nach einem Verlustzyklus wird der nächste Auftrag mit MartingaleMultiplier (Standard ×1.2) multipliziert. Ein Sicherheitsdeckel MaxVolume verhindert unkontrolliertes Wachstum.

Der Helfer verfolgt auch realisiertes PnL bei Fills, um zu entscheiden, ob der vorherige Zyklus profitabel war.

Parameter

Parameter Beschreibung
CandleType Primärer Zeitrahmen, der jeden Indikator versorgt.
EntryWprPeriod / ExitWprPeriod Williams %R-Längen für Einstiegs- und Ausstiegsbestätigungen.
MacdFastPeriod / MacdSlowPeriod / MacdSignalPeriod MACD-Konfiguration.
MacdThreshold Mindestwert der MACD-Hauptlinie für einen Verkauf.
StochasticEntryKPeriod, StochasticEntryDPeriod, StochasticEntrySlow Einstiegs-Stochastik-Parameter.
EntryStochasticLevel Niveau, das %K von oben kreuzen muss, um ein Signal zu validieren.
StochasticExitKPeriod, StochasticExitDPeriod, StochasticExitSlow Ausstiegs-Stochastik-Parameter.
ExitStochasticLevel Überverkaufte Grenze, die vor Gewinnmitnahme geprüft wird.
EntryWprThreshold / ExitWprThreshold Williams %R-Schwellenwerte für Einstiege/Ausstiege.
LossExitPips / ProfitExitPips Durchschnittliche Gewinngrenzen (in Pips) für defensive und Zielausstiege.
TakeProfitPips Schützender Take-Profit für jede Verkaufsorder.
InitialVolume Volumen des ersten Martingale-Schritts.
MartingaleMultiplier Faktor nach Verlusten.
MaxVolume Absolutes Cap für die nächste Lot-Größe.

Konvertierungshinweise

  • MetaTrader führt einzelne Tickets; StockSharp arbeitet mit einer Nettoposition. Die Strategie speichert daher jeden gefüllten Short (Volumen + Preis), um durchschnittliche Gewinnberechnungen zu reproduzieren und Martingale-Resets zu evaluieren.
  • Der MT4-"Martingale"-Block exponierte viele zusätzliche Modi (fest, prozentuales Risiko, 1326, Fibonacci usw.). Die ursprüngliche Konfiguration verwendete den einfachen Martingale-Zweig; nur dieses Verhalten wird hier repliziert.
  • Der Notfall-Stop-Loss war im Quellprojekt deaktiviert. Der Port spiegelt diese Einrichtung, indem nur ein Take-Profit angehängt und andere Ausstiege intern behandelt werden.

Verwendungstipps

  1. Fügen Sie die Strategie einem Portfolio und Wertpapier hinzu, dann setzen Sie denselben Zeitrahmen wie in den MT4-Backtests (die Standardwerte gehen von H1 aus).
  2. Stellen Sie sicher, dass Marktdaten abgeschlossene Kerzen liefern; Indikatoren verlassen sich auf CandleStates.Finished-Ereignisse.
  3. Überprüfen Sie die Kontohebel und erlaubten Lot-Größen. Der Standard-Martingale-Deckel (5 Lots) sollte an Ihre Broker-Anforderungen angepasst werden.
  4. Testen Sie gründlich — Martingale-Strategien verstärken das Risiko, wenn Märkte stark gegen den Short-Bias tendieren.
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 KillerSell20Strategy : 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 KillerSell20Strategy()
	{
		_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;
	}
}