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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class n_candles_sequence_strategy(Strategy):
"""Opens positions after detecting N identical-direction candles in a row."""
def __init__(self):
super(n_candles_sequence_strategy, self).__init__()
self._consecutive = self.Param("ConsecutiveCandles", 3) \
.SetGreaterThanZero() \
.SetDisplay("Consecutive Candles", "Number of identical candles in a row", "Entry")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Type of candles to analyze", "General")
self._direction = 0
self._count = 0
@property
def ConsecutiveCandles(self):
return self._consecutive.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(n_candles_sequence_strategy, self).OnStarted2(time)
self._direction = 0
self._count = 0
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
d = 0
if float(candle.ClosePrice) > float(candle.OpenPrice):
d = 1
elif float(candle.ClosePrice) < float(candle.OpenPrice):
d = -1
if d == 0:
self._direction = 0
self._count = 0
return
if d == self._direction:
self._count += 1
else:
self._direction = d
self._count = 1
if self._count < self.ConsecutiveCandles:
return
if d > 0 and self.Position <= 0:
self.BuyMarket()
elif d < 0 and self.Position >= 0:
self.SellMarket()
def OnReseted(self):
super(n_candles_sequence_strategy, self).OnReseted()
self._direction = 0
self._count = 0
def CreateClone(self):
return n_candles_sequence_strategy()