book
Checkout our new book! Hands on AI Trading with Python, QuantConnect, and AWS Learn More arrow

Option Strategies

Short Jelly Roll

Introduction

A Short Jelly Roll, or simply Short Roll, is a combination of a short call calendar spread and a long put calendar spread. It is the inverse of a jelly roll. It consist of selling a put and buying a call of the same expiry, as well as buying a put and selling a call with a further expiry, where all of the contracts have the same strike prices. This strategy serves as an arbitrage on Option mispricing due to the temporary disparity between the call spread and the put spread synthetic portfolios. It is a delta-, gamma-, vega-, and theta-neutral strategy, but sensitive to rho (interest rate) and phi (dividend yield).

Implementation

Follow these steps to implement the short jelly roll strategy:

  1. In the initialize method, set the start date, set the end date, and create an Option universe.
  2. Select Language:
    def initialize(self) -> None:
        self.set_start_date(2017, 4, 1)
        self.set_end_date(2017, 4, 22)
        self.set_cash(100000)
    
        self.universe_settings.asynchronous = True
        option = self.add_option("GOOG", Resolution.MINUTE)
        self._symbol = option.symbol
        option.set_filter(lambda x: x.include_weeklys().jelly_roll(5.0, 30, 60))
  3. In the on_data method, select the expiry and strikes of the contracts in the strategy legs.
  4. Select Language:
    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
        strike = sorted([x.strike for x in chain], key=lambda x: abs(x - chain.underlying.price))[0]
        contracts = [x for x in chain if x.strike == strike]
        far_expiry = max([x.expiry for x in contracts])
        nearer_expiries = [x.expiry for x in contracts if x.expiry < far_expiry]
        if not nearer_expiries:
            return
        near_expiry = min(nearer_expiries)
  5. In the on_data method, select the contracts and place the order.
  6. Approach A: Call the OptionStrategies.jelly_roshort_jelly_rollll method with the details of each leg and then pass the result to the buy method.

    Select Language:
    short_jelly_roll = OptionStrategies.short_jelly_roll(self._symbol, strike, near_expiry, far_expiry)
    self.buy(short_jelly_roll, 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 Language:
    # Select the call and put contracts
    near_call = next(filter(lambda x: x.right == OptionRight.CALL and x.expiry == near_expiry, contracts))
    far_call = next(filter(lambda x: x.right == OptionRight.CALL and x.expiry == far_expiry, contracts))
    near_put = next(filter(lambda x: x.right == OptionRight.PUT and x.expiry == near_expiry, contracts))
    call_put = next(filter(lambda x: x.right == OptionRight.PUT and x.expiry == far_expiry, contracts))           
    
    legs = [
        Leg.create(near_call.symbol, 1),
        Leg.create(far_call.symbol, -1),
        Leg.create(near_put.symbol, -1),
        Leg.create(call_put.symbol, 1),
    ]
    self.combo_market_order(legs, 1)

Strategy Payoff

This is a delta-, gamma-, vega-, and theta-neutral strategy. The payoff is

CT1T1=(ST1K)+PT1T1=(KST1)+PayoffT1=(CT1T1CT1T0PT1T1+PT1T0+PT2T1PT2T0CT2T1+CT2T0)×mfee whereCT1T1=Market value of Call with expiry at T1 at time T1CT2T1=Market value of Call with expiry at T2 at time T1PT1T1=Market value of Put with expiry at T1 at time T1PT2T1=Market value of Put with expiry at T2 at time T1ST1=Underlying asset price at time T1K=Strike pricePayoffT1=Payout total at time T1CT1T0=Market value of Call with expiry at T1 when the trade openedCT2T0=Market value of Call with expiry at T2 when the trade openedPT1T0=Market value of Put with expiry at T2 when the trade openedPT2T0=Market value of Put with expiry at T2 when the trade openedm=Contract multiplierT1=Time T1 as the near expirationT2=Time T2 as the far expiration

The following chart shows the payoff at expiration:

short jelly roll strategy payoff

The payoff is dependent on the market prices of the options, but in theory, if assuming call-put parity exists, the expected payoff would be

PayoffT1=Payoffput calendar spreadPayoffcall calendar spread=DK×(T2T1)×r wherer=Continuous compounding interest rateD=Dividend payment during the life of the option

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:

AssetPrice at position open ($)Price at the first expiry ($)Strike ($)
Near-expiry Call22.8023.75832.50
Near-expiry Put18.1012.85832.50
Far-expiry Call19.5024.45832.50
Far-expiry Put23.5013.70832.50
Underlying Equity-843.2500-

Therefore, the payoff is

PayoffT1=(CT1T1CT10PT1T1+PT10+PT2T1PT20CT2T1+CT20)×mfee=(23.7522.8012.85+18.10+13.7023.5024.45+19.50)×1001.00×4=859.00

So, the strategy loses $859.

The following algorithm implements a short jelly roll Option strategy:

Select Language:
class ShortJellyRollOptionStrategy(QCAlgorithm):
    def initialize(self) -> None:
        self.set_start_date(2017, 4, 1)
        self.set_end_date(2017, 4, 23)
        self.set_cash(100000)
        
        option = self.add_option("GOOG", Resolution.MINUTE)
        self._symbol = option.symbol

        # set our strike/expiry filter for this option chain
        option.set_filter(lambda x: x.include_weeklys().jelly_roll(5.0, 30, 60))

    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
        strike = sorted([x.strike for x in chain], key=lambda x: abs(x - chain.underlying.price))[0]
        contracts = [x for x in chain if x.strike == strike]
        far_expiry = max([x.expiry for x in contracts])
        near_expiry = min([x.expiry for x in contracts])

        short_jelly_roll = OptionStrategies.short_jelly_roll(self._symbol, strike, near_expiry, far_expiry)
        self.buy(short_jelly_roll, 1)

You can also see our Videos. You can also get in touch with us via Discord.

Did you find this page helpful?

Contribute to the documentation: