mirror of
https://github.com/JKorf/CryptoExchange.Net.git
synced 2026-08-12 17:03:10 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 271743b669 | |||
| f4797caf37 | |||
| 62c9769c72 | |||
| 92d7bc1e2e | |||
| 99e4f96f63 | |||
| 94d8afe149 | |||
| 90ad59c63a | |||
| c2273edfaa | |||
| 236283f4dd |
@@ -6,7 +6,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<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="NUnit" Version="4.2.2"></PackageReference>
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0"></PackageReference>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using CryptoExchange.Net.Objects;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Legacy;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
|
||||
namespace CryptoExchange.Net.UnitTests
|
||||
@@ -70,5 +71,20 @@ namespace CryptoExchange.Net.UnitTests
|
||||
var result = ExchangeHelpers.Normalize(input);
|
||||
Assert.That(expected == result.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("123", "BKR", 32, true, "BKR|JK|123")]
|
||||
[TestCase("123", "BKR", 32, false, "123")]
|
||||
[TestCase("123123123123123123123123123123", "BKR", 32, true, "123123123123123123123123123123")] // 30
|
||||
[TestCase("123123123123123123123123123", "BKR", 32, true, "123123123123123123123123123")] // 27
|
||||
[TestCase("1231231231231231231231231", "BKR", 32, true, "BKR|JK|1231231231231231231231231")] // 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>
|
||||
<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>
|
||||
<PackageVersion>8.3.0</PackageVersion>
|
||||
<AssemblyVersion>8.3.0</AssemblyVersion>
|
||||
<FileVersion>8.3.0</FileVersion>
|
||||
<PackageVersion>8.4.1</PackageVersion>
|
||||
<AssemblyVersion>8.4.1</AssemblyVersion>
|
||||
<FileVersion>8.4.1</FileVersion>
|
||||
<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>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
@@ -20,7 +20,7 @@
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<PackageReleaseNotes>https://github.com/JKorf/CryptoExchange.Net?tab=readme-ov-file#release-notes</PackageReleaseNotes>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<LangVersion>12.0</LangVersion>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
@@ -48,17 +48,17 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</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" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" />
|
||||
<PackageReference Include="System.Text.Json" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.0" />
|
||||
</ItemGroup>
|
||||
</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>
|
||||
/// ctor
|
||||
/// </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;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace CryptoExchange.Net.SharedApis
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </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;
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </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>
|
||||
public int? MaxTotalDataPoints { get; set; }
|
||||
/// <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
|
||||
/// </summary>
|
||||
public TimeSpan? MaxAge { get; set; }
|
||||
@@ -31,14 +27,13 @@ namespace CryptoExchange.Net.SharedApis
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </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[]
|
||||
{
|
||||
SharedKlineInterval.FiveMinutes,
|
||||
SharedKlineInterval.FifteenMinutes,
|
||||
SharedKlineInterval.OneHour,
|
||||
SharedKlineInterval.FifteenMinutes,
|
||||
SharedKlineInterval.OneDay,
|
||||
SharedKlineInterval.OneWeek,
|
||||
SharedKlineInterval.OneMonth
|
||||
@@ -48,7 +43,7 @@ namespace CryptoExchange.Net.SharedApis
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </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;
|
||||
}
|
||||
@@ -69,8 +64,8 @@ namespace CryptoExchange.Net.SharedApis
|
||||
if (MaxAge.HasValue && request.StartTime < DateTime.UtcNow.Add(-MaxAge.Value))
|
||||
return new ArgumentError($"Only the most recent {MaxAge} klines are available");
|
||||
|
||||
if (MaxRequestDataPoints.HasValue && request.Limit > MaxRequestDataPoints.Value)
|
||||
return new ArgumentError($"Only {MaxRequestDataPoints} klines can be retrieved per request");
|
||||
if (request.Limit > MaxLimit)
|
||||
return new ArgumentError($"Only {MaxLimit} klines can be retrieved per request");
|
||||
|
||||
if (MaxTotalDataPoints.HasValue)
|
||||
{
|
||||
@@ -96,8 +91,6 @@ namespace CryptoExchange.Net.SharedApis
|
||||
sb.AppendLine($"Max age of data: {MaxAge}");
|
||||
if (MaxTotalDataPoints != null)
|
||||
sb.AppendLine($"Max total data points available: {MaxTotalDataPoints}");
|
||||
if (MaxRequestDataPoints != null)
|
||||
sb.AppendLine($"Max data points per request: {MaxRequestDataPoints}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </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>
|
||||
/// ctor
|
||||
/// </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>
|
||||
/// ctor
|
||||
/// </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;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Text;
|
||||
using CryptoExchange.Net.Objects;
|
||||
using System.Text;
|
||||
|
||||
namespace CryptoExchange.Net.SharedApis
|
||||
{
|
||||
@@ -13,12 +14,24 @@ namespace CryptoExchange.Net.SharedApis
|
||||
/// </summary>
|
||||
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>
|
||||
/// ctor
|
||||
/// </summary>
|
||||
public PaginatedEndpointOptions(SharedPaginationSupport paginationType, bool needsAuthentication) : base(needsAuthentication)
|
||||
public PaginatedEndpointOptions(SharedPaginationSupport paginationType, bool timePeriodSupport, int maxLimit, bool needsAuthentication) : base(needsAuthentication)
|
||||
{
|
||||
PaginationSupport = paginationType;
|
||||
TimePeriodFilterSupport = timePeriodSupport;
|
||||
MaxLimit = maxLimit;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -26,6 +39,8 @@ namespace CryptoExchange.Net.SharedApis
|
||||
{
|
||||
var sb = new StringBuilder(base.ToString(exchange));
|
||||
sb.AppendLine($"Pagination type: {PaginationSupport}");
|
||||
sb.AppendLine($"Time period filter support: {TimePeriodFilterSupport}");
|
||||
sb.AppendLine($"Max limit: {MaxLimit}");
|
||||
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))
|
||||
&& propertyValue.GetType() != typeof(string))
|
||||
{
|
||||
if (propValue.Type != JTokenType.Array)
|
||||
return;
|
||||
|
||||
var jObjs = (JArray)propValue;
|
||||
var list = (IEnumerable)propertyValue;
|
||||
var enumerator = list.GetEnumerator();
|
||||
@@ -372,7 +375,8 @@ namespace CryptoExchange.Net.Testing.Comparers
|
||||
}
|
||||
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}");
|
||||
}
|
||||
else if (objectValue is bool bl)
|
||||
|
||||
@@ -173,17 +173,20 @@ namespace CryptoExchange.Net.Testing
|
||||
|
||||
foreach (var clientInterface in clientInterfaces)
|
||||
{
|
||||
var implementation = assembly.GetTypes().Single(t => clientInterface.IsAssignableFrom(t) && t != clientInterface);
|
||||
int methods = 0;
|
||||
foreach (var method in implementation.GetMethods().Where(m => implementationTypes.IsAssignableFrom(m.ReturnType)))
|
||||
var implementations = assembly.GetTypes().Where(t => clientInterface.IsAssignableFrom(t) && t != clientInterface);
|
||||
foreach (var implementation in implementations)
|
||||
{
|
||||
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++;
|
||||
}
|
||||
int methods = 0;
|
||||
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());
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ namespace CryptoExchange.Net.Trackers.Klines
|
||||
if (_restClient.GetKlinesOptions.MaxAge != null && DateTime.UtcNow.Add(-_restClient.GetKlinesOptions.MaxAge.Value) > startTime)
|
||||
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 data = new List<SharedKline>();
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
{
|
||||
// Options section, select this section during DI registration using Configuration.GetSection("ExchangeApiOptions")
|
||||
"ExchangeApiOptions": {
|
||||
// API credentials for both REST and Websocket client
|
||||
"ApiCredentials": {
|
||||
"Key": "APIKEY",
|
||||
"Secret": "SECRET"
|
||||
"Secret": "SECRET",
|
||||
"PassPhrase": "Phrase" // Optional passphrase for exchanges which need it
|
||||
},
|
||||
// Set the environment by name
|
||||
"Environment": {
|
||||
"name": "live"
|
||||
},
|
||||
"Rest":{
|
||||
// REST client options
|
||||
"Rest": {
|
||||
"RequestTimeout": "00:00:20",
|
||||
"CachingEnabled": true,
|
||||
"OutputOriginalData": true,
|
||||
@@ -18,7 +23,8 @@
|
||||
"Password": "Pass"
|
||||
}
|
||||
},
|
||||
"Socket":{
|
||||
// Socket client options
|
||||
"Socket": {
|
||||
"RequestTimeout": "00:00:05",
|
||||
"SocketSubscriptionsCombineTarget": 15
|
||||
}
|
||||
|
||||
@@ -50,6 +50,18 @@ 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).
|
||||
|
||||
## Release notes
|
||||
* 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
|
||||
* Added support for IOptions injection, allowing options to be read from IConfiguration
|
||||
* Added handling of Infinity values in decimal converter
|
||||
|
||||
Reference in New Issue
Block a user