Ver en GitHub

Estrategia de impulso e-TurboFx

Descripción general

La e-TurboFx Momentum Strategy es una adaptación directa del MetaTrader 4 asesor experto original "e-TurboFx". El sistema escanea las velas terminadas más recientes y busca tramos direccionales donde los cuerpos de las velas continúan expandiéndose. Las velas bajistas consecutivas con un tamaño de cuerpo creciente indican una capitulación potencial que puede desvanecerse con una entrada larga, mientras que las velas alcistas consecutivas con cuerpos en expansión insinúan un repunte demasiado extendido que puede venderse en corto. La implementación StockSharp mantiene la lógica impulsada por eventos a través de suscripciones de velas y adjunta automáticamente protección opcional de stop-loss y take-profit.

Lógica comercial

  1. Suscríbase a un tipo de vela configurable (período de tiempo) y procese solo velas terminadas.
  2. Realice un seguimiento de dos secuencias separadas: una para velas bajistas y otra para velas alcistas.
  3. Para cada vela, mida el tamaño absoluto del cuerpo (|Close - Open|).
  4. Restablezca la secuencia de dirección opuesta tan pronto como una vela se cierre en la otra dirección.
  5. Dentro de cada secuencia se requieren cuerpos estrictamente en expansión: cada nueva vela debe tener un cuerpo más grande que la anterior. Cualquier contracción reinicia el contador de secuencia desde 1.
  6. Cuando el número de velas en una secuencia alcanza DepthAnalysis, activa una entrada al mercado en la dirección opuesta a la última secuencia (comprar después de rachas bajistas, vender después de rachas alcistas).
  7. Una vez que una posición esté abierta, pausar la detección de señales hasta que la estrategia regrese a una posición plana. El StartProtection integrado gestiona distancias opcionales de stop-loss y take-profit expresadas en pasos de precio (ticks).

Este comportamiento reproduce el algoritmo MQL4 en el que el asesor experto comprobó las últimas N velas cerradas y confirmó que todos los cuerpos estaban alineados en la misma dirección y que cada cuerpo era más grande que el cuerpo de la siguiente vela más antigua.

Detalles de implementación

  • Utiliza la suscripción de vela de alto nivel API con SubscribeCandles y Bind para cumplir con las pautas del proyecto.
  • Mantiene solo campos escalares (_bearishSequence, _bullishSequence, _previousBearishBody, _previousBullishBody) para evitar colecciones personalizadas y depender del estado interno entre eventos.
  • Llama a StartProtection solo una vez en OnStarted para configurar órdenes opcionales de stop-loss y take-profit en pasos de precio. Un valor de 0 desactiva cada orden de protección al igual que el experto original.
  • Proporciona extensos comentarios en inglés en el código fuente, incluidas explicaciones para restablecimientos y activadores de entrada.
  • Dibuja velas y operaciones propias en un área del gráfico cuando se ejecuta dentro de Designer o la interfaz de usuario para facilitar la depuración.

Parámetros

Parámetro Descripción Predeterminado
DepthAnalysis Número de velas terminadas consecutivas requeridas en una dirección con cuerpos en expansión antes de abrir una operación. 3
TakeProfitSteps Distancia de obtención de beneficios medida en pasos del precio de cambio (ticks). Establezca en 0 para deshabilitar la obtención de ganancias. 120
StopLossSteps Distancia de stop-loss medida en pasos del precio de cambio (ticks). Establezca en 0 para desactivar el stop loss. 70
TradeVolume Volumen enviado con cada orden de mercado. Cambiar este parámetro también actualiza la base Strategy.Volume. 0.1
CandleType Tipo de datos de vela (período de tiempo) suscrito para el análisis. 1 hour

Todos los parámetros numéricos exponen metadatos de optimización para que la estrategia se pueda ajustar en optimizadores StockSharp si se desea.

Notas y recomendaciones

  • Debido a que la estrategia reacciona a la expansión del cuerpo de la vela, el período de tiempo elegido afecta significativamente la frecuencia de la señal. Intervalos más cortos producen más intercambios, pero pueden requerir distancias de protección más estrechas.
  • Asegúrese de que la seguridad conectada defina un PriceStep válido; de lo contrario, las distancias de protección escalonadas no se pueden convertir a precios absolutos.
  • Realice una prueba retrospectiva del puerto dentro del diseñador StockSharp antes de la implementación en vivo para validar cómo se traducen la parada y el destino para el instrumento seleccionado.
  • La estrategia mantiene una única posición abierta a la vez. Después de cada salida, los contadores se reinician y el patrón debe reconstruirse desde cero, reflejando el comportamiento original de MQL4.
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>
/// Momentum reversal strategy that tracks consecutive candles with expanding bodies.
/// </summary>
public class ETurboFxMomentumStrategy : Strategy
{
	private readonly StrategyParam<int> _depthAnalysis;
	private readonly StrategyParam<decimal> _takeProfitSteps;
	private readonly StrategyParam<decimal> _stopLossSteps;
	private readonly StrategyParam<decimal> _tradeVolume;
	private readonly StrategyParam<DataType> _candleType;

	private int _bearishSequence;
	private int _bullishSequence;
	private decimal _previousBearishBody;
	private decimal _previousBullishBody;

	/// <summary>
	/// Number of recent candles analysed for momentum confirmation.
	/// </summary>
	public int DepthAnalysis
	{
		get => _depthAnalysis.Value;
		set => _depthAnalysis.Value = value;
	}

