Auf GitHub ansehen

Get-Trend-Strategie

Übersicht

Diese Strategie ist ein StockSharp-Port des MetaTrader-Expertenberaters "Get trend", der ursprünglich für den M15-Handel mit einem H1-Bestätigungsfilter konzipiert wurde. Der Algorithmus kombiniert geglättete gleitende Durchschnitte und einen stochastischen Oszillator, um Mean-Reversion-Einstiege zu timen, die mit einem übergeordneten Trend übereinstimmen.

Handelslogik

  • Primärer Zeitrahmen: 15-Minuten-Kerzen werden für die Signalgenerierung und Orderausführung verwendet.
  • Bestätigungs-Zeitrahmen: Stündliche Kerzen liefern den übergeordneten geglätteten gleitenden Durchschnitt und den Schlusskurs zur Validierung des vorherrschenden Trends.
  • Trendfilter: Sowohl der M15- als auch der H1-Schluss müssen auf derselben Seite ihrer jeweiligen geglätteten gleitenden Durchschnitte liegen. Zusätzlich muss der M15-Schluss innerhalb eines konfigurierbaren Abstands von seinem gleitenden Durchschnitt bleiben, um einen Pullback-Einstieg zu gewährleisten.
  • Momentum-Auslöser: Long-Trades erfordern, dass die stochastische %K-Linie %D im überverkauften Bereich (unter 20) nach oben kreuzt. Short-Trades erfordern die umgekehrte Kreuzung im überkauften Bereich (über 80).
  • Ordersteuerung: Positionen werden mit festen Stop-Loss- und Take-Profit-Niveaus in Preispunkten geschützt. Ein optionaler Trailing-Stop zieht den Ausstieg enger, sobald sich der Preis weit genug zugunsten des Trades bewegt.

Einstiegsbedingungen

Long-Setup

  1. M15-Schluss liegt unter dem geglätteten gleitenden Durchschnitt von M15.
  2. H1-Schluss liegt unter dem geglätteten gleitenden Durchschnitt von H1.
  3. Der Abstand zwischen M15-Schluss und M15-Durchschnitt überschreitet den Price Threshold nicht (in Punkten/Ticks).
  4. Stochastik %K und %D liegen beide unter 20.
  5. Der vorherige %K-Wert lag unter %D, und der aktuelle %K-Wert kreuzte %D nach oben.
  6. Keine bestehende Long-Position (eine Short-Position wird geschlossen und umgekehrt).

Short-Setup

  1. M15-Schluss liegt über dem geglätteten gleitenden Durchschnitt von M15.
  2. H1-Schluss liegt über dem geglätteten gleitenden Durchschnitt von H1.
  3. Der Abstand zwischen M15-Schluss und M15-Durchschnitt überschreitet den Price Threshold nicht.
  4. Stochastik %K und %D liegen beide über 80.
  5. Der vorherige %K-Wert lag über %D, und der aktuelle %K-Wert kreuzte %D nach unten.
  6. Keine bestehende Short-Position (eine Long-Position wird geschlossen und umgekehrt).

Ausstiegsregeln

  • Stop-Loss: In absoluten Preispunkten vom Einstiegspreis festgelegt.
  • Take-Profit: In absoluten Preispunkten vom Einstiegspreis festgelegt.
  • Trailing-Stop: Wenn aktiviert, wird der Stop enger gezogen, sobald sich der Preis über die Trailing-Distanz hinaus bewegt, um Gewinne zu sichern und dabei den konfigurierten Trailing-Offset einzuhalten.

Parameter

Parameter Beschreibung Standard
M15CandleType Kerzentyp für die Signalgenerierung. 15-Minuten-Zeitrahmen
H1CandleType Kerzentyp für die Bestätigung. 1-Stunden-Zeitrahmen
MaM15Length Länge des geglätteten MA auf M15-Kerzen. 99
MaH1Length Länge des geglätteten MA auf H1-Kerzen. 184
StochasticLength %K-Periode des stochastischen Oszillators. 27
StochasticSignalLength %D-Glättungsperiode. 3
ThresholdPoints Maximaler Abstand (in Punkten) zwischen Preis und M15-MA für Einstiege. 10
TakeProfitPoints Take-Profit-Distanz (in Punkten). 540
StopLossPoints Stop-Loss-Distanz (in Punkten). 90
TrailingStopPoints Trailing-Stop-Distanz (in Punkten). 20
TradeVolume Basisordervolumen beim Öffnen neuer Trades. 0.1

