1
0
mirror of https://github.com/JKorf/CryptoExchange.Net synced 2025-10-31 10:27:48 +00:00

Compare commits

..

12 Commits

Author SHA1 Message Date
Jkorf
995cd3d84c Updated to version 9.11.1 2025-10-30 14:33:26 +01:00
Jkorf
919cdf0075 Updated CryptoExchange.Net version 2025-10-30 14:32:41 +01:00
Jkorf
d181c9cfc1 Updated to version 9.11.0 2025-10-30 14:21:30 +01:00
Jkorf
5943142c44 Updated to version 9.11.0 2025-10-30 13:47:48 +01:00
Jkorf
dbc430e838 Added StaticLogger to LibraryHelpers, updated warning logging for converters to use StaticLogger 2025-10-30 12:52:05 +01:00
Jkorf
7413d03d31 Added client reference helper to LibraryHelpers 2025-10-30 11:25:27 +01:00
Jkorf
dd60067684 Updated examples 2025-10-27 12:00:51 +01:00
Jkorf
04e4ddf525 Fixed exception when initial trade snapshot has no items in TradeTracker 2025-10-27 11:59:54 +01:00
Jkorf
99bf6d7c75 Added Upbit reference 2025-10-27 11:44:12 +01:00
Jkorf
99a203933c Added missing release notes 2025-10-15 13:59:10 +02:00
Jkorf
b43d2a2040 Updated to version 9.10.0 2025-10-15 13:36:33 +02:00
Jkorf
ba9c406def Updated CryptoExchange.Net version 2025-10-15 13:34:50 +02:00
21 changed files with 176 additions and 85 deletions

View File

@ -6,9 +6,9 @@
<PackageId>CryptoExchange.Net.Protobuf</PackageId> <PackageId>CryptoExchange.Net.Protobuf</PackageId>
<Authors>JKorf</Authors> <Authors>JKorf</Authors>
<Description>Protobuf support for CryptoExchange.Net</Description> <Description>Protobuf support for CryptoExchange.Net</Description>
<PackageVersion>9.9.0</PackageVersion> <PackageVersion>9.11.1</PackageVersion>
<AssemblyVersion>9.9.0</AssemblyVersion> <AssemblyVersion>9.11.1</AssemblyVersion>
<FileVersion>9.9.0</FileVersion> <FileVersion>9.11.1</FileVersion>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance> <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PackageTags>CryptoExchange;CryptoExchange.Net</PackageTags> <PackageTags>CryptoExchange;CryptoExchange.Net</PackageTags>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
@ -41,7 +41,7 @@
<DocumentationFile>CryptoExchange.Net.Protobuf.xml</DocumentationFile> <DocumentationFile>CryptoExchange.Net.Protobuf.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CryptoExchange.Net" Version="9.9.0" /> <PackageReference Include="CryptoExchange.Net" Version="9.11.0" />
<PackageReference Include="protobuf-net" Version="3.2.56" /> <PackageReference Include="protobuf-net" Version="3.2.56" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -5,6 +5,15 @@
Protobuf support for CryptoExchange.Net. Protobuf support for CryptoExchange.Net.
## Release notes ## Release notes
* Version 9.11.1 - 30 Oct 2025
* Updated CryptoExchange.Net version to 9.11.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
* Version 9.11.0 - 30 Oct 2025
* Updated CryptoExchange.Net version to 9.11.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
* Version 9.10.0 - 15 Oct 2025
* Updated CryptoExchange.Net version to 9.10.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
* Version 9.9.0 - 06 Oct 2025 * Version 9.9.0 - 06 Oct 2025
* Updated CryptoExchange.Net version to 9.9.0, see https://github.com/JKorf/CryptoExchange.Net/releases/ * Updated CryptoExchange.Net version to 9.9.0, see https://github.com/JKorf/CryptoExchange.Net/releases/

View File

@ -6,10 +6,10 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1"></PackageReference> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0"></PackageReference>
<PackageReference Include="Moq" Version="4.20.72" /> <PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="NUnit" Version="4.3.2"></PackageReference> <PackageReference Include="NUnit" Version="4.4.0"></PackageReference>
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0"></PackageReference> <PackageReference Include="NUnit3TestAdapter" Version="5.2.0"></PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -21,6 +21,8 @@ namespace CryptoExchange.Net.Clients
protected BaseRestClient(ILoggerFactory? loggerFactory, string name) : base(loggerFactory, name) protected BaseRestClient(ILoggerFactory? loggerFactory, string name) : base(loggerFactory, name)
{ {
_logger = loggerFactory?.CreateLogger(name + ".RestClient") ?? NullLoggerFactory.Instance.CreateLogger(name); _logger = loggerFactory?.CreateLogger(name + ".RestClient") ?? NullLoggerFactory.Instance.CreateLogger(name);
LibraryHelpers.StaticLogger = loggerFactory?.CreateLogger(name);
} }
} }
} }

