Lean  $LEAN_TAG$
FundamentalInstanceProvider.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2023 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 System.Runtime.CompilerServices;
20 
22 {
23  /// <summary>
24  /// Per symbol we will have a fundamental class provider so the instances can be reused
25  /// </summary>
27  {
28  private static readonly Dictionary<SecurityIdentifier, FundamentalInstanceProvider> _cache = new();
29 
30  private readonly FundamentalTimeProvider _timeProvider;
31  private readonly FinancialStatements _financialStatements;
32  private readonly OperationRatios _operationRatios;
33  private readonly SecurityReference _securityReference;
34  private readonly CompanyReference _companyReference;
35  private readonly CompanyProfile _companyProfile;
36  private readonly AssetClassification _assetClassification;
37  private readonly ValuationRatios _valuationRatios;
38  private readonly EarningRatios _earningRatios;
39  private readonly EarningReports _earningReports;
40 
41  /// <summary>
42  /// Get's the fundamental instance provider for the requested symbol
43  /// </summary>
44  /// <param name="symbol">The requested symbol</param>
45  /// <returns>The unique instance provider</returns>
46  public static FundamentalInstanceProvider Get(Symbol symbol)
47  {
48  FundamentalInstanceProvider result = null;
49  lock (_cache)
50  {
51  _cache.TryGetValue(symbol.ID, out result);
52  }
53 
54  if (result == null)
55  {
56  // we create the fundamental instance provider without holding the cache lock, this is because it uses the pygil
57  // Deadlock case: if the main thread has PyGil and wants to take lock on cache (security.Fundamentals use case) and the data
58  // stack thread takes the lock on the cache (creating new fundamentals) and next wants the pygil deadlock!
59  result = new FundamentalInstanceProvider(symbol);
60  lock (_cache)
61  {
62  _cache[symbol.ID] = result;
63  }
64  }
65  return result;
66  }
67 
68  /// <summary>
69  /// Creates a new fundamental instance provider
70  /// </summary>
71  /// <param name="symbol">The target symbol</param>
72  private FundamentalInstanceProvider(Symbol symbol)
73  {
74  _timeProvider = new();
75  _financialStatements = new(_timeProvider, symbol.ID);
76  _operationRatios = new(_timeProvider, symbol.ID);
77  _securityReference = new(_timeProvider, symbol.ID);
78  _companyReference = new(_timeProvider, symbol.ID);
79  _companyProfile = new(_timeProvider, symbol.ID);
80  _assetClassification = new(_timeProvider, symbol.ID);
81  _valuationRatios = new(_timeProvider, symbol.ID);
82  _earningRatios = new(_timeProvider, symbol.ID);
83  _earningReports = new(_timeProvider, symbol.ID);
84  }
85 
86  /// <summary>
87  /// Returns the ValuationRatios instance
88  /// </summary>
89  [MethodImpl(MethodImplOptions.AggressiveInlining)]
90  public ValuationRatios GetValuationRatios(DateTime time)
91  {
92  _timeProvider.Time = time;
93  return _valuationRatios;
94  }
95 
96  /// <summary>
97  /// Returns the EarningRatios instance
98  /// </summary>
99  [MethodImpl(MethodImplOptions.AggressiveInlining)]
100  public EarningRatios GetEarningRatios(DateTime time)
101  {
102  _timeProvider.Time = time;
103  return _earningRatios;
104  }
105 
106  /// <summary>
107  /// Returns the EarningReports instance
108  /// </summary>
109  [MethodImpl(MethodImplOptions.AggressiveInlining)]
110  public EarningReports GetEarningReports(DateTime time)
111  {
112  _timeProvider.Time = time;
113  return _earningReports;
114  }
115 
116  /// <summary>
117  /// Returns the OperationRatios instance
118  /// </summary>
119  [MethodImpl(MethodImplOptions.AggressiveInlining)]
120  public OperationRatios GetOperationRatios(DateTime time)
121  {
122  _timeProvider.Time = time;
123  return _operationRatios;
124  }
125 
126  /// <summary>
127  /// Returns the FinancialStatements instance
128  /// </summary>
129  [MethodImpl(MethodImplOptions.AggressiveInlining)]
131  {
132  _timeProvider.Time = time;
133  return _financialStatements;
134  }
135 
136  /// <summary>
137  /// Returns the SecurityReference instance
138  /// </summary>
139  [MethodImpl(MethodImplOptions.AggressiveInlining)]
141  {
142  _timeProvider.Time = time;
143  return _securityReference;
144  }
145 
146  /// <summary>
147  /// Returns the CompanyReference instance
148  /// </summary>
149  [MethodImpl(MethodImplOptions.AggressiveInlining)]
150  public CompanyReference GetCompanyReference(DateTime time)
151  {
152  _timeProvider.Time = time;
153  return _companyReference;
154  }
155 
156  /// <summary>
157  /// Returns the CompanyProfile instance
158  /// </summary>
159  [MethodImpl(MethodImplOptions.AggressiveInlining)]
160  public CompanyProfile GetCompanyProfile(DateTime time)
161  {
162  _timeProvider.Time = time;
163  return _companyProfile;
164  }
165 
166  /// <summary>
167  /// Returns the AssetClassification instance
168  /// </summary>
169  [MethodImpl(MethodImplOptions.AggressiveInlining)]
171  {
172  _timeProvider.Time = time;
173  return _assetClassification;
174  }
175 
176  private class FundamentalTimeProvider : ITimeProvider
177  {
178  public DateTime Time;
179 
180  [MethodImpl(MethodImplOptions.AggressiveInlining)]
181  public DateTime GetUtcNow() => Time;
182  }
183  }
184 }