Стратегия N Candles отслеживает последовательности свечей, которые закрываются в одном направлении. Когда формируется заданное количество подряд идущих бычьих или медвежьих свечей, стратегия открывает позицию по направлению импульса. Реализация является адаптацией советника MetaTrader «N Candles v4» и сохраняет его параметры управления рисками, настройку в пунктах и алгоритм трейлинг-стопа, перенесённый на высокоуровневый API StockSharp.
Условия входа
Анализ выполняется только по завершённым свечам.
Свечи с закрытием выше открытия считаются бычьими, ниже открытия — медвежьими, а дожи обнуляют счётчик последовательности.
При появлении ConsecutiveCandles свечей подряд стратегия отправляет рыночную заявку в сторону движения.
В зависимости от параметра AccountingMode используются ограничения по суммарному объёму (неттинг) или по количеству позиций в каждом направлении (хеджинг).
Управление выходом
Параметры StopLossPips и TakeProfitPips задают фиксированные уровни выхода в пунктах относительно средней цены входа.
Если TrailingStopPips больше нуля, активируется трейлинг-стоп:
При отсутствии фиксированного стоп-лосса (например, когда StopLossPips равен нулю) стоп переводится в безубыток, когда цена проходит TrailingStopPips пунктов в прибыль.
После появления стоп-лосса он смещается вслед за ценой, как только разница между ценой и стопом превышает TrailingStopPips + TrailingStepPips.
Расчёт защитных уровней выполняется при каждом изменении позиции; на каждой свече проверяется, достигнут ли стоп или тейк-профит, и при срабатывании позиция немедленно закрывается.
Параметры
Параметр
Описание
Значение по умолчанию
ConsecutiveCandles
Количество одинаковых свечей для сигнала.
3
TakeProfitPips
Размер тейк-профита в пунктах (0 — отключить).
50
StopLossPips
Размер стоп-лосса в пунктах (0 — отключить).
50
TrailingStopPips
Дистанция трейлинг-стопа в пунктах (0 — отключить).
10
TrailingStepPips
Дополнительное смещение для переноса трейлинг-стопа.
4
MaxPositionsPerDirection
Максимум входов в одном направлении при хеджинге.
2
MaxNetVolume
Максимальный суммарный объём позиции при неттинге.
2
AccountingMode
Режим учёта: Netting или Hedging.
Netting
CandleType
Тип свечей для анализа.
1-минутные свечи
Параметры, заданные в пунктах, преобразуются в ценовые величины на основе шага цены инструмента. Для инструментов с 3 или 5 знаками после запятой размер пункта умножается на 10, как в оригинальном советнике.
Особенности реализации
Используется высокоуровневая подписка на свечи (SubscribeCandles), что избавляет от ручного хранения истории.
Для имитации поведения оригинального трейлинг-стопа стратегия отслеживает максимальную (для лонгов) или минимальную (для шортов) цену после входа.
Лимиты по позициям автоматически масштабируются вместе с базовым объёмом стратегии (Volume).
При закрытии позиции по стопу или тейк-профиту в лог выводится соответствующее сообщение, что упрощает анализ тестов.
Рекомендации по использованию
Выбирайте режим Hedging, если требуется моделировать площадку с независимыми позициями по каждому направлению; режим Netting соответствует единой неттинговой позиции.
Установите TrailingStepPips в ноль, чтобы трейлинг-стоп сдвигался при каждом продвижении цены на TrailingStopPips пунктов.
Если важно реагировать на внутрибарные колебания, используйте более мелкий таймфрейм свечей.
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()