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