Auf GitHub ansehen

GlamTrader-Strategie

Die GlamTrader-Strategie ist eine StockSharp High-Level-API-Konvertierung des MetaTrader-Expertenberaters GlamTrader.mq5. Der ursprüngliche Roboter verbindet einen verschobenen gleitenden Durchschnitt mit dem Laguerre-RSI-Oszillator und dem Awesome Oscillator, um den Momentum zu filtern, bevor eine einzige Marktposition eröffnet wird. Der Port bewahrt den exakten Entscheidungsbaum und die Geldmanagement-Regeln, während Orderausführung, Charting und Risikokontrollen an StockSharp-Konventionen angepasst werden.

Funktionsweise der Strategie

  1. Die durch CandleType definierte Kerzenserie abonnieren (standardmäßig M15). Der ausgewählte Zeitrahmen versorgt jeden Indikator.
  2. Einen konfigurierbaren gleitenden Durchschnitt auf der ausgewählten AppliedPrice-Quelle erstellen und ihn um MaShift Bars verschieben, um den in MetaTrader verwendeten verschobenen Puffer zu reproduzieren.
  3. Den Laguerre-RSI-Filter intern mithilfe des vierstufigen rekursiven Filters nachbilden (LaguerreGamma steuert den Glättungsfaktor). Der Wert bleibt im Bereich [0;1] wie der ursprüngliche benutzerdefinierte Indikator.
  4. Den Awesome Oscillator mit Standard-5/34-einfachen Durchschnitten des Median-Preises berechnen und die aktuellen und vorherigen Messwerte für die Steilheitserkennung speichern.
  5. Nur wenn keine Position offen ist:
    • Long-Einstieg – gleitender Durchschnitt über dem aktuellen Schlusskurs, Laguerre RSI über 0.15, und Awesome Oscillator steigend im Vergleich zur vorherigen Bar.
    • Short-Einstieg – gleitender Durchschnitt unter dem aktuellen Schlusskurs, Laguerre RSI unter 0.75, und Awesome Oscillator fallend im Vergleich zur vorherigen Bar.
  6. Beim Einstieg wandelt die Strategie Stop-Loss/Take-Profit-Abstände von Pips in Preisabstände unter Verwendung der Instrument-Tick-Größe um. Abstände werden für 3- oder 5-stellige Kurse genau wie Point * 10 in MQL angepasst.
  7. Während eine Position aktiv ist, spiegelt der Algorithmus die ursprüngliche Trailing-Routine: sobald der Preis mehr als TrailingStopPips + TrailingStepPips voranzieht, wird der Stop auf TrailingStopPips hinter (oder über) dem Markt nachgezogen. Exits werden ausgeführt, wenn der Kerzenrange den Trailing-Stop- oder Take-Profit-Preis berührt.

Einstiegs- und Ausstiegslogik

  • Immer höchstens eine Position halten. Entgegengesetzte Signale werden ignoriert, bis der aktuelle Trade geschlossen ist.
  • Long-Trades erfordern einen bearischen verschobenen gleitenden Durchschnitt (Preis kreuzt über die Linie), Laguerre RSI verlässt die überverkaufte Zone (> 0.15), und zunehmendes Awesome Oscillator-Momentum.
  • Short-Trades erfordern einen bullischen verschobenen gleitenden Durchschnitt (Preis kreuzt unter die Linie), Laguerre RSI fällt aus der überkauften Zone (< 0.75), und abnehmendes Awesome Oscillator-Momentum.
  • Stops und Ziele werden durch Preisvergleiche gegen Kerzenhochs/-tiefs durchgesetzt, so dass Intrabar-Treffer respektiert werden, obwohl die Logik auf abgeschlossenen Kerzen läuft.
  • Trailing folgt der MetaTrader-Regel: der Stop bewegt sich nur, nachdem der Preis um den Stop-Abstand plus den Trailing-Schritt voranzieht, und kehrt nie zurück.

Parameter

Parameter Typ Standard Beschreibung
CandleType DataType TimeSpan.FromMinutes(15).TimeFrame() Zeitrahmen für Indikatorberechnungen und Entscheidungsfindung.
TradeVolume decimal 1 Volumen für Marktorders.
StopLossBuyPips decimal 50 Stop-Loss-Abstand in Pips für Long-Einstiege.
TakeProfitBuyPips decimal 50 Take-Profit-Abstand in Pips für Long-Einstiege.
StopLossSellPips decimal 50 Stop-Loss-Abstand in Pips für Short-Einstiege.
TakeProfitSellPips decimal 50 Take-Profit-Abstand in Pips für Short-Einstiege.
TrailingStopPips decimal 5 Trailing-Stop-Abstand in Pips. Auf null setzen um Trailing zu deaktivieren.
TrailingStepPips decimal 15 Zusätzlicher Gewinn (in Pips) erforderlich bevor der Trailing-Stop sich bewegen kann.
MaPeriod int 14 Lookback-Länge des gleitenden Durchschnitts.
MaShift int 1 Positive Verschiebung auf den gleitenden Durchschnitt angewendet.
MaMethod MaMethod LinearWeighted Typ des gleitenden Durchschnitts (einfach, exponentiell, geglättet oder linear gewichtet).
AppliedPrice AppliedPrice Weighted Preisquelle für gleitenden Durchschnitt und Laguerre-Filter.
LaguerreGamma decimal 0.7 Laguerre-Glättungskoeffizient (Bereich 0–1).

Verwendungshinweise

  1. Die Strategie an das gewünschte Wertpapier anhängen, sicherstellen, dass das Broker-Modell Tick-Größen-/Schrittinformationen liefert, und CandleType auf den gewünschten Zeitrahmen einstellen.
  2. Pip-basierte Risikoparameter an die Instrumentvolatilität anpassen. Die Konvertierung normalisiert Abstände automatisch mit PriceStep; fünfstellige FX-Symbole erhalten den erwarteten 10×-Multiplikator.
  3. Optionale Chart-Helfer zeichnen den gleitenden Durchschnitt im Preisbereich und tragen den Awesome Oscillator in einem separaten Panel zusammen mit den Trades ein.
  4. Die Strategie starten. Sie verwaltet Stops und Trailing intern und spiegelt die OpenBuy-, OpenSell- und Trailing-Routinen aus dem ursprünglichen MQL-Code.

Hinweise

  • Die Laguerre-RSI-Implementierung spiegelt den laguerre.mq5-Indikator, einschließlich der CU/(CU+CD)-Normalisierung.
  • Awesome Oscillator-Werte kommen aus StockSharp's eingebautem Indikator, daher ist kein manuelles Puffer-Kopieren erforderlich.
  • Da die Logik auf abgeschlossenen Kerzen ausgewertet wird, bleiben Backtests und Live-Trading deterministisch und frei von Tick-Level-Repainting.
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>
/// GlamTrader strategy using EMA crossover with momentum confirmation.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class GlamTraderStrategy : 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;

	/// <summary>
	/// Fast EMA period.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA period.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in price steps.
	/// </summary>
	public int StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Take-profit distance in price steps.
	/// </summary>
	public int TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="GlamTraderStrategy"/> class.
	/// </summary>
	public GlamTraderStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 25)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast EMA period", "Indicator");

		_slowPeriod = Param(nameof(SlowPeriod), 100)
			.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");
	}

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

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

		_fast = null;
		_slow = null;
		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
		_cooldown = 0;
	}

	/// <inheritdoc />
	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;

		// Check SL/TP
		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 80;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

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

			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 80;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}
		}

		// EMA crossover
		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();

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

			SellMarket();
			_entryPrice = close;
			_cooldown = 80;
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}