1
0
mirror of https://github.com/JKorf/CryptoExchange.Net.git synced 2026-10-16 16:10:53 +00:00

Feature/9.0.0 (#236)

* Added support for Native AOT compilation
* Updated all IEnumerable response types to array response types
* Added Pass support for ApiCredentials, removing the need for most implementations to add their own ApiCredentials type
* Added KeepAliveTimeout setting setting ping frame timeouts for SocketApiClient
* Added IBookTickerRestClient Shared interface for requesting book tickers
* Added ISpotTriggerOrderRestClient Shared interface for managing spot trigger orders
* Added ISpotOrderClientIdClient Shared interface for managing spot orders by client order id
* Added IFuturesTriggerOrderRestClient Shared interface for managing futures trigger orders
* Added IFuturesOrderClientIdClient Shared interface for managing futures orders by client order id
* Added IFuturesTpSlRestClient Shared interface for setting TP/SL on open futures positions
* Added GenerateClientOrderId to ISpotOrderRestClient and IFuturesOrderRestClient interface
* Added OptionalExchangeParameters and Supported properties to EndpointOptions
* Refactor Shared interfaces quantity parameters and properties to use SharedQuantity
* Added SharedSymbol property to Shared interface models returning a symbol
* Added TriggerPrice, IsTriggerOrder, TakeProfitPrice, StopLossPrice and IsCloseOrder to SharedFuturesOrder response model
* Added MaxShortLeverage and MaxLongLeverage to SharedFuturesSymbol response model
* Added StopLossPrice and TakeProfitPrice to SharedPosition response model
* Added TriggerPrice and IsTriggerOrder to SharedSpotOrder response model
* Added QuoteVolume property to SharedSpotTicker response model
* Added AssetAlias configuration models
* Added static ExchangeSymbolCache for tracking symbol information from exchanges
* Added static CallResult.SuccessResult to be used instead of constructing success CallResult instance
* Added static ApplyRules, RandomHexString and RandomLong helper methods to ExchangeHelpers class
* Added AsErrorWithData To CallResult
* Added OriginalData property to CallResult
* Added support for adjusting the rate limit key per call, allowing for ratelimiting depending on request parameters
* Added implementation for integration testing ISymbolOrderBook instances
* Added implementation for integration testing socket subscriptions
* Added implementation for testing socket queries
* Updated request cancellation logging to Debug level
* Updated logging SourceContext to include the client type
* Updated some logging logic, errors no longer contain any data, exception are not logged as string but instead forwarded to structured logging
* Fixed warning for Enum parsing throwing exception and output warnings for each object in a response to only once to prevent slowing down execution
* Fixed memory leak in AsyncAutoRestEvent
* Fixed logging for ping frame timeout
* Fixed warning getting logged when user stops SymbolOrderBook instance
* Fixed socket client `UnsubscribeAll` not unsubscribing dedicated connections
* Fixed memory leak in Rest client cache
* Fixed integers bigger than int16 not getting correctly parsed to enums
* Fixed issue where the default options were overridden when using SetApiCredentials
* Removed Newtonsoft.Json dependency
* Removed legacy Rest client code
* Removed legacy ISpotClient and IFuturesClient support
This commit is contained in:
Jan Korf
2025-05-13 10:15:30 +02:00
committed by GitHub
parent 3d6267da93
commit 6b14cdbf06
182 changed files with 3159 additions and 3950 deletions
@@ -100,7 +100,7 @@ namespace CryptoExchange.Net.SharedApis
string exchange,
TradingMode[]? dataTradeModes,
HttpStatusCode? code,
IEnumerable<KeyValuePair<string, IEnumerable<string>>>? responseHeaders,
KeyValuePair<string, string[]>[]? responseHeaders,
TimeSpan? responseTime,
long? responseLength,
string? originalData,
@@ -108,7 +108,7 @@ namespace CryptoExchange.Net.SharedApis
string? requestUrl,
string? requestBody,
HttpMethod? requestMethod,
IEnumerable<KeyValuePair<string, IEnumerable<string>>>? requestHeaders,
KeyValuePair<string, string[]>[]? requestHeaders,
ResultDataSource dataSource,
[AllowNull] T data,
Error? error,
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
@@ -17,6 +18,10 @@ namespace CryptoExchange.Net.SharedApis
/// </summary>
public List<ParameterDescription> RequiredExchangeParameters { get; set; } = new List<ParameterDescription>();
/// <summary>
/// Optional exchange-specific parameters
/// </summary>
public List<ParameterDescription> OptionalExchangeParameters { get; set; } = new List<ParameterDescription>();
/// <summary>
/// Endpoint name
/// </summary>
public string EndpointName { get; set; }
@@ -28,6 +33,10 @@ namespace CryptoExchange.Net.SharedApis
/// Whether the call requires authentication
/// </summary>
public bool NeedsAuthentication { get; set; }
/// <summary>
/// Whether the call is supported by the exchange
/// </summary>
public bool Supported { get; set; } = true;
/// <summary>
/// ctor
@@ -71,12 +80,16 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public virtual string ToString(string exchange)
{
if (!Supported)
return $"{exchange} {EndpointName} NOT SUPPORTED";
var sb = new StringBuilder();
sb.AppendLine($"{exchange} {EndpointName}");
if (!string.IsNullOrEmpty(RequestNotes))
sb.AppendLine(RequestNotes);
sb.AppendLine($"Needs authentication: {NeedsAuthentication}");
sb.AppendLine($"Required exchange specific parameters: {string.Join(", ", RequiredExchangeParameters.Select(x => x.ToString()))}");
sb.AppendLine($"Optional exchange specific parameters: {string.Join(", ", OptionalExchangeParameters.Select(x => x.ToString()))}");
return sb.ToString();
}
}
@@ -85,7 +98,11 @@ namespace CryptoExchange.Net.SharedApis
/// Options for an exchange endpoint
/// </summary>
/// <typeparam name="T">Type of data</typeparam>
#if NET5_0_OR_GREATER
public class EndpointOptions<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T> : EndpointOptions where T : SharedRequest
#else
public class EndpointOptions<T> : EndpointOptions where T : SharedRequest
#endif
{
/// <summary>
/// Required optional parameters in the request
@@ -130,6 +147,9 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc />
public override string ToString(string exchange)
{
if (!Supported)
return $"{exchange} {EndpointName} NOT SUPPORTED";
var sb = new StringBuilder();
sb.AppendLine($"{exchange} {typeof(T).Name}");
sb.AppendLine($"Needs authentication: {NeedsAuthentication}");
@@ -139,6 +159,8 @@ namespace CryptoExchange.Net.SharedApis
sb.AppendLine($"Required optional parameters: {string.Join(", ", RequiredOptionalParameters.Select(x => x.ToString()))}");
if (RequiredExchangeParameters.Any())
sb.AppendLine($"Required exchange specific parameters: {string.Join(", ", RequiredExchangeParameters.Select(x => x.ToString()))}");
if (OptionalExchangeParameters.Any())
sb.AppendLine($"Optional exchange specific parameters: {string.Join(", ", RequiredExchangeParameters.Select(x => x.ToString()))}");
return sb.ToString();
}
}
@@ -14,7 +14,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// The supported kline intervals
/// </summary>
public IEnumerable<SharedKlineInterval> SupportIntervals { get; }
public SharedKlineInterval[] SupportIntervals { get; }
/// <summary>
/// Max number of data points which can be requested
/// </summary>
@@ -14,7 +14,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Supported order book depths
/// </summary>
public IEnumerable<int>? SupportedLimits { get; set; }
public int[]? SupportedLimits { get; set; }
/// <summary>
/// The min order book depth
@@ -37,7 +37,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public GetOrderBookOptions(IEnumerable<int> supportedLimits, bool authenticated) : base(authenticated)
public GetOrderBookOptions(int[] supportedLimits, bool authenticated) : base(authenticated)
{
SupportedLimits = supportedLimits;
}
@@ -1,4 +1,5 @@
using CryptoExchange.Net.Objects;
using System.Diagnostics.CodeAnalysis;
using System.Text;
namespace CryptoExchange.Net.SharedApis
@@ -7,7 +8,11 @@ namespace CryptoExchange.Net.SharedApis
/// Options for paginated endpoints
/// </summary>
/// <typeparam name="T"></typeparam>
#if NET5_0_OR_GREATER
public class PaginatedEndpointOptions<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T> : EndpointOptions<T> where T : SharedRequest
#else
public class PaginatedEndpointOptions<T> : EndpointOptions<T> where T : SharedRequest
#endif
{
/// <summary>
/// Type of pagination supported
@@ -10,11 +10,17 @@ namespace CryptoExchange.Net.SharedApis
/// </summary>
public class PlaceFuturesOrderOptions : EndpointOptions<PlaceFuturesOrderRequest>
{
/// <summary>
/// Whether or not the API supports setting take profit / stop loss with the order
/// </summary>
public bool SupportsTpSl { get; set; }
/// <summary>
/// ctor
/// </summary>
public PlaceFuturesOrderOptions() : base(true)
public PlaceFuturesOrderOptions(bool supportsTpSl) : base(true)
{
SupportsTpSl = supportsTpSl;
}
/// <summary>
@@ -25,10 +31,13 @@ namespace CryptoExchange.Net.SharedApis
PlaceFuturesOrderRequest request,
TradingMode? tradingMode,
TradingMode[] supportedApiTypes,
IEnumerable<SharedOrderType> supportedOrderTypes,
IEnumerable<SharedTimeInForce> supportedTimeInForce,
SharedOrderType[] supportedOrderTypes,
SharedTimeInForce[] supportedTimeInForce,
SharedQuantitySupport quantitySupport)
{
if (!SupportsTpSl && (request.StopLossPrice != null || request.TakeProfitPrice != null))
return new ArgumentError("Tp/Sl parameters not supported");
if (request.OrderType == SharedOrderType.Other)
throw new ArgumentException("OrderType can't be `Other`", nameof(request.OrderType));
@@ -38,7 +47,7 @@ namespace CryptoExchange.Net.SharedApis
if (request.TimeInForce != null && !supportedTimeInForce.Contains(request.TimeInForce.Value))
return new ArgumentError("Order time in force not supported");
var quantityError = quantitySupport.Validate(request.Side, request.OrderType, request.Quantity, request.QuoteQuantity);
var quantityError = quantitySupport.Validate(request.Side, request.OrderType, request.Quantity);
if (quantityError != null)
return quantityError;
@@ -0,0 +1,44 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for placing a new spot trigger order
/// </summary>
public class PlaceFuturesTriggerOrderOptions : EndpointOptions<PlaceFuturesTriggerOrderRequest>
{
/// <summary>
/// When true the API holds the funds until the order is triggered or canceled. When true the funds will only be required when the order is triggered and will fail if the funds are not available at that time.
/// </summary>
public bool HoldsFunds { get; set; }
/// <summary>
/// ctor
/// </summary>
public PlaceFuturesTriggerOrderOptions(bool holdsFunds) : base(true)
{
HoldsFunds = holdsFunds;
}
/// <summary>
/// Validate a request
/// </summary>
public Error? ValidateRequest(
string exchange,
PlaceFuturesTriggerOrderRequest request,
TradingMode? tradingMode,
TradingMode[] supportedApiTypes,
SharedOrderSide side,
SharedQuantitySupport quantitySupport)
{
var quantityError = quantitySupport.Validate(side, request.OrderPrice == null ? SharedOrderType.Market : SharedOrderType.Limit, request.Quantity);
if (quantityError != null)
return quantityError;
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
}
}
}
@@ -26,8 +26,8 @@ namespace CryptoExchange.Net.SharedApis
PlaceSpotOrderRequest request,
TradingMode? tradingMode,
TradingMode[] supportedApiTypes,
IEnumerable<SharedOrderType> supportedOrderTypes,
IEnumerable<SharedTimeInForce> supportedTimeInForce,
SharedOrderType[] supportedOrderTypes,
SharedTimeInForce[] supportedTimeInForce,
SharedQuantitySupport quantitySupport)
{
if (request.OrderType == SharedOrderType.Other)
@@ -39,7 +39,7 @@ namespace CryptoExchange.Net.SharedApis
if (request.TimeInForce != null && !supportedTimeInForce.Contains(request.TimeInForce.Value))
return new ArgumentError("Order time in force not supported");
var quantityError = quantitySupport.Validate(request.Side, request.OrderType, request.Quantity, request.QuoteQuantity);
var quantityError = quantitySupport.Validate(request.Side, request.OrderType, request.Quantity);
if (quantityError != null)
return quantityError;
@@ -0,0 +1,43 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for placing a new spot trigger order
/// </summary>
public class PlaceSpotTriggerOrderOptions : EndpointOptions<PlaceSpotTriggerOrderRequest>
{
/// <summary>
/// When true the API holds the funds until the order is triggered or canceled. When true the funds will only be required when the order is triggered and will fail if the funds are not available at that time.
/// </summary>
public bool HoldsFunds { get; set; }
/// <summary>
/// ctor
/// </summary>
public PlaceSpotTriggerOrderOptions(bool holdsFunds) : base(true)
{
HoldsFunds = holdsFunds;
}
/// <summary>
/// Validate a request
/// </summary>
public Error? ValidateRequest(
string exchange,
PlaceSpotTriggerOrderRequest request,
TradingMode? tradingMode,
TradingMode[] supportedApiTypes,
SharedQuantitySupport quantitySupport)
{
var quantityError = quantitySupport.Validate(request.OrderSide, request.OrderPrice == null ? SharedOrderType.Market : SharedOrderType.Limit, request.Quantity);
if (quantityError != null)
return quantityError;
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
}
}
}
@@ -13,7 +13,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Kline intervals supported for updates
/// </summary>
public IEnumerable<SharedKlineInterval> SupportIntervals { get; }
public SharedKlineInterval[] SupportIntervals { get; }
/// <summary>
/// ctor
@@ -13,12 +13,12 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Order book depths supported for updates
/// </summary>
public IEnumerable<int> SupportedLimits { get; }
public int[] SupportedLimits { get; }
/// <summary>
/// ctor
/// </summary>
public SubscribeOrderBookOptions(bool needsAuthentication, IEnumerable<int> limits) : base(needsAuthentication)
public SubscribeOrderBookOptions(bool needsAuthentication, int[] limits) : base(needsAuthentication)
{
SupportedLimits = limits;
}
@@ -0,0 +1,56 @@
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to cancel a take profit / stop loss
/// </summary>
public record CancelTpSlRequest : SharedSymbolRequest
{
/// <summary>
/// Id of order to cancel
/// </summary>
public string? OrderId { get; set; }
/// <summary>
/// Position mode
/// </summary>
public SharedPositionMode? PositionMode { get; set; }
/// <summary>
/// Position side
/// </summary>
public SharedPositionSide? PositionSide { get; set; }
/// <summary>
/// Take profit / Stop loss side
/// </summary>
public SharedTpSlSide? TpSlSide { get; set; }
/// <summary>
/// Margin mode
/// </summary>
public SharedMarginMode? MarginMode { get; set; }
/// <summary>
/// ctor for canceling by order id
/// </summary>
/// <param name="symbol">Symbol the order is on</param>
/// <param name="orderId">Id of the order to close</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public CancelTpSlRequest(SharedSymbol symbol, string orderId, ExchangeParameters? exchangeParameters = null) : base(symbol, exchangeParameters)
{
OrderId = orderId;
}
/// <summary>
/// ctor for canceling without order id
/// </summary>
/// <param name="symbol">Symbol the order is on</param>
/// <param name="mode">The position mode of the account</param>
/// <param name="positionSide">The side of the position</param>
/// <param name="tpSlSide">The side to cancel</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public CancelTpSlRequest(SharedSymbol symbol, SharedPositionMode mode, SharedPositionSide positionSide, SharedTpSlSide tpSlSide, ExchangeParameters? exchangeParameters = null) : base(symbol, exchangeParameters)
{
PositionMode = mode;
PositionSide = positionSide;
TpSlSide = tpSlSide;
}
}
}
@@ -0,0 +1,17 @@
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to retrieve best bid/ask info for a symbol
/// </summary>
public record GetBookTickerRequest : SharedSymbolRequest
{
/// <summary>
/// ctor
/// </summary>
/// <param name="symbol">Symbol to retrieve book ticker for</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public GetBookTickerRequest(SharedSymbol symbol, ExchangeParameters? exchangeParameters = null) : base(symbol, exchangeParameters)
{
}
}
}
@@ -1,7 +1,7 @@
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to retrieve info on a specifc order
/// Request to retrieve info on a specific order
/// </summary>
public record GetOrderRequest : SharedSymbolRequest
{
@@ -18,13 +18,9 @@
/// </summary>
public SharedTimeInForce? TimeInForce { get; set; }
/// <summary>
/// Quantity of the order in base asset.
/// Quantity of the order
/// </summary>
public decimal? Quantity { get; set; }
/// <summary>
/// Quantity of the order in quote asset.
/// </summary>
public decimal? QuoteQuantity { get; set; }
public SharedQuantity? Quantity { get; set; }
/// <summary>
/// Price of the order
/// </summary>
@@ -50,6 +46,15 @@
/// </summary>
public decimal? Leverage { get; set; }
/// <summary>
/// Take profit price
/// </summary>
public decimal? TakeProfitPrice { get; set; }
/// <summary>
/// Stop loss price
/// </summary>
public decimal? StopLossPrice { get; set; }
/// <summary>
/// ctor
/// </summary>
@@ -58,7 +63,6 @@
/// <param name="side">Side of the order</param>
/// <param name="orderType">Type of the order</param>
/// <param name="quantity">Quantity of the order</param>
/// <param name="quoteQuantity">Quantity of the order in quote asset</param>
/// <param name="price">Price of the order</param>
/// <param name="timeInForce">Time in force</param>
/// <param name="clientOrderId">Client order id</param>
@@ -71,8 +75,7 @@
SharedSymbol symbol,
SharedOrderSide side,
SharedOrderType orderType,
decimal? quantity = null,
decimal? quoteQuantity = null,
SharedQuantity? quantity = null,
decimal? price = null,
bool? reduceOnly = null,
decimal? leverage = null,
@@ -85,7 +88,6 @@
Side = side;
OrderType = orderType;
Quantity = quantity;
QuoteQuantity = quoteQuantity;
Price = price;
MarginMode = marginMode;
ClientOrderId = clientOrderId;
@@ -0,0 +1,85 @@
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to place a new trigger order
/// </summary>
public record PlaceFuturesTriggerOrderRequest : SharedSymbolRequest
{
/// <summary>
/// Client order id
/// </summary>
public string? ClientOrderId { get; set; }
/// <summary>
/// Direction of the trigger order
/// </summary>
public SharedTriggerOrderDirection OrderDirection { get; set; }
/// <summary>
/// Price trigger direction
/// </summary>
public SharedTriggerPriceDirection PriceDirection { get; set; }
/// <summary>
/// Quantity of the order
/// </summary>
public SharedQuantity Quantity { get; set; }
/// <summary>
/// Price of the order
/// </summary>
public decimal? OrderPrice { get; set; }
/// <summary>
/// Trigger price
/// </summary>
public decimal TriggerPrice { get; set; }
/// <summary>
/// Time in force
/// </summary>
public SharedTimeInForce? TimeInForce { get; set; }
/// <summary>
/// Position mode
/// </summary>
public SharedPositionMode? PositionMode { get; set; }
/// <summary>
/// Position side
/// </summary>
public SharedPositionSide PositionSide { get; set; }
/// <summary>
/// Margin mode
/// </summary>
public SharedMarginMode? MarginMode { get; set; }
/// <summary>
/// Leverage
/// </summary>
public decimal? Leverage { get; set; }
/// <summary>
/// Trigger price type
/// </summary>
public SharedTriggerPriceType? TriggerPriceType { get; set; }
/// <summary>
/// ctor
/// </summary>
/// <param name="symbol">Symbol the order is on</param>
/// <param name="orderDirection">Direction of the order when triggered</param>
/// <param name="priceDirection">Price direction</param>
/// <param name="quantity">Quantity of the order</param>
/// <param name="positionSide">Position side</param>
/// <param name="triggerPrice">Price at which the order should activate</param>
/// <param name="orderPrice">Limit price for the order</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public PlaceFuturesTriggerOrderRequest(SharedSymbol symbol,
SharedTriggerPriceDirection priceDirection,
decimal triggerPrice,
SharedTriggerOrderDirection orderDirection,
SharedPositionSide positionSide,
SharedQuantity quantity,
decimal? orderPrice = null,
ExchangeParameters? exchangeParameters = null) : base(symbol, exchangeParameters)
{
PriceDirection = priceDirection;
PositionSide = positionSide;
Quantity = quantity;
OrderPrice = orderPrice;
TriggerPrice = triggerPrice;
OrderDirection = orderDirection;
}
}
}
@@ -18,13 +18,9 @@
/// </summary>
public SharedTimeInForce? TimeInForce { get; set; }
/// <summary>
/// Quantity of the order in base asset or contracts, depending on the exchange.
/// Quantity of the order
/// </summary>
public decimal? Quantity { get; set; }
/// <summary>
/// Quantity of the order in quote asset.
/// </summary>
public decimal? QuoteQuantity { get; set; }
public SharedQuantity? Quantity { get; set; }
/// <summary>
/// Price of the order
/// </summary>
@@ -41,7 +37,6 @@
/// <param name="side">Side of the order</param>
/// <param name="orderType">Type of the order</param>
/// <param name="quantity">Quantity of the order</param>
/// <param name="quoteQuantity">Quantity of the order in quote asset</param>
/// <param name="price">Price of the order</param>
/// <param name="timeInForce">Time in force</param>
/// <param name="clientOrderId">Client order id</param>
@@ -50,8 +45,7 @@
SharedSymbol symbol,
SharedOrderSide side,
SharedOrderType orderType,
decimal? quantity = null,
decimal? quoteQuantity = null,
SharedQuantity? quantity = null,
decimal? price = null,
SharedTimeInForce? timeInForce = null,
string? clientOrderId = null,
@@ -60,7 +54,6 @@
OrderType = orderType;
Side = side;
Quantity = quantity;
QuoteQuantity = quoteQuantity;
Price = price;
TimeInForce = timeInForce;
ClientOrderId = clientOrderId;
@@ -0,0 +1,62 @@
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to place a new trigger order
/// </summary>
public record PlaceSpotTriggerOrderRequest : SharedSymbolRequest
{
/// <summary>
/// Client order id
/// </summary>
public string? ClientOrderId { get; set; }
/// <summary>
/// Direction of the trigger order
/// </summary>
public SharedOrderSide OrderSide { get; set; }
/// <summary>
/// Price trigger direction
/// </summary>
public SharedTriggerPriceDirection PriceDirection { get; set; }
/// <summary>
/// Time in force
/// </summary>
public SharedTimeInForce? TimeInForce { get; set; }
/// <summary>
/// Quantity of the order
/// </summary>
public SharedQuantity Quantity { get; set; }
/// <summary>
/// Price of the order
/// </summary>
public decimal? OrderPrice { get; set; }
/// <summary>
/// Trigger price
/// </summary>
public decimal TriggerPrice { get; set; }
/// <summary>
/// ctor
/// </summary>
/// <param name="symbol">Symbol the order is on</param>
/// <param name="orderSide">Order side</param>
/// <param name="priceDirection">Price direction</param>
/// <param name="quantity">Quantity of the order</param>
/// <param name="triggerPrice">Price at which the order should activate</param>
/// <param name="orderPrice">Limit price for the order</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public PlaceSpotTriggerOrderRequest(SharedSymbol symbol,
SharedTriggerPriceDirection priceDirection,
decimal triggerPrice,
SharedOrderSide orderSide,
SharedQuantity quantity,
decimal? orderPrice = null,
ExchangeParameters? exchangeParameters = null) : base(symbol, exchangeParameters)
{
PriceDirection = priceDirection;
Quantity = quantity;
OrderPrice = orderPrice;
TriggerPrice = triggerPrice;
OrderSide = orderSide;
}
}
}
@@ -0,0 +1,50 @@
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Set a take profit and/or stop loss for an open position
/// </summary>
public record SetTpSlRequest : SharedSymbolRequest
{
/// <summary>
/// Position mode
/// </summary>
public SharedPositionMode? PositionMode { get; set; }
/// <summary>
/// Position side
/// </summary>
public SharedPositionSide PositionSide { get; set; }
/// <summary>
/// Margin mode
/// </summary>
public SharedMarginMode? MarginMode { get; set; }
/// <summary>
/// Take profit / Stop loss side
/// </summary>
public SharedTpSlSide TpSlSide { get; set; }
/// <summary>
/// Quantity to close. Only used for some API's which require a quantity in the order. Most API's will close the full position
/// </summary>
public decimal? Quantity { get; set; }
/// <summary>
/// Trigger price
/// </summary>
public decimal TriggerPrice { get; set; }
/// <summary>
/// ctor
/// </summary>
/// <param name="symbol">Symbol of the order</param>
/// <param name="positionSide">Position side</param>
/// <param name="tpSlSide">Take Profit / Stop Loss side</param>
/// <param name="triggerPrice">Trigger price</param>
/// <param name="exchangeParameters">Exchange specific parameters</param>
public SetTpSlRequest(SharedSymbol symbol, SharedPositionSide positionSide, SharedTpSlSide tpSlSide, decimal triggerPrice, ExchangeParameters? exchangeParameters = null)
: base(symbol, exchangeParameters)
{
PositionSide = positionSide;
TpSlSide = tpSlSide;
Symbol = symbol;
TriggerPrice = triggerPrice;
}
}
}
@@ -11,19 +11,19 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// Supported quantity notations for buy limit orders
/// </summary>
public SharedQuantityType BuyLimit { get; }
public SharedQuantityType BuyLimit { get; set; }
/// <summary>
/// Supported quantity notations for sell limit orders
/// </summary>
public SharedQuantityType SellLimit { get; }
public SharedQuantityType SellLimit { get; set; }
/// <summary>
/// Supported quantity notations for buy market orders
/// </summary>
public SharedQuantityType BuyMarket { get; }
public SharedQuantityType BuyMarket { get; set; }
/// <summary>
/// Supported quantity notations for sell market orders
/// </summary>
public SharedQuantityType SellMarket { get; }
public SharedQuantityType SellMarket { get; set; }
/// <summary>
/// ctor
@@ -36,7 +36,13 @@ namespace CryptoExchange.Net.SharedApis
SellMarket = sellMarket;
}
private SharedQuantityType GetSupportedQuantityType(SharedOrderSide side, SharedOrderType orderType)
/// <summary>
/// Get the supported quantity type for a specific order configuration
/// </summary>
/// <param name="side">Side of the order</param>
/// <param name="orderType">Type of the order</param>
/// <returns>The supported quantity type</returns>
public SharedQuantityType GetSupportedQuantityType(SharedOrderSide side, SharedOrderType orderType)
{
if (side == SharedOrderSide.Buy && (orderType == SharedOrderType.Limit || orderType == SharedOrderType.LimitMaker)) return BuyLimit;
if (side == SharedOrderSide.Buy && orderType == SharedOrderType.Market) return BuyMarket;
@@ -46,25 +52,45 @@ namespace CryptoExchange.Net.SharedApis
throw new ArgumentException("Unknown side/type combination");
}
/// <summary>
/// Get whether the API supports a specific quantity type for an order configuration
/// </summary>
/// <param name="side">Side of the order</param>
/// <param name="orderType">Type of the order</param>
/// <param name="quantityType">Type of quantity</param>
/// <returns>True if supported, false if not</returns>
public bool IsSupported(SharedOrderSide side, SharedOrderType orderType, SharedQuantityType quantityType)
{
var supportedType = GetSupportedQuantityType(side, orderType);
if (supportedType == quantityType)
return true;
if (supportedType == SharedQuantityType.BaseAndQuoteAsset && (quantityType == SharedQuantityType.BaseAsset || quantityType == SharedQuantityType.QuoteAsset))
return true;
return false;
}
/// <summary>
/// Validate a request
/// </summary>
/// <param name="side"></param>
/// <param name="type"></param>
/// <param name="quantity"></param>
/// <param name="quoteQuantity"></param>
/// <returns></returns>
public Error? Validate(SharedOrderSide side, SharedOrderType type, decimal? quantity, decimal? quoteQuantity)
public Error? Validate(SharedOrderSide side, SharedOrderType type, SharedQuantity? quantity)
{
var supportedType = GetSupportedQuantityType(side, type);
if (supportedType == SharedQuantityType.BaseAndQuoteAsset)
return null;
if ((supportedType == SharedQuantityType.BaseAsset || supportedType == SharedQuantityType.Contracts) && quoteQuantity != null)
return new ArgumentError($"Quote quantity not supported for {side}.{type} order, specify Quantity instead");
if (supportedType == SharedQuantityType.BaseAndQuoteAsset && quantity != null && quantity.QuantityInBaseAsset == null && quantity.QuantityInQuoteAsset == null)
return new ArgumentError($"Quantity for {side}.{type} required in base or quote asset");
if (supportedType == SharedQuantityType.QuoteAsset && quantity != null)
return new ArgumentError($"Quantity not supported for {side}.{type} order, specify QuoteQuantity instead");
if (supportedType == SharedQuantityType.QuoteAsset && quantity != null && quantity.QuantityInQuoteAsset == null)
return new ArgumentError($"Quantity for {side}.{type} required in quote asset");
if (supportedType == SharedQuantityType.BaseAsset && quantity != null && quantity.QuantityInBaseAsset == null && quantity.QuantityInContracts == null)
return new ArgumentError($"Quantity for {side}.{type} required in base asset");
if (supportedType == SharedQuantityType.Contracts && quantity != null && quantity.QuantityInContracts == null)
return new ArgumentError($"Quantity for {side}.{type} required in contracts");
return null;
}