Эта стратегия торгует импульсные пробои, которые возникают, когда последний завершённый бар закрывается значительно дальше старого контрольного бара. Она создана на основе советника MetaTrader «Nextbar» и сохраняет оригинальные механизмы управления рисками: стопы в пунктах, трейлинг и ограничение срока жизни позиции.
Конфигурация по умолчанию рассчитана на быстро движущиеся валютные пары или индексные фьючерсы с таймфреймом 15 минут, однако логику можно применять к любому инструменту, предоставляющему стандартные свечи. Все заявки исполняются по рынку с заданным объёмом.
Торговая логика
Поиск сигнала
После закрытия нового бара алгоритм сравнивает цену закрытия предыдущего бара с закрытием, которое произошло SignalBar баров назад.
Если предыдущее закрытие выше удалённого закрытия более чем на MinDistancePips, формируется длинный сигнал.
Если предыдущее закрытие ниже удалённого закрытия более чем на MinDistancePips, формируется короткий сигнал.
Флаг ReverseSignals инвертирует направления сигналов для контртрендовых сценариев.
Обработка заявок
Пока позиция открыта, новые сигналы игнорируются — стратегия, как и оригинальный советник, удерживает только одну позицию.
После входа сохраняется цена открытия и заранее рассчитываются уровни стоп-лосса и тейк-профита в ценовых величинах. Значения в пунктах переводятся через минимальный шаг цены инструмента (для пятизнака автоматически используется множитель ×10, чтобы соответствовать метатрейдеровскому понятию пункта).
Правила выхода
Стоп-лосс и тейк-профит – Оба уровня необязательны. Нулевое значение отключает соответствующую защиту. Стратегия контролирует минимумы и максимумы свечей, чтобы закрыть позицию при достижении уровней.
Трейлинг-стоп – При включении (TrailingStopPips > 0) стоп переносится ближе к текущей цене, как только прибыль превышает TrailingStopPips + TrailingStepPips. Расстояние между ценой и стопом никогда не сокращается, что обеспечивает монотонный трейлинг.
Срок жизни позиции – После нахождения в рынке LifetimeBars завершённых свечей позиция закрывается на открытии следующего бара независимо от результата. Это повторяет исходный механизм «закрыть через N баров».
Параметры
CandleType – Таймфрейм, на котором оцениваются сигналы. По умолчанию — 15-минутные свечи.
OrderVolume – Объём каждой рыночной заявки.
StopLossPips – Расстояние от цены входа до защитного стопа в пунктах.
TakeProfitPips – Расстояние от цены входа до цели по прибыли в пунктах.
TrailingStepPips – Дополнительная прибыль, необходимая для очередного переноса трейлинг-стопа. Игнорируется, если трейлинг отключён.
SignalBar – Количество баров между сравниваемыми закрытиями. Минимум два, чтобы не использовать текущую свечу.
MinDistancePips – Минимальная дистанция в пунктах между закрытиями для подтверждения сигнала.
LifetimeBars – Максимальное количество завершённых свечей, которые позиция может находиться в рынке. Ноль отключает таймер.
ReverseSignals – Инвертирует длинные и короткие сигналы.
Особенности реализации
Для расчёта используется короткий скользящий список закрытий, что позволяет обходиться без тяжёлых структур исторических данных.
Перевод пунктов в цены выполняется через шаг цены инструмента. Для котировок с 3 или 5 знаками после запятой автоматически используется традиционное определение пункта.
Все правила риска контролируются по завершённым свечам. Для внутрибаральной защиты можно сочетать стратегию с биржевыми стоп-заявками через настройки платформы.
Автоматические тесты для примера не поставляются. Обязательно проверяйте стратегию на истории перед реальной торговлей.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Next Bar Momentum strategy. Compares close with a reference bar for momentum breakout.
/// </summary>
public class NextBarMomentumStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _momentumPeriod;
private decimal? _prevMom;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int MomentumPeriod
{
get => _momentumPeriod.Value;
set => _momentumPeriod.Value = value;
}
public NextBarMomentumStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_momentumPeriod = Param(nameof(MomentumPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Momentum Period", "Rate of change lookback", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMom = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevMom = null;
var momentum = new Momentum { Length = MomentumPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(momentum, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
var momArea = CreateChartArea();
if (momArea != null)
DrawIndicator(momArea, momentum);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal momVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevMom = momVal;
return;
}
if (_prevMom == null)
{
_prevMom = momVal;
return;
}
// Momentum crosses above zero → buy
if (_prevMom.Value <= 100m && momVal > 100m && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Momentum crosses below zero → sell
else if (_prevMom.Value >= 100m && momVal < 100m && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevMom = momVal;
}
}
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.Indicators import Momentum
from StockSharp.Algo.Strategies import Strategy
class next_bar_momentum_strategy(Strategy):
def __init__(self):
super(next_bar_momentum_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._momentum_period = self.Param("MomentumPeriod", 10) \
.SetDisplay("Momentum Period", "Rate of change lookback", "Indicators")
self._prev_mom = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def MomentumPeriod(self):
return self._momentum_period.Value
def OnReseted(self):
super(next_bar_momentum_strategy, self).OnReseted()
self._prev_mom = None
def OnStarted2(self, time):
super(next_bar_momentum_strategy, self).OnStarted2(time)
self._prev_mom = None
momentum = Momentum()
momentum.Length = self.MomentumPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(momentum, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
ind_area = self.CreateChartArea()
if ind_area is not None:
self.DrawIndicator(ind_area, momentum)
def _on_process(self, candle, mom_value):
if candle.State != CandleStates.Finished:
return
mv = float(mom_value)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_mom = mv
return
if self._prev_mom is None:
self._prev_mom = mv
return
# Momentum crosses above 100
if self._prev_mom <= 100.0 and mv > 100.0 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Momentum crosses below 100
elif self._prev_mom >= 100.0 and mv < 100.0 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_mom = mv
def CreateClone(self):
return next_bar_momentum_strategy()