View File

@ -40,6 +40,8 @@ namespace CryptoExchange.Net.Clients
protected BaseSocketClient(ILoggerFactory? loggerFactory, string name) : base(loggerFactory, name) protected BaseSocketClient(ILoggerFactory? loggerFactory, string name) : base(loggerFactory, name)
{ {
_logger = loggerFactory?.CreateLogger(name + ".SocketClient") ?? NullLoggerFactory.Instance.CreateLogger(name); _logger = loggerFactory?.CreateLogger(name + ".SocketClient") ?? NullLoggerFactory.Instance.CreateLogger(name);
LibraryHelpers.StaticLogger = loggerFactory?.CreateLogger(name);
} }
/// <summary> /// <summary>

View File

@ -1,4 +1,5 @@
using System; using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Text.Json; using System.Text.Json;
@ -47,7 +48,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
if (string.IsNullOrEmpty(value)) if (string.IsNullOrEmpty(value))
{ {
if (typeToConvert == typeof(bool)) if (typeToConvert == typeof(bool))
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Received null bool value, but property type is not a nullable bool"); LibraryHelpers.StaticLogger?.LogWarning("Received null bool value, but property type is not a nullable bool");
return default; return default;
} }

View File

@ -1,4 +1,5 @@
using System; using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
@ -39,7 +40,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
if (reader.TokenType == JsonTokenType.Null) if (reader.TokenType == JsonTokenType.Null)
{ {
if (typeToConvert == typeof(DateTime)) if (typeToConvert == typeof(DateTime))
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | DateTime value of null, but property is not nullable"); LibraryHelpers.StaticLogger?.LogWarning("DateTime value of null, but property is not nullable");
return default; return default;
} }
@ -124,7 +125,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|| !int.TryParse(stringValue.Substring(8, 2), out var hour) || !int.TryParse(stringValue.Substring(8, 2), out var hour)
|| !int.TryParse(stringValue.Substring(10, 2), out var minute)) || !int.TryParse(stringValue.Substring(10, 2), out var minute))
{ {
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Unknown DateTime format: " + stringValue); LibraryHelpers.StaticLogger?.LogWarning("Unknown DateTime format: " + stringValue);
return default; return default;
} }
return new DateTime(year, month, day, hour, minute, 0, DateTimeKind.Utc); return new DateTime(year, month, day, hour, minute, 0, DateTimeKind.Utc);
@ -137,7 +138,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|| !int.TryParse(stringValue.Substring(4, 2), out var month) || !int.TryParse(stringValue.Substring(4, 2), out var month)
|| !int.TryParse(stringValue.Substring(6, 2), out var day)) || !int.TryParse(stringValue.Substring(6, 2), out var day))
{ {
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Unknown DateTime format: " + stringValue); LibraryHelpers.StaticLogger?.LogWarning("Unknown DateTime format: " + stringValue);
return default; return default;
} }
return new DateTime(year, month, day, 0, 0, 0, DateTimeKind.Utc); return new DateTime(year, month, day, 0, 0, 0, DateTimeKind.Utc);
@ -150,7 +151,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|| !int.TryParse(stringValue.Substring(2, 2), out var month) || !int.TryParse(stringValue.Substring(2, 2), out var month)
|| !int.TryParse(stringValue.Substring(4, 2), out var day)) || !int.TryParse(stringValue.Substring(4, 2), out var day))
{ {
Trace.WriteLine("{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Unknown DateTime format: " + stringValue); LibraryHelpers.StaticLogger?.LogWarning("Unknown DateTime format: " + stringValue);
return default; return default;
} }
return new DateTime(year + 2000, month, day, 0, 0, 0, DateTimeKind.Utc); return new DateTime(year + 2000, month, day, 0, 0, 0, DateTimeKind.Utc);
@ -179,7 +180,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|| !int.TryParse(values[1], out var month) || !int.TryParse(values[1], out var month)
|| !int.TryParse(values[2], out var day)) || !int.TryParse(values[2], out var day))
{ {
Trace.WriteLine("{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Unknown DateTime format: " + stringValue); LibraryHelpers.StaticLogger?.LogWarning("Unknown DateTime format: " + stringValue);
return default; return default;
} }

View File

