Auf GitHub ansehen

Die MasterMind-Umkehrstrategie (StockSharp Port)

Überblick

  • Port des MetaTrader 4-Expertenberaters „TheMasterMind“, der einen Stochastic-Oszillator mit Williams %R kombiniert, um extreme Umkehrungen zu erfassen.
  • Implementiert mit StockSharps High-Level-API unter Verwendung von Kerzenabonnements und Indikatorbindungen.
  • Handelt ein einzelnes Wertpapier und reagiert nur auf fertige Kerzen, was den ursprünglichen Ausführungsstil „Trade at Close“ widerspiegelt.

Handelslogik

  1. Indikatorvorbereitung
    • StochasticOscillator liefert die %D-Signalleitung mit konfigurierbarer %K/%D-Glättung und gesamter Lookback-Länge.
    • WilliamsR misst die relative Position des Schlusskurses innerhalb des aktuellen Hoch-/Tief-Bereichs.
  2. Eintrittsregeln
    • Kaufen, wenn %D <= 3 und Williams %R <= -99.5, was ein stochastisches überverkauftes Extrem zusammen mit einer tiefen WPR-Penetration unterhalb der Untergrenze signalisiert.
    • Verkaufen wenn %D >= 97 und Williams %R >= -0.5, was ein überkauftes Extrem signalisiert, das dadurch bestätigt wird, dass Williams %R nahe bei 0 bleibt.
    • Liegt eine Gegenposition vor, wird diese zunächst abgeflacht und anschließend eine neue Marktorder mit dem konfigurierten Basisvolumen gesendet.
  3. Ausgangsregeln
    • Umkehrsignale schließen die aktuelle Position und kehren die Richtung um (eine Position nach der anderen, entsprechend dem im MQL-Skript verwendeten Hedging-deaktivierten Modus).
    • Die optionalen StartProtection Stop-Loss-, Take-Profit- und Trailing-Stop-Dienste verarbeiten Schutzexits genau einmal pro Strategiestart.

Risikomanagement

  • Die Parameter StopLoss, TakeProfit, UseTrailingStop, TrailingStop und TrailingStep entsprechen den Geldverwaltungskontrollen des ursprünglichen EA.
  • Alle Entfernungen werden in absoluten Preiseinheiten ausgedrückt, um Maklerunabhängig zu bleiben. Lassen Sie sie bei 0, um die entsprechende Schutzfunktion zu deaktivieren.
  • StartProtection wird automatisch aktiviert, wenn mindestens einer der Schutzabstände ungleich Null ist.

Strategieparameter

Parameter Beschreibung Standard
TradeVolume Basislosgröße für jeden neuen Eintrag. 1
StochasticPeriod Totaler Lookback für den stochastischen Oszillator. 100
KPeriod %K Glättungslänge. 3
DPeriod %D Signallänge. 3
WilliamsPeriod Lookback-Länge für Williams %R. 100
StochasticBuyThreshold Obergrenze, unter der %D bleiben muss, um Long-Positionen zuzulassen. 3
StochasticSellThreshold Untergrenze, über der %D bleiben muss, um Kurzschlüsse zuzulassen. 97
WilliamsBuyLevel Überverkaufter Wert für Williams %R. -99.5
WilliamsSellLevel Überkaufter Wert für Williams %R. -0.5
StopLoss Absoluter Stop-Loss-Abstand. 0
TakeProfit Absolute Take-Profit-Distanz. 0
UseTrailingStop Aktiviert den nachgestellten Schutz, wenn true. false
TrailingStop Absoluter Trailing-Stop-Abstand. 0
TrailingStep Beim Nachlaufen wird ein Schritt angewendet. 0
CandleType Zeitrahmen für das primäre Kerzenabonnement (Standard 15 Minuten). 15m time frame

Implementierungshinweise

  • Die Strategie abonniert eine einzelne Kerzenserie über SubscribeCandles(CandleType) und bindet die stochastischen und Williams %R-Indikatoren über BindEx.
  • Handelsentscheidungen werden nur getroffen, wenn candle.State == CandleStates.Finished und IsFormedAndOnlineAndAllowTrading() erfüllt sind.
  • Diagrammhelfer (DrawCandles, DrawIndicator, DrawOwnTrades) werden aufgerufen, wenn ein Diagrammbereich zur Visualisierung der Indikatoren und Trades verfügbar ist.
  • Protokollierungsanweisungen (LogInfo) spiegeln die ursprünglichen Alarmzeichenfolgen wider und helfen dabei, den Entscheidungsprozess während des Live-Handels oder Backtestings zu verfolgen.
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

