Trades breakouts of the New York 9:30-9:45 opening range with optional moving-average based exits. Entries occur on the bar after a breakout if within the cutoff time and the price aligns with the moving average filter.
Details
Entry Criteria:
Previous candle closes beyond the opening range high (long) or low (short) before the cutoff time.
Current candle is the first after the breakout and satisfies the MA filter when enabled.
Long/Short: Configurable via TradeDirection.
Exit Criteria:
Stop at the opposite side of the opening range.
Take profit according to TakeProfitType: fixed risk-reward, moving average cross, or both.
Stops: Yes, at range boundaries.
Default Values:
CutoffHour = 12
CutoffMinute = 0
TradeDirection = LongOnly
TakeProfitType = FixedRiskReward
TpRatio = 2.5
MaType = SMA
MaLength = 100
CandleType = TimeSpan.FromMinutes(1)
Filters:
Category: Breakout
Direction: Configurable
Indicators: Moving Average
Stops: Yes
Complexity: Intermediate
Timeframe: Intraday
Seasonality: No
Neural networks: No
Divergence: No
Risk level: Medium
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class NyOpeningRangeBreakoutMaStopStrategy : Strategy
{
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<DataType> _candleType;
private decimal _dayHigh;
private decimal _dayLow;
private DateTime _currentDay;
private bool _rangeSet;
private bool _tradedToday;
public int MaLength { get => _maLength.Value; set => _maLength.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public NyOpeningRangeBreakoutMaStopStrategy()
{
_maLength = Param(nameof(MaLength), 50).SetGreaterThanZero();
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_dayHigh = 0m;
_dayLow = 0m;
_currentDay = default;
_rangeSet = false;
_tradedToday = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_dayHigh = 0m;
_dayLow = 0m;
_currentDay = default;
_rangeSet = false;
_tradedToday = false;
var ma = new SimpleMovingAverage { Length = MaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
DrawIndicator(area, ma);
}
}
private void ProcessCandle(ICandleMessage candle, decimal maValue)
{
if (candle.State != CandleStates.Finished)
return;
var day = candle.OpenTime.Date;
// New day
if (day != _currentDay)
{
_currentDay = day;
if (Position > 0) SellMarket();
else if (Position < 0) BuyMarket();
_dayHigh = candle.HighPrice;
_dayLow = candle.LowPrice;
_rangeSet = true;
_tradedToday = false;
return;
}
if (!_rangeSet || _tradedToday)
return;
// Exit on MA cross
if (Position > 0 && candle.ClosePrice < maValue)
{
SellMarket();
_tradedToday = true;
return;
}
if (Position < 0 && candle.ClosePrice > maValue)
{
BuyMarket();
_tradedToday = true;
return;
}
// Entry: breakout above first candle high
if (Position <= 0 && candle.ClosePrice > _dayHigh && candle.ClosePrice > maValue)
{
BuyMarket();
_tradedToday = true;
}
else if (Position >= 0 && candle.ClosePrice < _dayLow && candle.ClosePrice < maValue)
{
SellMarket();
_tradedToday = true;
}
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ny_opening_range_breakout_ma_stop_strategy(Strategy):
def __init__(self):
super(ny_opening_range_breakout_ma_stop_strategy, self).__init__()
self._ma_length = self.Param("MaLength", 50) \
.SetGreaterThanZero()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._day_high = 0.0
self._day_low = 0.0
self._current_day = None
self._range_set = False
self._traded_today = False
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(ny_opening_range_breakout_ma_stop_strategy, self).OnReseted()
self._day_high = 0.0
self._day_low = 0.0
self._current_day = None
self._range_set = False
self._traded_today = False
def OnStarted2(self, time):
super(ny_opening_range_breakout_ma_stop_strategy, self).OnStarted2(time)
self._day_high = 0.0
self._day_low = 0.0
self._current_day = None
self._range_set = False
self._traded_today = False
self._ma = SimpleMovingAverage()
self._ma.Length = self._ma_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._ma, self.OnProcess).Start()
def OnProcess(self, candle, ma_val):
if candle.State != CandleStates.Finished:
return
day = candle.OpenTime.Date
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
mv = float(ma_val)
if self._current_day is None or day != self._current_day:
self._current_day = day
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
self._day_high = high
self._day_low = low
self._range_set = True
self._traded_today = False
return
if not self._range_set or self._traded_today:
return
if self.Position > 0 and close < mv:
self.SellMarket()
self._traded_today = True
return
if self.Position < 0 and close > mv:
self.BuyMarket()
self._traded_today = True
return
if self.Position <= 0 and close > self._day_high and close > mv:
self.BuyMarket()
self._traded_today = True
elif self.Position >= 0 and close < self._day_low and close < mv:
self.SellMarket()
self._traded_today = True
def CreateClone(self):
return ny_opening_range_breakout_ma_stop_strategy()