From 169c6ca359686a32c8a16482898d0ef6052dd068 Mon Sep 17 00:00:00 2001 From: Roman Yavnikov <45608740+Romazes@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:23:57 +0200 Subject: [PATCH] Initialization via Job Packet and Rename DataQueueHandler to DataProvider (#2) * rename: namespaces * rename: DataQueueHandler -> DataProvider * feat: Config -> Globals in ValidateSubscription * feat: SetJob fix: IsConnected Property feat: handle of null warnings * feat: reference on Lean\Tests + init Globals * refactor: use CanSubscribe for Symbol in GetHistory() * refactor: missed Lean in namespaces * rename: QueueHandler -> DataProvider Tests revert: commented code * fix: workflow dotnet test path * fix: testcase param forex instead of equity * refactor: return null in wrong request of GetHistory() * feat: add stop spamming flags feat: null sign refactor: remove extra else block * remove: duplicate validation from downloader * revert: IsConnected handle connection --- .github/workflows/build.yml | 12 +- DataProcessing/CoinApiDataConverter.cs | 2 +- DataProcessing/DataProcessing.csproj | 2 +- Lean.DataSource.CoinAPI.sln | 4 +- .../CoinAPIDataDownloaderTests.cs | 17 ++- .../CoinAPIHistoryProviderTests.cs | 26 ++-- .../CoinAPISymbolMapperTests.cs | 2 +- .../CoinApiAdditionalTests.cs | 31 +++- ...lerTest.cs => CoinApiDataProviderTests.cs} | 16 +- .../CoinApiTestHelper.cs | 4 +- ...ntConnect.DataSource.CoinAPI.Tests.csproj} | 11 +- QuantConnect.CoinAPI.Tests/TestSetup.cs | 2 +- QuantConnect.CoinAPI/CoinAPIDataDownloader.cs | 34 ++--- ...vider.cs => CoinApiDataHistoryProvider.cs} | 97 +++++++++--- ...QueueHandler.cs => CoinApiDataProvider.cs} | 143 +++++++++++++----- QuantConnect.CoinAPI/CoinApiProduct.cs | 2 +- QuantConnect.CoinAPI/CoinApiSymbol.cs | 2 +- QuantConnect.CoinAPI/CoinApiSymbolMapper.cs | 4 +- QuantConnect.CoinAPI/Messages/BaseMessage.cs | 2 +- QuantConnect.CoinAPI/Messages/ErrorMessage.cs | 2 +- QuantConnect.CoinAPI/Messages/HelloMessage.cs | 2 +- .../Messages/HistoricalDataMessage.cs | 2 +- QuantConnect.CoinAPI/Messages/QuoteMessage.cs | 2 +- QuantConnect.CoinAPI/Messages/TradeMessage.cs | 2 +- .../Models/CoinApiErrorResponse.cs | 2 +- ...=> QuantConnect.DataSource.CoinAPI.csproj} | 8 +- 26 files changed, 285 insertions(+), 148 deletions(-) rename QuantConnect.CoinAPI.Tests/{CoinApiDataQueueHandlerTest.cs => CoinApiDataProviderTests.cs} (90%) rename QuantConnect.CoinAPI.Tests/{QuantConnect.CoinAPI.Tests.csproj => QuantConnect.DataSource.CoinAPI.Tests.csproj} (76%) rename QuantConnect.CoinAPI/{CoinApi.HistoryProvider.cs => CoinApiDataHistoryProvider.cs} (62%) rename QuantConnect.CoinAPI/{CoinApiDataQueueHandler.cs => CoinApiDataProvider.cs} (83%) rename QuantConnect.CoinAPI/{QuantConnect.CoinAPI.csproj => QuantConnect.DataSource.CoinAPI.csproj} (83%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1a93820..f6a6d06 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,14 +46,14 @@ jobs: image: quantconnect/lean:foundation options: -v /home/runner/work:/__w --workdir /__w/Lean.DataSource.CoinAPI/Lean.DataSource.CoinAPI -e QC_JOB_USER_ID=${{ secrets.QC_JOB_USER_ID }} -e QC_API_ACCESS_TOKEN=${{ secrets.QC_API_ACCESS_TOKEN }} -e QC_JOB_ORGANIZATION_ID=${{ secrets.QC_JOB_ORGANIZATION_ID }} -e QC_COINAPI_API_KEY=${{ secrets.QC_COINAPI_API_KEY }} - - name: Build QuantConnect.CoinAPI - run: dotnet build ./QuantConnect.CoinAPI/QuantConnect.CoinAPI.csproj /p:Configuration=Release /v:quiet /p:WarningLevel=1 + - name: Build QuantConnect.DataSource.CoinAPI + run: dotnet build ./QuantConnect.CoinAPI/QuantConnect.DataSource.CoinAPI.csproj /p:Configuration=Release /v:quiet /p:WarningLevel=1 - name: Build DataProcessing run: dotnet build ./DataProcessing/DataProcessing.csproj /p:Configuration=Release /v:quiet /p:WarningLevel=1 - - name: Build QuantConnect.CoinAPI.Tests - run: dotnet build ./QuantConnect.CoinAPI.Tests/QuantConnect.CoinAPI.Tests.csproj /p:Configuration=Release /v:quiet /p:WarningLevel=1 + - name: Build QuantConnect.DataSource.CoinAPI.Tests + run: dotnet build ./QuantConnect.CoinAPI.Tests/QuantConnect.DataSource.CoinAPI.Tests.csproj /p:Configuration=Release /v:quiet /p:WarningLevel=1 - - name: Run QuantConnect.CoinAPI.Tests - run: dotnet test ./QuantConnect.CoinAPI.Tests/bin/Release/QuantConnect.CoinAPI.Tests.dll \ No newline at end of file + - name: Run QuantConnect.DataSource.CoinAPI.Tests + run: dotnet test ./QuantConnect.CoinAPI.Tests/bin/Release/QuantConnect.Lean.DataSource.CoinAPI.Tests.dll \ No newline at end of file diff --git a/DataProcessing/CoinApiDataConverter.cs b/DataProcessing/CoinApiDataConverter.cs index ccd00e9..12b61ff 100644 --- a/DataProcessing/CoinApiDataConverter.cs +++ b/DataProcessing/CoinApiDataConverter.cs @@ -19,7 +19,7 @@ using System.Diagnostics; using QuantConnect.Logging; using QuantConnect.ToolBox; -using QuantConnect.CoinAPI; +using QuantConnect.Lean.DataSource.CoinAPI; namespace QuantConnect.DataProcessing { diff --git a/DataProcessing/DataProcessing.csproj b/DataProcessing/DataProcessing.csproj index a5bc5cb..d27e9b0 100644 --- a/DataProcessing/DataProcessing.csproj +++ b/DataProcessing/DataProcessing.csproj @@ -29,7 +29,7 @@ - + diff --git a/Lean.DataSource.CoinAPI.sln b/Lean.DataSource.CoinAPI.sln index 1c6eb39..c53e966 100644 --- a/Lean.DataSource.CoinAPI.sln +++ b/Lean.DataSource.CoinAPI.sln @@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.5.002.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuantConnect.CoinAPI", "QuantConnect.CoinAPI\QuantConnect.CoinAPI.csproj", "{2BEB31AD-5B1E-4D9B-A206-D67F3CA33A4C}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuantConnect.DataSource.CoinAPI", "QuantConnect.CoinAPI\QuantConnect.DataSource.CoinAPI.csproj", "{2BEB31AD-5B1E-4D9B-A206-D67F3CA33A4C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuantConnect.CoinAPI.Tests", "QuantConnect.CoinAPI.Tests\QuantConnect.CoinAPI.Tests.csproj", "{337CEE6E-639A-448D-95ED-2C1628E26AF2}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuantConnect.DataSource.CoinAPI.Tests", "QuantConnect.CoinAPI.Tests\QuantConnect.DataSource.CoinAPI.Tests.csproj", "{337CEE6E-639A-448D-95ED-2C1628E26AF2}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataProcessing", "DataProcessing\DataProcessing.csproj", "{881514B4-641E-4EDC-8020-6BEA0CC8F48C}" EndProject diff --git a/QuantConnect.CoinAPI.Tests/CoinAPIDataDownloaderTests.cs b/QuantConnect.CoinAPI.Tests/CoinAPIDataDownloaderTests.cs index 669d48a..e028d34 100644 --- a/QuantConnect.CoinAPI.Tests/CoinAPIDataDownloaderTests.cs +++ b/QuantConnect.CoinAPI.Tests/CoinAPIDataDownloaderTests.cs @@ -15,9 +15,10 @@ using NUnit.Framework; using QuantConnect.Util; +using QuantConnect.Tests; using QuantConnect.Logging; -namespace QuantConnect.CoinAPI.Tests +namespace QuantConnect.Lean.DataSource.CoinAPI.Tests { [TestFixture] public class CoinAPIDataDownloaderTests @@ -52,8 +53,9 @@ public void DownloadsHistoricalDataWithValidDataTestParameters(Symbol symbol, Re { var parameters = new DataDownloaderGetParameters(symbol, resolution, startDateTimeUtc, endDateTimeUtc, TickType.Trade); - var downloadResponse = _downloader.Get(parameters).ToList(); + var downloadResponse = _downloader.Get(parameters)?.ToList(); + Assert.IsNotNull(downloadResponse); Assert.IsNotEmpty(downloadResponse); Log.Trace($"{symbol}.{resolution}.[{startDateTimeUtc} - {endDateTimeUtc}]: Amount = {downloadResponse.Count}"); @@ -83,19 +85,20 @@ public void DownloadsHistoricalDataWithInvalidDataTestParameters(Symbol symbol, { var parameters = new DataDownloaderGetParameters(symbol, resolution, startDateTimeUtc, endDateTimeUtc, tickType); - var downloadResponse = _downloader.Get(parameters).ToList(); + var downloadResponse = _downloader.Get(parameters)?.ToList(); - Assert.IsEmpty(downloadResponse); + Assert.IsNull(downloadResponse); } private static IEnumerable HistoricalInvalidDataThrowExceptionTestCases { get { + TestGlobals.Initialize(); yield return new TestCaseData(Symbol.Create("BTCBTC", SecurityType.Crypto, Market.Binance)) .SetDescription($"Wrong Symbol - 'BTCBTC'"); - yield return new TestCaseData(Symbol.Create("ETHUSDT", SecurityType.Equity, Market.Binance)) - .SetDescription($"Wrong SecurityType - {SecurityType.Equity}"); + yield return new TestCaseData(Symbol.Create("ETHUSDT", SecurityType.Forex, Market.Binance)) + .SetDescription($"Wrong SecurityType - {SecurityType.Forex}"); } } @@ -104,7 +107,7 @@ public void DownloadsHistoricalDataWithInvalidDataTestParametersThrowException(S { var parameters = new DataDownloaderGetParameters(symbol, Resolution.Minute, new DateTime(2024, 1, 1), new DateTime(2024, 2, 1), TickType.Trade); - Assert.That(() => _downloader.Get(parameters).ToList(), Throws.Exception); + Assert.That(() => _downloader.Get(parameters)?.ToList(), Throws.Exception); } } } diff --git a/QuantConnect.CoinAPI.Tests/CoinAPIHistoryProviderTests.cs b/QuantConnect.CoinAPI.Tests/CoinAPIHistoryProviderTests.cs index a7c93fd..4408f77 100644 --- a/QuantConnect.CoinAPI.Tests/CoinAPIHistoryProviderTests.cs +++ b/QuantConnect.CoinAPI.Tests/CoinAPIHistoryProviderTests.cs @@ -20,7 +20,7 @@ using QuantConnect.Data.Market; using QuantConnect.Securities; -namespace QuantConnect.CoinAPI.Tests +namespace QuantConnect.Lean.DataSource.CoinAPI.Tests { [TestFixture] public class CoinAPIHistoryProviderTests @@ -52,7 +52,7 @@ public void OneTimeSetUp() [Test] [TestCaseSource(nameof(TestData))] - public void CanGetHistory(Symbol symbol, Resolution resolution, Type dataType, int period, bool isNonEmptyResult) + public void CanGetHistory(Symbol symbol, Resolution resolution, Type dataType, int period, bool isNotNullResult) { _coinApiDataQueueHandler.SetUpHistDataLimit(100); @@ -67,14 +67,15 @@ public void CanGetHistory(Symbol symbol, Resolution resolution, Type dataType, i resolution, true, false, DataNormalizationMode.Raw, TickType.Trade) }; - var slices = _coinApiDataQueueHandler.GetHistory(historyRequests, TimeZones.Utc).ToArray(); + var slices = _coinApiDataQueueHandler.GetHistory(historyRequests, TimeZones.Utc)?.ToArray(); - if (isNonEmptyResult) + if (isNotNullResult) { + Assert.IsNotNull(slices); // For resolution larger than second do more tests if (resolution > Resolution.Second) { - Assert.AreEqual(period, slices.Length); + Assert.That(slices.Length, Is.EqualTo(period)); var firstSliceTradeBars = slices.First().Bars.Values; @@ -83,8 +84,8 @@ public void CanGetHistory(Symbol symbol, Resolution resolution, Type dataType, i firstSliceTradeBars.DoForEach(tb => { var resTimeSpan = resolution.ToTimeSpan(); - Assert.AreEqual(resTimeSpan, tb.Period); - Assert.AreEqual(startTimeUtc.RoundUp(resTimeSpan), tb.Time); + Assert.That(tb.Period, Is.EqualTo(resTimeSpan)); + Assert.That(tb.Time, Is.EqualTo(startTimeUtc.RoundUp(resTimeSpan))); }); var lastSliceTradeBars = slices.Last().Bars.Values; @@ -92,8 +93,8 @@ public void CanGetHistory(Symbol symbol, Resolution resolution, Type dataType, i lastSliceTradeBars.DoForEach(tb => { var resTimeSpan = resolution.ToTimeSpan(); - Assert.AreEqual(resTimeSpan, tb.Period); - Assert.AreEqual(nowUtc.RoundDown(resTimeSpan), tb.Time); + Assert.That(tb.Period, Is.EqualTo(resTimeSpan)); + Assert.That(tb.Time, Is.EqualTo(nowUtc.RoundDown(resTimeSpan))); }); } // For res. second data counts, start/end dates may slightly vary from historical request's @@ -101,7 +102,7 @@ public void CanGetHistory(Symbol symbol, Resolution resolution, Type dataType, i else { Assert.IsTrue(slices.Length > 0); - Assert.AreEqual(resolution.ToTimeSpan(), slices.First().Bars.Values.FirstOrDefault()?.Period); + Assert.That(slices.First().Bars.Values.FirstOrDefault()?.Period, Is.EqualTo(resolution.ToTimeSpan())); } // Slices are ordered by time @@ -109,12 +110,11 @@ public void CanGetHistory(Symbol symbol, Resolution resolution, Type dataType, i } else { - // Empty - Assert.IsEmpty(slices); + Assert.IsNull(slices); } } - public class CoinApiDataQueueHandlerMock : CoinApiDataQueueHandler + public class CoinApiDataQueueHandlerMock : CoinApiDataProvider { public new void SetUpHistDataLimit(int limit) { diff --git a/QuantConnect.CoinAPI.Tests/CoinAPISymbolMapperTests.cs b/QuantConnect.CoinAPI.Tests/CoinAPISymbolMapperTests.cs index 7303fc1..1f2e9cd 100644 --- a/QuantConnect.CoinAPI.Tests/CoinAPISymbolMapperTests.cs +++ b/QuantConnect.CoinAPI.Tests/CoinAPISymbolMapperTests.cs @@ -16,7 +16,7 @@ using NUnit.Framework; -namespace QuantConnect.CoinAPI.Tests +namespace QuantConnect.Lean.DataSource.CoinAPI.Tests { [TestFixture] public class CoinAPISymbolMapperTests diff --git a/QuantConnect.CoinAPI.Tests/CoinApiAdditionalTests.cs b/QuantConnect.CoinAPI.Tests/CoinApiAdditionalTests.cs index 97ee813..23b76a1 100644 --- a/QuantConnect.CoinAPI.Tests/CoinApiAdditionalTests.cs +++ b/QuantConnect.CoinAPI.Tests/CoinApiAdditionalTests.cs @@ -14,9 +14,10 @@ */ using NUnit.Framework; +using QuantConnect.Packets; using QuantConnect.Configuration; -namespace QuantConnect.CoinAPI.Tests +namespace QuantConnect.Lean.DataSource.CoinAPI.Tests { [TestFixture] public class CoinApiAdditionalTests @@ -28,11 +29,37 @@ public void ThrowsOnFailedAuthentication() Assert.Throws(() => { - using var _coinApiDataQueueHandler = new CoinApiDataQueueHandler(); + using var _coinApiDataQueueHandler = new CoinApiDataProvider(); }); // reset api key TestSetup.GlobalSetup(); } + + [Test] + public void CanInitializeUsingJobPacket() + { + var apiKey = Config.Get("coinapi-api-key"); + Config.Set("coinapi-api-key", ""); + + var job = new LiveNodePacket + { + BrokerageData = new Dictionary() { + { "coinapi-api-key", "InvalidApiKeyThatWontBeUsed" }, + { "coinapi-product", "Startup" } + } + }; + + using var iexDataProvider = new CoinApiDataProvider(); + + // Throw because CoinApiSymbolMapper makes request to API (we have invalid api key in LiveNodePacket) + Assert.Throws(() => + { + iexDataProvider.SetJob(job); + }); + + // revert Config of ApiKey for another tests + Config.Set("coinapi-api-key", apiKey); + } } } diff --git a/QuantConnect.CoinAPI.Tests/CoinApiDataQueueHandlerTest.cs b/QuantConnect.CoinAPI.Tests/CoinApiDataProviderTests.cs similarity index 90% rename from QuantConnect.CoinAPI.Tests/CoinApiDataQueueHandlerTest.cs rename to QuantConnect.CoinAPI.Tests/CoinApiDataProviderTests.cs index c63d45a..692520e 100644 --- a/QuantConnect.CoinAPI.Tests/CoinApiDataQueueHandlerTest.cs +++ b/QuantConnect.CoinAPI.Tests/CoinApiDataProviderTests.cs @@ -21,12 +21,12 @@ using QuantConnect.Data.Market; using System.Collections.Concurrent; -namespace QuantConnect.CoinAPI.Tests +namespace QuantConnect.Lean.DataSource.CoinAPI.Tests { [TestFixture] - public class CoinApiDataQueueHandlerTest + public class CoinApiDataProviderTests { - private CoinApiDataQueueHandler _coinApiDataQueueHandler; + private CoinApiDataProvider _coinApiDataQueueHandler; private CancellationTokenSource _cancellationTokenSource; [SetUp] @@ -61,12 +61,12 @@ public void SubscribeToBTCUSDSecondOnCoinbaseDataStreamTest() _cancellationTokenSource.Token, tick => { - Log.Debug($"{nameof(CoinApiDataQueueHandlerTest)}.{nameof(SubscribeToBTCUSDSecondOnCoinbaseDataStreamTest)}: {tick}"); + Log.Debug($"{nameof(CoinApiDataProviderTests)}.{nameof(SubscribeToBTCUSDSecondOnCoinbaseDataStreamTest)}: {tick}"); tradeBars.Add(tick); if (tradeBars.Count > 5) { - resetEvent.Set(); + resetEvent.Set(); } }, () => _cancellationTokenSource.Cancel()); @@ -111,7 +111,7 @@ public void SubscribeToBTCUSDSecondOnDifferentMarkets() _cancellationTokenSource.Token, tick => { - Log.Debug($"{nameof(CoinApiDataQueueHandlerTest)}.{nameof(SubscribeToBTCUSDSecondOnDifferentMarkets)}: {tick}"); + Log.Debug($"{nameof(CoinApiDataProviderTests)}.{nameof(SubscribeToBTCUSDSecondOnDifferentMarkets)}: {tick}"); symbolBaseData[tick.Symbol].Add(tick); }, () => @@ -165,7 +165,7 @@ public void SubscribeToBTCUSDTFutureSecondBinance() _cancellationTokenSource.Token, tick => { - Log.Debug($"{nameof(CoinApiDataQueueHandlerTest)}.{nameof(SubscribeToBTCUSDTFutureSecondBinance)}: {tick}"); + Log.Debug($"{nameof(CoinApiDataProviderTests)}.{nameof(SubscribeToBTCUSDTFutureSecondBinance)}: {tick}"); tickData.Add(tick); if (tickData.Count > 5) @@ -190,7 +190,7 @@ public void SubscribeToBTCUSDTFutureSecondBinance() if (tickData.Count == 0) { - Assert.Fail($"{nameof(CoinApiDataQueueHandlerTest)}.{nameof(SubscribeToBTCUSDTFutureSecondBinance)} is nothing returned. {symbol}|{resolution}|tickData = {tickData.Count}"); + Assert.Fail($"{nameof(CoinApiDataProviderTests)}.{nameof(SubscribeToBTCUSDTFutureSecondBinance)} is nothing returned. {symbol}|{resolution}|tickData = {tickData.Count}"); } CoinApiTestHelper.AssertSymbol(tickData.First().Symbol, symbol); diff --git a/QuantConnect.CoinAPI.Tests/CoinApiTestHelper.cs b/QuantConnect.CoinAPI.Tests/CoinApiTestHelper.cs index 849a1c8..5d77a43 100644 --- a/QuantConnect.CoinAPI.Tests/CoinApiTestHelper.cs +++ b/QuantConnect.CoinAPI.Tests/CoinApiTestHelper.cs @@ -19,7 +19,7 @@ using QuantConnect.Logging; using QuantConnect.Data.Market; -namespace QuantConnect.CoinAPI.Tests +namespace QuantConnect.Lean.DataSource.CoinAPI.Tests { public static class CoinApiTestHelper { @@ -68,7 +68,7 @@ public static void AssertBaseData(List tradeBars, Resolution expectedR Assert.IsTrue(trade.Period.ToHigherResolutionEquivalent(true) == expectedResolution); break; default: - Assert.Fail($"{nameof(CoinApiDataQueueHandlerTest)}.{nameof(AssertBaseData)}: The tick type doesn't support"); + Assert.Fail($"{nameof(CoinApiDataProviderTests)}.{nameof(AssertBaseData)}: The tick type doesn't support"); break; } } diff --git a/QuantConnect.CoinAPI.Tests/QuantConnect.CoinAPI.Tests.csproj b/QuantConnect.CoinAPI.Tests/QuantConnect.DataSource.CoinAPI.Tests.csproj similarity index 76% rename from QuantConnect.CoinAPI.Tests/QuantConnect.CoinAPI.Tests.csproj rename to QuantConnect.CoinAPI.Tests/QuantConnect.DataSource.CoinAPI.Tests.csproj index d73485b..06c29fd 100644 --- a/QuantConnect.CoinAPI.Tests/QuantConnect.CoinAPI.Tests.csproj +++ b/QuantConnect.CoinAPI.Tests/QuantConnect.DataSource.CoinAPI.Tests.csproj @@ -5,10 +5,10 @@ AnyCPU net6.0 bin\$(Configuration)\ - QuantConnect.CoinAPI.Tests - QuantConnect.CoinAPI.Tests - QuantConnect.CoinAPI.Tests - QuantConnect.CoinAPI.Tests + QuantConnect.Lean.DataSource.CoinAPI.Tests + QuantConnect.Lean.DataSource.CoinAPI.Tests + QuantConnect.Lean.DataSource.CoinAPI.Tests + QuantConnect.Lean.DataSource.CoinAPI.Tests false enable enable @@ -30,7 +30,8 @@ - + + diff --git a/QuantConnect.CoinAPI.Tests/TestSetup.cs b/QuantConnect.CoinAPI.Tests/TestSetup.cs index 05bf2fb..b227107 100644 --- a/QuantConnect.CoinAPI.Tests/TestSetup.cs +++ b/QuantConnect.CoinAPI.Tests/TestSetup.cs @@ -19,7 +19,7 @@ using QuantConnect.Logging; using QuantConnect.Configuration; -namespace QuantConnect.CoinAPI.Tests +namespace QuantConnect.Lean.DataSource.CoinAPI.Tests { [SetUpFixture] public static class TestSetup diff --git a/QuantConnect.CoinAPI/CoinAPIDataDownloader.cs b/QuantConnect.CoinAPI/CoinAPIDataDownloader.cs index 5f9e336..de55552 100644 --- a/QuantConnect.CoinAPI/CoinAPIDataDownloader.cs +++ b/QuantConnect.CoinAPI/CoinAPIDataDownloader.cs @@ -19,38 +19,26 @@ using QuantConnect.Securities; using QuantConnect.Data.Market; -namespace QuantConnect.CoinAPI +namespace QuantConnect.Lean.DataSource.CoinAPI { public class CoinAPIDataDownloader : IDataDownloader, IDisposable { - private readonly CoinApiDataQueueHandler _historyProvider; + private readonly CoinApiDataProvider _historyProvider; private readonly MarketHoursDatabase _marketHoursDatabase; public CoinAPIDataDownloader() { - _historyProvider = new CoinApiDataQueueHandler(); + _historyProvider = new CoinApiDataProvider(); _marketHoursDatabase = MarketHoursDatabase.FromDataFolder(); } - public IEnumerable Get(DataDownloaderGetParameters dataDownloaderGetParameters) + public IEnumerable? Get(DataDownloaderGetParameters dataDownloaderGetParameters) { - if (dataDownloaderGetParameters.TickType != TickType.Trade) - { - Log.Error($"{nameof(CoinAPIDataDownloader)}.{nameof(Get)}: Not supported data type - {dataDownloaderGetParameters.TickType}. " + - $"Currently available support only for historical of type - {nameof(TickType.Trade)}"); - yield break; - } - - if (dataDownloaderGetParameters.EndUtc < dataDownloaderGetParameters.StartUtc) - { - Log.Error($"{nameof(CoinAPIDataDownloader)}.{nameof(Get)}:InvalidDateRange. The history request start date must precede the end date, no history returned"); - yield break; - } - var symbol = dataDownloaderGetParameters.Symbol; - var historyRequests = new HistoryRequest( + var history = _historyProvider.GetHistory( + new HistoryRequest( startTimeUtc: dataDownloaderGetParameters.StartUtc, endTimeUtc: dataDownloaderGetParameters.EndUtc, dataType: typeof(TradeBar), @@ -62,12 +50,16 @@ public IEnumerable Get(DataDownloaderGetParameters dataDownloaderGetPa includeExtendedMarketHours: true, isCustomData: false, dataNormalizationMode: DataNormalizationMode.Raw, - tickType: TickType.Trade); + tickType: TickType.Trade) + ); - foreach (var slice in _historyProvider.GetHistory(historyRequests)) + // historyRequest contains wrong data request + if (history == null) { - yield return slice; + return null; } + + return history.Select(slice => slice); } public void Dispose() diff --git a/QuantConnect.CoinAPI/CoinApi.HistoryProvider.cs b/QuantConnect.CoinAPI/CoinApiDataHistoryProvider.cs similarity index 62% rename from QuantConnect.CoinAPI/CoinApi.HistoryProvider.cs rename to QuantConnect.CoinAPI/CoinApiDataHistoryProvider.cs index 6f067ff..55c379b 100644 --- a/QuantConnect.CoinAPI/CoinApi.HistoryProvider.cs +++ b/QuantConnect.CoinAPI/CoinApiDataHistoryProvider.cs @@ -19,14 +19,14 @@ using QuantConnect.Data; using QuantConnect.Logging; using QuantConnect.Data.Market; -using QuantConnect.CoinAPI.Messages; +using QuantConnect.Lean.DataSource.CoinAPI.Messages; using QuantConnect.Lean.Engine.DataFeeds; using HistoryRequest = QuantConnect.Data.HistoryRequest; -using QuantConnect.CoinAPI.Models; +using QuantConnect.Lean.DataSource.CoinAPI.Models; -namespace QuantConnect.CoinAPI +namespace QuantConnect.Lean.DataSource.CoinAPI { - public partial class CoinApiDataQueueHandler + public partial class CoinApiDataProvider { private readonly RestClient restClient = new RestClient(); @@ -37,50 +37,103 @@ public partial class CoinApiDataQueueHandler /// private bool _invalidHistoryDataTypeWarningFired; + /// + /// Indicates whether the warning for invalid has been fired. + /// + private bool _invalidSecurityTypeWarningFired; + + /// + /// Indicates whether the warning for invalid has been fired. + /// + private bool _invalidResolutionTypeWarningFired; + + /// + /// Indicates whether a warning for an invalid start time has been fired, where the start time is greater than or equal to the end time in UTC. + /// + private bool _invalidStartTimeWarningFired; + public override void Initialize(HistoryProviderInitializeParameters parameters) { // NOP } - public override IEnumerable GetHistory(IEnumerable requests, DateTimeZone sliceTimeZone) + public override IEnumerable? GetHistory(IEnumerable requests, DateTimeZone sliceTimeZone) { var subscriptions = new List(); foreach (var request in requests) { var history = GetHistory(request); + + if (history == null) + { + continue; + } + var subscription = CreateSubscription(request, history); subscriptions.Add(subscription); } + + if (subscriptions.Count == 0) + { + return null; + } return CreateSliceEnumerableFromSubscriptions(subscriptions, sliceTimeZone); } - public IEnumerable GetHistory(HistoryRequest historyRequest) + public IEnumerable? GetHistory(HistoryRequest historyRequest) { - if (historyRequest.Symbol.SecurityType != SecurityType.Crypto && historyRequest.Symbol.SecurityType != SecurityType.CryptoFuture) + if (!CanSubscribe(historyRequest.Symbol)) { - Log.Error($"CoinApiDataQueueHandler.GetHistory(): Invalid security type {historyRequest.Symbol.SecurityType}"); - yield break; + if (!_invalidSecurityTypeWarningFired) + { + Log.Error($"CoinApiDataProvider.GetHistory(): Invalid security type {historyRequest.Symbol.SecurityType}"); + _invalidSecurityTypeWarningFired = true; + } + return null; } if (historyRequest.Resolution == Resolution.Tick) { - Log.Error($"CoinApiDataQueueHandler.GetHistory(): No historical ticks, only OHLCV timeseries"); - yield break; + if (!_invalidResolutionTypeWarningFired) + { + Log.Error($"CoinApiDataProvider.GetHistory(): No historical ticks, only OHLCV timeseries"); + _invalidResolutionTypeWarningFired = true; + } + return null; } if (historyRequest.DataType == typeof(QuoteBar)) { if (!_invalidHistoryDataTypeWarningFired) { - Log.Error("CoinApiDataQueueHandler.GetHistory(): No historical QuoteBars , only TradeBars"); + Log.Error("CoinApiDataProvider.GetHistory(): No historical QuoteBars , only TradeBars"); _invalidHistoryDataTypeWarningFired = true; } - yield break; + return null; + } + + if (historyRequest.EndTimeUtc < historyRequest.StartTimeUtc) + { + if (!_invalidStartTimeWarningFired) + { + Log.Error($"{nameof(CoinAPIDataDownloader)}.{nameof(GetHistory)}:InvalidDateRange. The history request start date must precede the end date, no history returned"); + _invalidStartTimeWarningFired = true; + } + return null; } - var resolutionTimeSpan = historyRequest.Resolution.ToTimeSpan(); - var lastRequestedBarStartTime = historyRequest.EndTimeUtc.RoundDown(resolutionTimeSpan); - var currentStartTime = historyRequest.StartTimeUtc.RoundUp(resolutionTimeSpan); + return GetHistory(historyRequest.Symbol, + historyRequest.Resolution, + historyRequest.StartTimeUtc, + historyRequest.EndTimeUtc + ); + } + + private IEnumerable GetHistory(Symbol symbol, Resolution resolution, DateTime startDateTimeUtc, DateTime endDateTimeUtc) + { + var resolutionTimeSpan = resolution.ToTimeSpan(); + var lastRequestedBarStartTime = endDateTimeUtc.RoundDown(resolutionTimeSpan); + var currentStartTime = startDateTimeUtc.RoundUp(resolutionTimeSpan); var currentEndTime = lastRequestedBarStartTime; // Perform a check of the number of bars requested, this must not exceed a static limit @@ -95,8 +148,8 @@ public IEnumerable GetHistory(HistoryRequest historyRequest) while (currentStartTime < lastRequestedBarStartTime) { - var coinApiSymbol = _symbolMapper.GetBrokerageSymbol(historyRequest.Symbol); - var coinApiPeriod = _ResolutionToCoinApiPeriodMappings[historyRequest.Resolution]; + var coinApiSymbol = _symbolMapper.GetBrokerageSymbol(symbol); + var coinApiPeriod = _ResolutionToCoinApiPeriodMappings[resolution]; // Time must be in ISO 8601 format var coinApiStartTime = currentStartTime.ToStringInvariant("s"); @@ -128,15 +181,15 @@ public IEnumerable GetHistory(HistoryRequest historyRequest) // Can be no historical data for a short period interval if (!coinApiHistoryBars.Any()) { - Log.Error($"CoinApiDataQueueHandler.GetHistory(): API returned no data for the requested period [{coinApiStartTime} - {coinApiEndTime}] for symbol [{historyRequest.Symbol}]"); + Log.Error($"CoinApiDataProvider.GetHistory(): API returned no data for the requested period [{coinApiStartTime} - {coinApiEndTime}] for symbol [{symbol}]"); continue; } foreach (var ohlcv in coinApiHistoryBars) { yield return - new TradeBar(ohlcv.TimePeriodStart, historyRequest.Symbol, ohlcv.PriceOpen, ohlcv.PriceHigh, - ohlcv.PriceLow, ohlcv.PriceClose, ohlcv.VolumeTraded, historyRequest.Resolution.ToTimeSpan()); + new TradeBar(ohlcv.TimePeriodStart, symbol, ohlcv.PriceOpen, ohlcv.PriceHigh, + ohlcv.PriceLow, ohlcv.PriceClose, ohlcv.VolumeTraded, resolutionTimeSpan); } currentStartTime = currentEndTime; @@ -150,7 +203,7 @@ private void TraceRestUsage(IRestResponse response) var used = GetHttpHeaderValue(response, "x-ratelimit-used"); var remaining = GetHttpHeaderValue(response, "x-ratelimit-remaining"); - Log.Trace($"CoinApiDataQueueHandler.TraceRestUsage(): Used {used}, Remaining {remaining}, Total {total}"); + Log.Trace($"CoinApiDataProvider.TraceRestUsage(): Used {used}, Remaining {remaining}, Total {total}"); } private string GetHttpHeaderValue(IRestResponse response, string propertyName) diff --git a/QuantConnect.CoinAPI/CoinApiDataQueueHandler.cs b/QuantConnect.CoinAPI/CoinApiDataProvider.cs similarity index 83% rename from QuantConnect.CoinAPI/CoinApiDataQueueHandler.cs rename to QuantConnect.CoinAPI/CoinApiDataProvider.cs index a2e612b..2791b13 100644 --- a/QuantConnect.CoinAPI/CoinApiDataQueueHandler.cs +++ b/QuantConnect.CoinAPI/CoinApiDataProvider.cs @@ -33,12 +33,12 @@ using CoinAPI.WebSocket.V1.DataModels; using QuantConnect.Lean.Engine.HistoricalData; -namespace QuantConnect.CoinAPI +namespace QuantConnect.Lean.DataSource.CoinAPI { /// /// An implementation of for CoinAPI /// - public partial class CoinApiDataQueueHandler : SynchronizingHistoryProvider, IDataQueueHandler + public partial class CoinApiDataProvider : SynchronizingHistoryProvider, IDataQueueHandler { protected int HistoricalDataPerRequestLimit = 10000; private static readonly Dictionary _ResolutionToCoinApiPeriodMappings = new Dictionary @@ -49,14 +49,14 @@ public partial class CoinApiDataQueueHandler : SynchronizingHistoryProvider, IDa { Resolution.Daily, "1DAY" }, }; - private readonly string _apiKey = Config.Get("coinapi-api-key"); - private readonly string[] _streamingDataType; - private readonly CoinApiWsClient _client; + private string? _apiKey; + private string[]? _streamingDataType; + private CoinApiWsClient? _client; private readonly object _locker = new object(); private ConcurrentDictionary _symbolCache = new ConcurrentDictionary(); - private readonly CoinApiSymbolMapper _symbolMapper = new CoinApiSymbolMapper(); - private readonly IDataAggregator _dataAggregator; - private readonly EventBasedDataQueueHandlerSubscriptionManager _subscriptionManager; + private CoinApiSymbolMapper? _symbolMapper; + private IDataAggregator? _dataAggregator; + private EventBasedDataQueueHandlerSubscriptionManager? _subscriptionManager; private readonly TimeSpan _subscribeDelay = TimeSpan.FromMilliseconds(250); private readonly object _lockerSubscriptions = new object(); @@ -69,32 +69,28 @@ public partial class CoinApiDataQueueHandler : SynchronizingHistoryProvider, IDa private readonly ConcurrentDictionary _previousQuotes = new ConcurrentDictionary(); /// - /// Initializes a new instance of the class + /// /// - public CoinApiDataQueueHandler() + private bool _initialized; + + /// + /// Initializes a new instance of the class + /// + public CoinApiDataProvider() { - _dataAggregator = Composer.Instance.GetPart(); - if (_dataAggregator == null) + if (!Config.TryGetValue("coinapi-api-key", out var configApiKey) || string.IsNullOrEmpty(configApiKey)) { - _dataAggregator = - Composer.Instance.GetExportedValueByTypeName(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"), forceTypeNameOnExisting: false); + // If the API key is not provided, we can't do anything. + // The handler might going to be initialized using a node packet job. + return; } - var product = Config.GetValue("coinapi-product"); - _streamingDataType = product < CoinApiProduct.Streamer - ? new[] { "trade" } - : new[] { "trade", "quote" }; - - Log.Trace($"{nameof(CoinApiDataQueueHandler)}: using plan '{product}'. Available data types: '{string.Join(",", _streamingDataType)}'"); - ValidateSubscription(); + if (!Config.TryGetValue("coinapi-product", out var product) || string.IsNullOrEmpty(product)) + { + product = "Free"; + } - _client = new CoinApiWsClient(); - _client.TradeEvent += OnTrade; - _client.QuoteEvent += OnQuote; - _client.Error += OnError; - _subscriptionManager = new EventBasedDataQueueHandlerSubscriptionManager(); - _subscriptionManager.SubscribeImpl += (s, t) => Subscribe(s); - _subscriptionManager.UnsubscribeImpl += (s, t) => Unsubscribe(s); + Initialize(configApiKey, product); } /// @@ -110,6 +106,11 @@ public IEnumerator Subscribe(SubscriptionDataConfig dataConfig, EventH return null; } + if (_dataAggregator == null || _subscriptionManager == null) + { + throw new InvalidOperationException($"{nameof(CoinApiDataProvider)}.{nameof(Subscribe)}: {nameof(_dataAggregator)} or {nameof(_subscriptionManager)} is not initialized."); + } + var enumerator = _dataAggregator.Add(dataConfig, newDataAvailableHandler); _subscriptionManager.Subscribe(dataConfig); @@ -122,6 +123,63 @@ public IEnumerator Subscribe(SubscriptionDataConfig dataConfig, EventH /// Job we're subscribing for public void SetJob(LiveNodePacket job) { + if (_initialized) + { + return; + } + + if (!job.BrokerageData.TryGetValue("coinapi-api-key", out var apiKey) || string.IsNullOrEmpty(apiKey)) + { + throw new ArgumentException("Invalid or missing Coin API key. Please ensure that the API key is set and not empty."); + } + + if (!job.BrokerageData.TryGetValue("coinapi-product", out var product) || string.IsNullOrEmpty(product)) + { + product = "Free"; + } + + Initialize(apiKey, product); + } + + /// + /// + /// + /// + /// + /// + private void Initialize(string apiKey, string product) + { + ValidateSubscription(); + + _apiKey = apiKey; + + if (!Enum.TryParse(product, true, out var parsedProduct) || !Enum.IsDefined(typeof(CoinApiProduct), parsedProduct)) + { + throw new ArgumentException($"An error occurred while parsing the price plan '{product}'. Please ensure that the provided price plan is valid and supported by the system."); + } + + _dataAggregator = Composer.Instance.GetPart(); + if (_dataAggregator == null) + { + _dataAggregator = + Composer.Instance.GetExportedValueByTypeName(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"), forceTypeNameOnExisting: false); + } + + _streamingDataType = parsedProduct < CoinApiProduct.Streamer + ? new[] { "trade" } + : new[] { "trade", "quote" }; + + Log.Trace($"{nameof(CoinApiDataProvider)}: using plan '{product}'. Available data types: '{string.Join(",", _streamingDataType)}'"); + + _symbolMapper = new CoinApiSymbolMapper(); + _client = new CoinApiWsClient(); + _client.TradeEvent += OnTrade; + _client.QuoteEvent += OnQuote; + _client.Error += OnError; + _subscriptionManager = new EventBasedDataQueueHandlerSubscriptionManager(); + _subscriptionManager.SubscribeImpl += (s, t) => Subscribe(s); + _subscriptionManager.UnsubscribeImpl += (s, t) => Unsubscribe(s); + _initialized = true; } /// @@ -140,8 +198,8 @@ private bool Subscribe(IEnumerable symbols) /// Subscription config to be removed public void Unsubscribe(SubscriptionDataConfig dataConfig) { - _subscriptionManager.Unsubscribe(dataConfig); - _dataAggregator.Remove(dataConfig); + _subscriptionManager?.Unsubscribe(dataConfig); + _dataAggregator?.Remove(dataConfig); } @@ -166,10 +224,13 @@ private bool Unsubscribe(IEnumerable symbols) /// public void Dispose() { - _client.TradeEvent -= OnTrade; - _client.QuoteEvent -= OnQuote; - _client.Error -= OnError; - _client.Dispose(); + if (_client != null) + { + _client.TradeEvent -= OnTrade; + _client.QuoteEvent -= OnQuote; + _client.Error -= OnError; + _client.Dispose(); + } _dataAggregator.DisposeSafely(); } @@ -179,7 +240,7 @@ public void Dispose() /// List of LEAN markets (exchanges) to subscribe public void SubscribeMarkets(List markets) { - Log.Trace($"CoinApiDataQueueHandler.SubscribeMarkets(): {string.Join(",", markets)}"); + Log.Trace($"CoinApiDataProvider.SubscribeMarkets(): {string.Join(",", markets)}"); // we add '_' to be more precise, for example requesting 'BINANCE' doesn't match 'BINANCEUS' SendHelloMessage(markets.Select(x => string.Concat(_symbolMapper.GetExchangeId(x.ToLowerInvariant()), "_"))); @@ -263,7 +324,7 @@ private static bool CanSubscribe(Symbol symbol) /// The list of symbols to subscribe private void SubscribeSymbols(List symbolsToSubscribe) { - Log.Trace($"CoinApiDataQueueHandler.SubscribeSymbols(): {string.Join(",", symbolsToSubscribe)}"); + Log.Trace($"CoinApiDataProvider.SubscribeSymbols(): {string.Join(",", symbolsToSubscribe)}"); // subscribe to symbols using exact match SendHelloMessage(symbolsToSubscribe.Select(x => @@ -292,7 +353,7 @@ private void SendHelloMessage(IEnumerable subscribeFilter) list.Add("$no_symbol_requested$"); } - _client.SendHelloMessage(new Hello + _client?.SendHelloMessage(new Hello { apikey = Guid.Parse(_apiKey), heartbeat = true, @@ -401,9 +462,9 @@ private static void ValidateSubscription() try { const int productId = 335; - var userId = Config.GetInt("job-user-id"); - var token = Config.Get("api-access-token"); - var organizationId = Config.Get("job-organization-id", null); + var userId = Globals.UserId; + var token = Globals.UserToken; + var organizationId = Globals.OrganizationID; // Verify we can authenticate with this user and token var api = new ApiConnection(userId, token); if (!api.Connected) @@ -518,7 +579,7 @@ private static void ValidateSubscription() } catch (Exception e) { - Log.Error($"{nameof(CoinApiDataQueueHandler)}.{nameof(ValidateSubscription)}: Failed during validation, shutting down. Error : {e.Message}"); + Log.Error($"{nameof(CoinApiDataProvider)}.{nameof(ValidateSubscription)}: Failed during validation, shutting down. Error : {e.Message}"); throw; } } diff --git a/QuantConnect.CoinAPI/CoinApiProduct.cs b/QuantConnect.CoinAPI/CoinApiProduct.cs index 1055342..4316f74 100644 --- a/QuantConnect.CoinAPI/CoinApiProduct.cs +++ b/QuantConnect.CoinAPI/CoinApiProduct.cs @@ -13,7 +13,7 @@ * limitations under the License. */ -namespace QuantConnect.CoinAPI +namespace QuantConnect.Lean.DataSource.CoinAPI { /// /// Coin API's available tariff plans (or products). diff --git a/QuantConnect.CoinAPI/CoinApiSymbol.cs b/QuantConnect.CoinAPI/CoinApiSymbol.cs index 0f56083..02b8017 100644 --- a/QuantConnect.CoinAPI/CoinApiSymbol.cs +++ b/QuantConnect.CoinAPI/CoinApiSymbol.cs @@ -15,7 +15,7 @@ using Newtonsoft.Json; -namespace QuantConnect.CoinAPI +namespace QuantConnect.Lean.DataSource.CoinAPI { public class CoinApiSymbol { diff --git a/QuantConnect.CoinAPI/CoinApiSymbolMapper.cs b/QuantConnect.CoinAPI/CoinApiSymbolMapper.cs index 8a3d94e..40b60e1 100644 --- a/QuantConnect.CoinAPI/CoinApiSymbolMapper.cs +++ b/QuantConnect.CoinAPI/CoinApiSymbolMapper.cs @@ -18,9 +18,9 @@ using QuantConnect.Securities; using QuantConnect.Brokerages; using QuantConnect.Configuration; -using QuantConnect.CoinAPI.Models; +using QuantConnect.Lean.DataSource.CoinAPI.Models; -namespace QuantConnect.CoinAPI +namespace QuantConnect.Lean.DataSource.CoinAPI { /// /// Provides the mapping between Lean symbols and CoinAPI symbols. diff --git a/QuantConnect.CoinAPI/Messages/BaseMessage.cs b/QuantConnect.CoinAPI/Messages/BaseMessage.cs index a168252..bcbd138 100644 --- a/QuantConnect.CoinAPI/Messages/BaseMessage.cs +++ b/QuantConnect.CoinAPI/Messages/BaseMessage.cs @@ -15,7 +15,7 @@ using Newtonsoft.Json; -namespace QuantConnect.CoinAPI.Messages +namespace QuantConnect.Lean.DataSource.CoinAPI.Messages { public class BaseMessage { diff --git a/QuantConnect.CoinAPI/Messages/ErrorMessage.cs b/QuantConnect.CoinAPI/Messages/ErrorMessage.cs index 924c972..a62dcfc 100644 --- a/QuantConnect.CoinAPI/Messages/ErrorMessage.cs +++ b/QuantConnect.CoinAPI/Messages/ErrorMessage.cs @@ -15,7 +15,7 @@ using Newtonsoft.Json; -namespace QuantConnect.CoinAPI.Messages +namespace QuantConnect.Lean.DataSource.CoinAPI.Messages { public class ErrorMessage : BaseMessage { diff --git a/QuantConnect.CoinAPI/Messages/HelloMessage.cs b/QuantConnect.CoinAPI/Messages/HelloMessage.cs index cd79290..c69d554 100644 --- a/QuantConnect.CoinAPI/Messages/HelloMessage.cs +++ b/QuantConnect.CoinAPI/Messages/HelloMessage.cs @@ -15,7 +15,7 @@ using Newtonsoft.Json; -namespace QuantConnect.CoinAPI.Messages +namespace QuantConnect.Lean.DataSource.CoinAPI.Messages { public class HelloMessage { diff --git a/QuantConnect.CoinAPI/Messages/HistoricalDataMessage.cs b/QuantConnect.CoinAPI/Messages/HistoricalDataMessage.cs index ce5a83a..f612705 100644 --- a/QuantConnect.CoinAPI/Messages/HistoricalDataMessage.cs +++ b/QuantConnect.CoinAPI/Messages/HistoricalDataMessage.cs @@ -15,7 +15,7 @@ using Newtonsoft.Json; -namespace QuantConnect.CoinAPI.Messages +namespace QuantConnect.Lean.DataSource.CoinAPI.Messages { public class HistoricalDataMessage { diff --git a/QuantConnect.CoinAPI/Messages/QuoteMessage.cs b/QuantConnect.CoinAPI/Messages/QuoteMessage.cs index 59c273c..7350fe4 100644 --- a/QuantConnect.CoinAPI/Messages/QuoteMessage.cs +++ b/QuantConnect.CoinAPI/Messages/QuoteMessage.cs @@ -15,7 +15,7 @@ using Newtonsoft.Json; -namespace QuantConnect.CoinAPI.Messages +namespace QuantConnect.Lean.DataSource.CoinAPI.Messages { public class QuoteMessage : BaseMessage { diff --git a/QuantConnect.CoinAPI/Messages/TradeMessage.cs b/QuantConnect.CoinAPI/Messages/TradeMessage.cs index cfa6b31..2ef1ae3 100644 --- a/QuantConnect.CoinAPI/Messages/TradeMessage.cs +++ b/QuantConnect.CoinAPI/Messages/TradeMessage.cs @@ -15,7 +15,7 @@ using Newtonsoft.Json; -namespace QuantConnect.CoinAPI.Messages +namespace QuantConnect.Lean.DataSource.CoinAPI.Messages { public class TradeMessage : BaseMessage { diff --git a/QuantConnect.CoinAPI/Models/CoinApiErrorResponse.cs b/QuantConnect.CoinAPI/Models/CoinApiErrorResponse.cs index 56133b1..3e7553b 100644 --- a/QuantConnect.CoinAPI/Models/CoinApiErrorResponse.cs +++ b/QuantConnect.CoinAPI/Models/CoinApiErrorResponse.cs @@ -15,7 +15,7 @@ using Newtonsoft.Json; -namespace QuantConnect.CoinAPI.Models +namespace QuantConnect.Lean.DataSource.CoinAPI.Models { public readonly struct CoinApiErrorResponse { diff --git a/QuantConnect.CoinAPI/QuantConnect.CoinAPI.csproj b/QuantConnect.CoinAPI/QuantConnect.DataSource.CoinAPI.csproj similarity index 83% rename from QuantConnect.CoinAPI/QuantConnect.CoinAPI.csproj rename to QuantConnect.CoinAPI/QuantConnect.DataSource.CoinAPI.csproj index 3096705..410707d 100644 --- a/QuantConnect.CoinAPI/QuantConnect.CoinAPI.csproj +++ b/QuantConnect.CoinAPI/QuantConnect.DataSource.CoinAPI.csproj @@ -4,10 +4,10 @@ Release AnyCPU net6.0 - QuantConnect.CoinAPI - QuantConnect.CoinAPI - QuantConnect.CoinAPI - QuantConnect.CoinAPI + QuantConnect.Lean.DataSource.CoinAPI + QuantConnect.Lean.DataSource.CoinAPI + QuantConnect.Lean.DataSource.CoinAPI + QuantConnect.Lean.DataSource.CoinAPI Library bin\$(Configuration) false