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