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