@ -1,4 +1,6 @@
using CryptoExchange.Net.Attributes; using CryptoExchange.Net.Attributes;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
@ -79,7 +81,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
} }
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
return _enumConverter.ReadNullable(ref reader, typeToConvert, options, out var isEmptyString, out var warn); return _enumConverter.ReadNullable(ref reader, typeToConvert, options, out var isEmptyString);
} }
public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options) public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
@ -98,20 +100,13 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
/// <inheritdoc /> /// <inheritdoc />
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
var t = ReadNullable(ref reader, typeToConvert, options, out var isEmptyString, out var warn); var t = ReadNullable(ref reader, typeToConvert, options, out var isEmptyString);
if (t == null) if (t == null)
{ {
if (warn) if (isEmptyString && !_unknownValuesWarned.Contains(null))
{ {
if (isEmptyString) // We received an empty string and have no mapping for it, and the property isn't nullable
{ LibraryHelpers.StaticLogger?.LogWarning($"Received null or empty enum value, but property type is not a nullable enum. EnumType: {typeof(T).Name}. If you think {typeof(T).Name} should be nullable please open an issue on the Github repo");
// We received an empty string and have no mapping for it, and the property isn't nullable
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Received empty string as enum value, but property type is not a nullable enum. EnumType: {typeof(T).Name}. If you think {typeof(T).Name} should be nullable please open an issue on the Github repo");
}
else
{
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Received null enum value, but property type is not a nullable enum. EnumType: {typeof(T).Name}. If you think {typeof(T).Name} should be nullable please open an issue on the Github repo");
}
} }
return new T(); // return default value return new T(); // return default value
@ -122,10 +117,9 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
} }
} }
private T? ReadNullable(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, out bool isEmptyString, out bool warn) private T? ReadNullable(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, out bool isEmptyString)
{ {
isEmptyString = false; isEmptyString = false;
warn = false;
var enumType = typeof(T); var enumType = typeof(T);
if (_mapping == null) if (_mapping == null)
_mapping = AddMapping(); _mapping = AddMapping();
@ -154,9 +148,8 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
// We received an enum value but weren't able to parse it. // We received an enum value but weren't able to parse it.
if (!_unknownValuesWarned.Contains(stringValue)) if (!_unknownValuesWarned.Contains(stringValue))
{ {
warn = true;
_unknownValuesWarned.Add(stringValue!); _unknownValuesWarned.Add(stringValue!);
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Cannot map enum value. EnumType: {enumType.Name}, Value: {stringValue}, Known values: {string.Join(", ", _mapping.Select(m => m.Value))}. If you think {stringValue} should added please open an issue on the Github repo"); LibraryHelpers.StaticLogger?.LogWarning($"Cannot map enum value. EnumType: {enumType.Name}, Value: {stringValue}, Known values: {string.Join(", ", _mapping.Select(m => m.Value))}. If you think {stringValue} should added please open an issue on the Github repo");
} }
} }

View File

@ -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>9.10.0</PackageVersion> <PackageVersion>9.11.0</PackageVersion>
<AssemblyVersion>9.10.0</AssemblyVersion> <AssemblyVersion>9.11.0</AssemblyVersion>
<FileVersion>9.10.0</FileVersion> <FileVersion>9.11.0</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>
@ -51,11 +51,11 @@
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.6" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.6" /> <PackageReference Include="System.Text.Json" Version="9.0.10" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Transitive Client Packages"> <ItemGroup Label="Transitive Client Packages">
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.6" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.6" /> <PackageReference Include="Microsoft.Extensions.Http" Version="9.0.10" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,4 +1,5 @@
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using Microsoft.Extensions.Logging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
@ -12,11 +13,50 @@ namespace CryptoExchange.Net
/// </summary> /// </summary>
public static class LibraryHelpers public static class LibraryHelpers
{ {
private static ILogger? _staticLogger;
/// <summary>
/// Static logger
/// </summary>
public static ILogger? StaticLogger
{
get => _staticLogger;
internal set
{
if (_staticLogger != null)
return;
_staticLogger = value;
}
}
/// <summary> /// <summary>
/// Client order id separator /// Client order id separator
/// </summary> /// </summary>
public const string ClientOrderIdSeparator = "JK"; public const string ClientOrderIdSeparator = "JK";
private static Dictionary<string, string> _defaultClientReferences = new Dictionary<string, string>()
{
{ "Binance.Spot", "x-VICEW9VV" },
{ "Binance.Futures", "x-d63tKbx3" },
{ "BingX", "easytrading" },
{ "Bitfinex", "kCCe-CNBO" },
{ "Bitget", "6x21p" },
{ "BitMart", "EASYTRADING0001" },
{ "BitMEX", "Sent from JKorf" },
{ "BloFin", "5c07cf695885c282" },
{ "Bybit", "Zx000356" },
{ "CoinEx", "x-147866029-" },
{ "GateIo", "copytraderpw" },
{ "HTX", "AA1ef14811" },
{ "Kucoin.FuturesName", "Easytradingfutures" },
{ "Kucoin.FuturesKey", "9e08c05f-454d-4580-82af-2f4c7027fd00" },
{ "Kucoin.SpotName", "Easytrading" },
{ "Kucoin.SpotKey", "f8ae62cb-2b3d-420c-8c98-e1c17dd4e30a" },
{ "Mexc", "EASYT" },
{ "OKX", "1425d83a94fbBCDE" },
{ "XT", "4XWeqN10M1fcoI5L" },
};
/// <summary> /// <summary>
/// Apply broker id to a client order id /// Apply broker id to a client order id
/// </summary> /// </summary>
@ -47,6 +87,22 @@ namespace CryptoExchange.Net
return clientOrderId; return clientOrderId;
} }
/// <summary>
/// Get the client reference for an exchange if available
/// </summary>
public static string GetClientReference(Func<string?> optionsReference, string exchange, string? topic = null)
{
var optionsValue = optionsReference();
if (!string.IsNullOrEmpty(optionsValue))
return optionsValue!;
var key = exchange;
if (topic != null)
key += "." + topic;
return _defaultClientReferences.TryGetValue(key, out var id) ? id : throw new KeyNotFoundException($"{exchange} not found in configuration");
}
/// <summary> /// <summary>
/// Create a new HttpMessageHandler instance /// Create a new HttpMessageHandler instance
/// </summary> /// </summary>

View File

@ -325,9 +325,12 @@ namespace CryptoExchange.Net.Trackers.Trades
if (Period != null) if (Period != null)
items = items.Where(e => e.Timestamp >= DateTime.UtcNow.Add(-Period.Value)); items = items.Where(e => e.Timestamp >= DateTime.UtcNow.Add(-Period.Value));
_snapshotId = data.Max(d => d.Timestamp.Ticks); if (items.Any())
foreach (var item in items.OrderBy(d => d.Timestamp)) {
_data.Add(item); _snapshotId = data.Max(d => d.Timestamp.Ticks);
foreach (var item in items.OrderBy(d => d.Timestamp))
_data.Add(item);
}
_snapshotSet = true; _snapshotSet = true;
_changed = true; _changed = true;

View File

@ -5,31 +5,32 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Binance.Net" Version="11.8.0" /> <PackageReference Include="Binance.Net" Version="11.9.0" />
<PackageReference Include="Bitfinex.Net" Version="9.8.0" /> <PackageReference Include="Bitfinex.Net" Version="9.9.0" />
<PackageReference Include="BitMart.Net" Version="2.9.0" /> <PackageReference Include="BitMart.Net" Version="2.10.0" />
<PackageReference Include="BloFin.Net" Version="1.1.0" /> <PackageReference Include="BloFin.Net" Version="1.2.0" />
<PackageReference Include="Bybit.Net" Version="5.9.0" /> <PackageReference Include="Bybit.Net" Version="5.10.1" />
<PackageReference Include="CoinEx.Net" Version="9.8.0" /> <PackageReference Include="CoinEx.Net" Version="9.9.0" />
<PackageReference Include="CoinW.Net" Version="1.5.0" /> <PackageReference Include="CoinW.Net" Version="1.6.0" />
<PackageReference Include="CryptoCom.Net" Version="2.9.0" /> <PackageReference Include="CryptoCom.Net" Version="2.10.0" />
<PackageReference Include="DeepCoin.Net" Version="2.8.0" /> <PackageReference Include="DeepCoin.Net" Version="2.9.0" />
<PackageReference Include="GateIo.Net" Version="2.9.1" /> <PackageReference Include="GateIo.Net" Version="2.11.0" />
<PackageReference Include="HyperLiquid.Net" Version="2.13.0" /> <PackageReference Include="HyperLiquid.Net" Version="2.14.0" />
<PackageReference Include="JK.BingX.Net" Version="2.8.0" /> <PackageReference Include="JK.BingX.Net" Version="2.9.0" />
<PackageReference Include="JK.Bitget.Net" Version="2.8.0" /> <PackageReference Include="JK.Bitget.Net" Version="2.9.0" />
<PackageReference Include="JK.Mexc.Net" Version="3.9.0" /> <PackageReference Include="JK.Mexc.Net" Version="3.10.0" />
<PackageReference Include="JK.OKX.Net" Version="3.8.0" /> <PackageReference Include="JK.OKX.Net" Version="3.9.0" />
<PackageReference Include="Jkorf.Aster.Net" Version="1.0.0" /> <PackageReference Include="Jkorf.Aster.Net" Version="1.1.0" />
<PackageReference Include="JKorf.BitMEX.Net" Version="2.8.0" /> <PackageReference Include="JKorf.BitMEX.Net" Version="2.9.0" />
<PackageReference Include="JKorf.Coinbase.Net" Version="2.8.0" /> <PackageReference Include="JKorf.Coinbase.Net" Version="2.9.0" />
<PackageReference Include="JKorf.HTX.Net" Version="7.8.0" /> <PackageReference Include="JKorf.HTX.Net" Version="7.9.0" />
<PackageReference Include="KrakenExchange.Net" Version="6.8.0" /> <PackageReference Include="JKorf.Upbit.Net" Version="1.0.0" />
<PackageReference Include="Kucoin.Net" Version="7.8.0" /> <PackageReference Include="KrakenExchange.Net" Version="6.9.0" />
<PackageReference Include="Kucoin.Net" Version="7.9.0" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" /> <PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Toobit.Net" Version="1.7.0" /> <PackageReference Include="Toobit.Net" Version="1.8.0" />
<PackageReference Include="WhiteBit.Net" Version="2.9.0" /> <PackageReference Include="WhiteBit.Net" Version="2.10.0" />
<PackageReference Include="XT.Net" Version="2.8.0" /> <PackageReference Include="XT.Net" Version="2.9.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -21,6 +21,7 @@
@inject IMexcRestClient mexcClient @inject IMexcRestClient mexcClient
@inject IOKXRestClient okxClient @inject IOKXRestClient okxClient
@inject IToobitRestClient toobitClient @inject IToobitRestClient toobitClient
@inject IUpbitRestClient upbitClient
@inject IWhiteBitRestClient whitebitClient @inject IWhiteBitRestClient whitebitClient
@inject IXTRestClient xtClient @inject IXTRestClient xtClient
@ -57,6 +58,7 @@
var mexcTask = mexcClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT"); var mexcTask = mexcClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
var okxTask = okxClient.UnifiedApi.ExchangeData.GetTickerAsync("BTC-USDT"); var okxTask = okxClient.UnifiedApi.ExchangeData.GetTickerAsync("BTC-USDT");
var toobitTask = toobitClient.SpotApi.ExchangeData.GetTickersAsync("BTCUSDT"); var toobitTask = toobitClient.SpotApi.ExchangeData.GetTickersAsync("BTCUSDT");
var upbitTask = upbitClient.SpotApi.ExchangeData.GetTickerAsync("USDT-BTC");
var whitebitTask = whitebitClient.V4Api.ExchangeData.GetTickersAsync(); var whitebitTask = whitebitClient.V4Api.ExchangeData.GetTickersAsync();
var xtTask = xtClient.SpotApi.ExchangeData.GetTickersAsync("btc_usdt"); var xtTask = xtClient.SpotApi.ExchangeData.GetTickersAsync("btc_usdt");
@ -136,6 +138,9 @@
if (toobitTask.Result.Success) if (toobitTask.Result.Success)
_prices.Add("Toobit", toobitTask.Result.Data.Single().LastPrice ?? 0); _prices.Add("Toobit", toobitTask.Result.Data.Single().LastPrice ?? 0);
if (upbitTask.Result.Success)
_prices.Add("Upbit", upbitTask.Result.Data.LastPrice);
if (whitebitTask.Result.Success){ if (whitebitTask.Result.Success){
// WhiteBit API doesn't offer an endpoint to filter for a specific ticker, so we have to filter client side // WhiteBit API doesn't offer an endpoint to filter for a specific ticker, so we have to filter client side
var tickers = whitebitTask.Result.Data; var tickers = whitebitTask.Result.Data;

View File

@ -21,6 +21,7 @@
@inject IMexcSocketClient mexcSocketClient @inject IMexcSocketClient mexcSocketClient
@inject IOKXSocketClient okxSocketClient @inject IOKXSocketClient okxSocketClient
@inject IToobitSocketClient toobitSocketClient @inject IToobitSocketClient toobitSocketClient
@inject IUpbitSocketClient upbitSocketClient
@inject IWhiteBitSocketClient whitebitSocketClient @inject IWhiteBitSocketClient whitebitSocketClient
@inject IXTSocketClient xtSocketClient @inject IXTSocketClient xtSocketClient
@using System.Collections.Concurrent @using System.Collections.Concurrent
@ -72,6 +73,7 @@
okxSocketClient.UnifiedApi.ExchangeData.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("OKX", data.Data.LastPrice ?? 0)), okxSocketClient.UnifiedApi.ExchangeData.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("OKX", data.Data.LastPrice ?? 0)),
// Toobit doesn't support the ETH/BTC pair // Toobit doesn't support the ETH/BTC pair
//toobitSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Toobit", data.Data.LastPrice ?? 0)), //toobitSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Toobit", data.Data.LastPrice ?? 0)),
upbitSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("BTC-ETH", data => UpdateData("Upbit", data.Data.LastPrice)),
whitebitSocketClient.V4Api.SubscribeToTickerUpdatesAsync("ETH_BTC", data => UpdateData("WhiteBit", data.Data.Ticker.LastPrice)), whitebitSocketClient.V4Api.SubscribeToTickerUpdatesAsync("ETH_BTC", data => UpdateData("WhiteBit", data.Data.Ticker.LastPrice)),
}; };

