Lean  $LEAN_TAG$
BaseChain.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 Python.Runtime;
21 using QuantConnect.Python;
23 using QuantConnect.Util;
24 
26 {
27  /// <summary>
28  /// Base representation of an entire chain of contracts for a single underlying security.
29  /// This type is <see cref="IEnumerable{T}"/> where T is <see cref="OptionContract"/>, <see cref="FuturesContract"/>, etc.
30  /// </summary>
31  public class BaseChain<T, TContractsCollection> : BaseData, IEnumerable<T>
32  where T : BaseContract
33  where TContractsCollection : DataDictionary<T>, new()
34  {
35  private Dictionary<Type, Dictionary<Symbol, List<BaseData>>> _auxiliaryData;
36  private readonly Lazy<PyObject> _dataframe;
37  private readonly bool _flatten;
38 
39  private Dictionary<Type, Dictionary<Symbol, List<BaseData>>> AuxiliaryData
40  {
41  get
42  {
43  if (_auxiliaryData == null)
44  {
45  _auxiliaryData = new Dictionary<Type, Dictionary<Symbol, List<BaseData>>>();
46  }
47 
48  return _auxiliaryData;
49  }
50  }
51 
52  /// <summary>
53  /// Gets the most recent trade information for the underlying. This may
54  /// be a <see cref="Tick"/> or a <see cref="TradeBar"/>
55  /// </summary>
56  [PandasIgnore]
57  public BaseData Underlying
58  {
59  get; internal set;
60  }
61 
62  /// <summary>
63  /// Gets all ticks for every option contract in this chain, keyed by option symbol
64  /// </summary>
65  [PandasIgnore]
66  public Ticks Ticks
67  {
68  get; protected set;
69  }
70 
71  /// <summary>
72  /// Gets all trade bars for every option contract in this chain, keyed by option symbol
73  /// </summary>
74  [PandasIgnore]
75  public TradeBars TradeBars
76  {
77  get; protected set;
78  }
79 
80  /// <summary>
81  /// Gets all quote bars for every option contract in this chain, keyed by option symbol
82  /// </summary>
83  [PandasIgnore]
84  public QuoteBars QuoteBars
85  {
86  get; protected set;
87  }
88 
89  /// <summary>
90  /// Gets all contracts in the chain, keyed by option symbol
91  /// </summary>
92  public TContractsCollection Contracts
93  {
94  get; private set;
95  }
96 
97  /// <summary>
98  /// Gets the set of symbols that passed the <see cref="Option.ContractFilter"/>
99  /// </summary>
100  [PandasIgnore]
101  public HashSet<Symbol> FilteredContracts
102  {
103  get; protected set;
104  }
105 
106  /// <summary>
107  /// The data frame representation of the option chain
108  /// </summary>
109  [PandasIgnore]
110  public PyObject DataFrame => _dataframe.Value;
111 
112  /// <summary>
113  /// Initializes a new default instance of the <see cref="BaseChain{T, TContractsCollection}"/> class
114  /// </summary>
115  protected BaseChain(MarketDataType dataType, bool flatten)
116  {
117  DataType = dataType;
118  _flatten = flatten;
119  _dataframe = new Lazy<PyObject>(
120  () =>
121  {
122  if (!PythonEngine.IsInitialized)
123  {
124  return null;
125  }
126  return new PandasConverter().GetDataFrame(new[] { this }, symbolOnlyIndex: true, flatten: _flatten);
127  },
128  isThreadSafe: false);
129  }
130 
131  /// <summary>
132  /// Initializes a new instance of the <see cref="BaseChain{T, TContractsCollection}"/> class
133  /// </summary>
134  /// <param name="canonicalOptionSymbol">The symbol for this chain.</param>
135  /// <param name="time">The time of this chain</param>
136  /// <param name="flatten">Whether to flatten the data frame</param>
137  protected BaseChain(Symbol canonicalOptionSymbol, DateTime time, MarketDataType dataType, bool flatten = true)
138  : this(dataType, flatten)
139  {
140  Time = time;
141  Symbol = canonicalOptionSymbol;
142  Ticks = new Ticks(time);
143  TradeBars = new TradeBars(time);
144  QuoteBars = new QuoteBars(time);
145  FilteredContracts = new HashSet<Symbol>();
146  Underlying = new QuoteBar();
147  Contracts = new();
148  Contracts.Time = time;
149  }
150 
151  /// <summary>
152  /// Initializes a new instance of the <see cref="BaseChain{T, TContractsCollection}"/> class as a copy of the specified chain
153  /// </summary>
155  : this(other.DataType, other._flatten)
156  {
157  Symbol = other.Symbol;
158  Time = other.Time;
159  Value = other.Value;
160  Underlying = other.Underlying;
161  Ticks = other.Ticks;
162  QuoteBars = other.QuoteBars;
163  TradeBars = other.TradeBars;
164  Contracts = other.Contracts;
166  }
167 
168  /// <summary>
169  /// Gets the auxiliary data with the specified type and symbol
170  /// </summary>
171  /// <typeparam name="TAux">The type of auxiliary data</typeparam>
172  /// <param name="symbol">The symbol of the auxiliary data</param>
173  /// <returns>The last auxiliary data with the specified type and symbol</returns>
174  public TAux GetAux<TAux>(Symbol symbol)
175  {
176  List<BaseData> list;
177  Dictionary<Symbol, List<BaseData>> dictionary;
178  if (!AuxiliaryData.TryGetValue(typeof(TAux), out dictionary) || !dictionary.TryGetValue(symbol, out list))
179  {
180  return default;
181  }
182  return list.OfType<TAux>().LastOrDefault();
183  }
184 
185  /// <summary>
186  /// Gets all auxiliary data of the specified type as a dictionary keyed by symbol
187  /// </summary>
188  /// <typeparam name="TAux">The type of auxiliary data</typeparam>
189  /// <returns>A dictionary containing all auxiliary data of the specified type</returns>
191  {
192  Dictionary<Symbol, List<BaseData>> d;
193  if (!AuxiliaryData.TryGetValue(typeof(TAux), out d))
194  {
195  return new DataDictionary<TAux>();
196  }
197  var dictionary = new DataDictionary<TAux>();
198  foreach (var kvp in d)
199  {
200  var item = kvp.Value.OfType<TAux>().LastOrDefault();
201  if (item != null)
202  {
203  dictionary.Add(kvp.Key, item);
204  }
205  }
206  return dictionary;
207  }
208 
209  /// <summary>
210  /// Gets all auxiliary data of the specified type as a dictionary keyed by symbol
211  /// </summary>
212  /// <typeparam name="TAux">The type of auxiliary data</typeparam>
213  /// <returns>A dictionary containing all auxiliary data of the specified type</returns>
214  public Dictionary<Symbol, List<BaseData>> GetAuxList<TAux>()
215  {
216  Dictionary<Symbol, List<BaseData>> dictionary;
217  if (!AuxiliaryData.TryGetValue(typeof(TAux), out dictionary))
218  {
219  return new Dictionary<Symbol, List<BaseData>>();
220  }
221  return dictionary;
222  }
223 
224  /// <summary>
225  /// Gets a list of auxiliary data with the specified type and symbol
226  /// </summary>
227  /// <typeparam name="TAux">The type of auxiliary data</typeparam>
228  /// <param name="symbol">The symbol of the auxiliary data</param>
229  /// <returns>The list of auxiliary data with the specified type and symbol</returns>
230  public List<TAux> GetAuxList<TAux>(Symbol symbol)
231  {
232  List<BaseData> list;
233  Dictionary<Symbol, List<BaseData>> dictionary;
234  if (!AuxiliaryData.TryGetValue(typeof(TAux), out dictionary) || !dictionary.TryGetValue(symbol, out list))
235  {
236  return new List<TAux>();
237  }
238  return list.OfType<TAux>().ToList();
239  }
240 
241  /// <summary>
242  /// Returns an enumerator that iterates through the collection.
243  /// </summary>
244  /// <returns>
245  /// An enumerator that can be used to iterate through the collection.
246  /// </returns>
247  public IEnumerator<T> GetEnumerator()
248  {
249  return Contracts.Values.GetEnumerator();
250  }
251 
252  /// <summary>
253  /// Returns an enumerator that iterates through a collection.
254  /// </summary>
255  /// <returns>
256  /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
257  /// </returns>
258  IEnumerator IEnumerable.GetEnumerator()
259  {
260  return GetEnumerator();
261  }
262 
263  /// <summary>
264  /// Adds the specified data to this chain
265  /// </summary>
266  /// <param name="data">The data to be added</param>
267  internal void AddData(BaseData data)
268  {
269  switch (data)
270  {
271  case Tick tick:
272  Ticks.Add(tick.Symbol, tick);
273  break;
274 
275  case TradeBar tradeBar:
276  TradeBars[tradeBar.Symbol] = tradeBar;
277  break;
278 
279  case QuoteBar quoteBar:
280  QuoteBars[quoteBar.Symbol] = quoteBar;
281  break;
282 
283  default:
284  if (data.DataType == MarketDataType.Base)
285  {
286  AddAuxData(data);
287  }
288  break;
289  }
290  }
291 
292  /// <summary>
293  /// Adds the specified auxiliary data to this option chain
294  /// </summary>
295  /// <param name="baseData">The auxiliary data to be added</param>
296  private void AddAuxData(BaseData baseData)
297  {
298  var type = baseData.GetType();
299  Dictionary<Symbol, List<BaseData>> dictionary;
300  if (!AuxiliaryData.TryGetValue(type, out dictionary))
301  {
302  dictionary = new Dictionary<Symbol, List<BaseData>>();
303  AuxiliaryData[type] = dictionary;
304  }
305 
306  List<BaseData> list;
307  if (!dictionary.TryGetValue(baseData.Symbol, out list))
308  {
309  list = new List<BaseData>();
310  dictionary[baseData.Symbol] = list;
311  }
312  list.Add(baseData);
313  }
314  }
315 }