Auf GitHub ansehen

Exp XWPR Histogramm Vol Direkt Strategie

Überblick

Diese Strategie ist ein StockSharp-Port des MetaTrader Expert Advisors Exp_XWPR_Histogram_Vol_Direct. Sie reproduziert den ursprünglichen Ansatz, Williams %R-Werte nach Volumen zu gewichten, das Ergebnis zu glätten und Trades zu eröffnen, wenn sich die Histogrammsteigung ändert. Orders werden bei vollständig ausgebildeten Kerzen ausgelöst und verwenden optionale Schutz-Stop-Loss und Take-Profit in Preisschritten.

Kernlogik

  1. Williams %R auf dem ausgewählten Zeitrahmen berechnen.
  2. Den Oszillator um +50 verschieben, mit der gewählten Volumenquelle (Tick oder Real) multiplizieren und den Strom mit einem konfigurierbaren gleitenden Durchschnitt glätten.
  3. Das Rohvolumen mit demselben gleitenden Durchschnitt glätten, um die Indikatorbänder (HighLevel2, HighLevel1, LowLevel1, LowLevel2) wieder aufzubauen.
  4. Die Farbe der Histogrammsteigung verfolgen: blau (0) wenn der geglättete Wert steigt, magenta (1) wenn er fällt. Die Strategie hält einen kurzen Historiebuffer, um die letzten zwei abgeschlossenen Farben unter Berücksichtigung des SignalShift-Parameters zu vergleichen.
  5. Aktionen ausführen, wenn sich die vorherige Farbe ändert:
    • Farbübergang 0 → 1: Shorts schließen (wenn aktiviert) und optional eine neue Long-Position eröffnen.
    • Farbübergang 1 → 0: Longs schließen (wenn aktiviert) und optional eine neue Short-Position eröffnen.

Die Zonenklassifikation (Neutral/Bullisch/Bärisch/Extrem) wird für Kontext protokolliert, blockiert aber keine Trades, was dem Verhalten des ursprünglichen Advisors entspricht, der nur den Farbbuffer liest.

Parameter

Parameter Beschreibung
WilliamsPeriod Rückschaulänge für Williams %R.
HighLevel2, HighLevel1, LowLevel1, LowLevel2 Multiplikatoren auf das geglättete Volumen zum Wiederaufbau der Indikatorbänder.
SmoothingType Gleitender-Durchschnitt-Familie für sowohl den gewichteten Wert als auch die Volumenströme (SMA, EMA, SMMA, WMA, Hull, VWMA, DEMA, TEMA).
SmoothingLength Länge des glättenden gleitenden Durchschnitts.
SignalShift Wie viele Balken zurück der Farbbuffer gelesen wird (1 reproduziert den MetaTrader-Standard).
EnableLongEntries / EnableShortEntries Long/Short-Positionen öffnen erlauben oder sperren.
EnableLongExits / EnableShortExits Long/Short-Positionen schließen erlauben oder sperren.
VolumeSource Zwischen Tick-Anzahl oder Realvolumen für die Gewichtung wählen.
StopLossPoints / TakeProfitPoints Optionale Schutzziele in Preisschritten ausgedrückt.
CandleType Kerzentyp und Zeitrahmen für Analyse und Handel.

Verwenden Sie die Basis-Volume-Eigenschaft der Strategie, um die Einstiegsgröße zu definieren. Positionsumkehrungen werden durch das Senden der absoluten Positionsmenge plus der konfigurierten Lot-Größe behandelt, ähnlich dem MQL-Expert-Advisor.

Verwendungshinweise

  • Die Glättungsphase (MA_Phase in MetaTrader) wird nicht unterstützt, da StockSharp-gleitende Durchschnitte diesen Parameter nicht offenlegen.
  • Stellen Sie sicher, dass ausreichend Geschichte für den gewählten Zeitrahmen geladen ist, damit die gleitenden Durchschnitte vollständig ausgebildet sind, bevor das Trading beginnt.
  • Die Strategie funktioniert auf jedem von StockSharp unterstützten Instrument; setzen Sie CandleType auf die gewünschte Auflösung (zum Beispiel 4-Stunden-Zeitrahmen, um den ursprünglichen Standardwerten zu entsprechen).
  • Die Tick-Volumen-Gewichtung erfordert Datenquellen, die Tick-Anzahlen in Kerzenmeldungen bereitstellen. Andernfalls zu Realvolumen wechseln.

