Auf GitHub ansehen

ForexProfitBoost-Strategie

Überblick

Die ForexProfitBoost-Strategie ist ein reversal-basiertes Handelssystem, das einen schnellen Exponential Moving Average (EMA) und einen langsamen Simple Moving Average (SMA) kombiniert. Die Strategie wartet darauf, dass der schnelle EMA den langsamen SMA kreuzt, und handelt dann entgegen der Kreuzungsrichtung in Erwartung einer Preiskorrektur. Optionale Stop-Loss- und Take-Profit-Niveaus in absoluten Preispunkten können für das Risikomanagement konfiguriert werden.

Indikatoren

  • EMA (schnell): Standardperiode 7.
  • SMA (langsam): Standardperiode 21.

Handelsregeln

  1. Abonnieren des ausgewählten Kerzen-Zeitrahmens.
  2. Berechnung von EMA und SMA auf jeder abgeschlossenen Kerze.
  3. Wenn der schnelle EMA den langsamen SMA nach unten kreuzt:
    • Short-Positionen schließen.
    • Neue Long-Position öffnen.
  4. Wenn der schnelle EMA den langsamen SMA nach oben kreuzt:
    • Long-Positionen schließen.
    • Neue Short-Position öffnen.
  5. Stop-Loss- und Take-Profit-Niveaus relativ zum Einstiegspreis anwenden, sofern angegeben.

Parameter

Name Beschreibung Standard
FastPeriod Periode für den schnellen EMA. 7
SlowPeriod Periode für den langsamen SMA. 21
StopLoss Stop-Loss-Abstand in Preispunkten. 1000
TakeProfit Take-Profit-Abstand in Preispunkten. 2000
CandleType Zeitrahmen für Berechnungen. 1 Stunde

Hinweise

  • Die Strategie verwendet die High-Level StockSharp API und speichert keine historischen Sammlungen.
  • Trades werden ausschließlich mit Marktaufträgen nach Abschluss einer Kerze ausgeführt.
  • Alle Kommentare im Quellcode sind auf Englisch verfasst, wie gefordert.
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>
/// Forex Profit Boost reversal strategy.
/// Opens long when fast EMA crosses below slow SMA.
/// Opens short when fast EMA crosses above slow SMA.
/// Uses optional stop-loss and take-profit in price points.
/// </summary>
public class ForexProfitBoostStrategy : Strategy
{
	private static readonly TimeSpan _signalCooldown = TimeSpan.FromHours(18);

	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<decimal> _stopLoss;
	private readonly StrategyParam<decimal> _takeProfit;
	private readonly StrategyParam<DataType> _candleType;

	private bool? _wasFastAboveSlow;
	private decimal _entryPrice;
	private bool _isLongPosition;
	private DateTime _lastSignalTime;

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

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

	/// <summary>
	/// Stop loss distance in price points.
	/// </summary>
	public decimal StopLoss
	{
		get => _stopLoss.Value;
		set => _stopLoss.Value = value;
	}

	/// <summary>
	/// Take profit distance in price points.
	/// </summary>
	public decimal TakeProfit
	{
		get => _takeProfit.Value;
		set => _takeProfit.Value = value;
	}

	/// <summary>
	/// The type of candles used for strategy calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of <see cref="ForexProfitBoostStrategy"/>.
	/// </summary>
	public ForexProfitBoostStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 7)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA Period", "Period of the fast EMA", "Parameters")
			
			.SetOptimize(5, 15, 1);

		_slowPeriod = Param(nameof(SlowPeriod), 21)
			.SetGreaterThanZero()
			.SetDisplay("Slow SMA Period", "Period of the slow SMA", "Parameters")
			
			.SetOptimize(10, 40, 5);

		_stopLoss = Param(nameof(StopLoss), 1000m)
			.SetDisplay("Stop Loss", "Stop loss distance in price points", "Risk Management")
			
			.SetOptimize(500m, 2000m, 100m);

		_takeProfit = Param(nameof(TakeProfit), 2000m)
			.SetDisplay("Take Profit", "Take profit distance in price points", "Risk Management")
			
			.SetOptimize(1000m, 4000m, 100m);

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");
	}

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

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

		_wasFastAboveSlow = null;
		_entryPrice = 0m;
		_isLongPosition = false;
		_lastSignalTime = default;
	}

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

		var fastEma = new ExponentialMovingAverage { Length = FastPeriod };
		var slowSma = new SimpleMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);

		subscription
			.Bind(fastEma, slowSma, ProcessCandle)
			.Start();

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var isFastAboveSlow = fastValue > slowValue;

		if (_wasFastAboveSlow is null)
		{
			_wasFastAboveSlow = isFastAboveSlow;
			return;
		}

		var isBullishSignal = _wasFastAboveSlow == true && !isFastAboveSlow;
		var isBearishSignal = _wasFastAboveSlow == false && isFastAboveSlow;

		if ((isBullishSignal || isBearishSignal) && _lastSignalTime != default && candle.CloseTime < _lastSignalTime + _signalCooldown)
		{
			_wasFastAboveSlow = isFastAboveSlow;
			CheckRisk(candle.ClosePrice);
			return;
		}

		// Detect crossover and trade against the direction (reversal)
		if (isBullishSignal)
		{
			// Fast EMA crossed below slow SMA -> open long
			if (Position <= 0)
			{
				var volume = Position < 0 ? Math.Abs(Position) + Volume : Volume;

				BuyMarket(volume);
				_entryPrice = candle.ClosePrice;
				_isLongPosition = true;
				_lastSignalTime = candle.CloseTime;
			}
		}
		else if (isBearishSignal)
		{
			// Fast EMA crossed above slow SMA -> open short
			if (Position >= 0)
			{
				var volume = Position > 0 ? Position + Volume : Volume;

				SellMarket(volume);
				_entryPrice = candle.ClosePrice;
				_isLongPosition = false;
				_lastSignalTime = candle.CloseTime;
			}
		}

		// Update crossover state
		_wasFastAboveSlow = isFastAboveSlow;

		// Check stop loss and take profit
		CheckRisk(candle.ClosePrice);
	}

	private void CheckRisk(decimal currentPrice)
	{
		if (Position == 0 || _entryPrice == 0)
			return;

		if (_isLongPosition)
		{
			if (_stopLoss.Value > 0m && currentPrice <= _entryPrice - _stopLoss.Value)
			{
				SellMarket(Position);
				_entryPrice = 0m;
				return;
			}

			if (_takeProfit.Value > 0m && currentPrice >= _entryPrice + _takeProfit.Value)
			{
				SellMarket(Position);
				_entryPrice = 0m;
			}
		}
		else
		{
			if (_stopLoss.Value > 0m && currentPrice >= _entryPrice + _stopLoss.Value)
			{
				BuyMarket(Math.Abs(Position));
				_entryPrice = 0m;
				return;
			}

			if (_takeProfit.Value > 0m && currentPrice <= _entryPrice - _takeProfit.Value)
			{
				BuyMarket(Math.Abs(Position));
				_entryPrice = 0m;
			}
		}
	}
}