Auf GitHub ansehen

Universalsignal-Demo

Diese Strategie repliziert den MetaTrader 5 „Universal Signal“-Experten unter Verwendung von StockSharp High-Level-APIs. Es wertet acht gewichtete Marktmuster aus und fasst sie zu einem einzigen zusammengesetzten Score zusammen. Wenn der Score konfigurierbare Schwellenwerte überschreitet, öffnet oder schließt die Strategie Long- und Short-Positionen, optional unter Verwendung ausstehender Limit-Orders, die nach einer festgelegten Anzahl von Balken ablaufen.

Strategieparameter

  • CandleType – Kerzendaten, die für die Analyse verwendet werden.
  • SignalThresholdOpen – minimale Gesamtpunktzahl, die zum Öffnen einer Position erforderlich ist.
  • SignalThresholdClose – Gegenpunktzahl erforderlich, um eine bestehende Position zu verlassen.
  • PriceLevel – Preisversatz für die Platzierung ausstehender Limiteinträge (0 bedeutet Marktausführung).
  • StopLevel / TakeLevel – absolute Stop-Loss- und Take-Profit-Distanzen, die vom eingebauten Schutzmodul verwendet werden.
  • SignalExpiration – Anzahl der Balken, nach denen noch aktive ausstehende Einträge storniert werden.
  • Pattern0WeightPattern7Weight – Gewichtung, die vor der Aggregation auf jedes Muster angewendet wird.
  • UniversalWeight – endgültiger Multiplikator, der auf die Summe aller Musterbeiträge angewendet wird.
  • ShortMaPeriod, LongMaPeriod, RsiPeriod, BollingerPeriod, BollingerWidth, TrendSmaPeriod, VolumeSmaPeriod – Indikatoreinstellungen, die in den Musterprüfungen verwendet werden.

Handelslogik

  1. Abonnieren Sie den konfigurierten Kerzenstrom und binden Sie EMA, RSI, MACD Signal, Bollinger Bänder und unterstützende SMAs.
  2. Berechnen Sie nach jeder fertigen Kerze acht boolesche Muster (Trendausrichtung, RSI-Momentum, MACD-Histogramm, Bollinger-Positionierung, Kerzenrichtung und Volumenexpansion).
  3. Multiplizieren Sie jedes Muster mit seiner Gewichtung, summieren Sie die Beiträge und wenden Sie die globale Gewichtung an, um das Endergebnis zu erhalten.
  4. Schließen Sie offene Positionen, wenn der Score die Schließschwelle in die entgegengesetzte Richtung überschreitet.
  5. Eröffnen Sie neue Long- oder Short-Positionen, wenn der Score den Eröffnungsschwellenwert überschreitet. Wenn PriceLevel positiv ist, senden Sie eine um die konfigurierte Distanz versetzte Limit-Order und stornieren Sie diese automatisch nach SignalExpiration Balken.
  6. StartProtection legt mithilfe der Risikomanagement-Helfer von StockSharp feste Stop-Loss- und Take-Profit-Levels für alle Positionen fest.

Bei der Konvertierung bleibt der flexible Gewichtungsworkflow des ursprünglichen MQL5-Experten erhalten, während die StockSharp-Codierungskonventionen und die indikatorbasierte Verarbeitung befolgt werden.

namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Universal Signal Demo strategy: Multi-indicator scoring.
/// Combines RSI and EMA signals to generate aggregate score for entries.
/// </summary>
public class UniversalSignalDemoStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _signalCooldownCandles;
	private int _prevScore;
	private int _candlesSinceTrade;
	private bool _hasPrevScore;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public UniversalSignalDemoStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_rsiPeriod = Param(nameof(RsiPeriod), 21)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI period", "Indicators");
		_emaPeriod = Param(nameof(EmaPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA period", "Indicators");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevScore = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		_hasPrevScore = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevScore = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		_hasPrevScore = false;
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(rsi, ema, ProcessCandle).Start();
	}

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

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		var close = candle.ClosePrice;
		var score = 0;

		// RSI signal
		if (rsiValue < 30) score += 2;
		else if (rsiValue < 45) score += 1;
		else if (rsiValue > 70) score -= 2;
		else if (rsiValue > 55) score -= 1;

		// EMA signal
		if (close > emaValue) score += 1;
		else if (close < emaValue) score -= 1;

		// Candle direction
		if (candle.ClosePrice > candle.OpenPrice) score += 1;
		else if (candle.ClosePrice < candle.OpenPrice) score -= 1;

		var crossedUp = (!_hasPrevScore || _prevScore < 2) && score >= 2;
		var crossedDown = (!_hasPrevScore || _prevScore > -2) && score <= -2;

		if (crossedUp && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
		{
			BuyMarket();
			_candlesSinceTrade = 0;
		}
		else if (crossedDown && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
		{
			SellMarket();
			_candlesSinceTrade = 0;
		}

		_prevScore = score;
		_hasPrevScore = true;
	}
}