Protokollierung und Visualisierung

Die Strategie zeichnet Kerzen und den Williams %R-Indikator im Standard-Chartbereich. Handelsaktionen protokollieren die erkannte Zone und den geglätteten Histogrammwert, um das Debugging und den Vergleich mit der MetaTrader-Referenzimplementierung zu unterstützen.

namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;

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

/// <summary>
/// Direct Williams %R histogram strategy with volume-weighted smoothing.
/// Trades only on strong bullish and bearish zone flips.
/// </summary>
public class ExpXwprHistogramVolDirectStrategy : Strategy
{
	private readonly StrategyParam<int> _williamsPeriod;
	private readonly StrategyParam<int> _highLevel1;
	private readonly StrategyParam<int> _lowLevel1;
	private readonly StrategyParam<MovingAverageKinds> _smoothingType;
	private readonly StrategyParam<int> _smoothingLength;
	private readonly StrategyParam<bool> _enableLongEntries;
	private readonly StrategyParam<bool> _enableShortEntries;
	private readonly StrategyParam<bool> _enableLongExits;
	private readonly StrategyParam<bool> _enableShortExits;
	private readonly StrategyParam<VolumeSources> _volumeSource;
	private readonly StrategyParam<int> _signalCooldownBars;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;
	private readonly StrategyParam<DataType> _candleType;

	private WilliamsR _williams;
	private DecimalLengthIndicator _valueSmoother;
	private DecimalLengthIndicator _volumeSmoother;
	private int? _previousZone;
	private int _cooldownRemaining;