Alle punktbasierten Parameter werden mit dem PriceStep des Instruments multipliziert, um sie in absolute Preisinkremente umzurechnen.

Implementierungshinweise

  • Die Strategie verwendet StockSharp's High-Level-API mit Kerzenabonnements und Indikatorenbindung (BindEx), um manuelle Bufferverwaltung zu vermeiden.
  • Die Trailing-Stop-Logik spiegelt die MetaTrader-Version: Sie aktiviert sich, sobald der unrealisierte Gewinn die Trailing-Distanz überschreitet, und zieht den Stop kontinuierlich in Richtung Preis.
  • Aktive Orders werden vor dem Wenden von Positionen storniert, um widersprüchliche Orders im Orderbuch zu vermeiden.
  • Chartbereiche zeigen M15-Kerzen mit dem geglätteten gleitenden Durchschnitt und ein dediziertes stochastisches Panel für visuelle Diagnosen.

Verwendungshinweise

  • Konfigurieren Sie die Kerzentypen passend zum Datenanbieter (z. B. können volumenbasierte Kerzen ersetzt werden, wenn sie dasselbe DataType-Konzept unterstützen).
  • Passen Sie den Schwellenwert und die Stop-Parameter an, wenn Sie mit Instrumenten unterschiedlicher Volatilität oder Tick-Größen handeln.
  • Für beste Ergebnisse wenden Sie die Strategie auf Trendinstrumente an, bei denen Pullbacks zur gleitenden Durchschnittslinie häufig vorkommen.
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>
/// Trend-following strategy converted from the MetaTrader "Get trend" expert.
/// </summary>
public class GetTrendStrategy : Strategy
{
	private readonly StrategyParam<DataType> _m15CandleType;
	private readonly StrategyParam<DataType> _h1CandleType;
	private readonly StrategyParam<int> _maM15Length;
	private readonly StrategyParam<int> _maH1Length;
	private readonly StrategyParam<int> _stochasticLength;
	private readonly StrategyParam<int> _stochasticSignalLength;
	private readonly StrategyParam<decimal> _thresholdPoints;
	private readonly StrategyParam<decimal> _takeProfitPoints;
	private readonly StrategyParam<decimal> _stopLossPoints;
	private readonly StrategyParam<decimal> _trailingStopPoints;
	private readonly StrategyParam<decimal> _tradeVolume;

	private SmoothedMovingAverage _maM15;
	private SmoothedMovingAverage _maH1;
	private StochasticOscillator _stochastic;

	private decimal? _maH1Value;
	private decimal? _lastH1Close;

	private decimal? _prevStochFast;
	private decimal? _prevStochSlow;

	private decimal? _entryPrice;
	private decimal? _stopPrice;
	private decimal? _takePrice;

