Quant BuffetRelax, Not Over Thinking

Timing of Option Returns

Log in to collect

Academic paper

The Timing of Option Returns

AuthorsAdriano Tosi; Alexandre Ziegler

Institute
  • NZPharmac
  • ?Wellington Management
  • CHUniversity of Zurich
  • Institute of Finance and Banking
  • ?University of Zurich - Department of Banking and Finance

Strategy in a nutshell

The strategy focuses on highly liquid S&P 500 index options, excluding securities priced below $0.1. Each month, the top 25% of the most traded front-month put options are selected and classified as ATM, ITM, or OTM. The investor shorts front-month OTM puts one week before expiration and holds them until expiration. The portfolio is equally weighted, maintaining positions for one week, targeting liquid, heavily traded options near expiry.

Economic rationale

Option returns are concentrated in the final days before expiration due to heightened convexity risk rather than volatility risk. Close to maturity, OTM puts become highly sensitive to price jumps in the underlying, capturing the option premium associated with jump risk during this critical period.

Backtest performance

Annualised return8.69%
Volatility3.11%
Beta0.751
Sharpe ratio2.8
Sortino ratio0.613
Win rate100%

Full Python code

from AlgorithmImports import *
#endregion
class TimingOptionReturns(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2010, 1, 1)
self.SetCash(100000)

data = self.AddEquity("SPY", Resolution.Minute)
self.symbol = data.Symbol

option = self.AddOption("SPY", Resolution.Minute)
option.SetFilter(-20, 20, 0, 7)

def OnData(self, slice):
if self.symbol not in slice:
    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 = slice[self.symbol].Value
        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 - 7))
        strikes = [i.Strike for i in puts]
        
        # Determine 5% out-of-the-money strike.
        otm_strike = min(strikes, key = lambda x:abs(x - float(0.95) * underlying_price))
        otm_put = [i for i in puts if i.Expiry == expiry and i.Strike == otm_strike]

        if otm_put:
            # Sell 10% OTM put.
            options_q = int(self.Portfolio.MarginRemaining / (underlying_price * 100))
            self.Sell(otm_put[0].Symbol, options_q)