Стратегия пробоя, повторяющая логику советника MetaTrader «Previous Candle Breakdown 2». Алгоритм отслеживает последнюю
закрытую свечу на настраиваемом таймфрейме и открывает сделки, когда цена пробивает её максимум или минимум на указанное
количество пунктов. Дополнительные фильтры по скользящим средним, торговое окно по времени, выбор фиксированного объёма или
расчёт объёма по проценту риска, а также многоуровневая защита позиции полностью воссоздают поведение оригинального MQL-кода
внутри StockSharp.
Краткое описание
Условия входа: Покупка при пробое максимума предыдущей свечи с учётом отступа. Продажа при пробое минимума с тем же
отступом.
Фильтры: Пара скользящих средних с параметрами сдвига подтверждает направление. Дополнительно торговля ограничена окном
между временем начала и окончания.
Размер позиции: Можно задать фиксированный объём либо рассчитать объём автоматически на основе стоимости портфеля и
расстояния до стоп-лосса.
Управление риском: Стоп-лосс и тейк-профит в пунктах, трейлинг-стоп со шагом, глобальная цель прибыли для принудительного
закрытия всех позиций.
Масштабирование: Параметр MaxPositions ограничивает абсолютный объём позиции в каждом направлении.
ProfitClose = 100 (денежный эквивалент суммарной реализованной и нереализованной прибыли)
MaxPositions = 10 (максимальный абсолютный объём в контрактах/лотах на сторону)
OrderVolume = 0 (отключено), RiskPercent = 5 (используется, если OrderVolume равен нулю и задан стоп-лосс)
StartTime = 09:09, EndTime = 19:19
CandleType = свечи с периодом 4 часа
Логика торговли
Подписаться на выбранный поток свечей и хранить последнюю завершённую свечу.
Проверить, находится ли текущее время внутри торгового окна. При достижении ProfitClose сразу закрыть позицию.
Рассчитать уровни пробоя, прибавив/вычтя отступ в пунктах к максимуму и минимуму предыдущей свечи.
При пробое этих уровней и выполнении условий по скользящим средним открыть сделку, учитывая ограничение MaxPositions.
Установить стартовые стоп-лосс и тейк-профит относительно цены входа. Активировать трейлинг-стоп, когда цена проходит в
нужную сторону расстояние, превышающее трейлинг плюс шаг.
На каждой свече контролировать достижение стоп-лосса или тейк-профита, обновлять трейлинг-стоп и сбрасывать защитные уровни
после закрытия позиции.
Примечания
Расчёт «пункта» автоматически корректируется для инструментов с 3 или 5 знаками после запятой, как в MetaTrader.
При выборе риск-ориентированного объёма величина позиции оценивается на основе текущей стоимости портфеля и расстояния до
стоп-лосса.
Пробой оценивается по значениям закрытой свечи, поэтому внутрисвечные экстремумы учитываются через High/Low свечи.
MaxPositions работает с чистой позицией стратегии: при использовании дробных объёмов параметр задаёт максимально допустимый
модуль позиции в каждом направлении.
На график выводятся свечи, активные скользящие средние (если включены) и сделки, что облегчает визуальный контроль.
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>
/// Previous candle breakdown strategy v2.
/// Trades breakout of previous candle high/low filtered by EMA crossover.
/// </summary>
public class PreviousCandleBreakdown2Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevHigh;
private decimal? _prevLow;
private decimal? _prevFast;
private decimal? _prevSlow;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public PreviousCandleBreakdown2Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 10).SetGreaterThanZero().SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 30).SetGreaterThanZero().SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHigh = null;
_prevLow = null;
_prevFast = null;
_prevSlow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHigh = null; _prevLow = null; _prevFast = null; _prevSlow = null;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fast); DrawIndicator(area, slow); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_prevFast = fast;
_prevSlow = slow;
return;
}
if (_prevHigh == null || _prevLow == null || _prevFast == null || _prevSlow == null)
{
_prevHigh = candle.HighPrice; _prevLow = candle.LowPrice; _prevFast = fast; _prevSlow = slow;
return;
}
var close = candle.ClosePrice;
var bullTrend = fast > slow;
var bearTrend = fast < slow;
if (bullTrend && close > _prevHigh.Value && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (bearTrend && close < _prevLow.Value && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_prevHigh = candle.HighPrice; _prevLow = candle.LowPrice; _prevFast = fast; _prevSlow = slow;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class previous_candle_breakdown2_strategy(Strategy):
def __init__(self):
super(previous_candle_breakdown2_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 10) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 30) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._prev_high = None
self._prev_low = None
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(previous_candle_breakdown2_strategy, self).OnReseted()
self._prev_high = None
self._prev_low = None
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(previous_candle_breakdown2_strategy, self).OnStarted2(time)
self._prev_high = None
self._prev_low = None
self._prev_fast = None
self._prev_slow = None
fast = ExponentialMovingAverage()
fast.Length = self.FastPeriod
slow = ExponentialMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_fast = fv
self._prev_slow = sv
return
if self._prev_high is None or self._prev_low is None or self._prev_fast is None or self._prev_slow is None:
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_fast = fv
self._prev_slow = sv
return
close = float(candle.ClosePrice)
bull_trend = fv > sv
bear_trend = fv < sv
if bull_trend and close > self._prev_high and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif bear_trend and close < self._prev_low and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_fast = fv
self._prev_slow = sv
def CreateClone(self):
return previous_candle_breakdown2_strategy()