Ver no GitHub

Estratégia N Candles v3

Visão geral

Esta estratégia verifica as últimas velas concluídas e procura uma sequência onde as últimas N barras compartilham a mesma direção (todas altistas ou todas baixistas). Quando tal sequência aparece, entra na direção da sequência respeitando um limite sobre quantas posições podem ser abertas simultaneamente. A implementação migra o assessor especializado original do MetaTrader 5 para a API de alto nível do StockSharp.

Lógica de trading

  • O motor se subscreve ao tipo de vela configurado e processa apenas barras concluídas.
  • Para cada vela concluída, a direção do corpo é avaliada: altista, baixista ou neutra (doji).
  • Velas doji reiniciam o contador interno. Caso contrário, o contador aumenta quando a vela atual tem a mesma direção que as anteriores. Assim que o contador atinge o parâmetro Identical Candles, a estratégia emite uma nova ordem.
  • Sinais comprados fecham primeiro qualquer exposição vendida existente e depois adicionam uma unidade comprada enquanto o volume comprado total permanecer abaixo de Max Positions * Volume.
  • Sinais vendidos funcionam simetricamente para sequências baixistas.

Gestão de risco

  • Após cada operação executada, a estratégia coloca novas ordens de stop-loss e take-profit de proteção baseadas no preço médio de entrada da posição ativa.
  • As distâncias são medidas em passos de preço do instrumento: Take Profit Points multiplica o passo para calcular o alvo acima (comprado) ou abaixo (vendido) da entrada; Stop Loss Points usa a mesma ideia para o stop de proteção.
  • Um trailing stop escalonado pode substituir o stop inicial uma vez que o preço se mova Trailing Stop Points em favor da posição. O stop só é movido quando o preço avançou pelo menos Trailing Step Points além do nível de trailing anterior.

Parâmetros

  • Candle Type – Período ou fonte de velas a analisar.
  • Identical Candles – Número necessário de velas consecutivas com a mesma direção para acionar uma entrada.
  • Volume – Tamanho da ordem para cada nova entrada em unidades do instrumento.
  • Max Positions – Número máximo de unidades de entrada que podem estar abertas na mesma direção simultaneamente.
  • Take Profit Points – Distância do take-profit em múltiplos do passo de preço do instrumento.
  • Stop Loss Points – Distância do stop-loss em múltiplos do passo de preço do instrumento.
  • Trailing Stop Points – Distância do preço atual usada para ativar e manter o trailing stop. Definir como zero para desabilitar o trailing.
  • Trailing Step Points – Distância extra em passos de preço que deve ser coberta antes de mover novamente o trailing stop.

Notas adicionais

  • A estratégia opera de forma neteada: quando aparece um sinal na direção oposta, qualquer exposição existente do outro lado é fechada antes de adicionar uma nova posição.
  • Todas as ordens de proteção são recriadas após cada preenchimento para manter seu volume sincronizado com o tamanho da posição aberta.
  • Garantir que o instrumento forneça um PriceStep diferente de zero; caso contrário, o valor de passo padrão de 1 é usado.
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;
		}
	}
}