using StockSharp.Algo;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Stochastic + Williams %R reversal system ported from the MetaTrader expert "TheMasterMind".
/// </summary>
public class TheMasterMindReversalStrategy : Strategy
{
	private readonly StrategyParam<decimal> _tradeVolume;
	private readonly StrategyParam<int> _stochasticPeriod;
	private readonly StrategyParam<int> _kPeriod;
	private readonly StrategyParam<int> _dPeriod;
	private readonly StrategyParam<int> _williamsPeriod;
	private readonly StrategyParam<decimal> _stochasticBuyThreshold;
	private readonly StrategyParam<decimal> _stochasticSellThreshold;
	private readonly StrategyParam<decimal> _williamsBuyLevel;
	private readonly StrategyParam<decimal> _williamsSellLevel;
	private readonly StrategyParam<decimal> _stopLoss;
	private readonly StrategyParam<decimal> _takeProfit;
	private readonly StrategyParam<bool> _useTrailingStop;
	private readonly StrategyParam<decimal> _trailingStop;
	private readonly StrategyParam<decimal> _trailingStep;
	private readonly StrategyParam<DataType> _candleType;

	private StochasticOscillator _stochastic = null!;
	private WilliamsR _williams = null!;

	/// <summary>
	/// Trade volume in lots.
	/// </summary>
	public decimal TradeVolume
	{
		get => _tradeVolume.Value;
		set => _tradeVolume.Value = value;
	}

	/// <summary>
	/// Total period for the stochastic oscillator.
	/// </summary>
	public int StochasticPeriod
	{
		get => _stochasticPeriod.Value;
		set => _stochasticPeriod.Value = value;
	}

	/// <summary>
	/// %K smoothing period.
	/// </summary>
	public int KPeriod
	{
		get => _kPeriod.Value;
		set => _kPeriod.Value = value;
	}

	/// <summary>
	/// %D signal period.
	/// </summary>
	public int DPeriod
	{
		get => _dPeriod.Value;
		set => _dPeriod.Value = value;
	}

	/// <summary>
	/// Williams %R lookback length.
	/// </summary>
	public int WilliamsPeriod
	{
		get => _williamsPeriod.Value;
		set => _williamsPeriod.Value = value;
	}

	/// <summary>
	/// Stochastic signal threshold for longs.
	/// </summary>
	public decimal StochasticBuyThreshold
	{
		get => _stochasticBuyThreshold.Value;
		set => _stochasticBuyThreshold.Value = value;
	}

	/// <summary>
	/// Stochastic signal threshold for shorts.
	/// </summary>
	public decimal StochasticSellThreshold
	{
		get => _stochasticSellThreshold.Value;
		set => _stochasticSellThreshold.Value = value;
	}

	/// <summary>
	/// Williams %R oversold level.
	/// </summary>
	public decimal WilliamsBuyLevel
	{
		get => _williamsBuyLevel.Value;
		set => _williamsBuyLevel.Value = value;
	}

