Opening Range Breakout within Crude Oil
Log in to collectAcademic paper
Strategy in a nutshell
The strategy trades US crude oil futures using daily open-range breakout thresholds. Long positions are opened when prices rise above 1.6524% of the open, and short positions when prices fall below -1.5784%, with all positions closed at day’s end.
Economic rationale
The strategy exploits momentum and the Contraction-Expansion principle, capitalizing on days with large price movements. High volatility between 2001–2011 amplified profitability, allowing trends to continue within favorable market conditions.
Backtest performance
Annualised return9.77%
Beta-0.03
Sortino ratio-0.108
Win rate45%
Full Python code
import numpy as np
from AlgorithmImports import *
class OpeningRangeBreakoutCrudeOil(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2010, 1, 1)
self.SetCash(100000)
self.period:int = 21
self.treshhold_value:int = 2
self.future:Future = self.AddFuture(Futures.Energies.CrudeOilWTI, \
Resolution.Minute, \
dataNormalizationMode=DataNormalizationMode.BackwardsRatio, \
contractDepthOffset=0)
self.symbol:Symbol = self.future.Symbol
self.daily_ret:RollingWindow = RollingWindow[float](self.period)
self.recent_open:float = 0
self.day_close_flag:bool = False
self.day_open_flag:bool = False
self.market:Symbol = self.AddEquity('SPY', Resolution.Daily).Symbol
self.Schedule.On(self.DateRules.EveryDay(self.market), self.TimeRules.BeforeMarketClose(self.market, 1), self.DayClose)
self.Schedule.On(self.DateRules.EveryDay(self.market), self.TimeRules.AfterMarketOpen(self.market, 1), self.DayOpen)
def OnData(self, data: Slice) -> None:
# close
if self.day_close_flag:
self.day_close_flag = False
self.Liquidate()
if self.symbol in data and data[self.symbol]:
close:float = data[self.symbol].Close
if close != 0 and self.recent_open != 0 and close != self.recent_open:
todays_ret:float = close / self.recent_open - 1
self.daily_ret.Add(todays_ret)
if self.daily_ret.IsReady:
daily_returns:List[float] = list(self.daily_ret)
mean:float = np.mean(daily_returns)
std:float = np.std(daily_returns)
high_threshhold:float = mean + self.treshhold_value * std
low_threshhold:float = mean - self.treshhold_value * std
if todays_ret > high_threshhold:
if not self.Portfolio.Invested:
self.MarketOrder(self.future.Mapped, 1)
elif todays_ret < low_threshhold:
if not self.Portfolio.Invested:
self.MarketOrder(self.future.Mapped, -1)
self.recent_open = 0
# open
if self.day_open_flag:
self.day_open_flag = False
if self.symbol in data and data[self.symbol]:
self.recent_open = data[self.symbol].Open
def DayClose(self) -> None:
self.day_close_flag = True
def DayOpen(self) -> None:
self.day_open_flag = True