Skip to content

Commit

Permalink
Margin mode futures API updates
Browse files Browse the repository at this point in the history
Added FuturesApi.Account.GetMarginModeAsync endpoint
Added FuturesApi.Account.SetMarginModeAsync endpoint
Added FuturesApi.Account.GetCrossMarginLeverageAsync endpoint
Added FuturesApi.Account.SetCrossMarginLeverageAsync endpoint
Added marginMode parameter to FuturesApi.Trading.PlaceOrderAsync and PlaceMultipleOrdersAsync endpoints
Added onWalletUpdate update handler to FuturesApi.SubscribeToBalanceUpdatesAsync stream
Added FuturesApi.SubscribeToMarginModeUpdatesAsync stream
Added FuturesApi.SubscribeToCrossMarginLeverageUpdatesAsync stream
Updated various order and trade response/update models with margin mode properties
Update position models with MarginMode, PositionSide, Leverage and PositionFunding properties
  • Loading branch information
JKorf committed Oct 7, 2024
1 parent c958691 commit 7dec21d
Show file tree
Hide file tree
Showing 30 changed files with 779 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
GET
/api/v2/getCrossUserLeverage
true
{"code":"200000","data":{
"symbol":"XBTUSDTM",
"leverage":"3.00"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
GET
/api/v2/position/getMarginMode
true
{"code":"200000","data":{"symbol":"XBTUSDTM","marginMode":"ISOLATED"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
POST
/api/v2/changeCrossUserLeverage
true
{"code":"200000","data":{
"symbol":"XBTUSDTM",
"leverage":"3.00"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
POST
/api/v2/position/changeMarginMode
true
{"code":"200000","data":{"symbol":"XBTUSDTM","marginMode":"ISOLATED"}}
5 changes: 5 additions & 0 deletions Kucoin.Net.UnitTests/RestRequestTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Testing;
using Kucoin.Net.Clients;
using Kucoin.Net.Enums;
using Kucoin.Net.Objects;
using Kucoin.Net.Objects.Models.Futures;
using Kucoin.Net.Objects.Models.Spot;
Expand Down Expand Up @@ -185,6 +186,10 @@ public async Task ValidateFuturesAccountCalls()
await tester.ValidateAsync(client => client.FuturesApi.Account.SetRiskLimitLevelAsync("ETHUSDT", 1), "SetRiskLimitLevel");
await tester.ValidateAsync(client => client.FuturesApi.Account.GetMaxWithdrawMarginAsync("ETHUSDT"), "GetMaxWithdrawMargin");
await tester.ValidateAsync(client => client.FuturesApi.Account.GetTradingFeeAsync("ETHUSDT"), "GetTradingFee");
await tester.ValidateAsync(client => client.FuturesApi.Account.GetMarginModeAsync("123"), "GetMarginMode");
await tester.ValidateAsync(client => client.FuturesApi.Account.SetMarginModeAsync("123", FuturesMarginMode.Cross), "SetMarginMode");
await tester.ValidateAsync(client => client.FuturesApi.Account.GetCrossMarginLeverageAsync("123"), "GetCrossMarginLeverage");
await tester.ValidateAsync(client => client.FuturesApi.Account.SetCrossMarginLeverageAsync("123", 0.1m), "SetCrossMarginLeverage");
}

[Test]
Expand Down
10 changes: 7 additions & 3 deletions Kucoin.Net.UnitTests/SocketSubscriptionTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using CryptoExchange.Net.Authentication;
using CryptoExchange.Net.Testing;
using Kucoin.Net.Clients;
using Kucoin.Net.Enums;
using Kucoin.Net.Objects;
using Kucoin.Net.Objects.Models;
using Kucoin.Net.Objects.Models.Futures;
Expand Down Expand Up @@ -68,13 +69,16 @@ public async Task ValidateFuturesSubscriptions()
await tester.ValidateAsync<KucoinStreamTransactionStatisticsUpdate>((client, handler) => client.FuturesApi.SubscribeTo24HourSnapshotUpdatesAsync("XBTUSDM", handler), "Snapshot");
await tester.ValidateAsync<KucoinStreamFuturesOrderUpdate>((client, handler) => client.FuturesApi.SubscribeToOrderUpdatesAsync("XBTUSDM", handler), "Order");
await tester.ValidateAsync<KucoinStreamFuturesStopOrderUpdate>((client, handler) => client.FuturesApi.SubscribeToStopOrderUpdatesAsync(handler), "StopOrder");
await tester.ValidateAsync<KucoinStreamOrderMarginUpdate>((client, handler) => client.FuturesApi.SubscribeToBalanceUpdatesAsync(handler, null, null), "OrderMargin");
await tester.ValidateAsync<KucoinStreamFuturesBalanceUpdate>((client, handler) => client.FuturesApi.SubscribeToBalanceUpdatesAsync(null, handler, null), "Balance");
await tester.ValidateAsync<KucoinStreamFuturesWithdrawableUpdate>((client, handler) => client.FuturesApi.SubscribeToBalanceUpdatesAsync(null, null, handler), "Withdrawable");
await tester.ValidateAsync<KucoinStreamOrderMarginUpdate>((client, handler) => client.FuturesApi.SubscribeToBalanceUpdatesAsync(handler, null, null, null), "OrderMargin");
await tester.ValidateAsync<KucoinStreamFuturesBalanceUpdate>((client, handler) => client.FuturesApi.SubscribeToBalanceUpdatesAsync(null, handler, null, null), "Balance");
await tester.ValidateAsync<KucoinStreamFuturesWithdrawableUpdate>((client, handler) => client.FuturesApi.SubscribeToBalanceUpdatesAsync(null, null, handler, null), "Withdrawable");
await tester.ValidateAsync<KucoinStreamFuturesWalletUpdate>((client, handler) => client.FuturesApi.SubscribeToBalanceUpdatesAsync(null, null, null, handler), "Wallet");
await tester.ValidateAsync<KucoinPositionUpdate>((client, handler) => client.FuturesApi.SubscribeToPositionUpdatesAsync(handler, null, null, null), "Position");
await tester.ValidateAsync<KucoinPositionMarkPriceUpdate>((client, handler) => client.FuturesApi.SubscribeToPositionUpdatesAsync(null, handler, null, null), "PositionMarkPrice");
await tester.ValidateAsync<KucoinPositionFundingSettlementUpdate>((client, handler) => client.FuturesApi.SubscribeToPositionUpdatesAsync(null, null, handler, null), "FundingSettlement");
await tester.ValidateAsync<KucoinPositionRiskAdjustResultUpdate>((client, handler) => client.FuturesApi.SubscribeToPositionUpdatesAsync(null, null, null, handler), "RiskAdjust");
await tester.ValidateAsync<Dictionary<string, FuturesMarginMode>>((client, handler) => client.FuturesApi.SubscribeToMarginModeUpdatesAsync(handler), "MarginMode");
await tester.ValidateAsync<Dictionary<string, KucoinLeverageUpdate>>((client, handler) => client.FuturesApi.SubscribeToCrossMarginLeverageUpdatesAsync(handler), "CrossMarginLeverage");
}
}
}
15 changes: 15 additions & 0 deletions Kucoin.Net.UnitTests/Subscriptions/Futures/CrossMarginLeverage.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
> {"id":"|1|","type":"subscribe","topic":"/contract/crossLeverage","privateChannel":true,"response":true}
< { "id": "|1|", "type": "ack" }
=
{
"topic": "/contract/crossLeverage",
"type": "message",
"subject": "user.config",
"userId": "6687a53f5bda600001284bf",
"channelType": "private",
"data": {
"ANTUSDTM": {
"leverage": 5
}
}
}
13 changes: 13 additions & 0 deletions Kucoin.Net.UnitTests/Subscriptions/Futures/MarginMode.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
> {"id":"|1|","type":"subscribe","topic":"/contract/marginMode","privateChannel":true,"response":true}
< { "id": "|1|", "type": "ack" }
=
{
"topic": "/contract/marginMode",
"type": "message",
"subject": "user.config",
"userId": "6687a53f5bda600012b84bf",
"channelType": "private",
"data": {
"ANTUSDTM": "CROSS"
}
}
25 changes: 25 additions & 0 deletions Kucoin.Net.UnitTests/Subscriptions/Futures/Wallet.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
> {"id":"|1|","type":"subscribe","topic":"/contractAccount/wallet","privateChannel":true,"response":true}
< { "id": "|1|", "type": "ack" }
=
{
"userId": "xbc453tg732eba53a88ggyt8c",
"topic": "/contractAccount/wallet",
"subject": "walletBalance.change",
"data": {
"currency": "XBT", // Currency Symbol
"walletBalance": "2.002", // Wallet Balance
"availableBalance": "2.002", // Available Balance
"holdBalance": "0", // Frozen Balance
"isolatedOrderMargin": "0", // Margin of the isolated margin order
"isolatedPosMargin": "0", // Margin of the isolated margin position, including isolated margin funding fees
"isolatedUnPnl": "0", // Unrealized P&L in isolated margin mode
"isolatedFundingFeeMargin":"0", // Isolated margin funding fee
"crossOrderMargin": "0", // Margin of the cross margin order
"crossPosMargin": "0", // Margin of the cross margin position
"crossUnPnl": "0", // Unrealized P&L in cross margin mode
"equity": "2.002", // Equity
"totalCrossMargin": "2.002", // Total margin under cross margin mode
"version": "9", // Version. When holding a cross margin position, the available balance may change with fluctuations in the mark price, leading to discrepancies in the available balance for the same version number.
"timestamp": "1714632069838" // Last modified time
}
}
58 changes: 58 additions & 0 deletions Kucoin.Net/Clients/FuturesApi/KucoinRestClientFuturesApiAccount.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,64 @@ public async Task<WebCallResult<KucoinTradeFee>> GetTradingFeeAsync(string symbo

#endregion

#region Get Margin Mode

/// <inheritdoc />
public async Task<WebCallResult<KucoinMarginMode>> GetMarginModeAsync(string symbol, CancellationToken ct = default)
{
var parameters = new ParameterCollection();
parameters.Add("symbol", symbol);
var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v2/position/getMarginMode", KucoinExchange.RateLimiter.FuturesRest, 2, true);
var result = await _baseClient.SendAsync<KucoinMarginMode>(request, parameters, ct).ConfigureAwait(false);
return result;
}

#endregion

#region Set Margin Mode

/// <inheritdoc />
public async Task<WebCallResult<KucoinMarginMode>> SetMarginModeAsync(string symbol, FuturesMarginMode marginMode, CancellationToken ct = default)
{
var parameters = new ParameterCollection();
parameters.Add("symbol", symbol);
parameters.AddEnum("marginMode", marginMode);
var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v2/position/changeMarginMode", KucoinExchange.RateLimiter.FuturesRest, 2, true);
var result = await _baseClient.SendAsync<KucoinMarginMode>(request, parameters, ct).ConfigureAwait(false);
return result;
}

#endregion

#region Get Cross Margin Leverage

/// <inheritdoc />
public async Task<WebCallResult<KucoinLeverage>> GetCrossMarginLeverageAsync(string symbol, CancellationToken ct = default)
{
var parameters = new ParameterCollection();
parameters.Add("symbol", symbol);
var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v2/getCrossUserLeverage", KucoinExchange.RateLimiter.FuturesRest, 2, true);
var result = await _baseClient.SendAsync<KucoinLeverage>(request, parameters, ct).ConfigureAwait(false);
return result;
}

#endregion

#region Set Cross Margin Leverage

/// <inheritdoc />
public async Task<WebCallResult<KucoinLeverage>> SetCrossMarginLeverageAsync(string symbol, decimal leverage, CancellationToken ct = default)
{
var parameters = new ParameterCollection();
parameters.Add("symbol", symbol);
parameters.Add("leverage", leverage);
var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v2/changeCrossUserLeverage", KucoinExchange.RateLimiter.FuturesRest, 2, true);
var result = await _baseClient.SendAsync<KucoinLeverage>(request, parameters, ct).ConfigureAwait(false);
return result;
}

#endregion

#region Websocket token

internal async Task<WebCallResult<KucoinToken>> GetWebsocketTokenPublicAsync(CancellationToken ct = default)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ async Task<ExchangeWebResult<SharedId>> IFuturesOrderRestClient.PlaceFuturesOrde
reduceOnly: request.ReduceOnly,
timeInForce: GetTimeInForce(request.TimeInForce),
clientOrderId: request.ClientOrderId,
marginMode: request.MarginMode == null ? null : request.MarginMode == SharedMarginMode.Isolated ? FuturesMarginMode.Isolated: FuturesMarginMode.Cross,
ct: ct).ConfigureAwait(false);

if (!result)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public async Task<WebCallResult<KucoinOrderId>> PlaceOrderAsync(
bool? forceHold = null,
string? clientOrderId = null,
SelfTradePrevention? selfTradePrevention = null,
FuturesMarginMode? marginMode = null,
CancellationToken ct = default)
{
var parameters = new ParameterCollection();
Expand All @@ -78,6 +79,7 @@ public async Task<WebCallResult<KucoinOrderId>> PlaceOrderAsync(
parameters.AddOptionalParameter("iceberg", iceberg);
parameters.AddOptionalParameter("visibleSize", visibleSize?.ToString());
parameters.AddOptionalEnum("stp", selfTradePrevention);
parameters.AddOptionalEnum("marginMode", marginMode);

var request = _definitions.GetOrCreate(HttpMethod.Post, $"api/v1/orders", KucoinExchange.RateLimiter.FuturesRest, 2, true);
return await _baseClient.SendAsync<KucoinOrderId>(request, parameters, ct).ConfigureAwait(false);
Expand Down
17 changes: 16 additions & 1 deletion Kucoin.Net/Clients/FuturesApi/KucoinSocketClientFuturesApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,10 @@ public async Task<CallResult<UpdateSubscription>> SubscribeToBalanceUpdatesAsync
Action<DataEvent<KucoinStreamOrderMarginUpdate>>? onOrderMarginUpdate = null,
Action<DataEvent<KucoinStreamFuturesBalanceUpdate>>? onBalanceUpdate = null,
Action<DataEvent<KucoinStreamFuturesWithdrawableUpdate>>? onWithdrawableUpdate = null,
Action<DataEvent<KucoinStreamFuturesWalletUpdate>>? onWalletUpdate = null,
CancellationToken ct = default)
{
var subscription = new KucoinBalanceSubscription(_logger, onOrderMarginUpdate, onBalanceUpdate, onWithdrawableUpdate);
var subscription = new KucoinBalanceSubscription(_logger, onOrderMarginUpdate, onBalanceUpdate, onWithdrawableUpdate, onWalletUpdate);
return await SubscribeAsync("futures", subscription, ct).ConfigureAwait(false);
}

Expand Down Expand Up @@ -230,6 +231,20 @@ public async Task<CallResult<UpdateSubscription>> SubscribeToPositionUpdatesAsyn
return await SubscribeAsync("futures", subscription, ct).ConfigureAwait(false);
}

/// <inheritdoc />
public async Task<CallResult<UpdateSubscription>> SubscribeToMarginModeUpdatesAsync(Action<DataEvent<Dictionary<string, FuturesMarginMode>>> onData, CancellationToken ct = default)
{
var subscription = new KucoinSubscription<Dictionary<string, FuturesMarginMode>>(_logger, $"/contract/marginMode", null, onData, true);
return await SubscribeAsync("futures", subscription, ct).ConfigureAwait(false);
}

/// <inheritdoc />
public async Task<CallResult<UpdateSubscription>> SubscribeToCrossMarginLeverageUpdatesAsync(Action<DataEvent<Dictionary<string, KucoinLeverageUpdate>>> onData, CancellationToken ct = default)
{
var subscription = new KucoinSubscription<Dictionary<string, KucoinLeverageUpdate>>(_logger, $"/contract/crossLeverage", null, onData, true);
return await SubscribeAsync("futures", subscription, ct).ConfigureAwait(false);
}

/// <inheritdoc />
protected override async Task<CallResult<string?>> GetConnectionUrlAsync(string address, bool authenticated)
{
Expand Down
21 changes: 21 additions & 0 deletions Kucoin.Net/Enums/FuturesMarginMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using CryptoExchange.Net.Attributes;

namespace Kucoin.Net.Enums
{
/// <summary>
/// Margin mode
/// </summary>
public enum FuturesMarginMode
{
/// <summary>
/// Cross margin
/// </summary>
[Map("CROSS")]
Cross,
/// <summary>
/// Isolated margin
/// </summary>
[Map("ISOLATED")]
Isolated,
}
}
29 changes: 29 additions & 0 deletions Kucoin.Net/Enums/PositionSide.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using CryptoExchange.Net.Attributes;
using System;
using System.Collections.Generic;
using System.Text;

namespace Kucoin.Net.Enums
{
/// <summary>
/// Position side
/// </summary>
public enum PositionSide
{
/// <summary>
/// Both (One way position mode)
/// </summary>
[Map("BOTH")]
Both,
/// <summary>
/// Long
/// </summary>
[Map("LONG")]
Long,
/// <summary>
/// Short
/// </summary>
[Map("SHORT")]
Short
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -196,5 +196,40 @@ public interface IKucoinRestClientFuturesApiAccount
/// <param name="ct">Cancellation token</param>
/// <returns></returns>
Task<WebCallResult<KucoinTradeFee>> GetTradingFeeAsync(string symbol, CancellationToken ct = default);

/// <summary>
/// Get the current margin mode for a symbol
/// <para><a href="https://www.kucoin.com/docs/rest/futures-trading/positions/get-margin-mode" /></para>
/// </summary>
/// <param name="symbol">The symbol, for example `XBTUSDTM`</param>
/// <param name="ct">Cancellation token</param>
Task<WebCallResult<KucoinMarginMode>> GetMarginModeAsync(string symbol, CancellationToken ct = default);

/// <summary>
/// Set the margin mode for a symbol
/// <para><a href="https://www.kucoin.com/docs/rest/futures-trading/positions/modify-margin-mode" /></para>
/// </summary>
/// <param name="symbol">The symbol, for example `XBTUSDTM`</param>
/// <param name="marginMode">The new margin mode</param>
/// <param name="ct">Cancellation token</param>
Task<WebCallResult<KucoinMarginMode>> SetMarginModeAsync(string symbol, FuturesMarginMode marginMode, CancellationToken ct = default);

/// <summary>
/// Get the current cross margin leverage setting
/// <para><a href="https://www.kucoin.com/docs/rest/futures-trading/positions/get-cross-margin-leverage" /></para>
/// </summary>
/// <param name="symbol">The symbol, for example `XBTUSDTM`</param>
/// <param name="ct">Cancellation token</param>
Task<WebCallResult<KucoinLeverage>> GetCrossMarginLeverageAsync(string symbol, CancellationToken ct = default);

/// <summary>
/// Set a new cross margin leverage value
/// <para><a href="https://www.kucoin.com/docs/rest/futures-trading/positions/modify-cross-margin-leverage" /></para>
/// </summary>
/// <param name="symbol">The symbol, for example `XBTUSDTM`</param>
/// <param name="leverage">The leverage</param>
/// <param name="ct">Cancellation token</param>
Task<WebCallResult<KucoinLeverage>> SetCrossMarginLeverageAsync(string symbol, decimal leverage, CancellationToken ct = default);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public interface IKucoinRestClientFuturesApiTrading
/// <param name="forceHold">A mark to forcely hold the funds for an order, even though it's an order to reduce the position size. This helps the order stay on the order book and not get canceled when the position size changes. Set to false by default</param>
/// <param name="selfTradePrevention">Self Trade Prevention mode</param>
/// <param name="clientOrderId">Client order id</param>
/// <param name="marginMode">Margin mode, defaults to Isolated</param>
/// <param name="ct">Cancellation token</param>
/// <returns>Order details</returns>
Task<WebCallResult<KucoinOrderId>> PlaceOrderAsync(
Expand All @@ -62,6 +63,7 @@ Task<WebCallResult<KucoinOrderId>> PlaceOrderAsync(
bool? forceHold = null,
string? clientOrderId = null,
SelfTradePrevention? selfTradePrevention = null,
FuturesMarginMode? marginMode = null,
CancellationToken ct = default);

/// <summary>
Expand Down
Loading

0 comments on commit 7dec21d

Please sign in to comment.