Lean  $LEAN_TAG$
OptionStrategyDefinition.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.Collections.Immutable;
20 using System.Linq;
21 using System.Linq.Expressions;
22 
24 {
25  /// <summary>
26  /// Provides a definitional object for an <see cref="OptionStrategy"/>. This definition is used to 'match' option
27  /// positions via <see cref="OptionPositionCollection"/>. The <see cref="OptionStrategyMatcher"/> utilizes a full
28  /// collection of these definitional objects in order to match an algorithm's option position holdings to the
29  /// set of strategies in an effort to reduce the total margin required for holding the positions.
30  /// </summary>
31  public class OptionStrategyDefinition : IEnumerable<OptionStrategyLegDefinition>
32  {
33  /// <summary>
34  /// Gets the definition's name
35  /// </summary>
36  public string Name { get; }
37 
38  /// <summary>
39  /// Gets the number of underlying lots required to match this definition. A lot size
40  /// is equal to the contract's multiplier and is usually equal to 100.
41  /// </summary>
42  public int UnderlyingLots { get; }
43 
44  /// <summary>
45  /// Gets the option leg definitions. This list does NOT contain a definition for the
46  /// required underlying lots, due to its simplicity. Instead the required underlying
47  /// lots are defined via the <see cref="UnderlyingLots"/> property of the definition.
48  /// </summary>
49  public IReadOnlyList<OptionStrategyLegDefinition> Legs { get; }
50 
51  /// <summary>
52  /// Gets the total number of legs, INCLUDING the underlying leg if applicable. This
53  /// is used to perform a coarse filter as the minimum number of unique positions in
54  /// the positions collection.
55  /// </summary>
56  public int LegCount => Legs.Count + (UnderlyingLots == 0 ? 0 : 1);
57 
58  /// <summary>
59  /// Initializes a new instance of the <see cref="OptionStrategyDefinition"/> class
60  /// </summary>
61  /// <param name="name">The definition's name</param>
62  /// <param name="underlyingLots">The required number of underlying lots</param>
63  /// <param name="legs">Definitions for each option leg</param>
64  public OptionStrategyDefinition(string name, int underlyingLots, IEnumerable<OptionStrategyLegDefinition> legs)
65  {
66  Name = name;
67  Legs = legs.ToList();
68  UnderlyingLots = underlyingLots;
69  }
70 
71  /// <summary>
72  /// Creates the <see cref="OptionStrategy"/> instance using this definition and the provided leg matches
73  /// </summary>
74  public OptionStrategy CreateStrategy(IReadOnlyList<OptionStrategyLegDefinitionMatch> legs)
75  {
76  var underlying = legs[0].Position.Symbol;
77  if (underlying.HasUnderlying)
78  {
79  underlying = underlying.Underlying;
80  }
81 
82  var strategy = new OptionStrategy {Name = Name, Underlying = underlying};
83  for (int i = 0; i < Math.Min(Legs.Count, legs.Count); i++)
84  {
85  var leg = Legs[i].CreateLegData(legs[i]);
86  leg.Invoke(strategy.UnderlyingLegs.Add, strategy.OptionLegs.Add);
87  }
88 
89  return strategy;
90  }
91 
92  /// <summary>
93  /// Attempts to match the positions to this definition exactly once, by evaluating the enumerable and
94  /// taking the first entry matched. If not match is found, then false is returned and <paramref name="match"/>
95  /// will be null.
96  /// </summary>
98  {
99  match = Match(options, positions).FirstOrDefault();
100  return match != null;
101  }
102 
103  /// <summary>
104  /// Determines all possible matches for this definition using the provided <paramref name="positions"/>.
105  /// This includes OVERLAPPING matches. It's up to the actual matcher to make decisions based on which
106  /// matches to accept. This allows the matcher to prioritize matching certain positions over others.
107  /// </summary>
108  public IEnumerable<OptionStrategyDefinitionMatch> Match(OptionPositionCollection positions)
109  {
110  return Match(OptionStrategyMatcherOptions.ForDefinitions(this), positions);
111  }
112 
113  /// <summary>
114  /// Determines all possible matches for this definition using the provided <paramref name="positions"/>.
115  /// This includes OVERLAPPING matches. It's up to the actual matcher to make decisions based on which
116  /// matches to accept. This allows the matcher to prioritize matching certain positions over others.
117  /// </summary>
118  public IEnumerable<OptionStrategyDefinitionMatch> Match(
120  OptionPositionCollection positions
121  )
122  {
123  // TODO : Pass OptionStrategyMatcherOptions in and respect applicable options
124  if (positions.Count < LegCount)
125  {
126  return Enumerable.Empty<OptionStrategyDefinitionMatch>();
127  }
128 
129  var multiplier = int.MaxValue;
130 
131  // first check underlying lots has correct sign and sufficient magnitude
132  var underlyingLotsSign = Math.Sign(UnderlyingLots);
133  if (underlyingLotsSign != 0)
134  {
135  var underlyingPositionSign = Math.Sign(positions.UnderlyingQuantity);
136  if (underlyingLotsSign != underlyingPositionSign ||
137  Math.Abs(positions.UnderlyingQuantity) < Math.Abs(UnderlyingLots))
138  {
139  return Enumerable.Empty<OptionStrategyDefinitionMatch>();
140  }
141 
142  // set multiplier for underlying
143  multiplier = positions.UnderlyingQuantity / UnderlyingLots;
144  }
145 
146  // TODO : Consider add OptionStrategyLegDefinition for underlying for consistency purposes.
147  // Might want to enforce that it's always the first leg definition as well for easier slicing.
148  return Match(options,
149  ImmutableList<OptionStrategyLegDefinitionMatch>.Empty,
150  ImmutableList<OptionPosition>.Empty,
151  positions,
152  multiplier
153  ).Distinct();
154  }
155 
156  private IEnumerable<OptionStrategyDefinitionMatch> Match(
158  ImmutableList<OptionStrategyLegDefinitionMatch> legMatches,
159  ImmutableList<OptionPosition> legPositions,
160  OptionPositionCollection positions,
161  int multiplier
162  )
163  {
164  var nextLegIndex = legPositions.Count;
165  if (nextLegIndex == Legs.Count)
166  {
167  if (nextLegIndex > 0)
168  {
169  yield return new OptionStrategyDefinitionMatch(this, legMatches, multiplier);
170  }
171  }
172  else if (positions.Count >= LegCount - nextLegIndex)
173  {
174  // grab the next leg definition and perform the match, restricting total to configured maximum per leg
175  var nextLeg = Legs[nextLegIndex];
176  var maxLegMatch = options.GetMaximumLegMatches(nextLegIndex);
177  foreach (var legMatch in nextLeg.Match(options, legPositions, positions).Take(maxLegMatch))
178  {
179  // add match to the match we're constructing and deduct matched position from positions collection
180  // we track the min multiplier in line so when we're done, we have the total number of matches for
181  // the matched set of positions in this 'thread' (OptionStrategy.Quantity)
182  foreach (var definitionMatch in Match(options,
183  legMatches.Add(legMatch),
184  legPositions.Add(legMatch.Position),
185  positions - legMatch.Position,
186  Math.Min(multiplier, legMatch.Multiplier)
187  ))
188  {
189  yield return definitionMatch;
190  }
191  }
192  }
193  else
194  {
195  // positions.Count < LegsCount indicates a failed match
196 
197  // could include partial matches, would allow an algorithm to determine if adding a
198  // new position could help reduce overall margin exposure by completing a strategy
199  }
200  }
201 
202  /// <summary>Returns a string that represents the current object.</summary>
203  /// <returns>A string that represents the current object.</returns>
204  public override string ToString()
205  {
206  return Name;
207  }
208 
209  /// <summary>
210  /// Factory function for creating definitions
211  /// </summary>
212  public static OptionStrategyDefinition Create(string name, int underlyingLots, params OptionStrategyLegDefinition[] legs)
213  {
214  return new OptionStrategyDefinition(name, underlyingLots, legs);
215  }
216 
217  /// <summary>
218  /// Factory function for creating definitions
219  /// </summary>
220  public static OptionStrategyDefinition Create(string name, params OptionStrategyLegDefinition[] legs)
221  {
222  return new OptionStrategyDefinition(name, 0, legs);
223  }
224 
225  /// <summary>
226  /// Factory function for creating definitions
227  /// </summary>
228  public static OptionStrategyDefinition Create(string name, params Func<Builder, Builder>[] predicates)
229  {
230  return predicates.Aggregate(new Builder(name),
231  (builder, predicate) => predicate(builder)
232  ).Build();
233  }
234 
235  /// <summary>
236  /// Factory function for creating a call leg definition
237  /// </summary>
238  public static OptionStrategyLegDefinition CallLeg(int quantity,
239  params Expression<Func<IReadOnlyList<OptionPosition>, OptionPosition, bool>>[] predicates
240  )
241  {
242  return OptionStrategyLegDefinition.Create(OptionRight.Call, quantity, predicates);
243  }
244 
245  /// <summary>
246  /// Factory function for creating a put leg definition
247  /// </summary>
248  public static OptionStrategyLegDefinition PutLeg(int quantity,
249  params Expression<Func<IReadOnlyList<OptionPosition>, OptionPosition, bool>>[] predicates
250  )
251  {
252  return OptionStrategyLegDefinition.Create(OptionRight.Put, quantity, predicates);
253  }
254 
255  /// <summary>
256  /// Builder class supporting fluent syntax in constructing <see cref="OptionStrategyDefinition"/>.
257  /// </summary>
258  public class Builder
259  {
260  private readonly string _name;
261 
262  private int _underlyingLots;
263  private List<OptionStrategyLegDefinition> _legs;
264 
265  /// <summary>
266  /// Initializes a new instance of the <see cref="Builder"/> class
267  /// </summary>
268  public Builder(string name)
269  {
270  _name = name;
271  _legs = new List<OptionStrategyLegDefinition>();
272  }
273 
274  /// <summary>
275  /// Sets the required number of underlying lots
276  /// </summary>
277  public Builder WithUnderlyingLots(int lots)
278  {
279  if (_underlyingLots != 0)
280  {
281  throw new InvalidOperationException("Underlying lots has already been set.");
282  }
283 
284  _underlyingLots = lots;
285  return this;
286  }
287 
288  /// <summary>
289  /// Adds a call leg
290  /// </summary>
291  public Builder WithCall(int quantity,
292  params Expression<Func<IReadOnlyList<OptionPosition>, OptionPosition, bool>>[] predicates
293  )
294  {
295  _legs.Add(OptionStrategyLegDefinition.Create(OptionRight.Call, quantity, predicates));
296  return this;
297  }
298 
299  /// <summary>
300  /// Adds a put leg
301  /// </summary>
302  public Builder WithPut(int quantity,
303  params Expression<Func<IReadOnlyList<OptionPosition>, OptionPosition, bool>>[] predicates
304  )
305  {
306  _legs.Add(OptionStrategyLegDefinition.Create(OptionRight.Put, quantity, predicates));
307  return this;
308  }
309 
310  /// <summary>
311  /// Builds the <see cref="OptionStrategyDefinition"/>
312  /// </summary>
314  {
315  return new OptionStrategyDefinition(_name, _underlyingLots, _legs);
316  }
317  }
318 
319  /// <summary>Returns an enumerator that iterates through the collection.</summary>
320  /// <returns>An enumerator that can be used to iterate through the collection.</returns>
321  public IEnumerator<OptionStrategyLegDefinition> GetEnumerator()
322  {
323  return Legs.GetEnumerator();
324  }
325 
326  /// <summary>Returns an enumerator that iterates through a collection.</summary>
327  /// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
328  IEnumerator IEnumerable.GetEnumerator()
329  {
330  return GetEnumerator();
331  }
332  }
333 }