Auf GitHub ansehen

3207 – MA Trend-Strategie

Übersicht

Die MA Trend-Strategie repliziert den MetaTrader-Experten MA Trend.mq5 mit der High-Level-API von StockSharp. Der Bot folgt einem einzigen linear gewichteten gleitenden Durchschnitt mit einem konfigurierbaren Vorwärts-Shift. Wenn der Schlusskurs über den verschobenen Durchschnitt steigt, geht die Strategie long, während ein Fall unter den Durchschnitt Short-Positionen eröffnet. Optionale Stop-Loss-, Take-Profit- und Trailing-Stop-Regeln spiegeln die Risikokontrollen der ursprünglichen MQL-Implementierung wider.

Trading-Logik

  1. Den konfigurierten Kerzentyp abonnieren (Standard: 1-Minuten-Zeitrahmen) und einen gleitenden Durchschnitt mit der gewählten Methode und Preisquelle berechnen.
  2. Den gleitenden Durchschnittswert um die angeforderte Anzahl abgeschlossener Kerzen vorwärts verschieben, bevor er mit dem neuesten Schlusskurs verglichen wird.
  3. Signale generieren:
    • Long – Schlusskurs über dem verschobenen MA (umgekehrt, wenn ReverseSignals aktiviert ist).
    • Short – Schlusskurs unter dem verschobenen MA (umgekehrt, wenn ReverseSignals aktiviert ist).
  4. Positionsmanagement-Optionen anwenden:
    • Das entgegengesetzte Exposure schließen, bevor ein Trade eröffnet wird, wenn CloseOpposite true ist.
    • Neue Einstiege blockieren, wenn OnlyOnePosition aktiviert ist und bereits eine Position existiert.
  5. Ausstiege mit Stop-Loss-, Take-Profit- und Trailing-Stop-Distanzen in Pips verwalten. Die Trailing-Logik erfordert, dass der Preis sich um TrailingStopPips + TrailingStepPips bewegt, bevor der Stop enger gezogen wird, genau wie beim MQL-Experten.

Parameter

Name Typ Standard Beschreibung
OrderVolume decimal 0.1 Ordergröße in Lots/Kontrakten.
StopLossPips int 50 Stop-Loss-Distanz in Pips. Null deaktiviert den festen Stop.
TakeProfitPips int 140 Take-Profit-Distanz in Pips. Null deaktiviert das Ziel.
TrailingStopPips int 15 Trailing-Stop-Distanz. Auf null setzen, um Trailing zu deaktivieren.
TrailingStepPips int 5 Zusätzliche Pips erforderlich, bevor der Trailing Stop bewegt wird. Muss positiv bleiben, wenn TrailingStopPips größer als null ist.
MaPeriod int 12 Länge des gleitenden Durchschnitts.
MaShift int 3 Anzahl abgeschlossener Bars zum Vorwärtsverschieben des gleitenden Durchschnitts.
MaMethod MovingAverageKind Weighted Berechnungsmodus des gleitenden Durchschnitts (Simple, Exponential, Smoothed, Weighted).
AppliedPrice AppliedPriceMode Weighted Kerzenpreis als Indikatoreingang (Close, Open, High, Low, Median, Typical, Weighted).
OnlyOnePosition bool false Strategie auf eine einzelne offene Position beschränken.
ReverseSignals bool false Long/Short-Signalrichtungen umkehren.
CloseOpposite bool false Entgegengesetztes Exposure vor dem Einstieg in eine neue Position schließen.
CandleType DataType 1 minute Kerzentyp/Zeitrahmen für den Indikator.

Hinweise

  • Die Pip-Größe passt sich automatisch an Instrumente mit 3/5-Dezimal-Preisen an, um das ursprüngliche MetaTrader-Verhalten zu replizieren.
  • Die Trailing-Stop-Validierung reproduziert die MQL-Prüfung: wenn TrailingStopPips > 0 und TrailingStepPips <= 0, wirft die Strategie beim Start eine Ausnahme.
  • Alle Indikatorupdates und Orderentscheidungen verwenden nur abgeschlossene Kerzen, was deterministische Backtests gewährleistet.
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;

public class MaTrendStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public MaTrendStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}