Auf GitHub ansehen

Vlado Williams %R Schwellenwertstrategie

Überblick

Die Vlado Williams %R Threshold Strategy ist eine direkte Umsetzung des MetaTrader 4 Expert Advisors Vlado_www_forex-instruments_info.mq4. Der ursprüngliche Roboter handelt mit einem einzelnen Williams %R-Oszillator und ändert sein Marktrisiko immer dann, wenn der Indikator ein benutzerdefiniertes Niveau überschreitet. Dieser StockSharp-Port reproduziert das gleiche Verhalten beim Regimewechsel und stellt gleichzeitig jeden einstellbaren Wert als Strategieparameter für die Optimierung und UI-Steuerung bereit.

Schlüsselkonzepte

  • Tauscht die Richtung des Williams %R-Oszillators relativ zu einem Schwellenwert (Standard: -50).
  • Hält jeweils höchstens eine Marktposition und kehrt sich erst um, nachdem der vorherige Handel geschlossen wurde.
  • Optionale risikobasierte Positionsgrößenbestimmung, die die MetaTrader Money-Management-Formel AccountFreeMargin * MaximumRisk / price nachahmt.
  • Funktioniert mit jedem Kerzenzeitrahmen über den Parameter CandleType (standardmäßige 15-Minuten-Balken).

Handelslogik

  1. Abonnieren Sie den konfigurierten Kerzenstrom und berechnen Sie einen Williams %R der Länge WprLength (Standard 100).
  2. Wenn Williams %R über WprLevel steigt, markiert die Strategie eine bullische Tendenz:
    • Wenn keine Position offen ist und der vorherige Handel nicht lange dauerte, senden Sie eine marktgerechte Kauforder.
    • Besteht eine Short-Position, wird diese sofort geschlossen; Neue Long-Positionen werden bei der nächsten Kerze berücksichtigt, nachdem die Position flach ist.
  3. Wenn Williams %R unter WprLevel fällt, ändert sich die Tendenz in eine bärische Richtung:
    • Wenn keine Position offen ist und der vorherige Handel nicht leer war, senden Sie einen Markt-**Verkaufsauftrag.
    • Wenn eine Long-Position besteht, wird diese sofort abgeflacht.
  4. Die Positionsgröße wird durch CalculateOrderVolume bestimmt:
    • Wenn UseRiskMoneyManagement wahr ist, schätzt die Strategie das handelbare Volumen aus dem aktuellen Portfoliowert: Portfolio.CurrentValue × MaximumRiskPercent ÷ 100 ÷ ClosePrice.
    • Ansonsten wird die Basis Strategy.Volume verwendet.
    • Die resultierenden Lose werden auf das Instrument VolumeStep ausgerichtet und durch MinVolume / MaxVolume begrenzt, sofern diese Grenzen verfügbar sind.

Die Strategie vermeidet absichtlich die Eröffnung einer Umkehrposition in derselben Kerze, die den Ausstieg ausgelöst hat, und entspricht dem ursprünglichen EA-Fluss (CheckForClose wird vor CheckForOpen ausgeführt).

Konvertierungshinweise

  • Die Standardwerte für die Geldverwaltung folgen dem MT4-Skript: MaximumRiskPercent beginnt bei 10 und entspricht der ursprünglichen MaximumRisk = 10-Konstante, die ungefähr auf ein Mini-Lot pro Trade abzielte.
  • Der shift-Parameter (Indikatorverschiebung) von MetaTrader ist in der Quelldatei immer Null; deshalb wurde es weggelassen.
  • MT4-Farbargumente (z. B. Red, Blue) haben kein StockSharp-Äquivalent und werden ignoriert.
  • Slippage-Eingaben sind nicht erforderlich, da StockSharp Marktaufträge bereits den aktuell besten Preis verwenden.

Parameter

