The strategy buys SPY at close and sells at open, but due to slippage and fees, it’s better suited as an input for advanced strategies or trade execution optimization.

I. STRATEGY IN A NUTSHELL

The strategy involves buying the SPY ETF at its daily closing price and selling it at the next day’s opening. While it generates frequent trades, making it prone to slippage and fees, it is best used as an input for a more sophisticated strategy or as a trade execution tool to optimize entry and exit points.

II. ECONOMIC RATIONALE

This anomaly likely stems from companies publishing positive earnings surprises outside market hours starting in the mid-90s. Academic studies suggest high opening prices result from accumulated market orders, followed by a decline during the first trading hour. While an illiquidity premium may contribute to positive overnight returns, it explains only a small portion of the return difference between night and day.

III. SOURCE PAPER

Return Differences between Trading and Non-Trading Hours: Like Night and Day [Click to Open PDF]

Michael Cliff, Analysis Group, University of Utah – David Eccles School of Business; Michael J Cooper, Mitchell E, Daniels, Jr School of Business, Purdue University; Huseyin Gulen, Purdue University – Krannert School of Management

<Abstract>

We use transaction-level data and decompose the US equity premium into day (open to close) and night (close to open) returns. We document the striking result that the US equity premium over the last decade is solely due to overnight returns; the returns during the night are strongly positive, and returns during the day are close to zero and sometimes negative. This day and night effect holds for individual stocks, equity indexes, and futures contracts on equity indexes and is robust across the NYSE and Nasdaq exchanges. Night returns are consistently higher than day returns across days of the week, days of the month, and months of the year. The effect is driven in part by high opening prices which subsequently decline in the first hour of trading.

IV. BACKTEST PERFORMANCE

Annualised Return14%
VolatilityN/A
Beta0.341
Sharpe RatioN/A
Sortino Ratio0.369
Maximum DrawdownN/A
Win Rate55%

V. FULL PYTHON CODE

from AlgorithmImports import *
#endregion
class OvernightAnomaly(QCAlgorithm):
    def initialize(self):
        self.set_start_date(1998, 1, 2)
        self.set_cash(100_000)
        
        data: Equity = self.add_equity("SPY", Resolution.MINUTE)
        data.SetFeeModel(CustomFeeModel())
        self.symbol: Symbol = data.Symbol
        
        # NOTE: MarketOnClose orders must be placed with at least a 16 minute buffer before market close.
        self.schedule.on(self.date_rules.every_day(self.symbol), self.time_rules.before_market_close(self.symbol, 16), self.day_close)
    def day_close(self) -> None:
        if not self.portfolio.invested:
            q: int = self.portfolio.total_portfolio_value // self.securities[self.symbol].price
            self.market_on_close_order(self.symbol, q)
            self.market_on_open_order(self.symbol, -q)
# Custom fee model (trading strategy is backtested without transaction costs to show a theoretical potential of overnight factor)
class CustomFeeModel(FeeModel):
    def get_order_fee(self, parameters):
        fee = parameters.security.price * parameters.order.absolute_quantity * 0
        return OrderFee(CashAmount(fee, "USD"))

Leave a Reply

Discover more from Quant Buffet

Subscribe now to keep reading and get access to the full archive.

Continue reading