diff --git a/QuantConnect.SamcoBrokerage.Tests/SamcoBrokerageHistoryProviderTests.cs b/QuantConnect.SamcoBrokerage.Tests/SamcoBrokerageHistoryProviderTests.cs index 4d3d18b..3c21667 100644 --- a/QuantConnect.SamcoBrokerage.Tests/SamcoBrokerageHistoryProviderTests.cs +++ b/QuantConnect.SamcoBrokerage.Tests/SamcoBrokerageHistoryProviderTests.cs @@ -20,6 +20,7 @@ using QuantConnect.Data.Market; using QuantConnect.Logging; using QuantConnect.Securities; +using QuantConnect.Tests.Engine.DataFeeds; using System; namespace QuantConnect.Tests.Brokerages.Samco @@ -34,61 +35,77 @@ private static TestCaseData[] TestParameters return new[] { // valid parameters - new TestCaseData(Symbols.SBIN, Resolution.Tick, Time.OneMinute, false), - new TestCaseData(Symbols.SBIN, Resolution.Second, Time.OneMinute, false), - new TestCaseData(Symbols.SBIN, Resolution.Minute, Time.OneHour, false), - new TestCaseData(Symbols.SBIN, Resolution.Hour, Time.OneDay, false), - new TestCaseData(Symbols.SBIN, Resolution.Daily, TimeSpan.FromDays(15), false), - new TestCaseData(Symbols.SBIN, Resolution.Daily, TimeSpan.FromDays(-15), false) + new TestCaseData(Symbols.SBIN, Resolution.Minute, TickType.Trade, Time.OneHour, false), + + // invalid resolution + new TestCaseData(Symbols.SBIN, Resolution.Tick, TickType.Trade, Time.OneMinute, true), + new TestCaseData(Symbols.SBIN, Resolution.Second, TickType.Trade, Time.OneMinute, true), + new TestCaseData(Symbols.SBIN, Resolution.Hour, TickType.Trade, Time.OneDay, true), + new TestCaseData(Symbols.SBIN, Resolution.Daily, TickType.Trade, TimeSpan.FromDays(15), true), + + // invalid tick type + new TestCaseData(Symbols.SBIN, Resolution.Minute, TickType.Quote, Time.OneHour, true), + new TestCaseData(Symbols.SBIN, Resolution.Minute, TickType.OpenInterest, Time.OneHour, true), + + // invalid security type + new TestCaseData(Symbols.USDJPY, Resolution.Minute, TickType.Trade, Time.OneHour, true), + + // invalid market + new TestCaseData(Symbol.Create("SBIN", SecurityType.Equity, Market.USA), Resolution.Minute, TickType.Trade, Time.OneHour, true), + + // invalid time range + new TestCaseData(Symbols.SBIN, Resolution.Minute, TickType.Trade, TimeSpan.FromDays(-15), true), }; } } [Test, TestCaseSource(nameof(TestParameters))] - public void GetsHistory(Symbol symbol, Resolution resolution, TimeSpan period, bool throwsException) + public void GetsHistory(Symbol symbol, Resolution resolution, TickType tickType, TimeSpan period, bool unsupported) { - TestDelegate test = () => - { - var apiSecret = Config.Get("samco-client-password"); - var apiKey = Config.Get("samco-client-id"); - var yob = Config.Get("samco-year-of-birth"); - var tradingSegment = Config.Get("samco-trading-segment"); - var productType = Config.Get("samco-product-type"); - var brokerage = new SamcoBrokerage(tradingSegment, productType, apiKey, apiSecret, yob, null, null); - - var now = DateTime.UtcNow; - - var request = new HistoryRequest(now.Add(-period), - now, - typeof(TradeBar), - symbol, - resolution, - SecurityExchangeHours.AlwaysOpen(TimeZones.Kolkata), - TimeZones.Kolkata, - Resolution.Minute, - false, - false, - DataNormalizationMode.Adjusted, - TickType.Trade); - - var history = brokerage.GetHistory(request); - - foreach (var slice in history) - { - Log.Trace("{0}: {1} - {2} / {3}", slice.Time, slice.Symbol, slice.Price, slice.IsFillForward); - } + var apiSecret = Config.Get("samco-client-password"); + var apiKey = Config.Get("samco-client-id"); + var yob = Config.Get("samco-year-of-birth"); + var tradingSegment = Config.Get("samco-trading-segment"); + var productType = Config.Get("samco-product-type"); - Log.Trace("Base currency: " + brokerage.AccountBaseCurrency); - }; + var now = DateTime.UtcNow; - if (throwsException) + var algorithm = new AlgorithmStub(); + algorithm.Portfolio = new SecurityPortfolioManager(new SecurityManager(new TimeKeeper(now)), algorithm.Transactions, algorithm.Settings); + var security = algorithm.AddSecurity(symbol); + algorithm.Portfolio.Securities.Add(security); + + var brokerage = new SamcoBrokerage(tradingSegment, productType, apiKey, apiSecret, yob, algorithm, null); + + var request = new HistoryRequest(now.Add(-period), + now, + typeof(TradeBar), + symbol, + resolution, + SecurityExchangeHours.AlwaysOpen(TimeZones.Kolkata), + TimeZones.Kolkata, + Resolution.Minute, + false, + false, + DataNormalizationMode.Adjusted, + tickType); + + var history = brokerage.GetHistory(request); + + if (unsupported) { - Assert.Throws(test); + Assert.IsNull(history); + return; } - else + + Assert.IsNotNull(history); + + foreach (var slice in history) { - Assert.DoesNotThrow(test); + Log.Trace("{0}: {1} - {2} / {3}", slice.Time, slice.Symbol, slice.Price, slice.IsFillForward); } + + Log.Trace("Base currency: " + brokerage.AccountBaseCurrency); } } } \ No newline at end of file diff --git a/QuantConnect.SamcoBrokerage/SamcoBrokerage.cs b/QuantConnect.SamcoBrokerage/SamcoBrokerage.cs index 794509e..59f67a3 100644 --- a/QuantConnect.SamcoBrokerage/SamcoBrokerage.cs +++ b/QuantConnect.SamcoBrokerage/SamcoBrokerage.cs @@ -83,6 +83,12 @@ public partial class SamcoBrokerage : Brokerage, IDataQueueHandler private Task _checkConnectionTask; private bool _isInitialized; + private bool _loggedInvalidTickTypeForHistory; + private bool _loggedInvalidSecurityTypeForHistory; + private bool _loggedInvalidResolutionForHistory; + private bool _loggedInvalidTimeRangeForHistory; + private bool _loggedInvalidMarketForHistory; + /// /// The websockets client instance /// @@ -346,35 +352,59 @@ public override IEnumerable GetHistory(HistoryRequest request) // Samco API only allows us to support history requests for TickType.Trade if (request.TickType != TickType.Trade) { - OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidTickType", - $"{request.TickType} TickType not supported, no history returned")); - yield break; + if (!_loggedInvalidTickTypeForHistory) + { + OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidTickType", + $"{request.TickType} TickType not supported, no history returned")); + _loggedInvalidTickTypeForHistory = true; + } + return null; } - if (request.Symbol.SecurityType != SecurityType.Equity && request.Symbol.SecurityType != SecurityType.Future && request.Symbol.SecurityType != SecurityType.Option) + if (request.Symbol.SecurityType != SecurityType.Equity && + request.Symbol.SecurityType != SecurityType.Future && + request.Symbol.SecurityType != SecurityType.Option) { - OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidSecurityType", - $"{request.Symbol.SecurityType} security type not supported, no history returned")); - yield break; + if (!_loggedInvalidSecurityTypeForHistory) + { + OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidSecurityType", + $"{request.Symbol.SecurityType} security type not supported, no history returned")); + _loggedInvalidSecurityTypeForHistory = true; + } + return null; } - if (request.Resolution == Resolution.Tick || request.Resolution == Resolution.Second) + if (request.Symbol.ID.Market != Market.India) { - OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidResolution", - $"{request.Resolution} resolution not supported, no history returned")); - yield break; + if (!_loggedInvalidMarketForHistory) + { + OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidMarket", + $"{request.Symbol.ID.Market} market not supported, no history returned")); + _loggedInvalidMarketForHistory = true; + } + return null; } - if (request.StartTimeUtc >= request.EndTimeUtc) + if (request.Resolution != Resolution.Minute) { - OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidDateRange", - "The history request start date must precede the end date, no history returned")); - yield break; + if (!_loggedInvalidResolutionForHistory) + { + OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidResolution", + $"{request.Resolution} resolution not supported, no history returned")); + _loggedInvalidResolutionForHistory = true; + } + return null; } - if (request.Resolution != Resolution.Minute) + if (request.StartTimeUtc >= request.EndTimeUtc) { - throw new ArgumentException($"SamcoBrokerage.ConvertResolution: Unsupported resolution type: {request.Resolution}"); + if (!_loggedInvalidTimeRangeForHistory) + { + OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidDateRange", + "The history request start date must precede the end date, no history returned")); + _loggedInvalidTimeRangeForHistory = true; + } + return null; } var leanSymbol = request.Symbol; @@ -382,16 +412,9 @@ public override IEnumerable GetHistory(HistoryRequest request) var exchange = _symbolMapper.GetExchange(leanSymbol); var isIndex = leanSymbol.SecurityType == SecurityType.Index; - var history = _samcoAPI.GetIntradayCandles(request.Symbol, exchange, request.StartTimeLocal, request.EndTimeLocal, request.Resolution, isIndex); - - foreach (var baseData in history) - { - if (!securityExchange.DateTimeIsOpen(baseData.Time) && !request.IncludeExtendedMarketHours) - { - continue; - } - yield return baseData; - } + return _samcoAPI + .GetIntradayCandles(request.Symbol, exchange, request.StartTimeLocal, request.EndTimeLocal, request.Resolution, isIndex) + .Where(baseData => securityExchange.DateTimeIsOpen(baseData.Time) || request.IncludeExtendedMarketHours); } ///