View File

@ -25,6 +25,7 @@
@using Kucoin.Net.Interfaces @using Kucoin.Net.Interfaces
@using Mexc.Net.Interfaces @using Mexc.Net.Interfaces
@using OKX.Net.Interfaces; @using OKX.Net.Interfaces;
@using Upbit.Net.Interfaces;
@using Toobit.Net.Interfaces; @using Toobit.Net.Interfaces;
@using WhiteBit.Net.Interfaces @using WhiteBit.Net.Interfaces
@using XT.Net.Interfaces @using XT.Net.Interfaces
@ -50,6 +51,7 @@
@inject IMexcOrderBookFactory mexcFactory @inject IMexcOrderBookFactory mexcFactory
@inject IOKXOrderBookFactory okxFactory @inject IOKXOrderBookFactory okxFactory
@inject IToobitOrderBookFactory toobitFactory @inject IToobitOrderBookFactory toobitFactory
@inject IUpbitOrderBookFactory upbitFactory
@inject IWhiteBitOrderBookFactory whitebitFactory @inject IWhiteBitOrderBookFactory whitebitFactory
@inject IXTOrderBookFactory xtFactory @inject IXTOrderBookFactory xtFactory
@implements IDisposable @implements IDisposable
@ -107,6 +109,7 @@
{ "Mexc", mexcFactory.CreateSpot("ETHBTC") }, { "Mexc", mexcFactory.CreateSpot("ETHBTC") },
{ "OKX", okxFactory.Create("ETH-BTC") }, { "OKX", okxFactory.Create("ETH-BTC") },
{ "Toobit", toobitFactory.CreateSpot("ETHUSDT") }, { "Toobit", toobitFactory.CreateSpot("ETHUSDT") },
{ "Upbit", upbitFactory.CreateSpot("BTC-ETH") },
{ "WhiteBit", whitebitFactory.CreateV4("ETH_BTC") }, { "WhiteBit", whitebitFactory.CreateV4("ETH_BTC") },
{ "XT", xtFactory.CreateSpot("eth_btc") }, { "XT", xtFactory.CreateSpot("eth_btc") },
}; };

