Lean  $LEAN_TAG$
OptionContract.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 
18 using QuantConnect.Python;
21 using System;
22 
24 {
25  /// <summary>
26  /// Defines a single option contract at a specific expiration and strike price
27  /// </summary>
29  {
30  private IOptionData _optionData = OptionPriceModelResultData.Null;
31  private readonly SymbolProperties _symbolProperties;
32 
33  /// <summary>
34  /// Gets the option contract's symbol
35  /// </summary>
36  [PandasIgnore]
37  public Symbol Symbol
38  {
39  get; set;
40  }
41 
42  /// <summary>
43  /// The security identifier of the option symbol
44  /// </summary>
45  [PandasIgnore]
47 
48  /// <summary>
49  /// Gets the underlying security's symbol
50  /// </summary>
52 
53  /// <summary>
54  /// Gets the strike price
55  /// </summary>
56  public decimal Strike => Symbol.ID.StrikePrice;
57 
58  /// <summary>
59  /// Gets the strike price multiplied by the strike multiplier
60  /// </summary>
61  public decimal ScaledStrike => Strike * _symbolProperties.StrikeMultiplier;
62 
63  /// <summary>
64  /// Gets the expiration date
65  /// </summary>
66  public DateTime Expiry => Symbol.ID.Date;
67 
68  /// <summary>
69  /// Gets the right being purchased (call [right to buy] or put [right to sell])
70  /// </summary>
72 
73  /// <summary>
74  /// Gets the option style
75  /// </summary>
77 
78  /// <summary>
79  /// Gets the theoretical price of this option contract as computed by the <see cref="IOptionPriceModel"/>
80  /// </summary>
81  public decimal TheoreticalPrice => _optionData.TheoreticalPrice;
82 
83  /// <summary>
84  /// Gets the implied volatility of the option contract as computed by the <see cref="IOptionPriceModel"/>
85  /// </summary>
86  public decimal ImpliedVolatility => _optionData.ImpliedVolatility;
87 
88  /// <summary>
89  /// Gets the greeks for this contract
90  /// </summary>
91  public Greeks Greeks => _optionData.Greeks;
92 
93  /// <summary>
94  /// Gets the local date time this contract's data was last updated
95  /// </summary>
96  [PandasIgnore]
97  public DateTime Time
98  {
99  get; set;
100  }
101 
102  /// <summary>
103  /// Gets the open interest
104  /// </summary>
105  public decimal OpenInterest => _optionData.OpenInterest;
106 
107  /// <summary>
108  /// Gets the last price this contract traded at
109  /// </summary>
110  public decimal LastPrice => _optionData.LastPrice;
111 
112  /// <summary>
113  /// Gets the last volume this contract traded at
114  /// </summary>
115  public long Volume => _optionData.Volume;
116 
117  /// <summary>
118  /// Gets the current bid price
119  /// </summary>
120  public decimal BidPrice => _optionData.BidPrice;
121 
122  /// <summary>
123  /// Get the current bid size
124  /// </summary>
125  public long BidSize => _optionData.BidSize;
126 
127  /// <summary>
128  /// Gets the ask price
129  /// </summary>
130  public decimal AskPrice => _optionData.AskPrice;
131 
132  /// <summary>
133  /// Gets the current ask size
134  /// </summary>
135  public long AskSize => _optionData.AskSize;
136 
137  /// <summary>
138  /// Gets the last price the underlying security traded at
139  /// </summary>
140  public decimal UnderlyingLastPrice => _optionData.UnderlyingLastPrice;
141 
142  /// <summary>
143  /// Initializes a new instance of the <see cref="OptionContract"/> class
144  /// </summary>
145  /// <param name="security">The option contract security</param>
147  {
148  Symbol = security.Symbol;
149  _symbolProperties = security.SymbolProperties;
150  }
151 
152  /// <summary>
153  /// Initializes a new option contract from a given <see cref="OptionUniverse"/> instance
154  /// </summary>
155  /// <param name="contractData">The option universe contract data to use as source for this contract</param>
156  /// <param name="symbolProperties">The contract symbol properties</param>
157  public OptionContract(OptionUniverse contractData, SymbolProperties symbolProperties)
158  {
159  Symbol = contractData.Symbol;
160  _symbolProperties = symbolProperties;
161  _optionData = new OptionUniverseData(contractData);
162  }
163 
164  /// <summary>
165  /// Sets the option price model evaluator function to be used for this contract
166  /// </summary>
167  /// <param name="optionPriceModelEvaluator">Function delegate used to evaluate the option price model</param>
168  internal void SetOptionPriceModel(Func<OptionPriceModelResult> optionPriceModelEvaluator)
169  {
170  _optionData = new OptionPriceModelResultData(optionPriceModelEvaluator, _optionData as OptionPriceModelResultData);
171  }
172 
173  /// <summary>
174  /// Returns a string that represents the current object.
175  /// </summary>
176  /// <returns>
177  /// A string that represents the current object.
178  /// </returns>
179  public override string ToString() => Symbol.Value;
180 
181  /// <summary>
182  /// Creates a <see cref="OptionContract"/>
183  /// </summary>
184  /// <param name="baseData"></param>
185  /// <param name="security">Provides price properties for a <see cref="Security"/></param>
186  /// <param name="underlying">Last underlying security trade data</param>
187  /// <returns>Option contract</returns>
188  public static OptionContract Create(BaseData baseData, ISecurityPrice security, BaseData underlying)
189  => Create(baseData.EndTime, security, underlying);
190 
191  /// <summary>
192  /// Creates a <see cref="OptionContract"/>
193  /// </summary>
194  /// <param name="endTime">local date time this contract's data was last updated</param>
195  /// <param name="security">provides price properties for a <see cref="Security"/></param>
196  /// <param name="underlying">last underlying security trade data</param>
197  /// <returns>Option contract</returns>
198  public static OptionContract Create(DateTime endTime, ISecurityPrice security, BaseData underlying)
199  {
200  var contract = new OptionContract(security)
201  {
202  Time = endTime,
203  };
204  contract._optionData.SetUnderlying(underlying);
205 
206  return contract;
207  }
208 
209  /// <summary>
210  /// Creates a new option contract from a given <see cref="OptionUniverse"/> instance,
211  /// using its data to form a quote bar to source pricing data
212  /// </summary>
213  /// <param name="contractData">The option universe contract data to use as source for this contract</param>
214  /// <param name="symbolProperties">The contract symbol properties</param>
215  public static OptionContract Create(OptionUniverse contractData, SymbolProperties symbolProperties)
216  {
217  var contract = new OptionContract(contractData, symbolProperties)
218  {
219  Time = contractData.EndTime,
220  };
221 
222  return contract;
223  }
224 
225  /// <summary>
226  /// Implicit conversion into <see cref="Symbol"/>
227  /// </summary>
228  /// <param name="contract">The option contract to be converted</param>
229  public static implicit operator Symbol(OptionContract contract)
230  {
231  return contract.Symbol;
232  }
233 
234  /// <summary>
235  /// Updates the option contract with the new data, which can be a <see cref="Tick"/> or <see cref="TradeBar"/> or <see cref="QuoteBar"/>
236  /// </summary>
237  internal void Update(BaseData data)
238  {
239  if (data.Symbol == Symbol)
240  {
241  _optionData.Update(data);
242  }
243  else if (data.Symbol == UnderlyingSymbol)
244  {
245  _optionData.SetUnderlying(data);
246  }
247  }
248 
249  #region Option Contract Data Handlers
250 
251  private interface IOptionData
252  {
253  decimal LastPrice { get; }
254  decimal UnderlyingLastPrice { get; }
255  long Volume { get; }
256  decimal BidPrice { get; }
257  long BidSize { get; }
258  decimal AskPrice { get; }
259  long AskSize { get; }
260  decimal OpenInterest { get; }
261  decimal TheoreticalPrice { get; }
262  decimal ImpliedVolatility { get; }
263  Greeks Greeks { get; }
264 
265  void Update(BaseData data);
266 
267  void SetUnderlying(BaseData data);
268  }
269 
270  /// <summary>
271  /// Handles option data for a contract from actual price data (trade, quote, open interest) and theoretical price model results
272  /// </summary>
273  private class OptionPriceModelResultData : IOptionData
274  {
275  public static readonly OptionPriceModelResultData Null = new(() => OptionPriceModelResult.None);
276 
277  private readonly Lazy<OptionPriceModelResult> _optionPriceModelResult;
278  private TradeBar _tradeBar;
279  private QuoteBar _quoteBar;
280  private OpenInterest _openInterest;
281  private BaseData _underlying;
282 
283  public decimal LastPrice => _tradeBar?.Close ?? decimal.Zero;
284 
285  public decimal UnderlyingLastPrice => _underlying?.Price ?? decimal.Zero;
286 
287  public long Volume => (long)(_tradeBar?.Volume ?? 0L);
288 
289  public decimal BidPrice => _quoteBar?.Bid?.Close ?? decimal.Zero;
290 
291  public long BidSize => (long)(_quoteBar?.LastBidSize ?? 0L);
292 
293  public decimal AskPrice => _quoteBar?.Ask?.Close ?? decimal.Zero;
294 
295  public long AskSize => (long)(_quoteBar?.LastAskSize ?? 0L);
296 
297  public decimal OpenInterest => _openInterest?.Value ?? decimal.Zero;
298 
299  public decimal TheoreticalPrice => _optionPriceModelResult.Value.TheoreticalPrice;
300  public decimal ImpliedVolatility => _optionPriceModelResult.Value.ImpliedVolatility;
301  public Greeks Greeks => _optionPriceModelResult.Value.Greeks;
302 
303  public OptionPriceModelResultData(Func<OptionPriceModelResult> optionPriceModelEvaluator,
304  OptionPriceModelResultData previousOptionData = null)
305  {
306  _optionPriceModelResult = new(optionPriceModelEvaluator, isThreadSafe: false);
307 
308  if (previousOptionData != null)
309  {
310  _tradeBar = previousOptionData._tradeBar;
311  _quoteBar = previousOptionData._quoteBar;
312  _openInterest = previousOptionData._openInterest;
313  _underlying = previousOptionData._underlying;
314  }
315  }
316 
317  public void Update(BaseData data)
318  {
319  switch (data)
320  {
321  case TradeBar tradeBar:
322  _tradeBar = tradeBar;
323  break;
324  case QuoteBar quoteBar:
325  _quoteBar = quoteBar;
326  break;
327  case OpenInterest openInterest:
328  _openInterest = openInterest;
329  break;
330  }
331  }
332 
333  public void SetUnderlying(BaseData data)
334  {
335  _underlying = data;
336  }
337  }
338 
339  /// <summary>
340  /// Handles option data for a contract from a <see cref="OptionUniverse"/> instance
341  /// </summary>
342  private class OptionUniverseData : IOptionData
343  {
344  private readonly OptionUniverse _contractData;
345 
346  public decimal LastPrice => _contractData.Close;
347 
348  // TODO: Null check required for FOPs: since OptionUniverse does not support FOPs,
349  // these instances will by "synthetic" and will not have underlying data.
350  // Can be removed after FOPs are supported by OptionUniverse
351  public decimal UnderlyingLastPrice => _contractData?.Underlying?.Price ?? decimal.Zero;
352 
353  public long Volume => (long)_contractData.Volume;
354 
355  public decimal BidPrice => _contractData.Close;
356 
357  public long BidSize => 0;
358 
359  public decimal AskPrice => _contractData.Close;
360 
361  public long AskSize => 0;
362 
363  public decimal OpenInterest => _contractData.OpenInterest;
364 
365  public decimal TheoreticalPrice => decimal.Zero;
366 
367  public decimal ImpliedVolatility => _contractData.ImpliedVolatility;
368 
369  public Greeks Greeks => _contractData.Greeks;
370 
371  public OptionUniverseData(OptionUniverse contractData)
372  {
373  _contractData = contractData;
374  }
375 
376  public void Update(BaseData data)
377  {
378  }
379 
380  public void SetUnderlying(BaseData data)
381  {
382  }
383  }
384 
385  #endregion
386  }
387 }