/* * 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 QuantConnect.Orders; using System.Collections.Generic; using System.Linq; namespace QuantConnect.Securities.Option { /// /// Option strategy specification class. Describes option strategy and its parameters for trading. /// public class OptionStrategy { /// /// Option strategy name /// public string Name { get; set; } /// /// The canonical Option symbol of the strategy /// public Symbol CanonicalOption { get; set; } /// /// Underlying symbol of the strategy /// public Symbol Underlying { get; set; } /// /// Option strategy legs /// public List OptionLegs { get; set; } /// /// Option strategy underlying legs (usually 0 or 1 legs) /// public List UnderlyingLegs { get; set; } /// /// Creates a new instance of with the specified parameters /// /// The strategy name /// The canonical option symbol /// The option legs data /// The underlying legs data public OptionStrategy(string name, Symbol canonicalSymbol, List optionLegs = null, List underlyingLegs = null) { Name = name; CanonicalOption = canonicalSymbol; Underlying = canonicalSymbol.Underlying; OptionLegs = optionLegs ?? new List(); UnderlyingLegs = underlyingLegs ?? new List(); SetSymbols(); } /// /// Creates a new instance of with default parameters /// public OptionStrategy() { OptionLegs = new List(); UnderlyingLegs = new List(); } /// /// Sets the option legs symbols based on the canonical symbol and the leg data. /// If the canonical symbol is not set, it will be created using the underlying symbol. /// public void SetSymbols() { if (CanonicalOption == null) { if (Underlying == null) { // Let's be polite and try to get the underlying symbol from the underlying legs as a last resort var underlyingLeg = UnderlyingLegs.Count > 0 ? UnderlyingLegs[0] : null; if (underlyingLeg == null || underlyingLeg.Symbol == null) { return; } Underlying = underlyingLeg.Symbol; } CanonicalOption = Symbol.CreateCanonicalOption(Underlying); } foreach (var optionLeg in OptionLegs.Where(leg => leg.Symbol == null)) { var targetOption = CanonicalOption.ID.Symbol; optionLeg.Symbol = Symbol.CreateOption(Underlying, targetOption, Underlying.ID.Market, CanonicalOption.ID.OptionStyle, optionLeg.Right, optionLeg.Strike, optionLeg.Expiration); } } /// /// Creates a new instance of with the specified name and legs data. /// The method will try to infer the canonical symbol and underlying symbol from the legs data, but they can also be set manually after the strategy creation. /// public static OptionStrategy Create(string name, IEnumerable legs) { var underlyingLegs = new List(); var optionLegs = new List(); Symbol canonicalSymbol = null; foreach (var leg in legs) { if (leg is UnderlyingLegData underlyingLeg) { underlyingLegs.Add(underlyingLeg); } else if (leg is OptionLegData optionLeg) { optionLegs.Add(optionLeg); if (canonicalSymbol == null) { canonicalSymbol = optionLeg.Symbol.Canonical; } } else { throw new ArgumentException($"Invalid leg type: {leg.GetType().FullName}"); } } return new OptionStrategy(name, canonicalSymbol, optionLegs, underlyingLegs); } /// /// This class is a POCO containing basic data for the option legs of the strategy /// public class OptionLegData : Leg { /// /// Option right (type) of the option leg /// public OptionRight Right { get; set; } /// /// Expiration date of the leg /// public DateTime Expiration { get; set; } /// /// Strike price of the leg /// public decimal Strike { get; set; } /// /// Creates a new instance of from the specified parameters /// public static OptionLegData Create(int quantity, Symbol symbol, decimal? orderPrice = null) { return new OptionLegData { Symbol = symbol, Quantity = quantity, Expiration = symbol.ID.Date, OrderPrice = orderPrice, Right = symbol.ID.OptionRight, Strike = symbol.ID.StrikePrice }; } /// /// Returns a string that represents the option leg /// public override string ToString() { return $"Leg: {Quantity}. Right: {Right}. Strike: {Strike}. Expiration: {Expiration:yyyyMMdd}"; } } /// /// This class is a POCO containing basic data for the underlying leg of the strategy /// public class UnderlyingLegData : Leg { /// /// Creates a new instance of for the specified of underlying shares. /// public static UnderlyingLegData Create(int quantity, Symbol symbol, decimal? orderPrice = null) { var data = Create(quantity, orderPrice); data.Symbol = symbol; return data; } /// /// Creates a new instance of for the specified of underlying shares. /// public static UnderlyingLegData Create(int quantity, decimal? orderPrice = null) { return new UnderlyingLegData { Quantity = quantity, OrderPrice = orderPrice }; } /// /// Returns a string that represents the underlying leg. /// public override string ToString() { return Symbol != null ? $"Leg: {Quantity}. {Symbol}" : string.Empty; } } } }