	/// <summary>
	/// Initializes a new instance of the <see cref="GetTrendStrategy"/> class.
	/// </summary>
	public GetTrendStrategy()
	{
		_m15CandleType = Param(nameof(M15CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("M15 Candle", "Primary timeframe", "General");

		_h1CandleType = Param(nameof(H1CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("H1 Candle", "Higher timeframe", "General");

		_maM15Length = Param(nameof(MaM15Length), 99)
			.SetGreaterThanZero()
			.SetDisplay("M15 MA Length", "Smoothed MA length on M15", "Indicators")
			;

		_maH1Length = Param(nameof(MaH1Length), 184)
			.SetGreaterThanZero()
			.SetDisplay("H1 MA Length", "Smoothed MA length on H1", "Indicators")
			;

		_stochasticLength = Param(nameof(StochasticLength), 27)
			.SetGreaterThanZero()
			.SetDisplay("Stochastic %K", "%K period", "Indicators")
			;

		_stochasticSignalLength = Param(nameof(StochasticSignalLength), 3)
			.SetGreaterThanZero()
			.SetDisplay("Stochastic %D", "%D smoothing period", "Indicators");

		_thresholdPoints = Param(nameof(ThresholdPoints), 10m)
			.SetGreaterThanZero()
			.SetDisplay("Price Threshold", "Maximum distance from MA", "Filters")
			;

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 540m)
			.SetGreaterThanZero()
			.SetDisplay("Take Profit", "Take profit distance", "Risk")
			;

		_stopLossPoints = Param(nameof(StopLossPoints), 90m)
			.SetGreaterThanZero()
			.SetDisplay("Stop Loss", "Stop loss distance", "Risk")
			;

		_trailingStopPoints = Param(nameof(TrailingStopPoints), 20m)
			.SetDisplay("Trailing Stop", "Trailing stop distance", "Risk")
			;

		_tradeVolume = Param(nameof(TradeVolume), 0.1m)
			.SetGreaterThanZero()
			.SetDisplay("Trade Volume", "Order volume", "Risk");
	}

	/// <summary>
	/// Primary trading timeframe (M15 by default).
	/// </summary>
	public DataType M15CandleType
	{
		get => _m15CandleType.Value;
		set => _m15CandleType.Value = value;
	}

	/// <summary>
	/// Confirmation timeframe (H1 by default).
	/// </summary>
	public DataType H1CandleType
	{
		get => _h1CandleType.Value;
		set => _h1CandleType.Value = value;
	}

	/// <summary>
	/// Smoothed moving average length on the 15-minute chart.
	/// </summary>
	public int MaM15Length
	{
		get => _maM15Length.Value;
		set => _maM15Length.Value = value;
	}

	/// <summary>
	/// Smoothed moving average length on the hourly chart.
	/// </summary>
	public int MaH1Length
	{
		get => _maH1Length.Value;
		set => _maH1Length.Value = value;
	}

	/// <summary>
	/// %K period of the stochastic oscillator.
	/// </summary>
	public int StochasticLength
	{
		get => _stochasticLength.Value;
		set => _stochasticLength.Value = value;
	}

	/// <summary>
	/// %D period of the stochastic oscillator.
	/// </summary>
	public int StochasticSignalLength
	{
		get => _stochasticSignalLength.Value;
		set => _stochasticSignalLength.Value = value;
	}

	/// <summary>
	/// Maximum allowed distance between price and the M15 moving average in points.
	/// </summary>
	public decimal ThresholdPoints
	{
		get => _thresholdPoints.Value;
		set => _thresholdPoints.Value = value;
	}

	/// <summary>
	/// Take-profit distance expressed in points.
	/// </summary>
	public decimal TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Stop-loss distance expressed in points.
	/// </summary>
	public decimal StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Trailing stop distance expressed in points.
	/// </summary>
	public decimal TrailingStopPoints
	{
		get => _trailingStopPoints.Value;
		set => _trailingStopPoints.Value = value;
	}

	/// <summary>
	/// Default order volume.
	/// </summary>
	public decimal TradeVolume
	{
		get => _tradeVolume.Value;
		set => _tradeVolume.Value = value;
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, M15CandleType), (Security, H1CandleType)];

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

		_maH1Value = null;
		_lastH1Close = null;
		_prevStochFast = null;
		_prevStochSlow = null;
		_entryPrice = null;
		_stopPrice = null;
		_takePrice = null;
	}

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

		Volume = TradeVolume;

		_maM15 = new SmoothedMovingAverage { Length = MaM15Length };
		_maH1 = new SmoothedMovingAverage { Length = MaH1Length };
		_stochastic = new StochasticOscillator();
		_stochastic.K.Length = StochasticLength;
		_stochastic.D.Length = StochasticSignalLength;

		// Subscribe to 15-minute candles and bind the required indicators.
		var m15Subscription = SubscribeCandles(M15CandleType);
		m15Subscription
			.BindEx(_maM15, _stochastic, ProcessM15Candle)
			.Start();

		// Subscribe to hourly candles for trend confirmation.
		var h1Subscription = SubscribeCandles(H1CandleType);
		h1Subscription
			.Bind(_maH1, ProcessH1Candle)
			.Start();

		var priceArea = CreateChartArea();
		if (priceArea != null)
		{
			DrawCandles(priceArea, m15Subscription);
			DrawIndicator(priceArea, _maM15);
			DrawOwnTrades(priceArea);

			var oscillatorArea = CreateChartArea();
			if (oscillatorArea != null)
			{
				oscillatorArea.Title = "Stochastic";
				DrawIndicator(oscillatorArea, _stochastic);
			}
		}
	}

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

