diff --git a/CHANGELOG.md b/CHANGELOG.md index 579a427d..7aebca51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,15 @@ ## Change Log & Release Notes +* * Version 5.4.1010 - 09 Oct 2024 + * Updated WebSocket API endpoints and models to [2024-10-04](https://www.okx.com/docs-v5/log_en/#2024-10-04) version + * Moved Status methods to Public Client + * Moved Announcement methods to Public Client + * Removed Market alias for Public Client + * Added WS / Fills channel + * Refactoring and performance improvements + * Version 5.4.1009 - 09 Oct 2024 - * Updated endpoints and models to [2024-10-04](https://www.okx.com/docs-v5/log_en/#2024-10-04) version + * Updated Rest API endpoints and models to [2024-10-04](https://www.okx.com/docs-v5/log_en/#2024-10-04) version * Version 5.4.1001 - 01 Oct 2024 * ApiSharp 3.0.0 Update diff --git a/OKX.Api.Examples/Program.cs b/OKX.Api.Examples/Program.cs index 47df48ca..6baf0363 100644 --- a/OKX.Api.Examples/Program.cs +++ b/OKX.Api.Examples/Program.cs @@ -3,6 +3,19 @@ internal class Program { static async Task Main(string[] args) + { + var ws = new OKXWebSocketApiClient(); + ws.SetApiCredentials("ab69cb97-05fb-430f-b880-49940796f86e", "BCD93B0F1C9BCB9C2BE3AD3E2BE53DCE", "bo1144167AZ*"); + + await ws.Account.SubscribeToAccountUpdatesAsync(data => + { + Console.WriteLine($"Account Balance: {data}"); + }, "BTC", 100); + + Console.WriteLine("Press any key to exit..."); + Console.ReadKey(); + } + static async Task Main2(string[] args) { #region Rest Api Client Examples var api = new OkxRestApiClient(); @@ -66,13 +79,9 @@ static async Task Main(string[] args) var public_40 = await api.Public.GetExchangeRateAsync(); var public_41 = await api.Public.GetIndexComponentsAsync("BTC-USDT"); var public_42 = await api.Public.GetEconomicCalendarDataAsync("BTC-USDT"); - - // Status Methods (Unsigned) - var system_01 = await api.Status.GetSystemUpgradeStatusAsync(); - - // Announcement Methods - var announcement_01 = await api.Announcement.GetAnnouncementTypesAsync(); // (Unsigned) - var announcement_02 = await api.Announcement.GetAnnouncementsAsync(); // (Signed) + var public_43 = await api.Public.GetAnnouncementTypesAsync(); + var public_44 = await api.Public.GetAnnouncementsAsync(); + var public_45 = await api.Public.GetSystemUpgradeStatusAsync(); // Trading Account Methods (Signed) var account_01 = await api.Account.GetInstrumentsAsync(OkxInstrumentType.Spot); @@ -578,7 +587,10 @@ await ws.Trade.SubscribeToOrderUpdatesAsync((data) => { // ... Your logic here }, OkxInstrumentType.Futures, "INSTRUMENT-FAMILY", "INSTRUMENT-ID"); - + await ws.Trade.SubscribeToFillsAsync((data) => + { + // ... Your logic here + }); await ws.Trade.PlaceOrderAsync(new OkxTradeOrderPlaceRequest()); await ws.Trade.PlaceOrdersAsync(new List()); await ws.Trade.CancelOrderAsync(new OkxTradeOrderCancelRequest()); diff --git a/OKX.Api/Account/Clients/OkxAccountSocketClient.cs b/OKX.Api/Account/Clients/OkxAccountSocketClient.cs index 46bf0cbd..a4a0a9d9 100644 --- a/OKX.Api/Account/Clients/OkxAccountSocketClient.cs +++ b/OKX.Api/Account/Clients/OkxAccountSocketClient.cs @@ -6,15 +6,23 @@ public class OkxAccountSocketClient(OKXWebSocketApiClient root) { // Internal - internal OKXWebSocketApiClient Root { get; } = root; + internal OKXWebSocketApiClient _ { get; } = root; /// /// Retrieve account information. Data will be pushed when triggered by events such as placing/canceling order, and will also be pushed in regular interval according to subscription granularity. /// /// On Data Handler + /// Currency + /// 0: only push due to account events + /// The data will be pushed both by events and regularly if this field is omitted or set to other values than 0. + /// The following format should be strictly obeyed when using this field. /// Cancellation Token /// - public async Task> SubscribeToAccountUpdatesAsync(Action onData, CancellationToken ct = default) + public async Task> SubscribeToAccountUpdatesAsync( + Action onData, + string? currency = null, + int? updateInterval = null, + CancellationToken ct = default) { var internalHandler = new Action>>>(data => { @@ -24,9 +32,14 @@ public async Task> SubscribeToAccountUpd var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, new OkxSocketRequestArgument { - Channel = "account" + Channel = "account", + Currency = currency, + ExtraParameters = updateInterval.HasValue + ? new Dictionary { { "interval", updateInterval.Value.ToOkxString() } } + : null }); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Private, request, null, true, internalHandler, ct).ConfigureAwait(false); + + return await _.RootSubscribeAsync(OkxSocketEndpoint.Private, request, null, true, internalHandler, ct).ConfigureAwait(false); } /// @@ -65,7 +78,7 @@ public async Task> SubscribeToPositionUp InstrumentFamily = symbol.InstrumentFamily, }); var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Private, request, null, true, internalHandler, ct).ConfigureAwait(false); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Private, request, null, true, internalHandler, ct).ConfigureAwait(false); } /// @@ -86,7 +99,7 @@ public async Task> SubscribeToBalanceAnd { Channel = "balance_and_position" }); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Private, request, null, true, internalHandler, ct).ConfigureAwait(false); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Private, request, null, true, internalHandler, ct).ConfigureAwait(false); } /// @@ -113,7 +126,7 @@ public async Task> SubscribeToPositionRi Channel = "liquidation-warning", InstrumentType = instrumentType }); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Private, request, null, true, internalHandler, ct).ConfigureAwait(false); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Private, request, null, true, internalHandler, ct).ConfigureAwait(false); } /// @@ -135,6 +148,6 @@ public async Task> SubscribeToAccountGre { Channel = "account-greeks", }); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Private, request, null, true, internalHandler, ct).ConfigureAwait(false); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Private, request, null, true, internalHandler, ct).ConfigureAwait(false); } } \ No newline at end of file diff --git a/OKX.Api/Algo/Clients/OkxAlgoSocketClient.cs b/OKX.Api/Algo/Clients/OkxAlgoSocketClient.cs index 1f2eef08..ba1da723 100644 --- a/OKX.Api/Algo/Clients/OkxAlgoSocketClient.cs +++ b/OKX.Api/Algo/Clients/OkxAlgoSocketClient.cs @@ -6,7 +6,7 @@ public class OkxAlgoSocketClient(OKXWebSocketApiClient root) { // Internal - internal OKXWebSocketApiClient Root { get; } = root; + internal OKXWebSocketApiClient _ { get; } = root; /// /// Retrieve algo orders (includes trigger order, oco order, conditional order). Data will not be pushed when first subscribed. Data will only be pushed when triggered by events such as placing/canceling order. @@ -52,7 +52,7 @@ public async Task> SubscribeToAlgoOrderU InstrumentFamily = symbol.InstrumentFamily, }); var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Business, request, null, true, internalHandler, ct).ConfigureAwait(false); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Business, request, null, true, internalHandler, ct).ConfigureAwait(false); } @@ -100,7 +100,7 @@ public async Task> SubscribeToAdvanceAlg InstrumentFamily = symbol.InstrumentFamily, }); var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Business, request, null, true, internalHandler, ct).ConfigureAwait(false); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Business, request, null, true, internalHandler, ct).ConfigureAwait(false); } } \ No newline at end of file diff --git a/OKX.Api/Announcement/Clients/OkxAnnouncementRestClient.cs b/OKX.Api/Announcement/Clients/OkxAnnouncementRestClient.cs deleted file mode 100644 index 96535d6c..00000000 --- a/OKX.Api/Announcement/Clients/OkxAnnouncementRestClient.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace OKX.Api.Announcement; - -/// -/// OKX Rest Api Announcement Client -/// -public class OkxAnnouncementRestClient(OkxRestApiClient root) : OkxBaseRestClient(root) -{ - // Endpoints - private const string v5SupportAnnouncements = "api/v5/support/announcements"; - private const string v5SupportAnnouncementTypes = "api/v5/support/announcement-types"; - - /// - /// Authentication is required for this private endpoint. - /// Get announcements, the response is sorted by pTime with the most recent first. The sort will not be affected if the announcement is updated. Every page has 20 records - /// - /// Announcement type. Delivering the annType from "GET / Announcement types" - /// Returning all when it is not posted - /// Page for pagination. The default is 1 - /// Cancellation Token - /// - public Task> GetAnnouncementsAsync(string? type = null, int? page = null, CancellationToken ct = default) - { - var parameters = new ParameterCollection(); - parameters.AddOptional("annType", type); - parameters.AddOptional("page", page?.ToOkxString()); - - return ProcessOneRequestAsync(GetUri(v5SupportAnnouncements), HttpMethod.Get, ct, signed: true, queryParameters: parameters); - } - - /// - /// Authentication is not required for this public endpoint. Get announcements types - /// - /// Cancellation Token - /// - public Task>> GetAnnouncementTypesAsync(CancellationToken ct = default) - { - return ProcessListRequestAsync(GetUri(v5SupportAnnouncementTypes), HttpMethod.Get, ct, signed: false); - } -} diff --git a/OKX.Api/Base/Clients/OkxBaseRestClient.cs b/OKX.Api/Base/Clients/OkxBaseRestClient.cs index ed614b18..f8bc03fb 100644 --- a/OKX.Api/Base/Clients/OkxBaseRestClient.cs +++ b/OKX.Api/Base/Clients/OkxBaseRestClient.cs @@ -6,13 +6,13 @@ public abstract class OkxBaseRestClient : RestApiClient { internal ILogger Logger { get => _logger; } - internal OkxRestApiClient Root { get; } - internal OkxRestApiOptions Options { get { return Root.Options; } } + internal OkxRestApiClient _ { get; } + internal OkxRestApiOptions Options { get { return _.Options; } } internal TimeSyncState TimeSyncState { get; } = new("OKX RestApi"); internal OkxBaseRestClient(OkxRestApiClient root) : base(root.Logger, root.Options) { - Root = root; + _ = root; ManualParseError = true; RequestBodyFormat = RestRequestBodyFormat.Json; @@ -50,7 +50,7 @@ protected override Task TryParseErrorAsync(JToken error) return Task.FromResult(null); } /// - protected override Task> GetServerTimestampAsync() => Root.Public.GetServerTimeAsync(); + protected override Task> GetServerTimestampAsync() => _.Public.GetServerTimeAsync(); /// protected override TimeSyncInfo GetTimeSyncInfo() => new(Logger, Options.AutoTimestamp, Options.AutoTimestampInterval, TimeSyncState); /// diff --git a/OKX.Api/Base/Requests/OkxSocketRequests.cs b/OKX.Api/Base/Requests/OkxSocketRequests.cs index 80a827cb..bb91b49e 100644 --- a/OKX.Api/Base/Requests/OkxSocketRequests.cs +++ b/OKX.Api/Base/Requests/OkxSocketRequests.cs @@ -130,6 +130,18 @@ public record OkxSocketRequestArgument [JsonProperty("channel")] public string? Channel { get; set; } + /// + /// Currency + /// + [JsonProperty("ccy", NullValueHandling = NullValueHandling.Ignore)] + public string? Currency { get; set; } + + /// + /// Algo Order Id + /// + [JsonProperty("algoId", NullValueHandling = NullValueHandling.Ignore)] + public string? AlgoOrderId { get; set; } + /// /// Instrument Family /// @@ -149,10 +161,10 @@ public record OkxSocketRequestArgument public OkxInstrumentType? InstrumentType { get; set; } /// - /// Algo Order Id + /// Additional configuration /// - [JsonProperty("algoId", NullValueHandling = NullValueHandling.Ignore)] - public string? AlgoOrderId { get; set; } + [JsonProperty("extraParams", NullValueHandling = NullValueHandling.Ignore)] + public Dictionary? ExtraParameters { get; set; } } /// diff --git a/OKX.Api/Block/Clients/OkxBlockSocketClient.cs b/OKX.Api/Block/Clients/OkxBlockSocketClient.cs index b6b933ea..4c7cfe18 100644 --- a/OKX.Api/Block/Clients/OkxBlockSocketClient.cs +++ b/OKX.Api/Block/Clients/OkxBlockSocketClient.cs @@ -6,11 +6,12 @@ public class OkxBlockSocketClient(OKXWebSocketApiClient root) { // Internal - internal OKXWebSocketApiClient Root { get; } = root; + internal OKXWebSocketApiClient _ { get; } = root; // TODO: Rfqs channel // TODO: Quotes channel // TODO: Structure block trades channel + // TODO: Public structure block trades channel // TODO: Public block trades channel // TODO: Block tickers channel diff --git a/OKX.Api/CopyTrading/Clients/OkxCopyTradingSocketClient.cs b/OKX.Api/CopyTrading/Clients/OkxCopyTradingSocketClient.cs index b08e1f92..ad882d20 100644 --- a/OKX.Api/CopyTrading/Clients/OkxCopyTradingSocketClient.cs +++ b/OKX.Api/CopyTrading/Clients/OkxCopyTradingSocketClient.cs @@ -6,7 +6,7 @@ public class OkxCopyTradingSocketClient(OKXWebSocketApiClient root) { // Internal - internal OKXWebSocketApiClient Root { get; } = root; + internal OKXWebSocketApiClient _ { get; } = root; // WS / Copy trading notification channel // WS / Lead trading notification channel diff --git a/OKX.Api/Funding/Clients/OKXFundingSocketClient.cs b/OKX.Api/Funding/Clients/OKXFundingSocketClient.cs index 5b7e4a5d..4f2272cc 100644 --- a/OKX.Api/Funding/Clients/OKXFundingSocketClient.cs +++ b/OKX.Api/Funding/Clients/OKXFundingSocketClient.cs @@ -6,7 +6,7 @@ public class OkxFundingSocketClient(OKXWebSocketApiClient root) { // Internal - internal OKXWebSocketApiClient Root { get; } = root; + internal OKXWebSocketApiClient _ { get; } = root; // TODO: Deposit info channel // TODO: Withdrawal info channel diff --git a/OKX.Api/Grid/Clients/OkxGridSocketClient.cs b/OKX.Api/Grid/Clients/OkxGridSocketClient.cs index 1e869358..3a061fb8 100644 --- a/OKX.Api/Grid/Clients/OkxGridSocketClient.cs +++ b/OKX.Api/Grid/Clients/OkxGridSocketClient.cs @@ -6,7 +6,7 @@ public class OkxGridSocketClient(OKXWebSocketApiClient root) { // Internal - internal OKXWebSocketApiClient Root { get; } = root; + internal OKXWebSocketApiClient _ { get; } = root; // TODO: WS / Spot grid algo orders channel // TODO: WS / Contract grid algo orders channel diff --git a/OKX.Api/OKX.Api.csproj b/OKX.Api/OKX.Api.csproj index daf3de93..b1c31876 100644 --- a/OKX.Api/OKX.Api.csproj +++ b/OKX.Api/OKX.Api.csproj @@ -9,10 +9,10 @@ OKX.Api Burak Öner - 5.4.1009 - 5.4.1009 - 5.4.1009 - 5.4.1009 + 5.4.1010 + 5.4.1010 + 5.4.1010 + 5.4.1010 OKX V5 Api Wrapper. Up-to-date, most-complete, well-organized, well-documented, easy-to-use, multi-task and multi-thread compatible OKX Cryptocurrency Exchange Rest and Websocket Api Wrapper true OKX;OKEX;Binance;BNB;BTC;Api;Client;Rest;Web;Websocket;Socket;Wrapper;Crypto;Currency;Cryptocurrency;Exchange;Trade;Trading;Bitcoin;Spot;Margin;Futures;Derivates;Stock;Options;Swap; diff --git a/OKX.Api/OKXRestApiClient.cs b/OKX.Api/OKXRestApiClient.cs index 32864dee..510eeb92 100644 --- a/OKX.Api/OKXRestApiClient.cs +++ b/OKX.Api/OKXRestApiClient.cs @@ -55,11 +55,6 @@ public class OkxRestApiClient /// public OkxCopyTradingRestClient CopyTrading { get; } - /// - /// Alias for Public Client - /// - public OkxPublicRestClient Market { get => Public; } - /// /// Block Trading Client /// @@ -104,17 +99,6 @@ public class OkxRestApiClient /// Affiliate Client /// public OkxAffiliateRestClient Affiliate { get; } - - /// - /// Status Client - /// - public OkxStatusRestClient Status { get; } - - /// - /// Announcement Client - /// - public OkxAnnouncementRestClient Announcement { get; } - #endregion #region Constructors @@ -160,8 +144,6 @@ public OkxRestApiClient(ILogger? logger, OkxRestApiOptions options) Rubik = new OkxRubikRestClient(this); Broker = new OkxBrokerRestClient(this); Affiliate = new OkxAffiliateRestClient(this); - Status = new OkxStatusRestClient(this); - Announcement = new OkxAnnouncementRestClient(this); } #endregion @@ -200,8 +182,6 @@ public void SetApiCredentials(OkxApiCredentials credentials) Rubik.SetApiCredentials(credentials); Broker.SetApiCredentials(credentials); Affiliate.SetApiCredentials(credentials); - Status.SetApiCredentials(credentials); - Announcement.SetApiCredentials(credentials); } #endregion } diff --git a/OKX.Api/OKXWebSocketApiClient.cs b/OKX.Api/OKXWebSocketApiClient.cs index 7946b085..3f5f8e27 100644 --- a/OKX.Api/OKXWebSocketApiClient.cs +++ b/OKX.Api/OKXWebSocketApiClient.cs @@ -35,11 +35,6 @@ public class OKXWebSocketApiClient : OkxBaseSocketClient /// public OkxCopyTradingSocketClient CopyTrading { get; } - /// - /// Alias for Public Client - /// - public OkxPublicSocketClient Market { get => Public; } - /// /// Block Trading Client /// diff --git a/OKX.Api/Public/Clients/OkxPublicRestClient.cs b/OKX.Api/Public/Clients/OkxPublicRestClient.cs index d6824424..0e753eb2 100644 --- a/OKX.Api/Public/Clients/OkxPublicRestClient.cs +++ b/OKX.Api/Public/Clients/OkxPublicRestClient.cs @@ -48,9 +48,13 @@ public class OkxPublicRestClient(OkxRestApiClient root) : OkxBaseRestClient(root private const string v5MarketExchangeRate = "api/v5/market/exchange-rate"; private const string v5MarketIndexComponents = "api/v5/market/index-components"; private const string v5PublicEconomicCalendar = "api/v5/public/economic-calendar"; - + // System Endpoints private const string v5SystemStatus = "api/v5/system/status"; + + // Announcement Endpoints + private const string v5SupportAnnouncements = "api/v5/support/announcements"; + private const string v5SupportAnnouncementTypes = "api/v5/support/announcement-types"; #endregion #region Market Data Methods @@ -1060,4 +1064,54 @@ public Task>> GetEconomicCal } #endregion + #region System Status Methods + /// + /// Get event status of system upgrade. + /// Planned system maintenance that may result in short interruption (lasting less than 5 seconds) or websocket disconnection (users can immediately reconnect) will not be announced. + /// The maintenance will only be performed during times of low market volatility. + /// + /// System maintenance status + /// Cancellation Token + /// + public Task>> GetSystemUpgradeStatusAsync( + OkxPublicMaintenanceState? state = null, + CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddOptionalEnum("state", state); + + return ProcessListRequestAsync(GetUri(v5SystemStatus), HttpMethod.Get, ct, signed: false, queryParameters: parameters); + } + #endregion + + #region Announcement Methods + /// + /// Authentication is required for this private endpoint. + /// Get announcements, the response is sorted by pTime with the most recent first. The sort will not be affected if the announcement is updated. Every page has 20 records + /// + /// Announcement type. Delivering the annType from "GET / Announcement types" + /// Returning all when it is not posted + /// Page for pagination. The default is 1 + /// Cancellation Token + /// + public Task> GetAnnouncementsAsync(string? type = null, int? page = null, CancellationToken ct = default) + { + var parameters = new ParameterCollection(); + parameters.AddOptional("annType", type); + parameters.AddOptional("page", page?.ToOkxString()); + + return ProcessOneRequestAsync(GetUri(v5SupportAnnouncements), HttpMethod.Get, ct, signed: false, queryParameters: parameters); + } + + /// + /// Authentication is not required for this public endpoint. Get announcements types + /// + /// Cancellation Token + /// + public Task>> GetAnnouncementTypesAsync(CancellationToken ct = default) + { + return ProcessListRequestAsync(GetUri(v5SupportAnnouncementTypes), HttpMethod.Get, ct, signed: false); + } + #endregion + } diff --git a/OKX.Api/Public/Clients/OkxPublicSocketClient.cs b/OKX.Api/Public/Clients/OkxPublicSocketClient.cs index a4377c0f..ba9ceb6e 100644 --- a/OKX.Api/Public/Clients/OkxPublicSocketClient.cs +++ b/OKX.Api/Public/Clients/OkxPublicSocketClient.cs @@ -6,7 +6,183 @@ public class OkxPublicSocketClient(OKXWebSocketApiClient root) { // Internal - internal OKXWebSocketApiClient Root { get; } = root; + internal OKXWebSocketApiClient _ { get; } = root; + + #region Market Data + + /// + /// Retrieve the last traded price, bid price, ask price and 24-hour trading volume of instruments. Data will be pushed every 100 ms. + /// + /// On Data Handler + /// Instrument ID + /// Cancellation Token + /// + public async Task> SubscribeToTickersAsync(Action onData, string instrumentId, CancellationToken ct = default) + => await SubscribeToTickersAsync(onData, [instrumentId], ct).ConfigureAwait(false); + + /// + /// Retrieve the last traded price, bid price, ask price and 24-hour trading volume of instruments. Data will be pushed every 100 ms. + /// + /// On Data Handler + /// List of Instrument ID + /// Cancellation Token + /// + public async Task> SubscribeToTickersAsync(Action onData, IEnumerable instrumentIds, CancellationToken ct = default) + { + var internalHandler = new Action>>>(data => + { + foreach (var d in data.Data.Data) + if (d is not null) onData(d); + }); + + var arguments = new List(); + foreach (var instrumentId in instrumentIds) arguments.Add(new OkxSocketRequestArgument + { + Channel = "tickers", + InstrumentId = instrumentId, + }); + var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); + } + + /// + /// Retrieve the candlesticks data of an instrument. Data will be pushed every 500 ms. + /// + /// On Data Handler + /// Instrument ID + /// + /// Cancellation Token + /// + public async Task> SubscribeToCandlesticksAsync(Action onData, string instrumentId, OkxPeriod period, CancellationToken ct = default) + => await SubscribeToCandlesticksAsync(onData, [instrumentId], period, ct).ConfigureAwait(false); + + /// + /// Retrieve the candlesticks data of an instrument. Data will be pushed every 500 ms. + /// + /// On Data Handler + /// List of Instrument ID + /// + /// Cancellation Token + /// + public async Task> SubscribeToCandlesticksAsync(Action onData, IEnumerable instrumentIds, OkxPeriod period, CancellationToken ct = default) + { + var internalHandler = new Action>>>(data => + { + foreach (var d in data.Data.Data) + { + if (d is null) continue; + if (data.Data.Arguments is null) continue; + d.InstrumentId = data.Data.Arguments.InstrumentId; + onData(d); + } + }); + + var arguments = new List(); + foreach (var instrumentId in instrumentIds) arguments.Add(new OkxSocketRequestArgument + { + Channel = "candle" + MapConverter.GetString(period), + InstrumentId = instrumentId, + }); + var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Business, request, null, false, internalHandler, ct).ConfigureAwait(false); + } + + /// + /// Retrieve the recent trades data. Data will be pushed whenever there is a trade. + /// + /// On Data Handler + /// Instrument ID + /// Cancellation Token + /// + public async Task> SubscribeToTradesAsync(Action onData, string instrumentId, CancellationToken ct = default) + => await SubscribeToTradesAsync(onData, [instrumentId], ct).ConfigureAwait(false); + + /// + /// Retrieve the recent trades data. Data will be pushed whenever there is a trade. + /// + /// On Data Handler + /// List of Instrument ID + /// Cancellation Token + /// + public async Task> SubscribeToTradesAsync(Action onData, IEnumerable instrumentIds, CancellationToken ct = default) + { + var internalHandler = new Action>>>(data => + { + foreach (var d in data.Data.Data) + if (d is not null) onData(d); + }); + + var arguments = new List(); + foreach (var instrumentId in instrumentIds) arguments.Add(new OkxSocketRequestArgument + { + Channel = "trades", + InstrumentId = instrumentId, + }); + var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); + } + + // TODO: WS / All trades channel + + /// + /// Retrieve order book data. + /// Use books for 400 depth levels, book5 for 5 depth levels, books50-l2-tbt tick-by-tick 50 depth levels, and books-l2-tbt for tick-by-tick 400 depth levels. + /// books: 400 depth levels will be pushed in the initial full snapshot. Incremental data will be pushed every 100 ms when there is change in order book. + /// books5: 5 depth levels will be pushed every time.Data will be pushed every 200 ms when there is change in order book. + /// books50-l2-tbt: 50 depth levels will be pushed in the initial full snapshot. Incremental data will be pushed tick by tick, i.e.whenever there is change in order book. + /// books-l2-tbt: 400 depth levels will be pushed in the initial full snapshot. Incremental data will be pushed tick by tick, i.e.whenever there is change in order book. + /// + /// On Data Handler + /// Instrument ID + /// Order Book Type + /// Cancellation Token + /// + public async Task> SubscribeToOrderBookAsync(Action onData, string instrumentId, OkxOrderBookType orderBookType, CancellationToken ct = default) + => await SubscribeToOrderBookAsync(onData, [instrumentId], orderBookType, ct).ConfigureAwait(false); + + /// + /// Retrieve order book data. + /// Use books for 400 depth levels, book5 for 5 depth levels, books50-l2-tbt tick-by-tick 50 depth levels, and books-l2-tbt for tick-by-tick 400 depth levels. + /// books: 400 depth levels will be pushed in the initial full snapshot. Incremental data will be pushed every 100 ms when there is change in order book. + /// books5: 5 depth levels will be pushed every time.Data will be pushed every 200 ms when there is change in order book. + /// books50-l2-tbt: 50 depth levels will be pushed in the initial full snapshot. Incremental data will be pushed tick by tick, i.e.whenever there is change in order book. + /// books-l2-tbt: 400 depth levels will be pushed in the initial full snapshot. Incremental data will be pushed tick by tick, i.e.whenever there is change in order book. + /// + /// On Data Handler + /// List of Instrument ID + /// Order Book Type + /// Cancellation Token + /// + public async Task> SubscribeToOrderBookAsync(Action onData, IEnumerable instrumentIds, OkxOrderBookType orderBookType, CancellationToken ct = default) + { + var internalHandler = new Action>(data => + { + foreach (var d in data.Data.Data) + { + if (d is not null) + { + if (d is null) continue; + if (data.Data.Arguments is null) continue; + d.InstrumentId = data.Data.Arguments.InstrumentId; + d.Action = data.Data.Action; + onData(d); + } + } + }); + + var arguments = new List(); + foreach (var instrumentId in instrumentIds) arguments.Add(new OkxSocketRequestArgument + { + Channel = MapConverter.GetString(orderBookType), + InstrumentId = instrumentId, + }); + var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); + } + + // TODO: WS / Option trades channel + // TODO: WS / Call auction details channel + #endregion #region Public Data /// @@ -41,7 +217,7 @@ public async Task> SubscribeToInstrument InstrumentType = instrumentType, }); var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); } /// @@ -76,7 +252,7 @@ public async Task> SubscribeToOpenIntere InstrumentId = instrumentId, }); var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); } /// @@ -111,7 +287,7 @@ public async Task> SubscribeToFundingRat InstrumentId = instrumentId, }); var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); } /// @@ -146,7 +322,7 @@ public async Task> SubscribeToPriceLimit InstrumentId = instrumentId, }); var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); } /// @@ -181,7 +357,7 @@ public async Task> SubscribeToOptionSumm InstrumentFamily = instrumentFamily, }); var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); } /// @@ -222,7 +398,7 @@ public async Task> SubscribeToEstimatedP InstrumentId = symbol.InstrumentId, }); var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); } /// @@ -257,7 +433,7 @@ public async Task> SubscribeToMarkPriceA InstrumentId = instrumentId, }); var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); } /// @@ -292,7 +468,7 @@ public async Task> SubscribeToIndexTicke InstrumentId = instrumentId, }); var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); } /// @@ -334,7 +510,7 @@ public async Task> SubscribeToMarkPriceC InstrumentId = instrumentId, }); var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Business, request, null, false, internalHandler, ct).ConfigureAwait(false); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Business, request, null, false, internalHandler, ct).ConfigureAwait(false); } @@ -377,7 +553,7 @@ public async Task> SubscribeToIndexCandl InstrumentId = instrumentId, }); var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Business, request, null, false, internalHandler, ct).ConfigureAwait(false); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Business, request, null, false, internalHandler, ct).ConfigureAwait(false); } // TODO: Liquidation orders channel @@ -386,181 +562,6 @@ public async Task> SubscribeToIndexCandl #endregion - #region Market Data - - /// - /// Retrieve the last traded price, bid price, ask price and 24-hour trading volume of instruments. Data will be pushed every 100 ms. - /// - /// On Data Handler - /// Instrument ID - /// Cancellation Token - /// - public async Task> SubscribeToTickersAsync(Action onData, string instrumentId, CancellationToken ct = default) - => await SubscribeToTickersAsync(onData, [instrumentId], ct).ConfigureAwait(false); - - /// - /// Retrieve the last traded price, bid price, ask price and 24-hour trading volume of instruments. Data will be pushed every 100 ms. - /// - /// On Data Handler - /// List of Instrument ID - /// Cancellation Token - /// - public async Task> SubscribeToTickersAsync(Action onData, IEnumerable instrumentIds, CancellationToken ct = default) - { - var internalHandler = new Action>>>(data => - { - foreach (var d in data.Data.Data) - if (d is not null) onData(d); - }); - - var arguments = new List(); - foreach (var instrumentId in instrumentIds) arguments.Add(new OkxSocketRequestArgument - { - Channel = "tickers", - InstrumentId = instrumentId, - }); - var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); - } - - /// - /// Retrieve the candlesticks data of an instrument. Data will be pushed every 500 ms. - /// - /// On Data Handler - /// Instrument ID - /// - /// Cancellation Token - /// - public async Task> SubscribeToCandlesticksAsync(Action onData, string instrumentId, OkxPeriod period, CancellationToken ct = default) - => await SubscribeToCandlesticksAsync(onData, [instrumentId], period, ct).ConfigureAwait(false); - - /// - /// Retrieve the candlesticks data of an instrument. Data will be pushed every 500 ms. - /// - /// On Data Handler - /// List of Instrument ID - /// - /// Cancellation Token - /// - public async Task> SubscribeToCandlesticksAsync(Action onData, IEnumerable instrumentIds, OkxPeriod period, CancellationToken ct = default) - { - var internalHandler = new Action>>>(data => - { - foreach (var d in data.Data.Data) - { - if (d is null) continue; - if (data.Data.Arguments is null) continue; - d.InstrumentId = data.Data.Arguments.InstrumentId; - onData(d); - } - }); - - var arguments = new List(); - foreach (var instrumentId in instrumentIds) arguments.Add(new OkxSocketRequestArgument - { - Channel = "candle" + MapConverter.GetString(period), - InstrumentId = instrumentId, - }); - var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Business, request, null, false, internalHandler, ct).ConfigureAwait(false); - } - - /// - /// Retrieve the recent trades data. Data will be pushed whenever there is a trade. - /// - /// On Data Handler - /// Instrument ID - /// Cancellation Token - /// - public async Task> SubscribeToTradesAsync(Action onData, string instrumentId, CancellationToken ct = default) - => await SubscribeToTradesAsync(onData, [instrumentId], ct).ConfigureAwait(false); - - /// - /// Retrieve the recent trades data. Data will be pushed whenever there is a trade. - /// - /// On Data Handler - /// List of Instrument ID - /// Cancellation Token - /// - public async Task> SubscribeToTradesAsync(Action onData, IEnumerable instrumentIds, CancellationToken ct = default) - { - var internalHandler = new Action>>>(data => - { - foreach (var d in data.Data.Data) - if (d is not null) onData(d); - }); - - var arguments = new List(); - foreach (var instrumentId in instrumentIds) arguments.Add(new OkxSocketRequestArgument - { - Channel = "trades", - InstrumentId = instrumentId, - }); - var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); - } - - // TODO: WS / All trades channel - - /// - /// Retrieve order book data. - /// Use books for 400 depth levels, book5 for 5 depth levels, books50-l2-tbt tick-by-tick 50 depth levels, and books-l2-tbt for tick-by-tick 400 depth levels. - /// books: 400 depth levels will be pushed in the initial full snapshot. Incremental data will be pushed every 100 ms when there is change in order book. - /// books5: 5 depth levels will be pushed every time.Data will be pushed every 200 ms when there is change in order book. - /// books50-l2-tbt: 50 depth levels will be pushed in the initial full snapshot. Incremental data will be pushed tick by tick, i.e.whenever there is change in order book. - /// books-l2-tbt: 400 depth levels will be pushed in the initial full snapshot. Incremental data will be pushed tick by tick, i.e.whenever there is change in order book. - /// - /// On Data Handler - /// Instrument ID - /// Order Book Type - /// Cancellation Token - /// - public async Task> SubscribeToOrderBookAsync(Action onData, string instrumentId, OkxOrderBookType orderBookType, CancellationToken ct = default) - => await SubscribeToOrderBookAsync(onData, [instrumentId], orderBookType, ct).ConfigureAwait(false); - - /// - /// Retrieve order book data. - /// Use books for 400 depth levels, book5 for 5 depth levels, books50-l2-tbt tick-by-tick 50 depth levels, and books-l2-tbt for tick-by-tick 400 depth levels. - /// books: 400 depth levels will be pushed in the initial full snapshot. Incremental data will be pushed every 100 ms when there is change in order book. - /// books5: 5 depth levels will be pushed every time.Data will be pushed every 200 ms when there is change in order book. - /// books50-l2-tbt: 50 depth levels will be pushed in the initial full snapshot. Incremental data will be pushed tick by tick, i.e.whenever there is change in order book. - /// books-l2-tbt: 400 depth levels will be pushed in the initial full snapshot. Incremental data will be pushed tick by tick, i.e.whenever there is change in order book. - /// - /// On Data Handler - /// List of Instrument ID - /// Order Book Type - /// Cancellation Token - /// - public async Task> SubscribeToOrderBookAsync(Action onData, IEnumerable instrumentIds, OkxOrderBookType orderBookType, CancellationToken ct = default) - { - var internalHandler = new Action>(data => - { - foreach (var d in data.Data.Data) - { - if (d is not null) - { - if (d is null) continue; - if (data.Data.Arguments is null) continue; - d.InstrumentId = data.Data.Arguments.InstrumentId; - d.Action = data.Data.Action; - onData(d); - } - } - }); - - var arguments = new List(); - foreach (var instrumentId in instrumentIds) arguments.Add(new OkxSocketRequestArgument - { - Channel = MapConverter.GetString(orderBookType), - InstrumentId = instrumentId, - }); - var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); - } - - // TODO: WS / Option trades channel - #endregion - #region Status Updates /// /// Get the status of system maintenance and push when the system maintenance status changes. First subscription: "Push the latest change data"; every time there is a state change, push the changed content @@ -568,9 +569,9 @@ public async Task> SubscribeToOrderBookA /// On Data Handler /// Cancellation Token /// - public async Task> SubscribeToSystemUpgradeStatusAsync(Action onData, CancellationToken ct = default) + public async Task> SubscribeToSystemUpgradeStatusAsync(Action onData, CancellationToken ct = default) { - var internalHandler = new Action>>>(data => + var internalHandler = new Action>>>(data => { foreach (var d in data.Data.Data) if (d is not null) onData(d); @@ -580,7 +581,7 @@ public async Task> SubscribeToSystemUpgr { Channel = "status", }); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); } #endregion diff --git a/OKX.Api/Status/Enums/OkxStatusMaintenanceService.cs b/OKX.Api/Public/Enums/OkxPublicMaintenanceService.cs similarity index 95% rename from OKX.Api/Status/Enums/OkxStatusMaintenanceService.cs rename to OKX.Api/Public/Enums/OkxPublicMaintenanceService.cs index ba85e2e4..7bc774f4 100644 --- a/OKX.Api/Status/Enums/OkxStatusMaintenanceService.cs +++ b/OKX.Api/Public/Enums/OkxPublicMaintenanceService.cs @@ -1,9 +1,9 @@ -namespace OKX.Api.Status; +namespace OKX.Api.Public; /// /// OKX Maintenance Service /// -public enum OkxStatusMaintenanceService +public enum OkxPublicMaintenanceService { /// /// WebSocket diff --git a/OKX.Api/Status/Enums/OkxStatusMaintenanceState.cs b/OKX.Api/Public/Enums/OkxPublicMaintenanceState.cs similarity index 88% rename from OKX.Api/Status/Enums/OkxStatusMaintenanceState.cs rename to OKX.Api/Public/Enums/OkxPublicMaintenanceState.cs index 6779e851..44319801 100644 --- a/OKX.Api/Status/Enums/OkxStatusMaintenanceState.cs +++ b/OKX.Api/Public/Enums/OkxPublicMaintenanceState.cs @@ -1,9 +1,9 @@ -namespace OKX.Api.Status; +namespace OKX.Api.Public; /// /// OKX Maintenance State /// -public enum OkxStatusMaintenanceState +public enum OkxPublicMaintenanceState { /// /// Scheduled diff --git a/OKX.Api/Status/Enums/OkxStatusMaintenanceSystem.cs b/OKX.Api/Public/Enums/OkxPublicMaintenanceSystem.cs similarity index 78% rename from OKX.Api/Status/Enums/OkxStatusMaintenanceSystem.cs rename to OKX.Api/Public/Enums/OkxPublicMaintenanceSystem.cs index 69d4b88d..6c92f594 100644 --- a/OKX.Api/Status/Enums/OkxStatusMaintenanceSystem.cs +++ b/OKX.Api/Public/Enums/OkxPublicMaintenanceSystem.cs @@ -1,9 +1,9 @@ -namespace OKX.Api.Status; +namespace OKX.Api.Public; /// /// OKX Maintenance System /// -public enum OkxStatusMaintenanceSystem +public enum OkxPublicMaintenanceSystem { /// /// Classic diff --git a/OKX.Api/Announcement/Responses/OkxAnnouncement.cs b/OKX.Api/Public/Responses/OkxPublicAnnouncement.cs similarity index 89% rename from OKX.Api/Announcement/Responses/OkxAnnouncement.cs rename to OKX.Api/Public/Responses/OkxPublicAnnouncement.cs index 268eb9c1..ab26b864 100644 --- a/OKX.Api/Announcement/Responses/OkxAnnouncement.cs +++ b/OKX.Api/Public/Responses/OkxPublicAnnouncement.cs @@ -1,9 +1,9 @@ -namespace OKX.Api.Announcement; +namespace OKX.Api.Public; /// /// OKX Announcement /// -public record OkxAnnouncement +public record OkxPublicAnnouncement { /// /// Announcement type @@ -21,7 +21,7 @@ public record OkxAnnouncement /// System maintenance status /// [JsonProperty("state")] - public OkxStatusMaintenanceState Status { get; set; } + public OkxPublicMaintenanceState Status { get; set; } /// /// Publish time. Unix timestamp format in milliseconds, e.g. 1597026383085 diff --git a/OKX.Api/Announcement/Responses/OkxAnnouncementType.cs b/OKX.Api/Public/Responses/OkxPublicAnnouncementType.cs similarity index 84% rename from OKX.Api/Announcement/Responses/OkxAnnouncementType.cs rename to OKX.Api/Public/Responses/OkxPublicAnnouncementType.cs index 41550a8f..90692a9f 100644 --- a/OKX.Api/Announcement/Responses/OkxAnnouncementType.cs +++ b/OKX.Api/Public/Responses/OkxPublicAnnouncementType.cs @@ -1,9 +1,9 @@ -namespace OKX.Api.Announcement; +namespace OKX.Api.Public; /// /// OKX Announcement Type /// -public record OkxAnnouncementType +public record OkxPublicAnnouncementType { /// /// Announcement type diff --git a/OKX.Api/Announcement/Responses/OkxAnnouncements.cs b/OKX.Api/Public/Responses/OkxPublicAnnouncements.cs similarity index 69% rename from OKX.Api/Announcement/Responses/OkxAnnouncements.cs rename to OKX.Api/Public/Responses/OkxPublicAnnouncements.cs index f2427845..8aa548ca 100644 --- a/OKX.Api/Announcement/Responses/OkxAnnouncements.cs +++ b/OKX.Api/Public/Responses/OkxPublicAnnouncements.cs @@ -1,9 +1,9 @@ -namespace OKX.Api.Announcement; +namespace OKX.Api.Public; /// /// OKX Announcement Wrapper /// -public record OkxAnnouncements +public record OkxPublicAnnouncements { /// /// Total number of pages @@ -15,5 +15,5 @@ public record OkxAnnouncements /// List of announcements /// [JsonProperty("details")] - public List Details { get; set; } = []; + public List Details { get; set; } = []; } diff --git a/OKX.Api/Status/Responses/OkxStatusMaintenance.cs b/OKX.Api/Public/Responses/OkxPublicMaintenance.cs similarity index 92% rename from OKX.Api/Status/Responses/OkxStatusMaintenance.cs rename to OKX.Api/Public/Responses/OkxPublicMaintenance.cs index 619769e8..e9d1057f 100644 --- a/OKX.Api/Status/Responses/OkxStatusMaintenance.cs +++ b/OKX.Api/Public/Responses/OkxPublicMaintenance.cs @@ -1,9 +1,9 @@ -namespace OKX.Api.Status; +namespace OKX.Api.Public; /// /// System Status /// -public record OkxStatusMaintenance +public record OkxPublicMaintenance { /// /// The title of system maintenance instructions @@ -15,7 +15,7 @@ public record OkxStatusMaintenance /// System maintenance status /// [JsonProperty("state")] - public OkxStatusMaintenanceState Status { get; set; } + public OkxPublicMaintenanceState Status { get; set; } /// /// Begin time of system maintenance, Unix timestamp format in milliseconds, e.g. 1617788463867 @@ -63,13 +63,13 @@ public record OkxStatusMaintenance /// Service type, 0:WebSocket ; 1:Spot/Margin ; 2:Futures ; 3:Perpetual ; 4:Options ; 5:Trading service /// [JsonProperty("serviceType")] - public OkxStatusMaintenanceService Service { get; set; } + public OkxPublicMaintenanceService Service { get; set; } /// /// Service type, 0:WebSocket ; 1:Spot/Margin ; 2:Futures ; 3:Perpetual ; 4:Options ; 5:Trading service /// [JsonProperty("system")] - public OkxStatusMaintenanceSystem System { get; set; } + public OkxPublicMaintenanceSystem System { get; set; } /// /// Rescheduled description,e.g. Rescheduled from 2021-01-26T16:30:00.000Z to 2021-01-28T16:30:00.000Z diff --git a/OKX.Api/RecurringBuy/Clients/OkxRecurringBuySocketClient.cs b/OKX.Api/RecurringBuy/Clients/OkxRecurringBuySocketClient.cs index 08926c2f..f2b0fe76 100644 --- a/OKX.Api/RecurringBuy/Clients/OkxRecurringBuySocketClient.cs +++ b/OKX.Api/RecurringBuy/Clients/OkxRecurringBuySocketClient.cs @@ -6,8 +6,8 @@ public class OkxRecurringBuySocketClient(OKXWebSocketApiClient root) { // Internal - internal OKXWebSocketApiClient Root { get; } = root; - internal OkxWebSocketApiOptions Options { get { return Root.Options; } } + internal OKXWebSocketApiClient _ { get; } = root; + internal OkxWebSocketApiOptions Options { get { return _.Options; } } /// /// Retrieve recurring buy orders. @@ -25,12 +25,6 @@ public async Task> SubscribeToOrderUpdat long? algoOrderId = null, CancellationToken ct = default) { - var internalHandler = new Action>>>(data => - { - foreach (var d in data.Data.Data) - if (d is not null) onData(d); - }); - var args = new OkxSocketRequestArgument { Channel = "algo-recurring-buy", @@ -39,6 +33,12 @@ public async Task> SubscribeToOrderUpdat if(algoOrderId.HasValue)args.AlgoOrderId = algoOrderId.ToOkxString(); var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, args); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); + var internalHandler = new Action>>>(data => + { + foreach (var d in data.Data.Data) + if (d is not null) onData(d); + }); + + return await _.RootSubscribeAsync(OkxSocketEndpoint.Public, request, null, false, internalHandler, ct).ConfigureAwait(false); } } \ No newline at end of file diff --git a/OKX.Api/Spread/Clients/OkxSpreadSocketClient.cs b/OKX.Api/Spread/Clients/OkxSpreadSocketClient.cs index 3777f90f..4480f311 100644 --- a/OKX.Api/Spread/Clients/OkxSpreadSocketClient.cs +++ b/OKX.Api/Spread/Clients/OkxSpreadSocketClient.cs @@ -6,12 +6,19 @@ public class OkxSpreadSocketClient(OKXWebSocketApiClient root) { // Internal - internal OKXWebSocketApiClient Root { get; } = root; + internal OKXWebSocketApiClient _ { get; } = root; + + // TODO: WS / Place order + // TODO: WS / Amend order + // TODO: WS / Cancel order + // TODO: WS / Cancel all orders // TODO: Order channel // TODO: Trades channel + // TODO: Order book channel // TODO: Public Trades channel // TODO: Tickers channel + // TODO: Candlesticks channel } \ No newline at end of file diff --git a/OKX.Api/Status/Clients/OkxStatusRestClient.cs b/OKX.Api/Status/Clients/OkxStatusRestClient.cs deleted file mode 100644 index 6daedcd9..00000000 --- a/OKX.Api/Status/Clients/OkxStatusRestClient.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace OKX.Api.Status; - -/// -/// OKX Rest Api Status Client -/// -public class OkxStatusRestClient(OkxRestApiClient root) : OkxBaseRestClient(root) -{ - // Endpoints - private const string v5SystemStatus = "api/v5/system/status"; - - /// - /// Get event status of system upgrade. - /// Planned system maintenance that may result in short interruption (lasting less than 5 seconds) or websocket disconnection (users can immediately reconnect) will not be announced. - /// The maintenance will only be performed during times of low market volatility. - /// - /// System maintenance status - /// Cancellation Token - /// - public Task>> GetSystemUpgradeStatusAsync( - OkxStatusMaintenanceState? state = null, - CancellationToken ct = default) - { - var parameters = new ParameterCollection(); - parameters.AddOptionalEnum("state", state); - - return ProcessListRequestAsync(GetUri(v5SystemStatus), HttpMethod.Get, ct, signed: false, queryParameters: parameters); - } - -} diff --git a/OKX.Api/Trade/Clients/OkxTradeSocketClient.cs b/OKX.Api/Trade/Clients/OkxTradeSocketClient.cs index ffa2ee51..7be94386 100644 --- a/OKX.Api/Trade/Clients/OkxTradeSocketClient.cs +++ b/OKX.Api/Trade/Clients/OkxTradeSocketClient.cs @@ -6,8 +6,8 @@ public class OkxTradeSocketClient(OKXWebSocketApiClient root) { // Internal - internal OKXWebSocketApiClient Root { get; } = root; - internal OkxWebSocketApiOptions Options { get { return Root.Options; } } + internal OKXWebSocketApiClient _ { get; } = root; + internal OkxWebSocketApiOptions Options { get { return _.Options; } } /// /// Retrieve order information. Data will not be pushed when first subscribed. Data will only be pushed when triggered by events such as placing/canceling order. @@ -38,22 +38,65 @@ public async Task> SubscribeToOrderUpdat IEnumerable symbols, CancellationToken ct = default) { + var arguments = new List(); + foreach (var symbol in symbols) arguments.Add(new OkxSocketRequestArgument + { + Channel = "orders", + InstrumentId = symbol.InstrumentId, + InstrumentType = symbol.InstrumentType, + InstrumentFamily = symbol.InstrumentFamily, + }); + var internalHandler = new Action>>>(data => { foreach (var d in data.Data.Data) if (d is not null) onData(d); }); + var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Private, request, null, true, internalHandler, ct).ConfigureAwait(false); + } + + /// + /// Retrieve transaction information. Data will not be pushed when first subscribed. Data will only be pushed when there are order book fill events, where tradeId > 0. + /// + /// On Data Handler + /// Instrument ID + /// Cancellation Token + /// + public async Task> SubscribeToFillsAsync( + Action onData, + string? instrumentId = null, + CancellationToken ct = default) + => await SubscribeToFillsAsync(onData, [instrumentId], ct).ConfigureAwait(false); + + /// + /// Retrieve transaction information. Data will not be pushed when first subscribed. Data will only be pushed when there are order book fill events, where tradeId > 0. + /// + /// On Data Handler + /// Instrument IDs + /// Cancellation Token + /// + public async Task> SubscribeToFillsAsync( + Action onData, + IEnumerable instrumentIds, + CancellationToken ct = default) + { var arguments = new List(); - foreach (var symbol in symbols) arguments.Add(new OkxSocketRequestArgument + foreach (var instrumentId in instrumentIds) arguments.Add(new OkxSocketRequestArgument { - Channel = "orders", - InstrumentId = symbol.InstrumentId, - InstrumentType = symbol.InstrumentType, - InstrumentFamily = symbol.InstrumentFamily, + Channel = "fills", + InstrumentId = instrumentId, }); + + var internalHandler = new Action>>>(data => + { + foreach (var d in data.Data.Data) + if (d is not null) onData(d); + }); + var request = new OkxSocketRequest(OkxSocketOperation.Subscribe, arguments); - return await Root.RootSubscribeAsync(OkxSocketEndpoint.Private, request, null, true, internalHandler, ct).ConfigureAwait(false); + return await _.RootSubscribeAsync(OkxSocketEndpoint.Private, request, null, true, internalHandler, ct).ConfigureAwait(false); } /// @@ -64,8 +107,8 @@ public async Task> SubscribeToOrderUpdat public async Task> PlaceOrderAsync(OkxTradeOrderPlaceRequest request) { request.Tag = Options.BrokerId; - var req = new OkxSocketRequest(Root.RequestId().ToString(), OkxSocketOperation.Order, [request]); - return await Root.RootQueryAsync(OkxSocketEndpoint.Private, req, true).ConfigureAwait(false); + var req = new OkxSocketRequest(_.RequestId().ToString(), OkxSocketOperation.Order, [request]); + return await _.RootQueryAsync(OkxSocketEndpoint.Private, req, true).ConfigureAwait(false); } /// @@ -76,8 +119,8 @@ public async Task> PlaceOrderAsync(OkxTra public async Task>> PlaceOrdersAsync(IEnumerable requests) { foreach (var order in requests) order.Tag = Options.BrokerId; - var req = new OkxSocketRequest(Root.RequestId().ToString(), OkxSocketOperation.BatchOrders, requests); - return await Root.RootQueryAsync>(OkxSocketEndpoint.Private, req, true).ConfigureAwait(false); + var req = new OkxSocketRequest(_.RequestId().ToString(), OkxSocketOperation.BatchOrders, requests); + return await _.RootQueryAsync>(OkxSocketEndpoint.Private, req, true).ConfigureAwait(false); } /// @@ -87,8 +130,8 @@ public async Task>> PlaceOrde /// public async Task> CancelOrderAsync(OkxTradeOrderCancelRequest request) { - var req = new OkxSocketRequest(Root.RequestId().ToString(), OkxSocketOperation.CancelOrder, [request]); - return await Root.RootQueryAsync(OkxSocketEndpoint.Private, req, true).ConfigureAwait(false); + var req = new OkxSocketRequest(_.RequestId().ToString(), OkxSocketOperation.CancelOrder, [request]); + return await _.RootQueryAsync(OkxSocketEndpoint.Private, req, true).ConfigureAwait(false); } /// @@ -98,8 +141,8 @@ public async Task> CancelOrderAsync(OkxTradeOrde /// public async Task>> CancelOrdersAsync(IEnumerable requests) { - var req = new OkxSocketRequest(Root.RequestId().ToString(), OkxSocketOperation.BatchAmendOrders, requests); - return await Root.RootQueryAsync>(OkxSocketEndpoint.Private, req, true).ConfigureAwait(false); + var req = new OkxSocketRequest(_.RequestId().ToString(), OkxSocketOperation.BatchAmendOrders, requests); + return await _.RootQueryAsync>(OkxSocketEndpoint.Private, req, true).ConfigureAwait(false); } /// @@ -109,8 +152,8 @@ public async Task>> CancelOrdersAsyn /// public async Task> AmendOrderAsync(OkxTradeOrderAmendRequest request) { - var req = new OkxSocketRequest(Root.RequestId().ToString(), OkxSocketOperation.AmendOrder, [request]); - return await Root.RootQueryAsync(OkxSocketEndpoint.Private, req, true).ConfigureAwait(false); + var req = new OkxSocketRequest(_.RequestId().ToString(), OkxSocketOperation.AmendOrder, [request]); + return await _.RootQueryAsync(OkxSocketEndpoint.Private, req, true).ConfigureAwait(false); } /// @@ -120,8 +163,8 @@ public async Task> AmendOrderAsync(OkxTradeOrderA /// public async Task>> AmendOrdersAsync(IEnumerable requests) { - var req = new OkxSocketRequest(Root.RequestId().ToString(), OkxSocketOperation.BatchAmendOrders, requests); - return await Root.RootQueryAsync>(OkxSocketEndpoint.Private, req, true).ConfigureAwait(false); + var req = new OkxSocketRequest(_.RequestId().ToString(), OkxSocketOperation.BatchAmendOrders, requests); + return await _.RootQueryAsync>(OkxSocketEndpoint.Private, req, true).ConfigureAwait(false); } /// @@ -132,8 +175,8 @@ public async Task>> AmendOrdersAsync( /// public async Task> MassCancelAsync(OkxTradeMassCancelRequest request) { - var req = new OkxSocketRequest(Root.RequestId().ToString(), OkxSocketOperation.MassCancel, [request]); - return await Root.RootQueryAsync(OkxSocketEndpoint.Private, req, true).ConfigureAwait(false); + var req = new OkxSocketRequest(_.RequestId().ToString(), OkxSocketOperation.MassCancel, [request]); + return await _.RootQueryAsync(OkxSocketEndpoint.Private, req, true).ConfigureAwait(false); } } \ No newline at end of file diff --git a/OKX.Api/Trade/Responses/OkxTradeFill.cs b/OKX.Api/Trade/Responses/OkxTradeFill.cs new file mode 100644 index 00000000..22edb665 --- /dev/null +++ b/OKX.Api/Trade/Responses/OkxTradeFill.cs @@ -0,0 +1,67 @@ +namespace OKX.Api.Trade; + +/// +/// OkxTradeFill +/// +public record OkxTradeFill +{ + /// + /// Instrument ID + /// + [JsonProperty("instId")] + public string InstrumentId { get; set; } = string.Empty; + + /// + /// Filled quantity + /// + [JsonProperty("fillSz")] + public decimal FilledQuantity { get; set; } + + /// + /// Last filled price + /// + [JsonProperty("fillPx")] + public decimal FilledPrice { get; set; } + + /// + /// Trade direction + /// + [JsonProperty("side")] + public OkxTradeOrderSide OrderSide { get; set; } + + /// + /// Unix timestamp of the transaction's generation time in milliseconds. + /// + [JsonProperty("ts")] + public long Timestamp { get; set; } + + /// + /// the transaction's generation time + /// + [JsonIgnore] + public DateTime Time => Timestamp.ConvertFromMilliseconds(); + + /// + /// Order ID + /// + [JsonProperty("ordId")] + public long OrderId { get; set; } + + /// + /// The last trade ID in the trades aggregation + /// + [JsonProperty("tradeId")] + public long? TradeId { get; set; } + + /// + /// Liquidity taker or maker, T: taker M: maker + /// + [JsonProperty("execType")] + public OkxTradeOrderRole LiquidityRole { get; set; } + + /// + /// The count of trades aggregated + /// + [JsonProperty("count")] + public int Count { get; set; } +} diff --git a/OKX.Api/Usings.cs b/OKX.Api/Usings.cs index b856f5a8..97c5743d 100644 --- a/OKX.Api/Usings.cs +++ b/OKX.Api/Usings.cs @@ -15,7 +15,6 @@ global using OKX.Api.Account; global using OKX.Api.Affiliate; global using OKX.Api.Algo; -global using OKX.Api.Announcement; global using OKX.Api.Authentication; global using OKX.Api.Base; global using OKX.Api.Block; @@ -30,7 +29,6 @@ global using OKX.Api.Rubik; global using OKX.Api.SignalBot; global using OKX.Api.Spread; -global using OKX.Api.Status; global using OKX.Api.SubAccount; global using OKX.Api.Trade; global using System; diff --git a/README.md b/README.md index 8e8a44ba..4f574a99 100644 --- a/README.md +++ b/README.md @@ -115,14 +115,9 @@ var public_39 = await api.Public.GetOracleAsync(); var public_40 = await api.Public.GetExchangeRateAsync(); var public_41 = await api.Public.GetIndexComponentsAsync("BTC-USDT"); var public_42 = await api.Public.GetEconomicCalendarDataAsync("BTC-USDT"); - -// Status Methods (Unsigned) -var system_01 = await api.Status.GetSystemUpgradeStatusAsync(); - -// Announcement Methods -var announcement_01 = await api.Announcement.GetAnnouncementTypesAsync(); // (Unsigned) -var announcement_02 = await api.Announcement.GetAnnouncementsAsync(); // (Signed) - +var public_43 = await api.Public.GetAnnouncementTypesAsync(); +var public_44 = await api.Public.GetAnnouncementsAsync(); +var public_45 = await api.Public.GetSystemUpgradeStatusAsync(); // Trading Account Methods (Signed) var account_01 = await api.Account.GetInstrumentsAsync(OkxInstrumentType.Spot); @@ -625,7 +620,10 @@ await ws.Trading.SubscribeToOrderUpdatesAsync((data) => { // ... Your logic here }, OkxInstrumentType.Futures, "INSTRUMENT-FAMILY", "INSTRUMENT-ID"); - +await ws.Trade.SubscribeToFillsAsync((data) => +{ + // ... Your logic here +}); await ws.Trading.PlaceOrderAsync(new Trade.Models.OkxOrderPlaceRequest()); await ws.Trading.PlaceOrdersAsync(new List()); await ws.Trading.CancelOrderAsync(new Trade.Models.OkxOrderCancelRequest()); diff --git a/SYNC.md b/SYNC.md deleted file mode 100644 index a0804828..00000000 --- a/SYNC.md +++ /dev/null @@ -1,17 +0,0 @@ -Account @ 2024-10-04 -Trading @ 2024-10-04 -Algo @ 2024-10-04 -Grid @ 2024-10-04 -RecurringBuy @ 2024-10-04 -CopyTrading @ 2024-10-04 -Public @ 2024-10-04 -Block @ 2024-10-04 -Spread @ 2024-10-04 -Rubik @ 2024-10-04 -Funding @ 2024-10-04 -SubAccount @ 2024-10-04 -Financial @ 2024-10-04 -Affiliate @ 2024-10-04 -Announcement @ 2024-10-04 -SignalBot @ 2024-10-04 -Broker @