La estrategia N Candles escanea el mercado en busca de velas consecutivas que cierran todas en la misma dirección. Una vez que ha aparecido un número configurable de velas alcistas o bajistas, la estrategia entra en la dirección de la secuencia. La implementación es una conversión directa del asesor experto de MetaTrader "N Candles v4" y preserva sus controles de riesgo, configuración basada en pips y comportamiento opcional de trailing stop dentro de la API de alto nivel de StockSharp.
Criterios de entrada
Cada vela terminada se evalúa una vez.
Las velas que cierran al alza se cuentan como alcistas, las que cierran a la baja como bajistas, y las velas doji reinician la secuencia.
Cuando aparecen ConsecutiveCandles velas alcistas (o bajistas) en una fila, la estrategia envía una orden de mercado en la dirección del movimiento.
Se aplican límites de apilamiento estilo cobertura o límites de exposición estilo neteado dependiendo del AccountingMode seleccionado.
Gestión de salidas
StopLossPips y TakeProfitPips definen niveles de salida estáticos medidos en pips desde el precio de entrada promedio de la posición activa.
Si TrailingStopPips es mayor que cero, el nivel del stop sigue al precio más favorable:
Cuando no existe un stop fijo (por ejemplo cuando StopLossPips es cero), la estrategia espera hasta que el precio se mueva TrailingStopPips a favor de la operación antes de colocar un stop de equilibrio.
Una vez que se ha establecido un stop, se mueve hacia el mercado cuando la distancia entre el precio y el stop supera TrailingStopPips + TrailingStepPips.
Los niveles de protección se recalculan cuando cambia el tamaño de la posición y se comprueban contra cada vela terminada, garantizando que cualquier evento de stop-loss o take-profit cierre la operación inmediatamente.
Parámetros
Nombre
Descripción
Predeterminado
ConsecutiveCandles
Número de velas idénticas requeridas para disparar una entrada.
3
TakeProfitPips
Distancia del take-profit en pips. Usar cero para deshabilitar el objetivo.
50
StopLossPips
Distancia del stop-loss en pips. Usar cero para deshabilitar el stop.
50
TrailingStopPips
Distancia del trailing stop en pips. Cero deshabilita el trailing.
10
TrailingStepPips
Movimiento adicional requerido antes de que el trailing stop avance.
4
MaxPositionsPerDirection
Número máximo de entradas apiladas por dirección en cobertura.
2
MaxNetVolume
Tamaño máximo de posición neta absoluta al operar en modo neteado.
2
AccountingMode
Cambiar entre Netting (límite de volumen) y Hedging (límite de recuento de entradas).
Netting
CandleType
Agregación de velas usada para la detección de patrones.
Velas de 1 minuto
Todos los parámetros basados en pips se convierten a offsets de precio usando el tamaño del tick del instrumento. Si el instrumento tiene 3 o 5 decimales, el tamaño del pip se escala por un factor de diez para reflejar la definición de MetaTrader.
Notas de implementación
La estrategia depende de la suscripción de velas de alto nivel de StockSharp (SubscribeCandles) y evita buffers de historial manuales.
La lógica de protección hace un seguimiento del precio más alto (para largos) o más bajo (para cortos) visto después de la entrada para emular el comportamiento de trailing original.
Los límites de posición se adaptan automáticamente al Volume base de la estrategia. Aumentar Volume expande los tamaños de órdenes de stop y take-profit proporcionalmente.
Se emiten mensajes de registro cada vez que una salida de protección (stop o take-profit) cierra una posición, proporcionando claridad durante los backtests.
Consejos de uso
Elegir el modo Hedging cuando se simulan plataformas que permiten múltiples tickets por dirección, o quedarse con Netting para reflejar cuentas de posición única.
Establecer TrailingStepPips en cero para un trailing stop clásico que se mueve cada vez que el mercado avanza TrailingStopPips.
Debido a que las salidas se evalúan en velas completadas, considerar un intervalo de velas más corto si la precisión intrabar es 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()