Auf GitHub ansehen

Zwei-MA-Vier-Stufen-Strategie

Diese Strategie repliziert den MetaTrader-Experten "2MA_4Level" unter Verwendung der StockSharp High-Level-API. Sie handelt ein einzelnes Instrument mit zwei geglätteten gleitenden Durchschnitten (SMMA), die auf dem Medianpreis berechnet werden, und überwacht fünf relative Kreuzungszonen zwischen der schnellen und langsamen Kurve. Einstiege sind nur erlaubt, wenn keine Position offen ist, und jeder Handel wird durch pip-basierte Stop-Loss- und Take-Profit-Offsets geschützt.

Logik

  • Eine schnelle und eine langsame SMMA werden auf der ausgewählten Kerzenserie berechnet (standardmäßig 50 und 130 Perioden).
  • Die vorherigen und aktuellen SMMA-Werte der abgeschlossenen Kerze werden ausgewertet, um einen Kreuzungspunkt zu erkennen.
  • Die Kreuzung wird gegen fünf Schwellenwerte geprüft, die aus dem langsamen MA aufgebaut werden:
    • der reine langsame MA (ohne Offset),
    • langsamer MA + MostTopLevel Pips,
    • langsamer MA + TopLevel Pips,
    • langsamer MA - LowermostLevel Pips,
    • langsamer MA - LowerLevel Pips.
  • Wenn der schnelle MA über einen Schwellenwert kreuzt, wird eine Long-Position eröffnet (wenn flat). Ein Kreuz unterhalb eines Schwellenwerts öffnet eine Short-Position.
  • Stop-Loss- und Take-Profit-Levels werden über StartProtection unter Verwendung des Instrument-Pip-Werts (Security.PriceStep) angehängt.

Die Strategie pyramidiert keine Positionen: Ein neuer Handel kann erst nach Schließung des vorherigen durch Stop oder Ziel eröffnet werden.

Parameter

Parameter Standard Beschreibung
FastPeriod 50 Länge des schnellen geglätteten gleitenden Durchschnitts. Muss kleiner als SlowPeriod sein.
SlowPeriod 130 Länge des langsamen geglätteten gleitenden Durchschnitts.
MostTopLevel 500 Oberer Offset (in Pips) für die weiteste bullische/bärische Bestätigung. Muss größer als TopLevel sein.
TopLevel 250 Oberer Offset (in Pips) für die sekundäre bullische/bärische Bestätigung.
LowerLevel 250 Unterer Offset (in Pips) für die sekundäre bärische/bullische Bestätigung. Muss kleiner als LowermostLevel sein.
LowermostLevel 500 Unterer Offset (in Pips) für die weiteste bärische/bullische Bestätigung.
TakeProfitPips 55 Abstand vom Einstieg zum Take-Profit, ausgedrückt in Pips.
StopLossPips 260 Abstand vom Einstieg zum Stop-Loss, ausgedrückt in Pips.
CandleType 15-Minuten-Zeitrahmen Kerzenserie für SMMA-Berechnungen und Signalverarbeitung.

Implementierungsdetails

  • Der Medianpreis ((High + Low) / 2) speist beide SMMAs und entspricht der MT5-Konfiguration mit PRICE_MEDIAN.
  • Der Kreuzungstest vergleicht die letzte abgeschlossene Kerze mit der vorherigen, wodurch jede Abhängigkeit von teilweise gebildeten Bars entfällt.
  • StartProtection verbindet Stop-Loss und Take-Profit einmalig beim Start, sodass jede Order automatisch die konfigurierten Risikolimits erbt.
  • Die Strategie stoppt sich während OnStarted, wenn ungültige Parameterkombinationen angegeben werden (z.B. FastPeriod >= SlowPeriod).

Verwendungshinweise

  1. Verbinden Sie die Strategie mit einem Instrument mit definiertem PriceStep; andernfalls fällt die Pip-Konvertierung auf den Wert 1 zurück.
  2. Geeignet für Hedging-Konten in MT5; in StockSharp verhält es sich gleich, indem jeweils nur eine offene Position sichergestellt wird.
  3. Optimierungs-Hooks (SetCanOptimize) sind für beide MA-Perioden aktiviert, sodass Sie Parameter-Sweeps direkt vom StockSharp-Optimizer ausführen können.
  4. Da die Strategie ausschließlich auf Stop-Loss- und Take-Profit-Ausstiegen basiert, stellen Sie sicher, dass die konfigurierten Abstände mit der Instrumentenvolatilität übereinstimmen, um längere Exponierungszeiten zu vermeiden.

