Lean  $LEAN_TAG$
TiingoPrice.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 
16 using System;
17 using System.Collections.Concurrent;
18 using System.Collections.Generic;
19 using Newtonsoft.Json;
20 using NodaTime;
23 using static QuantConnect.StringExtensions;
24 
26 {
27  /// <summary>
28  /// Tiingo daily price data
29  /// https://api.tiingo.com/docs/tiingo/daily
30  /// </summary>
31  /// <remarks>Requires setting <see cref="Tiingo.AuthCode"/></remarks>
32  public class TiingoPrice : TradeBar
33  {
34  private readonly ConcurrentDictionary<string, DateTime> _startDates = new ConcurrentDictionary<string, DateTime>();
35 
36  /// <summary>
37  /// The end time of this data. Some data covers spans (trade bars) and as such we want
38  /// to know the entire time span covered
39  /// </summary>
40  public override DateTime EndTime
41  {
42  get { return Time + Period; }
43  set { Time = value - Period; }
44  }
45 
46  /// <summary>
47  /// The period of this trade bar, (second, minute, daily, ect...)
48  /// </summary>
49  public override TimeSpan Period => QuantConnect.Time.OneDay;
50 
51  /// <summary>
52  /// The date this data pertains to
53  /// </summary>
54  [JsonProperty("date")]
55  public DateTime Date { get; set; }
56 
57  /// <summary>
58  /// The actual (not adjusted) open price of the asset on the specific date
59  /// </summary>
60  [JsonProperty("open")]
61  public override decimal Open { get; set; }
62 
63  /// <summary>
64  /// The actual (not adjusted) high price of the asset on the specific date
65  /// </summary>
66  [JsonProperty("high")]
67  public override decimal High { get; set; }
68 
69  /// <summary>
70  /// The actual (not adjusted) low price of the asset on the specific date
71  /// </summary>
72  [JsonProperty("low")]
73  public override decimal Low { get; set; }
74 
75  /// <summary>
76  /// The actual (not adjusted) closing price of the asset on the specific date
77  /// </summary>
78  [JsonProperty("close")]
79  public override decimal Close { get; set; }
80 
81  /// <summary>
82  /// The actual (not adjusted) number of shares traded during the day
83  /// </summary>
84  [JsonProperty("volume")]
85  public override decimal Volume { get; set; }
86 
87  /// <summary>
88  /// The adjusted opening price of the asset on the specific date. Returns null if not available.
89  /// </summary>
90  [JsonProperty("adjOpen")]
91  public decimal AdjustedOpen { get; set; }
92 
93  /// <summary>
94  /// The adjusted high price of the asset on the specific date. Returns null if not available.
95  /// </summary>
96  [JsonProperty("adjHigh")]
97  public decimal AdjustedHigh { get; set; }
98 
99  /// <summary>
100  /// The adjusted low price of the asset on the specific date. Returns null if not available.
101  /// </summary>
102  [JsonProperty("adjLow")]
103  public decimal AdjustedLow { get; set; }
104 
105  /// <summary>
106  /// The adjusted close price of the asset on the specific date. Returns null if not available.
107  /// </summary>
108  [JsonProperty("adjClose")]
109  public decimal AdjustedClose { get; set; }
110 
111  /// <summary>
112  /// The adjusted number of shares traded during the day - adjusted for splits. Returns null if not available
113  /// </summary>
114  [JsonProperty("adjVolume")]
115  public long AdjustedVolume { get; set; }
116 
117  /// <summary>
118  /// The dividend paid out on "date" (note that "date" will be the "exDate" for the dividend)
119  /// </summary>
120  [JsonProperty("divCash")]
121  public decimal Dividend { get; set; }
122 
123  /// <summary>
124  /// A factor used when a company splits or reverse splits. On days where there is ONLY a split (no dividend payment),
125  /// you can calculate the adjusted close as follows: adjClose = "Previous Close"/splitFactor
126  /// </summary>
127  [JsonProperty("splitFactor")]
128  public decimal SplitFactor { get; set; }
129 
130  /// <summary>
131  /// Initializes an instance of the <see cref="TiingoPrice"/> class.
132  /// </summary>
133  public TiingoPrice()
134  {
135  Symbol = Symbol.Empty;
136  DataType = MarketDataType.Base;
137  }
138 
139  /// <summary>
140  /// Return the URL string source of the file. This will be converted to a stream
141  /// </summary>
142  /// <param name="config">Configuration object</param>
143  /// <param name="date">Date of this source file</param>
144  /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
145  /// <returns>String URL of source file.</returns>
146  public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
147  {
148  DateTime startDate;
149  if (!_startDates.TryGetValue(config.Symbol.Value, out startDate))
150  {
151  startDate = date;
152  _startDates.TryAdd(config.Symbol.Value, startDate);
153  }
154 
155  var tiingoTicker = TiingoSymbolMapper.GetTiingoTicker(config.Symbol);
156  var source = Invariant($"https://api.tiingo.com/tiingo/daily/{tiingoTicker}/prices?startDate={startDate:yyyy-MM-dd}&token={Tiingo.AuthCode}");
157  return new SubscriptionDataSource(source, SubscriptionTransportMedium.RemoteFile, FileFormat.UnfoldingCollection);
158  }
159 
160  /// <summary>
161  /// Reader converts each line of the data source into BaseData objects. Each data type creates its own factory method,
162  /// and returns a new instance of the object
163  /// each time it is called. The returned object is assumed to be time stamped in the config.ExchangeTimeZone.
164  /// </summary>
165  /// <param name="config">Subscription data config setup object</param>
166  /// <param name="content">Content of the source document</param>
167  /// <param name="date">Date of the requested data</param>
168  /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
169  /// <returns>
170  /// Instance of the T:BaseData object generated by this line of the CSV
171  /// </returns>
172  public override BaseData Reader(SubscriptionDataConfig config, string content, DateTime date, bool isLiveMode)
173  {
174  var list = JsonConvert.DeserializeObject<List<TiingoPrice>>(content);
175 
176  foreach (var item in list)
177  {
178  item.Symbol = config.Symbol;
179  item.Time = item.Date;
180  item.Value = item.Close;
181  }
182 
183  return new BaseDataCollection(date, config.Symbol, list);
184  }
185 
186  /// <summary>
187  /// Indicates if there is support for mapping
188  /// </summary>
189  /// <returns>True indicates mapping should be used</returns>
190  public override bool RequiresMapping()
191  {
192  return true;
193  }
194 
195  /// <summary>
196  /// Specifies the data time zone for this data type. This is useful for custom data types
197  /// </summary>
198  /// <returns>The <see cref="DateTimeZone"/> of this data type</returns>
199  public override DateTimeZone DataTimeZone()
200  {
201  return TimeZones.Utc;
202  }
203 
204  /// <summary>
205  /// Gets the default resolution for this data and security type
206  /// </summary>
207  public override Resolution DefaultResolution()
208  {
209  return Resolution.Daily;
210  }
211 
212  /// <summary>
213  /// Gets the supported resolution for this data and security type
214  /// </summary>
215  public override List<Resolution> SupportedResolutions()
216  {
217  return DailyResolution;
218  }
219  }
220 }