1
0
mirror of https://github.com/JKorf/CryptoExchange.Net.git synced 2026-10-13 22:51:39 +00:00

Compare commits

...

3 Commits

Author SHA1 Message Date
Jkorf 08972d3d63 wip 2026-09-08 15:15:08 +02:00
Jkorf 357809fd0c wip 2026-09-08 15:08:34 +02:00
Jkorf 7629455dec wip 2026-09-07 15:23:08 +02:00
86 changed files with 2018 additions and 261 deletions
@@ -0,0 +1,120 @@
using NUnit.Framework;
using CryptoExchange.Net.SharedApis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace CryptoExchange.Net.UnitTests
{
[TestFixture]
internal class SharedParametersTests
{
[Test]
public void AllSharedAPIOptions_ShouldListAllDefaultParametersInRequestParameterRules()
{
var failures = new List<string>();
var optionsTypes = typeof(CapabilityOptions).Assembly
.GetTypes()
.Where(x => x.IsClass
&& !x.IsAbstract
&& typeof(CapabilityOptions).IsAssignableFrom(x))
.OrderBy(x => x.FullName)
.ToArray();
foreach (var optionsType in optionsTypes)
{
var genericOptionsType = GetGenericOptionsType(optionsType);
if (genericOptionsType == null)
{
if (optionsType == typeof(ClosePositionOptions))
continue;
failures.Add($"{optionsType.Name}: unable to determine request type");
continue;
}
var requestType = genericOptionsType.GetGenericArguments()[0];
var defaultRulesField = GetDefaultParameterRulesField(optionsType);
if (defaultRulesField == null)
{
failures.Add($"{optionsType.Name}: no _defaultParameterRules field found");
continue;
}
var rules = (RequestParameterDescription[]?)defaultRulesField.GetValue(null);
if (rules == null)
{
failures.Add($"{optionsType.Name}: _defaultParameterRules is null");
continue;
}
var constructorParameterNames = requestType
.GetConstructors(BindingFlags.Instance | BindingFlags.Public)
.SelectMany(x => x.GetParameters())
.Where(x => !string.Equals(
x.Name,
"exchangeParameters",
StringComparison.OrdinalIgnoreCase))
.Select(x => x.Name!)
.ToHashSet(StringComparer.OrdinalIgnoreCase);
var parameterProperties = requestType
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(x => x.DeclaringType == requestType
|| constructorParameterNames.Contains(x.Name))
.Where(x => x.Name != nameof(SharedRequest.ExchangeParameters))
.Select(x => x.Name)
.OrderBy(x => x)
.ToArray();
var ruleNames = rules
.Select(x => x.Name)
.ToHashSet(StringComparer.Ordinal);
foreach (var parameterProperty in parameterProperties)
{
if (!ruleNames.Contains(parameterProperty))
{
failures.Add(
$"{optionsType.Name}: request property " +
$"{requestType.Name}.{parameterProperty} has no default request parameter rule");
}
}
}
Assert.That(
failures,
Is.Empty,
$"Missing default request parameter rules:{Environment.NewLine}" +
string.Join(Environment.NewLine, failures));
}
private static Type? GetGenericOptionsType(Type optionsType)
{
for (var type = optionsType; type != null; type = type.BaseType)
{
if (type.IsGenericType
&& type.GetGenericTypeDefinition() == typeof(CapabilityOptions<,>))
return type;
}
return null;
}
private static FieldInfo? GetDefaultParameterRulesField(Type optionsType)
{
for (var type = optionsType; type != null; type = type.BaseType)
{
var field = type.GetField(
"_defaultParameterRules",
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
if (field != null)
return field;
}
return null;
}
}
}
@@ -0,0 +1,42 @@
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Ledger entry type
/// </summary>
public enum SharedLedgerEntryType
{
/// <summary>
/// Trade execution
/// </summary>
Trade,
/// <summary>
/// Withdrawal
/// </summary>
Withdrawal,
/// <summary>
/// Deposit
/// </summary>
Deposit,
/// <summary>
/// Transfer
/// </summary>
Transfer,
/// <summary>
/// Fee payment
/// </summary>
Fee,
/// <summary>
/// Funding fee payment
/// </summary>
FundingFee,
/// <summary>
/// Rebate
/// </summary>
Rebate,
/// <summary>
/// Other or unknown
/// </summary>
Unknown
}
}
@@ -0,0 +1,88 @@
using CryptoExchange.Net.Objects;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for closing position
/// </summary>
#pragma warning disable CS0618 // Type or member is obsolete
public class ClosePositionOptions : EndpointOptions
#pragma warning restore CS0618 // Type or member is obsolete
{
private static PropertyInfo[] _requestProperties = typeof(ClosePositionRequest).GetProperties();
/// <inheritdoc />
public override string Description => "Close an open futures position";
/// <inheritdoc />
public override Type CapabilityType => typeof(IFuturesOrderRestClient);
/// <inheritdoc />
public virtual RequestParameterRuleOverride[] ParameterRuleOverwrites
{
set => SetRequestParameters(value);
}
private static readonly RequestParameterDescription[] _defaultParameterRules = new[]
{
RequestParameterRule<ClosePositionRequest>.Required(x => x.Symbol, "The symbol of the position to close", new SharedSymbol(TradingMode.PerpetualLinear, "ETH", "USDT")),
RequestParameterRule<ClosePositionRequest>.Required(x => x.PositionMode, "The current position mode of the account", SharedPositionMode.OneWay),
RequestParameterRule<ClosePositionRequest>.Optional(x => x.PositionSide, "The side of the position to close", SharedPositionSide.Long),
RequestParameterRule<ClosePositionRequest>.Optional(x => x.MarginMode, "The margin mode of the position", SharedMarginMode.Cross),
RequestParameterRule<ClosePositionRequest>.Optional(x => x.Quantity, "The quantity of the position to close", 1m),
};
/// <summary>
/// ctor
/// </summary>
public ClosePositionOptions(string exchange, bool authenticated) : base(exchange, nameof(IFuturesOrderRestClient.ClosePositionAsync), true, _defaultParameterRules)
{
}
/// <summary>
/// Validate request
/// </summary>
public Error? ValidateRequest(ClosePositionRequest request, IFuturesOrderRestClient client)
{
if (NeedsAuthentication && !client.Authenticated)
return new NoApiCredentialsError();
foreach (var param in RequestParameterRules)
{
var property = _requestProperties.Single(x => x.Name == param.Name);
var value = property.GetValue(request);
if (param.Support == RequestParameterSupport.Required)
{
if (value == null)
{
return ArgumentError.Invalid(
param.Name,
$"Request parameter `{param.Name}` for exchange `{Exchange}` is required and should be provided. Example: {param.ExampleValue}");
}
}
}
if (request is SharedSymbolRequest symbolsRequest)
{
if (symbolsRequest.Symbols != null)
{
if (!SupportsMultipleSymbols)
return ArgumentError.Invalid(nameof(SharedSymbolRequest.Symbols), $"Only a single symbol parameter is allowed, multiple symbols are not supported");
if (symbolsRequest.Symbols.Length > MaxSymbolCount)
return ArgumentError.Invalid(nameof(SharedSymbolRequest.Symbols), $"Max number of symbols is {MaxSymbolCount} but {symbolsRequest.Symbols.Length} were passed");
}
}
return ValidateRequest(request.ExchangeParameters, request.TradingMode, client.SupportedTradingModes);
}
}
}
@@ -14,7 +14,7 @@ namespace CryptoExchange.Net.SharedApis
/// Use <see cref="CapabilityOptions.RequiredExchangeParameters"/> and <see cref="CapabilityOptions.OptionalExchangeParameters"/> to check for required and optional parameters for the request. <br />
/// Exchange specific parameters can be added to the request via the `ExchangeParameters` property of the request object.
/// </summary>
GetFuturesTickerOptions GetFuturesTickerOptions { get; }
GetTickerOptions GetFuturesTickerOptions { get; }
/// <summary>
/// Get ticker info for a specific futures symbol, see <see cref="GetFuturesTickerOptions"/> for request options and exchange specific required/optional parameters. <br />
/// </summary>
@@ -27,7 +27,7 @@ namespace CryptoExchange.Net.SharedApis
/// Use <see cref="CapabilityOptions.RequiredExchangeParameters"/> and <see cref="CapabilityOptions.OptionalExchangeParameters"/> to check for required and optional parameters for the request. <br />
/// Exchange specific parameters can be added to the request via the `ExchangeParameters` property of the request object.
/// </summary>
GetAllFuturesTickersOptions GetFuturesTickersOptions { get; }
GetAllTickersOptions GetFuturesTickersOptions { get; }
/// <summary>
/// Get ticker info for all futures symbols, see <see cref="GetFuturesTickersOptions"/> for request options and exchange specific required/optional parameters. <br />
/// </summary>
@@ -14,7 +14,7 @@ namespace CryptoExchange.Net.SharedApis
/// Use <see cref="CapabilityOptions.RequiredExchangeParameters"/> and <see cref="CapabilityOptions.OptionalExchangeParameters"/> to check for required and optional parameters for the request. <br />
/// Exchange specific parameters can be added to the request via the `ExchangeParameters` property of the request object.
/// </summary>
GetSpotTickerOptions GetSpotTickerOptions { get; }
GetTickerOptions GetSpotTickerOptions { get; }
/// <summary>
/// Get ticker for a specific spot symbol, see <see cref="GetSpotTickerOptions"/> for request options and exchange specific required/optional parameters. <br />
/// </summary>
@@ -26,7 +26,7 @@ namespace CryptoExchange.Net.SharedApis
/// Use <see cref="CapabilityOptions.RequiredExchangeParameters"/> and <see cref="CapabilityOptions.OptionalExchangeParameters"/> to check for required and optional parameters for the request. <br />
/// Exchange specific parameters can be added to the request via the `ExchangeParameters` property of the request object.
/// </summary>
GetAllSpotTickersOptions GetSpotTickersOptions { get; }
GetAllTickersOptions GetSpotTickersOptions { get; }
/// <summary>
/// Get tickers for all spot symbols, see <see cref="GetSpotTickersOptions"/> for request options and exchange specific required/optional parameters. <br />
/// </summary>
@@ -0,0 +1,51 @@
using System;
using System.Diagnostics;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Funding fee payment
/// </summary>
[DebuggerDisplay("[{Timestamp}] {Symbol,nq} {Fee} {Asset,nq}")]
public record SharedFundingFee
{
/// <summary>
/// Id
/// </summary>
public string? Id { get; set; }
/// <summary>
/// The symbol
/// </summary>
public string Symbol { get; set; }
/// <summary>
/// The asset the funding fee was paid in
/// </summary>
public string? Asset { get; set; }
/// <summary>
/// The funding fee paid
/// </summary>
public decimal Fee { get; set; }
/// <summary>
/// Timestamp
/// </summary>
public DateTime Timestamp { get; set; }
/// <summary>
/// The side of the position
/// </summary>
public SharedPositionSide? Side { get; set; }
/// <summary>
/// ctor
/// </summary>
public SharedFundingFee(string symbol, decimal fee, DateTime timestamp)
{
Symbol = symbol;
Fee = fee;
Timestamp = timestamp;
}
}
}
@@ -0,0 +1,34 @@
using System;
using System.Diagnostics;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Funding info
/// </summary>
public record SharedFundingInfo
{
/// <summary>
/// The current funding rate
/// </summary>
public decimal FundingRate { get; set; }
/// <summary>
/// Next funding timestamp
/// </summary>
public DateTime? NextFundingTime { get; set; }
/// <summary>
/// Funding interval in hours
/// </summary>
public int? FundingInterval { get; set; }
/// <summary>
/// ctor
/// </summary>
public SharedFundingInfo(decimal fundingRate, DateTime? nextFundingTime, int? fundingInterval)
{
FundingRate = fundingRate;
NextFundingTime = nextFundingTime;
FundingInterval = fundingInterval;
}
}
}
@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Ledger entry
/// </summary>
[DebuggerDisplay("[{Timestamp}] {DeltaQuantity} {Asset,nq}")]
public record SharedLedgerEntry
{
/// <summary>
/// The id of the entry
/// </summary>
public string? Id { get; set; }
/// <summary>
/// The id of the relation that changed the balance, for example a trade id or transfer id
/// </summary>
public string? RelationId { get; set; }
/// <summary>
/// The asset
/// </summary>
public string Asset { get; set; }
/// <summary>
/// The adjustment quantity, positive means the balance increased, negative means the balance decreased
/// </summary>
public decimal DeltaQuantity { get; set; }
/// <summary>
/// Timestamp of the action
/// </summary>
public DateTime Timestamp { get; set; }
/// <summary>
/// The type of the entry
/// </summary>
public SharedLedgerEntryType Type { get; set; }
/// <summary>
/// The type of the entry as string, can be used for entry types not recognized by the Shared API
/// </summary>
public string TypeString { get; set; }
/// <summary>
/// ctor
/// </summary>
public SharedLedgerEntry(string asset, decimal deltaQuantity, SharedLedgerEntryType type, string typeString, DateTime timestamp)
{
Asset = asset;
DeltaQuantity = deltaQuantity;
Type = type;
TypeString = typeString;
Timestamp = timestamp;
}
}
}
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Leverage tier
/// </summary>
[DebuggerDisplay("[{Index}] {MinNotional} - {MaxNotional}")]
public record SharedLeverageTier : SharedSymbolModel
{
/// <summary>
/// Tier index
/// </summary>
public int Index { get; set; }
/// <summary>
/// The asset the notional value is in
/// </summary>
public string? Asset { get; set; }
/// <summary>
/// Min notional value for activation of this tier
/// </summary>
public decimal MinNotional { get; set; }
/// <summary>
/// Max notional value for this tier, null if no limit
/// </summary>
public decimal? MaxNotional { get; set; }
/// <summary>
/// Maintenance margin rate for this tier
/// </summary>
public decimal? MaintenanceMarginRate { get; set; }
/// <summary>
/// Max leverage for this tier
/// </summary>
public decimal? MaxLeverage { get; set; }
/// <summary>
/// ctor
/// </summary>
public SharedLeverageTier(SharedSymbol? sharedSymbol, string symbol, int index, string? asset, decimal minNotional, decimal? maxNotional, decimal? maintenanceMarginRate, decimal? maxLeverage)
: base(sharedSymbol, symbol)
{
Index = index;
Asset = asset;
MinNotional = minNotional;
MaxNotional = maxNotional;
MaintenanceMarginRate = maintenanceMarginRate;
MaxLeverage = maxLeverage;
}
}
}
@@ -0,0 +1,50 @@
using System;
using System.Diagnostics;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Transfer info
/// </summary>
[DebuggerDisplay("[{Timestamp}] {Quantity} {Asset,nq} {FromAccountType} -> {ToAccountType}")]
public record SharedTransfer
{
/// <summary>
/// The id of the Transfer
/// </summary>
public string? Id { get; set; }
/// <summary>
/// The asset of the Transfer
/// </summary>
public string Asset { get; set; }
/// <summary>
/// The quantity that was Transfered
/// </summary>
public decimal Quantity { get; set; }
/// <summary>
/// Timestamp of the Transfer
/// </summary>
public DateTime Timestamp { get; set; }
/// <summary>
/// Source account type
/// </summary>
public SharedAccountType FromAccountType { get; set; }
/// <summary>
/// Target account type
/// </summary>
public SharedAccountType ToAccountType { get; set; }
/// <summary>
/// ctor
/// </summary>
public SharedTransfer(string asset, decimal quantity, SharedAccountType fromAccountType, SharedAccountType toAccountType, DateTime timestamp)
{
Asset = asset;
Quantity = quantity;
Timestamp = timestamp;
FromAccountType = fromAccountType;
ToAccountType = toAccountType;
}
}
}
@@ -1,5 +1,7 @@
using CryptoExchange.Net.Objects;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
@@ -227,6 +229,7 @@ namespace CryptoExchange.Net.SharedApis
where TCapability : ISharedApiCapability
{
private static PropertyInfo[] _requestProperties = typeof(TRequest).GetProperties();
private static readonly ConcurrentDictionary<(string Exchange, string Operation, string Parameter), byte> _loggedUnsupportedParameters = new();
/// <inheritdoc />
public override Type CapabilityType => typeof(TCapability);
@@ -274,9 +277,19 @@ namespace CryptoExchange.Net.SharedApis
{
if (value != null)
{
return ArgumentError.Invalid(
param.Name,
$"Request parameter `{param.Name}` is not supported by exchange `{Exchange}`");
// For backwards compatibility don't return an error for provided unsupported parameters, only log a warning.
// In a future release this will be changed to return an error.
//return ArgumentError.Invalid(
// param.Name,
// $"Request parameter `{param.Name}` is not supported by exchange `{Exchange}`");
var warningKey = (Exchange, OperationName, param.Name);
if (_loggedUnsupportedParameters.TryAdd(warningKey, 0))
{
LibraryHelpers.StaticLogger?.LogWarning("The parameter {ParamName} has a supplied value for {Exchange}.{CapabilityName}, but is not supported. " +
"In a future major release this will return an error. To check supported/required parameters use the option RequestParameterRules.",
param.Name, Exchange, OperationName);
}
}
}
}
@@ -37,10 +37,14 @@ namespace CryptoExchange.Net.SharedApis
GetBalancesRequest request,
IGetBalancesRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.AccountType != null && !IsValid(request.AccountType.Value))
return ArgumentError.Invalid(nameof(request.AccountType), "Invalid AccountType");
return base.ValidateRequest(request, client);
return error;
}
/// <summary>
@@ -32,6 +32,10 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(GetDepositsRequest request, IGetDepositHistoryRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.Direction), $"Ascending direction is not supported");
@@ -52,7 +56,7 @@ namespace CryptoExchange.Net.SharedApis
}
}
return base.ValidateRequest(request, client);
return null;
}
}
}
@@ -0,0 +1,28 @@
using CryptoExchange.Net.Objects;
using System;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting funding info
/// </summary>
public class GetFundingInfoOptions : PaginatedCapabilityOptions<GetFundingInfoRequest, IGetFundingInfoRest>
{
/// <inheritdoc />
public override string Description => "Retrieve the current funding info for a symbol";
private static readonly RequestParameterDescription[] _defaultParameterRules = new[]
{
RequestParameterRule<GetFundingInfoRequest>.Required(x => x.Symbol, "The symbol", new SharedSymbol(TradingMode.PerpetualLinear, "ETH", "USDT")),
};
/// <summary>
/// ctor
/// </summary>
public GetFundingInfoOptions(string exchange, bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit, bool needsAuthentication)
: base(exchange, supportsAscending, supportsDescending, timeFilterSupported, maxLimit, needsAuthentication, nameof(IGetFundingInfoRest.GetFundingInfoAsync), _defaultParameterRules)
{
}
}
}
@@ -0,0 +1,22 @@
using System;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to retrieve the current funding info for a symbol
/// </summary>
public record GetFundingInfoRequest : SharedSymbolRequest
{
/// <summary>
/// ctor
/// </summary>
/// <param name="symbol">Symbol to request funding info for</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetFundingInfoRequest(
SharedSymbol symbol,
ExchangeParameters? exchangeParameters = null)
: base(symbol, exchangeParameters)
{
}
}
}
@@ -0,0 +1,37 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Operation for retrieving the current funding info for a symbol on an exchange.
/// </summary>
public interface IGetFundingInfo : ISharedApiCapability
{
/// <summary>
/// Funding info request options.<br />
/// Use <see cref="CapabilityOptions.RequestParameterRules"/> and <see cref="CapabilityOptions.ExchangeParameterRules"/> to check for required and optional parameters for the request. <br />
/// Exchange specific parameters can be added to the request via the `ExchangeParameters` property of the request object.
/// </summary>
GetFundingInfoOptions GetFundingInfoOptions { get; }
/// <summary>
/// Get current funding info for a symbol, see <see cref="GetFundingInfoOptions"/> for request options and exchange specific required/optional parameters. <br />
/// </summary>
/// <param name="request">Request info</param>
/// <param name="ct">Cancellation token</param>
Task<ICallResult<SharedFundingInfo>> GetFundingInfoAsync(GetFundingInfoRequest request, CancellationToken ct = default);
}
/// <summary>
/// Operation for retrieving the current funding info for a symbol on an exchange via the REST API.
/// </summary>
public interface IGetFundingInfoRest : IGetFundingInfo, ISharedRest
{
/// <inheritdoc />
new Task<HttpResult<SharedFundingInfo>> GetFundingInfoAsync(GetFundingInfoRequest request, CancellationToken ct = default);
}
}
@@ -32,6 +32,10 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(GetFundingRateHistoryRequest request, IGetFundingRateHistoryRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.Direction), $"Ascending direction is not supported");
@@ -52,7 +56,7 @@ namespace CryptoExchange.Net.SharedApis
}
}
return base.ValidateRequest(request, client);
return null;
}
}
}
@@ -27,7 +27,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
/// <param name="symbol">Symbol to request klines for</param>
/// <param name="symbol">Symbol to request funding rate history for</param>
/// <param name="startTime">Filter by start time</param>
/// <param name="endTime">Filter by end time</param>
/// <param name="limit">Max number of results</param>
@@ -0,0 +1,62 @@
using CryptoExchange.Net.Objects;
using System;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting funding rate history
/// </summary>
public class GetUserFundingHistoryOptions : PaginatedCapabilityOptions<GetUserFundingHistoryRequest, IGetUserFundingHistoryRest>
{
/// <inheritdoc />
public override string Description => "Retrieve funding fee payments for the user";
private static readonly RequestParameterDescription[] _defaultParameterRules = new[]
{
RequestParameterRule<GetUserFundingHistoryRequest>.Optional(x => x.Symbol, "Filter by symbol", new SharedSymbol(TradingMode.PerpetualLinear, "ETH", "USDT")),
RequestParameterRule<GetUserFundingHistoryRequest>.Optional(x => x.StartTime, "Filter the result set by start time", DateTime.UtcNow.AddDays(-1)),
RequestParameterRule<GetUserFundingHistoryRequest>.Optional(x => x.EndTime, "Filter the result set by end time", DateTime.UtcNow.AddHours(-1)),
RequestParameterRule<GetUserFundingHistoryRequest>.Optional(x => x.Limit, "Limit the result set to a maximum number of items", 100),
RequestParameterRule<GetUserFundingHistoryRequest>.Optional(x => x.Direction, "The direction in which to retrieve the results", DataDirection.Descending),
};
/// <summary>
/// ctor
/// </summary>
public GetUserFundingHistoryOptions(string exchange, bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit, bool needsAuthentication)
: base(exchange, supportsAscending, supportsDescending, timeFilterSupported, maxLimit, needsAuthentication, nameof(IGetUserFundingHistoryRest.GetUserFundingHistoryAsync), _defaultParameterRules)
{
}
/// <inheritdoc />
public override Error? ValidateRequest(GetUserFundingHistoryRequest request, IGetUserFundingHistoryRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetUserFundingHistoryRequest.Direction), $"Ascending direction is not supported");
if (!SupportsDescending && request.Direction == DataDirection.Descending)
return ArgumentError.Invalid(nameof(GetUserFundingHistoryRequest.Direction), $"Descending direction is not supported");
if (MaxAge.HasValue && request.StartTime < DateTime.UtcNow.Add(-MaxAge.Value))
return ArgumentError.Invalid(nameof(GetUserFundingHistoryRequest.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(GetUserFundingHistoryRequest.StartTime), $"Time filter is not supported");
}
}
return null;
}
}
}
@@ -0,0 +1,56 @@
using System;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to retrieve user funding fee payment history
/// </summary>
public record GetUserFundingHistoryRequest : SharedRequest
{
/// <summary>
/// Filter be symbol
/// </summary>
public SharedSymbol? Symbol { get; set; }
/// <summary>
/// Filter by start time
/// </summary>
public DateTime? StartTime { get; set; }
/// <summary>
/// Filter by end time
/// </summary>
public DateTime? EndTime { get; set; }
/// <summary>
/// Max number of results
/// </summary>
public int? Limit { get; set; }
/// <summary>
/// Data direction
/// </summary>
public DataDirection? Direction { get; set; }
/// <summary>
/// ctor
/// </summary>
/// <param name="symbol">Symbol to request funding history</param>
/// <param name="startTime">Filter by start time</param>
/// <param name="endTime">Filter by end time</param>
/// <param name="limit">Max number of results</param>
/// <param name="direction">Data direction</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetUserFundingHistoryRequest(
SharedSymbol? symbol = null,
DateTime? startTime = null,
DateTime? endTime = null,
int? limit = null,
DataDirection? direction = null,
ExchangeParameters? exchangeParameters = null)
: base(null, exchangeParameters)
{
Symbol = symbol;
StartTime = startTime;
EndTime = endTime;
Limit = limit;
Direction = direction;
}
}
}
@@ -0,0 +1,39 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Operation for retrieving funding history for a user on an exchange.
/// </summary>
public interface IGetUserFundingHistory : ISharedApiCapability
{
/// <summary>
/// Funding history request options.<br />
/// Use <see cref="CapabilityOptions.RequestParameterRules"/> and <see cref="CapabilityOptions.ExchangeParameterRules"/> to check for required and optional parameters for the request. <br />
/// Exchange specific parameters can be added to the request via the `ExchangeParameters` property of the request object.
/// </summary>
GetUserFundingHistoryOptions GetUserFundingHistoryOptions { get; }
/// <summary>
/// Get funding history records, see <see cref="GetUserFundingHistoryOptions"/> for request options and exchange specific required/optional parameters. <br />
/// The result is paginated, if there are more results to be retrieved, the <see cref="HttpResult{T}.NextPageRequest"/> property of the result will contain the pagination request to be used for the next request to continue pagination.
/// </summary>
/// <param name="request">Request info</param>
/// <param name="nextPageToken">The pagination request from the previous request result <see cref="HttpResult{T}.NextPageRequest"/> property to continue pagination</param>
/// <param name="ct">Cancellation token</param>
Task<ICallResult<SharedFundingFee[]>> GetUserFundingHistoryAsync(GetUserFundingHistoryRequest request, PageRequest? nextPageToken = null, CancellationToken ct = default);
}
/// <summary>
/// Operation for retrieving funding history for a user on an exchange via the REST API.
/// </summary>
public interface IGetUserFundingHistoryRest : IGetUserFundingHistory, ISharedRest
{
/// <inheritdoc />
new Task<HttpResult<SharedFundingFee[]>> GetUserFundingHistoryAsync(GetUserFundingHistoryRequest request, PageRequest? nextPageToken = null, CancellationToken ct = default);
}
}
@@ -0,0 +1,28 @@
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Sockets;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Operation for subscribing to user Index price updates
/// </summary>
public interface ISubscribeIndexPriceSocket : ISharedSubscription
{
/// <summary>
/// Index price subscription options
/// </summary>
SubscribeIndexPriceOptions SubscribeIndexPriceOptions { get; }
/// <summary>
/// Subscribe to user Index price updates
/// </summary>
/// <param name="request">Request info</param>
/// <param name="handler">Update handler</param>
/// <param name="ct">Cancellation token, can be used to stop the updates</param>
/// <returns></returns>
Task<WebSocketResult<UpdateSubscription>> SubscribeToIndexPriceUpdatesAsync(SubscribeIndexPriceRequest request, Action<DataEvent<SharedIndexPrice>> handler, CancellationToken ct = default);
}
}
@@ -0,0 +1,29 @@
using CryptoExchange.Net.Objects;
using System;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for subscribing to Index price updates
/// </summary>
public class SubscribeIndexPriceOptions : CapabilityOptions<SubscribeIndexPriceRequest, ISubscribeIndexPriceSocket>
{
/// <inheritdoc />
public override string Description => "Subscribe to Index price updates for a symbol";
private static readonly RequestParameterDescription[] _defaultParameterRules = new[]
{
RequestParameterRule<SubscribeIndexPriceRequest>.Optional(x => x.Symbol, "The symbol to subscribe to", new SharedSymbol(TradingMode.Spot, "ETH", "USDT")),
RequestParameterRule<SubscribeIndexPriceRequest>.Optional(x => x.Symbols, "The symbols to subscribe to", new[] { new SharedSymbol(TradingMode.Spot, "ETH", "USDT") }),
};
/// <summary>
/// ctor
/// </summary>
public SubscribeIndexPriceOptions(string exchange, bool needsAuthentication)
: base(exchange, needsAuthentication, nameof(ISubscribeIndexPriceSocket.SubscribeToIndexPriceUpdatesAsync), _defaultParameterRules)
{
}
}
}
@@ -0,0 +1,38 @@
using System.Collections.Generic;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to subscribe to Index price updates for a symbol
/// </summary>
public record SubscribeIndexPriceRequest : SharedSymbolRequest
{
/// <summary>
/// ctor
/// </summary>
/// <param name="symbol">The symbol to subscribe to</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public SubscribeIndexPriceRequest(SharedSymbol symbol, ExchangeParameters? exchangeParameters = null)
: base(symbol, exchangeParameters)
{
}
/// <summary>
/// ctor
/// </summary>
/// <param name="symbols">The symbols to subscribe to</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public SubscribeIndexPriceRequest(IEnumerable<SharedSymbol> symbols, ExchangeParameters? exchangeParameters = null)
: base(symbols, exchangeParameters)
{
}
/// <summary>
/// ctor
/// </summary>
/// <param name="symbols">The symbols to subscribe to</param>
public SubscribeIndexPriceRequest(params SharedSymbol[] symbols) : base(symbols, null)
{
}
}
}
@@ -76,6 +76,10 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(GetKlinesRequest request, IGetIndexPriceKlinesRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (!IsSupported(request.Interval))
return ArgumentError.Invalid(nameof(GetKlinesRequest.Interval), $"Interval {request.Interval} not supported");
@@ -114,7 +118,7 @@ namespace CryptoExchange.Net.SharedApis
}
}
return base.ValidateRequest(request, client);
return null;
}
/// <inheritdoc />
@@ -76,6 +76,10 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(GetKlinesRequest request, IGetMarkPriceKlinesRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (!IsSupported(request.Interval))
return ArgumentError.Invalid(nameof(GetKlinesRequest.Interval), $"Interval {request.Interval} not supported");
@@ -114,7 +118,7 @@ namespace CryptoExchange.Net.SharedApis
}
}
return base.ValidateRequest(request, client);
return null;
}
/// <inheritdoc />
@@ -76,6 +76,10 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(GetKlinesRequest request, IGetKlinesRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (!IsSupported(request.Interval))
return ArgumentError.Invalid(nameof(GetKlinesRequest.Interval), $"Interval {request.Interval} not supported");
@@ -114,7 +118,7 @@ namespace CryptoExchange.Net.SharedApis
}
}
return base.ValidateRequest(request, client);
return null;
}
/// <inheritdoc />
@@ -69,10 +69,14 @@ namespace CryptoExchange.Net.SharedApis
/// </summary>
public override Error? ValidateRequest(SubscribeKlineRequest request, ISubscribeKlinesSocket client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (!IsSupported(request.Interval))
return ArgumentError.Invalid(nameof(SubscribeKlineRequest.Interval), "Interval not supported");
return base.ValidateRequest(request, client);
return null;
}
}
}
@@ -0,0 +1,62 @@
using CryptoExchange.Net.Objects;
using System;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting account ledger
/// </summary>
public class GetLedgerOptions : PaginatedCapabilityOptions<GetLedgerRequest, IGetLedgerRest>
{
/// <inheritdoc />
public override string Description => "Retrieve the account ledger";
private static readonly RequestParameterDescription[] _defaultParameterRules = new[]
{
RequestParameterRule<GetLedgerRequest>.Optional(x => x.Asset, "Filter the result set by asset", "ETH"),
RequestParameterRule<GetLedgerRequest>.Optional(x => x.StartTime, "Filter the result set by start time", DateTime.UtcNow.AddDays(-1)),
RequestParameterRule<GetLedgerRequest>.Optional(x => x.EndTime, "Filter the result set by end time", DateTime.UtcNow.AddHours(-1)),
RequestParameterRule<GetLedgerRequest>.Optional(x => x.Limit, "Limit the result set to a maximum number of items", 100),
RequestParameterRule<GetLedgerRequest>.Optional(x => x.Direction, "The direction in which to retrieve the results", DataDirection.Descending),
};
/// <summary>
/// ctor
/// </summary>
public GetLedgerOptions(string exchange, bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit)
: base(exchange, supportsAscending, supportsDescending, timeFilterSupported, maxLimit, true, nameof(IGetLedgerRest.GetLedgerAsync), _defaultParameterRules)
{
}
/// <inheritdoc />
public override Error? ValidateRequest(GetLedgerRequest request, IGetLedgerRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetLedgerRequest.Direction), $"Ascending direction is not supported");
if (!SupportsDescending && request.Direction == DataDirection.Descending)
return ArgumentError.Invalid(nameof(GetLedgerRequest.Direction), $"Descending direction is not supported");
if (MaxAge.HasValue && request.StartTime < DateTime.UtcNow.Add(-MaxAge.Value))
return ArgumentError.Invalid(nameof(GetLedgerRequest.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(GetLedgerRequest.StartTime), $"Time filter is not supported");
}
}
return null;
}
}
}
@@ -0,0 +1,55 @@
using System;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to retrieve the account ledger of the user
/// </summary>
public record GetLedgerRequest : SharedRequest
{
/// <summary>
/// Filter by asset
/// </summary>
public string? Asset { get; set; }
/// <summary>
/// Filter by start time
/// </summary>
public DateTime? StartTime { get; }
/// <summary>
/// Filter by end time
/// </summary>
public DateTime? EndTime { get; }
/// <summary>
/// Max number of results
/// </summary>
public int? Limit { get; }
/// <summary>
/// Data direction
/// </summary>
public DataDirection? Direction { get; set; }
/// <summary>
/// ctor
/// </summary>
/// <param name="asset">Filter by asset</param>
/// <param name="startTime">Filter by start time</param>
/// <param name="endTime">Filter by end time</param>
/// <param name="limit">Max number of results</param>
/// <param name="direction">Data direction</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetLedgerRequest(
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;
EndTime = endTime;
Limit = limit;
Direction = direction;
}
}
}
@@ -0,0 +1,39 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Operation for retrieving the account ledger from an exchange.
/// </summary>
public interface IGetLedger : ISharedApiCapability
{
/// <summary>
/// Ledger request options.<br />
/// Use <see cref="CapabilityOptions.RequestParameterRules"/> and <see cref="CapabilityOptions.ExchangeParameterRules"/> to check for required and optional parameters for the request. <br />
/// Exchange specific parameters can be added to the request via the `ExchangeParameters` property of the request object.
/// </summary>
GetLedgerOptions GetLedgerOptions { get; }
/// <summary>
/// Get ledger entries, see <see cref="SharedApis.GetLedgerOptions"/> for request options and exchange specific required/optional parameters. <br />
/// The result is paginated, if there are more results to be retrieved, the <see cref="HttpResult{T}.NextPageRequest"/> property of the result will contain the pagination request to be used for the next request to continue pagination.
/// </summary>
/// <param name="request">Request info</param>
/// <param name="nextPageToken">The pagination request from the previous request result <see cref="HttpResult{T}.NextPageRequest"/> property to continue pagination</param>
/// <param name="ct">Cancellation token</param>
Task<ICallResult<SharedLedgerEntry[]>> GetLedgerAsync(GetLedgerRequest request, PageRequest? nextPageToken = null, CancellationToken ct = default);
}
/// <summary>
/// Operation for retrieving the account ledger from an exchange via the REST API.
/// </summary>
public interface IGetLedgerRest : IGetLedger, ISharedRest
{
/// <inheritdoc />
new Task<HttpResult<SharedLedgerEntry[]>> GetLedgerAsync(GetLedgerRequest request, PageRequest? nextPageToken = null, CancellationToken ct = default);
}
}
@@ -5,7 +5,7 @@ using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting asset info
/// Options for requesting leverage info
/// </summary>
public class GetLeverageOptions : CapabilityOptions<GetLeverageRequest, IGetLeverageRest>
{
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting leverage tier info
/// </summary>
public class GetLeverageTiersOptions : CapabilityOptions<GetLeverageTiersRequest, IGetLeverageTiersRest>
{
/// <inheritdoc />
public override string Description => "Retrieve the current leverage tier information for a futures symbol";
private static readonly RequestParameterDescription[] _defaultParameterRules = new[]
{
RequestParameterRule<GetLeverageTiersRequest>.Required(x => x.Symbol, "The symbol to retrieve leverage tiers for", new SharedSymbol(TradingMode.PerpetualLinear, "ETH", "USDT")),
};
/// <summary>
/// ctor
/// </summary>
public GetLeverageTiersOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IGetLeverageTiersRest.GetLeverageTiersAsync), _defaultParameterRules)
{
}
}
}
@@ -0,0 +1,17 @@
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to retrieve the leverage tier information for a symbol
/// </summary>
public record GetLeverageTiersRequest : SharedSymbolRequest
{
/// <summary>
/// ctor
/// </summary>
/// <param name="symbol">Symbol to request leverage for</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetLeverageTiersRequest(SharedSymbol symbol, ExchangeParameters? exchangeParameters = null) : base(symbol, exchangeParameters)
{
}
}
}
@@ -0,0 +1,37 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Operation for retrieving leverage tier information for a symbol on an exchange.
/// </summary>
public interface IGetLeverageTiers : ISharedApiCapability
{
/// <summary>
/// Leverage tier request options.<br />
/// Use <see cref="CapabilityOptions.RequestParameterRules"/> and <see cref="CapabilityOptions.ExchangeParameterRules"/> to check for required and optional parameters for the request. <br />
/// Exchange specific parameters can be added to the request via the `ExchangeParameters` property of the request object.
/// </summary>
GetLeverageTiersOptions GetLeverageTiersOptions { get; }
/// <summary>
/// Get the leverage tiers for a symbol, see <see cref="GetLeverageTiersOptions"/> for request options and exchange specific required/optional parameters. <br />
/// </summary>
/// <param name="request">Request info</param>
/// <param name="ct">Cancellation token</param>
Task<ICallResult<SharedLeverageTier[]>> GetLeverageTiersAsync(GetLeverageTiersRequest request, CancellationToken ct = default);
}
/// <summary>
/// Operation for retrieving leverage tier information for a symbol on an exchange via the REST API.
/// </summary>
public interface IGetLeverageTiersRest : IGetLeverageTiers, ISharedRest
{
/// <inheritdoc />
new Task<HttpResult<SharedLeverageTier[]>> GetLeverageTiersAsync(GetLeverageTiersRequest request, CancellationToken ct = default);
}
}
@@ -0,0 +1,28 @@
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Sockets;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Operation for subscribing to user mark price updates
/// </summary>
public interface ISubscribeMarkPriceSocket : ISharedSubscription
{
/// <summary>
/// Mark price subscription options
/// </summary>
SubscribeMarkPriceOptions SubscribeMarkPriceOptions { get; }
/// <summary>
/// Subscribe to user mark price updates
/// </summary>
/// <param name="request">Request info</param>
/// <param name="handler">Update handler</param>
/// <param name="ct">Cancellation token, can be used to stop the updates</param>
/// <returns></returns>
Task<WebSocketResult<UpdateSubscription>> SubscribeToMarkPriceUpdatesAsync(SubscribeMarkPriceRequest request, Action<DataEvent<SharedMarkPrice>> handler, CancellationToken ct = default);
}
}
@@ -0,0 +1,29 @@
using CryptoExchange.Net.Objects;
using System;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for subscribing to mark price updates
/// </summary>
public class SubscribeMarkPriceOptions : CapabilityOptions<SubscribeMarkPriceRequest, ISubscribeMarkPriceSocket>
{
/// <inheritdoc />
public override string Description => "Subscribe to mark price updates for a symbol";
private static readonly RequestParameterDescription[] _defaultParameterRules = new[]
{
RequestParameterRule<SubscribeMarkPriceRequest>.Optional(x => x.Symbol, "The symbol to subscribe to", new SharedSymbol(TradingMode.Spot, "ETH", "USDT")),
RequestParameterRule<SubscribeMarkPriceRequest>.Optional(x => x.Symbols, "The symbols to subscribe to", new[] { new SharedSymbol(TradingMode.Spot, "ETH", "USDT") }),
};
/// <summary>
/// ctor
/// </summary>
public SubscribeMarkPriceOptions(string exchange, bool needsAuthentication)
: base(exchange, needsAuthentication, nameof(ISubscribeMarkPriceSocket.SubscribeToMarkPriceUpdatesAsync), _defaultParameterRules)
{
}
}
}
@@ -0,0 +1,38 @@
using System.Collections.Generic;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to subscribe to mark price updates for a symbol
/// </summary>
public record SubscribeMarkPriceRequest : SharedSymbolRequest
{
/// <summary>
/// ctor
/// </summary>
/// <param name="symbol">The symbol to subscribe to</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public SubscribeMarkPriceRequest(SharedSymbol symbol, ExchangeParameters? exchangeParameters = null)
: base(symbol, exchangeParameters)
{
}
/// <summary>
/// ctor
/// </summary>
/// <param name="symbols">The symbols to subscribe to</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public SubscribeMarkPriceRequest(IEnumerable<SharedSymbol> symbols, ExchangeParameters? exchangeParameters = null)
: base(symbols, exchangeParameters)
{
}
/// <summary>
/// ctor
/// </summary>
/// <param name="symbols">The symbols to subscribe to</param>
public SubscribeMarkPriceRequest(params SharedSymbol[] symbols) : base(symbols, null)
{
}
}
}
@@ -55,6 +55,10 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(GetOrderBookRequest request, IGetOrderBookRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.Limit == null)
return base.ValidateRequest(request, client);
@@ -67,7 +71,7 @@ namespace CryptoExchange.Net.SharedApis
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(request, client);
return null;
}
/// <inheritdoc />
@@ -44,10 +44,14 @@ namespace CryptoExchange.Net.SharedApis
/// </summary>
public override Error? ValidateRequest(SubscribeOrderBookRequest request, ISubscribeIncrementalOrderBookSocket client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.Limit != null && !SupportedLimits.Contains(request.Limit.Value))
return ArgumentError.Invalid(nameof(SubscribeOrderBookRequest.Limit), "Limit not supported");
return base.ValidateRequest(request, client);
return null;
}
}
}
@@ -37,10 +37,14 @@ namespace CryptoExchange.Net.SharedApis
/// </summary>
public override Error? ValidateRequest(SubscribeOrderBookRequest request, ISubscribeOrderBookSocket client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.Limit != null && !SupportedLimits.Contains(request.Limit.Value))
return ArgumentError.Invalid(nameof(SubscribeOrderBookRequest.Limit), "Limit not supported");
return base.ValidateRequest(request, client);
return null;
}
}
}
@@ -32,6 +32,10 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(GetClosedOrdersRequest request, IGetClosedFuturesOrdersRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetClosedOrdersRequest.Direction), $"Ascending direction is not supported");
@@ -52,7 +56,7 @@ namespace CryptoExchange.Net.SharedApis
}
}
return base.ValidateRequest(request, client);
return null;
}
}
@@ -50,6 +50,10 @@ namespace CryptoExchange.Net.SharedApis
IPlaceFuturesOrder client
)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (!SupportsTpSl && (request.StopLossPrice != null || request.TakeProfitPrice != null))
return ArgumentError.Invalid(nameof(PlaceFuturesOrderRequest.StopLossPrice) + " / " + nameof(PlaceFuturesOrderRequest.TakeProfitPrice), "Tp/Sl parameters not supported");
@@ -66,7 +70,7 @@ namespace CryptoExchange.Net.SharedApis
if (quantityError != null)
return quantityError;
return base.ValidateRequest(request, client);
return null;
}
}
@@ -0,0 +1,84 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Operation for placing multiple futures order on an exchange in single call.
/// </summary>
public interface IPlaceMultipleFuturesOrders : ISharedApiCapability
{
/// <summary>
/// How the trading fee is deducted
/// </summary>
SharedFeeDeductionType FuturesFeeDeductionType { get; }
/// <summary>
/// How the asset is determined in which the trading fee is paid
/// </summary>
SharedFeeAssetType FuturesFeeAssetType { get; }
/// <summary>
/// Supported order types for futures orders
/// </summary>
SharedOrderType[] FuturesSupportedOrderTypes { get; }
/// <summary>
/// Supported time in force types for placing futures orders
/// </summary>
SharedTimeInForce[] FuturesSupportedTimeInForce { get; }
/// <summary>
/// Supported quantity types for placing futures orders
/// </summary>
SharedQuantitySupport FuturesSupportedOrderQuantity { get; }
/// <summary>
/// Max number of orders per request
/// </summary>
public int? MaxFuturesOrdersPerRequest { get; }
/// <summary>
/// Whether orders for different symbols are allowed in a single place order request
/// </summary>
public bool PlaceMultipleFuturesOrdersAllowsMultipleSymbols { get; }
/// <summary>
/// Generate a new random client order id in a format that is accepted by the exchange.
/// </summary>
/// <returns></returns>
string GenerateClientOrderId();
/// <summary>
/// Futures place order request options.<br />
/// Use <see cref="CapabilityOptions.RequestParameterRules"/> and <see cref="CapabilityOptions.ExchangeParameterRules"/> to check for required and optional parameters for the request. <br />
/// Exchange specific parameters can be added to the request via the `ExchangeParameters` property of the request object.
/// </summary>
PlaceMultipleFuturesOrdersOptions PlaceMultipleFuturesOrdersOptions { get; }
/// <summary>
/// Place multiple new futures orders, see <see cref="PlaceMultipleFuturesOrdersOptions"/> for request options and exchange specific required/optional parameters. <br />
/// </summary>
/// <param name="request">Request info</param>
/// <param name="ct">Cancellation token</param>
Task<ICallResult<CallResult<SharedId>[]>> PlaceMultipleFuturesOrdersAsync(
PlaceMultipleFuturesOrdersRequest request,
CancellationToken ct = default);
}
/// <summary>
/// Operation for placing multiple futures orders on an exchange via the REST API.
/// </summary>
public interface IPlaceMultipleFuturesOrdersRest : IPlaceMultipleFuturesOrders, ISharedRest
{
/// <inheritdoc />
new Task<HttpResult<CallResult<SharedId>[]>> PlaceMultipleFuturesOrdersAsync(PlaceMultipleFuturesOrdersRequest request, CancellationToken ct = default);
}
/// <summary>
/// Operation for placing multiple futures orders on an exchange via the WebSocket API.
/// </summary>
public interface IPlaceMultipleFuturesOrdersSocket : IPlaceMultipleFuturesOrders, ISharedSocket
{
/// <inheritdoc />
new Task<QueryResult<CallResult<SharedId>[]>> PlaceMultipleFuturesOrdersAsync(PlaceMultipleFuturesOrdersRequest request, CancellationToken ct = default);
}
}
@@ -0,0 +1,80 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for placing multiple new futures orders
/// </summary>
public class PlaceMultipleFuturesOrdersOptions : CapabilityOptions<PlaceMultipleFuturesOrdersRequest, IPlaceMultipleFuturesOrders>
{
/// <inheritdoc />
public override string Description => "Place multiple new futures orders";
private static readonly RequestParameterDescription[] _defaultParameterRules = new[]
{
RequestParameterRule<PlaceMultipleSpotOrdersRequest>.Required(x => x.Orders, "The orders to place", []),
};
/// <summary>
/// ctor
/// </summary>
public PlaceMultipleFuturesOrdersOptions(string exchange)
: base(exchange, true, nameof(IPlaceMultipleFuturesOrders.PlaceMultipleFuturesOrdersAsync), _defaultParameterRules)
{
}
/// <summary>
/// Validate a request
/// </summary>
public override Error? ValidateRequest(
PlaceMultipleFuturesOrdersRequest request,
IPlaceMultipleFuturesOrders client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.Orders.Length > client.MaxFuturesOrdersPerRequest)
return ArgumentError.Invalid(nameof(PlaceMultipleFuturesOrdersRequest.Orders), $"Too many orders, max {client.MaxFuturesOrdersPerRequest}");
if (request.Orders.Length == 0)
return ArgumentError.Invalid(nameof(PlaceMultipleFuturesOrdersRequest.Orders), "No orders provided");
if (!client.PlaceMultipleFuturesOrdersAllowsMultipleSymbols)
{
var firstSymbol = request.Orders[0].Symbol;
if (request.Orders.Any(order => order.Symbol!.BaseAsset != firstSymbol!.BaseAsset
|| order.Symbol.QuoteAsset != firstSymbol.QuoteAsset
|| order.Symbol.TradingMode != firstSymbol.TradingMode))
{
return ArgumentError.Invalid(nameof(PlaceFuturesOrderRequest.Symbol), "All orders in the request should have matching symbols");
}
}
if (!request.Orders.All(x => x.TradingMode == request.TradingMode))
return ArgumentError.Invalid(nameof(PlaceFuturesOrderRequest.TradingMode), "All orders in the request should have matching trading modes");
foreach (var order in request.Orders)
{
if (order.OrderType == SharedOrderType.Other)
throw new ArgumentException("OrderType can't be `Other`", nameof(order.OrderType));
if (!client.FuturesSupportedOrderTypes.Contains(order.OrderType))
return ArgumentError.Invalid(nameof(PlaceFuturesOrderRequest.OrderType), "Order type not supported");
if (order.TimeInForce != null && !client.FuturesSupportedTimeInForce.Contains(order.TimeInForce.Value))
return ArgumentError.Invalid(nameof(PlaceFuturesOrderRequest.TimeInForce), "Order time in force not supported");
var quantityError = client.FuturesSupportedOrderQuantity.Validate(order.Side, order.OrderType, order.Quantity);
if (quantityError != null)
return quantityError;
}
return null;
}
}
}
@@ -0,0 +1,29 @@
using System.Collections.Generic;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to place multiple new futures orders
/// </summary>
public record PlaceMultipleFuturesOrdersRequest : SharedRequest
{
/// <summary>
/// Orders to place
/// </summary>
public PlaceFuturesOrderRequest[] Orders { get; set; }
/// <summary>
/// ctor
/// </summary>
/// <param name="orders">Orders to place</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public PlaceMultipleFuturesOrdersRequest(
IEnumerable<PlaceFuturesOrderRequest> orders,
ExchangeParameters? exchangeParameters = null) : base(null, exchangeParameters)
{
Orders = orders.ToArray();
TradingMode = Orders.FirstOrDefault()?.TradingMode;
}
}
}
@@ -30,10 +30,14 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(CancelOrderRequest request, ICancelSpotOrder client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.Symbol!.TradingMode != TradingMode.Spot)
return ArgumentError.Invalid("TradingMode", $"TradingMode.{request.Symbol!.TradingMode} is not supported, should be Spot");
return base.ValidateRequest(request, client);
return null;
}
}
}
@@ -30,10 +30,14 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(CancelOrderRequest request, ICancelSpotOrderByClientOrderIdRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.Symbol!.TradingMode != TradingMode.Spot)
return ArgumentError.Invalid("TradingMode", $"TradingMode.{request.Symbol!.TradingMode} is not supported, should be Spot");
return base.ValidateRequest(request, client);
return null;
}
}
}
@@ -35,10 +35,14 @@ namespace CryptoExchange.Net.SharedApis
EditSpotOrderRequest request,
IEditSpotOrder client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.Symbol!.TradingMode != TradingMode.Spot)
return ArgumentError.Invalid("TradingMode", $"TradingMode.{request.Symbol!.TradingMode} is not supported, should be Spot");
return base.ValidateRequest(request, client);
return null;
}
}
}
@@ -32,6 +32,10 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(GetClosedOrdersRequest request, IGetClosedSpotOrdersRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.Symbol!.TradingMode != TradingMode.Spot)
return ArgumentError.Invalid("TradingMode", $"TradingMode.{request.Symbol!.TradingMode} is not supported, should be Spot");
@@ -55,7 +59,7 @@ namespace CryptoExchange.Net.SharedApis
}
}
return base.ValidateRequest(request, client);
return null;
}
}
}
@@ -30,10 +30,14 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(GetOrderRequest request, IGetSpotOrderRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.Symbol!.TradingMode != TradingMode.Spot)
return ArgumentError.Invalid("TradingMode", $"TradingMode.{request.Symbol!.TradingMode} is not supported, should be Spot");
return base.ValidateRequest(request, client);
return null;
}
}
}
@@ -30,10 +30,14 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(GetOrderRequest request, IGetSpotOrderByClientOrderIdRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.Symbol!.TradingMode != TradingMode.Spot)
return ArgumentError.Invalid("TradingMode", $"TradingMode.{request.Symbol!.TradingMode} is not supported, should be Spot");
return base.ValidateRequest(request, client);
return null;
}
}
}
@@ -0,0 +1,84 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Operation for placing multiple spot order on an exchange in single call.
/// </summary>
public interface IPlaceMultipleSpotOrders : ISharedApiCapability
{
/// <summary>
/// How the trading fee is deducted
/// </summary>
SharedFeeDeductionType SpotFeeDeductionType { get; }
/// <summary>
/// How the asset is determined in which the trading fee is paid
/// </summary>
SharedFeeAssetType SpotFeeAssetType { get; }
/// <summary>
/// Supported order types for spot orders
/// </summary>
SharedOrderType[] SpotSupportedOrderTypes { get; }
/// <summary>
/// Supported time in force types for placing spot orders
/// </summary>
SharedTimeInForce[] SpotSupportedTimeInForce { get; }
/// <summary>
/// Supported quantity types for placing spot orders
/// </summary>
SharedQuantitySupport SpotSupportedOrderQuantity { get; }
/// <summary>
/// Max number of orders per request
/// </summary>
public int? MaxSpotOrdersPerRequest { get; }
/// <summary>
/// Whether orders for different symbols are allowed in a single place order request
/// </summary>
public bool PlaceMultipleSpotOrdersAllowsMultipleSymbols { get; }
/// <summary>
/// Generate a new random client order id in a format that is accepted by the exchange.
/// </summary>
/// <returns></returns>
string GenerateClientOrderId();
/// <summary>
/// Spot place order request options.<br />
/// Use <see cref="CapabilityOptions.RequestParameterRules"/> and <see cref="CapabilityOptions.ExchangeParameterRules"/> to check for required and optional parameters for the request. <br />
/// Exchange specific parameters can be added to the request via the `ExchangeParameters` property of the request object.
/// </summary>
PlaceMultipleSpotOrdersOptions PlaceMultipleSpotOrdersOptions { get; }
/// <summary>
/// Place multiple new spot orders, see <see cref="PlaceMultipleSpotOrdersOptions"/> for request options and exchange specific required/optional parameters. <br />
/// </summary>
/// <param name="request">Request info</param>
/// <param name="ct">Cancellation token</param>
Task<ICallResult<CallResult<SharedId>[]>> PlaceMultipleSpotOrdersAsync(
PlaceMultipleSpotOrdersRequest request,
CancellationToken ct = default);
}
/// <summary>
/// Operation for placing multiple spot orders on an exchange via the REST API.
/// </summary>s
public interface IPlaceMultipleSpotOrdersRest : IPlaceMultipleSpotOrders, ISharedRest
{
/// <inheritdoc />
new Task<HttpResult<CallResult<SharedId>[]>> PlaceMultipleSpotOrdersAsync(PlaceMultipleSpotOrdersRequest request, CancellationToken ct = default);
}
/// <summary>
/// Operation for placing multiple spot orders on an exchange via the WebSocket API.
/// </summary>
public interface IPlaceMultipleSpotOrdersSocket : IPlaceMultipleSpotOrders, ISharedSocket
{
/// <inheritdoc />
new Task<QueryResult<CallResult<SharedId>[]>> PlaceMultipleSpotOrdersAsync(PlaceMultipleSpotOrdersRequest request, CancellationToken ct = default);
}
}
@@ -0,0 +1,82 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for placing multiple new spot orders
/// </summary>
public class PlaceMultipleSpotOrdersOptions : CapabilityOptions<PlaceMultipleSpotOrdersRequest, IPlaceMultipleSpotOrders>
{
/// <inheritdoc />
public override string Description => "Place multiple new spot orders";
private static readonly RequestParameterDescription[] _defaultParameterRules = new[]
{
RequestParameterRule<PlaceMultipleSpotOrdersRequest>.Required(x => x.Orders, "The orders to place", []),
};
/// <summary>
/// ctor
/// </summary>
public PlaceMultipleSpotOrdersOptions(string exchange)
: base(exchange, true, nameof(IPlaceMultipleSpotOrders.PlaceMultipleSpotOrdersAsync), _defaultParameterRules)
{
}
/// <summary>
/// Validate a request
/// </summary>
public override Error? ValidateRequest(
PlaceMultipleSpotOrdersRequest request,
IPlaceMultipleSpotOrders client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.Orders.Length > client.MaxSpotOrdersPerRequest)
return ArgumentError.Invalid(nameof(PlaceMultipleSpotOrdersRequest.Orders), $"Too many orders, max {client.MaxSpotOrdersPerRequest}");
if (request.Orders.Length == 0)
return ArgumentError.Invalid(nameof(PlaceMultipleSpotOrdersRequest.Orders), "No orders provided");
if (request.TradingMode != TradingMode.Spot)
return ArgumentError.Invalid("TradingMode", $"TradingMode.{request.TradingMode} is not supported, should be Spot");
if (!client.PlaceMultipleSpotOrdersAllowsMultipleSymbols)
{
var firstSymbol = request.Orders[0].Symbol;
if (request.Orders.Any(order => order.Symbol!.BaseAsset != firstSymbol!.BaseAsset
|| order.Symbol.QuoteAsset != firstSymbol.QuoteAsset
|| order.Symbol.TradingMode != firstSymbol.TradingMode))
{
return ArgumentError.Invalid(nameof(PlaceSpotOrderRequest.Symbol), "All orders in the request should have matching symbols");
}
}
foreach (var order in request.Orders)
{
if (order.OrderType == SharedOrderType.Other)
throw new ArgumentException("OrderType can't be `Other`", nameof(order.OrderType));
if (!client.SpotSupportedOrderTypes.Contains(order.OrderType))
return ArgumentError.Invalid(nameof(PlaceSpotOrderRequest.OrderType), "Order type not supported");
if (order.TimeInForce != null && !client.SpotSupportedTimeInForce.Contains(order.TimeInForce.Value))
return ArgumentError.Invalid(nameof(PlaceSpotOrderRequest.TimeInForce), "Order time in force not supported");
var quantityError = client.SpotSupportedOrderQuantity.Validate(order.Side, order.OrderType, order.Quantity);
if (quantityError != null)
return quantityError;
}
return null;
}
}
}
@@ -0,0 +1,29 @@
using System.Collections.Generic;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to place multiple new spot orders
/// </summary>
public record PlaceMultipleSpotOrdersRequest : SharedRequest
{
/// <summary>
/// Orders to place
/// </summary>
public PlaceSpotOrderRequest[] Orders { get; set; }
/// <summary>
/// ctor
/// </summary>
/// <param name="orders">Orders to place</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public PlaceMultipleSpotOrdersRequest(
IEnumerable<PlaceSpotOrderRequest> orders,
ExchangeParameters? exchangeParameters = null) : base(null, exchangeParameters)
{
Orders = orders.ToArray();
TradingMode = Orders.FirstOrDefault()?.TradingMode;
}
}
}
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for fully closing a position
/// </summary>
public class CloseFullPositionOptions : CapabilityOptions<CloseFullPositionRequest, ICloseFullPositionRest>
{
/// <inheritdoc />
public override string Description => "Fully close an open futures position";
private static readonly RequestParameterDescription[] _defaultParameterRules = new[]
{
RequestParameterRule<CloseFullPositionRequest>.Required(x => x.Symbol, "The symbol of the position to close", new SharedSymbol(TradingMode.PerpetualLinear, "ETH", "USDT")),
RequestParameterRule<CloseFullPositionRequest>.Optional(x => x.PositionMode, "The current position mode of the account", SharedPositionMode.OneWay),
RequestParameterRule<CloseFullPositionRequest>.Optional(x => x.PositionSide, "The side of the position to close", SharedPositionSide.Long),
RequestParameterRule<CloseFullPositionRequest>.Optional(x => x.MarginMode, "The margin mode of the position", SharedMarginMode.Cross),
RequestParameterRule<CloseFullPositionRequest>.Optional(x => x.PositionId, "The id of the position to close", "12345"),
};
/// <summary>
/// ctor
/// </summary>
public CloseFullPositionOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(ICloseFullPositionRest.CloseFullPositionAsync), _defaultParameterRules)
{
}
}
}
@@ -0,0 +1,49 @@
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to fully close a currently open position
/// </summary>
public record CloseFullPositionRequest : SharedSymbolRequest
{
/// <summary>
/// The id of the position to close
/// </summary>
public string? PositionId { get; set; }
/// <summary>
/// The current position mode of the account for the symbol
/// </summary>
public SharedPositionMode? PositionMode { get; set; }
/// <summary>
/// The position side to close. Required when in hedge mode
/// </summary>
public SharedPositionSide? PositionSide { get; set; }
/// <summary>
/// Margin mode
/// </summary>
public SharedMarginMode? MarginMode { get; set; }
/// <summary>
/// ctor
/// </summary>
/// <param name="symbol">Symbol to close the position on</param>
/// <param name="mode">The current position mode of the account for the symbol</param>
/// <param name="positionSide">The position side to close. Required when in hedge mode</param>
/// <param name="marginMode">Margin mode</param>
/// <param name="positionId">Id of the position to close</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public CloseFullPositionRequest(
SharedSymbol symbol,
SharedPositionMode? mode = null,
SharedPositionSide? positionSide = null,
SharedMarginMode? marginMode = null,
string? positionId = null,
ExchangeParameters? exchangeParameters = null) : base(symbol, exchangeParameters)
{
PositionId = positionId;
PositionMode = mode;
PositionSide = positionSide;
MarginMode = marginMode;
}
}
}
@@ -1,31 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for closing position
/// </summary>
public class ClosePositionOptions : CapabilityOptions<ClosePositionRequest, IClosePositionRest>
{
/// <inheritdoc />
public override string Description => "Close an open futures position";
private static readonly RequestParameterDescription[] _defaultParameterRules = new[]
{
RequestParameterRule<ClosePositionRequest>.Required(x => x.Symbol, "The symbol of the position to close", new SharedSymbol(TradingMode.PerpetualLinear, "ETH", "USDT")),
RequestParameterRule<ClosePositionRequest>.Required(x => x.PositionMode, "The current position mode of the account", SharedPositionMode.OneWay),
RequestParameterRule<ClosePositionRequest>.Optional(x => x.PositionSide, "The side of the position to close", SharedPositionSide.Long),
RequestParameterRule<ClosePositionRequest>.Optional(x => x.MarginMode, "The margin mode of the position", SharedMarginMode.Cross),
RequestParameterRule<ClosePositionRequest>.Optional(x => x.Quantity, "The quantity of the position to close", 1m),
};
/// <summary>
/// ctor
/// </summary>
public ClosePositionOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IClosePositionRest.ClosePositionAsync), _defaultParameterRules)
{
}
}
}
@@ -8,31 +8,31 @@ using System.Threading.Tasks;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Operation for closing an open position on an exchange.
/// Operation for fully closing an open position on an exchange.
/// </summary>
public interface IClosePosition : ISharedApiCapability
public interface ICloseFullPosition : ISharedApiCapability
{
/// <summary>
/// Close position order request options.<br />
/// Use <see cref="CapabilityOptions.RequestParameterRules"/> and <see cref="CapabilityOptions.ExchangeParameterRules"/> to check for required and optional parameters for the request. <br />
/// Exchange specific parameters can be added to the request via the `ExchangeParameters` property of the request object.
/// </summary>
ClosePositionOptions ClosePositionOptions { get; }
CloseFullPositionOptions CloseFullPositionOptions { get; }
/// <summary>
/// Close a currently open position, see <see cref="ClosePositionOptions"/> for request options and exchange specific required/optional parameters. <br />
/// Fully close a currently open position, see <see cref="CloseFullPositionOptions"/> for request options and exchange specific required/optional parameters. <br />
/// </summary>
/// <param name="request">Request info</param>
/// <param name="ct">Cancellation token</param>
/// <returns></returns>
Task<ICallResult<SharedId>> ClosePositionAsync(ClosePositionRequest request, CancellationToken ct = default);
Task<ICallResult<SharedId>> CloseFullPositionAsync(CloseFullPositionRequest request, CancellationToken ct = default);
}
/// <summary>
/// Operation for closing an open position on an exchange via the REST API.
/// Operation for fully closing an open position on an exchange via the REST API.
/// </summary>
public interface IClosePositionRest : IClosePosition, ISharedRest
public interface ICloseFullPositionRest : ICloseFullPosition, ISharedRest
{
/// <inheritdoc />
new Task<HttpResult<SharedId>> ClosePositionAsync(ClosePositionRequest request, CancellationToken ct = default);
new Task<HttpResult<SharedId>> CloseFullPositionAsync(CloseFullPositionRequest request, CancellationToken ct = default);
}
}
@@ -33,6 +33,10 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(GetPositionHistoryRequest request, IGetPositionHistoryRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.Direction), $"Ascending direction is not supported");
@@ -53,7 +57,7 @@ namespace CryptoExchange.Net.SharedApis
}
}
return base.ValidateRequest(request, client);
return null;
}
}
}
@@ -32,21 +32,25 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(GetSymbolsRequest request, IGetFuturesSymbolsRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.BaseAssetType != null && request.BaseAssetSubType != null)
{
var error = ValidateAssetTypeCombination(request.BaseAssetType.Value, request.BaseAssetSubType.Value);
error = ValidateAssetTypeCombination(request.BaseAssetType.Value, request.BaseAssetSubType.Value);
if (error != null)
return error;
}
if (request.QuoteAssetType != null && request.QuoteAssetSubType != null)
{
var error = ValidateAssetTypeCombination(request.QuoteAssetType.Value, request.QuoteAssetSubType.Value);
error = ValidateAssetTypeCombination(request.QuoteAssetType.Value, request.QuoteAssetSubType.Value);
if (error != null)
return error;
}
return base.ValidateRequest(request, client);
return null;
}
private Error? ValidateAssetTypeCombination(SharedAssetType type, SharedAssetSubType subType)
@@ -33,21 +33,25 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(GetSymbolsRequest request, IGetSpotSymbolsRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.BaseAssetType != null && request.BaseAssetSubType != null)
{
var error = ValidateAssetTypeCombination(request.BaseAssetType.Value, request.BaseAssetSubType.Value);
error = ValidateAssetTypeCombination(request.BaseAssetType.Value, request.BaseAssetSubType.Value);
if (error != null)
return error;
}
if (request.QuoteAssetType != null && request.QuoteAssetSubType != null)
{
var error = ValidateAssetTypeCombination(request.QuoteAssetType.Value, request.QuoteAssetSubType.Value);
error = ValidateAssetTypeCombination(request.QuoteAssetType.Value, request.QuoteAssetSubType.Value);
if (error != null)
return error;
}
return base.ValidateRequest(request, client);
return null;
}
private Error? ValidateAssetTypeCombination(SharedAssetType type, SharedAssetSubType subType)
@@ -1,39 +0,0 @@
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting tickers
/// </summary>
public class GetAllFuturesTickersOptions : CapabilityOptions<GetTickersRequest, IGetAllFuturesTickersRest>
{
/// <inheritdoc />
public override string Description => "Retrieve price ticker information for all futures symbols";
private static readonly RequestParameterDescription[] _defaultParameterRules = new[]
{
RequestParameterRule<GetTickersRequest>.Optional(x => x.TradingMode, "Filter the tickers by trading mode", TradingMode.PerpetualLinear),
};
/// <summary>
/// Type of ticker calculation
/// </summary>
public SharedTickerType TickerType { get; set; } = SharedTickerType.Day24H;
/// <summary>
/// ctor
/// </summary>
public GetAllFuturesTickersOptions(string exchange, SharedTickerType? tickerCalcType = null) : base(exchange, false, nameof(IGetAllFuturesTickersRest.GetAllFuturesTickersAsync), _defaultParameterRules)
{
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();
}
}
}
@@ -1,37 +0,0 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Operation for retrieving all futures tickers from an exchange.
/// </summary>
public interface IGetAllFuturesTickers : ISharedApiCapability
{
/// <summary>
/// Futures get tickers request options.<br />
/// Use <see cref="CapabilityOptions.RequestParameterRules"/> and <see cref="CapabilityOptions.ExchangeParameterRules"/> to check for required and optional parameters for the request. <br />
/// Exchange specific parameters can be added to the request via the `ExchangeParameters` property of the request object.
/// </summary>
GetAllFuturesTickersOptions GetAllFuturesTickersOptions { get; }
/// <summary>
/// Get ticker info for all futures symbols, see <see cref="GetAllFuturesTickersOptions"/> for request options and exchange specific required/optional parameters. <br />
/// </summary>
/// <param name="request">Request info</param>
/// <param name="ct">Cancellation token</param>
Task<ICallResult<SharedFuturesTicker[]>> GetAllFuturesTickersAsync(GetTickersRequest request, CancellationToken ct = default);
}
/// <summary>
/// Operation for retrieving all futures tickers from an exchange via the REST API.
/// </summary>
public interface IGetAllFuturesTickersRest : IGetAllFuturesTickers
{
/// <inheritdoc />
new Task<HttpResult<SharedFuturesTicker[]>> GetAllFuturesTickersAsync(GetTickersRequest request, CancellationToken ct = default);
}
}
@@ -1,39 +0,0 @@
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting ticker
/// </summary>
public class GetFuturesTickerOptions : CapabilityOptions<GetTickerRequest, IGetFuturesTickerRest>
{
/// <inheritdoc />
public override string Description => "Retrieve price ticker information for a futures symbol";
private static readonly RequestParameterDescription[] _defaultParameterRules = new[]
{
RequestParameterRule<GetTickerRequest>.Required(x => x.Symbol, "The symbol to retrieve ticker information for", new SharedSymbol(TradingMode.PerpetualLinear, "ETH", "USDT")),
};
/// <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(IGetFuturesTickerRest.GetFuturesTickerAsync), _defaultParameterRules)
{
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();
}
}
}
@@ -1,37 +0,0 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Operation for retrieving a single futures ticker from an exchange.
/// </summary>
public interface IGetFuturesTicker : ISharedApiCapability
{
/// <summary>
/// Futures get ticker request options.<br />
/// Use <see cref="CapabilityOptions.RequestParameterRules"/> and <see cref="CapabilityOptions.ExchangeParameterRules"/> to check for required and optional parameters for the request. <br />
/// Exchange specific parameters can be added to the request via the `ExchangeParameters` property of the request object.
/// </summary>
GetFuturesTickerOptions GetFuturesTickerOptions { get; }
/// <summary>
/// Get ticker info for a specific futures symbol, see <see cref="GetFuturesTickerOptions"/> for request options and exchange specific required/optional parameters. <br />
/// </summary>
/// <param name="request">Request info</param>
/// <param name="ct">Cancellation token</param>
Task<ICallResult<SharedFuturesTicker>> GetFuturesTickerAsync(GetTickerRequest request, CancellationToken ct = default);
}
/// <summary>
/// Operation for retrieving a single futures ticker from an exchange via the REST API.
/// </summary>
public interface IGetFuturesTickerRest : IGetFuturesTicker, ISharedRest
{
/// <inheritdoc />
new Task<HttpResult<SharedFuturesTicker>> GetFuturesTickerAsync(GetTickerRequest request, CancellationToken ct = default);
}
}
@@ -5,14 +5,14 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for requesting tickers
/// </summary>
public class GetAllSpotTickersOptions : CapabilityOptions<GetTickersRequest, IGetAllSpotTickersRest>
public class GetAllTickersOptions : CapabilityOptions<GetTickersRequest, IGetAllTickersRest>
{
/// <inheritdoc />
public override string Description => "Retrieve price ticker information for all spot symbols";
public override string Description => "Retrieve price ticker information for all symbols in a trading mode";
private static readonly RequestParameterDescription[] _defaultParameterRules = new[]
{
RequestParameterRule<GetTickersRequest>.Optional(x => x.TradingMode, "Filter the tickers by trading mode", TradingMode.Spot),
RequestParameterRule<GetTickersRequest>.Optional(x => x.TradingMode, "The trading mode to retrieve tickers for", TradingMode.Spot),
};
/// <summary>
@@ -23,7 +23,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public GetAllSpotTickersOptions(string exchange, SharedTickerType? tickerCalcType = null) : base(exchange, false, nameof(IGetAllSpotTickersRest.GetAllSpotTickersAsync), _defaultParameterRules)
public GetAllTickersOptions(string exchange, SharedTickerType? tickerCalcType = null) : base(exchange, false, nameof(IGetAllTickersRest.GetAllTickersAsync), _defaultParameterRules)
{
TickerType = tickerCalcType ?? SharedTickerType.Day24H;
}
@@ -8,30 +8,30 @@ using System.Threading.Tasks;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Operation for retrieving a single spot ticker from an exchange.
/// Operation for retrieving all tickers from an exchange.
/// </summary>
public interface IGetSpotTicker : ISharedApiCapability
public interface IGetAllTickers : ISharedApiCapability
{
/// <summary>
/// Spot ticker request options.<br />
/// Tickers request options.<br />
/// Use <see cref="CapabilityOptions.RequestParameterRules"/> and <see cref="CapabilityOptions.ExchangeParameterRules"/> to check for required and optional parameters for the request. <br />
/// Exchange specific parameters can be added to the request via the `ExchangeParameters` property of the request object.
/// </summary>
GetSpotTickerOptions GetSpotTickerOptions { get; }
GetAllTickersOptions GetAllTickersOptions { get; }
/// <summary>
/// Get ticker for a specific spot symbol, see <see cref="GetSpotTickerOptions"/> for request options and exchange specific required/optional parameters. <br />
/// Get tickers for all symbols, see <see cref="GetAllTickersOptions"/> for request options and exchange specific required/optional parameters. <br />
/// </summary>
/// <param name="request">Request info</param>
/// <param name="ct">Cancellation token</param>
Task<ICallResult<SharedSpotTicker>> GetSpotTickerAsync(GetTickerRequest request, CancellationToken ct = default);
Task<ICallResult<SharedTicker[]>> GetAllTickersAsync(GetTickersRequest request, CancellationToken ct = default);
}
/// <summary>
/// Operation for retrieving a single spot ticker from an exchange via the REST API
/// Operation for retrieving all tickers from an exchange via the REST API.
/// </summary>
public interface IGetSpotTickerRest : IGetSpotTicker, ISharedRest
public interface IGetAllTickersRest : IGetAllTickers, ISharedRest
{
/// <inheritdoc />
new Task<HttpResult<SharedSpotTicker>> GetSpotTickerAsync(GetTickerRequest request, CancellationToken ct = default);
new Task<HttpResult<SharedTicker[]>> GetAllTickersAsync(GetTickersRequest request, CancellationToken ct = default);
}
}
@@ -5,10 +5,10 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Options for requesting ticker
/// </summary>
public class GetSpotTickerOptions : CapabilityOptions<GetTickerRequest, IGetSpotTickerRest>
public class GetTickerOptions : CapabilityOptions<GetTickerRequest, IGetTickerRest>
{
/// <inheritdoc />
public override string Description => "Retrieve price ticker information for a spot symbol";
public override string Description => "Retrieve price ticker information for a symbol";
private static readonly RequestParameterDescription[] _defaultParameterRules = new[]
{
@@ -23,7 +23,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public GetSpotTickerOptions(string exchange, SharedTickerType? tickerCalcType = null) : base(exchange, false, nameof(IGetSpotTickerRest.GetSpotTickerAsync), _defaultParameterRules)
public GetTickerOptions(string exchange, SharedTickerType? tickerCalcType = null) : base(exchange, false, nameof(IGetTickerRest.GetTickerAsync), _defaultParameterRules)
{
TickerType = tickerCalcType ?? SharedTickerType.Day24H;
}
@@ -8,30 +8,30 @@ using System.Threading.Tasks;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Operation for retrieving all spot tickers from an exchange.
/// Operation for retrieving a single ticker from an exchange.
/// </summary>
public interface IGetAllSpotTickers : ISharedApiCapability
public interface IGetTicker : ISharedApiCapability
{
/// <summary>
/// Spot tickers request options.<br />
/// Ticker request options.<br />
/// Use <see cref="CapabilityOptions.RequestParameterRules"/> and <see cref="CapabilityOptions.ExchangeParameterRules"/> to check for required and optional parameters for the request. <br />
/// Exchange specific parameters can be added to the request via the `ExchangeParameters` property of the request object.
/// </summary>
GetAllSpotTickersOptions GetAllSpotTickersOptions { get; }
GetTickerOptions GetTickerOptions { get; }
/// <summary>
/// Get tickers for all spot symbols, see <see cref="GetAllSpotTickersOptions"/> for request options and exchange specific required/optional parameters. <br />
/// Get ticker for a specific symbol, see <see cref="GetTickerOptions"/> for request options and exchange specific required/optional parameters. <br />
/// </summary>
/// <param name="request">Request info</param>
/// <param name="ct">Cancellation token</param>
Task<ICallResult<SharedSpotTicker[]>> GetAllSpotTickersAsync(GetTickersRequest request, CancellationToken ct = default);
Task<ICallResult<SharedTicker>> GetTickerAsync(GetTickerRequest request, CancellationToken ct = default);
}
/// <summary>
/// Operation for retrieving all spot tickers from an exchange via the REST API.
/// Operation for retrieving a single ticker from an exchange via the REST API
/// </summary>
public interface IGetAllSpotTickersRest : IGetAllSpotTickers, ISharedRest
public interface IGetTickerRest : IGetTicker, ISharedRest
{
/// <inheritdoc />
new Task<HttpResult<SharedSpotTicker[]>> GetAllSpotTickersAsync(GetTickersRequest request, CancellationToken ct = default);
new Task<HttpResult<SharedTicker>> GetTickerAsync(GetTickerRequest request, CancellationToken ct = default);
}
}
@@ -1,4 +1,4 @@
namespace CryptoExchange.Net.SharedApis
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to retrieve all symbol tickers
@@ -8,7 +8,7 @@
/// <summary>
/// ctor
/// </summary>
/// <param name="tradingMode">Trading mode</param>
/// <param name="tradingMode">The trading mode to retrieve tickers for. Required when using <see cref="IGetAllTickers"/>.</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetTickersRequest(TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null) : base(tradingMode, exchangeParameters)
{
@@ -32,6 +32,10 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(GetTradeHistoryRequest request, IGetTradeHistoryRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetTradeHistoryRequest.Direction), $"Ascending direction is not supported");
@@ -41,7 +45,7 @@ namespace CryptoExchange.Net.SharedApis
if (MaxAge.HasValue && request.StartTime < DateTime.UtcNow.Add(-MaxAge.Value))
return ArgumentError.Invalid(nameof(GetTradeHistoryRequest.StartTime), $"Only the most recent {MaxAge} period data is available");
return base.ValidateRequest(request, client);
return null;
}
}
}
@@ -0,0 +1,62 @@
using CryptoExchange.Net.Objects;
using System;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for requesting transfer history
/// </summary>
public class GetTransferHistoryOptions : PaginatedCapabilityOptions<GetTransferHistoryRequest, IGetTransferHistoryRest>
{
/// <inheritdoc />
public override string Description => "Retrieve spot user trade history";
private static readonly RequestParameterDescription[] _defaultParameterRules = new[]
{
RequestParameterRule<GetTransferHistoryRequest>.Optional(x => x.Asset, "Filter the result set by asset", "ETH"),
RequestParameterRule<GetTransferHistoryRequest>.Optional(x => x.StartTime, "Filter the result set by start time", DateTime.UtcNow.AddDays(-1)),
RequestParameterRule<GetTransferHistoryRequest>.Optional(x => x.EndTime, "Filter the result set by end time", DateTime.UtcNow.AddHours(-1)),
RequestParameterRule<GetTransferHistoryRequest>.Optional(x => x.Limit, "Limit the result set to a maximum number of items", 100),
RequestParameterRule<GetTransferHistoryRequest>.Optional(x => x.Direction, "The direction in which to retrieve the results", DataDirection.Descending),
};
/// <summary>
/// ctor
/// </summary>
public GetTransferHistoryOptions(string exchange, bool supportsAscending, bool supportsDescending, bool timeFilterSupported, int maxLimit)
: base(exchange, supportsAscending, supportsDescending, timeFilterSupported, maxLimit, true, nameof(IGetTransferHistoryRest.GetTransferHistoryAsync), _defaultParameterRules)
{
}
/// <inheritdoc />
public override Error? ValidateRequest(GetTransferHistoryRequest request, IGetTransferHistoryRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetTransferHistoryRequest.Direction), $"Ascending direction is not supported");
if (!SupportsDescending && request.Direction == DataDirection.Descending)
return ArgumentError.Invalid(nameof(GetTransferHistoryRequest.Direction), $"Descending direction is not supported");
if (MaxAge.HasValue && request.StartTime < DateTime.UtcNow.Add(-MaxAge.Value))
return ArgumentError.Invalid(nameof(GetTransferHistoryRequest.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(GetTransferHistoryRequest.StartTime), $"Time filter is not supported");
}
}
return null;
}
}
}
@@ -0,0 +1,55 @@
using System;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to retrieve the transfer history of the user
/// </summary>
public record GetTransferHistoryRequest : SharedRequest
{
/// <summary>
/// Filter by asset
/// </summary>
public string? Asset { get; set; }
/// <summary>
/// Filter by start time
/// </summary>
public DateTime? StartTime { get; }
/// <summary>
/// Filter by end time
/// </summary>
public DateTime? EndTime { get; }
/// <summary>
/// Max number of results
/// </summary>
public int? Limit { get; }
/// <summary>
/// Data direction
/// </summary>
public DataDirection? Direction { get; set; }
/// <summary>
/// ctor
/// </summary>
/// <param name="asset">Filter by asset</param>
/// <param name="startTime">Filter by start time</param>
/// <param name="endTime">Filter by end time</param>
/// <param name="limit">Max number of results</param>
/// <param name="direction">Data direction</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetTransferHistoryRequest(
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;
EndTime = endTime;
Limit = limit;
Direction = direction;
}
}
}
@@ -0,0 +1,39 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Operation for retrieving transfer history from an exchange.
/// </summary>
public interface IGetTransferHistory : ISharedApiCapability
{
/// <summary>
/// Transfer history request options.<br />
/// Use <see cref="CapabilityOptions.RequestParameterRules"/> and <see cref="CapabilityOptions.ExchangeParameterRules"/> to check for required and optional parameters for the request. <br />
/// Exchange specific parameters can be added to the request via the `ExchangeParameters` property of the request object.
/// </summary>
GetTransferHistoryOptions GetTransferHistoryOptions { get; }
/// <summary>
/// Get transfer history, see <see cref="SharedApis.GetTransferHistoryOptions"/> for request options and exchange specific required/optional parameters. <br />
/// The result is paginated, if there are more results to be retrieved, the <see cref="HttpResult{T}.NextPageRequest"/> property of the result will contain the pagination request to be used for the next request to continue pagination.
/// </summary>
/// <param name="request">Request info</param>
/// <param name="nextPageToken">The pagination request from the previous request result <see cref="HttpResult{T}.NextPageRequest"/> property to continue pagination</param>
/// <param name="ct">Cancellation token</param>
Task<ICallResult<SharedTransfer[]>> GetTransferHistoryAsync(GetTransferHistoryRequest request, PageRequest? nextPageToken = null, CancellationToken ct = default);
}
/// <summary>
/// Operation for retrieving transfer history from an exchange via the REST API.
/// </summary>
public interface IGetTransferHistoryRest : IGetTransferHistory, ISharedRest
{
/// <inheritdoc />
new Task<HttpResult<SharedTransfer[]>> GetTransferHistoryAsync(GetTransferHistoryRequest request, PageRequest? nextPageToken = null, CancellationToken ct = default);
}
}
@@ -42,13 +42,17 @@ namespace CryptoExchange.Net.SharedApis
TransferRequest request,
ITransferRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (!SupportedAccountTypes.Contains(request.FromAccountType))
return ArgumentError.Invalid(nameof(request.FromAccountType), "Invalid FromAccountType");
if (!SupportedAccountTypes.Contains(request.ToAccountType))
return ArgumentError.Invalid(nameof(request.FromAccountType), "Invalid ToAccountType");
return base.ValidateRequest(request, client);
return null;
}
/// <inheritdoc />
@@ -25,6 +25,7 @@ namespace CryptoExchange.Net.SharedApis
RequestParameterRule<PlaceFuturesTriggerOrderRequest>.Optional(x => x.MarginMode, "The margin mode of the order", SharedMarginMode.Cross),
RequestParameterRule<PlaceFuturesTriggerOrderRequest>.Optional(x => x.Leverage, "The leverage for the position", 10m),
RequestParameterRule<PlaceFuturesTriggerOrderRequest>.Optional(x => x.TriggerPriceType, "The price type used to trigger the order", SharedTriggerPriceType.LastPrice),
RequestParameterRule<PlaceFuturesTriggerOrderRequest>.Optional(x => x.ReduceOnly, "Whether the order is reduce only", true),
};
/// <summary>
@@ -39,5 +40,18 @@ namespace CryptoExchange.Net.SharedApis
{
HoldsFunds = holdsFunds;
}
/// <inheritdoc />
public override Error? ValidateRequest(PlaceFuturesTriggerOrderRequest request, IPlaceFuturesTriggerOrderRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.ReduceOnly == true && request.OrderDirection != SharedTriggerOrderDirection.Exit)
return ArgumentError.Invalid(nameof(request.ReduceOnly), "ReduceOnly can only be enabled for an exit trigger order");
return null;
}
}
}
@@ -53,6 +53,10 @@
/// Trigger price type
/// </summary>
public SharedTriggerPriceType? TriggerPriceType { get; set; }
/// <summary>
/// Reduce only flag
/// </summary>
public bool? ReduceOnly { get; set; }
/// <summary>
/// ctor
@@ -29,10 +29,14 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(CancelOrderRequest request, ICancelSpotTriggerOrderRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.Symbol!.TradingMode != TradingMode.Spot)
return ArgumentError.Invalid("TradingMode", $"TradingMode.{request.Symbol!.TradingMode} is not supported, should be Spot");
return base.ValidateRequest(request, client);
return null;
}
}
}
@@ -29,10 +29,14 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(GetOrderRequest request, IGetSpotTriggerOrderRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.Symbol!.TradingMode != TradingMode.Spot)
return ArgumentError.Invalid("TradingMode", $"TradingMode.{request.Symbol!.TradingMode} is not supported, should be Spot");
return base.ValidateRequest(request, client);
return null;
}
}
}
@@ -42,10 +42,14 @@ namespace CryptoExchange.Net.SharedApis
PlaceSpotTriggerOrderRequest request,
IPlaceSpotTriggerOrderRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.Symbol!.TradingMode != TradingMode.Spot)
return ArgumentError.Invalid("TradingMode", $"TradingMode.{request.Symbol!.TradingMode} is not supported, should be Spot");
return base.ValidateRequest(request, client);
return null;
}
}
}
@@ -32,6 +32,10 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(GetUserTradesRequest request, IGetFuturesUserTradeHistoryRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetUserTradesRequest.Direction), $"Ascending direction is not supported");
@@ -52,7 +56,7 @@ namespace CryptoExchange.Net.SharedApis
}
}
return base.ValidateRequest(request, client);
return null;
}
}
}
@@ -29,10 +29,14 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(GetOrderTradesRequest request, IGetSpotOrderTradesRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.Symbol!.TradingMode != TradingMode.Spot)
return ArgumentError.Invalid("TradingMode", $"TradingMode.{request.Symbol!.TradingMode} is not supported, should be Spot");
return base.ValidateRequest(request, client);
return null;
}
}
}
@@ -32,6 +32,10 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(GetUserTradesRequest request, IGetSpotUserTradeHistoryRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (request.Symbol!.TradingMode != TradingMode.Spot)
return ArgumentError.Invalid("TradingMode", $"TradingMode.{request.Symbol!.TradingMode} is not supported, should be Spot");
@@ -55,7 +59,7 @@ namespace CryptoExchange.Net.SharedApis
}
}
return base.ValidateRequest(request, client);
return null;
}
}
}
@@ -32,6 +32,10 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override Error? ValidateRequest(GetWithdrawalsRequest request, IGetWithdrawalHistoryRest client)
{
var error = base.ValidateRequest(request, client);
if (error != null)
return error;
if (!SupportsAscending && request.Direction == DataDirection.Ascending)
return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.Direction), $"Ascending direction is not supported");
@@ -52,7 +56,7 @@ namespace CryptoExchange.Net.SharedApis
}
}
return base.ValidateRequest(request, client);
return null;
}
}
}