View File

@ -26,6 +26,7 @@
@using Kucoin.Net.Interfaces @using Kucoin.Net.Interfaces
@using Mexc.Net.Interfaces @using Mexc.Net.Interfaces
@using OKX.Net.Interfaces; @using OKX.Net.Interfaces;
@using Upbit.Net.Interfaces;
@using Toobit.Net.Interfaces; @using Toobit.Net.Interfaces;
@using WhiteBit.Net.Interfaces @using WhiteBit.Net.Interfaces
@using XT.Net.Interfaces @using XT.Net.Interfaces
@ -51,11 +52,12 @@
@inject IMexcTrackerFactory mexcFactory @inject IMexcTrackerFactory mexcFactory
@inject IOKXTrackerFactory okxFactory @inject IOKXTrackerFactory okxFactory
@inject IToobitTrackerFactory toobitFactory @inject IToobitTrackerFactory toobitFactory
@inject IUpbitTrackerFactory upbitFactory
@inject IWhiteBitTrackerFactory whitebitFactory @inject IWhiteBitTrackerFactory whitebitFactory
@inject IXTTrackerFactory xtFactory @inject IXTTrackerFactory xtFactory
@implements IDisposable @implements IDisposable
<h3>ETH-BTC trade Trackers, live updates:</h3> <h3>Trade Trackers, live updates:</h3>
<div style="display:flex; flex-wrap: wrap;"> <div style="display:flex; flex-wrap: wrap;">
@foreach (var tracker in _trackers.OrderBy(p => p.Exchange)) @foreach (var tracker in _trackers.OrderBy(p => p.Exchange))
{ {
@ -103,11 +105,12 @@
{ mexcFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) }, { mexcFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
{ okxFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) }, { okxFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
{ toobitFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) }, { toobitFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
{ upbitFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
{ whitebitFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) }, { whitebitFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
{ xtFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) }, { xtFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
}; };
await Task.WhenAll(_trackers.Select(b => b.StartAsync())); await Task.WhenAll(_trackers.Select(b => b.StartAsync(false)));
// Use a manual update timer so the page isn't refreshed too often // Use a manual update timer so the page isn't refreshed too often
_timer = new Timer(500); _timer = new Timer(500);

View File

@ -54,6 +54,7 @@ namespace BlazorClient
services.AddMexc(); services.AddMexc();
services.AddOKX(); services.AddOKX();
services.AddToobit(); services.AddToobit();
services.AddUpbit();
services.AddWhiteBit(); services.AddWhiteBit();
services.AddXT(); services.AddXT();
} }

View File

@ -29,6 +29,7 @@
@using Kucoin.Net.Interfaces.Clients; @using Kucoin.Net.Interfaces.Clients;
@using Mexc.Net.Interfaces.Clients; @using Mexc.Net.Interfaces.Clients;
@using OKX.Net.Interfaces.Clients; @using OKX.Net.Interfaces.Clients;
@using Upbit.Net.Interfaces.Clients;
@using Toobit.Net.Interfaces.Clients; @using Toobit.Net.Interfaces.Clients;
@using WhiteBit.Net.Interfaces.Clients @using WhiteBit.Net.Interfaces.Clients
@using XT.Net.Interfaces.Clients @using XT.Net.Interfaces.Clients