	/// <summary>
	/// Williams %R overbought level.
	/// </summary>
	public decimal WilliamsSellLevel
	{
		get => _williamsSellLevel.Value;
		set => _williamsSellLevel.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in absolute price units.
	/// </summary>
	public decimal StopLoss
	{
		get => _stopLoss.Value;
		set => _stopLoss.Value = value;
	}

	/// <summary>
	/// Take-profit distance in absolute price units.
	/// </summary>
	public decimal TakeProfit
	{
		get => _takeProfit.Value;
		set => _takeProfit.Value = value;
	}

	/// <summary>
	/// Enables trailing stop management.
	/// </summary>
	public bool UseTrailingStop
	{
		get => _useTrailingStop.Value;
		set => _useTrailingStop.Value = value;
	}

	/// <summary>
	/// Trailing stop distance in absolute price units.
	/// </summary>
	public decimal TrailingStop
	{
		get => _trailingStop.Value;
		set => _trailingStop.Value = value;
	}

	/// <summary>
	/// Trailing step distance in absolute price units.
	/// </summary>
	public decimal TrailingStep
	{
		get => _trailingStep.Value;
		set => _trailingStep.Value = value;
	}

	/// <summary>
	/// Candle type used for calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
/// Initializes a new instance of the <see cref="TheMasterMindReversalStrategy"/> class.
/// </summary>
public TheMasterMindReversalStrategy()
	{
		_tradeVolume = Param(nameof(TradeVolume), 1m)
			.SetGreaterThanZero()
			.SetDisplay("Volume", "Base order size", "Trading")
			
			.SetOptimize(0.5m, 5m, 0.5m);

		_stochasticPeriod = Param(nameof(StochasticPeriod), 100)
			.SetGreaterThanZero()
			.SetDisplay("Stochastic Length", "Total lookback for stochastic", "Indicators")
			
			.SetOptimize(50, 150, 10);

		_kPeriod = Param(nameof(KPeriod), 3)
			.SetGreaterThanZero()
			.SetDisplay("%K Smoothing", "Stochastic %K smoothing length", "Indicators")
			
			.SetOptimize(1, 5, 1);

		_dPeriod = Param(nameof(DPeriod), 3)
			.SetGreaterThanZero()
			.SetDisplay("%D Signal", "Stochastic %D signal length", "Indicators")
			
			.SetOptimize(1, 5, 1);

		_williamsPeriod = Param(nameof(WilliamsPeriod), 100)
			.SetGreaterThanZero()
			.SetDisplay("Williams %R Length", "Lookback period for Williams %R", "Indicators")
			
			.SetOptimize(50, 150, 10);

		_stochasticBuyThreshold = Param(nameof(StochasticBuyThreshold), 3m)
			.SetDisplay("Stoch Buy Threshold", "%D level required to buy", "Signals");

		_stochasticSellThreshold = Param(nameof(StochasticSellThreshold), 97m)
			.SetDisplay("Stoch Sell Threshold", "%D level required to sell", "Signals");

		_williamsBuyLevel = Param(nameof(WilliamsBuyLevel), -99.5m)
			.SetDisplay("Williams Buy Level", "Williams %R oversold level", "Signals");

		_williamsSellLevel = Param(nameof(WilliamsSellLevel), -0.5m)
			.SetDisplay("Williams Sell Level", "Williams %R overbought level", "Signals");

		_stopLoss = Param(nameof(StopLoss), 0m)
			.SetDisplay("Stop Loss", "Protective stop distance", "Risk");

		_takeProfit = Param(nameof(TakeProfit), 0m)
			.SetDisplay("Take Profit", "Target distance", "Risk");

		_useTrailingStop = Param(nameof(UseTrailingStop), false)
			.SetDisplay("Use Trailing", "Enable trailing stop management", "Risk");

		_trailingStop = Param(nameof(TrailingStop), 0m)
			.SetDisplay("Trailing Stop", "Trailing stop distance", "Risk");

		_trailingStep = Param(nameof(TrailingStep), 0m)
			.SetDisplay("Trailing Step", "Trailing step distance", "Risk");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
			.SetDisplay("Candle Type", "Primary candle series", "Trading");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		Volume = TradeVolume;

		_stochastic = new StochasticOscillator();
		_stochastic.K.Length = StochasticPeriod;
		_stochastic.D.Length = DPeriod;

		_williams = new WilliamsR
		{
			Length = WilliamsPeriod
		};

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(_stochastic, _williams, ProcessSignals)
			.Start();

		// Note: BindEx passes all indicator values as IIndicatorValue

		var takeProfit = TakeProfit > 0m ? new Unit(TakeProfit, UnitTypes.Absolute) : null;
		var stopLoss = StopLoss > 0m ? new Unit(StopLoss, UnitTypes.Absolute) : null;

		if (takeProfit != null || stopLoss != null)
		{
			StartProtection(
				takeProfit: takeProfit,
				stopLoss: stopLoss,
				isStopTrailing: UseTrailingStop);
		}

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _stochastic);
			DrawIndicator(area, _williams);
			DrawOwnTrades(area);
		}
	}

	private void ProcessSignals(ICandleMessage candle, IIndicatorValue stochasticValue, IIndicatorValue williamsRawValue)
	{
		if (candle.State != CandleStates.Finished)
		return;

		var stochasticTyped = (StochasticOscillatorValue)stochasticValue;

		if (stochasticTyped.D is not decimal signalValue)
		return;

		var williamsValue = williamsRawValue.IsEmpty ? (decimal?)null : williamsRawValue.ToDecimal();
		if (williamsValue is null)
		return;

		if (!IsFormedAndOnlineAndAllowTrading())
		return;

		var buySignal = signalValue <= StochasticBuyThreshold && williamsValue.Value <= WilliamsBuyLevel;
		var sellSignal = signalValue >= StochasticSellThreshold && williamsValue.Value >= WilliamsSellLevel;

		if (buySignal)
		{
			LogInfo($"Buy setup detected. %D={signalValue:F2}, WilliamsR={williamsValue.Value:F2}");

			if (Position < 0)
			{
				BuyMarket(Math.Abs(Position));
			}

			if (Position <= 0)
			{
				BuyMarket(Volume);
			}

			return;
		}

		if (sellSignal)
		{
			LogInfo($"Sell setup detected. %D={signalValue:F2}, WilliamsR={williamsValue.Value:F2}");

			if (Position > 0)
			{
				SellMarket(Math.Abs(Position));
			}

			if (Position >= 0)
			{
				SellMarket(Volume);
			}
		}
	}
}