Ver no GitHub

Estratégia ForexProfitBoost

Visão geral

A estratégia ForexProfitBoost é um sistema de trading de reversão que combina uma Média Móvel Exponencial (EMA) rápida e uma Média Móvel Simples (SMA) lenta. A estratégia aguarda o cruzamento da EMA rápida com a SMA lenta e então opera contra a direção do cruzamento, esperando um recuo do preço. Níveis opcionais de stop-loss e take-profit em pontos de preço absolutos podem ser configurados para gestão de risco.

Indicadores

  • EMA (rápida): período padrão de 7.
  • SMA (lenta): período padrão de 21.

Regras de trading

  1. Assinar o período de candles selecionado.
  2. Calcular os valores de EMA e SMA em cada candle finalizado.
  3. Quando a EMA rápida cruza abaixo da SMA lenta:
    • Fechar quaisquer posições vendidas.
    • Abrir uma nova posição comprada.
  4. Quando a EMA rápida cruza acima da SMA lenta:
    • Fechar quaisquer posições compradas.
    • Abrir uma nova posição vendida.
  5. Aplicar níveis de stop-loss e take-profit relativos ao preço de entrada, se especificados.

Parâmetros

Nome Descrição Padrão
FastPeriod Período para a EMA rápida. 7
SlowPeriod Período para a SMA lenta. 21
StopLoss Distância do stop-loss em pontos de preço. 1000
TakeProfit Distância do take-profit em pontos de preço. 2000
CandleType Período utilizado para os cálculos. 1 hora

Notas

  • A estratégia usa a API de alto nível do StockSharp e não armazena coleções históricas.
  • As operações são executadas apenas com ordens a mercado após a finalização de um candle.
  • Todos os comentários no código fonte estão escritos em inglês, conforme exigido.
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;
			}
		}
	}
}