View File

@ -6,20 +6,20 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Binance.Net" Version="11.1.0" /> <PackageReference Include="Binance.Net" Version="11.9.0" />
<PackageReference Include="Bitfinex.Net" Version="9.1.0" /> <PackageReference Include="Bitfinex.Net" Version="9.9.0" />
<PackageReference Include="BitMart.Net" Version="2.1.0" /> <PackageReference Include="BitMart.Net" Version="2.10.0" />
<PackageReference Include="Bybit.Net" Version="5.1.0" /> <PackageReference Include="Bybit.Net" Version="5.10.1" />
<PackageReference Include="CoinEx.Net" Version="9.1.0" /> <PackageReference Include="CoinEx.Net" Version="9.9.0" />
<PackageReference Include="CryptoCom.Net" Version="2.1.0" /> <PackageReference Include="CryptoCom.Net" Version="2.10.0" />
<PackageReference Include="GateIo.Net" Version="2.1.0" /> <PackageReference Include="GateIo.Net" Version="2.11.0" />
<PackageReference Include="JK.Bitget.Net" Version="2.1.0" /> <PackageReference Include="JK.Bitget.Net" Version="2.9.0" />
<PackageReference Include="JK.Mexc.Net" Version="3.1.0" /> <PackageReference Include="JK.Mexc.Net" Version="3.10.0" />
<PackageReference Include="JK.OKX.Net" Version="3.1.0" /> <PackageReference Include="JK.OKX.Net" Version="3.9.0" />
<PackageReference Include="JKorf.Coinbase.Net" Version="2.1.0" /> <PackageReference Include="JKorf.Coinbase.Net" Version="2.9.0" />
<PackageReference Include="JKorf.HTX.Net" Version="7.1.0" /> <PackageReference Include="JKorf.HTX.Net" Version="7.9.0" />
<PackageReference Include="KrakenExchange.Net" Version="6.1.0" /> <PackageReference Include="KrakenExchange.Net" Version="6.9.0" />
<PackageReference Include="Kucoin.Net" Version="7.1.0" /> <PackageReference Include="Kucoin.Net" Version="7.9.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -8,9 +8,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Binance.Net" Version="11.1.0" /> <PackageReference Include="Binance.Net" Version="11.9.0" />
<PackageReference Include="BitMart.Net" Version="2.1.0" /> <PackageReference Include="BitMart.Net" Version="2.10.0" />
<PackageReference Include="JK.OKX.Net" Version="3.1.0" /> <PackageReference Include="JK.OKX.Net" Version="3.9.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -35,6 +35,7 @@ Full list of all libraries part of the CryptoExchange.Net ecosystem. Consider us
|![Mexc](https://raw.githubusercontent.com/JKorf/Mexc.Net/refs/heads/main/Mexc.Net/Icon/icon.png)|Mexc|CEX|[JKorf/Mexc.Net](https://github.com/JKorf/Mexc.Net)|[![Nuget version](https://img.shields.io/nuget/v/JK.Mexc.net.svg?style=flat-square)](https://www.nuget.org/packages/JK.Mexc.Net)|-|-| |![Mexc](https://raw.githubusercontent.com/JKorf/Mexc.Net/refs/heads/main/Mexc.Net/Icon/icon.png)|Mexc|CEX|[JKorf/Mexc.Net](https://github.com/JKorf/Mexc.Net)|[![Nuget version](https://img.shields.io/nuget/v/JK.Mexc.net.svg?style=flat-square)](https://www.nuget.org/packages/JK.Mexc.Net)|-|-|
|![OKX](https://raw.githubusercontent.com/JKorf/OKX.Net/refs/heads/main/OKX.Net/Icon/icon.png)|OKX|CEX|[JKorf/OKX.Net](https://github.com/JKorf/OKX.Net)|[![Nuget version](https://img.shields.io/nuget/v/JK.OKX.net.svg?style=flat-square)](https://www.nuget.org/packages/JK.OKX.Net)|[Link](https://www.okx.com/join/14592495)|20%| |![OKX](https://raw.githubusercontent.com/JKorf/OKX.Net/refs/heads/main/OKX.Net/Icon/icon.png)|OKX|CEX|[JKorf/OKX.Net](https://github.com/JKorf/OKX.Net)|[![Nuget version](https://img.shields.io/nuget/v/JK.OKX.net.svg?style=flat-square)](https://www.nuget.org/packages/JK.OKX.Net)|[Link](https://www.okx.com/join/14592495)|20%|
|![Toobit](https://raw.githubusercontent.com/JKorf/Toobit.Net/refs/heads/main/Toobit.Net/Icon/icon.png)|Toobit|CEX|[JKorf/Toobit.Net](https://github.com/JKorf/Toobit.Net)|[![Nuget version](https://img.shields.io/nuget/v/Toobit.net.svg?style=flat-square)](https://www.nuget.org/packages/Toobit.Net)|[Link](https://www.toobit.com/en-US/register?invite_code=zsV19h)|-| |![Toobit](https://raw.githubusercontent.com/JKorf/Toobit.Net/refs/heads/main/Toobit.Net/Icon/icon.png)|Toobit|CEX|[JKorf/Toobit.Net](https://github.com/JKorf/Toobit.Net)|[![Nuget version](https://img.shields.io/nuget/v/Toobit.net.svg?style=flat-square)](https://www.nuget.org/packages/Toobit.Net)|[Link](https://www.toobit.com/en-US/register?invite_code=zsV19h)|-|
|![Upbit](https://raw.githubusercontent.com/JKorf/Upbit.Net/refs/heads/main/Upbit.Net/Icon/icon.png)|Upbit|CEX|[JKorf/Upbit.Net](https://github.com/JKorf/Upbit.Net)|[![Nuget version](https://img.shields.io/nuget/v/JKorf.Upbit.net.svg?style=flat-square)](https://www.nuget.org/packages/JKorf.Upbit.Net)|-|-|
|![WhiteBit](https://raw.githubusercontent.com/JKorf/WhiteBit.Net/refs/heads/main/WhiteBit.Net/Icon/icon.png)|WhiteBit|CEX|[JKorf/WhiteBit.Net](https://github.com/JKorf/WhiteBit.Net)|[![Nuget version](https://img.shields.io/nuget/v/WhiteBit.net.svg?style=flat-square)](https://www.nuget.org/packages/WhiteBit.Net)|[Link](https://whitebit.com/referral/a8e59b59-186c-4662-824c-3095248e0edf)|-| |![WhiteBit](https://raw.githubusercontent.com/JKorf/WhiteBit.Net/refs/heads/main/WhiteBit.Net/Icon/icon.png)|WhiteBit|CEX|[JKorf/WhiteBit.Net](https://github.com/JKorf/WhiteBit.Net)|[![Nuget version](https://img.shields.io/nuget/v/WhiteBit.net.svg?style=flat-square)](https://www.nuget.org/packages/WhiteBit.Net)|[Link](https://whitebit.com/referral/a8e59b59-186c-4662-824c-3095248e0edf)|-|
|![XT](https://raw.githubusercontent.com/JKorf/XT.Net/refs/heads/main/XT.Net/Icon/icon.png)|XT|CEX|[JKorf/XT.Net](https://github.com/JKorf/XT.Net)|[![Nuget version](https://img.shields.io/nuget/v/XT.net.svg?style=flat-square)](https://www.nuget.org/packages/XT.Net)|[Link](https://www.xt.com/ru/accounts/register?ref=CZG39C)|25%| |![XT](https://raw.githubusercontent.com/JKorf/XT.Net/refs/heads/main/XT.Net/Icon/icon.png)|XT|CEX|[JKorf/XT.Net](https://github.com/JKorf/XT.Net)|[![Nuget version](https://img.shields.io/nuget/v/XT.net.svg?style=flat-square)](https://www.nuget.org/packages/XT.Net)|[Link](https://www.xt.com/ru/accounts/register?ref=CZG39C)|25%|
@ -65,10 +66,17 @@ Make a one time donation in a crypto currency of your choice. If you prefer to d
Alternatively, sponsor me on Github using [Github Sponsors](https://github.com/sponsors/JKorf). Alternatively, sponsor me on Github using [Github Sponsors](https://github.com/sponsors/JKorf).
## Release notes ## Release notes
* Version 9.11.0 - 30 Oct 2025
* Added StaticLogger to LibraryHelpers, updated warning logging for converters to use StaticLogger
* Added client reference helper to LibraryHelpers
* Fixed exception when initial trade snapshot has no items in TradeTracker
* Version 9.10.0 - 15 Oct 2025 * Version 9.10.0 - 15 Oct 2025
* Added ITransferRestClient Shared interface * Added ITransferRestClient Shared interface
* Added ClientOrderId property to SharedUserTrade model
* Updated IBalanceRestClient, GetBalancesRequest now mainly works with SharedAccountType type, allowing more options * Updated IBalanceRestClient, GetBalancesRequest now mainly works with SharedAccountType type, allowing more options
* Updated IBalanceRestClient, GetBalanceOptions now specifies supported account types * Updated IBalanceRestClient, GetBalanceOptions now specifies supported account types
* Updated DateTimeConverter to work primarily with decimal instead of double to prevent some floating point issues
* Version 9.9.0 - 06 Oct 2025 * Version 9.9.0 - 06 Oct 2025
* Updated socket Subscription status handling * Updated socket Subscription status handling