/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.Linq; using QuantConnect.Securities.Option.StrategyMatcher; namespace QuantConnect.Securities.Option { /// /// Provides methods for creating popular instances. /// These strategies can be directly bought and sold via: /// QCAlgorithm.Buy(OptionStrategy strategy, int quantity) /// QCAlgorithm.Sell(OptionStrategy strategy, int quantity) /// /// See also /// public static class OptionStrategies { /// /// Symbol properties database to use to get contract multipliers /// private static SymbolPropertiesDatabase _symbolPropertiesDatabase = SymbolPropertiesDatabase.FromDataFolder(); /// /// Creates a Covered Call strategy that consists of selling one call contract and buying 1 lot of the underlying. /// /// Option symbol /// The strike price for the call option contract /// The expiration date for the call option contract /// Option strategy specification public static OptionStrategy CoveredCall(Symbol canonicalOption, decimal strike, DateTime expiration) { CheckCanonicalOptionSymbol(canonicalOption, "CoveredCall"); CheckExpirationDate(expiration, "CoveredCall", nameof(expiration)); var underlyingQuantity = (int)_symbolPropertiesDatabase.GetSymbolProperties(canonicalOption.ID.Market, canonicalOption, canonicalOption.SecurityType, "").ContractMultiplier; return new OptionStrategy { Name = OptionStrategyDefinitions.CoveredCall.Name, Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = new List { new OptionStrategy.OptionLegData { Right = OptionRight.Call, Strike = strike, Quantity = -1, Expiration = expiration } }, UnderlyingLegs = new List { new OptionStrategy.UnderlyingLegData { Quantity = underlyingQuantity, Symbol = canonicalOption.Underlying } } }; } /// /// Creates a Protective Call strategy that consists of buying one call contract and selling 1 lot of the underlying. /// /// Option symbol /// The strike price for the call option contract /// The expiration date for the call option contract /// Option strategy specification public static OptionStrategy ProtectiveCall(Symbol canonicalOption, decimal strike, DateTime expiration) { // Since a protective call is an inverted covered call, we can just use the CoveredCall method and invert the legs return InvertStrategy(CoveredCall(canonicalOption, strike, expiration), OptionStrategyDefinitions.ProtectiveCall.Name); } /// /// Creates a Covered Put strategy that consists of selling 1 put contract and 1 lot of the underlying. /// /// Option symbol /// The strike price for the put option contract /// The expiration date for the put option contract /// Option strategy specification public static OptionStrategy CoveredPut(Symbol canonicalOption, decimal strike, DateTime expiration) { CheckCanonicalOptionSymbol(canonicalOption, "CoveredPut"); CheckExpirationDate(expiration, "CoveredPut", nameof(expiration)); var underlyingQuantity = -(int)_symbolPropertiesDatabase.GetSymbolProperties(canonicalOption.ID.Market, canonicalOption, canonicalOption.SecurityType, "").ContractMultiplier; return new OptionStrategy { Name = OptionStrategyDefinitions.CoveredPut.Name, Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = new List { new OptionStrategy.OptionLegData { Right = OptionRight.Put, Strike = strike, Quantity = -1, Expiration = expiration } }, UnderlyingLegs = new List { new OptionStrategy.UnderlyingLegData { Quantity = underlyingQuantity, Symbol = canonicalOption.Underlying } } }; } /// /// Creates a Protective Put strategy that consists of buying 1 put contract and 1 lot of the underlying. /// /// Option symbol /// The strike price for the put option contract /// The expiration date for the put option contract /// Option strategy specification public static OptionStrategy ProtectivePut(Symbol canonicalOption, decimal strike, DateTime expiration) { // Since a protective put is an inverted covered put, we can just use the CoveredPut method and invert the legs return InvertStrategy(CoveredPut(canonicalOption, strike, expiration), OptionStrategyDefinitions.ProtectivePut.Name); } /// /// Creates a Protective Collar strategy that consists of buying 1 put contract and 1 lot of the underlying. /// /// Option symbol /// The strike price for the call option contract /// The strike price for the put option contract /// Option expiration date /// Option strategy specification public static OptionStrategy ProtectiveCollar(Symbol canonicalOption, decimal callStrike, decimal putStrike, DateTime expiration) { if (callStrike < putStrike) { throw new ArgumentException("ProtectiveCollar: callStrike must be greater than putStrike", $"{nameof(callStrike)}, {nameof(putStrike)}"); } // Since a protective collar is a combination of protective put and covered call var coveredCall = CoveredCall(canonicalOption, callStrike, expiration); var protectivePut = ProtectivePut(canonicalOption, putStrike, expiration); return new OptionStrategy { Name = OptionStrategyDefinitions.ProtectiveCollar.Name, Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = coveredCall.OptionLegs.Concat(protectivePut.OptionLegs).ToList(), UnderlyingLegs = coveredCall.UnderlyingLegs // only 1 lot of long stock position }; } /// /// Creates a Conversion strategy that consists of buying 1 put contract, 1 lot of the underlying and selling 1 call contract. /// Put and call must have the same expiration date, underlying (multiplier), and strike price. /// /// Option symbol /// The strike price for the call and put option contract /// Option expiration date /// Option strategy specification public static OptionStrategy Conversion(Symbol canonicalOption, decimal strike, DateTime expiration) { var strategy = ProtectiveCollar(canonicalOption, strike, strike, expiration); strategy.Name = OptionStrategyDefinitions.Conversion.Name; return strategy; } /// /// Creates a Reverse Conversion strategy that consists of buying 1 put contract and 1 lot of the underlying. /// /// Option symbol /// The strike price for the put option contract /// Option expiration date /// Option strategy specification public static OptionStrategy ReverseConversion(Symbol canonicalOption, decimal strike, DateTime expiration) { // Since a reverse conversion is an inverted conversion, we can just use the Conversion method and invert the legs return InvertStrategy(Conversion(canonicalOption, strike, expiration), OptionStrategyDefinitions.ReverseConversion.Name); } /// /// Creates a Naked Call strategy that consists of selling 1 call contract. /// /// Option symbol /// The strike price for the call option contract /// The expiration date for the call option contract /// Option strategy specification public static OptionStrategy NakedCall(Symbol canonicalOption, decimal strike, DateTime expiration) { CheckCanonicalOptionSymbol(canonicalOption, "NakedCall"); CheckExpirationDate(expiration, "NakedCall", nameof(expiration)); return new OptionStrategy { Name = OptionStrategyDefinitions.NakedCall.Name, Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = new List { new OptionStrategy.OptionLegData { Right = OptionRight.Call, Strike = strike, Quantity = -1, Expiration = expiration } } }; } /// /// Creates a Naked Put strategy that consists of selling 1 put contract. /// /// Option symbol /// The strike price for the put option contract /// The expiration date for the put option contract /// Option strategy specification public static OptionStrategy NakedPut(Symbol canonicalOption, decimal strike, DateTime expiration) { CheckCanonicalOptionSymbol(canonicalOption, "NakedPut"); CheckExpirationDate(expiration, "NakedPut", nameof(expiration)); return new OptionStrategy { Name = OptionStrategyDefinitions.NakedPut.Name, Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = new List { new OptionStrategy.OptionLegData { Right = OptionRight.Put, Strike = strike, Quantity = -1, Expiration = expiration } } }; } /// /// Method creates new Bear Call Spread strategy, that consists of two calls with the same expiration but different strikes. /// The strike price of the short call is below the strike of the long call. This is a credit spread. /// /// Option symbol /// The strike price of the short call /// The strike price of the long call /// Option expiration date /// Option strategy specification public static OptionStrategy BearCallSpread( Symbol canonicalOption, decimal leg1Strike, decimal leg2Strike, DateTime expiration ) { CheckCanonicalOptionSymbol(canonicalOption, "BearCallSpread"); CheckExpirationDate(expiration, "BearCallSpread", nameof(expiration)); if (leg1Strike >= leg2Strike) { throw new ArgumentException("BearCallSpread: leg1Strike must be less than leg2Strike", $"{nameof(leg1Strike)}, {nameof(leg2Strike)}"); } return new OptionStrategy { Name = OptionStrategyDefinitions.BearCallSpread.Name, Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = new List { new OptionStrategy.OptionLegData { Right = OptionRight.Call, Strike = leg1Strike, Quantity = -1, Expiration = expiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Call, Strike = leg2Strike, Quantity = 1, Expiration = expiration } } }; } /// /// Method creates new Bear Put Spread strategy, that consists of two puts with the same expiration but different strikes. /// The strike price of the short put is below the strike of the long put. This is a debit spread. /// /// Option symbol /// The strike price of the long put /// The strike price of the short put /// Option expiration date /// Option strategy specification public static OptionStrategy BearPutSpread( Symbol canonicalOption, decimal leg1Strike, decimal leg2Strike, DateTime expiration ) { CheckCanonicalOptionSymbol(canonicalOption, "BearPutSpread"); CheckExpirationDate(expiration, "BearPutSpread", nameof(expiration)); if (leg1Strike <= leg2Strike) { throw new ArgumentException("BearPutSpread: leg1Strike must be greater than leg2Strike", $"{nameof(leg1Strike)}, {nameof(leg2Strike)}"); } return new OptionStrategy { Name = OptionStrategyDefinitions.BearPutSpread.Name, Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = new List { new OptionStrategy.OptionLegData { Right = OptionRight.Put, Strike = leg1Strike, Quantity = 1, Expiration = expiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Put, Strike = leg2Strike, Quantity = -1, Expiration = expiration } } }; } /// /// Method creates new Bull Call Spread strategy, that consists of two calls with the same expiration but different strikes. /// The strike price of the short call is higher than the strike of the long call. This is a debit spread. /// /// Option symbol /// The strike price of the long call /// The strike price of the short call /// Option expiration date /// Option strategy specification public static OptionStrategy BullCallSpread( Symbol canonicalOption, decimal leg1Strike, decimal leg2Strike, DateTime expiration ) { CheckCanonicalOptionSymbol(canonicalOption, "BullCallSpread"); CheckExpirationDate(expiration, "BullCallSpread", nameof(expiration)); if (leg1Strike >= leg2Strike) { throw new ArgumentException("BullCallSpread: leg1Strike must be less than leg2Strike", $"{nameof(leg1Strike)}, {nameof(leg2Strike)}"); } return new OptionStrategy { Name = OptionStrategyDefinitions.BullCallSpread.Name, Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = new List { new OptionStrategy.OptionLegData { Right = OptionRight.Call, Strike = leg1Strike, Quantity = 1, Expiration = expiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Call, Strike = leg2Strike, Quantity = -1, Expiration = expiration } } }; } /// /// Method creates new Bull Put Spread strategy, that consists of two puts with the same expiration but different strikes. /// The strike price of the short put is above the strike of the long put. This is a credit spread. /// /// Option symbol /// The strike price of the short put /// The strike price of the long put /// Option expiration date /// Option strategy specification public static OptionStrategy BullPutSpread( Symbol canonicalOption, decimal leg1Strike, decimal leg2Strike, DateTime expiration ) { CheckCanonicalOptionSymbol(canonicalOption, "BullPutSpread"); CheckExpirationDate(expiration, "BullPutSpread", nameof(expiration)); if (leg1Strike <= leg2Strike) { throw new ArgumentException("BullPutSpread: leg1Strike must be greater than leg2Strike", $"{nameof(leg1Strike)}, {nameof(leg2Strike)}"); } return new OptionStrategy { Name = OptionStrategyDefinitions.BullPutSpread.Name, Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = new List { new OptionStrategy.OptionLegData { Right = OptionRight.Put, Strike = leg1Strike, Quantity = -1, Expiration = expiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Put, Strike = leg2Strike, Quantity = 1, Expiration = expiration } } }; } /// /// Method creates new Straddle strategy, that is a combination of buying a call and buying a put, both with the same strike price and expiration. /// /// Option symbol /// The strike price of the both legs /// Option expiration date /// Option strategy specification public static OptionStrategy Straddle(Symbol canonicalOption, decimal strike, DateTime expiration) { CheckCanonicalOptionSymbol(canonicalOption, "Straddle"); CheckExpirationDate(expiration, "Straddle", nameof(expiration)); return new OptionStrategy { Name = OptionStrategyDefinitions.Straddle.Name, Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = new List { new OptionStrategy.OptionLegData { Right = OptionRight.Call, Strike = strike, Quantity = 1, Expiration = expiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Put, Strike = strike, Quantity = 1, Expiration = expiration } } }; } /// /// Creates a Short Straddle strategy that consists of selling a call and a put, both with the same strike price and expiration. /// /// Option symbol /// The strike price for the option contracts /// The expiration date for the option contracts /// Option strategy specification public static OptionStrategy ShortStraddle(Symbol canonicalOption, decimal strike, DateTime expiration) { // Since a short straddle is an inverted straddle, we can just use the Straddle method and invert the legs return InvertStrategy(Straddle(canonicalOption, strike, expiration), OptionStrategyDefinitions.ShortStraddle.Name); } /// /// Method creates new Strangle strategy, that buying a call option and a put option with the same expiration date /// The strike price of the call is above the strike of the put. /// /// Option symbol /// The strike price of the long call /// The strike price of the long put /// Option expiration date /// Option strategy specification public static OptionStrategy Strangle( Symbol canonicalOption, decimal callLegStrike, decimal putLegStrike, DateTime expiration ) { CheckCanonicalOptionSymbol(canonicalOption, "Strangle"); CheckExpirationDate(expiration, "Strangle", nameof(expiration)); if (callLegStrike <= putLegStrike) { throw new ArgumentException($"Strangle: {nameof(callLegStrike)} must be greater than {nameof(putLegStrike)}", $"{nameof(callLegStrike)}, {nameof(putLegStrike)}"); } return new OptionStrategy { Name = OptionStrategyDefinitions.Strangle.Name, Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = new List { new OptionStrategy.OptionLegData { Right = OptionRight.Call, Strike = callLegStrike, Quantity = 1, Expiration = expiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Put, Strike = putLegStrike, Quantity = 1, Expiration = expiration } } }; } /// /// Creates a Short Strangle strategy that consists of selling a call and a put, with the same expiration date and /// the call strike being above the put strike. /// /// Option symbol /// The strike price of the short call /// The strike price of the short put /// Option expiration date /// Option strategy specification public static OptionStrategy ShortStrangle(Symbol canonicalOption, decimal callLegStrike, decimal putLegStrike, DateTime expiration) { // Since a short strangle is an inverted strangle, we can just use the Strangle method and invert the legs return InvertStrategy(Strangle(canonicalOption, callLegStrike, putLegStrike, expiration), OptionStrategyDefinitions.ShortStrangle.Name); } /// /// Method creates new Call Butterfly strategy, that consists of two short calls at a middle strike, and one long call each at a lower and upper strike. /// The upper and lower strikes must both be equidistant from the middle strike. /// /// Option symbol /// The upper strike price of the long call /// The middle strike price of the two short calls /// The lower strike price of the long call /// Option expiration date /// Option strategy specification public static OptionStrategy CallButterfly( Symbol canonicalOption, decimal higherStrike, decimal middleStrike, decimal lowerStrike, DateTime expiration ) { CheckCanonicalOptionSymbol(canonicalOption, "CallButterfly"); CheckExpirationDate(expiration, "CallButterfly", nameof(expiration)); if (higherStrike <= middleStrike || lowerStrike >= middleStrike || higherStrike - middleStrike != middleStrike - lowerStrike) { throw new ArgumentException("ButterflyCall: upper and lower strikes must both be equidistant from the middle strike", $"{nameof(higherStrike)}, {nameof(middleStrike)}, {nameof(lowerStrike)}"); } return new OptionStrategy { Name = OptionStrategyDefinitions.ButterflyCall.Name, Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = new List { new OptionStrategy.OptionLegData { Right = OptionRight.Call, Strike = higherStrike, Quantity = 1, Expiration = expiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Call, Strike = middleStrike, Quantity = -2, Expiration = expiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Call, Strike = lowerStrike, Quantity = 1, Expiration = expiration } } }; } /// /// Creates a new Butterfly Call strategy that consists of two short calls at a middle strike, /// and one long call each at a lower and upper strike. /// The upper and lower strikes must both be equidistant from the middle strike. /// /// Option symbol /// The upper strike price of the long call /// The middle strike price of the two short calls /// The lower strike price of the long call /// Option expiration date /// Option strategy specification /// Alias for public static OptionStrategy ButterflyCall(Symbol canonicalOption, decimal higherStrike, decimal middleStrike, decimal lowerStrike, DateTime expiration) { return CallButterfly(canonicalOption, higherStrike, middleStrike, lowerStrike, expiration); } /// /// Creates a new Butterfly Call strategy that consists of two long calls at a middle strike, /// and one short call each at a lower and upper strike. /// The upper and lower strikes must both be equidistant from the middle strike. /// /// Option symbol /// The upper strike price of the short call /// The middle strike price of the two long calls /// The lower strike price of the short call /// Option expiration date /// Option strategy specification public static OptionStrategy ShortButterflyCall(Symbol canonicalOption, decimal higherStrike, decimal middleStrike, decimal lowerStrike, DateTime expiration) { // Since a short butterfly call is an inverted butterfly call, we can just use the ButterflyCall method and invert the legs return InvertStrategy(ButterflyCall(canonicalOption, higherStrike, middleStrike, lowerStrike, expiration), OptionStrategyDefinitions.ShortButterflyCall.Name); } /// /// Method creates new Put Butterfly strategy, that consists of two short puts at a middle strike, and one long put each at a lower and upper strike. /// The upper and lower strikes must both be equidistant from the middle strike. /// /// Option symbol /// The upper strike price of the long put /// The middle strike price of the two short puts /// The lower strike price of the long put /// Option expiration date /// Option strategy specification public static OptionStrategy PutButterfly( Symbol canonicalOption, decimal higherStrike, decimal middleStrike, decimal lowerStrike, DateTime expiration ) { CheckCanonicalOptionSymbol(canonicalOption, "PutButterfly"); CheckExpirationDate(expiration, "PutButterfly", nameof(expiration)); if (higherStrike <= middleStrike || lowerStrike >= middleStrike || higherStrike - middleStrike != middleStrike - lowerStrike) { throw new ArgumentException("ButterflyPut: upper and lower strikes must both be equidistant from the middle strike", $"{nameof(higherStrike)}, {nameof(middleStrike)}, {nameof(lowerStrike)}"); } return new OptionStrategy { Name = OptionStrategyDefinitions.ButterflyPut.Name, Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = new List { new OptionStrategy.OptionLegData { Right = OptionRight.Put, Strike = higherStrike, Quantity = 1, Expiration = expiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Put, Strike = middleStrike, Quantity = -2, Expiration = expiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Put, Strike = lowerStrike, Quantity = 1, Expiration = expiration } } }; } /// /// Creates a new Butterfly Put strategy that consists of two short puts at a middle strike, /// and one long put each at a lower and upper strike. /// The upper and lower strikes must both be equidistant from the middle strike. /// /// Option symbol /// The upper strike price of the long put /// The middle strike price of the two short puts /// The lower strike price of the long put /// Option expiration date /// Option strategy specification /// Alias for public static OptionStrategy ButterflyPut(Symbol canonicalOption, decimal higherStrike, decimal middleStrike, decimal lowerStrike, DateTime expiration) { return PutButterfly(canonicalOption, higherStrike, middleStrike, lowerStrike, expiration); } /// /// Creates a new Butterfly Put strategy that consists of two long puts at a middle strike, /// and one short put each at a lower and upper strike. /// The upper and lower strikes must both be equidistant from the middle strike. /// /// Option symbol /// The upper strike price of the short put /// The middle strike price of the two long puts /// The lower strike price of the short put /// Option expiration date /// Option strategy specification public static OptionStrategy ShortButterflyPut(Symbol canonicalOption, decimal higherStrike, decimal middleStrike, decimal lowerStrike, DateTime expiration) { // Since a short butterfly put is an inverted butterfly put, we can just use the ButterflyPut method and invert the legs return InvertStrategy(ButterflyPut(canonicalOption, higherStrike, middleStrike, lowerStrike, expiration), OptionStrategyDefinitions.ShortButterflyPut.Name); } /// /// Creates new Call Calendar Spread strategy which consists of a short and a long call /// with the same strikes but with the long call having a further expiration date. /// /// Option symbol /// The strike price of the both legs /// Near expiration date for the short option /// Far expiration date for the long option /// Option strategy specification public static OptionStrategy CallCalendarSpread(Symbol canonicalOption, decimal strike, DateTime nearExpiration, DateTime farExpiration) { CheckCanonicalOptionSymbol(canonicalOption, "CallCalendarSpread"); CheckExpirationDate(nearExpiration, "CallCalendarSpread", nameof(nearExpiration)); CheckExpirationDate(farExpiration, "CallCalendarSpread", nameof(farExpiration)); if (nearExpiration >= farExpiration) { throw new ArgumentException("CallCalendarSpread: near expiration must be less than far expiration", $"{nameof(nearExpiration)}, {nameof(farExpiration)}"); } return new OptionStrategy { Name = OptionStrategyDefinitions.CallCalendarSpread.Name, Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = new List { new OptionStrategy.OptionLegData { Right = OptionRight.Call, Strike = strike, Quantity = -1, Expiration = nearExpiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Call, Strike = strike, Quantity = 1, Expiration = farExpiration } } }; } /// /// Creates new Short Call Calendar Spread strategy which consists of a short and a long call /// with the same strikes but with the short call having a further expiration date. /// /// Option symbol /// The strike price of the both legs /// Near expiration date for the long option /// Far expiration date for the short option /// Option strategy specification public static OptionStrategy ShortCallCalendarSpread(Symbol canonicalOption, decimal strike, DateTime nearExpiration, DateTime farExpiration) { // Since a short call calendar spread is an inverted call calendar, we can just use the CallCalendarSpread method and invert the legs return InvertStrategy(CallCalendarSpread(canonicalOption, strike, nearExpiration, farExpiration), OptionStrategyDefinitions.ShortCallCalendarSpread.Name); } /// /// Creates new Put Calendar Spread strategy which consists of a short and a long put /// with the same strikes but with the long put having a further expiration date. /// /// Option symbol /// The strike price of the both legs /// Near expiration date for the short option /// Far expiration date for the long option /// Option strategy specification public static OptionStrategy PutCalendarSpread(Symbol canonicalOption, decimal strike, DateTime nearExpiration, DateTime farExpiration) { CheckCanonicalOptionSymbol(canonicalOption, "PutCalendarSpread"); CheckExpirationDate(nearExpiration, "PutCalendarSpread", nameof(nearExpiration)); CheckExpirationDate(farExpiration, "PutCalendarSpread", nameof(farExpiration)); if (nearExpiration >= farExpiration) { throw new ArgumentException("PutCalendarSpread: near expiration must be less than far expiration", $"{nameof(nearExpiration)}, {nameof(farExpiration)}"); } return new OptionStrategy { Name = OptionStrategyDefinitions.PutCalendarSpread.Name, Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = new List { new OptionStrategy.OptionLegData { Right = OptionRight.Put, Strike = strike, Quantity = -1, Expiration = nearExpiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Put, Strike = strike, Quantity = 1, Expiration = farExpiration } } }; } /// /// Creates new Short Put Calendar Spread strategy which consists of a short and a long put /// with the same strikes but with the short put having a further expiration date. /// /// Option symbol /// The strike price of the both legs /// Near expiration date for the long option /// Far expiration date for the short option /// Option strategy specification public static OptionStrategy ShortPutCalendarSpread(Symbol canonicalOption, decimal strike, DateTime nearExpiration, DateTime farExpiration) { // Since a short put calendar spread is an inverted put calendar, we can just use the PutCalendarSpread method and invert the legs return InvertStrategy(PutCalendarSpread(canonicalOption, strike, nearExpiration, farExpiration), OptionStrategyDefinitions.ShortPutCalendarSpread.Name); } /// /// Creates a new Iron Butterfly strategy which consists of a short ATM call, a short ATM put, a long OTM call, and a long OTM put. /// all with the same expiration date and with increasing strikes prices in the mentioned order. /// /// Option symbol /// OTM call option strike price /// 2 ATM options strike price /// OTM put option strike price /// Expiration date for all the options /// Option strategy specification public static OptionStrategy IronButterfly(Symbol canonicalOption, decimal otmPutStrike, decimal atmStrike, decimal otmCallStrike, DateTime expiration) { if (atmStrike - otmPutStrike != otmCallStrike - atmStrike) { throw new ArgumentException("IronButterfly: intervals between exercise prices must be equal"); } var strategy = IronCondor(canonicalOption, otmPutStrike, atmStrike, atmStrike, otmCallStrike, expiration); strategy.Name = OptionStrategyDefinitions.IronButterfly.Name; return strategy; } /// /// Creates a new Short Iron Butterfly strategy which consists of a long ATM call, a long ATM put, a short OTM call, and a short OTM put, /// all with the same expiration date and with increasing strikes prices in the mentioned order. /// It is the inverse of an . /// /// Option symbol /// OTM call option strike price /// 2 ATM options strike price /// OTM put option strike price /// Expiration date for all the options /// Option strategy specification public static OptionStrategy ShortIronButterfly(Symbol canonicalOption, decimal otmPutStrike, decimal atmStrike, decimal otmCallStrike, DateTime expiration) { return InvertStrategy(IronButterfly(canonicalOption, otmPutStrike, atmStrike, otmCallStrike, expiration), OptionStrategyDefinitions.ShortIronButterfly.Name); } /// /// Creates a new Iron Condor strategy which consists of a long put, a short put, a short call and a long option, /// all with the same expiration date and with increasing strikes prices in the mentioned order. /// /// Option symbol /// Long put option strike price /// Short put option strike price /// Short call option strike price /// Long call option strike price /// Expiration date for all the options /// Option strategy specification public static OptionStrategy IronCondor(Symbol canonicalOption, decimal longPutStrike, decimal shortPutStrike, decimal shortCallStrike, decimal longCallStrike, DateTime expiration) { CheckCanonicalOptionSymbol(canonicalOption, "IronCondor"); CheckExpirationDate(expiration, "IronCondor", nameof(expiration)); if (longPutStrike >= shortPutStrike || shortPutStrike > shortCallStrike || shortCallStrike >= longCallStrike) { throw new ArgumentException("IronCondor: strike prices must be in ascending order", $"{nameof(longPutStrike)}, {nameof(shortPutStrike)}, {nameof(shortCallStrike)}, {nameof(longCallStrike)}"); } return new OptionStrategy { Name = OptionStrategyDefinitions.IronCondor.Name, Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = new List { new OptionStrategy.OptionLegData { Right = OptionRight.Put, Strike = longPutStrike, Quantity = 1, Expiration = expiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Put, Strike = shortPutStrike, Quantity = -1, Expiration = expiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Call, Strike = shortCallStrike, Quantity = -1, Expiration = expiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Call, Strike = longCallStrike, Quantity = 1, Expiration = expiration } } }; } /// /// Creates a new Short Iron Condor strategy which consists of a short put, a long put, a long call and a short call, /// all with the same expiration date and with increasing strikes prices in the mentioned order. /// /// Option symbol /// Short put option strike price /// Long put option strike price /// Long call option strike price /// Short call option strike price /// Expiration date for all the options /// Option strategy specification public static OptionStrategy ShortIronCondor(Symbol canonicalOption, decimal shortPutStrike, decimal longPutStrike, decimal longCallStrike, decimal shortCallStrike, DateTime expiration) { return InvertStrategy(IronCondor(canonicalOption, shortPutStrike, longPutStrike, longCallStrike, shortCallStrike, expiration), OptionStrategyDefinitions.ShortIronCondor.Name); } /// /// Creates a Box Spread strategy which consists of a long call and a short put (buy side) of the same strikes, /// coupled with a short call and a long put (sell side) of higher but same strikes. All options have the same expiry. /// /// Option symbol /// The strike price of the sell side legs /// The strike price of the buy side legs /// Option expiration date /// Option strategy specification public static OptionStrategy BoxSpread(Symbol canonicalOption, decimal higherStrike, decimal lowerStrike, DateTime expiration) { if (higherStrike <= lowerStrike) { throw new ArgumentException($"BoxSpread: strike prices must be in descending order, {nameof(higherStrike)}, {nameof(lowerStrike)}"); } // It is a combination of a BearPutSpread and a BullCallSpread with the same expiry and strikes var bearPutSpread = BearPutSpread(canonicalOption, higherStrike, lowerStrike, expiration); var bullCallSpread = BullCallSpread(canonicalOption, lowerStrike, higherStrike, expiration); return new OptionStrategy { Name = OptionStrategyDefinitions.BoxSpread.Name, Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = bearPutSpread.OptionLegs.Concat(bullCallSpread.OptionLegs).ToList() }; } /// /// Creates a Short Box Spread strategy which consists of a long call and a short put (buy side) of the same strikes, /// coupled with a short call and a long put (sell side) of lower but same strikes. All options have the same expiry. /// /// Option symbol /// The strike price of the buy side /// The strike price of the sell side /// Option expiration date /// Option strategy specification public static OptionStrategy ShortBoxSpread(Symbol canonicalOption, decimal higherStrike, decimal lowerStrike, DateTime expiration) { // Since a short box spread is an inverted box spread, we can just use the BoxSpread method and invert the legs return InvertStrategy(BoxSpread(canonicalOption, higherStrike, lowerStrike, expiration), OptionStrategyDefinitions.ShortBoxSpread.Name); } /// /// Creates new Jelly Roll strategy which combines a long call calendar spread and a short put calendar spread /// with the same strikes and the same pair of expiration dates. /// /// Option symbol /// The strike price of the all legs /// Near expiration date for the short call and the long put /// Far expiration date for the long call and the short put /// Option strategy specification public static OptionStrategy JellyRoll(Symbol canonicalOption, decimal strike, DateTime nearExpiration, DateTime farExpiration) { var callCalendarSpread = CallCalendarSpread(canonicalOption, strike, nearExpiration, farExpiration); var shortPutCalendarSpread = ShortPutCalendarSpread(canonicalOption, strike, nearExpiration, farExpiration); return new OptionStrategy { Name = OptionStrategyDefinitions.JellyRoll.Name, Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = callCalendarSpread.OptionLegs.Concat(shortPutCalendarSpread.OptionLegs).ToList() }; } /// /// Creates new Short Jelly Roll strategy which combines a long call calendar spread and a short put calendar spread /// with the same strikes and the same pair of expiration dates. /// /// Option symbol /// The strike price of the all legs /// Near expiration date for the short call and the long put /// Far expiration date for the long call and the short put /// Option strategy specification public static OptionStrategy ShortJellyRoll(Symbol canonicalOption, decimal strike, DateTime nearExpiration, DateTime farExpiration) { return InvertStrategy(JellyRoll(canonicalOption, strike, nearExpiration, farExpiration), OptionStrategyDefinitions.ShortJellyRoll.Name); } /// /// Method creates new Bear Call Ladder strategy, that consists of three calls with the same expiration but different strikes. /// The strike price of the short call is below the strikes of the two long calls. /// /// Option symbol /// The strike price of the short call /// The middle strike price of one long call /// The strike price of one long call with higher strike price /// Option expiration date /// Option strategy specification public static OptionStrategy BearCallLadder( Symbol canonicalOption, decimal lowerStrike, decimal middleStrike, decimal higherStrike, DateTime expiration ) { CheckCanonicalOptionSymbol(canonicalOption, "BearCallLadder"); CheckExpirationDate(expiration, "BearCallLadder", nameof(expiration)); if (lowerStrike >= middleStrike || lowerStrike >= higherStrike || middleStrike >= higherStrike) { throw new ArgumentException("BearCallLadder: strike prices must be in ascending order", $"{nameof(lowerStrike)}, {nameof(middleStrike)}, {nameof(higherStrike)}"); } return new OptionStrategy { Name = OptionStrategyDefinitions.BearCallLadder.Name, Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = new List { new OptionStrategy.OptionLegData { Right = OptionRight.Call, Strike = lowerStrike, Quantity = -1, Expiration = expiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Call, Strike = middleStrike, Quantity = 1, Expiration = expiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Call, Strike = higherStrike, Quantity = 1, Expiration = expiration } } }; } /// /// Method creates new Bear Put Ladder strategy, that consists of three puts with the same expiration but different strikes. /// The strike price of the long put is above the strikes of the two short puts. /// /// Option symbol /// The strike price of the long put /// The middle strike price of one short put /// The strike price of one short put with lower strike price /// Option expiration date /// Option strategy specification public static OptionStrategy BearPutLadder( Symbol canonicalOption, decimal higherStrike, decimal middleStrike, decimal lowerStrike, DateTime expiration ) { CheckCanonicalOptionSymbol(canonicalOption, "BearPutLadder"); CheckExpirationDate(expiration, "BearPutLadder", nameof(expiration)); if (higherStrike <= middleStrike || higherStrike <= lowerStrike || middleStrike <= lowerStrike) { throw new ArgumentException("BearPutLadder: strike prices must be in descending order", $"{nameof(higherStrike)}, {nameof(middleStrike)}, {nameof(lowerStrike)}"); } return new OptionStrategy { Name = OptionStrategyDefinitions.BearPutLadder.Name, Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = new List { new OptionStrategy.OptionLegData { Right = OptionRight.Put, Strike = higherStrike, Quantity = 1, Expiration = expiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Put, Strike = middleStrike, Quantity = -1, Expiration = expiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Put, Strike = lowerStrike, Quantity = -1, Expiration = expiration } } }; } /// /// Method creates new Bull Call Ladder strategy, that consists of three calls with the same expiration but different strikes. /// The strike price of the long call is below the strikes of the two short calls. /// /// Option symbol /// The strike price of the long call /// The middle strike price of one short call /// The strike price of one short call with higher strike price /// Option expiration date /// Option strategy specification public static OptionStrategy BullCallLadder( Symbol canonicalOption, decimal lowerStrike, decimal middleStrike, decimal higherStrike, DateTime expiration ) { return InvertStrategy(BearCallLadder(canonicalOption, lowerStrike, middleStrike, higherStrike, expiration), OptionStrategyDefinitions.BullCallLadder.Name); } /// /// Method creates new Bull Put Ladder strategy, that consists of three puts with the same expiration but different strikes. /// The strike price of the short put is above the strikes of the two long puts. /// /// Option symbol /// The strike price of the short put /// The middle strike price of one long put /// The strike price of one long put with lower strike price /// Option expiration date /// Option strategy specification public static OptionStrategy BullPutLadder( Symbol canonicalOption, decimal higherStrike, decimal middleStrike, decimal lowerStrike, DateTime expiration ) { return InvertStrategy(BearPutLadder(canonicalOption, higherStrike, middleStrike, lowerStrike, expiration), OptionStrategyDefinitions.BullPutLadder.Name); } /// /// Method creates new Long Call Backspread strategy, that consists of two calls with the same expiration but different strikes. /// It involves selling the lower strike call, while buying twice the number of the higher strike call. /// /// Option symbol /// The strike price of the short call /// The strike price of the long call /// Option expiration date /// Option strategy specification public static OptionStrategy CallBackspread( Symbol canonicalOption, decimal lowerStrike, decimal higherStrike, DateTime expiration ) { CheckCanonicalOptionSymbol(canonicalOption, "CallBackspread"); CheckExpirationDate(expiration, "CallBackspread", nameof(expiration)); if (lowerStrike >= higherStrike) { throw new ArgumentException($"CallBackspread: strike prices must be in ascending order, {nameof(lowerStrike)}, {nameof(higherStrike)}"); } return new OptionStrategy { Name = "Call Backspread", Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = new List { new OptionStrategy.OptionLegData { Right = OptionRight.Call, Strike = lowerStrike, Quantity = -1, Expiration = expiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Call, Strike = higherStrike, Quantity = 2, Expiration = expiration } } }; } /// /// Method creates new Long Put Backspread strategy, that consists of two puts with the same expiration but different strikes. /// It involves selling the higher strike put, while buying twice the number of the lower strike put. /// /// Option symbol /// The strike price of the short put /// The strike price of the long put /// Option expiration date /// Option strategy specification public static OptionStrategy PutBackspread( Symbol canonicalOption, decimal higherStrike, decimal lowerStrike, DateTime expiration ) { CheckCanonicalOptionSymbol(canonicalOption, "PutBackspread"); CheckExpirationDate(expiration, "PutBackspread", nameof(expiration)); if (higherStrike <= lowerStrike) { throw new ArgumentException($"PutBackspread: strike prices must be in descending order, {nameof(higherStrike)}, {nameof(lowerStrike)}"); } return new OptionStrategy { Name = "Put Backspread", Underlying = canonicalOption.Underlying, CanonicalOption = canonicalOption, OptionLegs = new List { new OptionStrategy.OptionLegData { Right = OptionRight.Put, Strike = higherStrike, Quantity = -1, Expiration = expiration }, new OptionStrategy.OptionLegData { Right = OptionRight.Put, Strike = lowerStrike, Quantity = 2, Expiration = expiration } } }; } /// /// Method creates new Short Call Backspread strategy, that consists of two calls with the same expiration but different strikes. /// It involves buying the lower strike call, while shorting twice the number of the higher strike call. /// /// Option symbol /// The strike price of the long call /// The strike price of the short call /// Option expiration date public static OptionStrategy ShortCallBackspread( Symbol canonicalOption, decimal lowerStrike, decimal higherStrike, DateTime expiration ) { return InvertStrategy(CallBackspread(canonicalOption, lowerStrike, higherStrike, expiration), "Short Call Backspread"); } /// /// Method creates new Short Put Backspread strategy, that consists of two puts with the same expiration but different strikes. /// It involves buying the higher strike put, while selling twice the number of the lower strike put. /// /// Option symbol /// The strike price of the long put /// The strike price of the short put /// Option expiration date /// Option strategy specification public static OptionStrategy ShortPutBackspread( Symbol canonicalOption, decimal higherStrike, decimal lowerStrike, DateTime expiration ) { return InvertStrategy(PutBackspread(canonicalOption, higherStrike, lowerStrike, expiration), "Short Put Backspread"); } /// /// Checks that canonical option symbol is valid /// private static void CheckCanonicalOptionSymbol(Symbol canonicalOption, string strategyName) { if (!canonicalOption.HasUnderlying || canonicalOption.ID.StrikePrice != 0.0m) { throw new ArgumentException($"{strategyName}: canonicalOption must contain canonical option symbol", nameof(canonicalOption)); } } /// /// Checks that expiration date is valid /// private static void CheckExpirationDate(DateTime expiration, string strategyName, string parameterName) { if (expiration == DateTime.MaxValue || expiration == DateTime.MinValue) { throw new ArgumentException($"{strategyName}: expiration must contain expiration date", parameterName); } } /// /// Inverts the given strategy by multiplying all legs' quantities by -1 and changing the strategy name. /// private static OptionStrategy InvertStrategy(OptionStrategy strategy, string invertedStrategyName) { strategy.Name = invertedStrategyName; foreach (var leg in strategy.OptionLegs.Cast().Concat(strategy.UnderlyingLegs)) { leg.Quantity *= -1; } return strategy; } } }