diff --git a/CryptoExchange.Net.UnitTests/TestImplementations/TestBaseClient.cs b/CryptoExchange.Net.UnitTests/TestImplementations/TestBaseClient.cs index d9e2e91..df87c64 100644 --- a/CryptoExchange.Net.UnitTests/TestImplementations/TestBaseClient.cs +++ b/CryptoExchange.Net.UnitTests/TestImplementations/TestBaseClient.cs @@ -9,11 +9,11 @@ namespace CryptoExchange.Net.UnitTests { public class TestBaseClient: BaseClient { - public TestBaseClient(): base(new RestClientOptions("http://testurl.url"), null) + public TestBaseClient(): base("Test", new RestClientOptions("http://testurl.url"), null) { } - public TestBaseClient(RestClientOptions exchangeOptions) : base(exchangeOptions, exchangeOptions.ApiCredentials == null ? null : new TestAuthProvider(exchangeOptions.ApiCredentials)) + public TestBaseClient(RestClientOptions exchangeOptions) : base("Test", exchangeOptions, exchangeOptions.ApiCredentials == null ? null : new TestAuthProvider(exchangeOptions.ApiCredentials)) { } diff --git a/CryptoExchange.Net.UnitTests/TestImplementations/TestRestClient.cs b/CryptoExchange.Net.UnitTests/TestImplementations/TestRestClient.cs index 7189508..f38aa0c 100644 --- a/CryptoExchange.Net.UnitTests/TestImplementations/TestRestClient.cs +++ b/CryptoExchange.Net.UnitTests/TestImplementations/TestRestClient.cs @@ -16,12 +16,12 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations { public class TestRestClient: RestClient { - public TestRestClient() : base(new RestClientOptions("http://testurl.url"), null) + public TestRestClient() : base("Test", new RestClientOptions("http://testurl.url"), null) { RequestFactory = new Mock().Object; } - public TestRestClient(RestClientOptions exchangeOptions) : base(exchangeOptions, exchangeOptions.ApiCredentials == null ? null : new TestAuthProvider(exchangeOptions.ApiCredentials)) + public TestRestClient(RestClientOptions exchangeOptions) : base("Test", exchangeOptions, exchangeOptions.ApiCredentials == null ? null : new TestAuthProvider(exchangeOptions.ApiCredentials)) { RequestFactory = new Mock().Object; } diff --git a/CryptoExchange.Net.UnitTests/TestImplementations/TestSocketClient.cs b/CryptoExchange.Net.UnitTests/TestImplementations/TestSocketClient.cs index 43f18b9..886d948 100644 --- a/CryptoExchange.Net.UnitTests/TestImplementations/TestSocketClient.cs +++ b/CryptoExchange.Net.UnitTests/TestImplementations/TestSocketClient.cs @@ -15,7 +15,7 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations { } - public TestSocketClient(SocketClientOptions exchangeOptions) : base(exchangeOptions, exchangeOptions.ApiCredentials == null ? null : new TestAuthProvider(exchangeOptions.ApiCredentials)) + public TestSocketClient(SocketClientOptions exchangeOptions) : base("test", exchangeOptions, exchangeOptions.ApiCredentials == null ? null : new TestAuthProvider(exchangeOptions.ApiCredentials)) { SocketFactory = new Mock().Object; Mock.Get(SocketFactory).Setup(f => f.CreateWebsocket(It.IsAny(), It.IsAny())).Returns(new TestSocket()); diff --git a/CryptoExchange.Net/BaseClient.cs b/CryptoExchange.Net/BaseClient.cs index 43b9a96..a6749f1 100644 --- a/CryptoExchange.Net/BaseClient.cs +++ b/CryptoExchange.Net/BaseClient.cs @@ -25,6 +25,10 @@ namespace CryptoExchange.Net /// public string BaseAddress { get; } /// + /// The name of the client + /// + public string ClientName { get; } + /// /// The log object /// protected internal Log log; @@ -64,15 +68,17 @@ namespace CryptoExchange.Net /// /// ctor /// + /// /// /// - protected BaseClient(ClientOptions options, AuthenticationProvider? authenticationProvider) + protected BaseClient(string clientName, ClientOptions options, AuthenticationProvider? authenticationProvider) { - log = new Log(); + log = new Log(clientName); authProvider = authenticationProvider; log.UpdateWriters(options.LogWriters); log.Level = options.LogVerbosity; + ClientName = clientName; BaseAddress = options.BaseAddress; apiProxy = options.Proxy; diff --git a/CryptoExchange.Net/CryptoExchange.Net.csproj b/CryptoExchange.Net/CryptoExchange.Net.csproj index e2f8876..ecfb92c 100644 --- a/CryptoExchange.Net/CryptoExchange.Net.csproj +++ b/CryptoExchange.Net/CryptoExchange.Net.csproj @@ -1,4 +1,4 @@ - + netstandard2.0;netstandard2.1 @@ -21,7 +21,7 @@ CryptoExchange.Net.xml - + \ No newline at end of file diff --git a/CryptoExchange.Net/CryptoExchange.Net.xml b/CryptoExchange.Net/CryptoExchange.Net.xml index 8f96bdc..f67ab25 100644 --- a/CryptoExchange.Net/CryptoExchange.Net.xml +++ b/CryptoExchange.Net/CryptoExchange.Net.xml @@ -209,6 +209,11 @@ The address of the client + + + The name of the client + + The log object @@ -244,10 +249,11 @@ Last is used - + ctor + @@ -435,6 +441,105 @@ + + + Shared interface for exchange wrappers based on the CryptoExchange.Net package + + + + + Get the symbol name basset on a base and quote asset + + + + + + + + Get a list of symbols for the exchange + + + + + + Get a list of tickers for the exchange + + + + + + Get a list of candles for a given symbol on the exchange + + The symbol to retrieve the candles for + The timespan to retrieve the candles for. The supported value are dependent on the exchange + + + + + Get the order book for a symbol + + The symbol to get the book for + + + + + The recent trades for a symbol + + The symbol to get the trades for + + + + + Place an order + + The symbol the order is for + The side of the order + The type of the order + The quantity of the order + The price of the order, only for limit orders + [Optional] The account id to place the order on, required for some exchanges + The id of the resulting order + + + + Get an order by id + + The id + [Optional] The symbol the order is on, required for some exchanges + + + + + Get trades for an order by id + + The id + [Optional] The symbol the order is on, required for some exchanges + + + + + Get a list of open orders + + [Optional] The symbol to get open orders for, required for some exchanges. If the symbol is not required for the call, + the result will NOT be filtered by this + + + + + Get a list of closed orders + + [Optional] The symbol to get closed orders for, required for some exchanges. If the symbol is not required for the call, + the result will NOT be filtered by this + + + + + Cancel an order by id + + The id + [Optional] The symbol the order is on, required for some exchanges + + Helper methods @@ -712,6 +817,11 @@ The base address of the API + + + Client name + + Adds a rate limiter to the client. There are 2 choices, the and the . @@ -1065,7 +1175,12 @@ The verbosity of the logging - + + + Client name + + + ctor @@ -1304,6 +1419,15 @@ + + + Create an error result + + + + + + The result of a request @@ -2259,10 +2383,11 @@ Total requests made - + ctor + @@ -2423,10 +2548,11 @@ If false; data which is a response to a query won't get forwarded to subscriptions as well - + Create a socket client + Client name Client options Authentication provider @@ -3061,5 +3187,148 @@ + + + Specifies that is allowed as an input even if the + corresponding type disallows it. + + + + + Initializes a new instance of the class. + + + + + Specifies that is disallowed as an input even if the + corresponding type allows it. + + + + + Initializes a new instance of the class. + + + + + Specifies that a method that will never return under any circumstance. + + + + + Initializes a new instance of the class. + + + + + Specifies that the method will not return if the associated + parameter is passed the specified value. + + + + + Gets the condition parameter value. + Code after the method is considered unreachable by diagnostics if the argument + to the associated parameter matches this value. + + + + + Initializes a new instance of the + class with the specified parameter value. + + + The condition parameter value. + Code after the method is considered unreachable by diagnostics if the argument + to the associated parameter matches this value. + + + + + Specifies that an output may be even if the + corresponding type disallows it. + + + + + Initializes a new instance of the class. + + + + + Specifies that when a method returns , + the parameter may be even if the corresponding type disallows it. + + + + + Gets the return value condition. + If the method returns this value, the associated parameter may be . + + + + + Initializes the attribute with the specified return value condition. + + + The return value condition. + If the method returns this value, the associated parameter may be . + + + + + Specifies that an output is not even if the + corresponding type allows it. + + + + + Initializes a new instance of the class. + + + + + Specifies that the output will be non- if the + named parameter is non-. + + + + + Gets the associated parameter name. + The output will be non- if the argument to the + parameter specified is non-. + + + + + Initializes the attribute with the associated parameter name. + + + The associated parameter name. + The output will be non- if the argument to the + parameter specified is non-. + + + + + Specifies that when a method returns , + the parameter will not be even if the corresponding type allows it. + + + + + Gets the return value condition. + If the method returns this value, the associated parameter will not be . + + + + + Initializes the attribute with the specified return value condition. + + + The return value condition. + If the method returns this value, the associated parameter will not be . + + diff --git a/CryptoExchange.Net/ExchangeInterfaces/ICommonTrade.cs b/CryptoExchange.Net/ExchangeInterfaces/ICommonTrade.cs new file mode 100644 index 0000000..b1a424f --- /dev/null +++ b/CryptoExchange.Net/ExchangeInterfaces/ICommonTrade.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CryptoExchange.Net.ExchangeInterfaces +{ + /// + /// Common trade + /// + public interface ICommonTrade + { + /// + /// Id of the trade + /// + public string CommonId { get; } + /// + /// Price of the trade + /// + public decimal CommonPrice { get; } + /// + /// Quantity of the trade + /// + public decimal CommonQuantity { get; } + /// + /// Fee paid for the trade + /// + public decimal CommonFee { get; } + /// + /// The asset fee was paid in + /// + public string? CommonFeeAsset { get; } + } +} diff --git a/CryptoExchange.Net/ExchangeInterfaces/IExchangeClient.cs b/CryptoExchange.Net/ExchangeInterfaces/IExchangeClient.cs new file mode 100644 index 0000000..145f069 --- /dev/null +++ b/CryptoExchange.Net/ExchangeInterfaces/IExchangeClient.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using CryptoExchange.Net.Objects; + +namespace CryptoExchange.Net.ExchangeInterfaces +{ + /// + /// Shared interface for exchange wrappers based on the CryptoExchange.Net package + /// + public interface IExchangeClient + { + /// + /// Get the symbol name based on a base and quote asset + /// + /// + /// + /// + string GetSymbolName(string baseAsset, string quoteAsset); + + /// + /// Get a list of symbols for the exchange + /// + /// + Task>> GetSymbolsAsync(); + + /// + /// Get a list of tickers for the exchange + /// + /// + Task>> GetTickersAsync(); + + /// + /// Get a list of candles for a given symbol on the exchange + /// + /// The symbol to retrieve the candles for + /// The timespan to retrieve the candles for. The supported value are dependent on the exchange + /// + Task>> GetKlinesAsync(string symbol, TimeSpan timespan); + /// + /// Get the order book for a symbol + /// + /// The symbol to get the book for + /// + Task> GetOrderBookAsync(string symbol); + /// + /// The recent trades for a symbol + /// + /// The symbol to get the trades for + /// + Task>> GetRecentTradesAsync(string symbol); + + /// + /// Place an order + /// + /// The symbol the order is for + /// The side of the order + /// The type of the order + /// The quantity of the order + /// The price of the order, only for limit orders + /// [Optional] The account id to place the order on, required for some exchanges, ignored otherwise + /// The id of the resulting order + Task> PlaceOrderAsync(string symbol, OrderSide side, OrderType type, decimal quantity, decimal? price = null, string? accountId = null); + /// + /// Get an order by id + /// + /// The id + /// [Optional] The symbol the order is on, required for some exchanges, ignored otherwise + /// + Task> GetOrderAsync(string orderId, string? symbol = null); + /// + /// Get trades for an order by id + /// + /// The id + /// [Optional] The symbol the order is on, required for some exchanges, ignored otherwise + /// + Task>> GetTradesAsync(string orderId, string? symbol = null); + /// + /// Get a list of open orders + /// + /// [Optional] The symbol to get open orders for, required for some exchanges, ignored otherwise + /// + Task>> GetOpenOrdersAsync(string? symbol); + + /// + /// Get a list of closed orders + /// + /// [Optional] The symbol to get closed orders for, required for some exchanges, ignored otherwise + /// + Task>> GetClosedOrdersAsync(string? symbol); + /// + /// Cancel an order by id + /// + /// The id + /// [Optional] The symbol the order is on, required for some exchanges, ignored otherwise + /// + Task> CancelOrderAsync(string orderId, string? symbol); + + /// + /// Common order id + /// + public enum OrderType + { + /// + /// Limit type + /// + Limit, + /// + /// Market type + /// + Market, + /// + /// Other order type + /// + Other + } + + /// + /// Common order side + /// + public enum OrderSide + { + /// + /// Buy order + /// + Buy, + /// + /// Sell order + /// + Sell + } + } +} diff --git a/CryptoExchange.Net/ExchangeInterfaces/IKline.cs b/CryptoExchange.Net/ExchangeInterfaces/IKline.cs new file mode 100644 index 0000000..7fd74fc --- /dev/null +++ b/CryptoExchange.Net/ExchangeInterfaces/IKline.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CryptoExchange.Net.ExchangeInterfaces +{ + /// + /// Common kline + /// + public interface ICommonKline + { + /// + /// High price for this kline + /// + decimal CommonHigh { get; } + /// + /// Low price for this kline + /// + decimal CommonLow { get; } + /// + /// Open price for this kline + /// + decimal CommonOpen { get; } + /// + /// Close price for this kline + /// + decimal CommonClose { get; } + } +} diff --git a/CryptoExchange.Net/ExchangeInterfaces/IOrder.cs b/CryptoExchange.Net/ExchangeInterfaces/IOrder.cs new file mode 100644 index 0000000..cd93a5f --- /dev/null +++ b/CryptoExchange.Net/ExchangeInterfaces/IOrder.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CryptoExchange.Net.ExchangeInterfaces +{ + /// + /// Common order + /// + public interface ICommonOrder: ICommonOrderId + { + /// + /// Symbol of the order + /// + public string CommonSymbol { get; } + /// + /// Price of the order + /// + public decimal CommonPrice { get; } + /// + /// Quantity of the order + /// + public decimal CommonQuantity { get; } + /// + /// Status of the order + /// + public string CommonStatus { get; } + /// + /// Whether the order is active + /// + public bool IsActive { get; } + /// + /// Side of the order + /// + public IExchangeClient.OrderSide CommonSide { get; } + /// + /// Type of the order + /// + public IExchangeClient.OrderType CommonType { get; } + } +} diff --git a/CryptoExchange.Net/ExchangeInterfaces/IOrderBook.cs b/CryptoExchange.Net/ExchangeInterfaces/IOrderBook.cs new file mode 100644 index 0000000..d614110 --- /dev/null +++ b/CryptoExchange.Net/ExchangeInterfaces/IOrderBook.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using CryptoExchange.Net.Interfaces; + +namespace CryptoExchange.Net.ExchangeInterfaces +{ + /// + /// Common order book + /// + public interface ICommonOrderBook + { + /// + /// Bids + /// + IEnumerable CommonBids { get; } + /// + /// Asks + /// + IEnumerable CommonAsks { get; } + } +} diff --git a/CryptoExchange.Net/ExchangeInterfaces/IPlacedOrder.cs b/CryptoExchange.Net/ExchangeInterfaces/IPlacedOrder.cs new file mode 100644 index 0000000..0e66649 --- /dev/null +++ b/CryptoExchange.Net/ExchangeInterfaces/IPlacedOrder.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CryptoExchange.Net.ExchangeInterfaces +{ + /// + /// Common order id + /// + public interface ICommonOrderId + { + /// + /// Id of the order + /// + public string CommonId { get; } + } +} diff --git a/CryptoExchange.Net/ExchangeInterfaces/IRecentTrade.cs b/CryptoExchange.Net/ExchangeInterfaces/IRecentTrade.cs new file mode 100644 index 0000000..f9b59e4 --- /dev/null +++ b/CryptoExchange.Net/ExchangeInterfaces/IRecentTrade.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CryptoExchange.Net.ExchangeInterfaces +{ + /// + /// Recent trade + /// + public interface ICommonRecentTrade + { + /// + /// Price of the trade + /// + decimal CommonPrice { get; } + /// + /// Quantity of the trade + /// + decimal CommonQuantity { get; } + /// + /// Trade time + /// + DateTime CommonTradeTime { get; } + } +} diff --git a/CryptoExchange.Net/ExchangeInterfaces/ISymbol.cs b/CryptoExchange.Net/ExchangeInterfaces/ISymbol.cs new file mode 100644 index 0000000..2ff59a5 --- /dev/null +++ b/CryptoExchange.Net/ExchangeInterfaces/ISymbol.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CryptoExchange.Net.ExchangeInterfaces +{ + /// + /// Common symbol + /// + public interface ICommonSymbol + { + /// + /// Symbol name + /// + public string CommonName { get; } + /// + /// Minimum trade size + /// + public decimal CommonMinimumTradeSize { get; } + } +} diff --git a/CryptoExchange.Net/ExchangeInterfaces/ITicker.cs b/CryptoExchange.Net/ExchangeInterfaces/ITicker.cs new file mode 100644 index 0000000..53979b5 --- /dev/null +++ b/CryptoExchange.Net/ExchangeInterfaces/ITicker.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CryptoExchange.Net.ExchangeInterfaces +{ + /// + /// Common ticker + /// + public interface ICommonTicker + { + /// + /// Symbol name + /// + public string CommonSymbol { get; } + /// + /// High price + /// + public decimal CommonHigh { get; } + /// + /// Low price + /// + public decimal CommonLow { get; } + /// + /// Volume + /// + public decimal CommonVolume { get; } + } +} diff --git a/CryptoExchange.Net/Interfaces/IRestClient.cs b/CryptoExchange.Net/Interfaces/IRestClient.cs index 60d9e8b..f46f430 100644 --- a/CryptoExchange.Net/Interfaces/IRestClient.cs +++ b/CryptoExchange.Net/Interfaces/IRestClient.cs @@ -37,6 +37,11 @@ namespace CryptoExchange.Net.Interfaces /// string BaseAddress { get; } + /// + /// Client name + /// + string ClientName { get; } + /// /// Adds a rate limiter to the client. There are 2 choices, the and the . /// diff --git a/CryptoExchange.Net/Logging/Log.cs b/CryptoExchange.Net/Logging/Log.cs index d04189b..f37458a 100644 --- a/CryptoExchange.Net/Logging/Log.cs +++ b/CryptoExchange.Net/Logging/Log.cs @@ -17,11 +17,17 @@ namespace CryptoExchange.Net.Logging /// public LogVerbosity Level { get; set; } = LogVerbosity.Info; + /// + /// Client name + /// + public string ClientName { get; set; } + /// /// ctor /// - public Log() + public Log(string clientName) { + ClientName = clientName; writers = new List(); } @@ -44,7 +50,7 @@ namespace CryptoExchange.Net.Logging if ((int)logType < (int)Level) return; - var logMessage = $"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | {logType} | {message}"; + var logMessage = $"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | {ClientName.PadRight(10)} | {logType} | {message}"; foreach (var writer in writers.ToList()) { try diff --git a/CryptoExchange.Net/Objects/CallResult.cs b/CryptoExchange.Net/Objects/CallResult.cs index 6a13492..23409e5 100644 --- a/CryptoExchange.Net/Objects/CallResult.cs +++ b/CryptoExchange.Net/Objects/CallResult.cs @@ -154,6 +154,18 @@ namespace CryptoExchange.Net.Objects { return new WebCallResult(code, responseHeaders, error); } + + /// + /// Create an error result + /// + /// + /// + /// + /// + public static WebCallResult CreateErrorResult(WebCallResult result) + { + return new WebCallResult(result.ResponseStatusCode, result.ResponseHeaders, result.Error); + } } /// @@ -187,6 +199,17 @@ namespace CryptoExchange.Net.Objects ResponseHeaders = responseHeaders; } + public WebCallResult(WebCallResult callResult): base(callResult.Data, callResult.Error) + { + ResponseHeaders = callResult.ResponseHeaders; + ResponseStatusCode = callResult.ResponseStatusCode; + } + + public static WebCallResult CreateFrom(WebCallResult source) where Y : T + { + return new WebCallResult(source.ResponseStatusCode, source.ResponseHeaders, (T)source.Data, source.Error); + } + /// /// Create an error result /// diff --git a/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs b/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs index 729c68c..4cf138c 100644 --- a/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs +++ b/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs @@ -208,7 +208,7 @@ namespace CryptoExchange.Net.OrderBook asks = new SortedList(); bids = new SortedList(new DescComparer()); - log = new Log { Level = options.LogVerbosity }; + log = new Log(options.OrderBookName) { Level = options.LogVerbosity }; var writers = options.LogWriters ?? new List { new DebugTextWriter() }; log.UpdateWriters(writers.ToList()); } diff --git a/CryptoExchange.Net/RestClient.cs b/CryptoExchange.Net/RestClient.cs index b31d7b8..d3372fa 100644 --- a/CryptoExchange.Net/RestClient.cs +++ b/CryptoExchange.Net/RestClient.cs @@ -76,9 +76,10 @@ namespace CryptoExchange.Net /// /// ctor /// + /// /// /// - protected RestClient(RestClientOptions exchangeOptions, AuthenticationProvider? authenticationProvider) : base(exchangeOptions, authenticationProvider) + protected RestClient(string clientName, RestClientOptions exchangeOptions, AuthenticationProvider? authenticationProvider) : base(clientName, exchangeOptions, authenticationProvider) { if (exchangeOptions == null) throw new ArgumentNullException(nameof(exchangeOptions)); diff --git a/CryptoExchange.Net/SocketClient.cs b/CryptoExchange.Net/SocketClient.cs index 8475263..0332595 100644 --- a/CryptoExchange.Net/SocketClient.cs +++ b/CryptoExchange.Net/SocketClient.cs @@ -83,9 +83,10 @@ namespace CryptoExchange.Net /// /// Create a socket client /// + /// Client name /// Client options /// Authentication provider - protected SocketClient(SocketClientOptions exchangeOptions, AuthenticationProvider? authenticationProvider): base(exchangeOptions, authenticationProvider) + protected SocketClient(string clientName, SocketClientOptions exchangeOptions, AuthenticationProvider? authenticationProvider): base(clientName, exchangeOptions, authenticationProvider) { if (exchangeOptions == null) throw new ArgumentNullException(nameof(exchangeOptions));