|
16 | 16 | using System; |
17 | 17 | using System.Linq; |
18 | 18 | using NUnit.Framework; |
| 19 | +using QuantConnect.Data; |
| 20 | +using System.Diagnostics; |
| 21 | +using QuantConnect.Logging; |
| 22 | +using Microsoft.CodeAnalysis; |
| 23 | +using System.Collections.Generic; |
19 | 24 |
|
20 | 25 | namespace QuantConnect.Lean.DataSource.ThetaData.Tests |
21 | 26 | { |
@@ -114,5 +119,109 @@ public void GetHistoryTickTradeValidateOnDistinctData(string ticker, Resolution |
114 | 119 |
|
115 | 120 | Assert.That(history.Count, Is.EqualTo(distinctHistory.Count)); |
116 | 121 | } |
| 122 | + |
| 123 | + [TestCase("SPY", SecurityType.Equity, Resolution.Hour, "1998/01/02", "2025/02/16", new[] { TickType.Quote, TickType.Trade })] |
| 124 | + [TestCase("SPY", SecurityType.Equity, Resolution.Daily, "1998/01/02", "2025/02/16", new[] { TickType.Quote, TickType.Trade })] |
| 125 | + [TestCase("SPY", SecurityType.Equity, Resolution.Minute, "2025/01/02", "2025/02/16", new[] { TickType.Quote, TickType.Trade })] |
| 126 | + [TestCase("AAPL", SecurityType.Equity, Resolution.Hour, "1998/01/02", "2025/02/16", new[] { TickType.Quote, TickType.Trade })] |
| 127 | + public void GetHistoryRequestWithLongRange(string ticker, SecurityType securityType, Resolution resolution, DateTime startDate, DateTime endDate, TickType[] tickTypes) |
| 128 | + { |
| 129 | + var symbol = TestHelpers.CreateSymbol(ticker, securityType); |
| 130 | + |
| 131 | + var historyRequests = new List<HistoryRequest>(); |
| 132 | + foreach (var tickType in tickTypes) |
| 133 | + { |
| 134 | + historyRequests.Add(TestHelpers.CreateHistoryRequest(symbol, resolution, tickType, startDate, endDate)); |
| 135 | + } |
| 136 | + |
| 137 | + foreach (var historyRequest in historyRequests) |
| 138 | + { |
| 139 | + var stopwatch = Stopwatch.StartNew(); |
| 140 | + var history = _thetaDataProvider.GetHistory(historyRequest).ToList(); |
| 141 | + stopwatch.Stop(); |
| 142 | + |
| 143 | + Assert.IsNotEmpty(history); |
| 144 | + |
| 145 | + var firstDate = history.First().Time; |
| 146 | + var lastDate = history.Last().Time; |
| 147 | + |
| 148 | + Log.Trace($"[{nameof(ThetaDataHistoryProviderTests)}] Execution completed in {stopwatch.Elapsed.TotalMinutes:F2} min | " + |
| 149 | + $"Symbol: {historyRequest.Symbol}, Resolution: {resolution}, TickType: {historyRequest.TickType}, Count: {history.Count}, " + |
| 150 | + $"First Date: {firstDate:yyyy-MM-dd HH:mm:ss}, Last Date: {lastDate:yyyy-MM-dd HH:mm:ss}"); |
| 151 | + |
| 152 | + // Ensure historical data is returned in chronological order |
| 153 | + for (var i = 1; i < history.Count; i++) |
| 154 | + { |
| 155 | + if (history[i].Time < history[i - 1].Time) |
| 156 | + Assert.Fail("Historical data is not in chronological order."); |
| 157 | + } |
| 158 | + } |
| 159 | + } |
| 160 | + |
| 161 | + [TestCase("AAPL", SecurityType.Equity, Resolution.Minute, "2025/02/19", "2025/02/20", new[] { TickType.Quote, TickType.Trade })] |
| 162 | + [TestCase("AAPL", SecurityType.Equity, Resolution.Minute, "2025/02/18", "2025/02/20", new[] { TickType.Quote, TickType.Trade })] |
| 163 | + [TestCase("AAPL", SecurityType.Equity, Resolution.Minute, "2025/02/15", "2025/02/20", new[] { TickType.Quote, TickType.Trade })] |
| 164 | + [TestCase("AAPL", SecurityType.Equity, Resolution.Minute, "2025/02/10", "2025/02/20", new[] { TickType.Quote, TickType.Trade })] |
| 165 | + [TestCase("AAPL", SecurityType.Equity, Resolution.Hour, "2025/02/19", "2025/02/20", new[] { TickType.Quote, TickType.Trade })] |
| 166 | + [TestCase("AAPL", SecurityType.Equity, Resolution.Hour, "2025/02/18", "2025/02/20", new[] { TickType.Quote, TickType.Trade })] |
| 167 | + [TestCase("AAPL", SecurityType.Equity, Resolution.Hour, "2025/02/10", "2025/02/20", new[] { TickType.Quote, TickType.Trade })] |
| 168 | + [TestCase("AAPL", SecurityType.Equity, Resolution.Hour, "2025/02/01", "2025/02/20", new[] { TickType.Quote, TickType.Trade })] |
| 169 | + [TestCase("AAPL", SecurityType.Equity, Resolution.Hour, "2025/01/01", "2025/02/20", new[] { TickType.Quote, TickType.Trade })] |
| 170 | + [TestCase("AAPL", SecurityType.Equity, Resolution.Daily, "2025/02/19", "2025/02/20", new[] { TickType.Quote, TickType.Trade })] |
| 171 | + [TestCase("AAPL", SecurityType.Equity, Resolution.Daily, "2025/02/18", "2025/02/20", new[] { TickType.Quote, TickType.Trade })] |
| 172 | + [TestCase("AAPL", SecurityType.Equity, Resolution.Daily, "2025/02/15", "2025/02/20", new[] { TickType.Quote, TickType.Trade })] |
| 173 | + [TestCase("AAPL", SecurityType.Equity, Resolution.Daily, "2025/02/10", "2025/02/20", new[] { TickType.Quote, TickType.Trade })] |
| 174 | + [TestCase("AAPL", SecurityType.Equity, Resolution.Daily, "2025/01/01", "2025/02/20", new[] { TickType.Quote, TickType.Trade })] |
| 175 | + public void GetHistoryRequestWithCalculateAmountReturnsData(string ticker, SecurityType securityType, Resolution resolution, DateTime startDate, DateTime endDate, TickType[] tickTypes) |
| 176 | + { |
| 177 | + var symbol = TestHelpers.CreateSymbol(ticker, securityType); |
| 178 | + |
| 179 | + var historyRequests = new List<HistoryRequest>(); |
| 180 | + foreach (var tickType in tickTypes) |
| 181 | + { |
| 182 | + historyRequests.Add(TestHelpers.CreateHistoryRequest(symbol, resolution, tickType, startDate, endDate, includeExtendedMarketHours: false)); |
| 183 | + } |
| 184 | + |
| 185 | + foreach (var historyRequest in historyRequests) |
| 186 | + { |
| 187 | + var history = _thetaDataProvider.GetHistory(historyRequest).ToList(); |
| 188 | + //Log.Trace(string.Join("\n", history.Select(x => new { Time = x.Time, EndTime = x.EndTime, Data = x }))); |
| 189 | + |
| 190 | + int expectedAmount = CalculateExpectedHistoryAmount(historyRequest); |
| 191 | + |
| 192 | + Assert.AreEqual(expectedAmount, history.Count, "History data count does not match expected amount."); |
| 193 | + } |
| 194 | + } |
| 195 | + |
| 196 | + private int CalculateExpectedHistoryAmount(HistoryRequest request) |
| 197 | + { |
| 198 | + var endTime = request.EndTimeUtc.ConvertFromUtc(request.DataTimeZone); |
| 199 | + var currentDate = request.StartTimeUtc.ConvertFromUtc(request.DataTimeZone); |
| 200 | + int totalDataPoints = 0; |
| 201 | + |
| 202 | + while (currentDate < endTime) |
| 203 | + { |
| 204 | + if (request.ExchangeHours.IsDateOpen(currentDate, request.IncludeExtendedMarketHours)) |
| 205 | + { |
| 206 | + int dataPointsPerDay = GetDataPointsPerDay(request.Resolution); |
| 207 | + totalDataPoints += dataPointsPerDay; |
| 208 | + } |
| 209 | + |
| 210 | + currentDate = currentDate.AddDays(1); |
| 211 | + } |
| 212 | + |
| 213 | + return totalDataPoints; |
| 214 | + } |
| 215 | + |
| 216 | + private int GetDataPointsPerDay(Resolution resolution) |
| 217 | + { |
| 218 | + return resolution switch |
| 219 | + { |
| 220 | + Resolution.Minute => 390, // 720 minutes from 9:30 AM to 4:00 PM (Trading Hours) |
| 221 | + Resolution.Hour => 7, |
| 222 | + Resolution.Daily => 1, // 1 bar per day |
| 223 | + _ => throw new ArgumentOutOfRangeException(nameof(resolution), "Unsupported resolution") |
| 224 | + }; |
| 225 | + } |
117 | 226 | } |
118 | 227 | } |
0 commit comments