Dateien

  • CS/TwoMaFourLevelStrategy.cs – C#-Implementierung der Handelslogik.
  • README_ru.md – Russische Dokumentation.
  • README_zh.md – Chinesische Dokumentation.
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>
/// Two smoothed moving average crossover strategy with level offsets.
/// </summary>
public class TwoMaFourLevelStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _mostTopLevel;
	private readonly StrategyParam<int> _topLevel;
	private readonly StrategyParam<int> _lowerLevel;
	private readonly StrategyParam<int> _lowermostLevel;
	private readonly StrategyParam<int> _takeProfitPips;
	private readonly StrategyParam<int> _stopLossPips;
	private readonly StrategyParam<DataType> _candleType;

	private SmoothedMovingAverage _fastMa = null!;
	private SmoothedMovingAverage _slowMa = null!;
	private decimal? _prevFast;
	private decimal? _prevSlow;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int MostTopLevel { get => _mostTopLevel.Value; set => _mostTopLevel.Value = value; }
	public int TopLevel { get => _topLevel.Value; set => _topLevel.Value = value; }
	public int LowerLevel { get => _lowerLevel.Value; set => _lowerLevel.Value = value; }
	public int LowermostLevel { get => _lowermostLevel.Value; set => _lowermostLevel.Value = value; }
	public int TakeProfitPips { get => _takeProfitPips.Value; set => _takeProfitPips.Value = value; }
	public int StopLossPips { get => _stopLossPips.Value; set => _stopLossPips.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public TwoMaFourLevelStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Period of the fast smoothed MA", "Moving Averages")
			.SetOptimize(20, 150, 5);

		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Period of the slow smoothed MA", "Moving Averages")
			.SetOptimize(60, 300, 5);

		_mostTopLevel = Param(nameof(MostTopLevel), 2)
			.SetGreaterThanZero()
			.SetDisplay("Extreme Upper Level", "Highest positive offset in points", "Levels");

		_topLevel = Param(nameof(TopLevel), 1)
			.SetGreaterThanZero()
			.SetDisplay("Upper Level", "Second positive offset in points", "Levels");

		_lowerLevel = Param(nameof(LowerLevel), 1)
			.SetGreaterThanZero()
			.SetDisplay("Lower Level", "Second negative offset in points", "Levels");

		_lowermostLevel = Param(nameof(LowermostLevel), 2)
			.SetGreaterThanZero()
			.SetDisplay("Extreme Lower Level", "Largest negative offset in points", "Levels");

		_takeProfitPips = Param(nameof(TakeProfitPips), 500)
			.SetGreaterThanZero()
			.SetDisplay("Take Profit (pips)", "Distance to take profit", "Risk");

		_stopLossPips = Param(nameof(StopLossPips), 1000)
			.SetGreaterThanZero()
			.SetDisplay("Stop Loss (pips)", "Distance to stop loss", "Risk");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
			.SetDisplay("Candle Type", "Time frame for analysis", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		if (Security != null)
			yield return (Security, CandleType);
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = null;
		_prevSlow = null;
	}

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

		if (FastPeriod >= SlowPeriod)
		{
			this.LogError("FastPeriod must be less than SlowPeriod.");
			Stop();
			return;
		}

		if (MostTopLevel <= TopLevel)
		{
			this.LogError("MostTopLevel must be greater than TopLevel.");
			Stop();
			return;
		}

		if (LowerLevel >= LowermostLevel)
		{
			this.LogError("LowerLevel must be less than LowermostLevel.");
			Stop();
			return;
		}

		_fastMa = new SmoothedMovingAverage { Length = FastPeriod };
		_slowMa = new SmoothedMovingAverage { Length = SlowPeriod };

		var pip = Security?.PriceStep ?? 1m;

		StartProtection(
			takeProfit: new Unit(TakeProfitPips * pip, UnitTypes.Absolute),
			stopLoss: new Unit(StopLossPips * pip, UnitTypes.Absolute));

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

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

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

		if (_prevFast is null || _prevSlow is null)
		{
			_prevFast = fast;
			_prevSlow = slow;
			return;
		}

		var pip = Security?.PriceStep ?? 0.05m;
		var signal = GetSignal(fast, slow, _prevFast.Value, _prevSlow.Value, pip);

		if (signal > 0)
		{
			if (Position < 0)
				BuyMarket();
			if (Position <= 0)
				BuyMarket();
		}
		else if (signal < 0)
		{
			if (Position > 0)
				SellMarket();
			if (Position >= 0)
				SellMarket();
		}

		_prevFast = fast;
		_prevSlow = slow;
	}

	private int GetSignal(decimal fast, decimal slow, decimal prevFast, decimal prevSlow, decimal pip)
	{
		if (IsCrossUp(prevFast, fast, prevSlow, slow, 0m) ||
			IsCrossUp(prevFast, fast, prevSlow, slow, MostTopLevel * pip) ||
			IsCrossUp(prevFast, fast, prevSlow, slow, TopLevel * pip) ||
			IsCrossUp(prevFast, fast, prevSlow, slow, -LowermostLevel * pip) ||
			IsCrossUp(prevFast, fast, prevSlow, slow, -LowerLevel * pip))
		{
			return 1;
		}

		if (IsCrossDown(prevFast, fast, prevSlow, slow, 0m) ||
			IsCrossDown(prevFast, fast, prevSlow, slow, MostTopLevel * pip) ||
			IsCrossDown(prevFast, fast, prevSlow, slow, TopLevel * pip) ||
			IsCrossDown(prevFast, fast, prevSlow, slow, -LowermostLevel * pip) ||
			IsCrossDown(prevFast, fast, prevSlow, slow, -LowerLevel * pip))
		{
			return -1;
		}

		return 0;
	}

	private static bool IsCrossUp(decimal prevFast, decimal fast, decimal prevSlow, decimal slow, decimal offset)
	{
		var prevSlowShifted = prevSlow + offset;
		var slowShifted = slow + offset;
		return prevFast <= prevSlowShifted && fast > slowShifted;
	}

	private static bool IsCrossDown(decimal prevFast, decimal fast, decimal prevSlow, decimal slow, decimal offset)
	{
		var prevSlowShifted = prevSlow + offset;
		var slowShifted = slow + offset;
		return prevFast >= prevSlowShifted && fast < slowShifted;
	}
}