Option Strategies
Short Box Spread
Introduction
A Short Box Spread is the inverse of a box spread, as well as the combination of a bear call spread and a bull put spread. It consists of buying an OTM call at strike A, selling an ITM put at strike A, buying an OTM put and strike B<A, and selling an ITM call at strike B, where all of the contracts have the same expiry date. This strategy serves as an delta-neutral arbitration from Option mispricing. Note that it only attains a true profit when the risk-free return is greater than the risk-free interest rate.
Implementation
Follow these steps to implement the short box spread strategy:
- In the
initialize
method, set the start date, set the end date, subscribe to the underlying Equity, and create an Option universe. - In the
on_data
method, select the strike and expiry of the contracts in the strategy legs. - In the
on_data
method, select the contracts and place the orders.
def initialize(self) -> None: self.set_start_date(2017, 4, 1) self.set_end_date(2017, 4, 30) self.set_cash(100000) self.universe_settings.asynchronous = True option = self.add_option("GOOG", Resolution.MINUTE) self._symbol = option.symbol option.set_filter(lambda universe: universe.include_weeklys().box_spread(30, 5))
The box_spread
filter narrows the universe down to just the four contracts you need to form a short box spread.
def on_data(self, slice: Slice) -> None: if self.portfolio.invested: return # Get the OptionChain chain = slice.option_chains.get(self._symbol, None) if not chain: return # Select an expiry date and ITM & OTM strike prices expiry = max([x.expiry for x in chain]) contracts = [x for x in chain if x.expiry == expiry] lower_strike = min([x.strike for x in contracts]) higher_strike = max([x.strike for x in contracts])
Approach A: Call the OptionStrategies.short_box_spread
method with the details of each leg and then pass the result to the buy
method.
short_box_spread = OptionStrategies.short_box_spread(self._symbol, higher_strike, lower_strike, expiry) self.buy(short_box_spread, 1)
Approach B: Create a list of Leg
objects and then call the combo_market_order, combo_limit_order, or combo_leg_limit_order method.
# Select the call and put contracts itm_call = [x for x in chain if x.right == OptionRight.CALL and x.expiry == expiry and x.strike == lower_strike][0] otm_call = [x for x in chain if x.right == OptionRight.CALL and x.expiry == expiry and x.strike == higher_strike][0] itm_put = [x for x in chain if x.right == OptionRight.PUT and x.expiry == expiry and x.strike == higher_strike][0] otm_put = [x for x in chain if x.right == OptionRight.PUT and x.expiry == expiry and x.strike == lower_strike][0] legs = [ Leg.create(itm_call.symbol, -1), Leg.create(itm_put.symbol, -1), Leg.create(otm_call.symbol, 1), Leg.create(otm_put.symbol, 1), ] self.combo_market_order(legs, 1)
Strategy Payoff
This is a fixed payoff, delta-neutral strategy. The payoff is
CITMT=(ST−K−)+COTMT=(ST−K+)+PITMT=(K+−ST)+POTMT=(K−−ST)+PayoffT=(CITMT0−CITMT+PITMT0−PITMT−COTMT0+COTMT−POTMT0+POTMT)×m−fee=(K−−K++CITMT0+PITMT0−COTMT0−POTMT0)×m−feeThe following chart shows the payoff at expiration:

The payoff is only dependent on the strike price and the initial asset prices.
If the Option is American Option, there is a risk of early assignment on the contracts you sell.
Example
The following table shows the price details of the assets in the algorithm:
Asset | Price ($) | Strike ($) |
---|---|---|
ITM Call | 23.00 | 810.00 |
ITM Put | 23.80 | 857.50 |
OTM Call | 1.85 | 857.50 |
OTM Put | 2.75 | 810.00 |
Underlying Equity at expiration | 843.25 | - |
Therefore, the payoff is
PayoffT=(K−−K++CITM0+PITM0−COTM0−POTM0)×m−fee=(810.00−857.50+23.00+23.80−1.85−2.75)×100−1.00×4=−534.00So, the strategy loses $534.
The following algorithm implements a short box spread Option strategy:
class ProtectiveCollarOptionStrategy(QCAlgorithm): def initialize(self) -> None: self.SetStartDate(2017, 4, 1) self.SetEndDate(2017, 4, 30) self.SetCash(100000) self.UniverseSettings.Asynchronous = True self.equity_symbol = self.AddEquity("GOOG").Symbol option = self.AddOption("GOOG", Resolution.Minute) self.option_symbol = option.Symbol option.set_filter(lambda universe: universe.include_weeklys().box_spread(30, 5)) self.invest_ever = False def on_data(self, slice: Slice) -> None: if self.invest_ever: return # Get the OptionChain chain = slice.option_chains.get(self.option_symbol, None) if not chain: return # Select an expiry date expiry = sorted(chain, key = lambda x: x.expiry)[-1].expiry # Select the strike prices of the contracts ordered_contracts = sorted(chain, key = lambda x: x.strike) higher_strike = ordered_contracts[-1].strike lower_strike = ordered_contracts[0].strike box_spread = OptionStrategies.short_box_spread(self.option_symbol, higher_strike, lower_strike, expiry) self.buy(box_spread, 1) self.invest_ever = True def on_end_of_day(self, symbol: Symbol) -> None: if symbol == self.equity_symbol: self.Log(f"{self.time}::{symbol}::{self.securities[symbol].price}")