Price Gap Strategy in the US Stock Market
Log in to collectAcademic paper
Price Gap Anomaly in the US Stock Market: The Whole Story
Oleksiy Plastun; Xolani Sibande; Rangan Gupta; Mark E. Wohar
- UASumy State University
- ZAUniversity of Pretoria
- ?University of Pretoria - Department of Economics
- University of Nebraska at Omaha
Strategy in a nutshell
The strategy trades a single S&P 500 vehicle (CFD, ETF, or futures). A gap threshold is defined based on 100 historical price gaps over ten years. When the opening–closing price difference exceeds this threshold, the index is bought at the open and sold at the close; otherwise, the investor stays out.
Economic rationale
Price gaps arise from timing differences between closes and opens, driven by weekends, holidays, after-hours trading, or unexpected events like earnings. While no fundamental cause for abnormal returns is specified, statistical evidence shows these gaps often create profitable opportunities when openings deviate significantly from prior closes.
Backtest performance
Full Python code
import numpy as np
from AlgorithmImports import *
class PriceGapStrategy(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2000, 1, 1)
self.set_cash(100000)
self.symbol: Symbol = self.add_equity('SPY', Resolution.MINUTE).symbol
self.close_price: float = 0 # recent close price
self.gaps: List[float] = [] # daily gaps
self.min_gap_count: int = 100
self.schedule.on(self.date_rules.every_day(self.symbol), self.time_rules.before_market_close(self.symbol, 1), self.close)
self.schedule.on(self.date_rules.every_day(self.symbol), self.time_rules.after_market_open(self.symbol, 1), self.open)
def open(self) -> None:
if self.securities.contains_key(self.symbol):
if self.close_price != 0:
# Store recent gap.
open: float = self.securities[self.symbol].open
self.gaps.append((open / self.close_price) - 1)
self.close_price = 0
if len(self.gaps) < self.min_gap_count: return
# Gap signals.
gaps_mean: float = np.mean(self.gaps)
gaps_std: float = np.std(self.gaps)
todays_gap: float = self.gaps[-1]
if todays_gap > gaps_mean + 2 * gaps_std:
self.set_holdings(self.symbol, 1)
def close(self) -> None:
self.liquidate(self.symbol)
if self.securities.contains_key(self.symbol):
close: float = self.securities[self.symbol].close
if close != 0:
self.close_price = close