	public int WilliamsPeriod { get => _williamsPeriod.Value; set => _williamsPeriod.Value = value; }
	public int HighLevel1 { get => _highLevel1.Value; set => _highLevel1.Value = value; }
	public int LowLevel1 { get => _lowLevel1.Value; set => _lowLevel1.Value = value; }
	public MovingAverageKinds SmoothingType { get => _smoothingType.Value; set => _smoothingType.Value = value; }
	public int SmoothingLength { get => _smoothingLength.Value; set => _smoothingLength.Value = value; }
	public bool EnableLongEntries { get => _enableLongEntries.Value; set => _enableLongEntries.Value = value; }
	public bool EnableShortEntries { get => _enableShortEntries.Value; set => _enableShortEntries.Value = value; }
	public bool EnableLongExits { get => _enableLongExits.Value; set => _enableLongExits.Value = value; }
	public bool EnableShortExits { get => _enableShortExits.Value; set => _enableShortExits.Value = value; }
	public VolumeSources VolumeSource { get => _volumeSource.Value; set => _volumeSource.Value = value; }
	public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public ExpXwprHistogramVolDirectStrategy()
	{
		_williamsPeriod = Param(nameof(WilliamsPeriod), 14)
			.SetRange(5, 200)
			.SetDisplay("Williams %R Period", "Lookback for the Williams %R oscillator", "Indicator");

		_highLevel1 = Param(nameof(HighLevel1), 1)
			.SetRange(-200, 200)
			.SetDisplay("High Level 1", "Bullish threshold", "Indicator");

		_lowLevel1 = Param(nameof(LowLevel1), -1)
			.SetRange(-200, 200)
			.SetDisplay("Low Level 1", "Bearish threshold", "Indicator");

		_smoothingType = Param(nameof(SmoothingType), MovingAverageKinds.Simple)
			.SetDisplay("Smoothing Type", "Moving average type used for smoothing", "Indicator");

		_smoothingLength = Param(nameof(SmoothingLength), 12)
			.SetRange(2, 200)
			.SetDisplay("Smoothing Length", "Moving average length", "Indicator");

		_enableLongEntries = Param(nameof(EnableLongEntries), true)
			.SetDisplay("Enable Long Entries", "Allow the strategy to open long positions", "Trading Rules");

		_enableShortEntries = Param(nameof(EnableShortEntries), true)
			.SetDisplay("Enable Short Entries", "Allow the strategy to open short positions", "Trading Rules");

		_enableLongExits = Param(nameof(EnableLongExits), true)
			.SetDisplay("Enable Long Exits", "Allow the strategy to close long positions", "Trading Rules");

		_enableShortExits = Param(nameof(EnableShortExits), true)
			.SetDisplay("Enable Short Exits", "Allow the strategy to close short positions", "Trading Rules");

		_volumeSource = Param(nameof(VolumeSource), VolumeSources.Tick)
			.SetDisplay("Volume Source", "Type of volume used for weighting", "Indicator");

		_signalCooldownBars = Param(nameof(SignalCooldownBars), 5)
			.SetRange(1, 200)
			.SetDisplay("Signal Cooldown", "Bars to wait between new entries", "Trading Rules");

		_stopLossPoints = Param(nameof(StopLossPoints), 1000)
			.SetRange(0, 10000)
			.SetDisplay("Stop Loss (ticks)", "Protective stop distance in price steps", "Risk Management");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 2000)
			.SetRange(0, 10000)
			.SetDisplay("Take Profit (ticks)", "Profit target distance in price steps", "Risk Management");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle type used for analysis", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_williams = null;
		_valueSmoother = null;
		_volumeSmoother = null;
		_previousZone = null;
		_cooldownRemaining = 0;
	}

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

		_williams = new WilliamsR { Length = WilliamsPeriod };
		_valueSmoother = CreateMovingAverage(SmoothingType, SmoothingLength);
		_volumeSmoother = CreateMovingAverage(SmoothingType, SmoothingLength);
		_previousZone = null;
		_cooldownRemaining = 0;

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

		StartProtection(
			takeProfit: new Unit(2, UnitTypes.Percent),
			stopLoss: new Unit(1, UnitTypes.Percent)
		);
	}

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

		if (_cooldownRemaining > 0)
			_cooldownRemaining--;

		var williamsValue = _williams.Process(candle);
		if (!_williams.IsFormed)
			return;

		var wprValue = williamsValue.ToDecimal();

		// Williams %R ranges from -100 to 0; shift to 0..100
		var normalized = wprValue + 100m;
		var bullishLevel = 80m;
		var bearishLevel = 20m;
		var zone = normalized >= bullishLevel ? 1 : normalized <= bearishLevel ? -1 : 0;

		if (_previousZone == null)
		{
			_previousZone = zone;
			return;
		}

		if (_previousZone.Value != zone && _cooldownRemaining == 0 && Position == 0)
		{
			if (zone > 0 && EnableLongEntries)
			{
				BuyMarket();
				_cooldownRemaining = SignalCooldownBars;
			}
			else if (zone < 0 && EnableShortEntries)
			{
				SellMarket();
				_cooldownRemaining = SignalCooldownBars;
			}
		}

		_previousZone = zone;
	}

	private decimal GetWeightedVolume(ICandleMessage candle)
	{
		if (VolumeSource == VolumeSources.Tick && candle.TotalTicks is int ticks && ticks > 0)
			return ticks;

		return candle.TotalVolume > 0m ? candle.TotalVolume : 1m;
	}

	private static DecimalLengthIndicator CreateMovingAverage(MovingAverageKinds type, int length)
	{
		return type switch
		{
			MovingAverageKinds.Simple => new SMA { Length = length },
			MovingAverageKinds.Exponential => new EMA { Length = length },
			MovingAverageKinds.Smoothed => new SmoothedMovingAverage { Length = length },
			MovingAverageKinds.Weighted => new WeightedMovingAverage { Length = length },
			MovingAverageKinds.Hull => new HullMovingAverage { Length = length },
			MovingAverageKinds.VolumeWeighted => new VolumeWeightedMovingAverage { Length = length },
			MovingAverageKinds.DoubleExponential => new DoubleExponentialMovingAverage { Length = length },
			MovingAverageKinds.TripleExponential => new TripleExponentialMovingAverage { Length = length },
			_ => new SMA { Length = length },
		};
	}

	public enum MovingAverageKinds
	{
		Simple,
		Exponential,
		Smoothed,
		Weighted,
		Hull,
		VolumeWeighted,
		DoubleExponential,
		TripleExponential,
	}

	public enum VolumeSources
	{
		Tick,
		Real,
	}
}