/* * 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; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Linq.Expressions; namespace QuantConnect.Securities.Option.StrategyMatcher { /// /// Provides a definitional object for an . This definition is used to 'match' option /// positions via . The utilizes a full /// collection of these definitional objects in order to match an algorithm's option position holdings to the /// set of strategies in an effort to reduce the total margin required for holding the positions. /// public class OptionStrategyDefinition : IEnumerable { /// /// Gets the definition's name /// public string Name { get; } /// /// Gets the number of underlying lots required to match this definition. A lot size /// is equal to the contract's multiplier and is usually equal to 100. /// public int UnderlyingLots { get; } /// /// Gets the option leg definitions. This list does NOT contain a definition for the /// required underlying lots, due to its simplicity. Instead the required underlying /// lots are defined via the property of the definition. /// public IReadOnlyList Legs { get; } /// /// Gets the total number of legs, INCLUDING the underlying leg if applicable. This /// is used to perform a coarse filter as the minimum number of unique positions in /// the positions collection. /// public int LegCount => Legs.Count + (UnderlyingLots == 0 ? 0 : 1); /// /// Initializes a new instance of the class /// /// The definition's name /// The required number of underlying lots /// Definitions for each option leg public OptionStrategyDefinition(string name, int underlyingLots, IEnumerable legs) { Name = name; Legs = legs.ToList(); UnderlyingLots = underlyingLots; } /// /// Creates the instance using this definition and the provided leg matches /// public OptionStrategy CreateStrategy(IReadOnlyList legs) { var underlying = legs[0].Position.Symbol; if (underlying.HasUnderlying) { underlying = underlying.Underlying; } var strategy = new OptionStrategy {Name = Name, Underlying = underlying}; for (int i = 0; i < Math.Min(Legs.Count, legs.Count); i++) { var leg = Legs[i].CreateLegData(legs[i]); leg.Invoke(strategy.UnderlyingLegs.Add, strategy.OptionLegs.Add); } return strategy; } /// /// Attempts to match the positions to this definition exactly once, by evaluating the enumerable and /// taking the first entry matched. If not match is found, then false is returned and /// will be null. /// public bool TryMatchOnce(OptionStrategyMatcherOptions options, OptionPositionCollection positions, out OptionStrategyDefinitionMatch match) { match = Match(options, positions).FirstOrDefault(); return match != null; } /// /// Determines all possible matches for this definition using the provided . /// This includes OVERLAPPING matches. It's up to the actual matcher to make decisions based on which /// matches to accept. This allows the matcher to prioritize matching certain positions over others. /// public IEnumerable Match(OptionPositionCollection positions) { return Match(OptionStrategyMatcherOptions.ForDefinitions(this), positions); } /// /// Determines all possible matches for this definition using the provided . /// This includes OVERLAPPING matches. It's up to the actual matcher to make decisions based on which /// matches to accept. This allows the matcher to prioritize matching certain positions over others. /// public IEnumerable Match( OptionStrategyMatcherOptions options, OptionPositionCollection positions ) { // TODO : Pass OptionStrategyMatcherOptions in and respect applicable options if (positions.Count < LegCount) { return Enumerable.Empty(); } var multiplier = int.MaxValue; // first check underlying lots has correct sign and sufficient magnitude var underlyingLotsSign = Math.Sign(UnderlyingLots); if (underlyingLotsSign != 0) { var underlyingPositionSign = Math.Sign(positions.UnderlyingQuantity); if (underlyingLotsSign != underlyingPositionSign || Math.Abs(positions.UnderlyingQuantity) < Math.Abs(UnderlyingLots)) { return Enumerable.Empty(); } // set multiplier for underlying multiplier = positions.UnderlyingQuantity / UnderlyingLots; } // TODO : Consider add OptionStrategyLegDefinition for underlying for consistency purposes. // Might want to enforce that it's always the first leg definition as well for easier slicing. return Match(options, ImmutableList.Empty, ImmutableList.Empty, positions, multiplier ).Distinct(); } private IEnumerable Match( OptionStrategyMatcherOptions options, ImmutableList legMatches, ImmutableList legPositions, OptionPositionCollection positions, int multiplier ) { var nextLegIndex = legPositions.Count; if (nextLegIndex == Legs.Count) { if (nextLegIndex > 0) { yield return new OptionStrategyDefinitionMatch(this, legMatches, multiplier); } } else if (positions.Count >= LegCount - nextLegIndex) { // grab the next leg definition and perform the match, restricting total to configured maximum per leg var nextLeg = Legs[nextLegIndex]; var maxLegMatch = options.GetMaximumLegMatches(nextLegIndex); foreach (var legMatch in nextLeg.Match(options, legPositions, positions).Take(maxLegMatch)) { // add match to the match we're constructing and deduct matched position from positions collection // we track the min multiplier in line so when we're done, we have the total number of matches for // the matched set of positions in this 'thread' (OptionStrategy.Quantity) foreach (var definitionMatch in Match(options, legMatches.Add(legMatch), legPositions.Add(legMatch.Position), positions - legMatch.Position, Math.Min(multiplier, legMatch.Multiplier) )) { yield return definitionMatch; } } } else { // positions.Count < LegsCount indicates a failed match // could include partial matches, would allow an algorithm to determine if adding a // new position could help reduce overall margin exposure by completing a strategy } } /// Returns a string that represents the current object. /// A string that represents the current object. public override string ToString() { return Name; } /// /// Factory function for creating definitions /// public static OptionStrategyDefinition Create(string name, int underlyingLots, params OptionStrategyLegDefinition[] legs) { return new OptionStrategyDefinition(name, underlyingLots, legs); } /// /// Factory function for creating definitions /// public static OptionStrategyDefinition Create(string name, params OptionStrategyLegDefinition[] legs) { return new OptionStrategyDefinition(name, 0, legs); } /// /// Factory function for creating definitions /// public static OptionStrategyDefinition Create(string name, params Func[] predicates) { return predicates.Aggregate(new Builder(name), (builder, predicate) => predicate(builder) ).Build(); } /// /// Factory function for creating a call leg definition /// public static OptionStrategyLegDefinition CallLeg(int quantity, params Expression, OptionPosition, bool>>[] predicates ) { return OptionStrategyLegDefinition.Create(OptionRight.Call, quantity, predicates); } /// /// Factory function for creating a put leg definition /// public static OptionStrategyLegDefinition PutLeg(int quantity, params Expression, OptionPosition, bool>>[] predicates ) { return OptionStrategyLegDefinition.Create(OptionRight.Put, quantity, predicates); } /// /// Builder class supporting fluent syntax in constructing . /// public class Builder { private readonly string _name; private int _underlyingLots; private List _legs; /// /// Initializes a new instance of the class /// public Builder(string name) { _name = name; _legs = new List(); } /// /// Sets the required number of underlying lots /// public Builder WithUnderlyingLots(int lots) { if (_underlyingLots != 0) { throw new InvalidOperationException("Underlying lots has already been set."); } _underlyingLots = lots; return this; } /// /// Adds a call leg /// public Builder WithCall(int quantity, params Expression, OptionPosition, bool>>[] predicates ) { _legs.Add(OptionStrategyLegDefinition.Create(OptionRight.Call, quantity, predicates)); return this; } /// /// Adds a put leg /// public Builder WithPut(int quantity, params Expression, OptionPosition, bool>>[] predicates ) { _legs.Add(OptionStrategyLegDefinition.Create(OptionRight.Put, quantity, predicates)); return this; } /// /// Builds the /// public OptionStrategyDefinition Build() { return new OptionStrategyDefinition(_name, _underlyingLots, _legs); } } /// Returns an enumerator that iterates through the collection. /// An enumerator that can be used to iterate through the collection. public IEnumerator GetEnumerator() { return Legs.GetEnumerator(); } /// Returns an enumerator that iterates through a collection. /// An object that can be used to iterate through the collection. IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } }