Lean  $LEAN_TAG$
OptionUniverse.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.IO;
18 using System.Runtime.CompilerServices;
20 using QuantConnect.Python;
21 
23 {
24  /// <summary>
25  /// Represents a universe of options data
26  /// </summary>
28  {
29  private const int StartingGreeksCsvIndex = 7;
30 
31  /// <summary>
32  /// Open interest value of the option
33  /// </summary>
34  public override decimal OpenInterest
35  {
36  get
37  {
38  ThrowIfNotAnOption(nameof(OpenInterest));
39  return base.OpenInterest;
40  }
41  }
42 
43  /// <summary>
44  /// Implied volatility value of the option
45  /// </summary>
46  public decimal ImpliedVolatility
47  {
48  get
49  {
50  ThrowIfNotAnOption(nameof(ImpliedVolatility));
51  return CsvLine.GetDecimalFromCsv(6);
52  }
53  }
54 
55  /// <summary>
56  /// Greeks values of the option
57  /// </summary>
58  public Greeks Greeks
59  {
60  get
61  {
62  ThrowIfNotAnOption(nameof(Greeks));
63  return new PreCalculatedGreeks(CsvLine);
64  }
65  }
66 
67  /// <summary>
68  /// Creates a new instance of the <see cref="OptionUniverse"/> class
69  /// </summary>
70  public OptionUniverse()
71  {
72  }
73 
74  /// <summary>
75  /// Creates a new instance of the <see cref="OptionUniverse"/> class
76  /// </summary>
77  public OptionUniverse(DateTime date, Symbol symbol, string csv)
78  : base(date, symbol, csv)
79  {
80  }
81 
82  /// <summary>
83  /// Creates a new instance of the <see cref="OptionUniverse"/> class as a copy of the given instance
84  /// </summary>
86  : base(other)
87  {
88  }
89 
90  /// <summary>
91  /// Reader converts each line of the data source into BaseData objects. Each data type creates its own factory method, and returns a new instance of the object
92  /// each time it is called.
93  /// </summary>
94  /// <param name="config">Subscription data config setup object</param>
95  /// <param name="stream">Stream reader of the source document</param>
96  /// <param name="date">Date of the requested data</param>
97  /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
98  /// <returns>Instance of the T:BaseData object generated by this line of the CSV</returns>
99  public override BaseData Reader(SubscriptionDataConfig config, StreamReader stream, DateTime date, bool isLiveMode)
100  {
101  if (TryRead(config, stream, date, out var symbol, out var remainingLine))
102  {
103  return new OptionUniverse(date, symbol, remainingLine);
104  }
105 
106  return null;
107  }
108 
109  /// <summary>
110  /// Adds a new data point to this collection.
111  /// If the data point is for the underlying, it will be stored in the <see cref="BaseDataCollection.Underlying"/> property.
112  /// </summary>
113  /// <param name="newDataPoint">The new data point to add</param>
114  public override void Add(BaseData newDataPoint)
115  {
116  if (newDataPoint is BaseChainUniverseData optionUniverseDataPoint)
117  {
118  if (optionUniverseDataPoint.Symbol.HasUnderlying)
119  {
120  optionUniverseDataPoint.Underlying = Underlying;
121  base.Add(optionUniverseDataPoint);
122  }
123  else
124  {
125  Underlying = optionUniverseDataPoint;
126  foreach (BaseChainUniverseData data in Data)
127  {
128  data.Underlying = optionUniverseDataPoint;
129  }
130  }
131  }
132  }
133 
134  /// <summary>
135  /// Creates a copy of the instance
136  /// </summary>
137  /// <returns>Clone of the instance</returns>
138  public override BaseData Clone()
139  {
140  return new OptionUniverse(this);
141  }
142 
143  /// <summary>
144  /// Gets the CSV string representation of this universe entry
145  /// </summary>
146  public static string ToCsv(Symbol symbol, decimal open, decimal high, decimal low, decimal close, decimal volume, decimal? openInterest,
147  decimal? impliedVolatility, Greeks greeks)
148  {
149  if (symbol.SecurityType == SecurityType.FutureOption || symbol.SecurityType == SecurityType.Future)
150  {
151  return $"{symbol.ID},{symbol.Value},{open},{high},{low},{close},{volume},{openInterest}";
152  }
153 
154  return $"{symbol.ID},{symbol.Value},{open},{high},{low},{close},{volume},"
155  + $"{openInterest},{impliedVolatility},{greeks?.Delta},{greeks?.Gamma},{greeks?.Vega},{greeks?.Theta},{greeks?.Rho}";
156  }
157 
158  /// <summary>
159  /// Implicit conversion into <see cref="Symbol"/>
160  /// </summary>
161  /// <param name="data">The option universe data to be converted</param>
162 #pragma warning disable CA2225 // Operator overloads have named alternates
163  public static implicit operator Symbol(OptionUniverse data)
164 #pragma warning restore CA2225 // Operator overloads have named alternates
165  {
166  return data.Symbol;
167  }
168 
169  /// <summary>
170  /// Gets the CSV header string for this universe entry
171  /// </summary>
172  public static string CsvHeader(SecurityType securityType)
173  {
174  // FOPs don't have greeks
175  if (securityType == SecurityType.FutureOption || securityType == SecurityType.Future)
176  {
177  return "symbol_id,symbol_value,open,high,low,close,volume,open_interest";
178  }
179 
180  return "symbol_id,symbol_value,open,high,low,close,volume,open_interest,implied_volatility,delta,gamma,vega,theta,rho";
181  }
182 
183  [MethodImpl(MethodImplOptions.AggressiveInlining)]
184  private void ThrowIfNotAnOption(string propertyName)
185  {
186  if (!Symbol.SecurityType.IsOption())
187  {
188  throw new InvalidOperationException($"{propertyName} is only available for options.");
189  }
190  }
191 
192  /// <summary>
193  /// Pre-calculated greeks lazily parsed from csv line.
194  /// It parses the greeks values from the csv line only when they are requested to avoid holding decimals in memory.
195  /// </summary>
196  private class PreCalculatedGreeks : Greeks
197  {
198  private readonly string _csvLine;
199 
200  public override decimal Delta => _csvLine.GetDecimalFromCsv(StartingGreeksCsvIndex);
201 
202  public override decimal Gamma => _csvLine.GetDecimalFromCsv(StartingGreeksCsvIndex + 1);
203 
204  public override decimal Vega => _csvLine.GetDecimalFromCsv(StartingGreeksCsvIndex + 2);
205 
206  public override decimal Theta => _csvLine.GetDecimalFromCsv(StartingGreeksCsvIndex + 3);
207 
208  public override decimal Rho => _csvLine.GetDecimalFromCsv(StartingGreeksCsvIndex + 4);
209 
210  [PandasIgnore]
211  public override decimal Lambda => decimal.Zero;
212 
213  /// <summary>
214  /// Initializes a new default instance of the <see cref="PreCalculatedGreeks"/> class
215  /// </summary>
216  public PreCalculatedGreeks(string csvLine)
217  {
218  _csvLine = csvLine;
219  }
220 
221  /// <summary>
222  /// Gets a string representation of the greeks values
223  /// </summary>
224  public override string ToString()
225  {
226  return $"D: {Delta}, G: {Gamma}, V: {Vega}, T: {Theta}, R: {Rho}";
227  }
228  }
229  }
230 }