1
0
mirror of https://github.com/JKorf/CryptoExchange.Net.git synced 2026-11-06 10:17:55 +00:00

Shared API's V2 (#289)

Added V2 Shared API's infrastructure
Added request coalescing
Added rate limiting admission logic
Fixed some rate limiting issues
This commit is contained in:
Jan Korf
2026-09-23 09:15:39 +02:00
committed by GitHub
parent cecfbcba48
commit ae7504c26c
482 changed files with 12870 additions and 2150 deletions
@@ -0,0 +1,108 @@
using CryptoExchange.Net.Objects;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting a transfer
/// </summary>
public class GetBalancesOptions : CapabilityOptions<GetBalancesRequest, IGetBalances>
{
/// <inheritdoc />
public override string Description => "Retrieve account balances";
private static readonly RequestParameterDescription[] _defaultParameterRules = new[]
{
RequestParameterRule<GetBalancesRequest>.Optional(x => x.TradingMode, "Filter the balances by trading mode", TradingMode.Spot),
RequestParameterRule<GetBalancesRequest>.Optional(x => x.AccountType, "The account type to retrieve balances for", SharedAccountType.Spot),
};
/// <summary>
/// Supported account types
/// </summary>
public AccountTypeFilter[] SupportedAccountTypes { get; set; }
/// <summary>
/// ctor
/// </summary>
public GetBalancesOptions(string exchange, params AccountTypeFilter[] accountTypes) : base(exchange, true, nameof(IGetBalancesRest.GetBalancesAsync), _defaultParameterRules)
{
SupportedAccountTypes = accountTypes;
}
/// <summary>
/// Validate a request
/// </summary>
public override Error? ValidateRequest(
GetBalancesRequest request,
IGetBalances client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.AccountType != null && !IsValid(request.AccountType.Value))
return ArgumentError.Invalid(nameof(request.AccountType), "Invalid AccountType");
return error;
}
/// <summary>
/// Is the account type valid for this client
/// </summary>
/// <param name="accountType"></param>
/// <returns></returns>
public bool IsValid(SharedAccountType accountType)
{
if (accountType == SharedAccountType.Funding)
return SupportedAccountTypes.Contains(AccountTypeFilter.Funding);
if (accountType == SharedAccountType.Spot)
return SupportedAccountTypes.Contains(AccountTypeFilter.Spot);
if (accountType == SharedAccountType.PerpetualLinearFutures
|| accountType == SharedAccountType.PerpetualInverseFutures
|| accountType == SharedAccountType.DeliveryLinearFutures
|| accountType == SharedAccountType.DeliveryInverseFutures)
{
return SupportedAccountTypes.Contains(AccountTypeFilter.Futures);
}
if (accountType == SharedAccountType.CrossMargin
|| accountType == SharedAccountType.IsolatedMargin)
{
return SupportedAccountTypes.Contains(AccountTypeFilter.Margin);
}
return SupportedAccountTypes.Contains(AccountTypeFilter.Option);
}
}
/// <summary>
/// Account type filter
/// </summary>
public enum AccountTypeFilter
{
/// <summary>
/// Funding account
/// </summary>
Funding,
/// <summary>
/// Spot account
/// </summary>
Spot,
/// <summary>
/// Futures account
/// </summary>
Futures,
/// <summary>
/// Margin account
/// </summary>
Margin,
/// <summary>
/// Option account
/// </summary>
Option
}
}
@@ -0,0 +1,35 @@
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to retrieve balance info for the user
/// </summary>
public record GetBalancesRequest : SharedRequest
{
/// <summary>
/// Account type
/// </summary>
public SharedAccountType? AccountType { get; set; }
/// <summary>
/// ctor
/// </summary>
/// <param name="tradingMode">Trading mode</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetBalancesRequest(TradingMode tradingMode, ExchangeParameters? exchangeParameters = null)
: base(tradingMode, exchangeParameters)
{
AccountType = tradingMode.ToAccountType();
}
/// <summary>
/// ctor
/// </summary>
/// <param name="accountType">Account type</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetBalancesRequest(SharedAccountType? accountType = null, ExchangeParameters? exchangeParameters = null)
: base(null, exchangeParameters)
{
AccountType = accountType;
}
}
}
@@ -0,0 +1,39 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Operation for retrieving user asset balances from an exchange.
/// </summary>
public interface IGetBalances : ISharedApiCapability
{
/// <summary>
/// Balances request options.<br />
/// Use <see cref="CapabilityOptions.RequestParameterRules"/> and <see cref="CapabilityOptions.ExchangeParameterRules"/> to check for required and optional parameters for the request. <br />
/// Exchange specific parameters can be added to the request via the `ExchangeParameters` property of the request object.
/// </summary>
GetBalancesOptions GetBalancesOptions { get; }
/// <summary>
/// Get balances for the user, see <see cref="GetBalancesOptions"/> for request options and exchange specific required/optional parameters. <br />
/// </summary>
/// <param name="request">Request info</param>
/// <param name="ct">Cancellation token</param>
/// <returns></returns>
Task<IExchangeCallResult<SharedBalance[]>> GetBalancesAsync(GetBalancesRequest request, CancellationToken ct = default);
}
/// <summary>
/// Operation for retrieving user asset balances from an exchange via the REST API.
/// </summary>
public interface IGetBalancesRest : IGetBalances, ISharedRest
{
/// <inheritdoc />
new Task<HttpResult<SharedBalance[]>> GetBalancesAsync(GetBalancesRequest request, CancellationToken ct = default);
}
}
@@ -0,0 +1,28 @@
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Sockets;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Operation for subscribing to user balance updates
/// </summary>
public interface ISubscribeBalancesSocket : ISharedSubscription
{
/// <summary>
/// Balance subscription options
/// </summary>
SubscribeBalanceOptions SubscribeBalanceOptions { get; }
/// <summary>
/// Subscribe to user balance updates
/// </summary>
/// <param name="request">Request info</param>
/// <param name="handler">Update handler</param>
/// <param name="ct">Cancellation token, can be used to stop the updates</param>
/// <returns></returns>
Task<WebSocketResult<UpdateSubscription>> SubscribeToBalanceUpdatesAsync(SubscribeBalancesRequest request, Action<DataEvent<SharedBalance[]>> handler, CancellationToken ct = default);
}
}
@@ -0,0 +1,27 @@
using CryptoExchange.Net.Objects;
using System;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for subscribing to balance updates
/// </summary>
public class SubscribeBalanceOptions : CapabilityOptions<SubscribeBalancesRequest, ISubscribeBalancesSocket>
{
/// <inheritdoc />
public override string Description => "Subscribe to balance updates";
private static readonly RequestParameterDescription[] _defaultParameterRules = new[]
{
RequestParameterRule<SubscribeBalancesRequest>.Optional(x => x.TradingMode, "Filter balance updates by trading mode", TradingMode.Spot),
};
/// <summary>
/// ctor
/// </summary>
public SubscribeBalanceOptions(string exchange, bool needsAuthentication) : base(exchange, needsAuthentication, nameof(ISubscribeBalancesSocket.SubscribeToBalanceUpdatesAsync), _defaultParameterRules)
{
}
}
}
@@ -0,0 +1,18 @@
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to subscribe to balance updates
/// </summary>
public record SubscribeBalancesRequest: SharedRequest
{
/// <summary>
/// ctor
/// </summary>
/// <param name="tradingMode">Trading mode</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public SubscribeBalancesRequest(TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null)
: base(tradingMode, exchangeParameters)
{
}
}
}