Ver en GitHub

Estrategia N Candles v3

Descripción general

Esta estrategia escanea las últimas velas terminadas y busca una secuencia donde las últimas N barras comparten la misma dirección (todas alcistas o todas bajistas). Cuando aparece tal racha, entra en la dirección de la secuencia mientras respeta un límite sobre cuántas posiciones se pueden abrir a la vez. La implementación migra el asesor experto original de MetaTrader 5 a la API de alto nivel de StockSharp.

Lógica de trading

  • El motor se suscribe al tipo de vela configurado y procesa solo las barras completadas.
  • Para cada vela terminada se evalúa la dirección del cuerpo: alcista, bajista o neutral (doji).
  • Las velas doji reinician el contador interno. De lo contrario, el contador aumenta cuando la vela actual tiene la misma dirección que las anteriores. Una vez que el contador alcanza el parámetro Identical Candles, la estrategia emite una nueva orden.
  • Las señales largas cierran primero cualquier exposición corta existente y luego añaden una unidad larga mientras el volumen comprado total se mantenga por debajo de Max Positions * Volume.
  • Las señales cortas funcionan simétricamente para las rachas bajistas.

Gestión de riesgo

  • Después de cada operación ejecutada, la estrategia coloca nuevas órdenes de stop-loss y take-profit de protección basadas en el precio de entrada promedio de la posición activa.
  • Las distancias se miden en pasos de precio del instrumento: Take Profit Points multiplica el paso para calcular el objetivo por encima (largo) o por debajo (corto) de la entrada; Stop Loss Points usa la misma idea para el stop de protección.
  • Un trailing stop escalonado puede reemplazar el stop inicial una vez que el precio se mueva Trailing Stop Points a favor de la posición. El stop se mueve solo cuando el precio ha avanzado al menos Trailing Step Points más allá del nivel de trailing anterior.

Parámetros

  • Candle Type – Marco temporal o fuente de velas a analizar.
  • Identical Candles – Número requerido de velas consecutivas con la misma dirección para disparar una entrada.
  • Volume – Tamaño de la orden para cada nueva entrada en unidades del instrumento.
  • Max Positions – Número máximo de unidades de entrada que pueden estar abiertas en la misma dirección simultáneamente.
  • Take Profit Points – Distancia del take-profit en múltiplos del paso de precio del instrumento.
  • Stop Loss Points – Distancia del stop-loss en múltiplos del paso de precio del instrumento.
  • Trailing Stop Points – Distancia desde el precio actual usada para activar y mantener el trailing stop. Establecer en cero para deshabilitar el trailing.
  • Trailing Step Points – Distancia extra en pasos de precio que debe cubrirse antes de mover nuevamente el trailing stop.

Notas adicionales

  • La estrategia opera de manera neteada: cuando aparece una señal en dirección opuesta, cualquier exposición existente del otro lado se cierra antes de añadir una nueva posición.
  • Todas las órdenes protectoras se recrean después de cada ejecución para mantener su volumen sincronizado con el tamaño de la posición abierta.
  • Asegurarse de que el instrumento proporcione un PriceStep distinto de cero; de lo contrario, se usa el valor de paso predeterminado de 1.
using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy that opens positions after detecting a sequence of candles with the same direction.
/// Uses StartProtection for take profit and stop loss management.
/// </summary>
public class NCandlesV3Strategy : Strategy
{
	private readonly StrategyParam<int> _identicalCandles;
	private readonly StrategyParam<decimal> _takeProfitPoints;
	private readonly StrategyParam<decimal> _stopLossPoints;
	private readonly StrategyParam<DataType> _candleType;

	private int _sequenceDirection;
	private int _sequenceCount;

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

	/// <summary>
	/// Number of consecutive candles required before entering a trade.
	/// </summary>
	public int IdenticalCandles
	{
		get => _identicalCandles.Value;
		set => _identicalCandles.Value = value;
	}

	/// <summary>
	/// Take profit distance expressed in price steps.
	/// </summary>
	public decimal TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Stop loss distance expressed in price steps.
	/// </summary>
	public decimal StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the strategy.
	/// </summary>
	public NCandlesV3Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles analysed by the strategy", "General");

		_identicalCandles = Param(nameof(IdenticalCandles), 3)
			.SetRange(1, 10)
			.SetDisplay("Identical Candles", "Required number of equal candles", "Pattern");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 50m)
			.SetRange(0m, 500m)
			.SetDisplay("Take Profit Points", "Take profit distance in price steps", "Risk Management");

		_stopLossPoints = Param(nameof(StopLossPoints), 50m)
			.SetRange(0m, 500m)
			.SetDisplay("Stop Loss Points", "Stop loss distance in price steps", "Risk Management");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_sequenceDirection = 0;
		_sequenceCount = 0;
	}

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

		_sequenceDirection = 0;
		_sequenceCount = 0;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ProcessCandle)
			.Start();

		var step = Security?.PriceStep ?? 1m;
		var takeProfit = TakeProfitPoints > 0 ? TakeProfitPoints * step : (decimal?)null;
		var stopLoss = StopLossPoints > 0 ? StopLossPoints * step : (decimal?)null;

		if (takeProfit != null || stopLoss != null)
			StartProtection(takeProfit ?? 0, stopLoss ?? 0);

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

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

		var direction = GetDirection(candle);
		UpdateSequence(direction);

		if (_sequenceCount < IdenticalCandles)
			return;

		if (_sequenceDirection > 0 && Position <= 0)
		{
			BuyMarket();
		}
		else if (_sequenceDirection < 0 && Position >= 0)
		{
			SellMarket();
		}
	}

	private static int GetDirection(ICandleMessage candle)
	{
		if (candle.ClosePrice > candle.OpenPrice)
			return 1;
		if (candle.ClosePrice < candle.OpenPrice)
			return -1;
		return 0;
	}

	private void UpdateSequence(int direction)
	{
		if (direction == 0)
		{
			_sequenceDirection = 0;
			_sequenceCount = 0;
			return;
		}

		if (_sequenceDirection == direction)
		{
			_sequenceCount++;
		}
		else
		{
			_sequenceDirection = direction;
			_sequenceCount = 1;
		}
	}
}