Lean  $LEAN_TAG$
OptionFilterUniverse.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 
17 using System;
18 using System.Collections.Generic;
19 using QuantConnect.Data;
20 using System.Linq;
24 
26 {
27  /// <summary>
28  /// Represents options symbols universe used in filtering.
29  /// </summary>
30  public class OptionFilterUniverse : ContractSecurityFilterUniverse<OptionFilterUniverse>
31  {
32  // Fields used in relative strikes filter
33  private List<decimal> _uniqueStrikes;
34  private bool _refreshUniqueStrikes;
35  private DateTime _lastExchangeDate;
36  private readonly decimal _underlyingScaleFactor = 1;
37 
38  /// <summary>
39  /// The underlying price data
40  /// </summary>
41  protected BaseData UnderlyingInternal { get; set; }
42 
43  /// <summary>
44  /// The underlying price data
45  /// </summary>
46  public BaseData Underlying
47  {
48  get
49  {
50  return UnderlyingInternal;
51  }
52  }
53 
54  /// <summary>
55  /// Constructs OptionFilterUniverse
56  /// </summary>
57  /// <param name="option">The canonical option chain security</param>
59  {
60  _underlyingScaleFactor = option.SymbolProperties.StrikeMultiplier;
61  }
62 
63  /// <summary>
64  /// Constructs OptionFilterUniverse
65  /// </summary>
66  /// <remarks>Used for testing only</remarks>
67  public OptionFilterUniverse(IEnumerable<Symbol> allSymbols, BaseData underlying, decimal underlyingScaleFactor = 1)
68  : base(allSymbols, underlying.EndTime)
69  {
70  UnderlyingInternal = underlying;
71  _refreshUniqueStrikes = true;
72  _underlyingScaleFactor = underlyingScaleFactor;
73  }
74 
75  /// <summary>
76  /// Refreshes this option filter universe and allows specifying if the exchange date changed from last call
77  /// </summary>
78  /// <param name="allSymbols">All the options contract symbols</param>
79  /// <param name="underlying">The current underlying last data point</param>
80  /// <param name="localTime">The current local time</param>
81  public void Refresh(IEnumerable<Symbol> allSymbols, BaseData underlying, DateTime localTime)
82  {
83  base.Refresh(allSymbols, localTime);
84 
85  UnderlyingInternal = underlying;
86  _refreshUniqueStrikes = _lastExchangeDate != localTime.Date;
87  _lastExchangeDate = localTime.Date;
88  }
89 
90  /// <summary>
91  /// Determine if the given Option contract symbol is standard
92  /// </summary>
93  /// <returns>True if standard</returns>
94  protected override bool IsStandard(Symbol symbol)
95  {
96  switch (symbol.SecurityType)
97  {
98  case SecurityType.FutureOption:
99  return FutureOptionSymbol.IsStandard(symbol);
100  case SecurityType.IndexOption:
101  return IndexOptionSymbol.IsStandard(symbol);
102  default:
103  return OptionSymbol.IsStandard(symbol);
104  }
105  }
106 
107  /// <summary>
108  /// Applies filter selecting options contracts based on a range of strikes in relative terms
109  /// </summary>
110  /// <param name="minStrike">The minimum strike relative to the underlying price, for example, -1 would filter out contracts further than 1 strike below market price</param>
111  /// <param name="maxStrike">The maximum strike relative to the underlying price, for example, +1 would filter out contracts further than 1 strike above market price</param>
112  /// <returns>Universe with filter applied</returns>
113  public OptionFilterUniverse Strikes(int minStrike, int maxStrike)
114  {
115  if (UnderlyingInternal == null)
116  {
117  return this;
118  }
119 
120  if (_refreshUniqueStrikes || _uniqueStrikes == null)
121  {
122  // Each day we need to recompute the unique strikes list.
123  _uniqueStrikes = AllSymbols.Select(x => x.ID.StrikePrice)
124  .Distinct()
125  .OrderBy(strikePrice => strikePrice)
126  .ToList();
127  _refreshUniqueStrikes = false;
128  }
129 
130  // find the current price in the list of strikes
131  // When computing the strike prices we need to take into account
132  // that some option's strike prices are based on a fraction of
133  // the underlying. Thus we need to scale the underlying internal
134  // price so that we can find it among the strike prices
135  // using BinarySearch() method(as it is used below)
136  var exactPriceFound = true;
137  var index = _uniqueStrikes.BinarySearch(UnderlyingInternal.Price / _underlyingScaleFactor);
138 
139  // Return value of BinarySearch (from MSDN):
140  // The zero-based index of item in the sorted List<T>, if item is found;
141  // otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item
142  // or, if there is no larger element, the bitwise complement of Count.
143  if (index < 0)
144  {
145  // exact price not found
146  exactPriceFound = false;
147 
148  if (index == ~_uniqueStrikes.Count)
149  {
150  // there is no greater price, return empty
151  AllSymbols = Enumerable.Empty<Symbol>();
152  return this;
153  }
154 
155  index = ~index;
156  }
157 
158  // compute the bounds, no need to worry about rounding and such
159  var indexMinPrice = index + minStrike;
160  var indexMaxPrice = index + maxStrike;
161  if (!exactPriceFound)
162  {
163  if (minStrike < 0 && maxStrike > 0)
164  {
165  indexMaxPrice--;
166  }
167  else if (minStrike > 0)
168  {
169  indexMinPrice--;
170  indexMaxPrice--;
171  }
172  }
173 
174  if (indexMinPrice < 0)
175  {
176  indexMinPrice = 0;
177  }
178  else if (indexMinPrice >= _uniqueStrikes.Count)
179  {
180  // price out of range: return empty
181  AllSymbols = Enumerable.Empty<Symbol>();
182  return this;
183  }
184 
185  if (indexMaxPrice < 0)
186  {
187  // price out of range: return empty
188  AllSymbols = Enumerable.Empty<Symbol>();
189  return this;
190  }
191  if (indexMaxPrice >= _uniqueStrikes.Count)
192  {
193  indexMaxPrice = _uniqueStrikes.Count - 1;
194  }
195 
196  var minPrice = _uniqueStrikes[indexMinPrice];
197  var maxPrice = _uniqueStrikes[indexMaxPrice];
198 
199  AllSymbols = AllSymbols
200  .Where(symbol =>
201  {
202  var price = symbol.ID.StrikePrice;
203  return price >= minPrice && price <= maxPrice;
204  }
205  ).ToList();
206 
207  return this;
208  }
209 
210  /// <summary>
211  /// Sets universe of call options (if any) as a selection
212  /// </summary>
213  /// <returns>Universe with filter applied</returns>
215  {
216  return Contracts(contracts => contracts.Where(x => x.ID.OptionRight == OptionRight.Call));
217  }
218 
219  /// <summary>
220  /// Sets universe of put options (if any) as a selection
221  /// </summary>
222  /// <returns>Universe with filter applied</returns>
224  {
225  return Contracts(contracts => contracts.Where(x => x.ID.OptionRight == OptionRight.Put));
226  }
227  }
228 
229  /// <summary>
230  /// Extensions for Linq support
231  /// </summary>
232  public static class OptionFilterUniverseEx
233  {
234  /// <summary>
235  /// Filters universe
236  /// </summary>
237  /// <param name="universe">Universe to apply the filter too</param>
238  /// <param name="predicate">Bool function to determine which Symbol are filtered</param>
239  /// <returns>Universe with filter applied</returns>
240  public static OptionFilterUniverse Where(this OptionFilterUniverse universe, Func<Symbol, bool> predicate)
241  {
242  universe.AllSymbols = universe.AllSymbols.Where(predicate).ToList();
243  return universe;
244  }
245 
246  /// <summary>
247  /// Maps universe
248  /// </summary>
249  /// <param name="universe">Universe to apply the filter too</param>
250  /// <param name="mapFunc">Symbol function to determine which Symbols are filtered</param>
251  /// <returns>Universe with filter applied</returns>
252  public static OptionFilterUniverse Select(this OptionFilterUniverse universe, Func<Symbol, Symbol> mapFunc)
253  {
254  universe.AllSymbols = universe.AllSymbols.Select(mapFunc).ToList();
255  return universe;
256  }
257 
258  /// <summary>
259  /// Binds universe
260  /// </summary>
261  /// <param name="universe">Universe to apply the filter too</param>
262  /// <param name="mapFunc">Symbol function to determine which Symbols are filtered</param>
263  /// <returns>Universe with filter applied</returns>
264  public static OptionFilterUniverse SelectMany(this OptionFilterUniverse universe, Func<Symbol, IEnumerable<Symbol>> mapFunc)
265  {
266  universe.AllSymbols = universe.AllSymbols.SelectMany(mapFunc).ToList();
267  return universe;
268  }
269 
270  /// <summary>
271  /// Updates universe to only contain the symbols in the list
272  /// </summary>
273  /// <param name="universe">Universe to apply the filter too</param>
274  /// <param name="filterList">List of Symbols to keep in the Universe</param>
275  /// <returns>Universe with filter applied</returns>
276  public static OptionFilterUniverse WhereContains(this OptionFilterUniverse universe, List<Symbol> filterList)
277  {
278  universe.AllSymbols = universe.AllSymbols.Where(filterList.Contains).ToList();
279  return universe;
280  }
281  }
282 }