Ver no GitHub

Estratégia N Candles com Entradas em Sequência

Conceito

A estratégia N Candles escaneia o mercado em busca de velas consecutivas que fecham todas na mesma direção. Uma vez que um número configurável de velas altistas ou baixistas aparecer, a estratégia entra na direção da sequência. A implementação é uma conversão direta do assessor especializado do MetaTrader "N Candles v4" e preserva seus controles de risco, configuração baseada em pips e comportamento opcional de trailing stop dentro da API de alto nível do StockSharp.

Condições de entrada

  • Cada vela concluída é avaliada uma vez.
  • Velas que fecham para cima são contadas como altistas, velas que fecham para baixo como baixistas, e velas doji reiniciam a sequência.
  • Quando ConsecutiveCandles velas altistas (ou baixistas) aparecem em uma linha, a estratégia envia uma ordem de mercado na direção do movimento.
  • Limites de empilhamento estilo hedge ou limites de exposição estilo neteado são aplicados dependendo do AccountingMode selecionado.

Gestão de saídas

  • StopLossPips e TakeProfitPips definem níveis de saída estáticos medidos em pips a partir do preço médio de entrada da posição ativa.
  • Se TrailingStopPips for maior que zero, o nível do stop segue o preço mais favorável:
    • Quando não existe um stop fixo (por exemplo quando StopLossPips é zero), a estratégia aguarda até que o preço se mova TrailingStopPips em favor da operação antes de colocar um stop de equilíbrio.
    • Uma vez que um stop foi definido, ele se move em direção ao mercado quando a distância entre o preço e o stop supera TrailingStopPips + TrailingStepPips.
  • Os níveis de proteção são recalculados sempre que o tamanho da posição muda e são verificados contra cada vela concluída, garantindo que qualquer evento de stop-loss ou take-profit feche a operação imediatamente.

Parâmetros

Nome Descrição Padrão
ConsecutiveCandles Número de velas idênticas necessárias para acionar uma entrada. 3
TakeProfitPips Distância do take-profit em pips. Usar zero para desabilitar o alvo. 50
StopLossPips Distância do stop-loss em pips. Usar zero para desabilitar o stop. 50
TrailingStopPips Distância do trailing stop em pips. Zero desabilita o trailing. 10
TrailingStepPips Movimento adicional necessário antes de o trailing stop avançar. 4
MaxPositionsPerDirection Número máximo de entradas empilhadas por direção no hedging. 2
MaxNetVolume Tamanho máximo da posição líquida absoluta ao operar em modo neteado. 2
AccountingMode Alternar entre Netting (limite de volume) e Hedging (limite de contagem de entradas). Netting
CandleType Agregação de velas usada para detecção de padrões. Velas de 1 minuto

Todos os parâmetros baseados em pips são convertidos para offsets de preço usando o tamanho do tick do instrumento. Se o instrumento tiver 3 ou 5 casas decimais, o tamanho do pip é escalado por um fator de dez para refletir a definição do MetaTrader.

Notas de implementação

  • A estratégia depende da subscrição de velas de alto nível do StockSharp (SubscribeCandles) e evita buffers de histórico manuais.
  • A lógica de proteção rastreia o preço mais alto (para posições compradas) ou mais baixo (para posições vendidas) visto após a entrada para emular o comportamento de trailing original.
  • Os limites de posição se adaptam automaticamente ao Volume base da estratégia. Aumentar o Volume expande os tamanhos de ordens de stop e take-profit proporcionalmente.
  • Mensagens de log são emitidas sempre que uma saída de proteção (stop ou take-profit) fecha uma posição, fornecendo clareza durante os backtests.

Dicas de uso

  • Escolher o modo Hedging ao simular plataformas que permitem múltiplos tickets por direção, ou ficar com Netting para refletir contas de posição única.
  • Definir TrailingStepPips como zero para um trailing stop clássico que se move sempre que o mercado avança TrailingStopPips.
  • Como as saídas são avaliadas em velas concluídas, considerar um intervalo de velas mais curto se a precisão intrabar for crítica.
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 N identical candles in a row.
/// Enters in the direction of the candle streak.
/// </summary>
public class NCandlesSequenceStrategy : Strategy
{
	private readonly StrategyParam<int> _consecutiveCandles;
	private readonly StrategyParam<DataType> _candleType;

	private int _consecutiveDirection;
	private int _consecutiveCount;

	/// <summary>
	/// Number of identical candles required before entering a trade.
	/// </summary>
	public int ConsecutiveCandles
	{
		get => _consecutiveCandles.Value;
		set => _consecutiveCandles.Value = value;
	}

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

	/// <summary>
	/// Initializes a new instance of the strategy.
	/// </summary>
	public NCandlesSequenceStrategy()
	{
		_consecutiveCandles = Param(nameof(ConsecutiveCandles), 3)
			.SetGreaterThanZero()
			.SetDisplay("Consecutive Candles", "Number of identical candles in a row", "Entry")
			.SetOptimize(2, 6, 1);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to analyze", "General");
	}

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

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

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

		_consecutiveDirection = 0;
		_consecutiveCount = 0;

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

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

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

		var direction = GetCandleDirection(candle);

		if (direction == 0)
		{
			_consecutiveDirection = 0;
			_consecutiveCount = 0;
			return;
		}

		if (direction == _consecutiveDirection)
		{
			_consecutiveCount++;
		}
		else
		{
			_consecutiveDirection = direction;
			_consecutiveCount = 1;
		}

		if (_consecutiveCount < ConsecutiveCandles)
			return;

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

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