Option Strategies

Protective Collar


A Protective Collar is an options strategy that involves the combination a Covered Call and a long put (protective put), with a lower strike price than the shorted call. This could provide additional protection to limit the drawdown when the underlying price drop too deep when compared to the covered call.

This is a limited-profit-limited-loss strategy. The payoff is as follows:



$$Payoff=(Price^{T=expiry}_{underlying}-Price^{T=trade\ open}_{underlying}-Payoff_{call}+Payoff_{put}\\ +credit\ received_{call}-debit\ paid_{put})\times multiplier-commissions$$

protective collar strategy payoff

The maximum profit is the call strike minus underlying price, plus the net premium (or minus the net debit paid) of option trades after commission when opening the trade, where the underlying price has price the same as or above the strike price of the call at expiration.

The maximum profit is the underlying price minus put strike, plus the net premium (or minus the net debit paid) of option trades after commission when opening the trade, where the underlying price has price the same as or below the strike price of the put at expiration.

If the option is American option, there will be a risk of early assignment on the shorted option.


Follow these steps to implement the protective collar strategy.

  1. In the Initialize method, set the start date, end date, cash, and Option universe.
  2. private Symbol _symbol;
    public override void Initialize()
        SetStartDate(2017, 4, 1);
        SetEndDate(2017, 4, 30);
        var option = AddOption("GOOG", Resolution.Minute);
        _symbol = option.Symbol;
        option.SetFilter(universe => universe.Strikes(-10, 10)
                                                .Expiration(TimeSpan.FromDays(0), TimeSpan.FromDays(30)));
    def Initialize(self) -> None:
        self.SetStartDate(2017, 4, 1)
        self.SetEndDate(2017, 4, 30)
        equity = self.AddEquity("GOOG", Resolution.Minute)
        option = self.AddOption("GOOG", Resolution.Minute)
        self.symbol = option.Symbol
        # set our strike/expiry filter for this option chain
        option.SetFilter(-10, +10, timedelta(0), timedelta(30))
  3. In the OnData method, select the Option contracts.
  4. public override void OnData(Slice slice)
        if (Portfolio.Invested) return;
        // Get the OptionChain of the symbol
        var chain = slice.OptionChains.get(_symbol, null);
        if (chain == null || chain.Count() == 0) return;
        // sort the optionchain by expiration date and choose the furthest date
        var expiry = chain.OrderBy(x => x.Expiry).Last().Expiry;
        // filter the call and put options from the contracts expires on that date
        var calls = chain.Where(x => x.Expiry == expiry && x.Right == OptionRight.Call);
        var puts = chain.Where(x => x.Expiry == expiry && x.Right == OptionRight.Put);
        if (calls.Count() == 0 || puts.Count() == 0) return;
        // sort the contracts according to their strike prices
        var callContracts = calls.OrderBy(x => x.Strike);
        var putContracts = puts.OrderBy(x => x.Strike);
        // select the furthest OTM contracts
        var call = callContracts.Last();
        var put = putContracts.First();
    def OnData(self, slice: Slice) -> None:
        # avoid extra orders
        if self.Portfolio.Invested: return
        # Get the OptionChain of the self.symbol
        chain = slice.OptionChains.get(self.symbol, None)
        if not chain: return
        # choose the furthest expiration date within 30 days from now on
        expiry = sorted(chain, key = lambda x: x.Expiry)[-1].Expiry
        # filter the call options contracts
        call = [x for x in chain if x.Right == OptionRight.Call and x.Expiry == expiry]
        # filter the put options contracts
        put = [x for x in chain if x.Right == OptionRight.Put and x.Expiry == expiry]
        if not call or not put: return
        self.otm_call = sorted(call, key = lambda x: x.Strike)[-1]
        self.otm_put = sorted(put, key = lambda x: x.Strike)[0]
  5. In the OnData method, submit the order.
  6.     Sell(call.Symbol, 1);
        Buy(put.Symbol, 1);
        Buy(_equity, 100);
    self.Sell(self.otm_call.Symbol, 1) # sell the OTM call
    self.Buy(self.otm_put.Symbol, 1) # buy the OTM put
    self.Buy("GOOG", 100)     # buy 100 shares of the underlying stock


In this algorithm, we've realised the below payout at expiration (2017-4-22).

Price of call$ 2.85
Price of put$ 6.00
Price of underlying stock when trade open$ 833.17
Strike of call$ 845.00
Strike of put$ 822.50
Price of underlying at expiration$ 843.19
Commission per trade$ 1.00



So, the strategy gains $684.


Backtest using SetFilter

Backtest using OptionChainProvider

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: