diff --git a/CryptoExchange.Net.UnitTests/SharedParametersTests.cs b/CryptoExchange.Net.UnitTests/SharedParametersTests.cs new file mode 100644 index 00000000..0d2e71ab --- /dev/null +++ b/CryptoExchange.Net.UnitTests/SharedParametersTests.cs @@ -0,0 +1,117 @@ +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(); + + 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) + { + 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; + } + } +} diff --git a/CryptoExchange.Net/SharedApis/V2/Positions/ClosePosition/ClosePositionRequest.cs b/CryptoExchange.Net/SharedApis/Legacy/ClosePositionRequest.cs similarity index 100% rename from CryptoExchange.Net/SharedApis/V2/Positions/ClosePosition/ClosePositionRequest.cs rename to CryptoExchange.Net/SharedApis/Legacy/ClosePositionRequest.cs diff --git a/CryptoExchange.Net/SharedApis/Legacy/Options/ClosePositionOptions.cs b/CryptoExchange.Net/SharedApis/Legacy/Options/ClosePositionOptions.cs new file mode 100644 index 00000000..15edcf22 --- /dev/null +++ b/CryptoExchange.Net/SharedApis/Legacy/Options/ClosePositionOptions.cs @@ -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 +{ + /// + /// Options for closing position + /// +#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(); + + /// + public override string Description => "Close an open futures position"; + + /// + public override Type CapabilityType => typeof(IFuturesOrderRestClient); + + /// + public virtual RequestParameterRuleOverride[] ParameterRuleOverwrites + { + set => SetRequestParameters(value); + } + + private static readonly RequestParameterDescription[] _defaultParameterRules = new[] + { + RequestParameterRule.Required(x => x.Symbol, "The symbol of the position to close", new SharedSymbol(TradingMode.PerpetualLinear, "ETH", "USDT")), + RequestParameterRule.Required(x => x.PositionMode, "The current position mode of the account", SharedPositionMode.OneWay), + RequestParameterRule.Optional(x => x.PositionSide, "The side of the position to close", SharedPositionSide.Long), + RequestParameterRule.Optional(x => x.MarginMode, "The margin mode of the position", SharedMarginMode.Cross), + RequestParameterRule.Optional(x => x.Quantity, "The quantity of the position to close", 1m), + }; + + /// + /// ctor + /// + public ClosePositionOptions(string exchange, bool authenticated) : base(exchange, nameof(IFuturesOrderRestClient.ClosePositionAsync), true, _defaultParameterRules) + { + } + + /// + /// Validate request + /// + 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); + } + } +} diff --git a/CryptoExchange.Net/SharedApis/Options/CapabilityOptions.cs b/CryptoExchange.Net/SharedApis/Options/CapabilityOptions.cs index 77b58929..eac9bbd7 100644 --- a/CryptoExchange.Net/SharedApis/Options/CapabilityOptions.cs +++ b/CryptoExchange.Net/SharedApis/Options/CapabilityOptions.cs @@ -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(); /// 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); + } } } } diff --git a/CryptoExchange.Net/SharedApis/V2/Balances/GetBalances/GetBalancesOptions.cs b/CryptoExchange.Net/SharedApis/V2/Balances/GetBalances/GetBalancesOptions.cs index a63aca3c..9bac2589 100644 --- a/CryptoExchange.Net/SharedApis/V2/Balances/GetBalances/GetBalancesOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Balances/GetBalances/GetBalancesOptions.cs @@ -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; } /// diff --git a/CryptoExchange.Net/SharedApis/V2/Deposits/GetDepositHistory/GetDepositHistoryOptions.cs b/CryptoExchange.Net/SharedApis/V2/Deposits/GetDepositHistory/GetDepositHistoryOptions.cs index 79a698d9..969d998a 100644 --- a/CryptoExchange.Net/SharedApis/V2/Deposits/GetDepositHistory/GetDepositHistoryOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Deposits/GetDepositHistory/GetDepositHistoryOptions.cs @@ -32,6 +32,10 @@ namespace CryptoExchange.Net.SharedApis /// 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; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/Funding/GetFundingRateHistory/GetFundingRateHistoryOptions.cs b/CryptoExchange.Net/SharedApis/V2/Funding/GetFundingRateHistory/GetFundingRateHistoryOptions.cs index 5345f08c..7b8ccfc8 100644 --- a/CryptoExchange.Net/SharedApis/V2/Funding/GetFundingRateHistory/GetFundingRateHistoryOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Funding/GetFundingRateHistory/GetFundingRateHistoryOptions.cs @@ -32,6 +32,10 @@ namespace CryptoExchange.Net.SharedApis /// 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; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/Funding/GetUserFundingHistory/GetUserFundingHistoryOptions.cs b/CryptoExchange.Net/SharedApis/V2/Funding/GetUserFundingHistory/GetUserFundingHistoryOptions.cs index b52264e4..a9934174 100644 --- a/CryptoExchange.Net/SharedApis/V2/Funding/GetUserFundingHistory/GetUserFundingHistoryOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Funding/GetUserFundingHistory/GetUserFundingHistoryOptions.cs @@ -32,6 +32,10 @@ namespace CryptoExchange.Net.SharedApis /// 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"); @@ -52,7 +56,7 @@ namespace CryptoExchange.Net.SharedApis } } - return base.ValidateRequest(request, client); + return null; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/Klines/Futures/GetIndexPriceKlines/GetIndexPriceKlinesOptions.cs b/CryptoExchange.Net/SharedApis/V2/Klines/Futures/GetIndexPriceKlines/GetIndexPriceKlinesOptions.cs index 792920ad..54a8b768 100644 --- a/CryptoExchange.Net/SharedApis/V2/Klines/Futures/GetIndexPriceKlines/GetIndexPriceKlinesOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Klines/Futures/GetIndexPriceKlines/GetIndexPriceKlinesOptions.cs @@ -76,6 +76,10 @@ namespace CryptoExchange.Net.SharedApis /// 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; } /// diff --git a/CryptoExchange.Net/SharedApis/V2/Klines/Futures/GetMarkPriceKlines/GetMarkPriceKlinesOptions.cs b/CryptoExchange.Net/SharedApis/V2/Klines/Futures/GetMarkPriceKlines/GetMarkPriceKlinesOptions.cs index ddae22fc..e6db70b0 100644 --- a/CryptoExchange.Net/SharedApis/V2/Klines/Futures/GetMarkPriceKlines/GetMarkPriceKlinesOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Klines/Futures/GetMarkPriceKlines/GetMarkPriceKlinesOptions.cs @@ -76,6 +76,10 @@ namespace CryptoExchange.Net.SharedApis /// 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; } /// diff --git a/CryptoExchange.Net/SharedApis/V2/Klines/GetKlines/GetKlinesOptions.cs b/CryptoExchange.Net/SharedApis/V2/Klines/GetKlines/GetKlinesOptions.cs index bb0a6ebd..00e615c5 100644 --- a/CryptoExchange.Net/SharedApis/V2/Klines/GetKlines/GetKlinesOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Klines/GetKlines/GetKlinesOptions.cs @@ -76,6 +76,10 @@ namespace CryptoExchange.Net.SharedApis /// 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; } /// diff --git a/CryptoExchange.Net/SharedApis/V2/Klines/SubscribeKlines/SubscribeKlineOptions.cs b/CryptoExchange.Net/SharedApis/V2/Klines/SubscribeKlines/SubscribeKlineOptions.cs index 00304417..35b486cf 100644 --- a/CryptoExchange.Net/SharedApis/V2/Klines/SubscribeKlines/SubscribeKlineOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Klines/SubscribeKlines/SubscribeKlineOptions.cs @@ -69,10 +69,14 @@ namespace CryptoExchange.Net.SharedApis /// 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; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/Ledger/GetLedger/GetLedgerOptions.cs b/CryptoExchange.Net/SharedApis/V2/Ledger/GetLedger/GetLedgerOptions.cs index 0360ff85..16b3db52 100644 --- a/CryptoExchange.Net/SharedApis/V2/Ledger/GetLedger/GetLedgerOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Ledger/GetLedger/GetLedgerOptions.cs @@ -32,6 +32,10 @@ namespace CryptoExchange.Net.SharedApis /// 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"); @@ -52,7 +56,7 @@ namespace CryptoExchange.Net.SharedApis } } - return base.ValidateRequest(request, client); + return null; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/OrderBooks/GetOrderBook/GetOrderBookOptions.cs b/CryptoExchange.Net/SharedApis/V2/OrderBooks/GetOrderBook/GetOrderBookOptions.cs index 76bdfcbf..da135887 100644 --- a/CryptoExchange.Net/SharedApis/V2/OrderBooks/GetOrderBook/GetOrderBookOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/OrderBooks/GetOrderBook/GetOrderBookOptions.cs @@ -55,6 +55,10 @@ namespace CryptoExchange.Net.SharedApis /// 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; } /// diff --git a/CryptoExchange.Net/SharedApis/V2/OrderBooks/SubscribeIncrementalOrderBook/SubscribeIncrementalOrderBookOptions.cs b/CryptoExchange.Net/SharedApis/V2/OrderBooks/SubscribeIncrementalOrderBook/SubscribeIncrementalOrderBookOptions.cs index 803687e8..579680f0 100644 --- a/CryptoExchange.Net/SharedApis/V2/OrderBooks/SubscribeIncrementalOrderBook/SubscribeIncrementalOrderBookOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/OrderBooks/SubscribeIncrementalOrderBook/SubscribeIncrementalOrderBookOptions.cs @@ -44,10 +44,14 @@ namespace CryptoExchange.Net.SharedApis /// 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; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/OrderBooks/SubscribeOrderBook/SubscribeOrderBookOptions.cs b/CryptoExchange.Net/SharedApis/V2/OrderBooks/SubscribeOrderBook/SubscribeOrderBookOptions.cs index 0dee7819..f600f1df 100644 --- a/CryptoExchange.Net/SharedApis/V2/OrderBooks/SubscribeOrderBook/SubscribeOrderBookOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/OrderBooks/SubscribeOrderBook/SubscribeOrderBookOptions.cs @@ -37,10 +37,14 @@ namespace CryptoExchange.Net.SharedApis /// 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; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/Orders/Futures/GetClosedFuturesOrders/GetFuturesClosedOrdersOptions.cs b/CryptoExchange.Net/SharedApis/V2/Orders/Futures/GetClosedFuturesOrders/GetFuturesClosedOrdersOptions.cs index aa21ae5e..563a7148 100644 --- a/CryptoExchange.Net/SharedApis/V2/Orders/Futures/GetClosedFuturesOrders/GetFuturesClosedOrdersOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Orders/Futures/GetClosedFuturesOrders/GetFuturesClosedOrdersOptions.cs @@ -32,6 +32,10 @@ namespace CryptoExchange.Net.SharedApis /// 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; } } diff --git a/CryptoExchange.Net/SharedApis/V2/Orders/Futures/PlaceFuturesOrder/PlaceFuturesOrderOptions.cs b/CryptoExchange.Net/SharedApis/V2/Orders/Futures/PlaceFuturesOrder/PlaceFuturesOrderOptions.cs index b40d58d9..a19d75cd 100644 --- a/CryptoExchange.Net/SharedApis/V2/Orders/Futures/PlaceFuturesOrder/PlaceFuturesOrderOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Orders/Futures/PlaceFuturesOrder/PlaceFuturesOrderOptions.cs @@ -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; } } diff --git a/CryptoExchange.Net/SharedApis/V2/Orders/Spot/CancelSpotOrder/CancelSpotOrderOptions.cs b/CryptoExchange.Net/SharedApis/V2/Orders/Spot/CancelSpotOrder/CancelSpotOrderOptions.cs index f781676b..2218c122 100644 --- a/CryptoExchange.Net/SharedApis/V2/Orders/Spot/CancelSpotOrder/CancelSpotOrderOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Orders/Spot/CancelSpotOrder/CancelSpotOrderOptions.cs @@ -30,10 +30,14 @@ namespace CryptoExchange.Net.SharedApis /// 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; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/Orders/Spot/CancelSpotOrderByClientOrderId/CancelSpotOrderByClientOrderIdOptions.cs b/CryptoExchange.Net/SharedApis/V2/Orders/Spot/CancelSpotOrderByClientOrderId/CancelSpotOrderByClientOrderIdOptions.cs index 120332d2..ff1401c0 100644 --- a/CryptoExchange.Net/SharedApis/V2/Orders/Spot/CancelSpotOrderByClientOrderId/CancelSpotOrderByClientOrderIdOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Orders/Spot/CancelSpotOrderByClientOrderId/CancelSpotOrderByClientOrderIdOptions.cs @@ -30,10 +30,14 @@ namespace CryptoExchange.Net.SharedApis /// 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; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/Orders/Spot/EditSpotOrder/EditSpotOrderOptions.cs b/CryptoExchange.Net/SharedApis/V2/Orders/Spot/EditSpotOrder/EditSpotOrderOptions.cs index 7afe50af..0cd1f417 100644 --- a/CryptoExchange.Net/SharedApis/V2/Orders/Spot/EditSpotOrder/EditSpotOrderOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Orders/Spot/EditSpotOrder/EditSpotOrderOptions.cs @@ -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; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/Orders/Spot/GetClosedSpotOrders/GetSpotClosedOrdersOptions.cs b/CryptoExchange.Net/SharedApis/V2/Orders/Spot/GetClosedSpotOrders/GetSpotClosedOrdersOptions.cs index 6f27cef1..0ec83600 100644 --- a/CryptoExchange.Net/SharedApis/V2/Orders/Spot/GetClosedSpotOrders/GetSpotClosedOrdersOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Orders/Spot/GetClosedSpotOrders/GetSpotClosedOrdersOptions.cs @@ -32,6 +32,10 @@ namespace CryptoExchange.Net.SharedApis /// 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; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/Orders/Spot/GetSpotOrder/GetSpotOrderOptions.cs b/CryptoExchange.Net/SharedApis/V2/Orders/Spot/GetSpotOrder/GetSpotOrderOptions.cs index 9a2f8ee2..11cdbcbc 100644 --- a/CryptoExchange.Net/SharedApis/V2/Orders/Spot/GetSpotOrder/GetSpotOrderOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Orders/Spot/GetSpotOrder/GetSpotOrderOptions.cs @@ -30,10 +30,14 @@ namespace CryptoExchange.Net.SharedApis /// 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; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/Orders/Spot/GetSpotOrderByClientOrderId/GetSpotOrderByClientOrderIdOptions.cs b/CryptoExchange.Net/SharedApis/V2/Orders/Spot/GetSpotOrderByClientOrderId/GetSpotOrderByClientOrderIdOptions.cs index 2fb0ab95..0d09d2fa 100644 --- a/CryptoExchange.Net/SharedApis/V2/Orders/Spot/GetSpotOrderByClientOrderId/GetSpotOrderByClientOrderIdOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Orders/Spot/GetSpotOrderByClientOrderId/GetSpotOrderByClientOrderIdOptions.cs @@ -30,10 +30,14 @@ namespace CryptoExchange.Net.SharedApis /// 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; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/Positions/ClosePosition/CloseFullPositionOptions.cs b/CryptoExchange.Net/SharedApis/V2/Positions/ClosePosition/CloseFullPositionOptions.cs new file mode 100644 index 00000000..54bc9602 --- /dev/null +++ b/CryptoExchange.Net/SharedApis/V2/Positions/ClosePosition/CloseFullPositionOptions.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CryptoExchange.Net.SharedApis +{ + /// + /// Options for fully closing a position + /// + public class CloseFullPositionOptions : CapabilityOptions + { + /// + public override string Description => "Fully close an open futures position"; + + private static readonly RequestParameterDescription[] _defaultParameterRules = new[] + { + RequestParameterRule.Required(x => x.Symbol, "The symbol of the position to close", new SharedSymbol(TradingMode.PerpetualLinear, "ETH", "USDT")), + RequestParameterRule.Optional(x => x.PositionMode, "The current position mode of the account", SharedPositionMode.OneWay), + RequestParameterRule.Optional(x => x.PositionSide, "The side of the position to close", SharedPositionSide.Long), + RequestParameterRule.Optional(x => x.MarginMode, "The margin mode of the position", SharedMarginMode.Cross), + RequestParameterRule.Optional(x => x.PositionId, "The id of the position to close", "12345"), + }; + + /// + /// ctor + /// + public CloseFullPositionOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(ICloseFullPositionRest.CloseFullPositionAsync), _defaultParameterRules) + { + } + } +} diff --git a/CryptoExchange.Net/SharedApis/V2/Positions/ClosePosition/CloseFullPositionRequest.cs b/CryptoExchange.Net/SharedApis/V2/Positions/ClosePosition/CloseFullPositionRequest.cs new file mode 100644 index 00000000..3634ec51 --- /dev/null +++ b/CryptoExchange.Net/SharedApis/V2/Positions/ClosePosition/CloseFullPositionRequest.cs @@ -0,0 +1,49 @@ +namespace CryptoExchange.Net.SharedApis +{ + /// + /// Request to fully close a currently open position + /// + public record CloseFullPositionRequest : SharedSymbolRequest + { + /// + /// The id of the position to close + /// + public string? PositionId { get; set; } + + /// + /// The current position mode of the account for the symbol + /// + public SharedPositionMode? PositionMode { get; set; } + /// + /// The position side to close. Required when in hedge mode + /// + public SharedPositionSide? PositionSide { get; set; } + /// + /// Margin mode + /// + public SharedMarginMode? MarginMode { get; set; } + + /// + /// ctor + /// + /// Symbol to close the position on + /// The current position mode of the account for the symbol + /// The position side to close. Required when in hedge mode + /// Margin mode + /// Id of the position to close + /// Exchange specific parameters + 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; + } + } +} diff --git a/CryptoExchange.Net/SharedApis/V2/Positions/ClosePosition/ClosePositionOptions.cs b/CryptoExchange.Net/SharedApis/V2/Positions/ClosePosition/ClosePositionOptions.cs deleted file mode 100644 index 1fca44cf..00000000 --- a/CryptoExchange.Net/SharedApis/V2/Positions/ClosePosition/ClosePositionOptions.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace CryptoExchange.Net.SharedApis -{ - /// - /// Options for closing position - /// - public class ClosePositionOptions : CapabilityOptions - { - /// - public override string Description => "Close an open futures position"; - - private static readonly RequestParameterDescription[] _defaultParameterRules = new[] - { - RequestParameterRule.Required(x => x.Symbol, "The symbol of the position to close", new SharedSymbol(TradingMode.PerpetualLinear, "ETH", "USDT")), - RequestParameterRule.Required(x => x.PositionMode, "The current position mode of the account", SharedPositionMode.OneWay), - RequestParameterRule.Optional(x => x.PositionSide, "The side of the position to close", SharedPositionSide.Long), - RequestParameterRule.Optional(x => x.MarginMode, "The margin mode of the position", SharedMarginMode.Cross), - RequestParameterRule.Optional(x => x.Quantity, "The quantity of the position to close", 1m), - }; - - /// - /// ctor - /// - public ClosePositionOptions(string exchange, bool authenticated) : base(exchange, authenticated, nameof(IClosePositionRest.ClosePositionAsync), _defaultParameterRules) - { - } - } -} diff --git a/CryptoExchange.Net/SharedApis/V2/Positions/ClosePosition/IClosePosition.cs b/CryptoExchange.Net/SharedApis/V2/Positions/ClosePosition/ICloseFullPosition.cs similarity index 54% rename from CryptoExchange.Net/SharedApis/V2/Positions/ClosePosition/IClosePosition.cs rename to CryptoExchange.Net/SharedApis/V2/Positions/ClosePosition/ICloseFullPosition.cs index 32fcf35e..25b283ad 100644 --- a/CryptoExchange.Net/SharedApis/V2/Positions/ClosePosition/IClosePosition.cs +++ b/CryptoExchange.Net/SharedApis/V2/Positions/ClosePosition/ICloseFullPosition.cs @@ -8,31 +8,31 @@ using System.Threading.Tasks; namespace CryptoExchange.Net.SharedApis { /// - /// Operation for closing an open position on an exchange. + /// Operation for fully closing an open position on an exchange. /// - public interface IClosePosition : ISharedApiCapability + public interface ICloseFullPosition : ISharedApiCapability { /// /// Close position order request options.
/// Use and to check for required and optional parameters for the request.
/// Exchange specific parameters can be added to the request via the `ExchangeParameters` property of the request object. ///
- ClosePositionOptions ClosePositionOptions { get; } + CloseFullPositionOptions CloseFullPositionOptions { get; } /// - /// Close a currently open position, see for request options and exchange specific required/optional parameters.
+ /// Fully close a currently open position, see for request options and exchange specific required/optional parameters.
///
/// Request info /// Cancellation token /// - Task> ClosePositionAsync(ClosePositionRequest request, CancellationToken ct = default); + Task> CloseFullPositionAsync(CloseFullPositionRequest request, CancellationToken ct = default); } /// - /// 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. /// - public interface IClosePositionRest : IClosePosition, ISharedRest + public interface ICloseFullPositionRest : ICloseFullPosition, ISharedRest { /// - new Task> ClosePositionAsync(ClosePositionRequest request, CancellationToken ct = default); + new Task> CloseFullPositionAsync(CloseFullPositionRequest request, CancellationToken ct = default); } } diff --git a/CryptoExchange.Net/SharedApis/V2/Positions/GetPositionHistory/GetPositionHistoryOptions.cs b/CryptoExchange.Net/SharedApis/V2/Positions/GetPositionHistory/GetPositionHistoryOptions.cs index b70c6dc0..b79249a9 100644 --- a/CryptoExchange.Net/SharedApis/V2/Positions/GetPositionHistory/GetPositionHistoryOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Positions/GetPositionHistory/GetPositionHistoryOptions.cs @@ -33,6 +33,10 @@ namespace CryptoExchange.Net.SharedApis /// 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; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/Symbols/GetFuturesSymbols/GetFuturesSymbolsOptions.cs b/CryptoExchange.Net/SharedApis/V2/Symbols/GetFuturesSymbols/GetFuturesSymbolsOptions.cs index 9e1684c5..9de6c4bc 100644 --- a/CryptoExchange.Net/SharedApis/V2/Symbols/GetFuturesSymbols/GetFuturesSymbolsOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Symbols/GetFuturesSymbols/GetFuturesSymbolsOptions.cs @@ -32,21 +32,25 @@ namespace CryptoExchange.Net.SharedApis /// 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) diff --git a/CryptoExchange.Net/SharedApis/V2/Symbols/GetSpotSymbols/GetSpotSymbolsOptions.cs b/CryptoExchange.Net/SharedApis/V2/Symbols/GetSpotSymbols/GetSpotSymbolsOptions.cs index e1e03fdf..63f73084 100644 --- a/CryptoExchange.Net/SharedApis/V2/Symbols/GetSpotSymbols/GetSpotSymbolsOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Symbols/GetSpotSymbols/GetSpotSymbolsOptions.cs @@ -33,21 +33,25 @@ namespace CryptoExchange.Net.SharedApis /// 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) diff --git a/CryptoExchange.Net/SharedApis/V2/Tickers/GetAllTickers/GetAllTickersOptions.cs b/CryptoExchange.Net/SharedApis/V2/Tickers/GetAllTickers/GetAllTickersOptions.cs index 80ec1968..012e6d27 100644 --- a/CryptoExchange.Net/SharedApis/V2/Tickers/GetAllTickers/GetAllTickersOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Tickers/GetAllTickers/GetAllTickersOptions.cs @@ -8,11 +8,11 @@ namespace CryptoExchange.Net.SharedApis public class GetAllTickersOptions : CapabilityOptions { /// - public override string Description => "Retrieve price ticker information for all symbols"; + public override string Description => "Retrieve price ticker information for all symbols in a trading mode"; private static readonly RequestParameterDescription[] _defaultParameterRules = new[] { - RequestParameterRule.Optional(x => x.TradingMode, "Filter the tickers by trading mode", TradingMode.Spot), + RequestParameterRule.Optional(x => x.TradingMode, "The trading mode to retrieve tickers for", TradingMode.Spot), }; /// diff --git a/CryptoExchange.Net/SharedApis/V2/Tickers/GetTickersRequest.cs b/CryptoExchange.Net/SharedApis/V2/Tickers/GetTickersRequest.cs index af56d2ab..ce8b9bd0 100644 --- a/CryptoExchange.Net/SharedApis/V2/Tickers/GetTickersRequest.cs +++ b/CryptoExchange.Net/SharedApis/V2/Tickers/GetTickersRequest.cs @@ -1,4 +1,4 @@ -namespace CryptoExchange.Net.SharedApis +namespace CryptoExchange.Net.SharedApis { /// /// Request to retrieve all symbol tickers @@ -8,7 +8,7 @@ /// /// ctor /// - /// Trading mode + /// The trading mode to retrieve tickers for. Required when using . /// Exchange specific parameters public GetTickersRequest(TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null) : base(tradingMode, exchangeParameters) { diff --git a/CryptoExchange.Net/SharedApis/V2/Trades/GetTradeHistory/GetTradeHistoryOptions.cs b/CryptoExchange.Net/SharedApis/V2/Trades/GetTradeHistory/GetTradeHistoryOptions.cs index af54581c..3aa01de0 100644 --- a/CryptoExchange.Net/SharedApis/V2/Trades/GetTradeHistory/GetTradeHistoryOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Trades/GetTradeHistory/GetTradeHistoryOptions.cs @@ -32,6 +32,10 @@ namespace CryptoExchange.Net.SharedApis /// 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; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/Transfers/GetTransferHistory/GetTransferHistoryOptions.cs b/CryptoExchange.Net/SharedApis/V2/Transfers/GetTransferHistory/GetTransferHistoryOptions.cs index 7a56bc11..70af0bc5 100644 --- a/CryptoExchange.Net/SharedApis/V2/Transfers/GetTransferHistory/GetTransferHistoryOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Transfers/GetTransferHistory/GetTransferHistoryOptions.cs @@ -32,6 +32,10 @@ namespace CryptoExchange.Net.SharedApis /// 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"); @@ -52,7 +56,7 @@ namespace CryptoExchange.Net.SharedApis } } - return base.ValidateRequest(request, client); + return null; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/Transfers/Transfer/TransferOptions.cs b/CryptoExchange.Net/SharedApis/V2/Transfers/Transfer/TransferOptions.cs index 754f881f..7adf3a60 100644 --- a/CryptoExchange.Net/SharedApis/V2/Transfers/Transfer/TransferOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Transfers/Transfer/TransferOptions.cs @@ -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; } /// diff --git a/CryptoExchange.Net/SharedApis/V2/TriggerOrders/Futures/PlaceFuturesTriggerOrder/PlaceFuturesTriggerOrderOptions.cs b/CryptoExchange.Net/SharedApis/V2/TriggerOrders/Futures/PlaceFuturesTriggerOrder/PlaceFuturesTriggerOrderOptions.cs index a26f1ec9..2247f83b 100644 --- a/CryptoExchange.Net/SharedApis/V2/TriggerOrders/Futures/PlaceFuturesTriggerOrder/PlaceFuturesTriggerOrderOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/TriggerOrders/Futures/PlaceFuturesTriggerOrder/PlaceFuturesTriggerOrderOptions.cs @@ -25,6 +25,7 @@ namespace CryptoExchange.Net.SharedApis RequestParameterRule.Optional(x => x.MarginMode, "The margin mode of the order", SharedMarginMode.Cross), RequestParameterRule.Optional(x => x.Leverage, "The leverage for the position", 10m), RequestParameterRule.Optional(x => x.TriggerPriceType, "The price type used to trigger the order", SharedTriggerPriceType.LastPrice), + RequestParameterRule.Optional(x => x.ReduceOnly, "Whether the order is reduce only", true), }; /// @@ -39,5 +40,18 @@ namespace CryptoExchange.Net.SharedApis { HoldsFunds = holdsFunds; } + + /// + 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; + } } } diff --git a/CryptoExchange.Net/SharedApis/V2/TriggerOrders/Spot/CancelSpotTriggerOrder/CancelSpotTriggerOrderOptions.cs b/CryptoExchange.Net/SharedApis/V2/TriggerOrders/Spot/CancelSpotTriggerOrder/CancelSpotTriggerOrderOptions.cs index e588718c..aba1dc9e 100644 --- a/CryptoExchange.Net/SharedApis/V2/TriggerOrders/Spot/CancelSpotTriggerOrder/CancelSpotTriggerOrderOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/TriggerOrders/Spot/CancelSpotTriggerOrder/CancelSpotTriggerOrderOptions.cs @@ -29,10 +29,14 @@ namespace CryptoExchange.Net.SharedApis /// 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; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/TriggerOrders/Spot/GetSpotTriggerOrder/GetSpotTriggerOrderOptions.cs b/CryptoExchange.Net/SharedApis/V2/TriggerOrders/Spot/GetSpotTriggerOrder/GetSpotTriggerOrderOptions.cs index 58ac2e74..85994919 100644 --- a/CryptoExchange.Net/SharedApis/V2/TriggerOrders/Spot/GetSpotTriggerOrder/GetSpotTriggerOrderOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/TriggerOrders/Spot/GetSpotTriggerOrder/GetSpotTriggerOrderOptions.cs @@ -29,10 +29,14 @@ namespace CryptoExchange.Net.SharedApis /// 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; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/TriggerOrders/Spot/PlaceSpotTriggerOrder/PlaceSpotTriggerOrderOptions.cs b/CryptoExchange.Net/SharedApis/V2/TriggerOrders/Spot/PlaceSpotTriggerOrder/PlaceSpotTriggerOrderOptions.cs index 2c4272e9..46920d65 100644 --- a/CryptoExchange.Net/SharedApis/V2/TriggerOrders/Spot/PlaceSpotTriggerOrder/PlaceSpotTriggerOrderOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/TriggerOrders/Spot/PlaceSpotTriggerOrder/PlaceSpotTriggerOrderOptions.cs @@ -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; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/UserTrades/Futures/GetFuturesUserTradeHistory/GetFuturesUserTradeHistoryOptions.cs b/CryptoExchange.Net/SharedApis/V2/UserTrades/Futures/GetFuturesUserTradeHistory/GetFuturesUserTradeHistoryOptions.cs index ef799bc9..9549d6d3 100644 --- a/CryptoExchange.Net/SharedApis/V2/UserTrades/Futures/GetFuturesUserTradeHistory/GetFuturesUserTradeHistoryOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/UserTrades/Futures/GetFuturesUserTradeHistory/GetFuturesUserTradeHistoryOptions.cs @@ -32,6 +32,10 @@ namespace CryptoExchange.Net.SharedApis /// 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; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/UserTrades/Spot/GetSpotOrderTrades/GetSpotOrderTradesOptions.cs b/CryptoExchange.Net/SharedApis/V2/UserTrades/Spot/GetSpotOrderTrades/GetSpotOrderTradesOptions.cs index fce534f9..655f9b78 100644 --- a/CryptoExchange.Net/SharedApis/V2/UserTrades/Spot/GetSpotOrderTrades/GetSpotOrderTradesOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/UserTrades/Spot/GetSpotOrderTrades/GetSpotOrderTradesOptions.cs @@ -29,10 +29,14 @@ namespace CryptoExchange.Net.SharedApis /// 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; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/UserTrades/Spot/GetSpotUserTradeHistory/GetSpotUserTradeHistoryOptions.cs b/CryptoExchange.Net/SharedApis/V2/UserTrades/Spot/GetSpotUserTradeHistory/GetSpotUserTradeHistoryOptions.cs index 6459ba66..6561db80 100644 --- a/CryptoExchange.Net/SharedApis/V2/UserTrades/Spot/GetSpotUserTradeHistory/GetSpotUserTradeHistoryOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/UserTrades/Spot/GetSpotUserTradeHistory/GetSpotUserTradeHistoryOptions.cs @@ -32,6 +32,10 @@ namespace CryptoExchange.Net.SharedApis /// 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; } } } diff --git a/CryptoExchange.Net/SharedApis/V2/Withdrawals/GetWithdrawalHistory/GetWithdrawalHistoryOptions.cs b/CryptoExchange.Net/SharedApis/V2/Withdrawals/GetWithdrawalHistory/GetWithdrawalHistoryOptions.cs index de10678e..0305e80a 100644 --- a/CryptoExchange.Net/SharedApis/V2/Withdrawals/GetWithdrawalHistory/GetWithdrawalHistoryOptions.cs +++ b/CryptoExchange.Net/SharedApis/V2/Withdrawals/GetWithdrawalHistory/GetWithdrawalHistoryOptions.cs @@ -32,6 +32,10 @@ namespace CryptoExchange.Net.SharedApis /// 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; } } }