/*
* 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;
}
}
}