Lean  $LEAN_TAG$
InterestRateProvider.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.Logging;
19 using QuantConnect.Util;
20 using System;
21 using System.Collections.Generic;
22 using System.Globalization;
23 using System.IO;
24 using System.Linq;
25 
26 namespace QuantConnect.Data
27 {
28  /// <summary>
29  /// Fed US Primary Credit Rate at given date
30  /// </summary>
32  {
33  private static readonly DateTime _firstInterestRateDate = new DateTime(1998, 1, 1);
34  private static DateTime _lastInterestRateDate;
35  private static Dictionary<DateTime, decimal> _riskFreeRateProvider;
36  private static readonly object _lock = new();
37 
38  /// <summary>
39  /// Default Risk Free Rate of 1%
40  /// </summary>
41  public static readonly decimal DefaultRiskFreeRate = 0.01m;
42 
43  /// <summary>
44  /// Lazily loads the interest rate provider from disk and returns it
45  /// </summary>
46  private IReadOnlyDictionary<DateTime, decimal> RiskFreeRateProvider
47  {
48  get
49  {
50  // let's not lock if the provider is already loaded
51  if (_riskFreeRateProvider != null)
52  {
53  return _riskFreeRateProvider;
54  }
55 
56  lock (_lock)
57  {
58  if (_riskFreeRateProvider == null)
59  {
61  }
62  return _riskFreeRateProvider;
63  }
64  }
65  }
66 
67  /// <summary>
68  /// Get interest rate by a given date
69  /// </summary>
70  /// <param name="date">The date</param>
71  /// <returns>Interest rate on the given date</returns>
72  public decimal GetInterestRate(DateTime date)
73  {
74  if (!RiskFreeRateProvider.TryGetValue(date.Date, out var interestRate))
75  {
76  return date < _firstInterestRateDate
77  ? RiskFreeRateProvider[_firstInterestRateDate]
78  : RiskFreeRateProvider[_lastInterestRateDate];
79  }
80 
81  return interestRate;
82  }
83 
84  /// <summary>
85  /// Generate the daily historical US primary credit rate
86  /// </summary>
87  protected void LoadInterestRateProvider()
88  {
89  var directory = Path.Combine(Globals.DataFolder, "alternative", "interest-rate", "usa",
90  "interest-rate.csv");
91  _riskFreeRateProvider = FromCsvFile(directory, out var previousInterestRate);
92 
93  _lastInterestRateDate = DateTime.UtcNow.Date;
94 
95  // Sparse the discrete data points into continuous credit rate data for every day
96  for (var date = _firstInterestRateDate; date <= _lastInterestRateDate; date = date.AddDays(1))
97  {
98  if (!_riskFreeRateProvider.TryGetValue(date, out var currentRate))
99  {
100  _riskFreeRateProvider[date] = previousInterestRate;
101  continue;
102  }
103 
104  previousInterestRate = currentRate;
105  }
106  }
107 
108  /// <summary>
109  /// Reads Fed primary credit rate file and returns a dictionary of historical rate changes
110  /// </summary>
111  /// <param name="file">The csv file to be read</param>
112  /// <param name="firstInterestRate">The first interest rate on file</param>
113  /// <returns>Dictionary of historical credit rate change events</returns>
114  public static Dictionary<DateTime, decimal> FromCsvFile(string file, out decimal firstInterestRate)
115  {
116  var dataProvider = Composer.Instance.GetPart<IDataProvider>();
117 
118  var firstInterestRateSet = false;
119  firstInterestRate = DefaultRiskFreeRate;
120 
121  // skip the first header line, also skip #'s as these are comment lines
122  var interestRateProvider = new Dictionary<DateTime, decimal>();
123  foreach (var line in dataProvider.ReadLines(file).Skip(1)
124  .Where(x => !string.IsNullOrWhiteSpace(x)))
125  {
126  if (TryParse(line, out var date, out var interestRate))
127  {
128  if (!firstInterestRateSet)
129  {
130  firstInterestRate = interestRate;
131  firstInterestRateSet = true;
132  }
133 
134  interestRateProvider[date] = interestRate;
135  }
136  }
137 
138  if (interestRateProvider.Count == 0)
139  {
140  Log.Error($"InterestRateProvider.FromCsvFile(): no interest rates were loaded, please make sure the file is present '{file}'");
141  }
142 
143  return interestRateProvider;
144  }
145 
146  /// <summary>
147  /// Parse the string into the interest rate date and value
148  /// </summary>
149  /// <param name="csvLine">The csv line to be parsed</param>
150  /// <param name="date">Parsed interest rate date</param>
151  /// <param name="interestRate">Parsed interest rate value</param>
152  public static bool TryParse(string csvLine, out DateTime date, out decimal interestRate)
153  {
154  var line = csvLine.Split(',');
155 
156  if (!DateTime.TryParseExact(line[0], "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out date))
157  {
158  Log.Error($"Couldn't parse date/time while reading FED primary credit rate file. Line: {csvLine}");
159  interestRate = DefaultRiskFreeRate;
160  return false;
161  }
162 
163  if (!decimal.TryParse(line[1], NumberStyles.Any, CultureInfo.InvariantCulture, out interestRate))
164  {
165  Log.Error($"Couldn't parse primary credit rate while reading FED primary credit rate file. Line: {csvLine}");
166  return false;
167  }
168 
169  // Unit conversion from %
170  interestRate /= 100;
171  return true;
172  }
173  }
174 }