
The investment universe contains five futures contracts traded on the Chicago Mercantile Exchange—5 equity indices (DJIA, NASDAQ, NIKKEI 225, S&P400, and S&P500). (Always select the most liquid contract; the most liquid contract is generally the nearest-to-delivery (front) contract.)
ASSET CLASS: CFDs, futures | REGION: Global | FREQUENCY:
Intraday | MARKET: equities | KEYWORD: Overnight-Intraday Reversal
I. STRATEGY IN A NUTSHELL
Universe: Five CME equity index futures (DJIA, NASDAQ, NIKKEI 225, S&P400, S&P500). CO-OC reversal strategy: buy the two overnight losers and short the two overnight winners at the next day’s open, closing positions at day’s end. Portfolio is equally weighted and rebalanced daily.
II. ECONOMIC RATIONALE
Returns are driven by market microstructure and liquidity frictions rather than standard asset pricing factors or macroeconomic news, highlighting persistent inefficiencies even in highly liquid futures markets.
III. SOURCE PAPER
Overnight-Intraday Reversal Everywhere [Click to Open PDF]
Robert Kosowski, Imperial College Business School; Chun Liu, CEPR (Centre for Economic Policy Research), University of Toronto; Yang Liu, Tsinghua University – School of Economics and Management; Tianyu Wang, Hunan University – College of Finance and Statistics; [Next Author], Tsinghua University, School of Economics and Management
<Abstract>
A strategy that buys securities with low past overnight returns and sells securities with high past overnight returns generates sizeable out-of-sample intraday returns and Sharpe ratios in all major asset classes. This strategy – labeled as overnight-intraday reversal – delivers an average return that is about two to five times larger than those generated by the conventional reversal strategy. Investor sentiment, macro-news announcements, and market uncertainty fail to explain this overnight-intraday reversal return. Our findings are consistent with an asset class-specific market maker liquidity provision mechanism, and we find that cross sectional return dispersion could well predict the strategy returns.


IV. BACKTEST PERFORMANCE
| Annualised Return | 59.98% |
| Volatility | 10.87% |
| Beta | 0.017 |
| Sharpe Ratio | 5.52 |
| Sortino Ratio | -0.657 |
| Maximum Drawdown | N/A |
| Win Rate | 48% |
V. FULL PYTHON CODE
from AlgorithmImports import *
# endregion
class OvernightIntradayReversalinFutures(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2015, 1, 1)
self.SetCash(100000)
self.traded_percentage:float = 0.1
self.futures:List[Symbol] = []
self.last_close:Dict[Symbol, float] = {}
self.traded_count:int = 2
symbols:List[str] = [
Futures.Indices.SP400MidCapEmini,
Futures.Indices.SP500EMini,
Futures.Indices.MicroDow30EMini,
Futures.Indices.NASDAQ100EMini,
Futures.Indices.Nikkei225Dollar,
]
for symbol in symbols:
future:Future = self.AddFuture(symbol, Resolution.Minute, dataNormalizationMode=DataNormalizationMode.BackwardsRatio, contractDepthOffset=0)
self.futures.append(future.Symbol)
self.day_close_flag:bool = False
self.day_open_flag:bool = False
self.Schedule.On(self.DateRules.EveryDay(self.futures[1]), self.TimeRules.BeforeMarketClose(self.futures[1], 1), self.DayClose)
self.Schedule.On(self.DateRules.EveryDay(self.futures[1]), self.TimeRules.AfterMarketOpen(self.futures[1], 1), self.DayOpen)
def OnData(self, data: Slice) -> None:
if self.day_open_flag:
self.day_open_flag = False
returns:Dict[Symbol, float] = {symbol: data[symbol].Open / self.last_close[symbol] - 1 for symbol in self.futures if symbol in self.last_close and symbol in data and data[symbol]}
self.last_close.clear()
traded_count:int = self.traded_count if len(returns) >= len(self.futures) else 1
sorted_returns:List[Symbol] = sorted(returns, key=returns.get)
long:List[Symbol] = sorted_returns[:traded_count]
short:List[Symbol] = sorted_returns[-traded_count:]
for i, portfolio in enumerate([long, short]):
for symbol in portfolio:
self.SetHoldings(self.Securities[symbol].Mapped, ((-1) ** i) / len(portfolio) * self.traded_percentage)
if self.day_close_flag:
self.day_close_flag = False
self.Liquidate()
self.last_close = { symbol: data[symbol].Close for symbol in self.futures if symbol in data and data[symbol] }
def DayClose(self) -> None:
self.day_close_flag = True
def DayOpen(self) -> None:
self.day_open_flag = True
VI. Backtest Performance