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

CryptoExchange V12 (#281)

* Result types:
  * (Web)CallResult types are replaced by HttpResult, WebSocketResult and QueryResult with the same logic
  * Updated result types to record type
  * Result creation can be done with (Http/WebSocket/Query)Result.Ok(..) and .Fail(..)
  * Removed implicit result type conversion to bool, `if (result)` no longer works, instead use `if (result.Success)`
  * Replaced CallResult.SuccessResult with CallResult.Ok()
  * Fixed result object nullability hinting, for example Data might be null if Success isn't checked for true

* Parameters & serialization:
  * Added support for `enabled` and `disabled` strings to bool converter
  * Removed ParameterCollection type, has been replaced by Parameters type
  * Removed ArraySerialization, OrderParameters and ParameterOrderComparer properties from RestApiClient, moved to ParameterSerializationsSettings
  * Updated RestRequestConfiguration in AuthenticationProvider.ProcessRequest to contain the full RequestDefinition instead of copied fields	

* Clients:
  * Updated Api client constructor logging parameter from ILogger to ILoggerFactory? 
  * Added Api client constructor exchange name parameter
  * Added ToString overrides on base API types
  * Added Exchange property on BaseApiClient
  * Added ApiCredentials property on IRestApiClient and ISocketApiClient interfaces
  * Updated ILogger source from client name to topic specific client name
  * Removed logging from client creation
  * Fixed BaseRestClient SetApiCredentials not marked as virtual

* Rest:
  * Added BaseAddress to RequestDefinition object
  * Updated RestApiClient AuthenticationProvider logic from private to protected and virtual
  * Removed RestApiClient.SendAsync baseAddress parameter removed
  * Removed RestApiClient.SendAsync without type parameter

* WebSocket:
  * Updated MessageRouting definition into CreateForEvent for subscriptions and CreateForQuery for queries
  * Improved Query type safety with CeateForQuery which allows second parameter for specifying the result type
  * Renamed MessageRouter.CreateWithoutHandler to CreateVoid
  * Updated SocketApiClient.GetSocketConnection to check connection uri instead of Tag for finding compatible connections
  * Removed unused UnhandledMessageExpected property SocketApiClient
  * Fixed issue in SocketApiClient.GetSocketConnection causing requests to always wait the full max 10 seconds when there was a reconnecting socket
	
* Shared APIs:
  * Updated Option definitions to always require the exchange name as first parameter
  * Added missing dedicated option types
  * Added Discover method on ISharedClient interface, returning info on supported capabilities and operations
  * Added SharedRequest GetParamValue helper method accepting multiple parameter names
  * Added ResetStaticExchangeParameters method on ExchangeParameters
  * Added Status property to SharedWithdrawal model
  * Added TradingModes property to SharedBalance model
  * Updated ExchangeSymbolCache to support multiple environments and additional key separation
  * Updated Shared ExchangeParameters parameter names to be case insensitive
  * Updated code comments
  * Replaced ExchangeResult with ExchangeCallResult type
  * Removed AsExchangeResult/ExchangeWebResult
  * Removed TradingMode from the response model, only maintained on models where it makes sense
  * Removed IListenKey support, listen keys now rely on internal management with TokenManager

* Rate limiting:
  * Fixed websocket connection attempts counting towards rate limit even when server could not be reached
  * Removed host from rate limit methods, now part of the already provided RequestDefinition
  * Added amount parameter to RateLimit Reset method to allow partially resetting the limit

* Added TokenManager implementation for automatic listenkey/token management
* Added UserClientProvider base class
* Added async streaming on UserDataTracker items with StreamUpdatesAsync
* Added cancellation token support to UserDataTracker starting
* Added Unit type for non-result types
* Added ServerError constructor taking ErrorType and message to make it easier to create
* Added SupportedEnvironments property to PlatformInfo
* Updated SymbolOrderBook DoResyncAsync to return CallResult instead of CallResult<bool> which was redundant
* Various small performance improvements
This commit is contained in:
Jan Korf
2026-06-29 10:38:09 +02:00
committed by GitHub
parent afb84a1bf0
commit e823114623
285 changed files with 7029 additions and 3658 deletions
@@ -1,4 +1,5 @@
using System;
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
@@ -10,25 +11,47 @@ namespace CryptoExchange.Net.SharedApis
/// </summary>
public class ExchangeParameters
{
private readonly List<ExchangeParameter> _parameters;
private readonly static List<ExchangeParameter> _staticParameters = new List<ExchangeParameter>();
private readonly static Dictionary<string, Parameters> _staticProcessParameters = new Dictionary<string, Parameters>();
private readonly Dictionary<string, Parameters> _processParameters;
/// <summary>
/// ctor
/// Create a new ExchangeParameters instance with the provided parameters set
/// </summary>
/// <param name="parameters">The parameters to add</param>
/// <param name="parameters">Exchange parameters</param>
public ExchangeParameters(params ExchangeParameter[] parameters)
{
_parameters = parameters.ToList();
_processParameters = new Dictionary<string, Parameters>();
foreach (var parameter in parameters)
AddValue(parameter.Exchange, parameter.Name, parameter.Value);
}
/// <summary>
/// Add a new parameter value
/// Add a process parameter. Process parameters are used to determine the correct logic to execute, but are not necessarily passed to the API.<br />
/// To directly add or override parameters which are passed to the API, use AddRawParameter or AddRawValue instead.
/// </summary>
/// <param name="exchangeParameter"></param>
/// <param name="exchangeParameter">The exchange parameter to add</param>
public void AddValue(ExchangeParameter exchangeParameter)
{
_parameters.Add(exchangeParameter);
AddValue(exchangeParameter.Exchange, exchangeParameter.Name, exchangeParameter.Value);
}
/// <summary>
/// Add a process parameter. Process parameters are used to determine the correct logic to execute, but are not necessarily passed to the API.<br />
/// To directly add or override parameters which are passed to the API, use AddRawParameter or AddRawValue instead.
/// </summary>
/// <param name="exchange">Exchange to apply the parameter for</param>
/// <param name="key">Parameter name</param>
/// <param name="value">Parameter value</param>
public void AddValue(string exchange, string key, object value)
{
if (!_processParameters.TryGetValue(exchange, out var exchangeParameters))
{
exchangeParameters = new Parameters(ParameterSerializationSettings.Default);
_processParameters[exchange] = exchangeParameters;
}
exchangeParameters.AddRaw(key, value);
}
/// <summary>
@@ -40,8 +63,8 @@ namespace CryptoExchange.Net.SharedApis
/// <returns></returns>
public bool HasValue(string exchange, string name, Type type)
{
var val = _parameters.SingleOrDefault(x => x.Exchange.Equals(exchange, StringComparison.InvariantCulture) && x.Name.Equals(name, StringComparison.InvariantCulture));
val ??= _staticParameters.SingleOrDefault(x => x.Exchange.Equals(exchange, StringComparison.InvariantCulture) && x.Name.Equals(name, StringComparison.InvariantCulture));
var val = TryGetValue(_processParameters, exchange, name);
val ??= TryGetValue(_staticProcessParameters, exchange, name);
if (val == null)
return false;
@@ -49,7 +72,7 @@ namespace CryptoExchange.Net.SharedApis
try
{
Type t = Nullable.GetUnderlyingType(type) ?? type;
Convert.ChangeType(val.Value, t);
Convert.ChangeType(val, t);
return true;
}
catch
@@ -59,7 +82,7 @@ namespace CryptoExchange.Net.SharedApis
}
/// <summary>
/// Check whether a specific parameter is provided in the default parameters or the provided instance
/// Check whether a specific process parameter is provided in the default parameters or the provided instance
/// </summary>
/// <param name="exchangeParameters">The provided exchange parameter in the request</param>
/// <param name="exchange">The exchange name</param>
@@ -72,14 +95,14 @@ namespace CryptoExchange.Net.SharedApis
if (provided == true)
return true;
var val = _staticParameters.SingleOrDefault(x => x.Exchange.Equals(exchange, StringComparison.InvariantCulture) && x.Name.Equals(name, StringComparison.InvariantCulture));
var val = TryGetValue(_staticProcessParameters, exchange, name);
if (val == null)
return false;
try
{
Type t = Nullable.GetUnderlyingType(type) ?? type;
Convert.ChangeType(val.Value, t);
Convert.ChangeType(val, t);
return true;
}
catch
@@ -96,17 +119,18 @@ namespace CryptoExchange.Net.SharedApis
/// <param name="name">Parameter name</param>
public T? GetValue<T>(string exchange, string name)
{
var val = _parameters.SingleOrDefault(x => x.Exchange.Equals(exchange, StringComparison.InvariantCulture) && x.Name.Equals(name, StringComparison.InvariantCulture));
var val = TryGetValue(_processParameters, exchange, name);
val ??= TryGetValue(_staticProcessParameters, exchange, name);
if (val == null)
return default;
if (val.Value is T typeVal)
if (val is T typeVal)
return typeVal;
try
{
Type t = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
return (T)Convert.ChangeType(val.Value, t);
return (T)Convert.ChangeType(val, t);
}
catch
{
@@ -123,32 +147,28 @@ namespace CryptoExchange.Net.SharedApis
/// <param name="name">Parameter name</param>
public static T? GetValue<T>(ExchangeParameters? exchangeParameters, string exchange, string name)
{
T? value;
if (exchangeParameters == null)
{
var parameter = _staticParameters.SingleOrDefault(x => x.Exchange.Equals(exchange, StringComparison.InvariantCulture) && x.Name.Equals(name, StringComparison.InvariantCulture));
if (parameter == null)
return default;
if (exchangeParameters != null) {
if (parameter.Value is T val)
return val;
try
{
Type t = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
return (T)Convert.ChangeType(parameter.Value, t);
}
catch
{
throw new ArgumentException("Incorrect type for parameter, expected " + typeof(T).Name, name);
}
}
else
{
value = exchangeParameters.GetValue<T>(exchange, name);
var provided = exchangeParameters.GetValue<T>(exchange, name);
if (provided != null)
return provided;
}
return value;
var val = TryGetValue(_staticProcessParameters, exchange, name);
if (val == null)
return default;
try
{
var type = typeof(T);
Type t = Nullable.GetUnderlyingType(type) ?? type;
var result = Convert.ChangeType(val, t);
return (T)result;
}
catch
{
throw new ArgumentException("Incorrect type for parameter, expected " + typeof(T).Name, name);
}
}
/// <summary>
@@ -159,14 +179,14 @@ namespace CryptoExchange.Net.SharedApis
/// <param name="value">Parameter value</param>
public static void SetStaticParameter(string exchange, string key, object value)
{
var existing = _staticParameters.SingleOrDefault(x => x.Exchange.Equals(exchange, StringComparison.InvariantCulture) && x.Name.Equals(key, StringComparison.InvariantCulture));
if (existing != null)
if (!_staticProcessParameters.TryGetValue(exchange, out var exchangeParameters))
{
existing.Value = value;
return;
}
exchangeParameters = new Parameters(ParameterSerializationSettings.Default);
_staticProcessParameters[exchange] = exchangeParameters;
}
_staticParameters.Add(new ExchangeParameter(exchange, key, value));
exchangeParameters.Remove(key);
exchangeParameters.AddRaw(key, value);
}
/// <summary>
@@ -174,7 +194,23 @@ namespace CryptoExchange.Net.SharedApis
/// </summary>
public static void ResetStaticParameters()
{
_staticParameters.Clear();
_staticProcessParameters.Clear();
}
/// <summary>
/// Reset the static parameters, clears all parameters for an exchange exchanges
/// </summary>
public static void ResetStaticExchangeParameters(string exchange)
{
_staticProcessParameters.Remove(exchange);
}
private static object? TryGetValue(Dictionary<string, Parameters> list, string exchange, string key)
{
if (!list.TryGetValue(exchange, out var exchangeParams))
return null;
return exchangeParams.SingleOrDefault(x => x.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase)).Value;
}
}
}
}
@@ -1,55 +0,0 @@
using CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// A CallResult from an exchange
/// </summary>
/// <typeparam name="T"></typeparam>
public class ExchangeResult<T> : CallResult<T>
{
/// <summary>
/// The exchange
/// </summary>
public string Exchange { get; }
/// <summary>
/// ctor
/// </summary>
public ExchangeResult(
string exchange,
Error error) :
base(error)
{
Exchange = exchange;
}
/// <summary>
/// ctor
/// </summary>
public ExchangeResult(
string exchange,
CallResult<T> result) :
base(
result.Data,
result.OriginalData,
result.Error)
{
Exchange = exchange;
}
/// <summary>
/// ctor
/// </summary>
public ExchangeResult(
string exchange,
T result) :
base(result, null, null)
{
Exchange = exchange;
}
/// <inheritdoc />
public override string ToString() => $"{Exchange} - " + base.ToString();
}
}
@@ -1,153 +0,0 @@
using CryptoExchange.Net.Objects;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// A WebCallResult from an exchange
/// </summary>
/// <typeparam name="T">The result type</typeparam>
public class ExchangeWebResult<T> : WebCallResult<T>
{
/// <summary>
/// The exchange
/// </summary>
public string Exchange { get; }
/// <summary>
/// The trade modes for which the result data is
/// </summary>
public TradingMode[]? DataTradeMode { get; }
/// <summary>
/// Next page request, can be passed to the next request on the same endpoint to get the next page
/// </summary>
public PageRequest? NextPageRequest { get; }
/// <summary>
/// ctor
/// </summary>
public ExchangeWebResult(
string exchange,
Error error) :
base(error)
{
Exchange = exchange;
}
/// <summary>
/// ctor
/// </summary>
public ExchangeWebResult(
string exchange,
TradingMode dataTradeMode,
WebCallResult<T> result,
PageRequest? nextPageToken = null) :
base(result.ResponseStatusCode,
result.HttpVersion,
result.ResponseHeaders,
result.ResponseTime,
result.ResponseLength,
result.OriginalData,
result.RequestId,
result.RequestUrl,
result.RequestBody,
result.RequestMethod,
result.RequestHeaders,
result.DataSource,
result.Data,
result.Error)
{
DataTradeMode = new[] { dataTradeMode };
Exchange = exchange;
NextPageRequest = nextPageToken;
}
/// <summary>
/// ctor
/// </summary>
public ExchangeWebResult(
string exchange,
TradingMode[]? dataTradeModes,
WebCallResult<T> result,
PageRequest? nextPageRequest = null) :
base(result.ResponseStatusCode,
result.HttpVersion,
result.ResponseHeaders,
result.ResponseTime,
result.ResponseLength,
result.OriginalData,
result.RequestId,
result.RequestUrl,
result.RequestBody,
result.RequestMethod,
result.RequestHeaders,
result.DataSource,
result.Data,
result.Error)
{
DataTradeMode = dataTradeModes;
Exchange = exchange;
NextPageRequest = nextPageRequest;
}
/// <summary>
/// Create a new result
/// </summary>
public ExchangeWebResult(
string exchange,
TradingMode[]? dataTradeModes,
HttpStatusCode? code,
Version? httpVersion,
HttpResponseHeaders? responseHeaders,
TimeSpan? responseTime,
long? responseLength,
string? originalData,
int? requestId,
string? requestUrl,
string? requestBody,
HttpMethod? requestMethod,
HttpRequestHeaders? requestHeaders,
ResultDataSource dataSource,
[AllowNull] T data,
Error? error,
PageRequest? nextPageToken = null) : base(
code,
httpVersion,
responseHeaders,
responseTime,
responseLength,
originalData,
requestId,
requestUrl,
requestBody,
requestMethod,
requestHeaders,
dataSource,
data,
error)
{
DataTradeMode = dataTradeModes;
Exchange = exchange;
NextPageRequest = nextPageToken;
}
/// <summary>
/// Copy the ExchangeWebResult to a new data type
/// </summary>
/// <typeparam name="K">The new type</typeparam>
/// <param name="data">The data of the new type</param>
/// <returns></returns>
public new ExchangeWebResult<K> As<K>([AllowNull] K data)
{
return new ExchangeWebResult<K>(Exchange, DataTradeMode, ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, data, Error, NextPageRequest);
}
/// <inheritdoc />
public override string ToString() => $"{Exchange} - " + base.ToString();
}
}
@@ -0,0 +1,15 @@
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for canceling a futures order by client order id
/// </summary>
public class CancelFuturesOrderByClientOrderIdOptions : EndpointOptions<CancelOrderRequest, IFuturesOrderClientIdRestClient>
{
/// <summary>
/// ctor
/// </summary>
public CancelFuturesOrderByClientOrderIdOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IFuturesOrderClientIdRestClient.CancelFuturesOrderByClientOrderIdAsync))
{
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for canceling a futures order
/// </summary>
public class CancelFuturesOrderOptions : EndpointOptions<CancelOrderRequest, IFuturesOrderRestClient>
{
/// <summary>
/// ctor
/// </summary>
public CancelFuturesOrderOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IFuturesOrderRestClient.CancelFuturesOrderAsync))
{
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for canceling a TP/SL
/// </summary>
public class CancelFuturesTpSlOptions : EndpointOptions<CancelTpSlRequest, IFuturesTpSlRestClient>
{
/// <summary>
/// ctor
/// </summary>
public CancelFuturesTpSlOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IFuturesTpSlRestClient.CancelFuturesTpSlAsync))
{
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for canceling spot trigger order
/// </summary>
public class CancelFuturesTriggerOrderOptions : EndpointOptions<CancelOrderRequest, IFuturesTriggerOrderRestClient>
{
/// <summary>
/// ctor
/// </summary>
public CancelFuturesTriggerOrderOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IFuturesTriggerOrderRestClient.CancelFuturesTriggerOrderAsync))
{
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for canceling a spot order
/// </summary>
public class CancelSpotOrderByClientOrderIdOptions : EndpointOptions<CancelOrderRequest, ISpotOrderClientIdRestClient>
{
/// <summary>
/// ctor
/// </summary>
public CancelSpotOrderByClientOrderIdOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(ISpotOrderClientIdRestClient.CancelSpotOrderByClientOrderIdAsync))
{
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for canceling a spot order
/// </summary>
public class CancelSpotOrderOptions : EndpointOptions<CancelOrderRequest, ISpotOrderRestClient>
{
/// <summary>
/// ctor
/// </summary>
public CancelSpotOrderOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(ISpotOrderRestClient.CancelSpotOrderAsync))
{
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting spot trigger order
/// </summary>
public class CancelSpotTriggerOrderOptions : EndpointOptions<CancelOrderRequest, ISpotTriggerOrderRestClient>
{
/// <summary>
/// ctor
/// </summary>
public CancelSpotTriggerOrderOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(ISpotTriggerOrderRestClient.CancelSpotTriggerOrderAsync))
{
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for closing position
/// </summary>
public class ClosePositionOptions : EndpointOptions<ClosePositionRequest, IFuturesOrderRestClient>
{
/// <summary>
/// ctor
/// </summary>
public ClosePositionOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IFuturesOrderRestClient.ClosePositionAsync))
{
}
}
}
@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Text;
namespace CryptoExchange.Net.SharedApis
@@ -10,22 +11,26 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for an exchange endpoint
/// </summary>
public class EndpointOptions
public abstract class EndpointOptions
{
/// <summary>
/// Required exchange-specific parameters
/// Required exchange-specific parameters. These can be provided via the `exchangeParameters` property of the request object.
/// </summary>
public List<ParameterDescription> RequiredExchangeParameters { get; set; } = new List<ParameterDescription>();
/// <summary>
/// Optional exchange-specific parameters
/// Optional exchange-specific parameters. These can be provided via the `exchangeParameters` property of the request object.
/// </summary>
public List<ParameterDescription> OptionalExchangeParameters { get; set; } = new List<ParameterDescription>();
/// <summary>
/// Exchange
/// </summary>
public string Exchange { get; set; }
/// <summary>
/// Endpoint name
/// </summary>
public string EndpointName { get; set; }
/// <summary>
/// Information on the specific exchange request
/// Exchange specific additional info
/// </summary>
public string? RequestNotes { get; set; }
/// <summary>
@@ -33,15 +38,16 @@ namespace CryptoExchange.Net.SharedApis
/// </summary>
public bool NeedsAuthentication { get; set; }
/// <summary>
/// Whether the call is supported by the exchange
/// Whether the call is supported. If false the exchange API does not support this operation.
/// </summary>
public bool Supported { get; set; } = true;
/// <summary>
/// ctor
/// </summary>
public EndpointOptions(string endpointName, bool needAuthentication)
public EndpointOptions(string exchange, string endpointName, bool needAuthentication)
{
Exchange = exchange;
EndpointName = endpointName;
NeedsAuthentication = needAuthentication;
}
@@ -49,62 +55,67 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Validate a request
/// </summary>
/// <param name="exchange">Exchange name</param>
/// <param name="exchangeParameters">Provided exchange parameters</param>
/// <param name="tradingMode">Request trading mode</param>
/// <param name="supportedTradingModes">Supported trading modes</param>
/// <returns></returns>
public virtual Error? ValidateRequest(string exchange, ExchangeParameters? exchangeParameters, TradingMode? tradingMode, TradingMode[] supportedTradingModes)
public virtual Error? ValidateRequest(ExchangeParameters? exchangeParameters, TradingMode? tradingMode, TradingMode[] supportedTradingModes)
{
if (!Supported)
return ArgumentError.Invalid("Endpoint", $"Endpoint {Exchange} {EndpointName} is not supported by the API");
if (tradingMode != null && !supportedTradingModes.Contains(tradingMode.Value))
return ArgumentError.Invalid("TradingMode", $"TradingMode.{tradingMode} is not supported, supported types: {string.Join(", ", supportedTradingModes)}");
foreach (var param in RequiredExchangeParameters)
{
if (!string.IsNullOrEmpty(param.Name))
{
if (ExchangeParameters.HasValue(exchangeParameters, exchange, param.Name!, param.ValueType) != true)
return ArgumentError.Invalid(param.Name!, $"Required exchange parameter `{param.Name}` for exchange `{exchange}` is missing or has incorrect type. Expected type is {param.ValueType.Name}. Example: {param.ExampleValue}");
}
else
{
if (param.Names!.All(x => ExchangeParameters.HasValue(exchangeParameters, exchange, x, param.ValueType) != true))
return ArgumentError.Invalid(string.Join("/", param.Names!), $"One of exchange parameters `{string.Join(", ", param.Names!)}` for exchange `{exchange}` should be provided. Example: {param.ExampleValue}");
}
{
if (param.Names!.All(x => ExchangeParameters.HasValue(exchangeParameters, Exchange, x, param.ValueType) != true))
return ArgumentError.Invalid(string.Join("/", param.Names!), $"One of exchange parameters `{string.Join(", ", param.Names!)}` for exchange `{Exchange}` should be provided. Example: {param.ExampleValue}");
}
return null;
}
/// <inheritdoc />
public virtual string ToString(string exchange)
{
if (!Supported)
return $"{exchange} {EndpointName} NOT SUPPORTED";
var sb = new StringBuilder();
sb.AppendLine($"{exchange} {EndpointName}");
if (!string.IsNullOrEmpty(RequestNotes))
sb.AppendLine(RequestNotes);
sb.AppendLine($"Needs authentication: {NeedsAuthentication}");
sb.AppendLine($"Required exchange specific parameters: {string.Join(", ", RequiredExchangeParameters.Select(x => x.ToString()))}");
sb.AppendLine($"Optional exchange specific parameters: {string.Join(", ", OptionalExchangeParameters.Select(x => x.ToString()))}");
return sb.ToString();
}
}
/// <summary>
/// Options for an exchange endpoint
/// </summary>
/// <typeparam name="T">Type of data</typeparam>
/// <typeparam name="TRequest">Type of data</typeparam>
#if NET5_0_OR_GREATER
public class EndpointOptions<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T> : EndpointOptions where T : SharedRequest
public class EndpointOptions<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TRequest> : EndpointOptions
where TRequest : SharedRequest
#else
public class EndpointOptions<T> : EndpointOptions where T : SharedRequest
public abstract class EndpointOptions<TRequest> : EndpointOptions
where TRequest : SharedRequest
#endif
{
/// <summary>
/// Required optional parameters in the request
/// ctor
/// </summary>
public EndpointOptions(string exchange, bool needsAuthentication, string requestName) : base(exchange, requestName, needsAuthentication)
{
}
}
/// <summary>
/// Options for an exchange endpoint
/// </summary>
/// <typeparam name="TRequest">Type of data</typeparam>
/// <typeparam name="TClient">Type of the client</typeparam>
#if NET5_0_OR_GREATER
public abstract class EndpointOptions<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TRequest, TClient> : EndpointOptions <TRequest>
where TRequest : SharedRequest
where TClient : ISharedClient
#else
public abstract class EndpointOptions<TRequest, TClient> : EndpointOptions<TRequest>
where TRequest : SharedRequest
where TClient : ISharedClient
#endif
{
private static PropertyInfo[] _requestProperties = typeof(TRequest).GetProperties();
/// <summary>
/// Required optional parameters in the request. These can be provided via the `exchangeParameters` property of the request object.
/// </summary>
public List<ParameterDescription> RequiredOptionalParameters { get; set; } = new List<ParameterDescription>();
@@ -120,33 +131,25 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public EndpointOptions(bool needsAuthentication) : base(typeof(T).Name, needsAuthentication)
public EndpointOptions(string exchange, bool needsAuthentication, string requestName) : base(exchange, needsAuthentication, requestName)
{
}
/// <summary>
/// Validate a request
/// </summary>
/// <param name="exchange">Exchange name</param>
/// <param name="request">The request</param>
/// <param name="tradingMode">Request trading mode</param>
/// <param name="supportedTradingModes">Supported trading modes</param>
/// <param name="client">Containing client</param>
/// <returns></returns>
public virtual Error? ValidateRequest(string exchange, T request, TradingMode? tradingMode, TradingMode[] supportedTradingModes)
public virtual Error? ValidateRequest(TRequest request, TClient client)
{
if (NeedsAuthentication && !client.Authenticated)
return new NoApiCredentialsError();
foreach (var param in RequiredOptionalParameters)
{
if (!string.IsNullOrEmpty(param.Name))
{
if (typeof(T).GetProperty(param.Name)!.GetValue(request, null) == null)
return ArgumentError.Invalid(param.Name!, $"Required optional parameter `{param.Name}` for exchange `{exchange}` is missing. Example: {param.ExampleValue}");
}
else
{
if (param.Names!.All(x => typeof(T).GetProperty(param.Name!)!.GetValue(request, null) == null))
return ArgumentError.Invalid(string.Join("/", param.Names!), $"One of optional parameters `{string.Join(", ", param.Names!)}` for exchange `{exchange}` should be provided. Example: {param.ExampleValue}");
}
if (param.Names!.All(x => _requestProperties.Single(p => p.Name == x).GetValue(request, null) == null))
return ArgumentError.Invalid(string.Join("/", param.Names!), $"One of optional parameters `{string.Join(", ", param.Names!)}` for exchange `{Exchange}` should be provided. Example: {param.ExampleValue}");
}
if (request is SharedSymbolRequest symbolsRequest)
@@ -162,26 +165,38 @@ namespace CryptoExchange.Net.SharedApis
}
return ValidateRequest(exchange, request.ExchangeParameters, tradingMode, supportedTradingModes);
return ValidateRequest(request.ExchangeParameters, request.TradingMode, client.SupportedTradingModes);
}
/// <inheritdoc />
public override string ToString(string exchange)
public override string ToString()
{
if (!Supported)
return $"{exchange} {EndpointName} NOT SUPPORTED";
return $"{Exchange} {EndpointName} NOT SUPPORTED";
var sb = new StringBuilder();
sb.AppendLine($"{exchange} {typeof(T).Name}");
sb.AppendLine($"{Exchange} {EndpointName}");
sb.AppendLine($"Needs authentication: {NeedsAuthentication}");
if (!string.IsNullOrEmpty(RequestNotes))
sb.AppendLine(RequestNotes);
if (RequiredOptionalParameters.Any())
sb.AppendLine($"Required optional parameters: {string.Join(", ", RequiredOptionalParameters.Select(x => x.ToString()))}");
{
sb.AppendLine($"Required optional parameters:");
foreach(var param in RequiredOptionalParameters)
sb.AppendLine($" {param}");
}
if (RequiredExchangeParameters.Any())
sb.AppendLine($"Required exchange specific parameters: {string.Join(", ", RequiredExchangeParameters.Select(x => x.ToString()))}");
{
sb.AppendLine($"Required exchange specific parameters:");
foreach (var param in RequiredExchangeParameters)
sb.AppendLine($" {param}");
}
if (OptionalExchangeParameters.Any())
sb.AppendLine($"Optional exchange specific parameters: {string.Join(", ", RequiredExchangeParameters.Select(x => x.ToString()))}");
{
sb.AppendLine($"Optional exchange specific parameters:");
foreach (var param in OptionalExchangeParameters)
sb.AppendLine($" {param}");
}
return sb.ToString();
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting asset info
/// </summary>
public class GetAssetOptions : EndpointOptions<GetAssetRequest, IAssetsRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetAssetOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IAssetsRestClient.GetAssetAsync))
{
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting assets info
/// </summary>
public class GetAssetsOptions : EndpointOptions<GetAssetsRequest, IAssetsRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetAssetsOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IAssetsRestClient.GetAssetsAsync))
{
}
}
}
@@ -6,7 +6,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for requesting a transfer
/// </summary>
public class GetBalancesOptions : EndpointOptions<GetBalancesRequest>
public class GetBalancesOptions : EndpointOptions<GetBalancesRequest, IBalanceRestClient>
{
/// <summary>
/// Supported account types
@@ -16,7 +16,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public GetBalancesOptions(params AccountTypeFilter[] accountTypes) : base(true)
public GetBalancesOptions(string exchange, params AccountTypeFilter[] accountTypes) : base(exchange, true, nameof(IBalanceRestClient.GetBalancesAsync))
{
SupportedAccountTypes = accountTypes;
}
@@ -24,15 +24,14 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Validate a request
/// </summary>
public Error? ValidateRequest(
string exchange,
public override Error? ValidateRequest(
GetBalancesRequest request,
TradingMode[] supportedApiTypes)
IBalanceRestClient client)
{
if (request.AccountType != null && !IsValid(request.AccountType.Value))
return ArgumentError.Invalid(nameof(request.AccountType), "Invalid AccountType");
return base.ValidateRequest(exchange, request, null, supportedApiTypes);
return base.ValidateRequest(request, client);
}
/// <summary>
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting book ticker
/// </summary>
public class GetBookTickerOptions : EndpointOptions<GetBookTickerRequest, IBookTickerRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetBookTickerOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IBookTickerRestClient.GetBookTickerAsync))
{
}
}
}
@@ -1,54 +0,0 @@
using CryptoExchange.Net.Objects;
using System;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting closed orders
/// </summary>
public class GetClosedOrdersOptions : PaginatedEndpointOptions<GetClosedOrdersRequest>
{
/// <summary>
/// ctor
/// </summary>
public GetClosedOrdersOptions(bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit)
: base(supportsAscending, supportsDescending, timeFilterSupported, maxLimit, true)
{
}
/// <inheritdoc />
public override Error? ValidateRequest(string exchange, GetClosedOrdersRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
{
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.Direction), $"Ascending direction is not supported");
if (!SupportsDescending && request.Direction == DataDirection.Descending)
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.Direction), $"Descending direction is not supported");
if (MaxAge.HasValue && request.StartTime < DateTime.UtcNow.Add(-MaxAge.Value))
return ArgumentError.Invalid(nameof(GetKlinesRequest.StartTime), $"Only the most recent {MaxAge} period data is available");
if (!TimePeriodFilterSupport)
{
// When going descending we can still allow startTime filter to limit the results
var now = DateTime.UtcNow;
if ((request.Direction != DataDirection.Descending && request.StartTime != null)
|| (request.EndTime != null && now - request.EndTime > TimeSpan.FromSeconds(5)))
{
return ArgumentError.Invalid(nameof(GetDepositsRequest.StartTime), $"Time filter is not supported");
}
}
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
}
/// <inheritdoc />
public override string ToString(string exchange)
{
var sb = new StringBuilder(base.ToString(exchange));
sb.AppendLine($"Time filter supported: {TimePeriodFilterSupport}");
return sb.ToString();
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting deposit address
/// </summary>
public class GetDepositAddressesOptions : EndpointOptions<GetDepositAddressesRequest, IDepositRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetDepositAddressesOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IDepositRestClient.GetDepositAddressesAsync))
{
}
}
}
@@ -7,18 +7,18 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for requesting deposits
/// </summary>
public class GetDepositsOptions : PaginatedEndpointOptions<GetDepositsRequest>
public class GetDepositsOptions : PaginatedEndpointOptions<GetDepositsRequest, IDepositRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetDepositsOptions(bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit)
: base(supportsAscending, supportsDescending, timeFilterSupported, maxLimit, true)
public GetDepositsOptions(string exchange, bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit)
: base(exchange, supportsAscending, supportsDescending, timeFilterSupported, maxLimit, true, nameof(IDepositRestClient.GetDepositsAsync))
{
}
/// <inheritdoc />
public override Error? ValidateRequest(string exchange, GetDepositsRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
public override Error? ValidateRequest(GetDepositsRequest request, IDepositRestClient client)
{
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.Direction), $"Ascending direction is not supported");
@@ -40,15 +40,7 @@ namespace CryptoExchange.Net.SharedApis
}
}
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
}
/// <inheritdoc />
public override string ToString(string exchange)
{
var sb = new StringBuilder(base.ToString(exchange));
sb.AppendLine($"Time filter supported: {TimePeriodFilterSupport}");
return sb.ToString();
return base.ValidateRequest(request, client);
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting trading fee info
/// </summary>
public class GetFeeOptions : EndpointOptions<GetFeeRequest, IFeeRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetFeeOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IFeeRestClient.GetFeesAsync))
{
}
}
}
@@ -7,18 +7,18 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for requesting funding rate history
/// </summary>
public class GetFundingRateHistoryOptions : PaginatedEndpointOptions<GetFundingRateHistoryRequest>
public class GetFundingRateHistoryOptions : PaginatedEndpointOptions<GetFundingRateHistoryRequest, IFundingRateRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetFundingRateHistoryOptions(bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit, bool needsAuthentication)
: base(supportsAscending, supportsDescending, timeFilterSupported, maxLimit, needsAuthentication)
public GetFundingRateHistoryOptions(string exchange, bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit, bool needsAuthentication)
: base(exchange, supportsAscending, supportsDescending, timeFilterSupported, maxLimit, needsAuthentication, nameof(IFundingRateRestClient.GetFundingRateHistoryAsync))
{
}
/// <inheritdoc />
public override Error? ValidateRequest(string exchange, GetFundingRateHistoryRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
public override Error? ValidateRequest(GetFundingRateHistoryRequest request, IFundingRateRestClient client)
{
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.Direction), $"Ascending direction is not supported");
@@ -40,15 +40,7 @@ namespace CryptoExchange.Net.SharedApis
}
}
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
}
/// <inheritdoc />
public override string ToString(string exchange)
{
var sb = new StringBuilder(base.ToString(exchange));
sb.AppendLine($"Time filter supported: {TimePeriodFilterSupport}");
return sb.ToString();
return base.ValidateRequest(request, client);
}
}
}
@@ -0,0 +1,47 @@
using CryptoExchange.Net.Objects;
using System;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting closed orders
/// </summary>
public class GetFuturesClosedOrdersOptions : PaginatedEndpointOptions<GetClosedOrdersRequest, IFuturesOrderRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetFuturesClosedOrdersOptions(string exchange, bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit)
: base(exchange, supportsAscending, supportsDescending, timeFilterSupported, maxLimit, true, nameof(IFuturesOrderRestClient.GetClosedFuturesOrdersAsync))
{
}
/// <inheritdoc />
public override Error? ValidateRequest(GetClosedOrdersRequest request, IFuturesOrderRestClient client)
{
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetClosedOrdersRequest.Direction), $"Ascending direction is not supported");
if (!SupportsDescending && request.Direction == DataDirection.Descending)
return ArgumentError.Invalid(nameof(GetClosedOrdersRequest.Direction), $"Descending direction is not supported");
if (MaxAge.HasValue && request.StartTime < DateTime.UtcNow.Add(-MaxAge.Value))
return ArgumentError.Invalid(nameof(GetClosedOrdersRequest.StartTime), $"Only the most recent {MaxAge} period data is available");
if (!TimePeriodFilterSupport)
{
// When going descending we can still allow startTime filter to limit the results
var now = DateTime.UtcNow;
if ((request.Direction != DataDirection.Descending && request.StartTime != null)
|| (request.EndTime != null && now - request.EndTime > TimeSpan.FromSeconds(5)))
{
return ArgumentError.Invalid(nameof(GetClosedOrdersRequest.StartTime), $"Time filter is not supported");
}
}
return base.ValidateRequest(request, client);
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for getting a spot order by client order id
/// </summary>
public class GetFuturesOrderByClientOrderIdOptions : EndpointOptions<GetOrderRequest, IFuturesOrderClientIdRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetFuturesOrderByClientOrderIdOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IFuturesOrderClientIdRestClient.GetFuturesOrderByClientOrderIdAsync))
{
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for getting a futures order by id endpoint
/// </summary>
public class GetFuturesOrderOptions : EndpointOptions<GetOrderRequest, IFuturesOrderRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetFuturesOrderOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IFuturesOrderRestClient.GetFuturesOrderAsync))
{
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for getting a trades for an order
/// </summary>
public class GetFuturesOrderTradesOptions : EndpointOptions<GetOrderTradesRequest, IFuturesOrderRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetFuturesOrderTradesOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IFuturesOrderRestClient.GetFuturesOrderTradesAsync))
{
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting symbol info
/// </summary>
public class GetFuturesSymbolsOptions : EndpointOptions<GetSymbolsRequest, IFuturesSymbolRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetFuturesSymbolsOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IFuturesSymbolRestClient.GetFuturesSymbolsAsync))
{
}
}
}
@@ -0,0 +1,31 @@
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting ticker
/// </summary>
public class GetFuturesTickerOptions : EndpointOptions<GetTickerRequest, IFuturesTickerRestClient>
{
/// <summary>
/// Type of ticker calculation
/// </summary>
public SharedTickerType TickerType { get; set; } = SharedTickerType.Day24H;
/// <summary>
/// ctor
/// </summary>
public GetFuturesTickerOptions(string exchange, SharedTickerType? tickerCalcType = null) : base(exchange, false, nameof(IFuturesTickerRestClient.GetFuturesTickerAsync))
{
TickerType = tickerCalcType ?? SharedTickerType.Day24H;
}
/// <inheritdoc />
public override string ToString()
{
var sb = new StringBuilder(base.ToString());
sb.AppendLine($"Ticker data calculation type: {TickerType}");
return sb.ToString();
}
}
}
@@ -0,0 +1,31 @@
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting tickers
/// </summary>
public class GetFuturesTickersOptions : EndpointOptions<GetTickersRequest, IFuturesTickerRestClient>
{
/// <summary>
/// Type of ticker calculation
/// </summary>
public SharedTickerType TickerType { get; set; } = SharedTickerType.Day24H;
/// <summary>
/// ctor
/// </summary>
public GetFuturesTickersOptions(string exchange, SharedTickerType? tickerCalcType = null) : base(exchange, false, nameof(IFuturesTickerRestClient.GetFuturesTickersAsync))
{
TickerType = tickerCalcType ?? SharedTickerType.Day24H;
}
/// <inheritdoc />
public override string ToString()
{
var sb = new StringBuilder(base.ToString());
sb.AppendLine($"Ticker data calculation type: {TickerType}");
return sb.ToString();
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting futures trigger order
/// </summary>
public class GetFuturesTriggerOrderOptions : EndpointOptions<GetOrderRequest, IFuturesTriggerOrderRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetFuturesTriggerOrderOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IFuturesTriggerOrderRestClient.GetFuturesTriggerOrderAsync))
{
}
}
}
@@ -0,0 +1,46 @@
using CryptoExchange.Net.Objects;
using System;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting user trades
/// </summary>
public class GetFuturesUserTradesOptions : PaginatedEndpointOptions<GetUserTradesRequest, IFuturesOrderRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetFuturesUserTradesOptions(string exchange, bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit)
: base(exchange, supportsAscending, supportsDescending, timeFilterSupported, maxLimit, true, nameof(IFuturesOrderRestClient.GetFuturesUserTradesAsync))
{
}
/// <inheritdoc />
public override Error? ValidateRequest(GetUserTradesRequest request, IFuturesOrderRestClient client)
{
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetUserTradesRequest.Direction), $"Ascending direction is not supported");
if (!SupportsDescending && request.Direction == DataDirection.Descending)
return ArgumentError.Invalid(nameof(GetUserTradesRequest.Direction), $"Descending direction is not supported");
if (MaxAge.HasValue && request.StartTime < DateTime.UtcNow.Add(-MaxAge.Value))
return ArgumentError.Invalid(nameof(GetUserTradesRequest.StartTime), $"Only the most recent {MaxAge} period data is available");
if (!TimePeriodFilterSupport)
{
// When going descending we can still allow startTime filter to limit the results
var now = DateTime.UtcNow;
if ((request.Direction != DataDirection.Descending && request.StartTime != null)
|| (request.EndTime != null && now - request.EndTime > TimeSpan.FromSeconds(5)))
{
return ArgumentError.Invalid(nameof(GetDepositsRequest.StartTime), $"Time filter is not supported");
}
}
return base.ValidateRequest(request, client);
}
}
}
@@ -0,0 +1,117 @@
using CryptoExchange.Net.Objects;
using System;
using System.Linq;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting kline/candlestick data
/// </summary>
public class GetIndexPriceKlinesOptions : PaginatedEndpointOptions<GetKlinesRequest, IIndexPriceKlineRestClient>
{
/// <summary>
/// The supported kline intervals
/// </summary>
public SharedKlineInterval[] SupportIntervals { get; }
/// <summary>
/// Max number of data points which can be requested
/// </summary>
public int? MaxTotalDataPoints { get; set; }
/// <summary>
/// ctor
/// </summary>
public GetIndexPriceKlinesOptions(string exchange, bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit, bool needsAuthentication)
: base(exchange, supportsAscending, supportsDescending, timeFilterSupported, maxLimit, needsAuthentication, nameof(IIndexPriceKlineRestClient.GetIndexPriceKlinesAsync))
{
SupportIntervals = new[]
{
SharedKlineInterval.OneMinute,
SharedKlineInterval.ThreeMinutes,
SharedKlineInterval.FiveMinutes,
SharedKlineInterval.FifteenMinutes,
SharedKlineInterval.ThirtyMinutes,
SharedKlineInterval.OneHour,
SharedKlineInterval.TwoHours,
SharedKlineInterval.FourHours,
SharedKlineInterval.SixHours,
SharedKlineInterval.EightHours,
SharedKlineInterval.TwelveHours,
SharedKlineInterval.OneDay,
SharedKlineInterval.OneWeek,
SharedKlineInterval.OneMonth
};
}
/// <summary>
/// ctor
/// </summary>
public GetIndexPriceKlinesOptions(string exchange, bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit, bool needsAuthentication, params SharedKlineInterval[] intervals)
: base(exchange, supportsAscending, supportsDescending, timeFilterSupported, maxLimit, needsAuthentication, nameof(IIndexPriceKlineRestClient.GetIndexPriceKlinesAsync))
{
SupportIntervals = intervals;
}
/// <summary>
/// Check whether a specific interval is supported
/// </summary>
/// <param name="interval"></param>
/// <returns></returns>
public bool IsSupported(SharedKlineInterval interval) => SupportIntervals.Contains(interval);
/// <inheritdoc />
public override Error? ValidateRequest(GetKlinesRequest request, IIndexPriceKlineRestClient client)
{
if (!IsSupported(request.Interval))
return ArgumentError.Invalid(nameof(GetKlinesRequest.Interval), $"Interval {request.Interval} not supported");
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.Direction), $"Ascending direction is not supported");
if (!SupportsDescending && request.Direction == DataDirection.Descending)
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.Direction), $"Descending direction is not supported");
if (MaxAge.HasValue && request.StartTime < DateTime.UtcNow.Add(-MaxAge.Value))
return ArgumentError.Invalid(nameof(GetKlinesRequest.StartTime), $"Only the most recent {MaxAge} klines are available");
if (request.Limit > MaxLimit)
return ArgumentError.Invalid(nameof(GetKlinesRequest.Limit), $"Only {MaxLimit} klines can be retrieved per request");
if (!TimePeriodFilterSupport)
{
// When going descending we can still allow startTime filter to limit the results
var now = DateTime.UtcNow;
if ((request.Direction == DataDirection.Ascending && request.StartTime != null)
|| (request.EndTime != null && now - request.EndTime > TimeSpan.FromSeconds(5)))
{
return ArgumentError.Invalid(nameof(GetDepositsRequest.StartTime), $"Time filter is not supported");
}
}
if (MaxTotalDataPoints.HasValue)
{
if (request.Limit > MaxTotalDataPoints.Value)
return ArgumentError.Invalid(nameof(GetKlinesRequest.Limit), $"Only the most recent {MaxTotalDataPoints} klines are available");
if (request.StartTime.HasValue == true)
{
if (((request.EndTime ?? DateTime.UtcNow) - request.StartTime.Value).TotalSeconds / (int)request.Interval > MaxTotalDataPoints.Value)
return ArgumentError.Invalid(nameof(GetKlinesRequest.StartTime), $"Only the most recent {MaxTotalDataPoints} klines are available, time filter failed");
}
}
return base.ValidateRequest(request, client);
}
/// <inheritdoc />
public override string ToString()
{
var sb = new StringBuilder(base.ToString());
sb.AppendLine($"Supported SharedKlineInterval values: {string.Join(", ", SupportIntervals)}");
if (MaxTotalDataPoints != null)
sb.AppendLine($"Max total data points available: {MaxTotalDataPoints}");
return sb.ToString();
}
}
}
@@ -8,7 +8,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for requesting kline/candlestick data
/// </summary>
public class GetKlinesOptions : PaginatedEndpointOptions<GetKlinesRequest>
public class GetKlinesOptions : PaginatedEndpointOptions<GetKlinesRequest, IKlineRestClient>
{
/// <summary>
/// The supported kline intervals
@@ -22,8 +22,8 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public GetKlinesOptions(bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit, bool needsAuthentication)
: base(supportsAscending, supportsDescending, timeFilterSupported, maxLimit, needsAuthentication)
public GetKlinesOptions(string exchange, bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit, bool needsAuthentication)
: base(exchange, supportsAscending, supportsDescending, timeFilterSupported, maxLimit, needsAuthentication, nameof(IKlineRestClient.GetKlinesAsync))
{
SupportIntervals = new[]
{
@@ -47,8 +47,8 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public GetKlinesOptions(bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit, bool needsAuthentication, params SharedKlineInterval[] intervals)
: base(supportsAscending, supportsDescending, timeFilterSupported, maxLimit, needsAuthentication)
public GetKlinesOptions(string exchange, bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit, bool needsAuthentication, params SharedKlineInterval[] intervals)
: base(exchange, supportsAscending, supportsDescending, timeFilterSupported, maxLimit, needsAuthentication, nameof(IKlineRestClient.GetKlinesAsync))
{
SupportIntervals = intervals;
}
@@ -61,10 +61,10 @@ namespace CryptoExchange.Net.SharedApis
public bool IsSupported(SharedKlineInterval interval) => SupportIntervals.Contains(interval);
/// <inheritdoc />
public override Error? ValidateRequest(string exchange, GetKlinesRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
public override Error? ValidateRequest(GetKlinesRequest request, IKlineRestClient client)
{
if (!IsSupported(request.Interval))
return ArgumentError.Invalid(nameof(GetKlinesRequest.Interval), "Interval not supported");
return ArgumentError.Invalid(nameof(GetKlinesRequest.Interval), $"Interval {request.Interval} not supported");
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.Direction), $"Ascending direction is not supported");
@@ -101,17 +101,14 @@ namespace CryptoExchange.Net.SharedApis
}
}
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
return base.ValidateRequest(request, client);
}
/// <inheritdoc />
public override string ToString(string exchange)
public override string ToString()
{
var sb = new StringBuilder(base.ToString(exchange));
sb.AppendLine($"Time filter supported: {TimePeriodFilterSupport}");
var sb = new StringBuilder(base.ToString());
sb.AppendLine($"Supported SharedKlineInterval values: {string.Join(", ", SupportIntervals)}");
if (MaxAge != null)
sb.AppendLine($"Max age of data: {MaxAge}");
if (MaxTotalDataPoints != null)
sb.AppendLine($"Max total data points available: {MaxTotalDataPoints}");
return sb.ToString();
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting asset info
/// </summary>
public class GetLeverageOptions : EndpointOptions<GetLeverageRequest, ILeverageRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetLeverageOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(ILeverageRestClient.GetLeverageAsync))
{
}
}
}
@@ -0,0 +1,117 @@
using CryptoExchange.Net.Objects;
using System;
using System.Linq;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting kline/candlestick data
/// </summary>
public class GetMarkPriceKlinesOptions : PaginatedEndpointOptions<GetKlinesRequest, IMarkPriceKlineRestClient>
{
/// <summary>
/// The supported kline intervals
/// </summary>
public SharedKlineInterval[] SupportIntervals { get; }
/// <summary>
/// Max number of data points which can be requested
/// </summary>
public int? MaxTotalDataPoints { get; set; }
/// <summary>
/// ctor
/// </summary>
public GetMarkPriceKlinesOptions(string exchange, bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit, bool needsAuthentication)
: base(exchange, supportsAscending, supportsDescending, timeFilterSupported, maxLimit, needsAuthentication, nameof(IMarkPriceKlineRestClient.GetMarkPriceKlinesAsync))
{
SupportIntervals = new[]
{
SharedKlineInterval.OneMinute,
SharedKlineInterval.ThreeMinutes,
SharedKlineInterval.FiveMinutes,
SharedKlineInterval.FifteenMinutes,
SharedKlineInterval.ThirtyMinutes,
SharedKlineInterval.OneHour,
SharedKlineInterval.TwoHours,
SharedKlineInterval.FourHours,
SharedKlineInterval.SixHours,
SharedKlineInterval.EightHours,
SharedKlineInterval.TwelveHours,
SharedKlineInterval.OneDay,
SharedKlineInterval.OneWeek,
SharedKlineInterval.OneMonth
};
}
/// <summary>
/// ctor
/// </summary>
public GetMarkPriceKlinesOptions(string exchange, bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit, bool needsAuthentication, params SharedKlineInterval[] intervals)
: base(exchange, supportsAscending, supportsDescending, timeFilterSupported, maxLimit, needsAuthentication, nameof(IMarkPriceKlineRestClient.GetMarkPriceKlinesAsync))
{
SupportIntervals = intervals;
}
/// <summary>
/// Check whether a specific interval is supported
/// </summary>
/// <param name="interval"></param>
/// <returns></returns>
public bool IsSupported(SharedKlineInterval interval) => SupportIntervals.Contains(interval);
/// <inheritdoc />
public override Error? ValidateRequest(GetKlinesRequest request, IMarkPriceKlineRestClient client)
{
if (!IsSupported(request.Interval))
return ArgumentError.Invalid(nameof(GetKlinesRequest.Interval), $"Interval {request.Interval} not supported");
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.Direction), $"Ascending direction is not supported");
if (!SupportsDescending && request.Direction == DataDirection.Descending)
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.Direction), $"Descending direction is not supported");
if (MaxAge.HasValue && request.StartTime < DateTime.UtcNow.Add(-MaxAge.Value))
return ArgumentError.Invalid(nameof(GetKlinesRequest.StartTime), $"Only the most recent {MaxAge} klines are available");
if (request.Limit > MaxLimit)
return ArgumentError.Invalid(nameof(GetKlinesRequest.Limit), $"Only {MaxLimit} klines can be retrieved per request");
if (!TimePeriodFilterSupport)
{
// When going descending we can still allow startTime filter to limit the results
var now = DateTime.UtcNow;
if ((request.Direction == DataDirection.Ascending && request.StartTime != null)
|| (request.EndTime != null && now - request.EndTime > TimeSpan.FromSeconds(5)))
{
return ArgumentError.Invalid(nameof(GetDepositsRequest.StartTime), $"Time filter is not supported");
}
}
if (MaxTotalDataPoints.HasValue)
{
if (request.Limit > MaxTotalDataPoints.Value)
return ArgumentError.Invalid(nameof(GetKlinesRequest.Limit), $"Only the most recent {MaxTotalDataPoints} klines are available");
if (request.StartTime.HasValue == true)
{
if (((request.EndTime ?? DateTime.UtcNow) - request.StartTime.Value).TotalSeconds / (int)request.Interval > MaxTotalDataPoints.Value)
return ArgumentError.Invalid(nameof(GetKlinesRequest.StartTime), $"Only the most recent {MaxTotalDataPoints} klines are available, time filter failed");
}
}
return base.ValidateRequest(request, client);
}
/// <inheritdoc />
public override string ToString()
{
var sb = new StringBuilder(base.ToString());
sb.AppendLine($"Supported SharedKlineInterval values: {string.Join(", ", SupportIntervals)}");
if (MaxTotalDataPoints != null)
sb.AppendLine($"Max total data points available: {MaxTotalDataPoints}");
return sb.ToString();
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for getting a futures order by id endpoint
/// </summary>
public class GetOpenFuturesOrdersOptions : EndpointOptions<GetOpenOrdersRequest, IFuturesOrderRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetOpenFuturesOrdersOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IFuturesOrderRestClient.GetOpenFuturesOrdersAsync))
{
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting open interest
/// </summary>
public class GetOpenInterestOptions : EndpointOptions<GetOpenInterestRequest, IOpenInterestRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetOpenInterestOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IOpenInterestRestClient.GetOpenInterestAsync))
{
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for getting a spot order by id endpoint
/// </summary>
public class GetOpenSpotOrdersOptions : EndpointOptions<GetOpenOrdersRequest, ISpotOrderRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetOpenSpotOrdersOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(ISpotOrderRestClient.GetOpenSpotOrdersAsync))
{
}
}
}
@@ -8,7 +8,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for requesting order book
/// </summary>
public class GetOrderBookOptions : EndpointOptions<GetOrderBookRequest>
public class GetOrderBookOptions : EndpointOptions<GetOrderBookRequest, IOrderBookRestClient>
{
/// <summary>
/// Supported order book depths
@@ -27,7 +27,8 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public GetOrderBookOptions(int minLimit, int maxLimit, bool authenticated) : base(authenticated)
public GetOrderBookOptions(string exchange, int minLimit, int maxLimit, bool authenticated)
: base(exchange, authenticated, nameof(IOrderBookRestClient.GetOrderBookAsync))
{
MinLimit = minLimit;
MaxLimit = maxLimit;
@@ -36,33 +37,34 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public GetOrderBookOptions(int[] supportedLimits, bool authenticated) : base(authenticated)
public GetOrderBookOptions(string exchange, int[] supportedLimits, bool authenticated)
: base(exchange, authenticated, nameof(IOrderBookRestClient.GetOrderBookAsync))
{
SupportedLimits = supportedLimits;
}
/// <inheritdoc />
public override Error? ValidateRequest(string exchange, GetOrderBookRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
public override Error? ValidateRequest(GetOrderBookRequest request, IOrderBookRestClient client)
{
if (request.Limit == null)
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
return base.ValidateRequest(request, client);
if (MaxLimit.HasValue && request.Limit.Value > MaxLimit)
return ArgumentError.Invalid(nameof(GetOrderBookRequest.Limit), $"Max limit is {MaxLimit}");
if (MinLimit.HasValue && request.Limit.Value < MinLimit)
return ArgumentError.Invalid(nameof(GetOrderBookRequest.Limit), $"Min limit is {MaxLimit}");
return ArgumentError.Invalid(nameof(GetOrderBookRequest.Limit), $"Min limit is {MinLimit}");
if (SupportedLimits != null && !SupportedLimits.Contains(request.Limit.Value))
return ArgumentError.Invalid(nameof(GetOrderBookRequest.Limit), $"Limit should be one of " + string.Join(", ", SupportedLimits));
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
return base.ValidateRequest(request, client);
}
/// <inheritdoc />
public override string ToString(string exchange)
public override string ToString()
{
var sb = new StringBuilder(base.ToString(exchange));
var sb = new StringBuilder(base.ToString());
sb.AppendLine($"Supported limit values: [{(SupportedLimits != null ? string.Join(", ", SupportedLimits) : $"{MinLimit}..{MaxLimit}")}]");
return sb.ToString();
}
@@ -7,18 +7,18 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for requesting position history
/// </summary>
public class GetPositionHistoryOptions : PaginatedEndpointOptions<GetPositionHistoryRequest>
public class GetPositionHistoryOptions : PaginatedEndpointOptions<GetPositionHistoryRequest, IPositionHistoryRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetPositionHistoryOptions(bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit)
: base(supportsAscending, supportsDescending, timeFilterSupported, maxLimit, true)
public GetPositionHistoryOptions(string exchange, bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit)
: base(exchange, supportsAscending, supportsDescending, timeFilterSupported, maxLimit, true, nameof(IPositionHistoryRestClient.GetPositionHistoryAsync))
{
}
/// <inheritdoc />
public override Error? ValidateRequest(string exchange, GetPositionHistoryRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
public override Error? ValidateRequest(GetPositionHistoryRequest request, IPositionHistoryRestClient client)
{
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.Direction), $"Ascending direction is not supported");
@@ -40,15 +40,7 @@ namespace CryptoExchange.Net.SharedApis
}
}
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
}
/// <inheritdoc />
public override string ToString(string exchange)
{
var sb = new StringBuilder(base.ToString(exchange));
sb.AppendLine($"Time filter supported: {TimePeriodFilterSupport}");
return sb.ToString();
return base.ValidateRequest(request, client);
}
}
}
@@ -3,12 +3,12 @@
/// <summary>
/// Options for requesting current position mode
/// </summary>
public class GetPositionModeOptions : EndpointOptions<GetPositionModeRequest>
public class GetPositionModeOptions : EndpointOptions<GetPositionModeRequest, IPositionModeRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetPositionModeOptions() : base(true)
public GetPositionModeOptions(string exchange) : base(exchange, true, nameof(IPositionModeRestClient.GetPositionModeAsync))
{
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting open positions
/// </summary>
public class GetPositionsOptions : EndpointOptions<GetPositionsRequest, IFuturesOrderRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetPositionsOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IFuturesOrderRestClient.GetPositionsAsync))
{
}
}
}
@@ -6,7 +6,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for requesting recent trades
/// </summary>
public class GetRecentTradesOptions : EndpointOptions<GetRecentTradesRequest>
public class GetRecentTradesOptions : EndpointOptions<GetRecentTradesRequest, IRecentTradeRestClient>
{
/// <summary>
/// The max number of trades that can be requested
@@ -16,15 +16,16 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public GetRecentTradesOptions(int limit, bool authenticated) : base(authenticated)
public GetRecentTradesOptions(string exchange, int limit, bool authenticated)
: base(exchange, authenticated, nameof(IRecentTradeRestClient.GetRecentTradesAsync))
{
MaxLimit = limit;
}
/// <inheritdoc />
public override Error? ValidateRequest(string exchange, GetRecentTradesRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
public override Error? ValidateRequest(GetRecentTradesRequest request, IRecentTradeRestClient client)
{
var baseError = base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
var baseError = base.ValidateRequest(request, client);
if (baseError != null)
return baseError;
@@ -35,9 +36,9 @@ namespace CryptoExchange.Net.SharedApis
}
/// <inheritdoc />
public override string ToString(string exchange)
public override string ToString()
{
var sb = new StringBuilder(base.ToString(exchange));
var sb = new StringBuilder(base.ToString());
sb.AppendLine($"Max data points: {MaxLimit}");
return sb.ToString();
}
@@ -0,0 +1,46 @@
using CryptoExchange.Net.Objects;
using System;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting user trades
/// </summary>
public class GetSpotClosedOrdersOptions : PaginatedEndpointOptions<GetClosedOrdersRequest, ISpotOrderRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetSpotClosedOrdersOptions(string exchange, bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit)
: base(exchange, supportsAscending, supportsDescending, timeFilterSupported, maxLimit, true, nameof(ISpotOrderRestClient.GetClosedSpotOrdersAsync))
{
}
/// <inheritdoc />
public override Error? ValidateRequest(GetClosedOrdersRequest request, ISpotOrderRestClient client)
{
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetClosedOrdersRequest.Direction), $"Ascending direction is not supported");
if (!SupportsDescending && request.Direction == DataDirection.Descending)
return ArgumentError.Invalid(nameof(GetClosedOrdersRequest.Direction), $"Descending direction is not supported");
if (MaxAge.HasValue && request.StartTime < DateTime.UtcNow.Add(-MaxAge.Value))
return ArgumentError.Invalid(nameof(GetClosedOrdersRequest.StartTime), $"Only the most recent {MaxAge} period data is available");
if (!TimePeriodFilterSupport)
{
// When going descending we can still allow startTime filter to limit the results
var now = DateTime.UtcNow;
if ((request.Direction != DataDirection.Descending && request.StartTime != null)
|| (request.EndTime != null && now - request.EndTime > TimeSpan.FromSeconds(5)))
{
return ArgumentError.Invalid(nameof(GetClosedOrdersRequest.StartTime), $"Time filter is not supported");
}
}
return base.ValidateRequest(request, client);
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for getting a spot order by client order id endpoint
/// </summary>
public class GetSpotOrderByClientOrderIdOptions : EndpointOptions<GetOrderRequest, ISpotOrderClientIdRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetSpotOrderByClientOrderIdOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(ISpotOrderClientIdRestClient.GetSpotOrderByClientOrderIdAsync))
{
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for getting a spot order by id endpoint
/// </summary>
public class GetSpotOrderOptions : EndpointOptions<GetOrderRequest, ISpotOrderRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetSpotOrderOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(ISpotOrderRestClient.GetSpotOrderAsync))
{
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for getting trades for a spot order
/// </summary>
public class GetSpotOrderTradesOptions : EndpointOptions<GetOrderTradesRequest, ISpotOrderRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetSpotOrderTradesOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(ISpotOrderRestClient.GetSpotOrderTradesAsync))
{
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting symbol info
/// </summary>
public class GetSpotSymbolsOptions : EndpointOptions<GetSymbolsRequest, ISpotSymbolRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetSpotSymbolsOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(ISpotSymbolRestClient.GetSpotSymbolsAsync))
{
}
}
}
@@ -5,7 +5,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for requesting ticker
/// </summary>
public class GetTickerOptions : EndpointOptions<GetTickerRequest>
public class GetSpotTickerOptions : EndpointOptions<GetTickerRequest, ISpotTickerRestClient>
{
/// <summary>
/// Type of ticker calculation
@@ -15,16 +15,16 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public GetTickerOptions(SharedTickerType? tickerCalcType = null) : base(false)
public GetSpotTickerOptions(string exchange, SharedTickerType? tickerCalcType = null) : base(exchange, false, nameof(ISpotTickerRestClient.GetSpotTickerAsync))
{
TickerType = tickerCalcType ?? SharedTickerType.Day24H;
}
/// <inheritdoc />
public override string ToString(string exchange)
public override string ToString()
{
var sb = new StringBuilder(base.ToString(exchange));
sb.AppendLine($"Ticker time calc type: {TickerType}");
var sb = new StringBuilder(base.ToString());
sb.AppendLine($"Ticker data calculation type: {TickerType}");
return sb.ToString();
}
}
@@ -5,7 +5,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for requesting tickers
/// </summary>
public class GetTickersOptions : EndpointOptions<GetTickersRequest>
public class GetSpotTickersOptions : EndpointOptions<GetTickersRequest, ISpotTickerRestClient>
{
/// <summary>
/// Type of ticker calculation
@@ -15,16 +15,16 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public GetTickersOptions(SharedTickerType? tickerCalcType = null) : base(false)
public GetSpotTickersOptions(string exchange, SharedTickerType? tickerCalcType = null) : base(exchange, false, nameof(ISpotTickerRestClient.GetSpotTickersAsync))
{
TickerType = tickerCalcType ?? SharedTickerType.Day24H;
}
/// <inheritdoc />
public override string ToString(string exchange)
public override string ToString()
{
var sb = new StringBuilder(base.ToString(exchange));
sb.AppendLine($"Ticker time calc type: {TickerType}");
var sb = new StringBuilder(base.ToString());
sb.AppendLine($"Ticker data calculation type: {TickerType}");
return sb.ToString();
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting spot trigger order
/// </summary>
public class GetSpotTriggerOrderOptions : EndpointOptions<GetOrderRequest, ISpotTriggerOrderRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetSpotTriggerOrderOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(ISpotTriggerOrderRestClient.GetSpotTriggerOrderAsync))
{
}
}
}
@@ -0,0 +1,46 @@
using CryptoExchange.Net.Objects;
using System;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting user trades
/// </summary>
public class GetSpotUserTradesOptions : PaginatedEndpointOptions<GetUserTradesRequest, ISpotOrderRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetSpotUserTradesOptions(string exchange, bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit)
: base(exchange, supportsAscending, supportsDescending, timeFilterSupported, maxLimit, true, nameof(ISpotOrderRestClient.GetSpotUserTradesAsync))
{
}
/// <inheritdoc />
public override Error? ValidateRequest(GetUserTradesRequest request, ISpotOrderRestClient client)
{
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetUserTradesRequest.Direction), $"Ascending direction is not supported");
if (!SupportsDescending && request.Direction == DataDirection.Descending)
return ArgumentError.Invalid(nameof(GetUserTradesRequest.Direction), $"Descending direction is not supported");
if (MaxAge.HasValue && request.StartTime < DateTime.UtcNow.Add(-MaxAge.Value))
return ArgumentError.Invalid(nameof(GetUserTradesRequest.StartTime), $"Only the most recent {MaxAge} period data is available");
if (!TimePeriodFilterSupport)
{
// When going descending we can still allow startTime filter to limit the results
var now = DateTime.UtcNow;
if ((request.Direction != DataDirection.Descending && request.StartTime != null)
|| (request.EndTime != null && now - request.EndTime > TimeSpan.FromSeconds(5)))
{
return ArgumentError.Invalid(nameof(GetDepositsRequest.StartTime), $"Time filter is not supported");
}
}
return base.ValidateRequest(request, client);
}
}
}
@@ -7,29 +7,29 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for requesting trade history
/// </summary>
public class GetTradeHistoryOptions : PaginatedEndpointOptions<GetTradeHistoryRequest>
public class GetTradeHistoryOptions : PaginatedEndpointOptions<GetTradeHistoryRequest, ITradeHistoryRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetTradeHistoryOptions(bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit, bool needsAuthentication)
: base(supportsAscending, supportsDescending, timeFilterSupported, maxLimit, needsAuthentication)
public GetTradeHistoryOptions(string exchange, bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit, bool needsAuthentication)
: base(exchange, supportsAscending, supportsDescending, timeFilterSupported, maxLimit, needsAuthentication, nameof(ITradeHistoryRestClient.GetTradeHistoryAsync))
{
}
/// <inheritdoc />
public override Error? ValidateRequest(string exchange, GetTradeHistoryRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
public override Error? ValidateRequest(GetTradeHistoryRequest request, ITradeHistoryRestClient client)
{
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.Direction), $"Ascending direction is not supported");
return ArgumentError.Invalid(nameof(GetTradeHistoryRequest.Direction), $"Ascending direction is not supported");
if (!SupportsDescending && request.Direction == DataDirection.Descending)
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.Direction), $"Descending direction is not supported");
return ArgumentError.Invalid(nameof(GetTradeHistoryRequest.Direction), $"Descending direction is not supported");
if (MaxAge.HasValue && request.StartTime < DateTime.UtcNow.Add(-MaxAge.Value))
return ArgumentError.Invalid(nameof(GetKlinesRequest.StartTime), $"Only the most recent {MaxAge} period data is available");
return ArgumentError.Invalid(nameof(GetTradeHistoryRequest.StartTime), $"Only the most recent {MaxAge} period data is available");
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
return base.ValidateRequest(request, client);
}
}
}
@@ -1,54 +0,0 @@
using CryptoExchange.Net.Objects;
using System;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting user trades
/// </summary>
public class GetUserTradesOptions : PaginatedEndpointOptions<GetUserTradesRequest>
{
/// <summary>
/// ctor
/// </summary>
public GetUserTradesOptions(bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit)
: base(supportsAscending, supportsDescending, timeFilterSupported, maxLimit, true)
{
}
/// <inheritdoc />
public override Error? ValidateRequest(string exchange, GetUserTradesRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
{
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.Direction), $"Ascending direction is not supported");
if (!SupportsDescending && request.Direction == DataDirection.Descending)
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.Direction), $"Descending direction is not supported");
if (MaxAge.HasValue && request.StartTime < DateTime.UtcNow.Add(-MaxAge.Value))
return ArgumentError.Invalid(nameof(GetKlinesRequest.StartTime), $"Only the most recent {MaxAge} period data is available");
if (!TimePeriodFilterSupport)
{
// When going descending we can still allow startTime filter to limit the results
var now = DateTime.UtcNow;
if ((request.Direction != DataDirection.Descending && request.StartTime != null)
|| (request.EndTime != null && now - request.EndTime > TimeSpan.FromSeconds(5)))
{
return ArgumentError.Invalid(nameof(GetDepositsRequest.StartTime), $"Time filter is not supported");
}
}
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
}
/// <inheritdoc />
public override string ToString(string exchange)
{
var sb = new StringBuilder(base.ToString(exchange));
sb.AppendLine($"Time filter supported: {TimePeriodFilterSupport}");
return sb.ToString();
}
}
}
@@ -7,18 +7,18 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for requesting withdrawals
/// </summary>
public class GetWithdrawalsOptions : PaginatedEndpointOptions<GetWithdrawalsRequest>
public class GetWithdrawalsOptions : PaginatedEndpointOptions<GetWithdrawalsRequest, IWithdrawalRestClient>
{
/// <summary>
/// ctor
/// </summary>
public GetWithdrawalsOptions(bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit)
: base(supportsAscending, supportsDescending, timeFilterSupported, maxLimit, true)
public GetWithdrawalsOptions(string exchange, bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit)
: base(exchange, supportsAscending, supportsDescending, timeFilterSupported, maxLimit, true, nameof(IWithdrawalRestClient.GetWithdrawalsAsync))
{
}
/// <inheritdoc />
public override Error? ValidateRequest(string exchange, GetWithdrawalsRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
public override Error? ValidateRequest(GetWithdrawalsRequest request, IWithdrawalRestClient client)
{
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.Direction), $"Ascending direction is not supported");
@@ -27,7 +27,7 @@ namespace CryptoExchange.Net.SharedApis
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.Direction), $"Descending direction is not supported");
if (MaxAge.HasValue && request.StartTime < DateTime.UtcNow.Add(-MaxAge.Value))
return ArgumentError.Invalid(nameof(GetKlinesRequest.StartTime), $"Only the most recent {MaxAge} period data is available");
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.StartTime), $"Only the most recent {MaxAge} period data is available");
if (!TimePeriodFilterSupport)
{
@@ -36,19 +36,11 @@ namespace CryptoExchange.Net.SharedApis
if ((request.Direction != DataDirection.Descending && request.StartTime != null)
|| (request.EndTime != null && now - request.EndTime > TimeSpan.FromSeconds(5)))
{
return ArgumentError.Invalid(nameof(GetDepositsRequest.StartTime), $"Time filter is not supported");
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.StartTime), $"Time filter is not supported");
}
}
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
}
/// <inheritdoc />
public override string ToString(string exchange)
{
var sb = new StringBuilder(base.ToString(exchange));
sb.AppendLine($"Time filter supported: {TimePeriodFilterSupport}");
return sb.ToString();
return base.ValidateRequest(request, client);
}
}
}
@@ -7,11 +7,14 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for paginated endpoints
/// </summary>
/// <typeparam name="T"></typeparam>
#if NET5_0_OR_GREATER
public class PaginatedEndpointOptions<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T> : EndpointOptions<T> where T : SharedRequest
public class PaginatedEndpointOptions<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TRequest, TClient> : EndpointOptions<TRequest, TClient>
where TRequest : SharedRequest
where TClient : ISharedClient
#else
public class PaginatedEndpointOptions<T> : EndpointOptions<T> where T : SharedRequest
public abstract class PaginatedEndpointOptions<TRequest, TClient> : EndpointOptions<TRequest, TClient>
where TRequest : SharedRequest
where TClient : ISharedClient
#endif
{
/// <summary>
@@ -42,11 +45,13 @@ namespace CryptoExchange.Net.SharedApis
/// ctor
/// </summary>
public PaginatedEndpointOptions(
string exchange,
bool supportsAscending,
bool supportsDescending,
bool timePeriodSupport,
int maxLimit,
bool needsAuthentication) : base(needsAuthentication)
bool needsAuthentication,
string requestName) : base(exchange, needsAuthentication, requestName)
{
SupportsAscending = supportsAscending;
SupportsDescending = supportsDescending;
@@ -55,14 +60,15 @@ namespace CryptoExchange.Net.SharedApis
}
/// <inheritdoc />
public override string ToString(string exchange)
public override string ToString()
{
var sb = new StringBuilder(base.ToString(exchange));
var sb = new StringBuilder(base.ToString());
sb.AppendLine($"Ascending retrieval supported: {SupportsAscending}");
sb.AppendLine($"Descending retrieval supported: {SupportsDescending}");
sb.AppendLine($"Time period filter support: {TimePeriodFilterSupport}");
sb.AppendLine($"Time period filter supported: {TimePeriodFilterSupport}");
sb.AppendLine($"Max limit: {MaxLimit}");
sb.AppendLine($"Max age: {MaxAge}");
if (MaxAge.HasValue)
sb.AppendLine($"Max age: {MaxAge}");
return sb.ToString();
}
}
@@ -7,7 +7,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for placing a new futures order
/// </summary>
public class PlaceFuturesOrderOptions : EndpointOptions<PlaceFuturesOrderRequest>
public class PlaceFuturesOrderOptions : EndpointOptions<PlaceFuturesOrderRequest, IFuturesOrderRestClient>
{
/// <summary>
/// Whether or not the API supports setting take profit / stop loss with the order
@@ -17,7 +17,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public PlaceFuturesOrderOptions(bool supportsTpSl) : base(true)
public PlaceFuturesOrderOptions(string exchange, bool supportsTpSl) : base(exchange, true, nameof(IFuturesOrderRestClient.PlaceFuturesOrderAsync))
{
SupportsTpSl = supportsTpSl;
}
@@ -25,14 +25,10 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Validate a request
/// </summary>
public Error? ValidateRequest(
string exchange,
public override Error? ValidateRequest(
PlaceFuturesOrderRequest request,
TradingMode? tradingMode,
TradingMode[] supportedApiTypes,
SharedOrderType[] supportedOrderTypes,
SharedTimeInForce[] supportedTimeInForce,
SharedQuantitySupport quantitySupport)
IFuturesOrderRestClient client
)
{
if (!SupportsTpSl && (request.StopLossPrice != null || request.TakeProfitPrice != null))
return ArgumentError.Invalid(nameof(PlaceFuturesOrderRequest.StopLossPrice) + " / " + nameof(PlaceFuturesOrderRequest.TakeProfitPrice), "Tp/Sl parameters not supported");
@@ -40,17 +36,17 @@ namespace CryptoExchange.Net.SharedApis
if (request.OrderType == SharedOrderType.Other)
throw new ArgumentException("OrderType can't be `Other`", nameof(request.OrderType));
if (!supportedOrderTypes.Contains(request.OrderType))
if (!client.FuturesSupportedOrderTypes.Contains(request.OrderType))
return ArgumentError.Invalid(nameof(PlaceFuturesOrderRequest.OrderType), "Order type not supported");
if (request.TimeInForce != null && !supportedTimeInForce.Contains(request.TimeInForce.Value))
if (request.TimeInForce != null && !client.FuturesSupportedTimeInForce.Contains(request.TimeInForce.Value))
return ArgumentError.Invalid(nameof(PlaceFuturesOrderRequest.TimeInForce), "Order time in force not supported");
var quantityError = quantitySupport.Validate(request.Side, request.OrderType, request.Quantity);
var quantityError = client.FuturesSupportedOrderQuantity.Validate(request.Side, request.OrderType, request.Quantity);
if (quantityError != null)
return quantityError;
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
return base.ValidateRequest(request, client);
}
}
@@ -5,17 +5,17 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for placing a new spot trigger order
/// </summary>
public class PlaceFuturesTriggerOrderOptions : EndpointOptions<PlaceFuturesTriggerOrderRequest>
public class PlaceFuturesTriggerOrderOptions : EndpointOptions<PlaceFuturesTriggerOrderRequest, IFuturesTriggerOrderRestClient>
{
/// <summary>
/// When true the API holds the funds until the order is triggered or canceled. When true the funds will only be required when the order is triggered and will fail if the funds are not available at that time.
/// When true the API holds the funds until the order is triggered or canceled. When false the funds will only be required when the order is triggered and will fail if the funds are not available at that time.
/// </summary>
public bool HoldsFunds { get; set; }
/// <summary>
/// ctor
/// </summary>
public PlaceFuturesTriggerOrderOptions(bool holdsFunds) : base(true)
public PlaceFuturesTriggerOrderOptions(string exchange, bool holdsFunds) : base(exchange, true, nameof(IFuturesTriggerOrderRestClient.PlaceFuturesTriggerOrderAsync))
{
HoldsFunds = holdsFunds;
}
@@ -23,19 +23,15 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Validate a request
/// </summary>
public Error? ValidateRequest(
string exchange,
public override Error? ValidateRequest(
PlaceFuturesTriggerOrderRequest request,
TradingMode? tradingMode,
TradingMode[] supportedApiTypes,
SharedOrderSide side,
SharedQuantitySupport quantitySupport)
IFuturesTriggerOrderRestClient client)
{
var quantityError = quantitySupport.Validate(side, request.OrderPrice == null ? SharedOrderType.Market : SharedOrderType.Limit, request.Quantity);
if (quantityError != null)
return quantityError;
//var quantityError = client.FuturesSupportedOrderQuantity.Validate(request.OrderDirection, request.OrderPrice == null ? SharedOrderType.Market : SharedOrderType.Limit, request.Quantity);
//if (quantityError != null)
// return quantityError;
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
return base.ValidateRequest(request, client);
}
}
}
@@ -7,42 +7,37 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for placing a new spot order
/// </summary>
public class PlaceSpotOrderOptions : EndpointOptions<PlaceSpotOrderRequest>
public class PlaceSpotOrderOptions : EndpointOptions<PlaceSpotOrderRequest, ISpotOrderRestClient>
{
/// <summary>
/// ctor
/// </summary>
public PlaceSpotOrderOptions() : base(true)
public PlaceSpotOrderOptions(string exchange) : base(exchange, true, nameof(ISpotOrderRestClient.PlaceSpotOrderAsync))
{
}
/// <summary>
/// Validate a request
/// </summary>
public Error? ValidateRequest(
string exchange,
public override Error? ValidateRequest(
PlaceSpotOrderRequest request,
TradingMode? tradingMode,
TradingMode[] supportedApiTypes,
SharedOrderType[] supportedOrderTypes,
SharedTimeInForce[] supportedTimeInForce,
SharedQuantitySupport quantitySupport)
ISpotOrderRestClient client)
{
if (request.OrderType == SharedOrderType.Other)
throw new ArgumentException("OrderType can't be `Other`", nameof(request.OrderType));
if (!supportedOrderTypes.Contains(request.OrderType))
if (!client.SpotSupportedOrderTypes.Contains(request.OrderType))
return ArgumentError.Invalid(nameof(PlaceSpotOrderRequest.OrderType), "Order type not supported");
if (request.TimeInForce != null && !supportedTimeInForce.Contains(request.TimeInForce.Value))
if (request.TimeInForce != null && !client.SpotSupportedTimeInForce.Contains(request.TimeInForce.Value))
return ArgumentError.Invalid(nameof(PlaceSpotOrderRequest.TimeInForce), "Order time in force not supported");
var quantityError = quantitySupport.Validate(request.Side, request.OrderType, request.Quantity);
var quantityError = client.SpotSupportedOrderQuantity.Validate(request.Side, request.OrderType, request.Quantity);
if (quantityError != null)
return quantityError;
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
return base.ValidateRequest(request, client);
}
}
}
@@ -5,17 +5,17 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for placing a new spot trigger order
/// </summary>
public class PlaceSpotTriggerOrderOptions : EndpointOptions<PlaceSpotTriggerOrderRequest>
public class PlaceSpotTriggerOrderOptions : EndpointOptions<PlaceSpotTriggerOrderRequest, ISpotOrderRestClient>
{
/// <summary>
/// When true the API holds the funds until the order is triggered or canceled. When true the funds will only be required when the order is triggered and will fail if the funds are not available at that time.
/// When true the API holds the funds until the order is triggered or canceled. When false the funds will only be required when the order is triggered and will fail if the funds are not available at that time.
/// </summary>
public bool HoldsFunds { get; set; }
/// <summary>
/// ctor
/// </summary>
public PlaceSpotTriggerOrderOptions(bool holdsFunds) : base(true)
public PlaceSpotTriggerOrderOptions(string exchange, bool holdsFunds) : base(exchange, true, nameof(ISpotTriggerOrderRestClient.PlaceSpotTriggerOrderAsync))
{
HoldsFunds = holdsFunds;
}
@@ -23,18 +23,15 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Validate a request
/// </summary>
public Error? ValidateRequest(
string exchange,
public override Error? ValidateRequest(
PlaceSpotTriggerOrderRequest request,
TradingMode? tradingMode,
TradingMode[] supportedApiTypes,
SharedQuantitySupport quantitySupport)
ISpotOrderRestClient client)
{
var quantityError = quantitySupport.Validate(request.OrderSide, request.OrderPrice == null ? SharedOrderType.Market : SharedOrderType.Limit, request.Quantity);
var quantityError = client.SpotSupportedOrderQuantity.Validate(request.OrderSide, request.OrderPrice == null ? SharedOrderType.Market : SharedOrderType.Limit, request.Quantity);
if (quantityError != null)
return quantityError;
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
return base.ValidateRequest(request, client);
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for setting a TP/SL
/// </summary>
public class SetFuturesTpSlOptions : EndpointOptions<SetTpSlRequest, IFuturesTpSlRestClient>
{
/// <summary>
/// ctor
/// </summary>
public SetFuturesTpSlOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IFuturesTpSlRestClient.SetFuturesTpSlAsync))
{
}
}
}
@@ -3,12 +3,12 @@
/// <summary>
/// Options for setting leverage
/// </summary>
public class SetLeverageOptions : EndpointOptions<SetLeverageRequest>
public class SetLeverageOptions : EndpointOptions<SetLeverageRequest, ILeverageRestClient>
{
/// <summary>
/// ctor
/// </summary>
public SetLeverageOptions() : base(true)
public SetLeverageOptions(string exchange) : base(exchange, true, nameof(ILeverageRestClient.SetLeverageAsync))
{
}
}
@@ -3,12 +3,12 @@
/// <summary>
/// Options for setting position mode
/// </summary>
public class SetPositionModeOptions : EndpointOptions<SetPositionModeRequest>
public class SetPositionModeOptions : EndpointOptions<SetPositionModeRequest, IPositionModeRestClient>
{
/// <summary>
/// ctor
/// </summary>
public SetPositionModeOptions() : base(true)
public SetPositionModeOptions(string exchange) : base(exchange, true, nameof(IPositionModeRestClient.SetPositionModeOptions))
{
}
}
@@ -6,7 +6,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for requesting a transfer
/// </summary>
public class TransferOptions : EndpointOptions<TransferRequest>
public class TransferOptions : EndpointOptions<TransferRequest, ITransferRestClient>
{
/// <summary>
/// Supported account types
@@ -16,7 +16,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public TransferOptions(SharedAccountType[] accountTypes) : base(true)
public TransferOptions(string exchange, SharedAccountType[] accountTypes) : base(exchange, true, nameof(ITransferRestClient.TransferAsync))
{
SupportedAccountTypes = accountTypes;
}
@@ -24,11 +24,9 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Validate a request
/// </summary>
public new Error? ValidateRequest(
string exchange,
public override Error? ValidateRequest(
TransferRequest request,
TradingMode? tradingMode,
TradingMode[] supportedApiTypes)
ITransferRestClient client)
{
if (!SupportedAccountTypes.Contains(request.FromAccountType))
return ArgumentError.Invalid(nameof(request.FromAccountType), "Invalid FromAccountType");
@@ -36,7 +34,7 @@ namespace CryptoExchange.Net.SharedApis
if (!SupportedAccountTypes.Contains(request.ToAccountType))
return ArgumentError.Invalid(nameof(request.FromAccountType), "Invalid ToAccountType");
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
return base.ValidateRequest(request, client);
}
}
}
@@ -3,12 +3,12 @@
/// <summary>
/// Options for requesting a withdrawal
/// </summary>
public class WithdrawOptions : EndpointOptions<WithdrawRequest>
public class WithdrawOptions : EndpointOptions<WithdrawRequest, IWithdrawRestClient>
{
/// <summary>
/// ctor
/// </summary>
public WithdrawOptions() : base(true)
public WithdrawOptions(string exchange) : base(exchange, true, nameof(IWithdrawRestClient.WithdrawAsync))
{
}
}
@@ -1,4 +1,5 @@
using System;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
@@ -8,13 +9,9 @@ namespace CryptoExchange.Net.SharedApis
public class ParameterDescription
{
/// <summary>
/// Name of the parameter
/// Possible names for the parameter. One of these names can be used to provide the parameter in the ExchangeParameters of the request.
/// </summary>
public string? Name { get; set; }
/// <summary>
/// Names of the parameters
/// </summary>
public string[]? Names { get; set; }
public string[] Names { get; set; }
/// <summary>
/// Type of the value
/// </summary>
@@ -33,7 +30,7 @@ namespace CryptoExchange.Net.SharedApis
/// </summary>
public ParameterDescription(string parameterName, Type valueType, string description, object exampleValue)
{
Name = parameterName;
Names = [parameterName];
ValueType = valueType;
Description = description;
ExampleValue = exampleValue;
@@ -53,9 +50,7 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override string ToString()
{
if (Name != null)
return $"[{ValueType.Name}] {Name}: {Description} | example: {ExampleValue}";
return $"[{ValueType.Name}] {string.Join(" / ", Names!)}: {Description} | example: {ExampleValue}";
return $"[{ValueType.Name}] {string.Join(" / ", Names.Select(x => $"\"{x}\""))}: {Description}";
}
}
}
@@ -0,0 +1,19 @@
using CryptoExchange.Net.Objects;
using System;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for subscribing to balance updates
/// </summary>
public class SubscribeBalanceOptions : EndpointOptions<SubscribeBalancesRequest, IBalanceSocketClient>
{
/// <summary>
/// ctor
/// </summary>
public SubscribeBalanceOptions(string exchange, bool needsAuthentication) : base(exchange, needsAuthentication, nameof(IBalanceSocketClient.SubscribeToBalanceUpdatesAsync))
{
}
}
}
@@ -0,0 +1,19 @@
using CryptoExchange.Net.Objects;
using System;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for subscribing to book ticker updates
/// </summary>
public class SubscribeBookTickerOptions : EndpointOptions<SubscribeBookTickerRequest, IBookTickerSocketClient>
{
/// <summary>
/// ctor
/// </summary>
public SubscribeBookTickerOptions(string exchange, bool needsAuthentication) : base(exchange, needsAuthentication, nameof(IBookTickerSocketClient.SubscribeToBookTickerUpdatesAsync))
{
}
}
}
@@ -0,0 +1,19 @@
using CryptoExchange.Net.Objects;
using System;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for subscribing to order updates
/// </summary>
public class SubscribeFuturesOrderOptions : EndpointOptions<SubscribeFuturesOrderRequest, IFuturesOrderSocketClient>
{
/// <summary>
/// ctor
/// </summary>
public SubscribeFuturesOrderOptions(string exchange, bool needsAuthentication) : base(exchange, needsAuthentication, nameof(IFuturesOrderSocketClient.SubscribeToFuturesOrderUpdatesAsync))
{
}
}
}
@@ -7,7 +7,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for subscribing to kline/candlestick updates
/// </summary>
public class SubscribeKlineOptions : EndpointOptions<SubscribeKlineRequest>
public class SubscribeKlineOptions : EndpointOptions<SubscribeKlineRequest, IKlineSocketClient>
{
/// <summary>
/// Kline intervals supported for updates
@@ -17,7 +17,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public SubscribeKlineOptions(bool needsAuthentication) : base(needsAuthentication)
public SubscribeKlineOptions(string exchange, bool needsAuthentication) : base(exchange, needsAuthentication, nameof(IKlineSocketClient.SubscribeToKlineUpdatesAsync))
{
SupportIntervals = new[]
{
@@ -41,7 +41,8 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public SubscribeKlineOptions(bool needsAuthentication, params SharedKlineInterval[] intervals) : base(needsAuthentication)
public SubscribeKlineOptions(string exchange, bool needsAuthentication, params SharedKlineInterval[] intervals)
: base(exchange, needsAuthentication, nameof(IKlineSocketClient.SubscribeToKlineUpdatesAsync))
{
SupportIntervals = intervals;
}
@@ -56,12 +57,12 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Validate a request
/// </summary>
public override Error? ValidateRequest(string exchange, SubscribeKlineRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
public override Error? ValidateRequest(SubscribeKlineRequest request, IKlineSocketClient client)
{
if (!IsSupported(request.Interval))
return ArgumentError.Invalid(nameof(SubscribeKlineRequest.Interval), "Interval not supported");
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
return base.ValidateRequest(request, client);
}
}
}
@@ -7,7 +7,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for subscribing to order book snapshot updates
/// </summary>
public class SubscribeOrderBookOptions : EndpointOptions<SubscribeOrderBookRequest>
public class SubscribeOrderBookOptions : EndpointOptions<SubscribeOrderBookRequest, IOrderBookSocketClient>
{
/// <summary>
/// Order book depths supported for updates
@@ -17,7 +17,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public SubscribeOrderBookOptions(bool needsAuthentication, int[] limits) : base(needsAuthentication)
public SubscribeOrderBookOptions(string exchange, bool needsAuthentication, int[] limits) : base(exchange, needsAuthentication, nameof(IOrderBookSocketClient.SubscribeToOrderBookUpdatesAsync))
{
SupportedLimits = limits;
}
@@ -25,12 +25,12 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Validate a request
/// </summary>
public override Error? ValidateRequest(string exchange, SubscribeOrderBookRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
public override Error? ValidateRequest(SubscribeOrderBookRequest request, IOrderBookSocketClient client)
{
if (request.Limit != null && !SupportedLimits.Contains(request.Limit.Value))
return ArgumentError.Invalid(nameof(SubscribeOrderBookRequest.Limit), "Limit not supported");
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
return base.ValidateRequest(request, client);
}
}
}
@@ -0,0 +1,19 @@
using CryptoExchange.Net.Objects;
using System;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for subscribing to position updates
/// </summary>
public class SubscribePositionOptions : EndpointOptions<SubscribePositionRequest, IPositionSocketClient>
{
/// <summary>
/// ctor
/// </summary>
public SubscribePositionOptions(string exchange, bool needsAuthentication) : base(exchange, needsAuthentication, nameof(IPositionSocketClient.SubscribeToPositionUpdatesAsync))
{
}
}
}
@@ -0,0 +1,19 @@
using CryptoExchange.Net.Objects;
using System;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for subscribing to order updates
/// </summary>
public class SubscribeSpotOrderOptions : EndpointOptions<SubscribeSpotOrderRequest, ISpotOrderSocketClient>
{
/// <summary>
/// ctor
/// </summary>
public SubscribeSpotOrderOptions(string exchange, bool needsAuthentication) : base(exchange, needsAuthentication, nameof(ISpotOrderSocketClient.SubscribeToSpotOrderUpdatesAsync))
{
}
}
}
@@ -3,7 +3,7 @@
/// <summary>
/// Options for subscribing to ticker updates
/// </summary>
public class SubscribeTickerOptions : EndpointOptions<SubscribeTickerRequest>
public class SubscribeTickerOptions : EndpointOptions<SubscribeTickerRequest, ITickerSocketClient>
{
/// <summary>
/// Type of ticker calculation
@@ -13,7 +13,7 @@
/// <summary>
/// ctor
/// </summary>
public SubscribeTickerOptions(SharedTickerType? tickerCalcType = null) : base(false)
public SubscribeTickerOptions(string exchange, SharedTickerType? tickerCalcType = null) : base(exchange, false, nameof(ITickerSocketClient.SubscribeToTickerUpdatesAsync))
{
TickerType = tickerCalcType ?? SharedTickerType.Day24H;
}
@@ -3,7 +3,7 @@
/// <summary>
/// Options for subscribing to ticker updates
/// </summary>
public class SubscribeTickersOptions : EndpointOptions<SubscribeAllTickersRequest>
public class SubscribeTickersOptions : EndpointOptions<SubscribeAllTickersRequest, ITickersSocketClient>
{
/// <summary>
/// Type of ticker calculation
@@ -13,7 +13,7 @@
/// <summary>
/// ctor
/// </summary>
public SubscribeTickersOptions(SharedTickerType? tickerCalcType = null) : base(false)
public SubscribeTickersOptions(string exchange, SharedTickerType? tickerCalcType = null) : base(exchange, false, nameof(ITickersSocketClient.SubscribeToAllTickersUpdatesAsync))
{
TickerType = tickerCalcType ?? SharedTickerType.Day24H;
}
@@ -0,0 +1,19 @@
using CryptoExchange.Net.Objects;
using System;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for subscribing to trade updates
/// </summary>
public class SubscribeTradeOptions : EndpointOptions<SubscribeTradeRequest, ITradeSocketClient>
{
/// <summary>
/// ctor
/// </summary>
public SubscribeTradeOptions(string exchange, bool needsAuthentication) : base(exchange, needsAuthentication, nameof(ITradeSocketClient.SubscribeToTradeUpdatesAsync))
{
}
}
}
@@ -0,0 +1,19 @@
using CryptoExchange.Net.Objects;
using System;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for subscribing to user trade updates
/// </summary>
public class SubscribeUserTradeOptions : EndpointOptions<SubscribeUserTradeRequest, IUserTradeSocketClient>
{
/// <summary>
/// ctor
/// </summary>
public SubscribeUserTradeOptions(string exchange, bool needsAuthentication) : base(exchange, needsAuthentication, nameof(IUserTradeSocketClient.SubscribeToUserTradeUpdatesAsync))
{
}
}
}
@@ -15,7 +15,8 @@
/// </summary>
/// <param name="asset">Asset to retrieve info on</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetAssetRequest(string asset, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public GetAssetRequest(string asset, ExchangeParameters? exchangeParameters = null)
: base(null, exchangeParameters)
{
Asset = asset;
}
@@ -9,7 +9,8 @@
/// ctor
/// </summary>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetAssetsRequest(ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public GetAssetsRequest(ExchangeParameters? exchangeParameters = null)
: base(null, exchangeParameters)
{
}
}
@@ -15,7 +15,8 @@
/// </summary>
/// <param name="tradingMode">Trading mode</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetBalancesRequest(TradingMode tradingMode, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public GetBalancesRequest(TradingMode tradingMode, ExchangeParameters? exchangeParameters = null)
: base(tradingMode, exchangeParameters)
{
AccountType = tradingMode.ToAccountType();
}
@@ -25,7 +26,8 @@
/// </summary>
/// <param name="accountType">Account type</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetBalancesRequest(SharedAccountType? accountType = null, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public GetBalancesRequest(SharedAccountType? accountType = null, ExchangeParameters? exchangeParameters = null)
: base(null, exchangeParameters)
{
AccountType = accountType;
}
@@ -20,7 +20,8 @@
/// <param name="asset">Asset name to get address for</param>
/// <param name="network">Network name</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetDepositAddressesRequest(string asset, string? network = null, ExchangeParameters? exchangeParameters = null): base(exchangeParameters)
public GetDepositAddressesRequest(string asset, string? network = null, ExchangeParameters? exchangeParameters = null)
: base(null, exchangeParameters)
{
Asset = asset;
Network = network;
@@ -37,7 +37,8 @@ namespace CryptoExchange.Net.SharedApis
/// <param name="limit">Max number of results</param>
/// <param name="direction">Data direction</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetDepositsRequest(string? asset = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, DataDirection? direction = null, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public GetDepositsRequest(string? asset = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, DataDirection? direction = null, ExchangeParameters? exchangeParameters = null)
: base(null, exchangeParameters)
{
Asset = asset;
StartTime = startTime;
@@ -5,10 +5,6 @@
/// </summary>
public record GetOpenOrdersRequest : SharedRequest
{
/// <summary>
/// Trading mode
/// </summary>
public TradingMode? TradingMode { get; set; }
/// <summary>
/// Symbol filter
/// </summary>
@@ -19,9 +15,8 @@
/// </summary>
/// <param name="tradingMode">Trading mode</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetOpenOrdersRequest(TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public GetOpenOrdersRequest(TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null) : base(tradingMode, exchangeParameters)
{
TradingMode = tradingMode;
}
/// <summary>
@@ -29,7 +24,7 @@
/// </summary>
/// <param name="symbol">Symbol to retrieve open orders for</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetOpenOrdersRequest(SharedSymbol symbol, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public GetOpenOrdersRequest(SharedSymbol symbol, ExchangeParameters? exchangeParameters = null) : base(symbol.TradingMode, exchangeParameters)
{
Symbol = symbol;
}
@@ -7,10 +7,6 @@ namespace CryptoExchange.Net.SharedApis
/// </summary>
public record GetPositionHistoryRequest : SharedRequest
{
/// <summary>
/// Trading mode
/// </summary>
public TradingMode? TradingMode { get; set; }
/// <summary>
/// Symbol
/// </summary>
@@ -41,7 +37,8 @@ namespace CryptoExchange.Net.SharedApis
/// <param name="limit">Max number of results</param>
/// <param name="direction">Data direction</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetPositionHistoryRequest(SharedSymbol symbol, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, DataDirection? direction = null, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public GetPositionHistoryRequest(SharedSymbol symbol, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, DataDirection? direction = null, ExchangeParameters? exchangeParameters = null)
: base(symbol.TradingMode, exchangeParameters)
{
Symbol = symbol;
StartTime = startTime;
@@ -59,7 +56,8 @@ namespace CryptoExchange.Net.SharedApis
/// <param name="limit">Max number of results</param>
/// <param name="direction">Data direction</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetPositionHistoryRequest(TradingMode? tradeMode = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, DataDirection? direction = null, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public GetPositionHistoryRequest(TradingMode? tradeMode = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, DataDirection? direction = null, ExchangeParameters? exchangeParameters = null)
: base(tradeMode, exchangeParameters)
{
TradingMode = tradeMode;
StartTime = startTime;
@@ -5,10 +5,6 @@
/// </summary>
public record GetPositionModeRequest : SharedRequest
{
/// <summary>
/// Trading mode
/// </summary>
public TradingMode? TradingMode { get; set; }
/// <summary>
/// Symbol. Some exchanges set position mode per symbol
/// </summary>
@@ -19,7 +15,8 @@
/// </summary>
/// <param name="symbol">Symbol to retrieve position mode for</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetPositionModeRequest(SharedSymbol symbol, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public GetPositionModeRequest(SharedSymbol symbol, ExchangeParameters? exchangeParameters = null)
: base(symbol.TradingMode, exchangeParameters)
{
Symbol = symbol;
}
@@ -29,9 +26,9 @@
/// </summary>
/// <param name="tradingMode">Trading mode</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetPositionModeRequest(TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public GetPositionModeRequest(TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null)
: base(tradingMode, exchangeParameters)
{
TradingMode = tradingMode;
}
}
}
@@ -5,10 +5,6 @@
/// </summary>
public record GetPositionsRequest : SharedRequest
{
/// <summary>
/// Trading mode
/// </summary>
public TradingMode? TradingMode { get; set; }
/// <summary>
/// Symbol filter, required for some exchanges
/// </summary>
@@ -19,9 +15,8 @@
/// </summary>
/// <param name="tradingMode">Trading mode</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetPositionsRequest(TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public GetPositionsRequest(TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null) : base(tradingMode, exchangeParameters)
{
TradingMode = tradingMode;
}
/// <summary>
@@ -29,7 +24,7 @@
/// </summary>
/// <param name="symbol">Symbol to retriecve positions for</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetPositionsRequest(SharedSymbol symbol, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public GetPositionsRequest(SharedSymbol symbol, ExchangeParameters? exchangeParameters = null) : base(symbol.TradingMode, exchangeParameters)
{
Symbol = symbol;
}
@@ -5,19 +5,13 @@
/// </summary>
public record GetSymbolsRequest : SharedRequest
{
/// <summary>
/// Filter by trading mode
/// </summary>
public TradingMode? TradingMode { get; set; }
/// <summary>
/// ctor
/// </summary>
/// <param name="tradingMode">Trading mode filter</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetSymbolsRequest(TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public GetSymbolsRequest(TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null) : base(tradingMode, exchangeParameters)
{
TradingMode = tradingMode;
}
}
}
@@ -5,19 +5,13 @@
/// </summary>
public record GetTickersRequest : SharedRequest
{
/// <summary>
/// Trading mode
/// </summary>
public TradingMode? TradingMode { get; set; }
/// <summary>
/// ctor
/// </summary>
/// <param name="tradingMode">Trading mode</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetTickersRequest(TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public GetTickersRequest(TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null) : base(tradingMode, exchangeParameters)
{
TradingMode = tradingMode;
}
}
}
@@ -37,7 +37,8 @@ namespace CryptoExchange.Net.SharedApis
/// <param name="limit">Max number of results</param>
/// <param name="direction">Data direction</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetWithdrawalsRequest(string? asset = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, DataDirection? direction = null, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public GetWithdrawalsRequest(string? asset = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, DataDirection? direction = null, ExchangeParameters? exchangeParameters = null)
: base(null, exchangeParameters)
{
Asset = asset;
StartTime = startTime;
@@ -1,29 +0,0 @@
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to keep-alive the update stream for the specified listen key
/// </summary>
public record KeepAliveListenKeyRequest : SharedRequest
{
/// <summary>
/// The key to stop updates for
/// </summary>
public string ListenKey { get; set; }
/// <summary>
/// Trading mode
/// </summary>
public TradingMode? TradingMode { get; set; }
/// <summary>
/// ctor
/// </summary>
/// <param name="listenKey">The key to keep alive</param>
/// <param name="tradingMode">Trading mode</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public KeepAliveListenKeyRequest(string listenKey, TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
{
ListenKey = listenKey;
TradingMode = tradingMode;
}
}
}
@@ -59,7 +59,6 @@
/// ctor
/// </summary>
/// <param name="symbol">Symbol to place the order on</param>
/// <param name="side">Side of the order</param>
/// <param name="orderType">Type of the order</param>
/// <param name="quantity">Quantity of the order</param>
@@ -10,10 +10,6 @@
/// </summary>
public SharedSymbol? Symbol { get; set; }
/// <summary>
/// Trading mode
/// </summary>
public TradingMode? TradingMode { get; set; }
/// <summary>
/// Position mode to change to
/// </summary>
public SharedPositionMode PositionMode { get; set; }
@@ -24,7 +20,8 @@
/// <param name="positionMode">Position mode to change to</param>
/// <param name="tradingMode">Trading mode</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public SetPositionModeRequest(SharedPositionMode positionMode, TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public SetPositionModeRequest(SharedPositionMode positionMode, TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null)
: base(tradingMode, exchangeParameters)
{
TradingMode = tradingMode;
PositionMode = positionMode;
@@ -36,7 +33,8 @@
/// <param name="symbol">Symbol to change to position mode for</param>
/// <param name="positionMode">Position mode to change to</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public SetPositionModeRequest(SharedSymbol symbol, SharedPositionMode positionMode, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public SetPositionModeRequest(SharedSymbol symbol, SharedPositionMode positionMode, ExchangeParameters? exchangeParameters = null)
: base(symbol.TradingMode, exchangeParameters)
{
PositionMode = positionMode;
Symbol = symbol;
@@ -1,23 +0,0 @@
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to start the update stream for the current user
/// </summary>
public record StartListenKeyRequest : SharedRequest
{
/// <summary>
/// Trading mode
/// </summary>
public TradingMode? TradingMode { get; set; }
/// <summary>
/// ctor
/// </summary>
/// <param name="tradingMode">Trading mode</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public StartListenKeyRequest(TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
{
TradingMode = tradingMode;
}
}
}
@@ -1,29 +0,0 @@
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to stop the update stream for the specific listen key
/// </summary>
public record StopListenKeyRequest : SharedRequest
{
/// <summary>
/// The key to stop updates for
/// </summary>
public string ListenKey { get; set; }
/// <summary>
/// Trading mode
/// </summary>
public TradingMode? TradingMode { get; set; }
/// <summary>
/// ctor
/// </summary>
/// <param name="listenKey">The key to stop updates for</param>
/// <param name="tradingMode">Trading mode</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public StopListenKeyRequest(string listenKey, TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
{
ListenKey = listenKey;
TradingMode = tradingMode;
}
}
}
@@ -48,7 +48,7 @@
SharedAccountType toAccount,
string? fromSymbol = null,
string? toSymbol = null,
ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
ExchangeParameters? exchangeParameters = null) : base(null, exchangeParameters)
{
Asset = asset;
Quantity = quantity;
@@ -35,7 +35,8 @@
/// <param name="network">Network to use</param>
/// <param name="addressTag">Address tag</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public WithdrawRequest(string asset, decimal quantity, string address, string? network = null, string? addressTag = null, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public WithdrawRequest(string asset, decimal quantity, string address, string? network = null, string? addressTag = null, ExchangeParameters? exchangeParameters = null)
: base(null, exchangeParameters)
{
Asset = asset;
Address = address;
@@ -1,10 +1,17 @@
namespace CryptoExchange.Net.SharedApis
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request
/// </summary>
public record SharedRequest
{
/// <summary>
/// Trading mode
/// </summary>
public TradingMode? TradingMode { get; set; }
/// <summary>
/// Exchange parameters. Some calls may require exchange specific parameters to execute the request.
/// </summary>
@@ -13,9 +20,28 @@
/// <summary>
/// ctor
/// </summary>
public SharedRequest(ExchangeParameters? exchangeParameters = null)
public SharedRequest(TradingMode? tradingMode, ExchangeParameters? exchangeParameters = null)
{
TradingMode = tradingMode;
ExchangeParameters = exchangeParameters;
}
/// <summary>
/// Get the value of a parameter from this instance or the default values
/// </summary>
/// <typeparam name="T">Type of the parameter value</typeparam>
/// <param name="exchange">Exchange name</param>
/// <param name="names">Parameter name or names</param>
public T? GetParamValue<T>(string exchange, params string[] names)
{
foreach (var name in names)
{
var value = ExchangeParameters.GetValue<T>(ExchangeParameters, exchange, name);
if (value != null)
return value;
}
return default;
}
}
}
@@ -9,10 +9,6 @@ namespace CryptoExchange.Net.SharedApis
/// </summary>
public record SharedSymbolRequest : SharedRequest
{
/// <summary>
/// Trading mode
/// </summary>
public TradingMode TradingMode { get; }
/// <summary>
/// The symbol
/// </summary>
@@ -25,25 +21,33 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public SharedSymbolRequest(SharedSymbol symbol, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public SharedSymbolRequest(SharedSymbol symbol, ExchangeParameters? exchangeParameters = null) : base(symbol.TradingMode, exchangeParameters)
{
Symbol = symbol;
TradingMode = symbol.TradingMode;
}
/// <summary>
/// ctor
/// </summary>
public SharedSymbolRequest(IEnumerable<SharedSymbol> symbols, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public SharedSymbolRequest(IEnumerable<SharedSymbol> symbols, ExchangeParameters? exchangeParameters = null)
: base(symbols.FirstOrDefault()?.TradingMode ?? throw new ArgumentException("Empty symbol list"), exchangeParameters)
{
if (!symbols.Any())
throw new ArgumentException("Empty symbol list");
Symbols = symbols.ToArray();
if (symbols.GroupBy(x => x.TradingMode).Count() > 1)
throw new ArgumentException("All symbols in the symbol list should have the same trading mode");
Symbols = symbols.ToArray();
TradingMode = Symbols.First().TradingMode;
}
/// <summary>
/// Get the symbol name using the provided formatter
/// </summary>
public string SymbolName(Func<string, string, TradingMode, DateTime?, string> formatter)
=> Symbol?.GetSymbol(formatter) ?? throw new ArgumentException("Symbol is not set");
/// <summary>
/// Get the symbol names using the provided formatter
/// </summary>
public string[] SymbolNames(Func<string, string, TradingMode, DateTime?, string> formatter)
=> Symbols?.Select(x => x.GetSymbol(formatter)).ToArray() ?? (Symbol != null ? new[] { Symbol.GetSymbol(formatter) } : null) ?? throw new ArgumentException("Symbol is not set");
}
}
@@ -5,19 +5,13 @@
/// </summary>
public record SubscribeAllTickersRequest : SharedRequest
{
/// <summary>
/// Trading mode
/// </summary>
public TradingMode? TradingMode { get; set; }
/// <summary>
/// ctor
/// </summary>
/// <param name="tradingMode">Trading mode</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public SubscribeAllTickersRequest(TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public SubscribeAllTickersRequest(TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null) : base(tradingMode, exchangeParameters)
{
TradingMode = tradingMode;
}
}
}
@@ -5,25 +5,14 @@
/// </summary>
public record SubscribeBalancesRequest: SharedRequest
{
/// <summary>
/// Trading mode
/// </summary>
public TradingMode? TradingMode { get; set; }
/// <summary>
/// The listen key, needed for some exchanges. Can be obtained by the StartListenKeyAsync on the shared rest client
/// </summary>
public string? ListenKey { get; set; }
/// <summary>
/// ctor
/// </summary>
/// <param name="listenKey">Listen key, needed for some exchanges. Can be obtained by the StartListenKeyAsync on the shared rest client</param>
/// <param name="tradingMode">Trading mode</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public SubscribeBalancesRequest(string? listenKey = null, TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
public SubscribeBalancesRequest(TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null)
: base(tradingMode, exchangeParameters)
{
TradingMode = tradingMode;
ListenKey = listenKey;
}
}
}
@@ -5,25 +5,14 @@
/// </summary>
public record SubscribeFuturesOrderRequest : SharedRequest
{
/// <summary>
/// Trading mode
/// </summary>
public TradingMode? TradingMode { get; set; }
/// <summary>
/// The listen key, needed for some exchanges. Can be obtained by the StartListenKeyAsync on the shared rest client
/// </summary>
public string? ListenKey { get; set; }
/// <summary>
/// ctor
/// </summary>
/// <param name="listenKey">Listen key, needed for some exchanges. Can be obtained by the StartListenKeyAsync on the shared rest client</param>
/// <param name="tradingMode">Trading mode</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public SubscribeFuturesOrderRequest(string? listenKey = null, TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null): base(exchangeParameters)
public SubscribeFuturesOrderRequest(TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null)
: base(tradingMode, exchangeParameters)
{
TradingMode = tradingMode;
ListenKey = listenKey;
}
}
}

Some files were not shown because too many files have changed in this diff Show More