Lean  $LEAN_TAG$
OptionStrategyLegDefinition.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using System;
17 using System.Collections;
18 using System.Collections.Generic;
19 using System.Linq;
20 using System.Linq.Expressions;
21 
23 {
24  /// <summary>
25  /// Defines a single option leg in an option strategy. This definition supports direct
26  /// match (does position X match the definition) and position collection filtering (filter
27  /// collection to include matches)
28  /// </summary>
29  public class OptionStrategyLegDefinition : IEnumerable<OptionStrategyLegPredicate>
30  {
31  private readonly OptionStrategyLegPredicate[] _predicates;
32 
33  /// <summary>
34  /// Gets the unit quantity
35  /// </summary>
36  public int Quantity { get; }
37 
38  /// <summary>
39  /// Gets the contract right
40  /// </summary>
41  public OptionRight Right { get; }
42 
43  /// <summary>
44  /// Initializes a new instance of the <see cref="OptionStrategyLegDefinition"/> class
45  /// </summary>
46  /// <param name="right">The leg's contract right</param>
47  /// <param name="quantity">The leg's unit quantity</param>
48  /// <param name="predicates">The conditions a position must meet in order to match this definition</param>
49  public OptionStrategyLegDefinition(OptionRight right, int quantity, IEnumerable<OptionStrategyLegPredicate> predicates)
50  {
51  Right = right;
52  Quantity = quantity;
53  _predicates = predicates.ToArray();
54  }
55 
56  /// <summary>
57  /// Yields all possible matches for this leg definition held within the collection of <paramref name="positions"/>
58  /// </summary>
59  /// <param name="options">Strategy matcher options guiding matching behaviors</param>
60  /// <param name="legs">The preceding legs already matched for the parent strategy definition</param>
61  /// <param name="positions">The remaining, unmatched positions available to be matched against</param>
62  /// <returns>An enumerable of potential matches</returns>
63  public IEnumerable<OptionStrategyLegDefinitionMatch> Match(
65  IReadOnlyList<OptionPosition> legs,
66  OptionPositionCollection positions
67  )
68  {
69  foreach (var position in options.Enumerate(Filter(legs, positions, false)))
70  {
71  var multiplier = position.Quantity / Quantity;
72  if (multiplier != 0)
73  {
74  yield return new OptionStrategyLegDefinitionMatch(multiplier,
75  position.WithQuantity(multiplier * Quantity)
76  );
77  }
78  }
79  }
80 
81  /// <summary>
82  /// Filters the provided <paramref name="positions"/> collection such that any remaining positions are all
83  /// valid options that match this leg definition instance.
84  /// </summary>
85  public OptionPositionCollection Filter(IReadOnlyList<OptionPosition> legs, OptionPositionCollection positions, bool includeUnderlying = true)
86  {
87  // first filter down to applicable right
88  positions = positions.Slice(Right, includeUnderlying);
89  if (positions.IsEmpty)
90  {
91  return positions;
92  }
93 
94  // second filter according to the required side
95  var side = (PositionSide) Math.Sign(Quantity);
96  positions = positions.Slice(side, includeUnderlying);
97  if (positions.IsEmpty)
98  {
99  return positions;
100  }
101 
102  // these are ordered such that indexed filters are performed force and
103  // opaque/complex predicates follow since they require full table scans
104  foreach (var predicate in _predicates)
105  {
106  positions = predicate.Filter(legs, positions, includeUnderlying);
107  if (positions.IsEmpty)
108  {
109  break;
110  }
111  }
112 
113  // at this point, every position in the positions
114  // collection is a valid match for this definition
115  return positions;
116  }
117 
118  /// <summary>
119  /// Creates the appropriate <see cref="OptionStrategy.LegData"/> for the specified <paramref name="match"/>
120  /// </summary>
122  {
123  return CreateLegData(
124  match.Position.Symbol,
125  match.Position.Quantity / Quantity
126  );
127  }
128 
129  /// <summary>
130  /// Creates the appropriate <see cref="OptionStrategy.LegData"/> with the specified <paramref name="quantity"/>
131  /// </summary>
132  public static OptionStrategy.LegData CreateLegData(Symbol symbol, int quantity)
133  {
134  if (symbol.SecurityType == SecurityType.Option)
135  {
136  return OptionStrategy.OptionLegData.Create(quantity, symbol);
137  }
138 
139  return OptionStrategy.UnderlyingLegData.Create(quantity);
140  }
141 
142  /// <summary>
143  /// Determines whether or not this leg definition matches the specified <paramref name="position"/>,
144  /// and if so, what the resulting quantity of the <see cref="OptionStrategy.OptionLegData"/> should be.
145  /// </summary>
146  public bool TryMatch(OptionPosition position, out OptionStrategy.LegData leg)
147  {
148  if (Right != position.Right ||
149  Math.Sign(Quantity) != Math.Sign(position.Quantity))
150  {
151  leg = null;
152  return false;
153  }
154 
155  var quantity = position.Quantity / Quantity;
156  if (quantity == 0)
157  {
158  leg = null;
159  return false;
160  }
161 
162  leg = position.Symbol.SecurityType == SecurityType.Option
165 
166  return true;
167  }
168 
169  /// <summary>
170  /// Creates a new <see cref="OptionStrategyLegDefinition"/> matching the specified parameters
171  /// </summary>
172  public static OptionStrategyLegDefinition Create(OptionRight right, int quantity,
173  IEnumerable<Expression<Func<IReadOnlyList<OptionPosition>, OptionPosition, bool>>> predicates
174  )
175  {
176  return new OptionStrategyLegDefinition(right, quantity,
177  // sort predicates such that indexed predicates are evaluated first
178  // this leaves fewer positions to be evaluated by the full table scan
179  predicates.Select(OptionStrategyLegPredicate.Create).OrderBy(p => p.IsIndexed ? 0 : 1)
180  );
181  }
182 
183  /// <summary>Returns an enumerator that iterates through the collection.</summary>
184  /// <returns>An enumerator that can be used to iterate through the collection.</returns>
185  public IEnumerator<OptionStrategyLegPredicate> GetEnumerator()
186  {
187  foreach (var predicate in _predicates)
188  {
189  yield return predicate;
190  }
191  }
192 
193  IEnumerator IEnumerable.GetEnumerator()
194  {
195  return GetEnumerator();
196  }
197  }
198 }