Auf GitHub ansehen

Ma SAR ADX Bind Strategie

Überblick

Diese Strategie ist eine Konvertierung des ursprünglichen MaSarADX.mq5 MetaTrader 5-Expertenberaters auf die StockSharp High-Level-API. Das System kombiniert einen einfachen gleitenden Durchschnitt als Trendfilter mit Directional Movement Index (ADX)-Signalen und dem Parabolic SAR Trailing Stop. Handelsentscheidungen werden nur bei abgeschlossenen Kerzen ausgewertet, was das Verhalten des "ersten Tick einer neuen Bar" aus der MQL-Version repliziert. Wenn der Kerzenschlusskurs sowohl mit dem Trendtrend des gleitenden Durchschnitts als auch mit dem gerichteten Gleichgewicht des ADX übereinstimmt, wird eine Position geöffnet. Parabolic SAR leitet sowohl Traderichtung als auch Exits, indem er eine vollständige Liquidation erzwingt, wenn der Preis auf die entgegengesetzte Seite der SAR-Punkte kreuzt.

Indikatoren und Daten

  • Einfacher gleitender Durchschnitt (SMA) – liefert den primären Trendrichtungsfilter. Standardlänge: 100 Kerzen.
  • Average Directional Index (ADX) – liefert +DI und −DI zur Bestätigung der Richtungsstärke. Standardlänge: 14.
  • Parabolic SAR – dient als Stop-and-Reverse-Overlay und definiert Ausstiegsbedingungen. Standardbeschleunigung: 0.02; maximale Beschleunigung: 0.10.
  • Kerzen – jeder Zeitrahmen kann angefordert werden. Standardmäßig abonniert die Strategie 1-Stunden-Kerzen, aber der Parameter kann angepasst werden.

Die Implementierung abonniert StockSharp-Kerzenströme und bindet alle drei Indikatoren mit dem BindEx-Helper, sodass jeder Callback synchronisierte Werte für dieselbe Kerze empfängt.

Handelsstrategie

Long-Einstieg

  1. Kerzenschlusskurs liegt über dem gleitenden Durchschnitt.
  2. +DI ist größer oder gleich −DI, was bullischen Richtungsdruck anzeigt.
  3. Kerzenschlusskurs liegt über dem Parabolic SAR-Wert.
  4. Keine Long-Position ist aktuell offen (Position <= 0).

Wenn alle Regeln übereinstimmen, wird eine Kauf-Marktorder für das konfigurierte Basisvolumen plus Größe zur Abdeckung einer Short-Position gesendet.

Short-Einstieg

  1. Kerzenschlusskurs liegt unter dem gleitenden Durchschnitt.
  2. +DI ist kleiner oder gleich −DI, was bärischen Richtungsdruck anzeigt.
  3. Kerzenschlusskurs liegt unter dem Parabolic SAR-Wert.
  4. Keine Short-Position ist aktuell offen (Position >= 0).

Eine Verkauf-Marktorder wird platziert, wenn alle Short-Regeln übereinstimmen.

Ausstiege

  • Long-Positionen werden sofort geschlossen, wenn der Preis unter den Parabolic SAR fällt.
  • Short-Positionen werden gedeckt, wenn der Preis über den Parabolic SAR steigt.

Es werden keine separaten Stop-Loss- oder Take-Profit-Level hinzugefügt; der SAR-Kreuzungspunkt ist das einzige Ausstiegssignal, dem ursprünglichen Expertenberater folgend. Da Exits vor neuen Einstiegen ausgewertet werden, wird die Strategie nicht auf derselben Kerze von Short zu Long (oder umgekehrt) wechseln.

Parameter

Name Beschreibung Standard Hinweise
MaPeriod Länge des einfachen gleitenden Durchschnitts zur Definition des Trendfilters. 100 Optimierbar, muss größer als null sein.
AdxPeriod Periode der ADX-Berechnung, die +DI und −DI erzeugt. 14 Optimierbar, muss größer als null sein.
SarStep Beschleunigungsfaktor und Inkrement für den Parabolic SAR. 0.02 Entspricht dem MQL step-Parameter.
SarMax Maximaler Beschleunigungsfaktor für Parabolic SAR. 0.10 Spiegelt die MQL maximum-Einstellung wider.
Volume Basis-Ordergröße für neue Einstiege. 1 Ersetzt die margenbasierte Lot-Größenberechnung der MetaTrader-Version. Die tatsächliche Ordergröße ist Volume + |Position|, sodass Umkehrungen das bestehende Engagement glätten.
CandleType Der über StockSharp abonnierte Kerzendatentyp. 1 Stunde Auf jeden Zeitrahmen einstellbar.

Implementierungshinweise

  • Die Indikatorverarbeitung verwendet StockSharps High-Level-BindEx-Pipeline und stellt sicher, dass SMA, ADX und SAR im Gleichschritt ohne manuelles Puffern aktualisiert werden.
  • Exits werden auch dann ausgeführt, wenn AllowTrading vorübergehend deaktiviert ist, um Risikokontrollen jederzeit aktiv zu halten.
  • Grafik-Hilfstools sind enthalten: das primäre Panel zeigt Preis, SMA und SAR, während ein sekundäres Panel den ADX-Indikator für Diagnosen anzeigt.
  • Log-Anweisungen beschreiben jede Handelsentscheidung mit den zugrunde liegenden Indikatorwerten zur Vereinfachung von Vorwärtstests und Debugging.

Verwendungsrichtlinien

  1. Hängen Sie die Strategie an ein Wertpapier und Portfolio im Designer oder Backtester.
  2. Passen Sie den Kerzentyp an Ihren Handelshorizont an (z.B. M15-, H1- oder D1-Kerzen).
  3. Passen Sie den gleitenden Durchschnittszeitraum, den ADX-Zeitraum und SAR-Parameter an die Volatilität des Instruments an.
  4. Setzen Sie den Volume-Parameter auf Ihre bevorzugte Positionsgröße. Wenn Sie das adaptive Money Management aus dem ursprünglichen Skript benötigen, integrieren Sie Ihre eigene portfoliobasierte Größenbestimmung vor dem Senden von Orders.
  5. Führen Sie die Strategie aus. Trades werden erst ausgelöst, nachdem alle Indikatoren ausreichend historische Werte für ihre Formierung erzeugt haben.

Unterschiede zum ursprünglichen Expertenberater

  • Die margenbasierte Lot-Berechnung wurde durch einen festen Volume-Parameter ersetzt, um die Strategie broker-neutral in StockSharp zu halten.
  • Trade-Verwaltung, Indikatorwerte und die Auswertungsreihenfolge (Ausstieg vor Einstieg) folgen strikt der MetaTrader-Referenzlogik.
  • Alle Kommentare im Quellcode sind auf Englisch, um den Projektrichtlinien zu entsprechen.
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>
/// Conversion of the MaSarADX MetaTrader strategy to StockSharp high level API.
/// Combines a moving average, ADX directional movement and Parabolic SAR for entries and exits.
/// </summary>
public class MaSarAdxBindStrategy : Strategy
{
	private readonly StrategyParam<int> _maPeriod;
	private readonly StrategyParam<int> _adxPeriod;
	private readonly StrategyParam<decimal> _sarStep;
	private readonly StrategyParam<decimal> _sarMax;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _previousHigh;
	private decimal? _previousLow;
	private decimal? _previousClose;
	private decimal _smoothedPlusDm;
	private decimal _smoothedMinusDm;
	private decimal _smoothedTrueRange;
	private int _adxSamples;

	/// <summary>
	/// Moving average period used for the trend filter.
	/// </summary>
	public int MaPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

	/// <summary>
	/// Average Directional Index calculation period.
	/// </summary>
	public int AdxPeriod
	{
		get => _adxPeriod.Value;
		set => _adxPeriod.Value = value;
	}

	/// <summary>
	/// Acceleration step for the Parabolic SAR indicator.
	/// </summary>
	public decimal SarStep
	{
		get => _sarStep.Value;
		set => _sarStep.Value = value;
	}

	/// <summary>
	/// Maximum acceleration factor for the Parabolic SAR indicator.
	/// </summary>
	public decimal SarMax
	{
		get => _sarMax.Value;
		set => _sarMax.Value = value;
	}


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

	/// <summary>
	/// Initializes a new instance of <see cref="MaSarAdxBindStrategy"/>.
	/// </summary>
	public MaSarAdxBindStrategy()
	{
		_maPeriod = Param(nameof(MaPeriod), 120)
		.SetGreaterThanZero()
		.SetDisplay("MA Period", "Length of the trend moving average", "Indicators")
		
		.SetOptimize(20, 200, 10);

		_adxPeriod = Param(nameof(AdxPeriod), 18)
		.SetGreaterThanZero()
		.SetDisplay("ADX Period", "Length of the Average Directional Index", "Indicators")
		
		.SetOptimize(7, 28, 1);

		_sarStep = Param(nameof(SarStep), 0.02m)
		.SetRange(0.005m, 0.2m)
		.SetDisplay("SAR Step", "Acceleration step for Parabolic SAR", "Indicators")
		;

		_sarMax = Param(nameof(SarMax), 0.1m)
		.SetRange(0.05m, 1m)
		.SetDisplay("SAR Maximum", "Maximum acceleration for Parabolic SAR", "Indicators")
		;


		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(2).TimeFrame())
		.SetDisplay("Candle Type", "Type of candles to request", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_previousHigh = null;
		_previousLow = null;
		_previousClose = null;
		_smoothedPlusDm = 0m;
		_smoothedMinusDm = 0m;
		_smoothedTrueRange = 0m;
		_adxSamples = 0;
	}

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

		// Instantiate indicators used in the original MetaTrader script.
		var movingAverage = new SimpleMovingAverage
		{
			Length = MaPeriod
		};

		var parabolicSar = new ParabolicSar
		{
			Acceleration = SarStep,
			AccelerationStep = SarStep,
			AccelerationMax = SarMax
		};

		// Subscribe to candle data and bind indicator updates to a single handler.
		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(movingAverage, parabolicSar, ProcessCandle)
			.Start();

		// Draw the trading context for visual debugging when charts are available.
		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, movingAverage);
			DrawIndicator(area, parabolicSar);
			DrawOwnTrades(area);

		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal movingAverage, decimal sar)
	{
		// Work only with completed candles to mirror the original first-tick logic.
		if (candle.State != CandleStates.Finished)
			return;

		var (plusDi, minusDi, isReady) = UpdateDirectionalMovement(candle);
		if (!isReady)
			return;

		// Always allow risk exits even if trading is temporarily disabled.
		if (Position > 0 && candle.ClosePrice < sar)
		{
			SellMarket();
			return;
		}

		if (Position < 0 && candle.ClosePrice > sar)
		{
			BuyMarket();
			return;
		}

		// Entry conditions replicated from the MetaTrader version.
		var bullishSignal = candle.ClosePrice > movingAverage && plusDi >= minusDi && candle.ClosePrice > sar;
		var bearishSignal = candle.ClosePrice < movingAverage && plusDi <= minusDi && candle.ClosePrice < sar;

		if (bullishSignal && Position <= 0)
		{
			BuyMarket();
			return;
		}

		if (bearishSignal && Position >= 0)
		{
			SellMarket();
		}
	}

	private (decimal plusDi, decimal minusDi, bool isReady) UpdateDirectionalMovement(ICandleMessage candle)
	{
		if (_previousHigh is not decimal previousHigh ||
			_previousLow is not decimal previousLow ||
			_previousClose is not decimal previousClose)
		{
			_previousHigh = candle.HighPrice;
			_previousLow = candle.LowPrice;
			_previousClose = candle.ClosePrice;
			return (0m, 0m, false);
		}

		var upMove = candle.HighPrice - previousHigh;
		var downMove = previousLow - candle.LowPrice;
		var plusDm = upMove > downMove && upMove > 0m ? upMove : 0m;
		var minusDm = downMove > upMove && downMove > 0m ? downMove : 0m;
		var trueRange = Math.Max(
			candle.HighPrice - candle.LowPrice,
			Math.Max(
				Math.Abs(candle.HighPrice - previousClose),
				Math.Abs(candle.LowPrice - previousClose)));

		if (_adxSamples < AdxPeriod)
		{
			_smoothedPlusDm += plusDm;
			_smoothedMinusDm += minusDm;
			_smoothedTrueRange += trueRange;
			_adxSamples++;
		}
		else
		{
			_smoothedPlusDm = _smoothedPlusDm - (_smoothedPlusDm / AdxPeriod) + plusDm;
			_smoothedMinusDm = _smoothedMinusDm - (_smoothedMinusDm / AdxPeriod) + minusDm;
			_smoothedTrueRange = _smoothedTrueRange - (_smoothedTrueRange / AdxPeriod) + trueRange;
		}

		_previousHigh = candle.HighPrice;
		_previousLow = candle.LowPrice;
		_previousClose = candle.ClosePrice;

		if (_adxSamples < AdxPeriod || _smoothedTrueRange <= 0m)
			return (0m, 0m, false);

		return (
			100m * _smoothedPlusDm / _smoothedTrueRange,
			100m * _smoothedMinusDm / _smoothedTrueRange,
			true);
	}
}