/* * 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.Linq; using System.Linq.Expressions; namespace QuantConnect.Securities.Option.StrategyMatcher { /// /// Defines a single option leg in an option strategy. This definition supports direct /// match (does position X match the definition) and position collection filtering (filter /// collection to include matches) /// public class OptionStrategyLegDefinition : IEnumerable { private readonly OptionStrategyLegPredicate[] _predicates; /// /// Gets the unit quantity /// public int Quantity { get; } /// /// Gets the contract right /// public OptionRight Right { get; } /// /// Initializes a new instance of the class /// /// The leg's contract right /// The leg's unit quantity /// The conditions a position must meet in order to match this definition public OptionStrategyLegDefinition(OptionRight right, int quantity, IEnumerable predicates) { Right = right; Quantity = quantity; _predicates = predicates.ToArray(); } /// /// Yields all possible matches for this leg definition held within the collection of /// /// Strategy matcher options guiding matching behaviors /// The preceding legs already matched for the parent strategy definition /// The remaining, unmatched positions available to be matched against /// An enumerable of potential matches public IEnumerable Match( OptionStrategyMatcherOptions options, IReadOnlyList legs, OptionPositionCollection positions ) { foreach (var position in options.Enumerate(Filter(legs, positions, false))) { var multiplier = position.Quantity / Quantity; if (multiplier != 0) { yield return new OptionStrategyLegDefinitionMatch(multiplier, position.WithQuantity(multiplier * Quantity) ); } } } /// /// Filters the provided collection such that any remaining positions are all /// valid options that match this leg definition instance. /// public OptionPositionCollection Filter(IReadOnlyList legs, OptionPositionCollection positions, bool includeUnderlying = true) { // first filter down to applicable right positions = positions.Slice(Right, includeUnderlying); if (positions.IsEmpty) { return positions; } // second filter according to the required side var side = (PositionSide) Math.Sign(Quantity); positions = positions.Slice(side, includeUnderlying); if (positions.IsEmpty) { return positions; } // these are ordered such that indexed filters are performed force and // opaque/complex predicates follow since they require full table scans foreach (var predicate in _predicates) { positions = predicate.Filter(legs, positions, includeUnderlying); if (positions.IsEmpty) { break; } } // at this point, every position in the positions // collection is a valid match for this definition return positions; } /// /// Creates the appropriate for the specified /// public OptionStrategy.LegData CreateLegData(OptionStrategyLegDefinitionMatch match) { return CreateLegData( match.Position.Symbol, match.Position.Quantity / Quantity ); } /// /// Creates the appropriate with the specified /// public static OptionStrategy.LegData CreateLegData(Symbol symbol, int quantity) { if (symbol.SecurityType == SecurityType.Option) { return OptionStrategy.OptionLegData.Create(quantity, symbol); } return OptionStrategy.UnderlyingLegData.Create(quantity); } /// /// Determines whether or not this leg definition matches the specified , /// and if so, what the resulting quantity of the should be. /// public bool TryMatch(OptionPosition position, out OptionStrategy.LegData leg) { if (Right != position.Right || Math.Sign(Quantity) != Math.Sign(position.Quantity)) { leg = null; return false; } var quantity = position.Quantity / Quantity; if (quantity == 0) { leg = null; return false; } leg = position.Symbol.SecurityType == SecurityType.Option ? (OptionStrategy.LegData) OptionStrategy.OptionLegData.Create(quantity, position.Symbol) : OptionStrategy.UnderlyingLegData.Create(quantity); return true; } /// /// Creates a new matching the specified parameters /// public static OptionStrategyLegDefinition Create(OptionRight right, int quantity, IEnumerable, OptionPosition, bool>>> predicates ) { return new OptionStrategyLegDefinition(right, quantity, // sort predicates such that indexed predicates are evaluated first // this leaves fewer positions to be evaluated by the full table scan predicates.Select(OptionStrategyLegPredicate.Create).OrderBy(p => p.IsIndexed ? 0 : 1) ); } /// Returns an enumerator that iterates through the collection. /// An enumerator that can be used to iterate through the collection. public IEnumerator GetEnumerator() { foreach (var predicate in _predicates) { yield return predicate; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } }