/* * 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.Linq; using System.Collections.Generic; namespace QuantConnect.Securities.Option.StrategyMatcher { /// /// Defines a match of to a /// public class OptionStrategyDefinitionMatch : IEquatable { /// /// The matched /// public OptionStrategyDefinition Definition { get; } /// /// The number of times the definition is able to match the available positions. /// Since definitions are formed at the 'unit' level, such as having 1 contract, /// the multiplier defines how many times the definition matched. This multiplier /// is used to scale the quantity defined in each leg definition when creating the /// objects. /// public int Multiplier { get; } /// /// The instances matched to the definition. /// public IReadOnlyList Legs { get; } /// /// Initializes a new instance of the class /// public OptionStrategyDefinitionMatch( OptionStrategyDefinition definition, IReadOnlyList legs, int multiplier ) { Legs = legs; Multiplier = multiplier; Definition = definition; } /// /// Deducts the matched positions from the specified taking into account the multiplier /// public OptionPositionCollection RemoveFrom(OptionPositionCollection positions) { var optionPositions = Legs.Select(leg => leg.CreateOptionPosition(Multiplier)); if (Definition.UnderlyingLots != 0) { optionPositions = optionPositions.Concat(new[] { new OptionPosition(Legs[0].Position.Symbol.Underlying, Definition.UnderlyingLots * Multiplier) }); } return positions.RemoveRange(optionPositions); } /// /// Creates the instance this match represents /// public OptionStrategy CreateStrategy() { var legs = Legs.Select(leg => leg.CreateOptionStrategyLeg(Multiplier)); var strategy = new OptionStrategy { Name = Definition.Name, Underlying = Legs[0].Position.Underlying }; foreach (var optionLeg in legs) { optionLeg.Invoke(strategy.UnderlyingLegs.Add, strategy.OptionLegs.Add); } if (Definition.UnderlyingLots != 0) { strategy.UnderlyingLegs = new List { OptionStrategy.UnderlyingLegData.Create(Definition.UnderlyingLots * Multiplier, Legs[0].Position.Underlying) }; } return strategy; } /// Indicates whether the current object is equal to another object of the same type. /// An object to compare with this object. /// true if the current object is equal to the parameter; otherwise, false. public bool Equals(OptionStrategyDefinitionMatch other) { if (ReferenceEquals(null, other)) { return false; } if (ReferenceEquals(this, other)) { return true; } if (!Equals(Definition, other.Definition)) { return false; } // index legs by OptionPosition so we can do the equality while ignoring ordering var positions = other.Legs.ToDictionary(leg => leg.Position, leg => leg.Multiplier); foreach (var leg in other.Legs) { int multiplier; if (!positions.TryGetValue(leg.Position, out multiplier)) { return false; } if (leg.Multiplier != multiplier) { return false; } } return true; } /// Determines whether the specified object is equal to the current object. /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) { return false; } if (ReferenceEquals(this, obj)) { return true; } if (obj.GetType() != GetType()) { return false; } return Equals((OptionStrategyDefinitionMatch) obj); } /// Serves as the default hash function. /// A hash code for the current object. public override int GetHashCode() { unchecked { // we want to ensure that the ordering of legs does not impact equality operators in // pursuit of this, we compute the hash codes of each leg, placing them into an array // and then sort the array. using the sorted array, aggregates the hash codes var hashCode = Definition.GetHashCode(); var arr = new int[Legs.Count]; for (int i = 0; i < Legs.Count; i++) { arr[i] = Legs[i].GetHashCode(); } Array.Sort(arr); for (int i = 0; i < arr.Length; i++) { hashCode = (hashCode * 397) ^ arr[i]; } return hashCode; } } /// Returns a string that represents the current object. /// A string that represents the current object. public override string ToString() { return $"{Definition.Name}: {string.Join("|", Legs.Select(leg => leg.Position))}"; } /// /// OptionStrategyDefinitionMatch == Operator /// /// True if they are the same public static bool operator ==(OptionStrategyDefinitionMatch left, OptionStrategyDefinitionMatch right) { return Equals(left, right); } /// /// OptionStrategyDefinitionMatch != Operator /// /// True if they are not the same public static bool operator !=(OptionStrategyDefinitionMatch left, OptionStrategyDefinitionMatch right) { return !Equals(left, right); } } }