diff --git a/README.md b/README.md index 3383dcb..c01eb39 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,6 @@ - [Usage](#usage) - [Contact](#contact) - [Contributors](#contributors) -- [Donations](#donations) ## About **Bybit.Net.Api** offers an official, powerful, and efficient .NET connector to the [Bybit public Trading API](https://bybit-exchange.github.io/docs/v5/intro) @@ -59,8 +58,8 @@ Furthermore methods to install pakcage, please check [Nuget Repository](https:// - Base URL Setting: Allows setting to testnet or mainnet. by default [HTTP_MAINNET_URL](https://api.bybit.com) - Trade API: For create/amend/cancel single & batch orders, now supports dedicated class, dictionary. - Asset API: Deposit and withdrawal operations will automatically generate a transfer ID. -- Account API : Add new function Set Spot Hedging -- Position API : Add new function Confirm New Risk Limit +- Account API: Add new function Set Spot Hedging +- Position API: Add new function Confirm New Risk Limit ### WebSocket - Ping Pong Interval Parameter: Added by default (20 seconds). @@ -89,7 +88,7 @@ var orderInfo = await tradeService.PlaceOrder(category: Category.LINEAR, symbol: Console.WriteLine(orderInfo); ``` -- Place Bacth Order by Dictionary +- Place Batch Order by Dictionary ```DotNet Dictionary dict1 = new() { { "symbol", "XRPUSDT" }, { "orderType", "Limit" }, { "side", "Buy" }, { "qty", "10" }, { "price", "0.6080" }, { "timeInForce", "GTC" } }; Dictionary dict2 = new() { { "symbol", "BLZUSDT" }, { "orderType", "Limit" }, { "side", "Buy" }, { "qty", "10" }, { "price", "0.6080" }, { "timeInForce", "GTC" } }; @@ -98,7 +97,7 @@ var orderInfoString = await TradeService.PlaceBatchOrder(category: Category.LINE Console.WriteLine(orderInfoString); ``` -- Place Bacth Order by dedicated OrderRequest Class +- Place Batch Order by dedicated OrderRequest Class ```DotNet var order1 = new OrderRequest { Symbol = "XRPUSDT", OrderType = OrderType.LIMIT.Value, Side = Side.BUY.Value, Qty = "10", Price = "0.6080", TimeInForce = TimeInForce.GTC.Value }; var order2 = new OrderRequest { Symbol = "BLZUSDT", OrderType = OrderType.LIMIT.Value, Side = Side.BUY.Value, Qty = "10", Price = "0.6080", TimeInForce = TimeInForce.GTC.Value }; @@ -120,7 +119,7 @@ var positionInfo = await positionService.GetPositionInfo(category: Category.LINE Console.WriteLine(positionInfo); ``` -### Websocket public channel +### WebSocket public channel - Trade Subscribe ```DotNet var linearWebsocket = new BybitLinearWebSocket(useTestNet: true, pingIntevral: 5); @@ -133,10 +132,10 @@ linearWebsocket.OnMessageReceived( await linearWebsocket.ConnectAsync(new string[] { "publicTrade.BTCUSDT" }, CancellationToken.None); ``` -### Websocket private channel +### WebSocket private channel - Order Subscribe ```DotNet -var privateWebsocket = new(apiKey: "xxxxxxxxxxx", apiSecret: "xxxxxxxxxxxxxxx", useTestNet: true, pingIntevral: 5, maxAliveTime:"120s"); +var privateWebsocket = new BybitPrivateWebsocket(apiKey: "xxxxxxxxxxx", apiSecret: "xxxxxxxxxxxxxxx", useTestNet: true, pingIntevral: 5, maxAliveTime:"120s"); privateWebsocket.OnMessageReceived( (data) => { @@ -154,7 +153,7 @@ List of other contributors + -
- +
@@ -162,12 +161,20 @@ List of other contributors

- 💻 - 📖 + 💻 + 📖 +
+ + +
+ + Kolya + +
+
+ 💻 + 📖
- -## Donations -Your donations keep our development active and our community growing. Donate USDT to our [ERC20 Wallet Address](0x238bbb45af1254e2fd76564c9b56042c452f3d6e). - + \ No newline at end of file diff --git a/Src/Common/ApiServiceImp/BybitAccountService.cs b/Src/Common/ApiServiceImp/BybitAccountService.cs index 8304abb..8224211 100644 --- a/Src/Common/ApiServiceImp/BybitAccountService.cs +++ b/Src/Common/ApiServiceImp/BybitAccountService.cs @@ -435,5 +435,47 @@ public BybitAccountService(HttpClient httpClient, string apiKey, string apiSecre var result = await this.SendSignedAsync(MMP_STATE, HttpMethod.Get, query: query); return result; } + + private const string ACCOUNT_MANUAL_BORROW = "/v5/account/borrow"; + + /// + /// Manual Borrow — POST /v5/account/borrow + /// + public async Task ManualBorrow(string coin, string amount) + { + var body = new Dictionary + { + { "coin", coin }, + { "amount", amount } + }; + + var result = await this.SendSignedAsync( + ACCOUNT_MANUAL_BORROW, + HttpMethod.Post, + query: body); + + return result; + } + + private const string ACCOUNT_MANUAL_REPAY = "/v5/account/repay"; + + /// + /// Manual Repay — POST /v5/account/repay + /// + public async Task ManualRepay(string? coin = null, string? amount = null) + { + var body = new Dictionary(); + BybitParametersUtils.AddOptionalParameters(body, + ("coin", coin), + ("amount", amount) + ); + + var result = await this.SendSignedAsync( + ACCOUNT_MANUAL_REPAY, + HttpMethod.Post, + query: body); + + return result; + } } } diff --git a/Src/Common/ApiServiceImp/BybitEarnService.cs b/Src/Common/ApiServiceImp/BybitEarnService.cs index b678e5b..b8a1de6 100644 --- a/Src/Common/ApiServiceImp/BybitEarnService.cs +++ b/Src/Common/ApiServiceImp/BybitEarnService.cs @@ -7,7 +7,7 @@ namespace bybit.net.api.ApiServiceImp { - internal class BybitEarnService : BybitApiService + public class BybitEarnService : BybitApiService { public BybitEarnService(string apiKey, string apiSecret, string? url = null, string recvWindow = BybitConstants.DEFAULT_REC_WINDOW, bool debugMode = false) : this(httpClient: new HttpClient(), apiKey: apiKey, apiSecret: apiSecret, url: url, recvWindow: recvWindow, debugMode: debugMode) diff --git a/Src/Common/ApiServiceImp/BybitNewCryptoLoanService.cs b/Src/Common/ApiServiceImp/BybitNewCryptoLoanService.cs index 465db31..14aefbf 100644 --- a/Src/Common/ApiServiceImp/BybitNewCryptoLoanService.cs +++ b/Src/Common/ApiServiceImp/BybitNewCryptoLoanService.cs @@ -7,7 +7,7 @@ namespace bybit.net.api.ApiServiceImp { - internal class BybitNewCryptoLoanService : BybitApiService + public class BybitNewCryptoLoanService : BybitApiService { public BybitNewCryptoLoanService(string apiKey, string apiSecret, string? url = null, string recvWindow = BybitConstants.DEFAULT_REC_WINDOW, bool debugMode = false) : this(httpClient: new HttpClient(), apiKey: apiKey, apiSecret: apiSecret, url: url, recvWindow: recvWindow, debugMode: debugMode) diff --git a/Src/Common/ApiServiceImp/BybitP2PService.cs b/Src/Common/ApiServiceImp/BybitP2PService.cs index 0d6ffdb..70d36bf 100644 --- a/Src/Common/ApiServiceImp/BybitP2PService.cs +++ b/Src/Common/ApiServiceImp/BybitP2PService.cs @@ -7,7 +7,7 @@ namespace bybit.net.api.ApiServiceImp { - internal class BybitP2PService : BybitApiService + public class BybitP2PService : BybitApiService { public BybitP2PService(string apiKey, string apiSecret, string? url = null, string recvWindow = BybitConstants.DEFAULT_REC_WINDOW, bool debugMode = false) : this(httpClient: new HttpClient(), apiKey: apiKey, apiSecret: apiSecret, url: url, recvWindow: recvWindow, debugMode: debugMode) diff --git a/Src/Common/ApiServiceImp/BybitRFQService.cs b/Src/Common/ApiServiceImp/BybitRFQService.cs new file mode 100644 index 0000000..6b811f4 --- /dev/null +++ b/Src/Common/ApiServiceImp/BybitRFQService.cs @@ -0,0 +1,389 @@ +using bybit.net.api.Models.RFQ; +using bybit.net.api.Services; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.ApiServiceImp +{ + public class BybitRFQService : BybitApiService + { + public BybitRFQService(string apiKey, string apiSecret, string? url = null, string recvWindow = BybitConstants.DEFAULT_REC_WINDOW, bool debugMode = false) + : this(httpClient: new HttpClient(), apiKey: apiKey, apiSecret: apiSecret, url: url, recvWindow: recvWindow, debugMode: debugMode) + { + } + + public BybitRFQService(HttpClient httpClient, string apiKey, string apiSecret, string? url = null, string recvWindow = BybitConstants.DEFAULT_REC_WINDOW, bool debugMode = false) + : base(httpClient: httpClient, apiKey: apiKey, apiSecret: apiSecret, url: url, recvWindow: recvWindow, debugMode: debugMode) + { + } + + private const string CREATE_RQF = "/v5/rfq/create-rfq"; + + /// + /// Create RFQ — POST /v5/rfq/create-rfq + /// + public async Task CreateRfq( + IEnumerable counterparties, + IEnumerable list, + string? rfqLinkId = null, + bool? anonymous = null, + string? strategyType = null) + { + var body = new Dictionary + { + { "counterparties", counterparties?.ToArray() ?? Array.Empty() }, + { "list", list?.ToArray() ?? Array.Empty() } + }; + + BybitParametersUtils.AddOptionalParameters(body, + ("rfqLinkId", rfqLinkId), + ("anonymous", anonymous), + ("strategyType", strategyType) + ); + + var result = await this.SendSignedAsync( + CREATE_RQF, + HttpMethod.Post, + query: body); + + return result; + } + + private const string GET_RQF_CONFIG = "/v5/rfq/config"; + + /// + /// Get RFQ Configuration — GET /v5/rfq/config + /// + public async Task GetRfqConfig() + { + var query = new Dictionary(); + var result = await this.SendSignedAsync( + GET_RQF_CONFIG, + HttpMethod.Get, + query: query); + + return result; + } + + private const string CANCEL_RQF = "/v5/rfq/cancel-rfq"; + + /// + /// Cancel RFQ — POST /v5/rfq/cancel-rfq + /// + public async Task CancelRfq(string? rfqId = null, string? rfqLinkId = null) + { + var body = new Dictionary(); + BybitParametersUtils.AddOptionalParameters(body, + ("rfqId", rfqId), + ("rfqLinkId", rfqLinkId) + ); + + var result = await this.SendSignedAsync( + CANCEL_RQF, + HttpMethod.Post, + query: body); + + return result; + } + + private const string CANCEL_ALL_RQF = "/v5/rfq/cancel-all-rfq"; + + /// + /// Cancel All RFQs — POST /v5/rfq/cancel-all-rfq + /// + public async Task CancelAllRfqs() + { + var body = new Dictionary(); + var result = await this.SendSignedAsync( + CANCEL_ALL_RQF, + HttpMethod.Post, + query: body); + + return result; + } + + private const string CREATE_QUOTE = "/v5/rfq/create-quote"; + + /// + /// Create Quote — POST /v5/rfq/create-quote + /// + public async Task CreateQuote( + string rfqId, + IEnumerable? quoteBuyList = null, + IEnumerable? quoteSellList = null, + string? quoteLinkId = null, + bool? anonymous = null, + int? expireIn = null) + { + var body = new Dictionary + { + { "rfqId", rfqId } + }; + + BybitParametersUtils.AddOptionalParameters(body, + ("quoteLinkId", quoteLinkId), + ("anonymous", anonymous), + ("expireIn", expireIn), + ("quoteBuyList", quoteBuyList?.ToArray()), + ("quoteSellList", quoteSellList?.ToArray()) + ); + + var result = await this.SendSignedAsync( + CREATE_QUOTE, + HttpMethod.Post, + query: body); + + return result; + } + + private const string EXECUTE_QUOTE = "/v5/rfq/execute-quote"; + + /// + /// Execute Quote — POST /v5/rfq/execute-quote + /// + public async Task ExecuteQuote(string rfqId, string quoteId, string quoteSide) + { + var body = new Dictionary + { + { "rfqId", rfqId }, + { "quoteId", quoteId }, + { "quoteSide", quoteSide } + }; + + var result = await this.SendSignedAsync( + EXECUTE_QUOTE, + HttpMethod.Post, + query: body); + + return result; + } + + private const string CANCEL_QUOTE = "/v5/rfq/cancel-quote"; + + /// + /// Cancel Quote — POST /v5/rfq/cancel-quote + /// + public async Task CancelQuote( + string? quoteId = null, + string? rfqId = null, + string? quoteLinkId = null) + { + var body = new Dictionary(); + BybitParametersUtils.AddOptionalParameters(body, + ("quoteId", quoteId), + ("rfqId", rfqId), + ("quoteLinkId", quoteLinkId) + ); + + var result = await this.SendSignedAsync( + CANCEL_QUOTE, + HttpMethod.Post, + query: body); + + return result; + } + + private const string CANCEL_ALL_QUOTES = "/v5/rfq/cancel-all-quotes"; + + /// + /// Cancel All Quotes — POST /v5/rfq/cancel-all-quotes + /// + public async Task CancelAllQuotes() + { + var body = new Dictionary(); + var result = await this.SendSignedAsync( + CANCEL_ALL_QUOTES, + HttpMethod.Post, + query: body); + + return result; + } + + private const string GET_RFQ_REALTIME = "/v5/rfq/rfq-realtime"; + + /// + /// Get RFQs (real-time) — GET /v5/rfq/rfq-realtime + /// + public async Task GetRfqRealtime( + string? rfqId = null, + string? rfqLinkId = null, + string? traderType = null) // quote | request (default: quote) + { + var query = new Dictionary(); + + BybitParametersUtils.AddOptionalParameters(query, + ("rfqId", rfqId), + ("rfqLinkId", rfqLinkId), + ("traderType", traderType) + ); + + var result = await this.SendSignedAsync( + GET_RFQ_REALTIME, + HttpMethod.Get, + query: query); + + return result; + } + + private const string GET_RQF_LIST = "/v5/rfq/rfq-list"; + + /// + /// Get RFQs — GET /v5/rfq/rfq-list + /// + public async Task GetRfqList( + string? rfqId = null, + string? rfqLinkId = null, + string? traderType = null, // quote | request + string? status = null, // Active | Canceled | Filled | Expired | Failed + int? limit = null, // [1, 100], default 50 + string? cursor = null) + { + var query = new Dictionary(); + + BybitParametersUtils.AddOptionalParameters(query, + ("rfqId", rfqId), + ("rfqLinkId", rfqLinkId), + ("traderType", traderType), + ("status", status), + ("limit", limit), + ("cursor", cursor) + ); + + var result = await this.SendSignedAsync( + GET_RQF_LIST, + HttpMethod.Get, + query: query); + + return result; + } + + private const string GET_QUOTE_REALTIME = "/v5/rfq/quote-realtime"; + + /// + /// Get Quotes (real-time) — GET /v5/rfq/quote-realtime + /// + public async Task GetQuoteRealtime( + string? rfqId = null, + string? quoteId = null, + string? quoteLinkId = null, + string? traderType = null) // quote | request + { + var query = new Dictionary(); + + BybitParametersUtils.AddOptionalParameters(query, + ("rfqId", rfqId), + ("quoteId", quoteId), + ("quoteLinkId", quoteLinkId), + ("traderType", traderType) + ); + + var result = await this.SendSignedAsync( + GET_QUOTE_REALTIME, + HttpMethod.Get, + query: query); + + return result; + } + + private const string GET_QUOTE_LIST = "/v5/rfq/quote-list"; + + /// + /// Get Quotes — GET /v5/rfq/quote-list + /// + public async Task GetQuoteList( + string? rfqId = null, + string? quoteId = null, + string? quoteLinkId = null, + string? traderType = null, // quote | request + string? status = null, // Active | Canceled | PendingFill | Filled | Expired | Failed + int? limit = null, // [1,100], default 50 + string? cursor = null) + { + var query = new Dictionary(); + + BybitParametersUtils.AddOptionalParameters(query, + ("rfqId", rfqId), + ("quoteId", quoteId), + ("quoteLinkId", quoteLinkId), + ("traderType", traderType), + ("status", status), + ("limit", limit), + ("cursor", cursor) + ); + + var result = await this.SendSignedAsync( + GET_QUOTE_LIST, + HttpMethod.Get, + query: query); + + return result; + } + + private const string GET_RQF_TRADE_LIST = "/v5/rfq/trade-list"; + + /// + /// Get Trade History — GET /v5/rfq/trade-list + /// + public async Task GetRfqTradeList( + string? rfqId = null, + string? rfqLinkId = null, + string? quoteId = null, + string? quoteLinkId = null, + string? traderType = null, // quote | request (default: quote) + string? status = null, // Filled | Failed + int? limit = null, // [1,100], default 50 + string? cursor = null) + { + var query = new Dictionary(); + + BybitParametersUtils.AddOptionalParameters(query, + ("rfqId", rfqId), + ("rfqLinkId", rfqLinkId), + ("quoteId", quoteId), + ("quoteLinkId", quoteLinkId), + ("traderType", traderType), + ("status", status), + ("limit", limit), + ("cursor", cursor) + ); + + var result = await this.SendSignedAsync( + GET_RQF_TRADE_LIST, + HttpMethod.Get, + query: query); + + return result; + } + + private const string GET_RQF_PUBLIC_TRADES = "/v5/rfq/public-trades"; + + /// + /// Get Public Trades — GET /v5/rfq/public-trades + /// + public async Task GetRfqPublicTrades( + long? startTime = null, + long? endTime = null, + int? limit = null, + string? cursor = null) + { + var query = new Dictionary(); + + BybitParametersUtils.AddOptionalParameters(query, + ("startTime", startTime), + ("endTime", endTime), + ("limit", limit), + ("cursor", cursor) + ); + + var result = await this.SendPublicAsync( + GET_RQF_PUBLIC_TRADES, + HttpMethod.Get, + query: query); + + return result; + } + } +} diff --git a/Src/Common/ApiServiceImp/BybitRateLimitService.cs b/Src/Common/ApiServiceImp/BybitRateLimitService.cs new file mode 100644 index 0000000..a074f0e --- /dev/null +++ b/Src/Common/ApiServiceImp/BybitRateLimitService.cs @@ -0,0 +1,105 @@ +using bybit.net.api.Models.RateLimit; +using bybit.net.api.Services; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.ApiServiceImp +{ + public class BybitRateLimitService : BybitApiService + { + public BybitRateLimitService(string apiKey, string apiSecret, string? url = null, string recvWindow = BybitConstants.DEFAULT_REC_WINDOW, bool debugMode = false) + : this(httpClient: new HttpClient(), apiKey: apiKey, apiSecret: apiSecret, url: url, recvWindow: recvWindow, debugMode: debugMode) + { + } + + public BybitRateLimitService(HttpClient httpClient, string apiKey, string apiSecret, string? url = null, string recvWindow = BybitConstants.DEFAULT_REC_WINDOW, bool debugMode = false) + : base(httpClient: httpClient, apiKey: apiKey, apiSecret: apiSecret, url: url, recvWindow: recvWindow, debugMode: debugMode) + { + } + + private const string SET_API_RATE_LIMIT = "/v5/apilimit/set"; + + /// + /// Set Rate Limit — POST /v5/apilimit/set + /// + public async Task SetApiRateLimit(IEnumerable list) + { + var body = new Dictionary + { + { "list", list?.ToArray() ?? Array.Empty() } + }; + + var result = await this.SendSignedAsync( + SET_API_RATE_LIMIT, + HttpMethod.Post, + query: body); + + return result; + } + + private const string GET_API_RATE_LIMIT = "/v5/apilimit/query"; + + /// + /// Get Rate Limit — GET /v5/apilimit/query + /// + public async Task GetApiRateLimit(string uids) + { + var query = new Dictionary + { + { "uids", uids } + }; + + var result = await this.SendSignedAsync( + GET_API_RATE_LIMIT, + HttpMethod.Get, + query: query); + + return result; + } + + private const string GET_API_RATE_LIMIT_CAP = "/v5/apilimit/query-cap"; + + /// + /// Get Rate Limit Cap — GET /v5/apilimit/query-cap + /// + public async Task GetApiRateLimitCap() + { + var query = new Dictionary(); + var result = await this.SendSignedAsync( + GET_API_RATE_LIMIT_CAP, + HttpMethod.Get, + query: query); + + return result; + } + + private const string GET_API_RATE_LIMIT_ALL = "/v5/apilimit/query-all"; + + /// + /// Get All Rate Limits — GET /v5/apilimit/query-all + /// + public async Task GetAllApiRateLimits( + string? limit = null, // [1, 1000], default 1000 + string? cursor = null, + string? uids = null) // comma-separated; across different masters + { + var query = new Dictionary(); + + BybitParametersUtils.AddOptionalParameters(query, + ("limit", limit), + ("cursor", cursor), + ("uids", uids) + ); + + var result = await this.SendSignedAsync( + GET_API_RATE_LIMIT_ALL, + HttpMethod.Get, + query: query); + + return result; + } + } +} diff --git a/Src/Common/ApiServiceImp/BybitSpreadTradingService.cs b/Src/Common/ApiServiceImp/BybitSpreadTradingService.cs new file mode 100644 index 0000000..5f4d3c7 --- /dev/null +++ b/Src/Common/ApiServiceImp/BybitSpreadTradingService.cs @@ -0,0 +1,386 @@ +using bybit.net.api.Services; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.ApiServiceImp +{ + public class BybitSpreadTradingService : BybitApiService + { + public BybitSpreadTradingService(string apiKey, string apiSecret, string? url = null, string recvWindow = BybitConstants.DEFAULT_REC_WINDOW, bool debugMode = false) + : this(httpClient: new HttpClient(), apiKey: apiKey, apiSecret: apiSecret, url: url, recvWindow: recvWindow, debugMode: debugMode) + { + } + + public BybitSpreadTradingService(HttpClient httpClient, string apiKey, string apiSecret, string? url = null, string recvWindow = BybitConstants.DEFAULT_REC_WINDOW, bool debugMode = false) + : base(httpClient: httpClient, apiKey: apiKey, apiSecret: apiSecret, url: url, recvWindow: recvWindow, debugMode: debugMode) + { + } + + private const string GET_SPREAD_INSTRUMENT_INFO = "/v5/spread/instrument"; + + /// + /// Get Instruments Info + /// Query instrument specification of spread combinations. + /// HTTP GET /v5/spread/instrument + /// + /// Spread combination symbol name + /// Base coin (uppercase) + /// [1,500], default on server: 200 + /// Pagination cursor from response.nextPageCursor + /// Raw JSON string + public async Task GetSpreadInstrumentsInfo( + string? symbol = null, + string? baseCoin = null, + int? limit = null, + string? cursor = null) + { + var query = new Dictionary(); + + BybitParametersUtils.AddOptionalParameters(query, + ("symbol", symbol), + ("baseCoin", baseCoin), + ("limit", limit), + ("cursor", cursor) + ); + + var result = await this.SendPublicAsync( + GET_SPREAD_INSTRUMENT_INFO, + HttpMethod.Get, + query: query); + + return result; + } + + private const string GET_SPREAD_ORDERBOOK = "/v5/spread/orderbook"; + + /// + /// Get Orderbook + /// Query spread orderbook depth data. + /// HTTP GET /v5/spread/orderbook + /// + /// Spread combination symbol name (required) + /// Limit size for each bid/ask [1, 25]. Default: 1 + /// Raw JSON string + public async Task GetSpreadOrderbook(string symbol, int? limit = null) + { + var query = new Dictionary + { + { "symbol", symbol } + }; + + BybitParametersUtils.AddOptionalParameters(query, + ("limit", limit) + ); + + var result = await this.SendPublicAsync( + GET_SPREAD_ORDERBOOK, + HttpMethod.Get, + query: query); + + return result; + } + + private const string GET_SPREAD_TICKERS = "/v5/spread/tickers"; + + /// + /// Get Tickers + /// Latest price snapshot, best bid/ask, and 24h volume for spread combinations. + /// HTTP GET /v5/spread/tickers + /// + /// Spread combination symbol name (required) + /// Raw JSON string + public async Task GetSpreadTickers(string symbol) + { + var query = new Dictionary + { + { "symbol", symbol } + }; + + var result = await this.SendPublicAsync( + GET_SPREAD_TICKERS, + HttpMethod.Get, + query: query); + + return result; + } + + private const string GET_SPREAD_RECENT_TRADES = "/v5/spread/recent-trade"; + + /// + /// Get Recent Public Trades + /// Query recent public spread trading history in Bybit. + /// HTTP GET /v5/spread/recent-trade + /// + /// Spread combination symbol name (required) + /// Limit per page [1, 1000], default: 500 + /// Raw JSON string + public async Task GetSpreadRecentPublicTrades(string symbol, int? limit = null) + { + var query = new Dictionary + { + { "symbol", symbol } + }; + + BybitParametersUtils.AddOptionalParameters(query, + ("limit", limit) + ); + + var result = await this.SendPublicAsync( + GET_SPREAD_RECENT_TRADES, + HttpMethod.Get, + query: query); + + return result; + } + + private const string CREATE_SPREAD_ORDER = "/v5/spread/order/create"; + + /// + /// Create Order + /// HTTP POST /v5/spread/order/create + /// + /// Spread combination symbol name (required) + /// Order side: Buy | Sell (required) + /// Order type: Limit | Market (required) + /// Order qty (required) + /// Order price (optional; required for Limit) + /// + /// User-customized order ID (<=45 chars). Allowed: 0-9, a-z, A-Z, dash (-), underscore (_). + /// + /// IOC | FOK | GTC | PostOnly + /// Raw JSON string + public async Task CreateSpreadOrder( + string symbol, + string side, + string orderType, + string qty, + string? price = null, + string? orderLinkId = null, + string? timeInForce = null) + { + var body = new Dictionary + { + { "symbol", symbol }, + { "side", side }, + { "orderType", orderType }, + { "qty", qty } + }; + + BybitParametersUtils.AddOptionalParameters(body, + ("price", price), + ("orderLinkId", orderLinkId), + ("timeInForce", timeInForce) + ); + + var result = await this.SendSignedAsync( + CREATE_SPREAD_ORDER, + HttpMethod.Post, + query: body); + + return result; + } + + private const string AMEND_SPREAD_ORDER = "/v5/spread/order/amend"; + + /// + /// Amend Order + /// You can only modify unfilled or partially filled orders. + /// + /// Spread combination symbol name (required) + /// Spread combination order ID (either orderId or orderLinkId) + /// User customised order ID (either orderId or orderLinkId) + /// Order quantity after modification (either qty or price) + /// + /// Order price after modification. Use empty string ("") to keep price unchanged, "0" to set price to 0. + /// + /// Raw JSON string + public async Task AmendSpreadOrder( + string symbol, + string? orderId = null, + string? orderLinkId = null, + string? qty = null, + string? price = null) + { + var body = new Dictionary + { + { "symbol", symbol } + }; + + BybitParametersUtils.AddOptionalParameters(body, + ("orderId", orderId), + ("orderLinkId", orderLinkId), + ("qty", qty), + ("price", price) + ); + + var result = await this.SendSignedAsync( + AMEND_SPREAD_ORDER, + HttpMethod.Post, + query: body); + + return result; + } + + private const string CANCEL_SPREAD_ORDER = "/v5/spread/order/cancel"; + + /// + /// Cancel Order + /// HTTP POST /v5/spread/order/cancel + /// + /// Spread combination order ID + /// User customised order ID + /// Raw JSON string + public async Task CancelSpreadOrder( + string? orderId = null, + string? orderLinkId = null) + { + var body = new Dictionary(); + + BybitParametersUtils.AddOptionalParameters(body, + ("orderId", orderId), + ("orderLinkId", orderLinkId) + ); + + var result = await this.SendSignedAsync( + CANCEL_SPREAD_ORDER, + HttpMethod.Post, + query: body); + + return result; + } + + private const string CANCEL_ALL_SPREAD_ORDERS = "/v5/spread/order/cancel-all"; + + /// + /// Cancel All Orders + /// POST /v5/spread/order/cancel-all + /// + /// Spread combination symbol name + /// true | false + public async Task CancelAllSpreadOrders(string? symbol = null, bool? cancelAll = null) + { + var body = new Dictionary(); + BybitParametersUtils.AddOptionalParameters(body, + ("symbol", symbol), + ("cancelAll", cancelAll) + ); + + var result = await this.SendSignedAsync( + CANCEL_ALL_SPREAD_ORDERS, + HttpMethod.Post, + query: body); + + return result; + } + + private const string GET_SPREAD_OPEN_ORDERS = "/v5/spread/order/realtime"; + + /// + /// Get Open Orders + /// GET /v5/spread/order/realtime + /// + public async Task GetSpreadOpenOrders( + string? symbol = null, + string? baseCoin = null, + string? orderId = null, + string? orderLinkId = null, + int? limit = null, + string? cursor = null) + { + var query = new Dictionary(); + + BybitParametersUtils.AddOptionalParameters(query, + ("symbol", symbol), + ("baseCoin", baseCoin), + ("orderId", orderId), + ("orderLinkId", orderLinkId), + ("limit", limit), + ("cursor", cursor) + ); + + var result = await this.SendSignedAsync( + GET_SPREAD_OPEN_ORDERS, + HttpMethod.Get, + query: query); + + return result; + } + + private const string GET_SPREAD_ORDER_HISTORY = "/v5/spread/order/history"; + + /// + /// Get Order History + /// GET /v5/spread/order/history + /// + public async Task GetSpreadOrderHistory( + string? symbol = null, + string? baseCoin = null, + string? orderId = null, + string? orderLinkId = null, + long? startTime = null, + long? endTime = null, + int? limit = null, + string? cursor = null) + { + var query = new Dictionary(); + + BybitParametersUtils.AddOptionalParameters(query, + ("symbol", symbol), + ("baseCoin", baseCoin), + ("orderId", orderId), + ("orderLinkId", orderLinkId), + ("startTime", startTime), + ("endTime", endTime), + ("limit", limit), + ("cursor", cursor) + ); + + var result = await this.SendSignedAsync( + GET_SPREAD_ORDER_HISTORY, + HttpMethod.Get, + query: query); + + return result; + } + + private const string GET_SPREAD_TRADE_HISTORY = "/v5/spread/execution/list"; + + /// + /// Get Trade History + /// GET /v5/spread/execution/list + /// + public async Task GetSpreadTradeHistory( + string? symbol = null, + string? orderId = null, + string? orderLinkId = null, + long? startTime = null, + long? endTime = null, + int? limit = null, + string? cursor = null) + { + var query = new Dictionary(); + + BybitParametersUtils.AddOptionalParameters(query, + ("symbol", symbol), + ("orderId", orderId), + ("orderLinkId", orderLinkId), + ("startTime", startTime), + ("endTime", endTime), + ("limit", limit), + ("cursor", cursor) + ); + + var result = await this.SendSignedAsync( + GET_SPREAD_TRADE_HISTORY, + HttpMethod.Get, + query: query); + + return result; + } + + + } +} diff --git a/Src/Common/Models/RFQ/CancelAllRfqsResponse.cs b/Src/Common/Models/RFQ/CancelAllRfqsResponse.cs new file mode 100644 index 0000000..1dd9743 --- /dev/null +++ b/Src/Common/Models/RFQ/CancelAllRfqsResponse.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.Models.RFQ +{ + public class CancelAllRfqsResponse + { + public List? result { get; set; } + } + + public class CancelAllRfqItem + { + public string? rfqId { get; set; } // Inquiry ID + public string? rfqLinkId { get; set; } // Custom inquiry ID + public string? code { get; set; } // "0" means success + public string? msg { get; set; } // failure reason if any + } +} diff --git a/Src/Common/Models/RFQ/CancelRfqResponse.cs b/Src/Common/Models/RFQ/CancelRfqResponse.cs new file mode 100644 index 0000000..f507828 --- /dev/null +++ b/Src/Common/Models/RFQ/CancelRfqResponse.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.Models.RFQ +{ + public class CancelRfqResponse + { + public string? rfqId { get; set; } // Inquiry ID + public string? rfqLinkId { get; set; } // Custom inquiry ID + } +} diff --git a/Src/Common/Models/RFQ/CreateQuoteResponse.cs b/Src/Common/Models/RFQ/CreateQuoteResponse.cs new file mode 100644 index 0000000..db51eca --- /dev/null +++ b/Src/Common/Models/RFQ/CreateQuoteResponse.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.Models.RFQ +{ + public class CreateQuoteResponse + { + public CreateQuoteResult? result { get; set; } + } + + public class CreateQuoteResult + { + public string? rfqId { get; set; } // Inquiry ID + public string? quoteId { get; set; } // Quote ID + public string? quoteLinkId { get; set; } // Custom quote ID + public string? expiresAt { get; set; } // ms + public string? deskCode { get; set; } // quoter code + public string? status { get; set; } // Active | Canceled | Filled | Expired | Failed + } +} diff --git a/Src/Common/Models/RFQ/CreateRfqResponse.cs b/Src/Common/Models/RFQ/CreateRfqResponse.cs new file mode 100644 index 0000000..abad361 --- /dev/null +++ b/Src/Common/Models/RFQ/CreateRfqResponse.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.Models.RFQ +{ + public class CreateRfqResponse + { + public List? result { get; set; } // Order ID list + public List? list { get; set; } // RFQ info + } + + public class CreateRfqItem + { + public string? rfqId { get; set; } // Inquiry ID + public string? rfqLinkId { get; set; } // Custom inquiry ID + public string? status { get; set; } // Active | Canceled | Filled | Expired | Failed + public string? expiresAt { get; set; } // ms + public string? deskCode { get; set; } // Inquirer unique code + } +} diff --git a/Src/Common/Models/RFQ/ExecuteQuoteResponse.cs b/Src/Common/Models/RFQ/ExecuteQuoteResponse.cs new file mode 100644 index 0000000..cda8b36 --- /dev/null +++ b/Src/Common/Models/RFQ/ExecuteQuoteResponse.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.Models.RFQ +{ + public class ExecuteQuoteResponse + { + public ExecuteQuoteResult? result { get; set; } + } + + public class ExecuteQuoteResult + { + public string? rfqId { get; set; } // Inquiry ID + public string? rfqLinkId { get; set; } + public string? quoteId { get; set; } // Quote ID + public string? status { get; set; } // PendingFill | Failed + } +} diff --git a/Src/Common/Models/RFQ/GetQuoteRealtimeResponse.cs b/Src/Common/Models/RFQ/GetQuoteRealtimeResponse.cs new file mode 100644 index 0000000..c443871 --- /dev/null +++ b/Src/Common/Models/RFQ/GetQuoteRealtimeResponse.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.Models.RFQ +{ + public class GetQuoteRealtimeResponse + { + public List? list { get; set; } + } + + public class RfqQuoteRealtimeItem + { + public string? rfqId { get; set; } // Inquiry ID + public string? rfqLinkId { get; set; } // Custom RFQ ID (not public) + public string? quoteId { get; set; } // Quote ID + public string? quoteLinkId { get; set; } // Custom quote ID (not public) + public string? expiresAt { get; set; } // ms + public string? deskCode { get; set; } // inquirer desk code (hidden if anonymous) + public string? status { get; set; } // Active | PendingFill | Canceled | Filled | Expired | Failed + public string? execQuoteSide { get; set; } // Buy | Sell + public string? createdAt { get; set; } // ms epoch + public string? updatedAt { get; set; } // ms epoch + public List? quoteBuyList { get; set; } + public List? quoteSellList { get; set; } + } +} diff --git a/Src/Common/Models/RFQ/GetRfqConfigResponse.cs b/Src/Common/Models/RFQ/GetRfqConfigResponse.cs new file mode 100644 index 0000000..4d0c763 --- /dev/null +++ b/Src/Common/Models/RFQ/GetRfqConfigResponse.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.Models.RFQ +{ + public class GetRfqConfigResponse + { + public List? result { get; set; } // Order ID + public RfqConfig? list { get; set; } + } + + public class RfqConfig + { + public string? deskCode { get; set; } // your deskCode + public int? maxLegs { get; set; } // max legs + public int? maxLP { get; set; } // max LPs selectable + public int? maxActiveRfq { get; set; } // max unfinished RFQs + public int? rfqExpireTime { get; set; } // minutes + public int? minLimitQtySpotOrder { get; set; } // spot min qty + public int? minLimitQtyContractOrder { get; set; } // contract min qty + public int? minLimitQtyOptionOrder { get; set; } // option min qty + public List? strategyTypes { get; set; } + public List? counterparties { get; set; } + } + + public class RfqStrategyType + { + public string? strategyName { get; set; } + } + + public class RfqCounterparty + { + public string? traderName { get; set; } // quoter name + public string? deskCode { get; set; } // quoter desk code + public string? type { get; set; } // LP | null (normal) + } +} diff --git a/Src/Common/Models/RFQ/GetRfqListResponse.cs b/Src/Common/Models/RFQ/GetRfqListResponse.cs new file mode 100644 index 0000000..990c006 --- /dev/null +++ b/Src/Common/Models/RFQ/GetRfqListResponse.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.Models.RFQ +{ + public class GetRfqListResponse + { + public GetRfqListResult? result { get; set; } + } + + public class GetRfqListResult + { + public string? cursor { get; set; } + public List? list { get; set; } + } + + public class RfqItem + { + public string? rfqId { get; set; } + public string? rfqLinkId { get; set; } + public List? counterparties { get; set; } + public string? expiresAt { get; set; } + public string? strategyType { get; set; } + public string? status { get; set; } // Active | PendingFill | Canceled | Filled | Expired | Failed + public string? deskCode { get; set; } + public string? createdAt { get; set; } // ms epoch + public string? updatedAt { get; set; } // ms epoch + public List? legs { get; set; } // reused schema + } +} diff --git a/Src/Common/Models/RFQ/GetRfqPublicTradesResponse.cs b/Src/Common/Models/RFQ/GetRfqPublicTradesResponse.cs new file mode 100644 index 0000000..82797a9 --- /dev/null +++ b/Src/Common/Models/RFQ/GetRfqPublicTradesResponse.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.Models.RFQ +{ + public class GetRfqPublicTradesResponse + { + public GetRfqPublicTradesResult? result { get; set; } + } + + public class GetRfqPublicTradesResult + { + public string? cursor { get; set; } + public List? list { get; set; } + } + + public class RfqPublicTradeItem + { + public string? rfqId { get; set; } + public string? strategyType { get; set; } + public string? createdAt { get; set; } // ms epoch + public string? updatedAt { get; set; } // ms epoch + public List? legs { get; set; } + } + + public class RfqPublicTradeLeg + { + public string? category { get; set; } // linear | option | spot + public string? symbol { get; set; } + public string? side { get; set; } // Buy | Sell + public string? price { get; set; } + public string? qty { get; set; } + public string? markPrice { get; set; } + } +} diff --git a/Src/Common/Models/RFQ/GetRfqRealtimeResponse.cs b/Src/Common/Models/RFQ/GetRfqRealtimeResponse.cs new file mode 100644 index 0000000..b9ddc07 --- /dev/null +++ b/Src/Common/Models/RFQ/GetRfqRealtimeResponse.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.Models.RFQ +{ + public class GetRfqRealtimeResponse + { + public List? list { get; set; } + } + + public class RfqRealtimeItem + { + public string? rfqId { get; set; } // Inquiry ID + public string? rfqLinkId { get; set; } // Custom RFQ ID (not public) + public List? counterparties { get; set; } // bidders + public string? expiresAt { get; set; } // ms + public string? strategyType { get; set; } // inquiry label + public string? status { get; set; } // Active | PendingFill | Canceled | Filled | Expired | Failed + public string? deskCode { get; set; } // inquirer desk code (hidden if anonymous) + public string? createdAt { get; set; } // ms epoch + public string? updatedAt { get; set; } // ms epoch + public List? legs { get; set; } // combo legs + } +} diff --git a/Src/Common/Models/RFQ/GetRfqTradeListResponse.cs b/Src/Common/Models/RFQ/GetRfqTradeListResponse.cs new file mode 100644 index 0000000..257251c --- /dev/null +++ b/Src/Common/Models/RFQ/GetRfqTradeListResponse.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.Models.RFQ +{ + public class GetRfqTradeListResponse + { + public GetRfqTradeListResult? result { get; set; } + } + + public class GetRfqTradeListResult + { + public string? cursor { get; set; } + public List? list { get; set; } + } + + public class RfqTradeItem + { + public string? rfqId { get; set; } // Inquiry ID + public string? rfqLinkId { get; set; } // Custom RFQ ID (not public) + public string? quoteId { get; set; } // Executed quote ID + public string? quoteLinkId { get; set; } // Custom quote ID (not public) + public string? quoteSide { get; set; } // Buy | Sell + public string? strategyType { get; set; } // Inquiry label + public string? status { get; set; } // Filled | Failed + public string? rfqDeskCode { get; set; } // inquirer desk code (hidden if anonymous) + public string? quoteDeskCode { get; set; } // quoter desk code (hidden if anonymous) + public string? createdAt { get; set; } // ms epoch + public string? updatedAt { get; set; } // ms epoch + public List? legs { get; set; } // legs + } + + public class RfqTradeLeg + { + public string? category { get; set; } // linear | option | spot + public string? orderId { get; set; } // bybit order id + public string? symbol { get; set; } // instrument ID + public string? side { get; set; } // Buy | Sell + public string? price { get; set; } // execution price + public string? qty { get; set; } // executed quantity + public string? markPrice { get; set; } // futures markPrice / spot indexPrice / option underlying mark + public string? execFee { get; set; } // fee (base currency) + public string? execId { get; set; } // unique trade ID + public int? resultCode { get; set; } // 0 means success + public string? resultMessage { get; set; }// + public string? rejectParty { get; set; } // Taker | Maker | bybit (when rejected) + } +} diff --git a/Src/Common/Models/RFQ/RfqLeg.cs b/Src/Common/Models/RFQ/RfqLeg.cs new file mode 100644 index 0000000..49bbf98 --- /dev/null +++ b/Src/Common/Models/RFQ/RfqLeg.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.Models.RFQ +{ + public class RfqLeg + { + public string? category { get; set; } // spot | linear | option + public string? symbol { get; set; } // contract name + public string? side { get; set; } // Buy | Sell + public string? qty { get; set; } // quantity + } +} diff --git a/Src/Common/Models/RateLimit/ApiRateLimitSetItem.cs b/Src/Common/Models/RateLimit/ApiRateLimitSetItem.cs new file mode 100644 index 0000000..b9a23ef --- /dev/null +++ b/Src/Common/Models/RateLimit/ApiRateLimitSetItem.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.Models.RateLimit +{ + public class ApiRateLimitSetItem + { + public string? uids { get; set; } // Multiple UIDs separated by commas + public string? bizType { get; set; } // Business type + public int? rate { get; set; } // API rate limit per second + } +} diff --git a/Src/Common/Models/RateLimit/GetAllApiRateLimitsResponse.cs b/Src/Common/Models/RateLimit/GetAllApiRateLimitsResponse.cs new file mode 100644 index 0000000..fbd508f --- /dev/null +++ b/Src/Common/Models/RateLimit/GetAllApiRateLimitsResponse.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.Models.RateLimit +{ + public class GetAllApiRateLimitsResponse + { + public string? nextPageCursor { get; set; } + public List? list { get; set; } // reuse GetApiRateLimitItem + } + + public class GetApiRateLimitItem + { + public string? uids { get; set; } // Multiple UIDs separated by commas + public string? bizType { get; set; } // Business type + public int? rate { get; set; } // API rate limit per second + } +} diff --git a/Src/Common/Models/RateLimit/GetApiRateLimitCapResponse.cs b/Src/Common/Models/RateLimit/GetApiRateLimitCapResponse.cs new file mode 100644 index 0000000..98a3c7e --- /dev/null +++ b/Src/Common/Models/RateLimit/GetApiRateLimitCapResponse.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.Models.RateLimit +{ + public class GetApiRateLimitCapResponse + { + public List? list { get; set; } + } + + public class ApiRateLimitCapItem + { + public string? bizType { get; set; } // Business type + public int? totalRate { get; set; } // Total usage across master + subs + public int? insCap { get; set; } // Institutional-level cap per second + public int? uidCap { get; set; } // UID-level cap per second + } +} diff --git a/Src/Common/Models/RateLimit/SetApiRateLimitResponse.cs b/Src/Common/Models/RateLimit/SetApiRateLimitResponse.cs new file mode 100644 index 0000000..5a1707b --- /dev/null +++ b/Src/Common/Models/RateLimit/SetApiRateLimitResponse.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.Models.RateLimit +{ + public class SetApiRateLimitResponse + { + public List? list { get; set; } + } + + public class ApiRateLimitResultItem + { + public string? uids { get; set; } // Multiple UIDs separated by commas + public string? bizType { get; set; } // Business type + public int? rate { get; set; } // API rate limit per second + public bool? success { get; set; } // Whether request was successful + public string? msg { get; set; } // Result message + } +} diff --git a/Src/Common/Models/SpreadTrading/GetSpreadInstrumentsInfoResult.cs b/Src/Common/Models/SpreadTrading/GetSpreadInstrumentsInfoResult.cs new file mode 100644 index 0000000..dde98d2 --- /dev/null +++ b/Src/Common/Models/SpreadTrading/GetSpreadInstrumentsInfoResult.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.Models.SpreadTrading +{ + public class GetSpreadInstrumentsInfoResult + { + public List? list { get; set; } + public string? nextPageCursor { get; set; } + } + + public class SpreadInstrument + { + public string? symbol { get; set; } // Spread combination symbol name + public string? contractType { get; set; } // FundingRateArb | CarryTrade | FutureSpread | PerpBasis + public string? status { get; set; } // Trading | Settling + public string? baseCoin { get; set; } + public string? quoteCoin { get; set; } + public string? settleCoin { get; set; } + public string? tickSize { get; set; } // price tick + public string? minPrice { get; set; } + public string? maxPrice { get; set; } + public string? lotSize { get; set; } // qty precision + public string? minSize { get; set; } + public string? maxSize { get; set; } + public string? launchTime { get; set; } // ms timestamp + public string? deliveryTime { get; set; } // ms timestamp + public List? legs { get; set; } + } + + public class SpreadInstrumentLeg + { + public string? symbol { get; set; } // Leg symbol name + public string? contractType { get; set; } // LinearPerpetual | LinearFutures | Spot + } +} diff --git a/Src/Common/Models/SpreadTrading/GetSpreadOpenOrdersResult.cs b/Src/Common/Models/SpreadTrading/GetSpreadOpenOrdersResult.cs new file mode 100644 index 0000000..7ff1246 --- /dev/null +++ b/Src/Common/Models/SpreadTrading/GetSpreadOpenOrdersResult.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.Models.SpreadTrading +{ + public class GetSpreadOpenOrdersResult + { + public List? list { get; set; } + public string? nextPageCursor { get; set; } + } + + public class SpreadOpenOrder + { + public string? symbol { get; set; } // Spread combination symbol name + public string? baseCoin { get; set; } // Base coin + public string? orderType { get; set; } // Market | Limit + public string? orderLinkId { get; set; } // User customised order ID + public string? side { get; set; } // Buy | Sell + public string? timeInForce { get; set; } // GTC | FOK | IOC | PostOnly + public string? orderId { get; set; } // Spread combination order ID + public string? leavesQty { get; set; } // Remaining qty not executed + public string? orderStatus { get; set; } // New | PartiallyFilled + public string? cumExecQty { get; set; } // Cumulative executed qty + public string? price { get; set; } // Order price + public string? qty { get; set; } // Order qty + public string? createdTime { get; set; } // ms timestamp + public string? updatedTime { get; set; } // ms timestamp + } +} diff --git a/Src/Common/Models/SpreadTrading/GetSpreadOrderHistoryResult.cs b/Src/Common/Models/SpreadTrading/GetSpreadOrderHistoryResult.cs new file mode 100644 index 0000000..c28a1cf --- /dev/null +++ b/Src/Common/Models/SpreadTrading/GetSpreadOrderHistoryResult.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.Models.SpreadTrading +{ + public class GetSpreadOrderHistoryResult + { + public List? list { get; set; } + public string? nextPageCursor { get; set; } + } + + public class SpreadOrderHistoryItem + { + public string? symbol { get; set; } // Spread combination symbol name + public string? orderType { get; set; } // Market | Limit + public string? orderLinkId { get; set; } + public string? orderId { get; set; } + public string? contractType { get; set; } // FundingRateArb | CarryTrade | FutureSpread | PerpBasis + public string? cxlRejReason { get; set; } // Reject reason + public string? orderStatus { get; set; } // Rejected | Cancelled | Filled + public string? price { get; set; } + public string? orderQty { get; set; } + public string? timeInForce { get; set; } // GTC | FOK | IOC | PostOnly + public string? baseCoin { get; set; } + public string? createdAt { get; set; } // ms timestamp + public string? updatedAt { get; set; } // ms timestamp + public string? side { get; set; } // Buy | Sell + public string? leavesQty { get; set; } // Remaining qty (meaningless if cancelled) + public string? settleCoin { get; set; } + public string? cumExecQty { get; set; } + public string? qty { get; set; } + public string? leg1Symbol { get; set; } + public string? leg1ProdType { get; set; } // Futures | Spot + public string? leg1OrderId { get; set; } + public string? leg1Side { get; set; } + public string? leg2ProdType { get; set; } // Futures | Spot + public string? leg2OrderId { get; set; } + public string? leg2Symbol { get; set; } + public string? leg2Side { get; set; } // typo in docs: "orde" -> "order" + } +} diff --git a/Src/Common/Models/SpreadTrading/GetSpreadTradeHistoryResult.cs b/Src/Common/Models/SpreadTrading/GetSpreadTradeHistoryResult.cs new file mode 100644 index 0000000..3d44bcf --- /dev/null +++ b/Src/Common/Models/SpreadTrading/GetSpreadTradeHistoryResult.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.Models.SpreadTrading +{ + public class GetSpreadTradeHistoryResult + { + public List? list { get; set; } + public string? nextPageCursor { get; set; } + } + + public class SpreadTrade + { + public string? symbol { get; set; } // Spread combination symbol name + public string? orderLinkId { get; set; } // User customised order ID + public string? side { get; set; } // Buy | Sell + public string? orderId { get; set; } // Spread combination order ID + public string? execPrice { get; set; } // Combo exec price + public string? execTime { get; set; } // Combo exec timestamp (ms) + public string? execType { get; set; } // Combo exec type, Trade + public string? execQty { get; set; } // Combo exec qty + public string? execId { get; set; } // Combo exec ID + public List? legs { get; set; } // Legs execution info + } + + public class SpreadTradeLeg + { + public string? symbol { get; set; } // Leg symbol name + public string? side { get; set; } // Buy | Sell + public string? execPrice { get; set; } // Leg exec price + public string? execTime { get; set; } // Leg exec timestamp (ms) + public string? execValue { get; set; } // Leg exec value + public string? execType { get; set; } // Leg exec type + public string? category { get; set; } // linear | spot + public string? execQty { get; set; } // Leg exec qty + public string? execFee { get; set; } // Leg exec fee (deprecated for Spot) + public string? execFeeV2 { get; set; } // Leg exec fee (Spot only) + public string? feeCurrency { get; set; } // Leg fee currency + public string? execId { get; set; } // Leg exec ID + } +} diff --git a/Src/Common/Utils/BybitConstants.cs b/Src/Common/Utils/BybitConstants.cs index e4edba7..b2f9902 100644 --- a/Src/Common/Utils/BybitConstants.cs +++ b/Src/Common/Utils/BybitConstants.cs @@ -22,6 +22,11 @@ public static class BybitConstants // WebSocket private channel public const string WEBSOCKET_PRIVATE_MAINNET = "wss://stream.bybit.com/v5/private"; public const string WEBSOCKET_PRIVATE_TESTNET = "wss://stream-testnet.bybit.com/v5/private"; + + // WebSocket Spread Trading Public + public const string WEBSOCKET_SPREAD_PUBLIC_MAINNET = "wss://stream.bybit.com/v5/public/spread"; + public const string WEBSOCKET_SPREAD_PUBLIC_TESTNET = "wss://stream-testnet.bybit.com/v5/public/spread"; + #region V3 public const string V3_CONTRACT_PRIVATE = "wss://stream.bybit.com/contract/private/v3"; public const string V3_UNIFIED_PRIVATE = "wss://stream.bybit.com/unified/private/v3"; diff --git a/Src/Common/Utils/VersionInfo.cs b/Src/Common/Utils/VersionInfo.cs index 10a0f9b..76e136f 100644 --- a/Src/Common/Utils/VersionInfo.cs +++ b/Src/Common/Utils/VersionInfo.cs @@ -2,7 +2,7 @@ { public static class VersionInfo { - private static readonly string version = "1.1.3"; + private static readonly string version = "1.2.1"; public static string GetVersion { diff --git a/Src/Common/WebSocketStream/BybitSpreadTradingWebSocket.cs b/Src/Common/WebSocketStream/BybitSpreadTradingWebSocket.cs new file mode 100644 index 0000000..8c7c237 --- /dev/null +++ b/Src/Common/WebSocketStream/BybitSpreadTradingWebSocket.cs @@ -0,0 +1,28 @@ +using bybit.net.api.Websockets; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.WebSockets; +using System.Text; +using System.Threading.Tasks; + +namespace bybit.net.api.WebSocketStream +{ + internal class BybitSpreadTradingWebSocket : BybitWebSocket + { + public BybitSpreadTradingWebSocket(bool useTestNet = false, int pingIntevral = 20, int receiveBufferSize = 8192, string? apiKey = null, string? apiSecret = null, bool debugMode = false) + : base(new BybitWebSocketHandler(new ClientWebSocket()), GetStreamUrl(useTestNet), pingIntevral, receiveBufferSize, apiKey, apiSecret, debugMode) + { + } + + public BybitSpreadTradingWebSocket(IBybitWebSocketHandler handler, bool useTestNet = false, int pingIntevral = 20, int receiveBufferSize = 8192, string? apiKey = null, string? apiSecret = null, bool debugMode = false) + : base(handler, GetStreamUrl(useTestNet), pingIntevral, receiveBufferSize, apiKey, apiSecret, debugMode) + { + } + + private static string GetStreamUrl(bool useTestNet) + { + return !useTestNet ? BybitConstants.WEBSOCKET_SPREAD_PUBLIC_MAINNET : BybitConstants.WEBSOCKET_SPREAD_PUBLIC_TESTNET; + } + } +} diff --git a/Src/Common/bybit.net.api.csproj b/Src/Common/bybit.net.api.csproj index fc73afd..bdb96fa 100644 --- a/Src/Common/bybit.net.api.csproj +++ b/Src/Common/bybit.net.api.csproj @@ -25,8 +25,8 @@ Dive into a plethora of functionalities: README.md https://github.com/wuhewuhe/bybit.net.api git - 1.2.0 - 1.2.0 + 1.2.1 + 1.2.1 en Bybit Future Spot Trading Option Inverse Linear Api Crypto Rest Http Https Webscoket WSS OpenApi diff --git a/Tests/bybit.api.test/SpreadTradingServiceTest.cs b/Tests/bybit.api.test/SpreadTradingServiceTest.cs new file mode 100644 index 0000000..3e5d265 --- /dev/null +++ b/Tests/bybit.api.test/SpreadTradingServiceTest.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using bybit.net.api; +using bybit.net.api.ApiServiceImp; +using Xunit; + +namespace bybit.api.test +{ + public class SpreadTradingServiceTest + { + readonly BybitSpreadTradingService SpreadService = new( + apiKey: "", + apiSecret: "", + url: BybitConstants.HTTP_TESTNET_URL, + debugMode: true); + + #region Get Instruments Info + [Fact] + public async Task Check_GetSpreadInstrumentsInfo() + { + var resp = await SpreadService.GetSpreadInstrumentsInfo( + symbol: null, + baseCoin: null, + limit: 10, + cursor: null); + + await Console.Out.WriteLineAsync(resp); + Assert.NotNull(resp); + } + #endregion + } +}