← All backtests

LEAPS + Protective Spreads Strategy

GrowthThetaRules built-in

Own a ~2-year 0.90 Δ LEAPS. At +10% (when IV Rank is 20 or more), write a 0.30 Δ call on the LEAPS call's expiry: a spread with a locked worst case. Past the call, bank it and start again. A second LEAPS starts once the first is locked. Until each lock, sell income calls; always hold a cheap crash put.
Default settings, 2020 to Nov 4, 2026.

By stock and year

Select a row to chart it below. Shade is the gap to buying and holding that stock.

Unhedged: nothing protects against a crash beyond what the strategy itself holds.

Stock2020202120222023202420252026 YTDCAGRB&H CAGRMax drawdown
+45.6%+30.4+70.6%+43.7−14.3%+5.2+33.5%+9.3+28.1%+4.8+16.6%+0.2+13.4%+1.0 +25.8%+13.4%−17.6%
+111.1%+66.0+53.6%+26.8−38.0%−4.9+70.9%+17.1+20.8%−4.1+20.7%+0.5+10.2%−2.4 +28.3%+18.5%−40.7%
+49.0%+30.5+75.6%+62.3−15.2%+6.4+26.0%+10.9+22.6%+12.5+11.9%+0.5+17.8%+2.4 +24.6%+8.2%−32.3%
+93.5%−24.3+67.2%−58.0−38.3%+12.0+247.6%+8.6+150.8%−20.3+56.5%+17.6+19.2%+19.1 +66.1%+65.2%−61.1%
+119.5%+42.8+91.9%+58.1−11.6%+15.3+37.1%−11.1+22.1%−7.9+9.6%+1.0+22.2%−6.4 +36.3%+25.2%−21.5%
Gap to buy and hold: behind ahead · option prices before Feb 2024 are modelled, real quotes after

Growth of $10,000

Weekly closes on a log scale; the red shade is the strategy's drawdown from its last high. Returns are on the capital the strategy needed at its peak, with interest on idle cash. Hypothetical: how the backtests are made.

Rules · the when / do flow, read-only until you make a copy
Show flow

The strategy runs as 4 tracks side by side. Each day, the first rule whose conditions hold in each track acts.

1. LEAPS

One ~2-year LEAPS per lot: bought, rolled when old or deep in the money, banked past the lock

  1. PS1Buy the LEAPS call When: leg.leap{lot}.open == 0Do: buy call (as leap{lot}) delta leap_delta (0.9) dte leap_dte (730) dte_min leap_dte - 120 dte_max leap_dte + 120
  2. PS5The LEAPS call is getting old or deep in the money: swap it for a fresh one When: leg.leap{lot}.open == 1 and leg.lock_call{lot}.open == 0 and leg.leap{lot}.dte <= leap_roll_dte or leg.leap{lot}.delta >= leap_roll_delta and not (iv_rank > roll_ivr_max and leg.leap{lot}.dte > 90)Do: close income{lot}; close leap{lot}; buy call (as leap{lot}) delta leap_delta (0.9) dte leap_dte (730) dte_min leap_dte - 120 dte_max leap_dte + 120
  3. PS5The LEAPS call is getting old or deep in the money: close the spread, buy a fresh LEAPS When: leg.leap{lot}.open == 1 and leg.lock_call{lot}.open == 1 and leg.leap{lot}.dte <= leap_roll_dte or leg.leap{lot}.delta >= leap_roll_delta and not (iv_rank > roll_ivr_max and leg.leap{lot}.dte > 90)Do: close lock_call{lot}; close leap{lot}; buy call (as leap{lot}) delta leap_delta (0.9) dte leap_dte (730) dte_min leap_dte - 120 dte_max leap_dte + 120
  4. PS4Past the call: the spread is near its most, so bank it and start a fresh LEAPS When: leg.leap{lot}.open == 1 and leg.lock_call{lot}.open == 1 and spot >= leg.lock_call{lot}.strikeDo: close leap{lot}; close lock_call{lot}; buy call (as leap{lot}) delta leap_delta (0.9) dte leap_dte (730) dte_min leap_dte - 120 dte_max leap_dte + 120

2. Income call