		// Store the latest H1 moving average and close for trend checks.
		_maH1Value = maValue;
		_lastH1Close = candle.ClosePrice;
	}

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

		if (!maValue.IsFinal || !stochasticValue.IsFinal)
			return;

		var ma = maValue.ToDecimal();
		var stochTyped = (StochasticOscillatorValue)stochasticValue;

		if (stochTyped.K is not decimal stochFast || stochTyped.D is not decimal stochSlow)
			return;

		// Manage protective levels before looking for new entries.
		ManageOpenPosition(candle);

		if (_maH1Value is not decimal maH1 || _lastH1Close is not decimal priceH1)
			goto UpdateStochastic;

		var priceM15 = candle.ClosePrice;
		var priceStep = Security?.PriceStep ?? 1m;
		var threshold = ThresholdPoints * priceStep;

		var nearLowerBand = priceM15 < ma && priceH1 < maH1 && ma - priceM15 <= threshold;
		var nearUpperBand = priceM15 > ma && priceH1 > maH1 && priceM15 - ma <= threshold;

		var crossUp = _prevStochFast is decimal prevFastUp && _prevStochSlow is decimal prevSlowUp && prevFastUp < prevSlowUp && stochFast > stochSlow;
		var crossDown = _prevStochFast is decimal prevFastDown && _prevStochSlow is decimal prevSlowDown && prevFastDown > prevSlowDown && stochFast < stochSlow;

		if (nearLowerBand && stochSlow < 20m && stochFast < 20m && crossUp && Position <= 0)
		{
			EnterLong(candle.ClosePrice, priceStep);
		}
		else if (nearUpperBand && stochSlow > 80m && stochFast > 80m && crossDown && Position >= 0)
		{
			EnterShort(candle.ClosePrice, priceStep);
		}

	UpdateStochastic:
		_prevStochFast = stochFast;
		_prevStochSlow = stochSlow;
	}

	private void EnterLong(decimal entryPrice, decimal priceStep)
	{
		// Cancel opposite orders and flip the position if needed.
		CancelActiveOrders();

		BuyMarket();

		_entryPrice = entryPrice;
		_takePrice = entryPrice + TakeProfitPoints * priceStep;
		_stopPrice = entryPrice - StopLossPoints * priceStep;
	}

	private void EnterShort(decimal entryPrice, decimal priceStep)
	{
		// Cancel opposite orders and flip the position if needed.
		CancelActiveOrders();

		SellMarket();

		_entryPrice = entryPrice;
		_takePrice = entryPrice - TakeProfitPoints * priceStep;
		_stopPrice = entryPrice + StopLossPoints * priceStep;
	}

	private void ManageOpenPosition(ICandleMessage candle)
	{
		if (Position == 0)
			return;

		var priceStep = Security?.PriceStep ?? 1m;
		var trailingDistance = TrailingStopPoints * priceStep;

		if (Position > 0)
		{
			if (_entryPrice is decimal entry && TrailingStopPoints > 0 && candle.ClosePrice - entry >= trailingDistance)
			{
				var candidate = candle.ClosePrice - trailingDistance;
				_stopPrice = _stopPrice.HasValue ? Math.Max(_stopPrice.Value, candidate) : candidate;
			}

			if (_takePrice is decimal take && candle.HighPrice >= take)
			{
				SellMarket();
				ResetProtection();
				return;
			}

			if (_stopPrice is decimal stop && candle.LowPrice <= stop)
			{
				SellMarket();
				ResetProtection();
			}
		}
		else if (Position < 0)
		{
			if (_entryPrice is decimal entry && TrailingStopPoints > 0 && entry - candle.ClosePrice >= trailingDistance)
			{
				var candidate = candle.ClosePrice + trailingDistance;
				_stopPrice = _stopPrice.HasValue ? Math.Min(_stopPrice.Value, candidate) : candidate;
			}

			if (_takePrice is decimal take && candle.LowPrice <= take)
			{
				BuyMarket();
				ResetProtection();
				return;
			}

			if (_stopPrice is decimal stop && candle.HighPrice >= stop)
			{
				BuyMarket();
				ResetProtection();
			}
		}
	}

	private void ResetProtection()
	{
		_entryPrice = null;
		_stopPrice = null;
		_takePrice = null;
	}
}