Auf GitHub ansehen

Währungsstärke v1.1-Strategie

Überblick

Die Währungsstärke v1.1-Strategie repliziert den MetaTrader Expert Advisor Currency Strength v1.1. Sie misst die relative Stärke der acht wichtigsten Währungen (USD, EUR, JPY, CAD, AUD, NZD, GBP, CHF) anhand täglicher prozentualer Veränderungen für 26 liquide FX-Paare. Wenn die Stärke zweier Währungen einen konfigurierbaren Schwellenwert überschreitet, öffnet die Strategie eine Position im entsprechenden Währungspaar in Richtung der stärkeren Währung.

Markt und Daten

  • Instrumentuniversum: 26 wichtige und Cross-FX-Paare (USDJPY, USDCAD, AUDUSD, USDCHF, GBPUSD, EURUSD, NZDUSD, EURJPY, EURCAD, EURGBP, EURCHF, EURAUD, EURNZD, AUDNZD, AUDCAD, AUDCHF, AUDJPY, CHFJPY, GBPCHF, GBPAUD, GBPCAD, GBPJPY, CADJPY, NZDJPY, GBPNZD, CADCHF).
  • Datenhäufigkeit: Tageskerzen (D1). Nur abgeschlossene Kerzen werden verarbeitet, um konsistente Berechnungen aufrechtzuerhalten.
  • Erforderliche Felder: Eröffnungs-, Hoch-, Tief- und Schlusspreise jeder Kerze.

Berechnung der Währungsstärke

Die tägliche prozentuale Veränderung für jedes Paar wird berechnet als:

(change) = (Close − Open) / Open × 100

Diese paarbezogenen Veränderungen werden dann in Währungsstärkeindizes kombiniert:

  • EUR-Stärke = Durchschnitt von EURJPY, EURCAD, EURGBP, EURCHF, EURAUD, EURUSD, EURNZD
  • USD-Stärke = Durchschnitt von USDJPY, USDCAD, –AUDUSD, USDCHF, –GBPUSD, –EURUSD, –NZDUSD
  • JPY-Stärke = negativer Durchschnitt von USDJPY, EURJPY, AUDJPY, CHFJPY, GBPJPY, CADJPY, NZDJPY
  • CAD-Stärke = Durchschnitt von CADCHF, CADJPY, –GBPCAD, –AUDCAD, –EURCAD, –USDCAD
  • AUD-Stärke = Durchschnitt von AUDUSD, AUDNZD, AUDCAD, AUDCHF, AUDJPY, –EURAUD, –GBPAUD
  • NZD-Stärke = Durchschnitt von NZDUSD, NZDJPY, –EURNZD, –AUDNZD, –GBPNZD
  • GBP-Stärke = Durchschnitt von GBPUSD, –EURGBP, GBPCHF, GBPAUD, GBPCAD, GBPJPY, GBPNZD
  • CHF-Stärke = Durchschnitt von CHFJPY, –USDCHF, –EURCHF, –AUDCHF, –GBPCHF, –CADCHF

Jeder Durchschnitt verwendet dieselbe Anzahl von Komponenten wie im ursprünglichen Expert Advisor, um das Gewichtungsschema zu erhalten.

Handelslogik

  1. Nachdem alle 26 Paare eine neue abgeschlossene Tageskerze melden, werden die Stärken neu berechnet.
  2. Für jedes Paar vergleicht die Strategie die zwei relevanten Währungsstärken. Wenn die absolute Differenz den Parameter DifferenceThreshold überschreitet, wird ein Handelssignal generiert.
  3. Die Signalrichtung folgt der stärkeren Währung:
    • Wenn Basiswährungsstärke > Notierungswährungsstärke → Paar kaufen.
    • Wenn Basiswährungsstärke < Notierungswährungsstärke → Paar verkaufen.
  4. Trades sind nur erlaubt, wenn die Tageskerze des Paares mit dem Signal übereinstimmt (Schluss über Eröffnung für Käufe, Schluss unter Eröffnung für Verkäufe), was den Trendfilter des ursprünglichen EA widerspiegelt.
  5. Bestehende Nettopositionen werden respektiert. Wenn ein Umkehrsignal erscheint, während eine entgegengesetzte Position offen ist, schließt die Strategie die aktuelle Position und dreht in die neue Richtung mit einer einzigen Marktorder.
  6. Wenn TradeOncePerDay aktiviert ist, kann jedes Paar maximal einmal pro Handelstag Long einsteigen und maximal einmal pro Handelstag Short einsteigen.

Risikomanagement und Ausstiege

  • Die optionale UseSlTp-Flag aktiviert Stop-Loss- und Take-Profit-Logik, die auf der Tageskerze jedes Paares ausgeführt wird. Die Abstände werden in Pips definiert (StopLossPips, TakeProfitPips).
  • Die Schutzlogik wertet das Tageshoch/-tief der jüngsten Kerze aus. Wenn diese Extreme die jeweiligen Ziele erreichen, wird die Position beim nächsten Auswertungsschritt zum Marktpreis geschlossen.
  • Ohne SL/TP bleiben Positionen offen, bis ein entgegengesetztes Signal eine Umkehrung erzwingt oder die Strategie manuell gestoppt wird, was das Verhalten des Quell-EA widerspiegelt.

Strategieparameter

Parameter Beschreibung
CandleType Zeitrahmen für Kerzen (Standard: täglich).
DifferenceThreshold Minimale Stärkelücke (in Prozentpunkten), die erforderlich ist, um einen Trade auszulösen.
TradeOncePerDay Wenn true, begrenzt jedes Paar auf einen Long- und einen Short-Einstieg pro Tag.
UseSlTp Aktiviert die tägliche Auswertung von Stop-Loss- und Take-Profit-Niveaus.
TakeProfitPips Take-Profit-Abstand in Pips gemessen.
StopLossPips Stop-Loss-Abstand in Pips gemessen.
Paar-Parameter Individuelle Security-Eingaben für die 26 FX-Paare. Jedes muss vor dem Starten der Strategie zugewiesen werden.
Volume Basisklasseneigenschaft, die die Handelsgröße definiert (Standard 0.01 Lot).

Implementierungshinweise

  • Die Strategie abonniert jedes Paar separat über die High-Level-Kerzenabonnement-API (SubscribeCandles).
  • Die Kerzenbehandlung ignoriert strikt unvollständige Kerzen und erfüllt die StockSharp-Konvertierungsrichtlinien.
  • Stärkeberechnungen und Signalgenerierung laufen nur, wenn alle Paare dasselbe Handelsdatum melden, was synchronisierte Währungskörbe garantiert.
  • Interne Wörterbücher verfolgen die letzten Handelsdaten pro Richtung und speichern Einstiegsinformationen für Schutzausstiege.

Verwendungstipps

  1. Alle 26 Wertpapiere vor dem Starten der Strategie zuweisen; fehlende Eingaben werfen eine Ausnahme, um Teilberechnungen zu verhindern.
  2. Sicherstellen, dass der Datenprovider Tageskerzen für jedes konfigurierte Paar liefert, damit die Währungsstärken synchron bleiben.
  3. DifferenceThreshold anpassen, um die Signalhäufigkeit zu kontrollieren. Kleinere Schwellenwerte führen zu häufigeren Trades, aber auch mehr Umkehrungen.
  4. Die pip-basierten Stops an die Quotierungsgenauigkeit des eigenen Brokers kalibrieren; der Standard geht von Bruchpip-Preisen aus.
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>
/// Strategy that trades based on percentage change momentum of candles.
/// Simplified from the original multi-pair currency strength approach to single-security.
/// </summary>
public class CurrencyStrengthV11Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<decimal> _differenceThreshold;

	private decimal? _prevChange;
	private decimal? _prevMomentum;
	private decimal _entryPrice;
	private DateTimeOffset? _lastTradeTime;

	/// <summary>
	/// Candle type for analysis.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Minimum percentage change to trigger trades.
	/// </summary>
	public decimal DifferenceThreshold
	{
		get => _differenceThreshold.Value;
		set => _differenceThreshold.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the strategy.
	/// </summary>
	public CurrencyStrengthV11Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for strength calculation", "General");

		_differenceThreshold = Param(nameof(DifferenceThreshold), 0.2m)
			.SetDisplay("Threshold", "Minimum percentage change to trigger trade", "Parameters")
			.SetOptimize(0.05m, 1m, 0.05m);
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, CandleType);
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevChange = null;
		_prevMomentum = null;
		_entryPrice = 0m;
		_lastTradeTime = null;
	}

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

		SubscribeCandles(CandleType)
			.Bind(ProcessCandle)
			.Start();
	}

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

		if (!IsFormed)
			return;

		var change = candle.OpenPrice != 0m
			? (candle.ClosePrice - candle.OpenPrice) / candle.OpenPrice * 100m
			: 0m;

		if (_prevChange == null)
		{
			_prevChange = change;
			return;
		}

		var momentum = change - _prevChange.Value;
		var cooldownPassed = _lastTradeTime is null || candle.CloseTime - _lastTradeTime >= TimeSpan.FromHours(24);
		var longSignal = _prevMomentum is decimal prevMomentum && prevMomentum <= DifferenceThreshold && momentum > DifferenceThreshold;
		var shortSignal = _prevMomentum is decimal prevMomentum2 && prevMomentum2 >= -DifferenceThreshold && momentum < -DifferenceThreshold;

		if (cooldownPassed && longSignal && Position <= 0)
		{
			if (Position < 0)
				BuyMarket(Math.Abs(Position));
			BuyMarket(Volume > 0m ? Volume : 1m);
			_entryPrice = candle.ClosePrice;
			_lastTradeTime = candle.CloseTime;
		}
		else if (cooldownPassed && shortSignal && Position >= 0)
		{
			if (Position > 0)
				SellMarket(Position);
			SellMarket(Volume > 0m ? Volume : 1m);
			_entryPrice = candle.ClosePrice;
			_lastTradeTime = candle.CloseTime;
		}

		_prevChange = change;
		_prevMomentum = momentum;
	}
}