mirror of
https://github.com/JKorf/CryptoExchange.Net.git
synced 2026-08-12 17:03:10 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0b7107401f | |||
| 06add65354 | |||
| 773d288497 | |||
| fd4e8da938 | |||
| 271743b669 | |||
| f4797caf37 | |||
| 62c9769c72 | |||
| 92d7bc1e2e | |||
| 99e4f96f63 | |||
| 94d8afe149 | |||
| 90ad59c63a | |||
| c2273edfaa | |||
| 236283f4dd |
@@ -6,7 +6,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1"></PackageReference>
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0"></PackageReference>
|
||||||
<PackageReference Include="Moq" Version="4.20.72" />
|
<PackageReference Include="Moq" Version="4.20.72" />
|
||||||
<PackageReference Include="NUnit" Version="4.2.2"></PackageReference>
|
<PackageReference Include="NUnit" Version="4.2.2"></PackageReference>
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0"></PackageReference>
|
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0"></PackageReference>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using CryptoExchange.Net.Objects;
|
using CryptoExchange.Net.Objects;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NUnit.Framework.Legacy;
|
using NUnit.Framework.Legacy;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
|
||||||
namespace CryptoExchange.Net.UnitTests
|
namespace CryptoExchange.Net.UnitTests
|
||||||
@@ -70,5 +71,20 @@ namespace CryptoExchange.Net.UnitTests
|
|||||||
var result = ExchangeHelpers.Normalize(input);
|
var result = ExchangeHelpers.Normalize(input);
|
||||||
Assert.That(expected == result.ToString(CultureInfo.InvariantCulture));
|
Assert.That(expected == result.ToString(CultureInfo.InvariantCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[TestCase("123", "BKR", 32, true, "BKRJK123")]
|
||||||
|
[TestCase("123", "BKR", 32, false, "123")]
|
||||||
|
[TestCase("123123123123123123123123123123", "BKR", 32, true, "123123123123123123123123123123")] // 30
|
||||||
|
[TestCase("12312312312312312312312312312", "BKR", 32, true, "12312312312312312312312312312")] // 27
|
||||||
|
[TestCase("123123123123123123123123123", "BKR", 32, true, "BKRJK123123123123123123123123123")] // 25
|
||||||
|
[TestCase(null, "BKR", 32, true, null)]
|
||||||
|
public void ApplyBrokerIdTests(string clientOrderId, string brokerId, int maxLength, bool allowValueAdjustement, string expected)
|
||||||
|
{
|
||||||
|
var result = LibraryHelpers.ApplyBrokerId(clientOrderId, brokerId, maxLength, allowValueAdjustement);
|
||||||
|
|
||||||
|
if (expected != null)
|
||||||
|
Assert.That(result, Is.EqualTo(expected));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net.Converters.SystemTextJson
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converter for comma seperated enum values
|
||||||
|
/// </summary>
|
||||||
|
public class CommaSplitEnumConverter<T> : JsonConverter<IEnumerable<T>> where T : Enum
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override IEnumerable<T>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return (reader.GetString()?.Split(',').Select(x => EnumConverter.ParseString<T>(x)).ToArray() ?? new T[0])!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Write(Utf8JsonWriter writer, IEnumerable<T> value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(string.Join(",", value.Select(x => EnumConverter.GetString(x))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net.Converters.SystemTextJson
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Attribute for allowing specifying a JsonConverter with constructor parameters
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
|
public class JsonConverterCtorAttribute<T> : JsonConverterAttribute where T : JsonConverter
|
||||||
|
{
|
||||||
|
private readonly object[] _parameters;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
public JsonConverterCtorAttribute(params object[] parameters) => _parameters = parameters;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override JsonConverter CreateConverter(Type typeToConvert)
|
||||||
|
{
|
||||||
|
return (T)Activator.CreateInstance(typeof(T), _parameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net.Converters.SystemTextJson
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Replace a value on a string property
|
||||||
|
/// </summary>
|
||||||
|
public class ReplaceConverter : JsonConverter<string>
|
||||||
|
{
|
||||||
|
private readonly (string ValueToReplace, string ValueToReplaceWith)[] _replacementSets;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
public ReplaceConverter(params string[] replaceSets)
|
||||||
|
{
|
||||||
|
_replacementSets = replaceSets.Select(x =>
|
||||||
|
{
|
||||||
|
var split = x.Split(new string[] { "->" }, StringSplitOptions.None);
|
||||||
|
if (split.Length != 2)
|
||||||
|
throw new ArgumentException("Invalid replacement config");
|
||||||
|
return (split[0], split[1]);
|
||||||
|
}).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var value = reader.GetString();
|
||||||
|
foreach (var set in _replacementSets)
|
||||||
|
value = value?.Replace(set.ValueToReplace, set.ValueToReplaceWith);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) => writer.WriteStringValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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>8.3.0</PackageVersion>
|
<PackageVersion>8.4.3</PackageVersion>
|
||||||
<AssemblyVersion>8.3.0</AssemblyVersion>
|
<AssemblyVersion>8.4.3</AssemblyVersion>
|
||||||
<FileVersion>8.3.0</FileVersion>
|
<FileVersion>8.4.3</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</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</PackageTags>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<PackageReleaseNotes>https://github.com/JKorf/CryptoExchange.Net?tab=readme-ov-file#release-notes</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/JKorf/CryptoExchange.Net?tab=readme-ov-file#release-notes</PackageReleaseNotes>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<LangVersion>10.0</LangVersion>
|
<LangVersion>12.0</LangVersion>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -48,17 +48,17 @@
|
|||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0">
|
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" />
|
||||||
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
<PackageReference Include="System.Text.Json" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Helpers for client libraries
|
||||||
|
/// </summary>
|
||||||
|
public static class LibraryHelpers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Client order id seperator
|
||||||
|
/// </summary>
|
||||||
|
public const string ClientOrderIdSeperator = "JK";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Apply broker id to a client order id
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientOrderId"></param>
|
||||||
|
/// <param name="brokerId"></param>
|
||||||
|
/// <param name="maxLength"></param>
|
||||||
|
/// <param name="allowValueAdjustement"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string ApplyBrokerId(string? clientOrderId, string brokerId, int maxLength, bool allowValueAdjustement)
|
||||||
|
{
|
||||||
|
var reservedLength = brokerId.Length + ClientOrderIdSeperator.Length;
|
||||||
|
|
||||||
|
if ((clientOrderId?.Length + reservedLength) > maxLength)
|
||||||
|
return clientOrderId!;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(clientOrderId))
|
||||||
|
{
|
||||||
|
if (allowValueAdjustement)
|
||||||
|
clientOrderId = brokerId + ClientOrderIdSeperator + clientOrderId;
|
||||||
|
|
||||||
|
return clientOrderId!;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clientOrderId = ExchangeHelpers.AppendRandomString(brokerId + ClientOrderIdSeperator, maxLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientOrderId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
using CryptoExchange.Net.Authentication;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net.Objects.Options
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Library options
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TRestOptions"></typeparam>
|
||||||
|
/// <typeparam name="TSocketOptions"></typeparam>
|
||||||
|
/// <typeparam name="TApiCredentials"></typeparam>
|
||||||
|
/// <typeparam name="TEnvironment"></typeparam>
|
||||||
|
public class LibraryOptions<TRestOptions, TSocketOptions, TApiCredentials, TEnvironment>
|
||||||
|
where TRestOptions: RestExchangeOptions, new()
|
||||||
|
where TSocketOptions: SocketExchangeOptions, new()
|
||||||
|
where TApiCredentials: ApiCredentials
|
||||||
|
where TEnvironment: TradeEnvironment
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Rest client options
|
||||||
|
/// </summary>
|
||||||
|
public TRestOptions Rest { get; set; } = new TRestOptions();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Socket client options
|
||||||
|
/// </summary>
|
||||||
|
public TSocketOptions Socket { get; set; } = new TSocketOptions();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Trade environment. Contains info about URL's to use to connect to the API.
|
||||||
|
/// </summary>
|
||||||
|
public TEnvironment? Environment { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The api credentials used for signing requests.
|
||||||
|
/// </summary>
|
||||||
|
public TApiCredentials? ApiCredentials { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The DI service lifetime for the socket client
|
||||||
|
/// </summary>
|
||||||
|
public ServiceLifetime? SocketClientLifeTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copy values from these options to the target options
|
||||||
|
/// </summary>
|
||||||
|
public T Set<T>(T targetOptions) where T: LibraryOptions<TRestOptions, TSocketOptions, TApiCredentials, TEnvironment>
|
||||||
|
{
|
||||||
|
targetOptions.ApiCredentials = ApiCredentials;
|
||||||
|
targetOptions.Environment = Environment;
|
||||||
|
targetOptions.SocketClientLifeTime = SocketClientLifeTime;
|
||||||
|
targetOptions.Rest = Rest.Set(targetOptions.Rest);
|
||||||
|
targetOptions.Socket = Socket.Set(targetOptions.Socket);
|
||||||
|
|
||||||
|
return targetOptions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net.SharedApis
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Client for requesting user trading fees
|
||||||
|
/// </summary>
|
||||||
|
public interface IFeeRestClient : ISharedClient
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Fee request options
|
||||||
|
/// </summary>
|
||||||
|
EndpointOptions<GetFeeRequest> GetFeeOptions { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get trading fees for a symbol
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">Request info</param>
|
||||||
|
/// <param name="ct">Cancellation token</param>
|
||||||
|
Task<ExchangeWebResult<SharedFee>> GetFeesAsync(GetFeeRequest request, CancellationToken ct = default);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public GetClosedOrdersOptions(SharedPaginationSupport paginationType, bool timeFilterSupported) : base(paginationType, true)
|
public GetClosedOrdersOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit) : base(paginationType, timeFilterSupported, maxLimit, true)
|
||||||
{
|
{
|
||||||
TimeFilterSupported = timeFilterSupported;
|
TimeFilterSupported = timeFilterSupported;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public GetDepositsOptions(SharedPaginationSupport paginationType, bool timeFilterSupported) : base(paginationType, true)
|
public GetDepositsOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit) : base(paginationType, timeFilterSupported, maxLimit, true)
|
||||||
{
|
{
|
||||||
TimeFilterSupported = timeFilterSupported;
|
TimeFilterSupported = timeFilterSupported;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public GetFundingRateHistoryOptions(SharedPaginationSupport paginationType, bool needsAuthentication) : base(paginationType, needsAuthentication)
|
public GetFundingRateHistoryOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit, bool needsAuthentication) : base(paginationType, timeFilterSupported, maxLimit, needsAuthentication)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,10 +20,6 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int? MaxTotalDataPoints { get; set; }
|
public int? MaxTotalDataPoints { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Max number of data points which can be requested in a single request
|
|
||||||
/// </summary>
|
|
||||||
public int? MaxRequestDataPoints { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// The max age of the data that can be requested
|
/// The max age of the data that can be requested
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TimeSpan? MaxAge { get; set; }
|
public TimeSpan? MaxAge { get; set; }
|
||||||
@@ -31,14 +27,13 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public GetKlinesOptions(SharedPaginationSupport paginationType, bool needsAuthentication) : base(paginationType, needsAuthentication)
|
public GetKlinesOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit, bool needsAuthentication) : base(paginationType, timeFilterSupported, maxLimit, needsAuthentication)
|
||||||
{
|
{
|
||||||
SupportIntervals = new[]
|
SupportIntervals = new[]
|
||||||
{
|
{
|
||||||
SharedKlineInterval.FiveMinutes,
|
SharedKlineInterval.FiveMinutes,
|
||||||
SharedKlineInterval.FifteenMinutes,
|
SharedKlineInterval.FifteenMinutes,
|
||||||
SharedKlineInterval.OneHour,
|
SharedKlineInterval.OneHour,
|
||||||
SharedKlineInterval.FifteenMinutes,
|
|
||||||
SharedKlineInterval.OneDay,
|
SharedKlineInterval.OneDay,
|
||||||
SharedKlineInterval.OneWeek,
|
SharedKlineInterval.OneWeek,
|
||||||
SharedKlineInterval.OneMonth
|
SharedKlineInterval.OneMonth
|
||||||
@@ -48,7 +43,7 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public GetKlinesOptions(SharedPaginationSupport paginationType, bool needsAuthentication, params SharedKlineInterval[] intervals) : base(paginationType, needsAuthentication)
|
public GetKlinesOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit, bool needsAuthentication, params SharedKlineInterval[] intervals) : base(paginationType, timeFilterSupported, maxLimit, needsAuthentication)
|
||||||
{
|
{
|
||||||
SupportIntervals = intervals;
|
SupportIntervals = intervals;
|
||||||
}
|
}
|
||||||
@@ -69,8 +64,8 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
if (MaxAge.HasValue && request.StartTime < DateTime.UtcNow.Add(-MaxAge.Value))
|
if (MaxAge.HasValue && request.StartTime < DateTime.UtcNow.Add(-MaxAge.Value))
|
||||||
return new ArgumentError($"Only the most recent {MaxAge} klines are available");
|
return new ArgumentError($"Only the most recent {MaxAge} klines are available");
|
||||||
|
|
||||||
if (MaxRequestDataPoints.HasValue && request.Limit > MaxRequestDataPoints.Value)
|
if (request.Limit > MaxLimit)
|
||||||
return new ArgumentError($"Only {MaxRequestDataPoints} klines can be retrieved per request");
|
return new ArgumentError($"Only {MaxLimit} klines can be retrieved per request");
|
||||||
|
|
||||||
if (MaxTotalDataPoints.HasValue)
|
if (MaxTotalDataPoints.HasValue)
|
||||||
{
|
{
|
||||||
@@ -96,8 +91,6 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
sb.AppendLine($"Max age of data: {MaxAge}");
|
sb.AppendLine($"Max age of data: {MaxAge}");
|
||||||
if (MaxTotalDataPoints != null)
|
if (MaxTotalDataPoints != null)
|
||||||
sb.AppendLine($"Max total data points available: {MaxTotalDataPoints}");
|
sb.AppendLine($"Max total data points available: {MaxTotalDataPoints}");
|
||||||
if (MaxRequestDataPoints != null)
|
|
||||||
sb.AppendLine($"Max data points per request: {MaxRequestDataPoints}");
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public GetPositionHistoryOptions(SharedPaginationSupport paginationType) : base(paginationType, true)
|
public GetPositionHistoryOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit) : base(paginationType, timeFilterSupported, maxLimit, true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public GetTradeHistoryOptions(SharedPaginationSupport paginationType, bool needsAuthentication) : base(paginationType, needsAuthentication)
|
public GetTradeHistoryOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit, bool needsAuthentication) : base(paginationType, timeFilterSupported, maxLimit, needsAuthentication)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public GetWithdrawalsOptions(SharedPaginationSupport paginationType, bool timeFilterSupported) : base(paginationType, true)
|
public GetWithdrawalsOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit) : base(paginationType, timeFilterSupported, maxLimit, true)
|
||||||
{
|
{
|
||||||
TimeFilterSupported = timeFilterSupported;
|
TimeFilterSupported = timeFilterSupported;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Text;
|
using CryptoExchange.Net.Objects;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace CryptoExchange.Net.SharedApis
|
namespace CryptoExchange.Net.SharedApis
|
||||||
{
|
{
|
||||||
@@ -13,12 +14,24 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public SharedPaginationSupport PaginationSupport { get; }
|
public SharedPaginationSupport PaginationSupport { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether filtering based on start/end time is supported
|
||||||
|
/// </summary>
|
||||||
|
public bool TimePeriodFilterSupport { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max amount of results that can be requested
|
||||||
|
/// </summary>
|
||||||
|
public int MaxLimit { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PaginatedEndpointOptions(SharedPaginationSupport paginationType, bool needsAuthentication) : base(needsAuthentication)
|
public PaginatedEndpointOptions(SharedPaginationSupport paginationType, bool timePeriodSupport, int maxLimit, bool needsAuthentication) : base(needsAuthentication)
|
||||||
{
|
{
|
||||||
PaginationSupport = paginationType;
|
PaginationSupport = paginationType;
|
||||||
|
TimePeriodFilterSupport = timePeriodSupport;
|
||||||
|
MaxLimit = maxLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -26,6 +39,8 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
{
|
{
|
||||||
var sb = new StringBuilder(base.ToString(exchange));
|
var sb = new StringBuilder(base.ToString(exchange));
|
||||||
sb.AppendLine($"Pagination type: {PaginationSupport}");
|
sb.AppendLine($"Pagination type: {PaginationSupport}");
|
||||||
|
sb.AppendLine($"Time period filter support: {TimePeriodFilterSupport}");
|
||||||
|
sb.AppendLine($"Max limit: {MaxLimit}");
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net.SharedApis
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Request to retrieve trading fees
|
||||||
|
/// </summary>
|
||||||
|
public record GetFeeRequest : SharedSymbolRequest
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="symbol">Symbol to retrieve fees for</param>
|
||||||
|
/// <param name="exchangeParameters">Exchange specific parameters</param>
|
||||||
|
public GetFeeRequest(SharedSymbol symbol, ExchangeParameters? exchangeParameters = null) : base(symbol, exchangeParameters)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net.SharedApis
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Trading fee info
|
||||||
|
/// </summary>
|
||||||
|
public record SharedFee
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Taker fee percentage
|
||||||
|
/// </summary>
|
||||||
|
public decimal TakerFee { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Maker fee percentage
|
||||||
|
/// </summary>
|
||||||
|
public decimal MakerFee { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
public SharedFee(decimal makerFee, decimal takerFee)
|
||||||
|
{
|
||||||
|
MakerFee = makerFee;
|
||||||
|
TakerFee = takerFee;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -224,6 +224,9 @@ namespace CryptoExchange.Net.Testing.Comparers
|
|||||||
else if (propertyValue.GetType().GetInterfaces().Contains(typeof(IEnumerable))
|
else if (propertyValue.GetType().GetInterfaces().Contains(typeof(IEnumerable))
|
||||||
&& propertyValue.GetType() != typeof(string))
|
&& propertyValue.GetType() != typeof(string))
|
||||||
{
|
{
|
||||||
|
if (propValue.Type != JTokenType.Array)
|
||||||
|
return;
|
||||||
|
|
||||||
var jObjs = (JArray)propValue;
|
var jObjs = (JArray)propValue;
|
||||||
var list = (IEnumerable)propertyValue;
|
var list = (IEnumerable)propertyValue;
|
||||||
var enumerator = list.GetEnumerator();
|
var enumerator = list.GetEnumerator();
|
||||||
@@ -372,7 +375,8 @@ namespace CryptoExchange.Net.Testing.Comparers
|
|||||||
}
|
}
|
||||||
else if (objectValue is DateTime time)
|
else if (objectValue is DateTime time)
|
||||||
{
|
{
|
||||||
if (time != DateTimeConverter.ParseFromString(jsonValue.Value<string>()!))
|
var jsonStr = jsonValue.Value<string>()!;
|
||||||
|
if (!string.IsNullOrEmpty(jsonStr) && time != DateTimeConverter.ParseFromString(jsonStr))
|
||||||
throw new Exception($"{method}: {property} not equal: {jsonValue.Value<string>()} vs {time}");
|
throw new Exception($"{method}: {property} not equal: {jsonValue.Value<string>()} vs {time}");
|
||||||
}
|
}
|
||||||
else if (objectValue is bool bl)
|
else if (objectValue is bool bl)
|
||||||
|
|||||||
@@ -173,17 +173,20 @@ namespace CryptoExchange.Net.Testing
|
|||||||
|
|
||||||
foreach (var clientInterface in clientInterfaces)
|
foreach (var clientInterface in clientInterfaces)
|
||||||
{
|
{
|
||||||
var implementation = assembly.GetTypes().Single(t => clientInterface.IsAssignableFrom(t) && t != clientInterface);
|
var implementations = assembly.GetTypes().Where(t => clientInterface.IsAssignableFrom(t) && t != clientInterface);
|
||||||
int methods = 0;
|
foreach (var implementation in implementations)
|
||||||
foreach (var method in implementation.GetMethods().Where(m => implementationTypes.IsAssignableFrom(m.ReturnType)))
|
|
||||||
{
|
{
|
||||||
var interfaceMethod = clientInterface.GetMethod(method.Name, method.GetParameters().Select(p => p.ParameterType).ToArray());
|
int methods = 0;
|
||||||
if (interfaceMethod == null)
|
foreach (var method in implementation.GetMethods().Where(m => implementationTypes.IsAssignableFrom(m.ReturnType)))
|
||||||
throw new Exception($"Missing interface for method {method.Name} in {implementation.Name} implementing interface {clientInterface.Name}");
|
{
|
||||||
methods++;
|
var interfaceMethod = clientInterface.GetMethod(method.Name, method.GetParameters().Select(p => p.ParameterType).ToArray());
|
||||||
}
|
if (interfaceMethod == null)
|
||||||
|
throw new Exception($"Missing interface for method {method.Name} in {implementation.Name} implementing interface {clientInterface.Name}");
|
||||||
|
methods++;
|
||||||
|
}
|
||||||
|
|
||||||
Debug.WriteLine($"{clientInterface.Name} {methods} methods validated");
|
Debug.WriteLine($"{clientInterface.Name} {methods} methods validated");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace CryptoExchange.Net.Trackers.Klines
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The internal data structure
|
/// The internal data structure
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly Dictionary<DateTime, SharedKline> _data = new Dictionary<DateTime, SharedKline>();
|
protected readonly SortedDictionary<DateTime, SharedKline> _data = new SortedDictionary<DateTime, SharedKline>();
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The pre-snapshot queue buffering updates received before the snapshot is set and which will be applied after the snapshot was set
|
/// The pre-snapshot queue buffering updates received before the snapshot is set and which will be applied after the snapshot was set
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -229,7 +229,7 @@ namespace CryptoExchange.Net.Trackers.Klines
|
|||||||
if (_restClient.GetKlinesOptions.MaxAge != null && DateTime.UtcNow.Add(-_restClient.GetKlinesOptions.MaxAge.Value) > startTime)
|
if (_restClient.GetKlinesOptions.MaxAge != null && DateTime.UtcNow.Add(-_restClient.GetKlinesOptions.MaxAge.Value) > startTime)
|
||||||
startTime = DateTime.UtcNow.Add(-_restClient.GetKlinesOptions.MaxAge.Value);
|
startTime = DateTime.UtcNow.Add(-_restClient.GetKlinesOptions.MaxAge.Value);
|
||||||
|
|
||||||
var limit = Math.Min(_restClient.GetKlinesOptions.MaxRequestDataPoints ?? _restClient.GetKlinesOptions.MaxTotalDataPoints ?? 100, 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);
|
||||||
var data = new List<SharedKline>();
|
var data = new List<SharedKline>();
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
{
|
{
|
||||||
|
// Options section, select this section during DI registration using Configuration.GetSection("ExchangeApiOptions")
|
||||||
"ExchangeApiOptions": {
|
"ExchangeApiOptions": {
|
||||||
|
// API credentials for both REST and Websocket client
|
||||||
"ApiCredentials": {
|
"ApiCredentials": {
|
||||||
"Key": "APIKEY",
|
"Key": "APIKEY",
|
||||||
"Secret": "SECRET"
|
"Secret": "SECRET",
|
||||||
|
"PassPhrase": "Phrase" // Optional passphrase for exchanges which need it
|
||||||
},
|
},
|
||||||
|
// Set the environment by name
|
||||||
"Environment": {
|
"Environment": {
|
||||||
"name": "live"
|
"name": "live"
|
||||||
},
|
},
|
||||||
"Rest":{
|
// REST client options
|
||||||
|
"Rest": {
|
||||||
"RequestTimeout": "00:00:20",
|
"RequestTimeout": "00:00:20",
|
||||||
"CachingEnabled": true,
|
"CachingEnabled": true,
|
||||||
"OutputOriginalData": true,
|
"OutputOriginalData": true,
|
||||||
@@ -18,7 +23,8 @@
|
|||||||
"Password": "Pass"
|
"Password": "Pass"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Socket":{
|
// Socket client options
|
||||||
|
"Socket": {
|
||||||
"RequestTimeout": "00:00:05",
|
"RequestTimeout": "00:00:05",
|
||||||
"SocketSubscriptionsCombineTarget": 15
|
"SocketSubscriptionsCombineTarget": 15
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,24 @@ 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 8.4.3 - 03 Dec 2024
|
||||||
|
* Fixed KlineTracker update handling
|
||||||
|
|
||||||
|
* Version 8.4.2 - 02 Dec 2024
|
||||||
|
* Removed special characters in ClientOrderIdSeperator to adhere to field content rules
|
||||||
|
|
||||||
|
* Version 8.4.1 - 02 Dec 2024
|
||||||
|
* Added JsonConverterCtorAttribute to allow specifying a custom JsonConverter with constructor parameters on properties
|
||||||
|
* Added ReplaceConverter System.Text.Json converter
|
||||||
|
* Added LibraryHelpers class for internal helper methods
|
||||||
|
|
||||||
|
* Version 8.4.0 - 28 Nov 2024
|
||||||
|
* Added GetFeesAsync Shared REST client support
|
||||||
|
* Added LibraryOptions base class
|
||||||
|
* Added CommaSplitEnumConverter System.Text.Json converter
|
||||||
|
* Added TimePeriodFilterSupport and MaxLimit properties to PaginatedEndpointOptions
|
||||||
|
* Updated package dependency versions
|
||||||
|
|
||||||
* Version 8.3.0 - 19 Nov 2024
|
* Version 8.3.0 - 19 Nov 2024
|
||||||
* Added support for IOptions injection, allowing options to be read from IConfiguration
|
* Added support for IOptions injection, allowing options to be read from IConfiguration
|
||||||
* Added handling of Infinity values in decimal converter
|
* Added handling of Infinity values in decimal converter
|
||||||
|
|||||||
Reference in New Issue
Block a user