Parameter Typ Standard Beschreibung
CandleType DataType 15-minütiger Zeitrahmen Zeitrahmen sowohl für die Signalberechnung als auch für die Auftragsauslösung.
WprLength int 100 Lookback-Periode des Williams %R-Oszillators.
WprLevel decimal -50 Schwelle, die bullische und bärische Regime trennt.
UseRiskMoneyManagement bool false Schaltet die risikobasierte Positionsgröße um.
MaximumRiskPercent decimal 10 Prozentsatz des pro Trade eingesetzten Portfolio-Eigenkapitals, wenn das Risikomanagement aktiviert ist.

Tipp: Kombinieren Sie die Strategie mit StartProtection() oder externen Risikokontrollen, wenn Sie eine automatische Stop-Loss-Abwicklung benötigen. Das ursprüngliche EA stützte sich ebenfalls auf manuelle Überwachung und definierte keine harten Stopps.

Nutzungsrichtlinien

  1. Hängen Sie die Strategie an ein Wertpapier an, das genaue PriceStep-, StepPrice-, VolumeStep- und Volumenlimits offenlegt, damit der Positionsgrößen-Helfer Aufträge korrekt normalisieren kann.
  2. Stellen Sie Volume auf die gewünschte Fallback-Losgröße ein. Es wird immer dann verwendet, wenn kein Portfolio-Eigenkapital verfügbar ist oder UseRiskMoneyManagement deaktiviert ist.
  3. Optimieren Sie WprLevel und WprLength, um das System an verschiedene Märkte anzupassen. Enge Schwellenwerte (z. B. -20 / -80) machen die Strategie selektiver, während breite Schwellenwerte (-50) dafür sorgen, dass fast immer investiert wird.
  4. Die Strategie folgt dem Trend: Unter unterschiedlichen Bedingungen wird sie häufig umkehren. Erwägen Sie bei Bedarf die Kombination mit Filtern wie längerfristigen Trendprüfungen oder Volatilitätsschwellenwerten.

Unterschiede zur MetaTrader-Version

  • Verwendet Kerzenabonnements und Indikatorbindungen aus der StockSharp-Hochebene API; Es gibt keine manuelle Bestellschleife oder Verlaufsscans.
  • Die Risikogröße basiert auf Portfolio.CurrentValue. Wenn die Kontobewertung fehlt, greift die Logik auf das statische Volume zurück und entspricht dem MT4-Verhalten, bei dem mm=0 eine feste Losgröße erzwingt.
  • Alle Kommentare und Parameterbeschreibungen sind aus Gründen der Konsistenz mit den Repository-Richtlinien auf Englisch.

