---
title: "Long Jelly Roll"
description: "A Long Jelly Roll is an arbitrage strategy that combines a long call calendar spread and a short put calendar spread"
canonical: https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/option-strategies/long-jelly-roll
markdown: https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/option-strategies/long-jelly-roll.md
language: python
source: https://github.com/QuantConnect/Documentation/tree/master/03%20Writing%20Algorithms/22%20Trading%20and%20Orders/08%20Option%20Strategies/32%20Long%20Jelly%20Roll
modified: 2026-10-31
index: https://www.quantconnect.com/llms.txt
---

# Long Jelly Roll

[Documentation](https://www.quantconnect.com/docs/v2.md) > [Writing Algorithms](https://www.quantconnect.com/docs/v2/writing-algorithms.md) > Trading and Orders > [Option Strategies](https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/option-strategies.md) > Long Jelly Roll

> Code examples are shown in Python. Append `?lang=csharp` to this URL for the C# version of this page.

## Introduction

A long **Jelly Roll**, or simply **Roll**, is a combination of a [long call calendar spread](https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/option-strategies/long-call-calendar-spread.md) and a [short put calendar spread](https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/option-strategies/short-put-calendar-spread.md). It consists of buying a put and selling a call of the same expiry, as well as buying a call and selling a put with a further expiry, where all of the contracts have the same strike price. 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](https://www.quantconnect.com/docs/v2/writing-algorithms/securities/asset-classes/equity-options/greeks-and-implied-volatility/indicators.md#04-Delta)-, [gamma](https://www.quantconnect.com/docs/v2/writing-algorithms/securities/asset-classes/equity-options/greeks-and-implied-volatility/indicators.md#05-Gamma)-, [vega](https://www.quantconnect.com/docs/v2/writing-algorithms/securities/asset-classes/equity-options/greeks-and-implied-volatility/indicators.md#06-Vega)-, and [theta](https://www.quantconnect.com/docs/v2/writing-algorithms/securities/asset-classes/equity-options/greeks-and-implied-volatility/indicators.md#07-Theta)-neutral strategy, but sensitive to [rho](https://www.quantconnect.com/docs/v2/writing-algorithms/securities/asset-classes/equity-options/greeks-and-implied-volatility/indicators.md#08-Rho) (interest rate) and phi ([dividend yield](https://www.quantconnect.com/docs/v2/writing-algorithms/reality-modeling/dividend-yield/key-concepts.md)).

## Implementation

Follow these steps to implement the long jelly roll strategy:

1. In the `initialize` method, set the start date, set the end date, and create an [Option universe](https://www.quantconnect.com/docs/v2/writing-algorithms/universes/equity-options.md).
   ```python
   def initialize(self) -> None:
       self.set_start_date(2024, 9, 1)
       self.set_end_date(2024, 12, 31)
       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.jelly_roll(5.0, 30, 60))
   ```
2. In the `on_data` method, select the expiry and strikes of the contracts in the strategy legs.
   ```python
   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])
   ```
3. In the `on_data` method, select the contracts and place the order.
   **Approach A:** Call the `OptionStrategies.jelly_roll` method with the details of each leg and then pass the result to the `buy` method.
   ```python
   jelly_roll = OptionStrategies.jelly_roll(self._symbol, strike, near_expiry, far_expiry)
   self.buy(jelly_roll, 1)
   ```
   **Approach B:** Create a list of `Leg` objects and then call the [combo_market_order](https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/order-types/combo-market-orders.md), [combo_limit_order](https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/order-types/combo-limit-orders.md), or [combo_leg_limit_order](https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/order-types/combo-leg-limit-orders.md) method.
   ```python
   # 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

$$ \begin{array}{rcll} C_{T_1}^{T_1} & = & (S_{T_1} - K)^{+}\\ P_{T_1}^{T_1} & = & (K - S_{T_1})^{+}\\ Payoff_{T_1} & = & (P_{T_1}^{T_1} - P_{T_0}^{T_1} - C_{T_1}^{T_1} + C_{T_0}^{T_1} + C_{T_1}^{T_2} - C_{T_0}^{T_2} - P_{T_1}^{T_2} + P_{T_0}^{T_2})\times m - fee\\ \end{array} $$ $$ \begin{array}{rcll} \textrm{where} & C_{T_1}^{T_1} & = & \textrm{Market value of Call with expiry at T1 at time T1}\\ & C_{T_1}^{T_2} & = & \textrm{Market value of Call with expiry at T2 at time T1}\\ & P_{T_1}^{T_1} & = & \textrm{Market value of Put with expiry at T1 at time T1}\\ & P_{T_1}^{T_2} & = & \textrm{Market value of Put with expiry at T2 at time T1}\\ & S_{T_1} & = & \textrm{Underlying asset price at time T1}\\ & K & = & \textrm{Strike price}\\ & Payoff_T & = & \textrm{Payout total at time T}\\ & C_{T_0}^{T_1} & = & \textrm{Market value of Call with expiry at T1 when the trade opened}\\ & C_{T_0}^{T_2} & = & \textrm{Market value of Call with expiry at T2 when the trade opened}\\ & P_{T_0}^{T_1} & = & \textrm{Market value of Put with expiry at T2 when the trade opened}\\ & P_{T_0}^{T_2} & = & \textrm{Market value of Put with expiry at T2 when the trade opened}\\ & m & = & \textrm{Contract multiplier}\\ & T_1 & = & \textrm{Time T1 as the near expiration}\\ & T_2 & = & \textrm{Time T2 as the far expiration} \end{array} $$

In this example, you traded near-expiry and far-expiry calls and puts at a strike of $832.50.

The following chart shows the payoff at expiration:

![Long Jelly Roll strategy payoff at expiration](https://cdn.quantconnect.com/docs/i/writing-algorithms/trading-and-orders/option-strategies/long-jelly-roll.png)

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

$$ \begin{array}{rcll} Payoff_{T_1} & = & Payoff_{\textrm{call calendar spread}} - Payoff_{\textrm{put calendar spread}}\\ & = & K \times (T_2 - T_1) \times r - D \end{array} $$ $$ \begin{array}{rcll} \textrm{where} & r & = & \textrm{Continuous compounding interest rate}\\ & D & = & \textrm{Dividend payment during the life of the option} \end{array} $$

If the Option is American Option, there is a risk of [early assignment](https://www.quantconnect.com/docs/v2/writing-algorithms/reality-modeling/options-models/assignment.md) on the contracts you sell.

## Example

The following table shows the price details of the assets in the algorithm:

| Asset | Price at position open ($) | Price at the first expiry ($) | Strike ($) |
| --- | --- | --- | --- |
| Near-expiry Call | 18.50 | 23.75 | 832.50 |
| Near-expiry Put | 22.50 | 12.85 | 832.50 |
| Far-expiry Call | 23.90 | 24.45 | 832.50 |
| Far-expiry Put | 19.00 | 13.70 | 832.50 |
| Underlying Equity | - | 843.2500 | - |

Therefore, the payoff is

$$ \begin{array}{rcll} Payoff_{T_1} & = & (P_{T_1}^{T_1} - P_0^{T_1} - C_{T_1}^{T_1} + C_0^{T_1} + C_{T_1}^{T_2} - C_0^{T_2} - P_{T_1}^{T_2} + P_0^{T_2})\times m - fee\\ & = & (12.85 - 22.50 - 23.75 + 18.50 + 24.45 - 23.90 - 13.70 + 19.00)\times100 - 1.00\times4\\ & = & -909.00\\ \end{array} $$

So, the strategy loses $909.

The following algorithm implements a long jelly roll Option strategy:

```python
class JellyRollOptionStrategy(QCAlgorithm):
    def initialize(self) -> None:
        self.set_start_date(2024, 9, 1)
        self.set_end_date(2024, 12, 31)
        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.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])

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