Until the lock: a call sold above the LEAPS call, rolled near expiry or at half its credit

  1. PS8The income call is about to expire or has earned half: roll it to a new one When: leg.leap{lot}.open == 1 and leg.lock_call{lot}.open == 0 and spot < leg.leap{lot}.entry_spot * (1 + lock_step) or iv_rank < lock_ivr_min and leg.income{lot}.open == 1 and (leg.income{lot}.dte <= 3 and (leg.income{lot}.mark >= 0.05 or leg.income{lot}.strike < spot * 1.02)) or (income_take > 0 and leg.income{lot}.dte > 3 and leg.income{lot}.mark <= leg.income{lot}.cost * (1 - income_take))Do: roll income{lot} delta income_delta (0.2) dte income_dte (40) dte_min 21 dte_max 60 strike_min max(spot, leg.leap{lot}.strike) + 0.001 require new.mark >= 0.05 and new.strike > max(spot, leg.leap{lot}.strike)
  2. PS8The income call is done and no new one is worth selling: buy it back When: leg.leap{lot}.open == 1 and leg.lock_call{lot}.open == 0 and spot < leg.leap{lot}.entry_spot * (1 + lock_step) or iv_rank < lock_ivr_min and leg.income{lot}.open == 1 and (leg.income{lot}.dte <= 3 and (leg.income{lot}.mark >= 0.05 or leg.income{lot}.strike < spot * 1.02)) or (income_take > 0 and leg.income{lot}.dte > 3 and leg.income{lot}.mark <= leg.income{lot}.cost * (1 - income_take))Do: close income{lot}
  3. PS8No income call while waiting: sell one above the price and the LEAPS call When: leg.leap{lot}.open == 1 and leg.lock_call{lot}.open == 0 and spot < leg.leap{lot}.entry_spot * (1 + lock_step) or iv_rank < lock_ivr_min and leg.income{lot}.open == 0 and leg.leap{lot}.age >= 1Do: sell call (as income{lot}) delta income_delta (0.2) dte income_dte (40) dte_min 21 dte_max 60 strike_min max(spot, leg.leap{lot}.strike) + 0.001 require new.mark >= 0.05 and new.strike > max(spot, leg.leap{lot}.strike)

3. Lock call

At +lock_step: a call on the LEAPS call's expiry turns it into a spread with a locked floor

  1. PS2The stock is up enough and IV is not too low: write the lock call on the LEAPS call's expiry When: leg.leap{lot}.open == 1 and leg.lock_call{lot}.open == 0 and spot >= leg.leap{lot}.entry_spot * (1 + lock_step) and not (iv_rank < lock_ivr_min)Do: close income{lot}; sell call (as lock_call{lot}) delta lock_delta (0.3) expiry leg.leap{lot}.expiry strike_min max(spot, leg.leap{lot}.strike) + 0.001 require new.strike > max(spot, leg.leap{lot}.strike)

4. Crash put

Always hold a cheap far put under each LEAPS call

  1. PS7No LEAPS: sell the crash puts When: leg.leap{lot}.open == 0 and leg.hedge{lot}.open == 1 or leg.lhedge{lot}.open == 1Do: close hedge{lot}; close lhedge{lot}
  2. PS7The lock went on: swap the crash put for the locked size When: leg.lock_call{lot}.open == 1 and leg.hedge{lot}.open == 1 and locked_hedge_delta > 0Do: close hedge{lot}; buy put (as lhedge{lot}) delta locked_hedge_delta (0.05) dte hedge_dte (90) dte_min hedge_replace_dte + 10 strike_max spot - 0.001 require new.strike < spot
  3. PS7The lock went on: sell the crash put When: leg.lock_call{lot}.open == 1 and leg.hedge{lot}.open == 1Do: close hedge{lot}
  4. PS7The lock came off: swap back to the full crash put When: leg.lock_call{lot}.open == 0 and leg.lhedge{lot}.open == 1Do: close lhedge{lot}; buy put (as hedge{lot}) delta hedge_delta (0.05) dte hedge_dte (90) dte_min hedge_replace_dte + 10 strike_max spot - 0.001 require new.strike < spot
  5. PS7The crash put is old, has paid off, or is too far out: swap it for a fresh one When: leg.hedge{lot}.open == 1 and leg.hedge{lot}.dte <= hedge_replace_dte or leg.hedge{lot}.mark >= leg.hedge{lot}.cost * hedge_take_x or leg.hedge{lot}.delta >= hedge_take_delta or leg.hedge{lot}.delta < hedge_restrike * hedge_delta / hedge_deltaDo: close hedge{lot}; buy put (as hedge{lot}) delta hedge_delta (0.05) dte hedge_dte (90) dte_min hedge_replace_dte + 10 strike_max spot - 0.001 require new.strike < spot
  6. PS7The locked crash put is old, has paid off, or is too far out: swap it When: leg.lhedge{lot}.open == 1 and leg.lhedge{lot}.dte <= hedge_replace_dte or leg.lhedge{lot}.mark >= leg.lhedge{lot}.cost * hedge_take_x or leg.lhedge{lot}.delta >= hedge_take_delta or leg.lhedge{lot}.delta < hedge_restrike * locked_hedge_delta / hedge_deltaDo: close lhedge{lot}; buy put (as lhedge{lot}) delta locked_hedge_delta (0.05) dte hedge_dte (90) dte_min hedge_replace_dte + 10 strike_max spot - 0.001 require new.strike < spot
  7. PS7No crash put under an unlocked LEAPS: buy one When: hedge_delta > 0 and leg.leap{lot}.open == 1 and leg.lock_call{lot}.open == 0 and leg.hedge{lot}.open == 0Do: buy put (as hedge{lot}) delta hedge_delta (0.05) dte hedge_dte (90) dte_min hedge_replace_dte + 10 strike_max spot - 0.001 require new.strike < spot
  8. PS7No crash put under a locked LEAPS: buy the smaller one When: leg.lock_call{lot}.open == 1 and leg.lhedge{lot}.open == 0 and locked_hedge_delta > 0Do: buy put (as lhedge{lot}) delta locked_hedge_delta (0.05) dte hedge_dte (90) dte_min hedge_replace_dte + 10 strike_max spot - 0.001 require new.strike < spot

Parameters and defaults

Every named number in the rules can be changed in the Rule Builder. These are the defaults the backtest used.

ParameterDefault
leap_delta0.9
leap_dte730
leap_roll_dte270
leap_roll_delta0.95
roll_ivr_max70
lock_step0.1
lock_delta0.3
lock_ivr_min20
income_delta0.2
income_dte40
income_take0.5
hedge_delta0.05
locked_hedge_delta0.05
hedge_dte90
hedge_replace_dte30
hedge_take_x3
hedge_take_delta0.35
hedge_restrike0.03

Make a copy to change any of it and run your own backtest.

Trade log · every open, roll, assignment and close for SPY
Show trades

Loading…

growth

Own a ~2-year 0.90 Δ LEAPS. At +10% (when IV Rank is 20 or more), write a 0.30 Δ call on the LEAPS call's expiry: a spread with a locked worst case. Past the call, bank it and start again. A second LEAPS starts once the first is locked. Until each lock, sell income calls; always hold a cheap crash put.

What it is

Hold a two-year 0.90 delta LEAPS. When the stock is up 10% and IV Rank is at least 20, sell a 0.30 delta call on the LEAPS call's own expiry, turning it into a call spread with a locked worst case. Past the short call, take the profit and start again. A second LEAPS starts once the first is locked. Before each lock, sell income calls, and always hold a cheap crash put.

When to use it

Use it when you are bullish but want gains locked in stages rather than left open, and you are comfortable trading a capped upside per spread for a known worst case.

Maximum risk

Before a lock, the LEAPS call can lose most of its value in a fall (the crash put helps in sharp ones). Once locked as a spread, the loss is limited to the net cost of the spread, and the gain is capped at its width.

Questions

Why write the lock call on the LEAPS call's expiry?

Same-expiry calls make a true vertical spread: the worst case is fixed and does not depend on how the two legs' time values drift.

Why wait for IV Rank 20 before locking?

Selling the lock call when implied volatility is very low pays little for capping the upside. Waiting for some volatility gets a better price.

What does the second LEAPS do?

Once the first LEAPS is locked, its upside is capped. A second LEAPS restores open-ended upside while the first one runs to its target.

Related strategies