	/// <summary>
	/// Take profit distance measured in price steps (ticks).
	/// A value of zero disables the take profit order.
	/// </summary>
	public decimal TakeProfitSteps
	{
		get => _takeProfitSteps.Value;
		set => _takeProfitSteps.Value = value;
	}

	/// <summary>
	/// Stop loss distance measured in price steps (ticks).
	/// A value of zero disables the protective stop.
	/// </summary>
	public decimal StopLossSteps
	{
		get => _stopLossSteps.Value;
		set => _stopLossSteps.Value = value;
	}

	/// <summary>
	/// Volume used when sending market orders.
	/// </summary>
	public decimal TradeVolume
	{
		get => _tradeVolume.Value;
		set
		{
			_tradeVolume.Value = value;
			Volume = value;
		}
	}

	/// <summary>
	/// Candle type analysed by the strategy.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="ETurboFxMomentumStrategy" /> class.
	/// </summary>
	public ETurboFxMomentumStrategy()
	{
		_depthAnalysis = Param(nameof(DepthAnalysis), 3)
			.SetGreaterThanZero()
			.SetDisplay("Depth Analysis", "Number of finished candles used for pattern detection", "Trading Rules")
			
			.SetOptimize(2, 6, 1);

		_takeProfitSteps = Param(nameof(TakeProfitSteps), 120m)
			.SetNotNegative()
			.SetDisplay("Take Profit (steps)", "Take profit distance in price steps (ticks)", "Risk Management")
			
			.SetOptimize(60m, 180m, 20m);

		_stopLossSteps = Param(nameof(StopLossSteps), 70m)
			.SetNotNegative()
			.SetDisplay("Stop Loss (steps)", "Stop loss distance in price steps (ticks)", "Risk Management")
			
			.SetOptimize(40m, 120m, 10m);

		_tradeVolume = Param(nameof(TradeVolume), 0.1m)
			.SetGreaterThanZero()
			.SetDisplay("Trade Volume", "Order volume used for entries", "Trading Rules")
			
			.SetOptimize(0.1m, 0.5m, 0.1m);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe of the candles analysed by the strategy", "Market Data");
	}

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

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

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

		ResetState();
		Volume = TradeVolume;

		var takeProfitUnit = CreateStepUnit(TakeProfitSteps);
		var stopLossUnit = CreateStepUnit(StopLossSteps);

		if (takeProfitUnit != null || stopLossUnit != null)
		{
			// Configure protective orders once the strategy starts.
			StartProtection(
				takeProfit: takeProfitUnit,
				stopLoss: stopLossUnit,
				isStopTrailing: false,
				useMarketOrders: true);
		}

		var subscription = SubscribeCandles(CandleType);

		subscription
			.Bind(ProcessCandle)
			.Start();

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

	private Unit CreateStepUnit(decimal steps)
	{
		if (steps <= 0)
			return null;

		// Convert the user-friendly tick distance into a StockSharp Unit instance.
		return new Unit(steps, UnitTypes.Absolute);
	}

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

		// indicators formed check removed

		if (Position != 0)
		{
			// Do not look for new signals while a position is active.
			ResetState();
			return;
		}

		var bodySize = Math.Abs(candle.ClosePrice - candle.OpenPrice);

		// The original expert compared absolute bodies of the latest N closed candles.
		// Measuring the body here reproduces that behaviour candle by candle.

		if (candle.ClosePrice < candle.OpenPrice)
		{
			HandleBearishCandle(bodySize);
		}
		else if (candle.ClosePrice > candle.OpenPrice)
		{
			HandleBullishCandle(bodySize);
		}
		else
		{
			// Neutral candle breaks both sequences.
			ResetState();
		}
	}

	private void HandleBearishCandle(decimal bodySize)
	{
		// Bearish candles reset the bullish path and allow the downside streak to continue.
		ResetBullishSequence();

		if (bodySize <= 0)
		{
			ResetBearishSequence();
			return;
		}

		if (_bearishSequence == 0 || bodySize > _previousBearishBody)
		{
			// Body is larger than the previous bearish candle, extend the sequence.
			_bearishSequence++;
		}
		else
		{
			// Sequence restarts because body did not expand.
			_bearishSequence = 1;
		}

		_previousBearishBody = bodySize;

		if (_bearishSequence >= DepthAnalysis)
		{
			// Expanding bearish bodies suggest exhaustion that can trigger a long entry.
			BuyMarket();
			ResetBearishSequence();
		}
	}

	private void HandleBullishCandle(decimal bodySize)
	{
		// Bullish candles reset the bearish path and allow the upside streak to continue.
		ResetBearishSequence();

		if (bodySize <= 0)
		{
			ResetBullishSequence();
			return;
		}

		if (_bullishSequence == 0 || bodySize > _previousBullishBody)
		{
			// Body is larger than the previous bullish candle, extend the sequence.
			_bullishSequence++;
		}
		else
		{
			// Sequence restarts because body did not expand.
			_bullishSequence = 1;
		}

		_previousBullishBody = bodySize;

		if (_bullishSequence >= DepthAnalysis)
		{
			// Expanding bullish bodies suggest potential reversal to the downside.
			SellMarket();
			ResetBullishSequence();
		}
	}

	private void ResetBearishSequence()
	{
		_bearishSequence = 0;
		_previousBearishBody = 0m;
	}

	private void ResetBullishSequence()
	{
		_bullishSequence = 0;
		_previousBullishBody = 0m;
	}

	private void ResetState()
	{
		ResetBearishSequence();
		ResetBullishSequence();
	}
}