Validierungs-Checkliste

  • ✅ Strategiedatei, kompiliert mit den StockSharp-Strategievorlagenkonventionen (Tabs, dateibezogener Namespace, XML-Inheritdoc).
  • ✅ Parameter, die über Param() erstellt und gegebenenfalls zur Optimierung markiert wurden.
  • ✅ Williams %R-Werte, die über Bind verbraucht wurden, ohne direkten GetValue()-Zugriff.
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;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Port of the "Vlado" MetaTrader expert advisor that trades Williams %R level breakouts.
/// </summary>
public class VladoWilliamsPercentRangeStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _wprLength;
	private readonly StrategyParam<decimal> _wprLevel;
	private readonly StrategyParam<bool> _useRiskMoneyManagement;
	private readonly StrategyParam<decimal> _maximumRiskPercent;

	private bool _buySignal;
	private bool _sellSignal;
	private int _lastSignal;

	private WilliamsR _williamsR;

	/// <summary>
	/// Initializes a new instance of the <see cref="VladoWilliamsPercentRangeStrategy"/> class.
	/// </summary>
	public VladoWilliamsPercentRangeStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Primary timeframe for the strategy", "General");

		_wprLength = Param(nameof(WprLength), 100)
			.SetGreaterThanZero()
			.SetDisplay("Williams %R Period", "Lookback period for Williams %R", "Indicators")
			;

		_wprLevel = Param(nameof(WprLevel), -50m)
			.SetDisplay("Williams %R Level", "Threshold that flips the bias", "Signals")
			;

		_useRiskMoneyManagement = Param(nameof(UseRiskMoneyManagement), false)
			.SetDisplay("Risk Money Management", "Recalculate volume from equity before entries", "Risk")
			;

		_maximumRiskPercent = Param(nameof(MaximumRiskPercent), 10m)
			.SetDisplay("Maximum Risk Percent", "Equity percentage used when sizing orders", "Risk")
			;
	}

	/// <summary>
	/// Candle type processed by the strategy.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

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

	/// <summary>
	/// Threshold that toggles bullish or bearish bias.
	/// </summary>
	public decimal WprLevel
	{
		get => _wprLevel.Value;
		set => _wprLevel.Value = value;
	}

	/// <summary>
	/// Enables risk based volume sizing similar to the MetaTrader version.
	/// </summary>
	public bool UseRiskMoneyManagement
	{
		get => _useRiskMoneyManagement.Value;
		set => _useRiskMoneyManagement.Value = value;
	}

	/// <summary>
	/// Fraction of the current equity used to size entries when risk management is enabled.
	/// </summary>
	public decimal MaximumRiskPercent
	{
		get => _maximumRiskPercent.Value;
		set => _maximumRiskPercent.Value = value;
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_williamsR = null;
		_buySignal = false;
		_sellSignal = false;
		_lastSignal = 0;
	}

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

		_williamsR = new WilliamsR
		{
			Length = WprLength
		};

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_williamsR, ProcessCandle)
			.Start();

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

	private void ProcessCandle(ICandleMessage candle, decimal wprValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		UpdateSignals(wprValue);

		if (Position != 0)
		{
			if (Position > 0 && _sellSignal)
			{
				// Exit long positions when the bearish Williams %R regime appears.
				if (Position > 0) SellMarket(); else BuyMarket();
				return;
			}

			if (Position < 0 && _buySignal)
			{
				// Exit short positions when the bullish Williams %R regime appears.
				if (Position > 0) SellMarket(); else BuyMarket();
				return;
			}

			return;
		}

		// No open position - evaluate fresh entries.
		var volume = CalculateOrderVolume(candle.ClosePrice);
		if (volume <= 0m)
			return;

		if (_sellSignal && _lastSignal != -1)
		{
			// Enter short once Williams %R falls below the chosen level.
			SellMarket();
			_lastSignal = -1;
			return;
		}

		if (_buySignal && _lastSignal != 1)
		{
			// Enter long once Williams %R rises above the chosen level.
			BuyMarket();
			_lastSignal = 1;
		}
	}

	private void UpdateSignals(decimal wprValue)
	{
		// Williams %R values are negative: less negative indicates bullish momentum.
		if (wprValue > WprLevel)
		{
			_buySignal = true;
			_sellSignal = false;
		}
		else if (wprValue < WprLevel)
		{
			_sellSignal = true;
			_buySignal = false;
		}
	}

	private decimal CalculateOrderVolume(decimal referencePrice)
	{
		var volume = Volume;

		if (UseRiskMoneyManagement && MaximumRiskPercent > 0m && referencePrice > 0m)
		{
			var equity = Portfolio?.CurrentValue ?? 0m;
			if (equity > 0m)
			{
				// Convert risk capital to volume using the latest close price as approximation.
				volume = equity * (MaximumRiskPercent / 100m) / referencePrice;
			}
		}

		return NormalizeVolume(volume);
	}

	private decimal NormalizeVolume(decimal volume)
	{
		var normalized = volume;

		if (Security?.VolumeStep is decimal step && step > 0m)
		{
			var steps = decimal.Floor(normalized / step);
			normalized = steps * step;

			if (normalized <= 0m)
				normalized = step;
		}

		if (Security?.MinVolume is decimal minVolume && minVolume > 0m && normalized < minVolume)
			normalized = minVolume;

		if (Security?.MaxVolume is decimal maxVolume && maxVolume > 0m && normalized > maxVolume)
			normalized = maxVolume;

		if (normalized <= 0m && volume > 0m)
			normalized = volume;

		return normalized;
	}
}