Auf GitHub ansehen

ATR-Normalisiertes-Histogramm-Strategie

Überblick

Die ATR-Normalisiertes-Histogramm-Strategie reproduziert das Verhalten des MetaTrader-Experten Exp_ATR_Normalize_Histogram innerhalb von StockSharp. Das System beobachtet das normalisierte Verhältnis zwischen dem geglätteten Abstand vom Schlusskurs zum Tief und dem geglätteten True Range. Farbänderungen des Histogramms steuern sowohl Ein- als auch Ausstiege und emulieren die Multi-Buffer-Logik der ursprünglichen MQL5-Implementierung.

Indikatorberechnung

  1. Für jede abgeschlossene Kerze berechnet die Strategie:

    • diff = Close − Low.
    • range = max(High, vorheriger Close) − min(Low, vorheriger Close).
  2. Jede Reihe wird unabhängig mit den gewählten Methoden und Längen geglättet. Fünf Methoden sind verfügbar: Simple, Exponential, Smoothed (RMA), Weighted und Jurik. Nicht unterstützte MQL-Methoden (JurX, Parabolic, T3, VIDYA, AMA) fallen auf den einfachen gleitenden Durchschnitt zurück.

  3. Der normalisierte Histogrammwert wird berechnet als

    normalized = 100 × smoothedDiff / max(|smoothedRange|, PriceStep).

  4. Schwellenwerte unterteilen das Histogramm in fünf Bänder. Übergänge zwischen Bändern spiegeln den Farb-Buffer wider, der vom MQL-Indikator erzeugt wird.

Signallogik

  • EinstiegsfilterSignalBar wählt aus, welcher historische Balken ausgewertet werden soll (Standard 1, der zuletzt geschlossene Balken). Die Strategie vergleicht die Farbe dieses Balkens mit dem vorherigen:
    • Ein Übergang vom bullischen Extrem (Farbe 0) zu einer anderen Farbe eröffnet eine Long-Position, wenn Long-Trades aktiviert sind.
    • Ein Übergang vom bärischen Extrem (Farbe 4) zu einer anderen Farbe eröffnet eine Short-Position, wenn Short-Trades aktiviert sind.
  • Ausstiegsfilter – die Farbe des vorherigen Balkens allein reicht aus, um Positionen zu schließen:
    • Farbe 0 schließt Short-Positionen, wenn Short-Exits aktiviert sind.
    • Farbe 4 schließt Long-Positionen, wenn Long-Exits aktiviert sind.
  • Ausstiege werden vor neuen Einstiegen verarbeitet, damit die Strategie niemals überlappende Trades hält.

Risikomanagement

Die Strategie verfolgt den letzten Ausführungspreis und erzwingt optional Schutz-Stops und Ziele in Instrumentpunkten. Die Konvertierung verwendet Security.PriceStep und entspricht damit dem "Punkte"-Konzept des ursprünglichen Experten. Wenn eines der Limits innerhalb der Kerze erreicht wird, wird die Position sofort geschlossen und die Handelsrichtung kann beim folgenden Signal wechseln.

Parameter

  • CandleType – Zeitrahmen für die Berechnung.
  • FirstSmoothingMethod / SecondSmoothingMethod – Glättungstyp für diff- und range-Streams.
  • FirstLength / SecondLength – Perioden für die Glätter.
  • HighLevel, MiddleLevel, LowLevel – Histogramm-Schwellenwerte (Standard 60/50/40).
  • SignalBar – Versatz für die Buffer-Auswertung (Minimum 1).
  • EnableBuyEntries, EnableSellEntries, EnableBuyExits, EnableSellExits – Schalter zur Verwaltung der vier Handelsrichtungen.
  • TradeVolume – Basis-Ordergröße. Die Strategie gleicht vorhandene Exposition automatisch aus, wenn die Richtung gewechselt wird.
  • StopLossPoints, TakeProfitPoints – optionale Schutzabstände in Punkten; auf null setzen, um zu deaktivieren.

Hinweise und Unterschiede zur MQL-Version

  • Beide Glättungsstufen sind unabhängig konfigurierbar, aber nur die fünf StockSharp-Implementierungen für gleitende Durchschnitte sind verfügbar. Wenn eine andere MQL-Methode gewählt wird, verwendet die Strategie den einfachen gleitenden Durchschnitt mit unveränderter Länge.
  • Die SignalBar-Logik folgt dem Buffer-Versatz aus CopyBuffer, sodass größere Versatze den gewählten Balken weiterhin mit seinem unmittelbaren Vorgänger vergleichen.
  • Die Geldmanagement-Parameter des ursprünglichen Experten (MM, MMMode, Deviation) werden auf einen einzigen TradeVolume-Parameter vereinfacht. Die Orderausführung erfolgt zum Marktpreis mit optionalem Stop/Ziel-Monitoring.
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;

/// <summary>
/// Strategy based on normalized ATR histogram transitions (simplified).
/// Uses ATR to measure volatility and trades on regime changes.
/// </summary>
public class AtrNormalizeHistogramStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<int> _emaLength;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

	public AtrNormalizeHistogramStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetGreaterThanZero()
			.SetDisplay("ATR Length", "ATR period", "Indicators");

		_emaLength = Param(nameof(EmaLength), 20)
			.SetGreaterThanZero()
			.SetDisplay("EMA Length", "EMA period for trend", "Indicators");
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var atr = new AverageTrueRange { Length = AtrLength };
		var ema = new ExponentialMovingAverage { Length = EmaLength };

		decimal prevAtr = 0;
		bool hasPrev = false;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(atr, ema, (ICandleMessage candle, decimal atrValue, decimal emaValue) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!hasPrev)
				{
					prevAtr = atrValue;
					hasPrev = true;
					return;
				}

				if (!IsFormedAndOnlineAndAllowTrading())
				{
					prevAtr = atrValue;
					return;
				}

				var price = candle.ClosePrice;

				// ATR expanding + price above EMA = bullish breakout
				if (atrValue > prevAtr * 1.1m && price > emaValue && Position <= 0)
				{
					BuyMarket();
				}
				// ATR expanding + price below EMA = bearish breakout
				else if (atrValue > prevAtr * 1.1m && price < emaValue && Position >= 0)
				{
					SellMarket();
				}

				prevAtr = atrValue;
			})
			.Start();

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