mirror of
https://github.com/JKorf/CryptoExchange.Net.git
synced 2026-08-14 01:42:55 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 38a7b981ce | |||
| c41cc3c4c7 | |||
| 4accc8039b | |||
| 87c86ec0c0 | |||
| d9850da282 | |||
| c4a8b02054 |
@@ -15,6 +15,7 @@ namespace CryptoExchange.Net.UnitTests.ConverterTests
|
|||||||
[TestCase("1620777600000")]
|
[TestCase("1620777600000")]
|
||||||
[TestCase("2021-05-12T00:00:00.000Z")]
|
[TestCase("2021-05-12T00:00:00.000Z")]
|
||||||
[TestCase("2021-05-12T00:00:00.000000000Z")]
|
[TestCase("2021-05-12T00:00:00.000000000Z")]
|
||||||
|
[TestCase("2021-05-12 00:00:00.000000+00:00:00")]
|
||||||
[TestCase("0.000000", true)]
|
[TestCase("0.000000", true)]
|
||||||
[TestCase("0", true)]
|
[TestCase("0", true)]
|
||||||
[TestCase("", true)]
|
[TestCase("", true)]
|
||||||
|
|||||||
@@ -198,6 +198,9 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
return new DateTime(year, month, day, 0, 0, 0, DateTimeKind.Utc);
|
return new DateTime(year, month, day, 0, 0, 0, DateTimeKind.Utc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stringValue.EndsWith("+00:00:00"))
|
||||||
|
return DateTime.Parse(stringValue.Substring(0, stringValue.Length - 9), CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal);
|
||||||
|
|
||||||
return DateTime.Parse(stringValue, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal);
|
return DateTime.Parse(stringValue, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
<PackageId>CryptoExchange.Net</PackageId>
|
<PackageId>CryptoExchange.Net</PackageId>
|
||||||
<Authors>JKorf</Authors>
|
<Authors>JKorf</Authors>
|
||||||
<Description>CryptoExchange.Net is a base library which is used to implement different cryptocurrency (exchange) API's. It provides a standardized way of implementing different API's, which results in a very similar experience for users of the API implementations.</Description>
|
<Description>CryptoExchange.Net is a base library which is used to implement different cryptocurrency (exchange) API's. It provides a standardized way of implementing different API's, which results in a very similar experience for users of the API implementations.</Description>
|
||||||
<PackageVersion>12.0.2</PackageVersion>
|
<PackageVersion>12.1.1</PackageVersion>
|
||||||
<AssemblyVersion>12.0.2</AssemblyVersion>
|
<AssemblyVersion>12.1.1</AssemblyVersion>
|
||||||
<FileVersion>12.0.2</FileVersion>
|
<FileVersion>12.1.1</FileVersion>
|
||||||
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
||||||
<PackageTags>OKX;OKX.Net;Mexc;Mexc.Net;Kucoin;Kucoin.Net;Kraken;Kraken.Net;Huobi;Huobi.Net;CoinEx;CoinEx.Net;Bybit;Bybit.Net;Bitget;Bitget.Net;Bitfinex;Bitfinex.Net;Binance;Binance.Net;CryptoCurrency;CryptoCurrency Exchange;CryptoExchange.Net</PackageTags>
|
<PackageTags>OKX;OKX.Net;Mexc;Mexc.Net;Kucoin;Kucoin.Net;Kraken;Kraken.Net;Huobi;Huobi.Net;CoinEx;CoinEx.Net;Bybit;Bybit.Net;Bitget;Bitget.Net;Bitfinex;Bitfinex.Net;Binance;Binance.Net;CryptoCurrency;CryptoCurrency Exchange;CryptoExchange.Net</PackageTags>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
|
|||||||
@@ -24,8 +24,9 @@ namespace CryptoExchange.Net.Interfaces
|
|||||||
/// <param name="interval">Kline interval</param>
|
/// <param name="interval">Kline interval</param>
|
||||||
/// <param name="limit">The max amount of klines to retain</param>
|
/// <param name="limit">The max amount of klines to retain</param>
|
||||||
/// <param name="period">The max period the data should be retained</param>
|
/// <param name="period">The max period the data should be retained</param>
|
||||||
|
/// <param name="exchangeParameters">Exchange parameters</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
IKlineTracker CreateKlineTracker(SharedSymbol symbol, SharedKlineInterval interval, int? limit = null, TimeSpan? period = null);
|
IKlineTracker CreateKlineTracker(SharedSymbol symbol, SharedKlineInterval interval, int? limit = null, TimeSpan? period = null, ExchangeParameters? exchangeParameters = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the factory supports creating a TradeTracker instance for this symbol
|
/// Whether the factory supports creating a TradeTracker instance for this symbol
|
||||||
@@ -39,7 +40,8 @@ namespace CryptoExchange.Net.Interfaces
|
|||||||
/// <param name="symbol">The symbol</param>
|
/// <param name="symbol">The symbol</param>
|
||||||
/// <param name="limit">The max amount of trades to retain</param>
|
/// <param name="limit">The max amount of trades to retain</param>
|
||||||
/// <param name="period">The max period the data should be retained</param>
|
/// <param name="period">The max period the data should be retained</param>
|
||||||
|
/// <param name="exchangeParameters">Exchange parameters</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
ITradeTracker CreateTradeTracker(SharedSymbol symbol, int? limit = null, TimeSpan? period = null);
|
ITradeTracker CreateTradeTracker(SharedSymbol symbol, int? limit = null, TimeSpan? period = null, ExchangeParameters? exchangeParameters = null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// Add key as comma separated values
|
/// Add key as comma separated values
|
||||||
/// </summary>
|
/// </summary>
|
||||||
#if NET5_0_OR_GREATER
|
#if NET5_0_OR_GREATER
|
||||||
public void AddCommaSeparated<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] T>(string key, IEnumerable<T> values)
|
public void AddCommaSeparated<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] T>(string key, IEnumerable<T>? values)
|
||||||
#else
|
#else
|
||||||
public void AddCommaSeparated<T>(string key, IEnumerable<T>? values)
|
public void AddCommaSeparated<T>(string key, IEnumerable<T>? values)
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -63,7 +63,10 @@ namespace CryptoExchange.Net.Testing.Comparers
|
|||||||
foreach (var dictProp in jObj.EnumerateObject())
|
foreach (var dictProp in jObj.EnumerateObject())
|
||||||
{
|
{
|
||||||
if (!dict.Contains(dictProp.Name))
|
if (!dict.Contains(dictProp.Name))
|
||||||
|
{
|
||||||
outputExceptions.Add(new Exception($"{method}: Dictionary has no value for {dictProp.Name} while input json `{dictProp.Name}` has value {dictProp.Value}"));
|
outputExceptions.Add(new Exception($"{method}: Dictionary has no value for {dictProp.Name} while input json `{dictProp.Name}` has value {dictProp.Value}"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (dictProp.Value.ValueKind == JsonValueKind.Object)
|
if (dictProp.Value.ValueKind == JsonValueKind.Object)
|
||||||
{
|
{
|
||||||
@@ -80,6 +83,7 @@ namespace CryptoExchange.Net.Testing.Comparers
|
|||||||
|
|
||||||
// Property value not correct
|
// Property value not correct
|
||||||
outputExceptions.Add(new Exception($"{method}: Dictionary entry `{dictProp.Name}` has no value while input json has value {dictProp.Value}"));
|
outputExceptions.Add(new Exception($"{method}: Dictionary entry `{dictProp.Name}` has no value while input json has value {dictProp.Value}"));
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,7 +135,10 @@ namespace CryptoExchange.Net.Testing.Comparers
|
|||||||
{
|
{
|
||||||
var value = enumerator.Current;
|
var value = enumerator.Current;
|
||||||
if (value == default && jObj.ValueKind != JsonValueKind.Null)
|
if (value == default && jObj.ValueKind != JsonValueKind.Null)
|
||||||
|
{
|
||||||
outputExceptions.Add(new Exception($"{method}: Array has no value while input json array has value {jObj}"));
|
outputExceptions.Add(new Exception($"{method}: Array has no value while input json array has value {jObj}"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,7 +174,7 @@ namespace CryptoExchange.Net.Testing.Comparers
|
|||||||
}
|
}
|
||||||
|
|
||||||
Debug.WriteLine($"Successfully validated {method}");
|
Debug.WriteLine($"Successfully validated {method}");
|
||||||
return outputExceptions;
|
return outputExceptions.Distinct().ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CheckObject(string method, JsonProperty prop, object obj, List<string>? ignoreProperties, List<Exception> outputExceptions)
|
private static void CheckObject(string method, JsonProperty prop, object obj, List<string>? ignoreProperties, List<Exception> outputExceptions)
|
||||||
@@ -225,7 +232,10 @@ namespace CryptoExchange.Net.Testing.Comparers
|
|||||||
|
|
||||||
// Property value not correct
|
// Property value not correct
|
||||||
if (propValue.ToString() != "0")
|
if (propValue.ToString() != "0")
|
||||||
|
{
|
||||||
outputExceptions.Add(new Exception($"{method}: Property `{propertyName}` has no value while input json `{propName}` has value {propValue}"));
|
outputExceptions.Add(new Exception($"{method}: Property `{propertyName}` has no value while input json `{propName}` has value {propValue}"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((propertyValue == default && (propValue.ValueKind == JsonValueKind.Null || string.IsNullOrEmpty(propValue.ToString()))) || propValue.ToString() == "0")
|
if ((propertyValue == default && (propValue.ValueKind == JsonValueKind.Null || string.IsNullOrEmpty(propValue.ToString()))) || propValue.ToString() == "0")
|
||||||
@@ -237,7 +247,10 @@ namespace CryptoExchange.Net.Testing.Comparers
|
|||||||
foreach (var dictProp in propValue.EnumerateObject())
|
foreach (var dictProp in propValue.EnumerateObject())
|
||||||
{
|
{
|
||||||
if (!dict.Contains(dictProp.Name))
|
if (!dict.Contains(dictProp.Name))
|
||||||
|
{
|
||||||
outputExceptions.Add(new Exception($"{method}: Property `{propertyName}` has no value while input json `{propName}` has value {propValue}"));
|
outputExceptions.Add(new Exception($"{method}: Property `{propertyName}` has no value while input json `{propName}` has value {propValue}"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (dictProp.Value.ValueKind == JsonValueKind.Object)
|
if (dictProp.Value.ValueKind == JsonValueKind.Object)
|
||||||
{
|
{
|
||||||
@@ -246,8 +259,11 @@ namespace CryptoExchange.Net.Testing.Comparers
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (dict[dictProp.Name] == default && dictProp.Value.ValueKind != JsonValueKind.Null)
|
if (dict[dictProp.Name] == default && dictProp.Value.ValueKind != JsonValueKind.Null)
|
||||||
|
{
|
||||||
// Property value not correct
|
// Property value not correct
|
||||||
outputExceptions.Add(new Exception($"{method}: Dictionary entry `{dictProp.Name}` has no value while input json has value {propValue} for"));
|
outputExceptions.Add(new Exception($"{method}: Dictionary entry `{dictProp.Name}` has no value while input json has value {propValue} for"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -304,7 +320,10 @@ namespace CryptoExchange.Net.Testing.Comparers
|
|||||||
{
|
{
|
||||||
var value = enumerator.Current;
|
var value = enumerator.Current;
|
||||||
if (value == default && jToken.ValueKind != JsonValueKind.Null)
|
if (value == default && jToken.ValueKind != JsonValueKind.Null)
|
||||||
|
{
|
||||||
outputExceptions.Add(new Exception($"{method}: Property `{propertyName}` has no value while input json `{propName}` has value {jToken}"));
|
outputExceptions.Add(new Exception($"{method}: Property `{propertyName}` has no value while input json `{propName}` has value {jToken}"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
CheckValues(method, propertyName!, propertyType, jToken, value!, outputExceptions);
|
CheckValues(method, propertyName!, propertyType, jToken, value!, outputExceptions);
|
||||||
}
|
}
|
||||||
@@ -368,7 +387,10 @@ namespace CryptoExchange.Net.Testing.Comparers
|
|||||||
{
|
{
|
||||||
var value = enumerator.Current;
|
var value = enumerator.Current;
|
||||||
if (value == default && jObj.ValueKind != JsonValueKind.Null)
|
if (value == default && jObj.ValueKind != JsonValueKind.Null)
|
||||||
|
{
|
||||||
outputExceptions.Add(new Exception($"{method}: Array has no value while input json array has value {jObj}"));
|
outputExceptions.Add(new Exception($"{method}: Array has no value while input json array has value {jObj}"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ namespace CryptoExchange.Net.Trackers.Klines
|
|||||||
private readonly IKlineRestClient _restClient;
|
private readonly IKlineRestClient _restClient;
|
||||||
private SyncStatus _status;
|
private SyncStatus _status;
|
||||||
private bool _startWithSnapshot;
|
private bool _startWithSnapshot;
|
||||||
|
private ExchangeParameters? _exchangeParameters;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The internal data structure
|
/// The internal data structure
|
||||||
@@ -157,9 +158,11 @@ namespace CryptoExchange.Net.Trackers.Klines
|
|||||||
SharedSymbol symbol,
|
SharedSymbol symbol,
|
||||||
SharedKlineInterval interval,
|
SharedKlineInterval interval,
|
||||||
int? limit = null,
|
int? limit = null,
|
||||||
TimeSpan? period = null)
|
TimeSpan? period = null,
|
||||||
|
ExchangeParameters? exchangeParameters = null)
|
||||||
{
|
{
|
||||||
_logger = logger ?? new NullLogger<KlineTracker>();
|
_logger = logger ?? new NullLogger<KlineTracker>();
|
||||||
|
_exchangeParameters = exchangeParameters;
|
||||||
Symbol = symbol;
|
Symbol = symbol;
|
||||||
SymbolName = socketClient.FormatSymbol(symbol.BaseAsset, symbol.QuoteAsset, symbol.TradingMode, symbol.DeliverTime);
|
SymbolName = socketClient.FormatSymbol(symbol.BaseAsset, symbol.QuoteAsset, symbol.TradingMode, symbol.DeliverTime);
|
||||||
Exchange = restClient.Exchange;
|
Exchange = restClient.Exchange;
|
||||||
@@ -180,7 +183,7 @@ namespace CryptoExchange.Net.Trackers.Klines
|
|||||||
Status = SyncStatus.Syncing;
|
Status = SyncStatus.Syncing;
|
||||||
_logger.KlineTrackerStarting(SymbolName);
|
_logger.KlineTrackerStarting(SymbolName);
|
||||||
|
|
||||||
var subResult = await _socketClient.SubscribeToKlineUpdatesAsync(new SubscribeKlineRequest(Symbol, Interval),
|
var subResult = await _socketClient.SubscribeToKlineUpdatesAsync(new SubscribeKlineRequest(Symbol, Interval, exchangeParameters: _exchangeParameters),
|
||||||
update =>
|
update =>
|
||||||
{
|
{
|
||||||
AddOrUpdate(update.Data);
|
AddOrUpdate(update.Data);
|
||||||
@@ -237,7 +240,7 @@ namespace CryptoExchange.Net.Trackers.Klines
|
|||||||
|
|
||||||
var limit = Math.Min(_restClient.GetKlinesOptions.MaxLimit, Limit ?? 100);
|
var limit = Math.Min(_restClient.GetKlinesOptions.MaxLimit, Limit ?? 100);
|
||||||
|
|
||||||
var request = new GetKlinesRequest(Symbol, Interval, startTime, DateTime.UtcNow, limit: limit);
|
var request = new GetKlinesRequest(Symbol, Interval, startTime, DateTime.UtcNow, limit: limit, exchangeParameters: _exchangeParameters);
|
||||||
var data = new List<SharedKline>();
|
var data = new List<SharedKline>();
|
||||||
await foreach (var result in ExchangeHelpers.ExecutePages(_restClient.GetKlinesAsync, request).ConfigureAwait(false))
|
await foreach (var result in ExchangeHelpers.ExecutePages(_restClient.GetKlinesAsync, request).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ namespace CryptoExchange.Net.Trackers.Trades
|
|||||||
private SyncStatus _status;
|
private SyncStatus _status;
|
||||||
private long _snapshotId;
|
private long _snapshotId;
|
||||||
private bool _startWithSnapshot;
|
private bool _startWithSnapshot;
|
||||||
|
private ExchangeParameters? _exchangeParameters;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The internal data structure
|
/// The internal data structure
|
||||||
@@ -154,12 +155,14 @@ namespace CryptoExchange.Net.Trackers.Trades
|
|||||||
ITradeSocketClient socketClient,
|
ITradeSocketClient socketClient,
|
||||||
SharedSymbol symbol,
|
SharedSymbol symbol,
|
||||||
int? limit = null,
|
int? limit = null,
|
||||||
TimeSpan? period = null)
|
TimeSpan? period = null,
|
||||||
|
ExchangeParameters? exchangeParameters = null)
|
||||||
{
|
{
|
||||||
_logger = logger ?? new NullLogger<TradeTracker>();
|
_logger = logger ?? new NullLogger<TradeTracker>();
|
||||||
_recentRestClient = recentRestClient;
|
_recentRestClient = recentRestClient;
|
||||||
_historyRestClient = historyRestClient;
|
_historyRestClient = historyRestClient;
|
||||||
_socketClient = socketClient;
|
_socketClient = socketClient;
|
||||||
|
_exchangeParameters = exchangeParameters;
|
||||||
Exchange = socketClient.Exchange;
|
Exchange = socketClient.Exchange;
|
||||||
Symbol = symbol;
|
Symbol = symbol;
|
||||||
SymbolName = socketClient.FormatSymbol(symbol.BaseAsset, symbol.QuoteAsset, symbol.TradingMode, symbol.DeliverTime);
|
SymbolName = socketClient.FormatSymbol(symbol.BaseAsset, symbol.QuoteAsset, symbol.TradingMode, symbol.DeliverTime);
|
||||||
@@ -203,7 +206,7 @@ namespace CryptoExchange.Net.Trackers.Trades
|
|||||||
_startWithSnapshot = startWithSnapshot;
|
_startWithSnapshot = startWithSnapshot;
|
||||||
Status = SyncStatus.Syncing;
|
Status = SyncStatus.Syncing;
|
||||||
_logger.TradeTrackerStarting(SymbolName);
|
_logger.TradeTrackerStarting(SymbolName);
|
||||||
var subResult = await _socketClient.SubscribeToTradeUpdatesAsync(new SubscribeTradeRequest(Symbol),
|
var subResult = await _socketClient.SubscribeToTradeUpdatesAsync(new SubscribeTradeRequest(Symbol, exchangeParameters: _exchangeParameters),
|
||||||
update =>
|
update =>
|
||||||
{
|
{
|
||||||
AddData(update.Data);
|
AddData(update.Data);
|
||||||
@@ -257,7 +260,7 @@ namespace CryptoExchange.Net.Trackers.Trades
|
|||||||
if (_historyRestClient != null)
|
if (_historyRestClient != null)
|
||||||
{
|
{
|
||||||
var startTime = Period == null ? DateTime.UtcNow.AddMinutes(-5) : DateTime.UtcNow.Add(-Period.Value);
|
var startTime = Period == null ? DateTime.UtcNow.AddMinutes(-5) : DateTime.UtcNow.Add(-Period.Value);
|
||||||
var request = new GetTradeHistoryRequest(Symbol, startTime, DateTime.UtcNow);
|
var request = new GetTradeHistoryRequest(Symbol, startTime, DateTime.UtcNow, exchangeParameters: _exchangeParameters);
|
||||||
var data = new List<SharedTrade>();
|
var data = new List<SharedTrade>();
|
||||||
await foreach (var result in ExchangeHelpers.ExecutePages(_historyRestClient.GetTradeHistoryAsync, request).ConfigureAwait(false))
|
await foreach (var result in ExchangeHelpers.ExecutePages(_historyRestClient.GetTradeHistoryAsync, request).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
@@ -278,7 +281,7 @@ namespace CryptoExchange.Net.Trackers.Trades
|
|||||||
if (Limit.HasValue)
|
if (Limit.HasValue)
|
||||||
limit = Math.Min(_recentRestClient.GetRecentTradesOptions.MaxLimit, Limit.Value);
|
limit = Math.Min(_recentRestClient.GetRecentTradesOptions.MaxLimit, Limit.Value);
|
||||||
|
|
||||||
var snapshot = await _recentRestClient.GetRecentTradesAsync(new GetRecentTradesRequest(Symbol, limit)).ConfigureAwait(false);
|
var snapshot = await _recentRestClient.GetRecentTradesAsync(new GetRecentTradesRequest(Symbol, limit, exchangeParameters: _exchangeParameters)).ConfigureAwait(false);
|
||||||
if (!snapshot.Success)
|
if (!snapshot.Success)
|
||||||
{
|
{
|
||||||
return CallResult.Fail(snapshot.Error);
|
return CallResult.Fail(snapshot.Error);
|
||||||
|
|||||||
@@ -127,6 +127,14 @@ Various:
|
|||||||
* PlatformInfo now required support environment names in the constructor
|
* PlatformInfo now required support environment names in the constructor
|
||||||
|
|
||||||
## Release notes
|
## Release notes
|
||||||
|
* Version 12.1.1 - 11 Jul 2026
|
||||||
|
* Added timestamp deserialization support for yyyy-MM-dd HH:mm:ss.ffffff+00:00:00
|
||||||
|
|
||||||
|
* Version 12.1.0 - 09 Jul 2026
|
||||||
|
* Added ExchangeParameters parameter to KlineTracker, TradeTracker and ITrackerFactory methods
|
||||||
|
* Updated some testing logic
|
||||||
|
* Fixed nullability operator on Parameters.AddCommaSeperated
|
||||||
|
|
||||||
* Version 12.0.2 - 01 Jul 2026
|
* Version 12.0.2 - 01 Jul 2026
|
||||||
* Updated test validation to output a list of issues instead of throwing on the first
|
* Updated test validation to output a list of issues instead of throwing on the first
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user