From 72f9174ae90e0237a86eb174c587a2b26c69eb2f Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Fri, 23 Feb 2024 12:47:30 -0400 Subject: [PATCH 1/3] Return null on unsupported history and data download requests --- .../OandaBrokerageHistoryProviderTests.cs | 123 ++++++++---------- .../OandaBrokerageTests.cs | 17 ++- .../OandaDataDownloader.cs | 33 +++-- .../OandaDownloaderProgram.cs | 4 + QuantConnect.OandaBrokerage/OandaBrokerage.cs | 39 +++++- .../OandaRestApiBase.cs | 14 +- 6 files changed, 137 insertions(+), 93 deletions(-) diff --git a/QuantConnect.OandaBrokerage.Tests/OandaBrokerageHistoryProviderTests.cs b/QuantConnect.OandaBrokerage.Tests/OandaBrokerageHistoryProviderTests.cs index 291f2c6..771e53b 100644 --- a/QuantConnect.OandaBrokerage.Tests/OandaBrokerageHistoryProviderTests.cs +++ b/QuantConnect.OandaBrokerage.Tests/OandaBrokerageHistoryProviderTests.cs @@ -14,14 +14,13 @@ */ using System; +using System.Linq; using NodaTime; using NUnit.Framework; using QuantConnect.Brokerages.Oanda; using QuantConnect.Configuration; using QuantConnect.Data; using QuantConnect.Data.Market; -using QuantConnect.Lean.Engine.DataFeeds; -using QuantConnect.Lean.Engine.HistoricalData; using QuantConnect.Logging; using QuantConnect.Securities; using Environment = QuantConnect.Brokerages.Oanda.Environment; @@ -35,94 +34,82 @@ private static TestCaseData[] TestParameters { get { + TestGlobals.Initialize(); var eurusd = Symbol.Create("EURUSD", SecurityType.Forex, Market.Oanda); return new[] { // valid parameters - new TestCaseData(eurusd, Resolution.Second, Time.OneMinute, false), - new TestCaseData(eurusd, Resolution.Minute, Time.OneHour, false), - new TestCaseData(eurusd, Resolution.Hour, Time.OneDay, false), - new TestCaseData(eurusd, Resolution.Daily, TimeSpan.FromDays(15), false), + new TestCaseData(eurusd, Resolution.Second, Time.OneMinute, TickType.Quote, false, false), + new TestCaseData(eurusd, Resolution.Minute, Time.OneHour, TickType.Quote, false, false), + new TestCaseData(eurusd, Resolution.Hour, Time.OneDay, TickType.Quote, false, false), + new TestCaseData(eurusd, Resolution.Daily, TimeSpan.FromDays(15), TickType.Quote, false, false), - // invalid resolution, throws "System.ArgumentException : Unsupported resolution: Tick" - new TestCaseData(eurusd, Resolution.Tick, TimeSpan.FromSeconds(15), true), + // invalid resolution, null result + new TestCaseData(eurusd, Resolution.Tick, TimeSpan.FromSeconds(15), TickType.Quote, false, true), // invalid period, no error, empty result - new TestCaseData(eurusd, Resolution.Daily, TimeSpan.FromDays(-15), false), + new TestCaseData(eurusd, Resolution.Daily, TimeSpan.FromDays(-15), TickType.Quote, true, false), - // invalid symbol, no error, empty result - new TestCaseData(Symbol.Create("XYZ", SecurityType.Forex, Market.FXCM), Resolution.Daily, TimeSpan.FromDays(15), false), + // invalid symbol, null result + new TestCaseData(Symbol.Create("XYZ", SecurityType.Forex, Market.FXCM), Resolution.Daily, TimeSpan.FromDays(15), TickType.Quote, false, true), - // invalid security type, no error, empty result - new TestCaseData(Symbols.AAPL, Resolution.Daily, TimeSpan.FromDays(15), false), + // invalid security type, null result + new TestCaseData(Symbols.AAPL, Resolution.Daily, TimeSpan.FromDays(15), TickType.Quote, false, true), + + // invalid market, null result + new TestCaseData(Symbol.Create("EURUSD", SecurityType.Forex, Market.USA), Resolution.Daily, TimeSpan.FromDays(15), TickType.Quote, false, true), + + // invalid tick type, null result + new TestCaseData(eurusd, Resolution.Daily, TimeSpan.FromDays(15), TickType.Trade, false, true), + new TestCaseData(eurusd, Resolution.Daily, TimeSpan.FromDays(15), TickType.OpenInterest, false, true), }; } } [Test, TestCaseSource(nameof(TestParameters))] - public void GetsHistory(Symbol symbol, Resolution resolution, TimeSpan period, bool throwsException) + public void GetsHistory(Symbol symbol, Resolution resolution, TimeSpan period, TickType tickType, bool shouldBeEmpty, bool unsupported) { - TestDelegate test = () => + var environment = Config.Get("oanda-environment").ConvertTo(); + var accessToken = Config.Get("oanda-access-token"); + var accountId = Config.Get("oanda-account-id"); + + var brokerage = new OandaBrokerage(null, null, null, environment, accessToken, accountId); + + var now = DateTime.UtcNow; + var request = new HistoryRequest(now.Add(-period), + now, + tickType == TickType.Quote ? typeof(QuoteBar) : typeof(TradeBar), + symbol, + resolution, + SecurityExchangeHours.AlwaysOpen(TimeZones.EasternStandard), + DateTimeZone.Utc, + Resolution.Minute, + false, + false, + DataNormalizationMode.Adjusted, + tickType); + + var history = brokerage.GetHistory(request)?.ToList(); + + if (unsupported) { - var environment = Config.Get("oanda-environment").ConvertTo(); - var accessToken = Config.Get("oanda-access-token"); - var accountId = Config.Get("oanda-account-id"); - - var brokerage = new OandaBrokerage(null, null, null, environment, accessToken, accountId); - - var historyProvider = new BrokerageHistoryProvider(); - historyProvider.SetBrokerage(brokerage); - historyProvider.Initialize(new HistoryProviderInitializeParameters(null, null, null, null, null, null, null, false, new DataPermissionManager(), null)); - - var now = DateTime.UtcNow; - - var requests = new[] - { - new HistoryRequest(now.Add(-period), - now, - typeof(QuoteBar), - symbol, - resolution, - SecurityExchangeHours.AlwaysOpen(TimeZones.EasternStandard), - DateTimeZone.Utc, - Resolution.Minute, - false, - false, - DataNormalizationMode.Adjusted, - TickType.Quote) - }; - - var history = historyProvider.GetHistory(requests, TimeZones.Utc); + Assert.IsNull(history); + return; + } - foreach (var slice in history) - { - if (resolution == Resolution.Tick) - { - foreach (var tick in slice.Ticks[symbol]) - { - Log.Trace("{0}: {1} - {2} / {3}", tick.Time, tick.Symbol, tick.BidPrice, tick.AskPrice); - } - } - else - { - var bar = slice.QuoteBars[symbol]; - - Log.Trace("{0}: {1} - O={2}, H={3}, L={4}, C={5}", bar.Time, bar.Symbol, bar.Open, bar.High, bar.Low, bar.Close); - } - } - - Log.Trace("Data points retrieved: " + historyProvider.DataPointCount); - }; - - if (throwsException) + if (shouldBeEmpty) { - Assert.Throws(test); + Assert.IsEmpty(history); + return; } - else + + foreach (var bar in history.Cast()) { - Assert.DoesNotThrow(test); + Log.Trace("{0}: {1} - O={2}, H={3}, L={4}, C={5}", bar.Time, bar.Symbol, bar.Open, bar.High, bar.Low, bar.Close); } + + Log.Trace("Data points retrieved: " + history.Count); } } } \ No newline at end of file diff --git a/QuantConnect.OandaBrokerage.Tests/OandaBrokerageTests.cs b/QuantConnect.OandaBrokerage.Tests/OandaBrokerageTests.cs index e85a1bd..5cc6a2d 100644 --- a/QuantConnect.OandaBrokerage.Tests/OandaBrokerageTests.cs +++ b/QuantConnect.OandaBrokerage.Tests/OandaBrokerageTests.cs @@ -55,12 +55,17 @@ protected override IBrokerage CreateBrokerage(IOrderProvider orderProvider, ISec /// /// Provides the data required to test each order type in various cases /// - public static TestCaseData[] OrderParameters => new[] + public static IEnumerable OrderParameters { - new TestCaseData(new MarketOrderTestParameters(Symbol.Create("EURUSD", SecurityType.Forex, Market.Oanda))).SetName("MarketOrder"), - new TestCaseData(new LimitOrderTestParameters(Symbol.Create("EURUSD", SecurityType.Forex, Market.Oanda), 5m, 0.32m)).SetName("LimitOrder"), - new TestCaseData(new StopMarketOrderTestParameters(Symbol.Create("EURUSD", SecurityType.Forex, Market.Oanda), 5m, 0.32m)).SetName("StopMarketOrder") - }; + get + { + TestGlobals.Initialize(); + + yield return new TestCaseData(new MarketOrderTestParameters(Symbol.Create("EURUSD", SecurityType.Forex, Market.Oanda))).SetName("MarketOrder"); + yield return new TestCaseData(new LimitOrderTestParameters(Symbol.Create("EURUSD", SecurityType.Forex, Market.Oanda), 5m, 0.32m)).SetName("LimitOrder"); + yield return new TestCaseData(new StopMarketOrderTestParameters(Symbol.Create("EURUSD", SecurityType.Forex, Market.Oanda), 5m, 0.32m)).SetName("StopMarketOrder"); + } + } /// /// Gets the symbol to be traded, must be shortable @@ -107,7 +112,7 @@ public void ValidateMarketOrders() { order.Status = orderEvent.Status; orders[order.Id] = order; - } + } } }; oanda.OrdersStatusChanged += orderStatusChangedCallback; diff --git a/QuantConnect.OandaBrokerage.ToolBox/OandaDataDownloader.cs b/QuantConnect.OandaBrokerage.ToolBox/OandaDataDownloader.cs index bfc4da1..425c5e9 100644 --- a/QuantConnect.OandaBrokerage.ToolBox/OandaDataDownloader.cs +++ b/QuantConnect.OandaBrokerage.ToolBox/OandaDataDownloader.cs @@ -76,26 +76,41 @@ public IEnumerable Get(DataDownloaderGetParameters dataDownloaderGetPa { var symbol = dataDownloaderGetParameters.Symbol; var resolution = dataDownloaderGetParameters.Resolution; - var startUtc = dataDownloaderGetParameters.StartUtc; - var endUtc = dataDownloaderGetParameters.EndUtc; var tickType = dataDownloaderGetParameters.TickType; if (tickType != TickType.Quote) { - yield break; + Logging.Log.Trace("OandaDataDownloader.Get(): Unsupported tick type: " + tickType); + return null; } if (!_symbolMapper.IsKnownLeanSymbol(symbol)) - throw new ArgumentException("Invalid symbol requested: " + symbol.Value); + { + Logging.Log.Trace("OandaDataDownloader.Get(): Unsupported symbol: " + symbol); + return null; + } if (resolution == Resolution.Tick) - throw new NotSupportedException("Resolution not available: " + resolution); + { + Logging.Log.Trace("OandaDataDownloader.Get(): Unsupported resolution: " + resolution); + return null; + } if (symbol.ID.SecurityType != SecurityType.Forex && symbol.ID.SecurityType != SecurityType.Cfd) - throw new NotSupportedException("SecurityType not available: " + symbol.ID.SecurityType); + { + Logging.Log.Trace("OandaDataDownloader.Get(): Unsupported security type: " + symbol.ID.SecurityType); + return null; + } - if (endUtc < startUtc) - throw new ArgumentException("The end date must be greater or equal than the start date."); + return GetData(dataDownloaderGetParameters); + } + + private IEnumerable GetData(DataDownloaderGetParameters parameters) + { + var symbol = parameters.Symbol; + var resolution = parameters.Resolution; + var startUtc = parameters.StartUtc; + var endUtc = parameters.EndUtc; var barsTotalInPeriod = new List(); var barsToSave = new List(); @@ -175,7 +190,7 @@ public IEnumerable Get(DataDownloaderGetParameters dataDownloaderGetPa break; } } - + /// /// Groups a list of bars into a dictionary keyed by date diff --git a/QuantConnect.OandaBrokerage.ToolBox/OandaDownloaderProgram.cs b/QuantConnect.OandaBrokerage.ToolBox/OandaDownloaderProgram.cs index 38a71b6..df526b7 100644 --- a/QuantConnect.OandaBrokerage.ToolBox/OandaDownloaderProgram.cs +++ b/QuantConnect.OandaBrokerage.ToolBox/OandaDownloaderProgram.cs @@ -67,6 +67,10 @@ public static void OandaDownloader(IList tickers, string resolution, Dat var symbol = Symbol.Create(ticker, securityType, market); var data = downloader.Get(new DataDownloaderGetParameters(symbol, castResolution, startDate, endDate, TickType.Quote)); + if (data == null) + { + continue; + } if (allResolutions) { diff --git a/QuantConnect.OandaBrokerage/OandaBrokerage.cs b/QuantConnect.OandaBrokerage/OandaBrokerage.cs index 33e719b..2666b0c 100644 --- a/QuantConnect.OandaBrokerage/OandaBrokerage.cs +++ b/QuantConnect.OandaBrokerage/OandaBrokerage.cs @@ -51,6 +51,10 @@ public class OandaBrokerage : Brokerage, IDataQueueHandler private OandaRestApiBase _api; private bool _isInitialized; + private bool _unsupportedAssetForHistoryLogged; + private bool _unsupportedResolutionForHistoryLogged; + private bool _unsupportedTickTypeForHistoryLogged; + /// /// The maximum number of bars per historical data request /// @@ -220,12 +224,41 @@ public override bool CancelOrder(Order order) /// An enumerable of bars covering the span specified in the request public override IEnumerable GetHistory(HistoryRequest request) { - if (!_symbolMapper.IsKnownLeanSymbol(request.Symbol)) + if (!_api.CanSubscribe(request.Symbol) || !_symbolMapper.IsKnownLeanSymbol(request.Symbol)) + { + if (!_unsupportedAssetForHistoryLogged) + { + Log.Trace($"OandaBrokerage.GetHistory(): Unsupported asset: {request.Symbol}, no history returned"); + _unsupportedAssetForHistoryLogged = true; + } + return null; + } + + if (request.Resolution == Resolution.Tick) + { + if (!_unsupportedResolutionForHistoryLogged) + { + Log.Trace($"OandaBrokerage.GetHistory(): Unsupported resolution: {request.Resolution}, no history returned"); + _unsupportedResolutionForHistoryLogged = true; + } + return null; + } + + if (request.TickType != TickType.Quote) { - Log.Trace("OandaBrokerage.GetHistory(): Invalid symbol: {0}, no history returned", request.Symbol.Value); - yield break; + if (!_unsupportedTickTypeForHistoryLogged) + { + Log.Trace($"OandaBrokerage.GetHistory(): Unsupported tick type: {request.TickType}, no history returned"); + _unsupportedTickTypeForHistoryLogged = true; + } + return null; } + return GetHistoryImpl(request); + } + + private IEnumerable GetHistoryImpl(HistoryRequest request) + { var exchangeTimeZone = MarketHoursDatabase.FromDataFolder().GetExchangeHours(Market.Oanda, request.Symbol, request.Symbol.SecurityType).TimeZone; // Oanda only has 5-second bars, we return these for Resolution.Second diff --git a/QuantConnect.OandaBrokerage/OandaRestApiBase.cs b/QuantConnect.OandaBrokerage/OandaRestApiBase.cs index b898e4a..dc88dfe 100644 --- a/QuantConnect.OandaBrokerage/OandaRestApiBase.cs +++ b/QuantConnect.OandaBrokerage/OandaRestApiBase.cs @@ -397,14 +397,14 @@ public void Unsubscribe(SubscriptionDataConfig dataConfig) /// /// Returns true if this brokerage supports the specified symbol /// - private bool CanSubscribe(Symbol symbol) + public bool CanSubscribe(Symbol symbol) { - // ignore unsupported security types - if (symbol.ID.SecurityType != SecurityType.Forex && symbol.ID.SecurityType != SecurityType.Cfd) - return false; - - // ignore universe symbols - return !symbol.Value.Contains("-UNIVERSE-") && symbol.ID.Market == Market.Oanda; + return + // ignore unsupported security types + (symbol.ID.SecurityType == SecurityType.Forex || symbol.ID.SecurityType == SecurityType.Cfd) && + // ignore universe symbols + !symbol.Value.Contains("-UNIVERSE-") && + symbol.ID.Market == Market.Oanda; } private bool Refresh() From e98a21557c1bb50db670dcf3d9de839be3fd5cec Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Fri, 23 Feb 2024 15:58:26 -0400 Subject: [PATCH 2/3] Minor improvements --- .../OandaBrokerageHistoryProviderTests.cs | 32 ++++++++----------- .../OandaDataDownloader.cs | 6 ++++ QuantConnect.OandaBrokerage/OandaBrokerage.cs | 12 +++++++ 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/QuantConnect.OandaBrokerage.Tests/OandaBrokerageHistoryProviderTests.cs b/QuantConnect.OandaBrokerage.Tests/OandaBrokerageHistoryProviderTests.cs index 771e53b..49ddfc4 100644 --- a/QuantConnect.OandaBrokerage.Tests/OandaBrokerageHistoryProviderTests.cs +++ b/QuantConnect.OandaBrokerage.Tests/OandaBrokerageHistoryProviderTests.cs @@ -40,35 +40,35 @@ private static TestCaseData[] TestParameters return new[] { // valid parameters - new TestCaseData(eurusd, Resolution.Second, Time.OneMinute, TickType.Quote, false, false), - new TestCaseData(eurusd, Resolution.Minute, Time.OneHour, TickType.Quote, false, false), - new TestCaseData(eurusd, Resolution.Hour, Time.OneDay, TickType.Quote, false, false), - new TestCaseData(eurusd, Resolution.Daily, TimeSpan.FromDays(15), TickType.Quote, false, false), + new TestCaseData(eurusd, Resolution.Second, Time.OneMinute, TickType.Quote, false), + new TestCaseData(eurusd, Resolution.Minute, Time.OneHour, TickType.Quote, false), + new TestCaseData(eurusd, Resolution.Hour, Time.OneDay, TickType.Quote, false), + new TestCaseData(eurusd, Resolution.Daily, TimeSpan.FromDays(15), TickType.Quote, false), // invalid resolution, null result - new TestCaseData(eurusd, Resolution.Tick, TimeSpan.FromSeconds(15), TickType.Quote, false, true), + new TestCaseData(eurusd, Resolution.Tick, TimeSpan.FromSeconds(15), TickType.Quote, true), - // invalid period, no error, empty result - new TestCaseData(eurusd, Resolution.Daily, TimeSpan.FromDays(-15), TickType.Quote, true, false), + // invalid period, null result + new TestCaseData(eurusd, Resolution.Daily, TimeSpan.FromDays(-15), TickType.Quote, true), // invalid symbol, null result - new TestCaseData(Symbol.Create("XYZ", SecurityType.Forex, Market.FXCM), Resolution.Daily, TimeSpan.FromDays(15), TickType.Quote, false, true), + new TestCaseData(Symbol.Create("XYZ", SecurityType.Forex, Market.FXCM), Resolution.Daily, TimeSpan.FromDays(15), TickType.Quote, true), // invalid security type, null result - new TestCaseData(Symbols.AAPL, Resolution.Daily, TimeSpan.FromDays(15), TickType.Quote, false, true), + new TestCaseData(Symbols.AAPL, Resolution.Daily, TimeSpan.FromDays(15), TickType.Quote, true), // invalid market, null result - new TestCaseData(Symbol.Create("EURUSD", SecurityType.Forex, Market.USA), Resolution.Daily, TimeSpan.FromDays(15), TickType.Quote, false, true), + new TestCaseData(Symbol.Create("EURUSD", SecurityType.Forex, Market.USA), Resolution.Daily, TimeSpan.FromDays(15), TickType.Quote, true), // invalid tick type, null result - new TestCaseData(eurusd, Resolution.Daily, TimeSpan.FromDays(15), TickType.Trade, false, true), - new TestCaseData(eurusd, Resolution.Daily, TimeSpan.FromDays(15), TickType.OpenInterest, false, true), + new TestCaseData(eurusd, Resolution.Daily, TimeSpan.FromDays(15), TickType.Trade, true), + new TestCaseData(eurusd, Resolution.Daily, TimeSpan.FromDays(15), TickType.OpenInterest, true), }; } } [Test, TestCaseSource(nameof(TestParameters))] - public void GetsHistory(Symbol symbol, Resolution resolution, TimeSpan period, TickType tickType, bool shouldBeEmpty, bool unsupported) + public void GetsHistory(Symbol symbol, Resolution resolution, TimeSpan period, TickType tickType, bool unsupported) { var environment = Config.Get("oanda-environment").ConvertTo(); var accessToken = Config.Get("oanda-access-token"); @@ -98,11 +98,7 @@ public void GetsHistory(Symbol symbol, Resolution resolution, TimeSpan period, T return; } - if (shouldBeEmpty) - { - Assert.IsEmpty(history); - return; - } + Assert.IsNotNull(history); foreach (var bar in history.Cast()) { diff --git a/QuantConnect.OandaBrokerage.ToolBox/OandaDataDownloader.cs b/QuantConnect.OandaBrokerage.ToolBox/OandaDataDownloader.cs index 425c5e9..15268c9 100644 --- a/QuantConnect.OandaBrokerage.ToolBox/OandaDataDownloader.cs +++ b/QuantConnect.OandaBrokerage.ToolBox/OandaDataDownloader.cs @@ -102,6 +102,12 @@ public IEnumerable Get(DataDownloaderGetParameters dataDownloaderGetPa return null; } + if (dataDownloaderGetParameters.StartUtc >= dataDownloaderGetParameters.EndUtc) + { + Logging.Log.Trace("OandaDataDownloader.Get(): The history request start date must precede the end date, no history returned"); + return null; + } + return GetData(dataDownloaderGetParameters); } diff --git a/QuantConnect.OandaBrokerage/OandaBrokerage.cs b/QuantConnect.OandaBrokerage/OandaBrokerage.cs index 2666b0c..2958bc5 100644 --- a/QuantConnect.OandaBrokerage/OandaBrokerage.cs +++ b/QuantConnect.OandaBrokerage/OandaBrokerage.cs @@ -54,6 +54,7 @@ public class OandaBrokerage : Brokerage, IDataQueueHandler private bool _unsupportedAssetForHistoryLogged; private bool _unsupportedResolutionForHistoryLogged; private bool _unsupportedTickTypeForHistoryLogged; + private bool _invalidTimeRangeHistoryLogged; /// /// The maximum number of bars per historical data request @@ -254,6 +255,17 @@ public override IEnumerable GetHistory(HistoryRequest request) return null; } + if (request.StartTimeUtc >= request.EndTimeUtc) + { + if (!_invalidTimeRangeHistoryLogged) + { + Log.Trace("OandaBrokerage.GetHistory(): The request start date must precede the end date, no history returned."); + _invalidTimeRangeHistoryLogged = true; + } + + return null; + } + return GetHistoryImpl(request); } From 5abbf464b25ea4e35daf10b739bbd899f5f5c20a Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Mon, 26 Feb 2024 18:18:08 -0400 Subject: [PATCH 3/3] Centralize history request validation --- .../OandaDataDownloader.cs | 54 ++----------------- QuantConnect.OandaBrokerage/OandaBrokerage.cs | 43 ++++++++++----- 2 files changed, 35 insertions(+), 62 deletions(-) diff --git a/QuantConnect.OandaBrokerage.ToolBox/OandaDataDownloader.cs b/QuantConnect.OandaBrokerage.ToolBox/OandaDataDownloader.cs index 15268c9..82205e0 100644 --- a/QuantConnect.OandaBrokerage.ToolBox/OandaDataDownloader.cs +++ b/QuantConnect.OandaBrokerage.ToolBox/OandaDataDownloader.cs @@ -70,49 +70,15 @@ public SecurityType GetSecurityType(string symbol) /// /// Get historical data enumerable for a single symbol, type and resolution given this start and end time (in UTC). /// - /// model class for passing in parameters for historical data + /// model class for passing in parameters for historical data /// Enumerable of base data for this symbol - public IEnumerable Get(DataDownloaderGetParameters dataDownloaderGetParameters) + public IEnumerable Get(DataDownloaderGetParameters parameters) { - var symbol = dataDownloaderGetParameters.Symbol; - var resolution = dataDownloaderGetParameters.Resolution; - var tickType = dataDownloaderGetParameters.TickType; - - if (tickType != TickType.Quote) - { - Logging.Log.Trace("OandaDataDownloader.Get(): Unsupported tick type: " + tickType); - return null; - } - - if (!_symbolMapper.IsKnownLeanSymbol(symbol)) - { - Logging.Log.Trace("OandaDataDownloader.Get(): Unsupported symbol: " + symbol); - return null; - } - - if (resolution == Resolution.Tick) - { - Logging.Log.Trace("OandaDataDownloader.Get(): Unsupported resolution: " + resolution); - return null; - } - - if (symbol.ID.SecurityType != SecurityType.Forex && symbol.ID.SecurityType != SecurityType.Cfd) + if (!_brokerage.IsValidHistoryRequest(parameters.Symbol, parameters.StartUtc, parameters.EndUtc, parameters.Resolution, parameters.TickType)) { - Logging.Log.Trace("OandaDataDownloader.Get(): Unsupported security type: " + symbol.ID.SecurityType); return null; } - if (dataDownloaderGetParameters.StartUtc >= dataDownloaderGetParameters.EndUtc) - { - Logging.Log.Trace("OandaDataDownloader.Get(): The history request start date must precede the end date, no history returned"); - return null; - } - - return GetData(dataDownloaderGetParameters); - } - - private IEnumerable GetData(DataDownloaderGetParameters parameters) - { var symbol = parameters.Symbol; var resolution = parameters.Resolution; var startUtc = parameters.StartUtc; @@ -183,21 +149,9 @@ private IEnumerable GetData(DataDownloaderGetParameters parameters) barsTotalInPeriod.AddRange(barsToSave); } - switch (resolution) - { - case Resolution.Second: - case Resolution.Minute: - case Resolution.Hour: - case Resolution.Daily: - foreach (var bar in LeanData.AggregateQuoteBars(barsTotalInPeriod, symbol, resolution.ToTimeSpan())) - { - yield return bar; - } - break; - } + return LeanData.AggregateQuoteBars(barsTotalInPeriod, symbol, resolution.ToTimeSpan()); } - /// /// Groups a list of bars into a dictionary keyed by date /// diff --git a/QuantConnect.OandaBrokerage/OandaBrokerage.cs b/QuantConnect.OandaBrokerage/OandaBrokerage.cs index 2958bc5..76add58 100644 --- a/QuantConnect.OandaBrokerage/OandaBrokerage.cs +++ b/QuantConnect.OandaBrokerage/OandaBrokerage.cs @@ -225,37 +225,56 @@ public override bool CancelOrder(Order order) /// An enumerable of bars covering the span specified in the request public override IEnumerable GetHistory(HistoryRequest request) { - if (!_api.CanSubscribe(request.Symbol) || !_symbolMapper.IsKnownLeanSymbol(request.Symbol)) + if (!IsValidHistoryRequest(request.Symbol, request.StartTimeUtc, request.EndTimeUtc, request.Resolution, request.TickType)) + { + return null; + } + + return GetHistoryImpl(request); + } + + /// + /// Validates the historical data request parameters + /// + /// The asset symbol data is being requested for + /// The UTC start time of the request + /// The UTC end time of the request + /// The resolution of the requested data + /// The tick type of the requested data + /// Whether the parameters are valid for a history request + public bool IsValidHistoryRequest(Symbol symbol, DateTime startTimeUtc, DateTime endTimeUtc, Resolution resolution, TickType tickType) + { + if (!_api.CanSubscribe(symbol) || !_symbolMapper.IsKnownLeanSymbol(symbol)) { if (!_unsupportedAssetForHistoryLogged) { - Log.Trace($"OandaBrokerage.GetHistory(): Unsupported asset: {request.Symbol}, no history returned"); + Log.Trace($"OandaBrokerage.GetHistory(): Unsupported asset: {symbol}, no history returned"); _unsupportedAssetForHistoryLogged = true; } - return null; + return false; } - if (request.Resolution == Resolution.Tick) + if (resolution == Resolution.Tick) { if (!_unsupportedResolutionForHistoryLogged) { - Log.Trace($"OandaBrokerage.GetHistory(): Unsupported resolution: {request.Resolution}, no history returned"); + Log.Trace($"OandaBrokerage.GetHistory(): Unsupported resolution: {resolution}, no history returned"); _unsupportedResolutionForHistoryLogged = true; } - return null; + return false; } - if (request.TickType != TickType.Quote) + if (tickType != TickType.Quote) { if (!_unsupportedTickTypeForHistoryLogged) { - Log.Trace($"OandaBrokerage.GetHistory(): Unsupported tick type: {request.TickType}, no history returned"); + Log.Trace($"OandaBrokerage.GetHistory(): Unsupported tick type: {tickType}, no history returned"); _unsupportedTickTypeForHistoryLogged = true; } - return null; + return false; } - if (request.StartTimeUtc >= request.EndTimeUtc) + if (startTimeUtc >= endTimeUtc) { if (!_invalidTimeRangeHistoryLogged) { @@ -263,10 +282,10 @@ public override IEnumerable GetHistory(HistoryRequest request) _invalidTimeRangeHistoryLogged = true; } - return null; + return false; } - return GetHistoryImpl(request); + return true; } private IEnumerable GetHistoryImpl(HistoryRequest request)