Стратегия Session Breakout повторяет логику советника MetaTrader "Session breakout". Она анализирует утреннюю европейскую сессию,
измеряет её ценовой диапазон и при достаточно узком диапазоне готовится торговать пробой во время американской сессии. Реализаци
я использует высокоуровневый API StockSharp, ограничивает количество сделок одним покупочным и одним продажным входом в сутки и
автоматически добавляет защитные ордера (стоп-лосс и тейк-профит).
Торговая логика
В начале каждого торгового дня состояние сбрасывается; выходные пропускаются. Торговля по понедельникам включается отдельным п
араметром.
Во время европейской сессии (по умолчанию 06:00–12:00) стратегия отслеживает завершённые свечи и сохраняет максимум и минимум.
В момент старта американской сессии диапазон признаётся "маленьким", если его ширина меньше значения SmallSessionThresholdPi ps в пунктах.
При наличии узкого диапазона стратегия отслеживает свечи американской сессии (по умолчанию 12:00–16:00) и ждёт закрытия хотя б
ы одной свечи США (EuropeSessionStartHour + 5 до EuropeSessionStartHour + 10).
Сигнал на покупку появляется, когда свеча полностью находится выше европейского максимума плюс буфер (BreakoutBufferPips). С
игнал на продажу возникает, если свеча располагается ниже минимума минус буфер.
После входа стратегия выставляет стоп-лосс и тейк-профит в пунктах и запрещает новые сделки в том же направлении до конца дня.
Параметры
Параметр
Описание
Volume
Объём ордера для покупок и продаж.
EuropeSessionStartHour
Час начала отслеживания европейского диапазона.
EuropeSessionEndHour
Час завершения отслеживания европейского диапазона.
UsSessionStartHour
Час начала окна американской сессии.
UsSessionEndHour
Час окончания окна американской сессии.
SmallSessionThresholdPips
Максимальная ширина диапазона в пунктах, чтобы считать его сжатием.
BreakoutBufferPips
Дополнительный буфер над/под диапазоном перед входом.
TradeOnMonday
Разрешает торговлю по понедельникам (выходные всегда исключены).
TakeProfitPips
Расстояние от цены входа до тейк-профита.
StopLossPips
Расстояние от цены входа до стоп-лосса.
CandleType
Тип свечей для расчётов (по умолчанию 15-минутные).
Примечания
Размер пункта определяется полем PriceStep выбранного инструмента. Подбирайте параметры в пунктах в соответствии с условиями
контракта.
Ордеры генерируются по закрытию подходящей свечи, поэтому в тестах вход происходит по цене закрытия. В реальной торговле испол
нение может отличаться из-за рыночных условий.
В сутки допускается только один длинный и один короткий вход. Логика соответствует оригинальному советнику и использует стандарт
ные средства риск-менеджмента StockSharp.
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>
/// Session breakout strategy that tracks a range during first hours and trades breakouts later.
/// Accumulates high/low during range hours (0-8), then trades breakouts during active hours (8-20).
/// </summary>
public class SessionBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _rangeStartHour;
private readonly StrategyParam<int> _rangeEndHour;
private readonly StrategyParam<int> _tradeEndHour;
private readonly StrategyParam<DataType> _candleType;
private DateTime _currentDate;
private decimal? _rangeHigh;
private decimal? _rangeLow;
private bool _rangeComplete;
private bool _tradedToday;
public int RangeStartHour { get => _rangeStartHour.Value; set => _rangeStartHour.Value = value; }
public int RangeEndHour { get => _rangeEndHour.Value; set => _rangeEndHour.Value = value; }
public int TradeEndHour { get => _tradeEndHour.Value; set => _tradeEndHour.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public SessionBreakoutStrategy()
{
_rangeStartHour = Param(nameof(RangeStartHour), 0)
.SetDisplay("Range Start", "Hour to start tracking range", "Sessions");
_rangeEndHour = Param(nameof(RangeEndHour), 8)
.SetDisplay("Range End", "Hour to stop tracking range", "Sessions");
_tradeEndHour = Param(nameof(TradeEndHour), 20)
.SetDisplay("Trade End", "Hour to stop trading", "Sessions");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_currentDate = default;
_rangeHigh = null;
_rangeLow = null;
_rangeComplete = false;
_tradedToday = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_currentDate = default;
_rangeHigh = null;
_rangeLow = null;
_rangeComplete = false;
_tradedToday = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var candleDate = candle.OpenTime.Date;
var hour = candle.OpenTime.Hour;
// Reset on new day
if (candleDate != _currentDate)
{
_currentDate = candleDate;
_rangeHigh = null;
_rangeLow = null;
_rangeComplete = false;
_tradedToday = false;
}
// Build range during accumulation hours
if (hour >= RangeStartHour && hour < RangeEndHour)
{
if (_rangeHigh == null || candle.HighPrice > _rangeHigh)
_rangeHigh = candle.HighPrice;
if (_rangeLow == null || candle.LowPrice < _rangeLow)
_rangeLow = candle.LowPrice;
return;
}
// Mark range as complete
if (!_rangeComplete && hour >= RangeEndHour && _rangeHigh != null && _rangeLow != null)
_rangeComplete = true;
if (!_rangeComplete || _rangeHigh == null || _rangeLow == null)
return;
// Trade during active hours
if (hour >= RangeEndHour && hour < TradeEndHour)
{
// Breakout above range high - buy
if (Position <= 0 && candle.ClosePrice > _rangeHigh.Value && !_tradedToday)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_tradedToday = true;
}
// Breakout below range low - sell
else if (Position >= 0 && candle.ClosePrice < _rangeLow.Value && !_tradedToday)
{
if (Position > 0)
SellMarket();
SellMarket();
_tradedToday = true;
}
}
// Close at end of day
if (hour >= TradeEndHour && Position != 0)
{
if (Position > 0)
SellMarket();
else
BuyMarket();
}
}
}
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
from datatype_extensions import *
from indicator_extensions import *
class session_breakout_strategy(Strategy):
def __init__(self):
super(session_breakout_strategy, self).__init__()
self._range_start = self.Param("RangeStartHour", 0).SetDisplay("Range Start", "Hour to start tracking range", "Sessions")
self._range_end = self.Param("RangeEndHour", 8).SetDisplay("Range End", "Hour to stop tracking range", "Sessions")
self._trade_end = self.Param("TradeEndHour", 20).SetDisplay("Trade End", "Hour to stop trading", "Sessions")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))).SetDisplay("Candle Type", "Candle timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(session_breakout_strategy, self).OnReseted()
self._current_date = None
self._range_high = None
self._range_low = None
self._range_complete = False
self._traded_today = False
def OnStarted2(self, time):
super(session_breakout_strategy, self).OnStarted2(time)
self._current_date = None
self._range_high = None
self._range_low = None
self._range_complete = False
self._traded_today = False
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self.OnProcess).Start()
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
candle_date = candle.OpenTime.Date
hour = candle.OpenTime.Hour
if self._current_date is None or candle_date != self._current_date:
self._current_date = candle_date
self._range_high = None
self._range_low = None
self._range_complete = False
self._traded_today = False
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
if hour >= self._range_start.Value and hour < self._range_end.Value:
if self._range_high is None or high > self._range_high:
self._range_high = high
if self._range_low is None or low < self._range_low:
self._range_low = low
return
if not self._range_complete and hour >= self._range_end.Value and self._range_high is not None and self._range_low is not None:
self._range_complete = True
if not self._range_complete or self._range_high is None or self._range_low is None:
return
if hour >= self._range_end.Value and hour < self._trade_end.Value:
if self.Position <= 0 and close > self._range_high and not self._traded_today:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._traded_today = True
elif self.Position >= 0 and close < self._range_low and not self._traded_today:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._traded_today = True
if hour >= self._trade_end.Value and self.Position != 0:
if self.Position > 0:
self.SellMarket()
else:
self.BuyMarket()
def CreateClone(self):
return session_breakout_strategy()