diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/CancelWithdrawal.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/CancelWithdrawal.txt
new file mode 100644
index 00000000..5c64df66
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/CancelWithdrawal.txt
@@ -0,0 +1,6 @@
+POST
+/v1/dw/withdraw-virtual/1/cancel
+true
+{
+ "data": 700
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/GetAccountHistory.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetAccountHistory.txt
new file mode 100644
index 00000000..e224004f
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetAccountHistory.txt
@@ -0,0 +1,29 @@
+GET
+/v1/account/history
+true
+{
+ "status": "ok",
+ "data": [
+ {
+ "account-id": 10000001,
+ "currency": "usdt",
+ "record-id": 359044707902783800,
+ "transact-amt": "-10.000000000000000000",
+ "transact-type": "other-types",
+ "avail-balance": "81.850043797676510303",
+ "acct-balance": "97.010043797676510318",
+ "transact-time": 1629882096557
+ },
+ {
+ "account-id": 10000001,
+ "currency": "usdt",
+ "record-id": 359044690723242100,
+ "transact-amt": "-10.000000000000000000",
+ "transact-type": "transfer",
+ "avail-balance": "81.850043797676510303",
+ "acct-balance": "87.010043797676510318",
+ "transact-time": 1629882096569
+ }
+ ],
+ "next-id": 47996522235
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/GetAccountLedger.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetAccountLedger.txt
new file mode 100644
index 00000000..8bf2f8fa
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetAccountLedger.txt
@@ -0,0 +1,33 @@
+GET
+/v2/account/ledger
+true
+{
+ "code": 200,
+ "message": "success",
+ "data": [
+ {
+ "accountId": 10000001,
+ "currency": "usdt",
+ "transactAmt": 10,
+ "transactType": "transfer",
+ "transferType": "margin-transfer-out",
+ "transactId": 0,
+ "transactTime": 1629882331066,
+ "transferer": 28483123,
+ "transferee": 13496526
+ },
+ {
+ "accountId": 10000001,
+ "currency": "usdt",
+ "transactAmt": -10,
+ "transactType": "transfer",
+ "transferType": "margin-transfer-in",
+ "transactId": 0,
+ "transactTime": 1629882096562,
+ "transferer": 13496526,
+ "transferee": 28483123
+ }
+ ],
+ "nextId": 1624316679,
+ "ok": true
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/GetAccounts.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetAccounts.txt
new file mode 100644
index 00000000..9d55381e
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetAccounts.txt
@@ -0,0 +1,26 @@
+GET
+/v1/account/accounts
+true
+{
+ "status": "ok",
+ "data": [
+ {
+ "id": 10000001,
+ "type": "spot",
+ "subtype": "",
+ "state": "working"
+ },
+ {
+ "id": 10000002,
+ "type": "otc",
+ "subtype": "",
+ "state": "working"
+ },
+ {
+ "id": 10000003,
+ "type": "point",
+ "subtype": "",
+ "state": "working"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/GetApiKeyInfo.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetApiKeyInfo.txt
new file mode 100644
index 00000000..33a8dfee
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetApiKeyInfo.txt
@@ -0,0 +1,30 @@
+GET
+/v2/user/api-key
+true
+{
+ "code": 200,
+ "message": "success",
+ "data": [
+ {
+ "accessKey": "160bb889-b7XXXXbe-e0XXXXf5-ghxertfvbf",
+ "status": "normal",
+ "note": "host",
+ "permission": "trade,readOnly",
+ "ipAddresses": "192.168.0.1,192.168.1.1",
+ "validDays": -1,
+ "createTime": 1615192704000,
+ "updateTime": 1623030338000
+ },
+ {
+ "accessKey": "5000d371-edXXXXf5tf-40XXXX8b-ab8e5",
+ "status": "normal",
+ "note": "host two",
+ "permission": "readOnly,trade,withdraw",
+ "ipAddresses": "",
+ "validDays": 7,
+ "createTime": 1623158078000,
+ "updateTime": 1629875976000
+ }
+ ],
+ "ok": true
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/GetAssetValuation.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetAssetValuation.txt
new file mode 100644
index 00000000..d9dd2dc3
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetAssetValuation.txt
@@ -0,0 +1,11 @@
+GET
+/v2/account/asset-valuation
+true
+{
+ "code": 200,
+ "data": {
+ "balance": "34.75",
+ "timestamp": 1594901254363
+ },
+ "ok": true
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/GetBalances.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetBalances.txt
new file mode 100644
index 00000000..a9b7ba3c
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetBalances.txt
@@ -0,0 +1,37 @@
+GET
+/v1/account/accounts/1/balance
+true
+{
+ "status": "ok",
+ "data": {
+ "id": 1000001,
+ "type": "spot",
+ "state": "working",
+ "list": [
+ {
+ "currency": "usdt",
+ "type": "trade",
+ "balance": "91.850043797676510303",
+ "debt": "invalid",
+ "available": "invalid",
+ "seq-num": "477"
+ },
+ {
+ "currency": "usdt",
+ "type": "frozen",
+ "balance": "5.160000000000000015",
+ "debt": "invalid",
+ "available": "invalid",
+ "seq-num": "477"
+ },
+ {
+ "currency": "poly",
+ "type": "trade",
+ "balance": "147.928994082840236",
+ "debt": "invalid",
+ "available": "invalid",
+ "seq-num": "2"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/GetDeductAssets.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetDeductAssets.txt
new file mode 100644
index 00000000..bf9b062d
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetDeductAssets.txt
@@ -0,0 +1,10 @@
+GET
+/v1/account/overview/info
+true
+{
+ "code": 200,
+ "data": {
+ "currency": "HTX,TRX"
+ },
+ "success": true
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/GetDepositAddresses.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetDepositAddresses.txt
new file mode 100644
index 00000000..a63b8540
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetDepositAddresses.txt
@@ -0,0 +1,29 @@
+GET
+/v2/account/deposit/address
+true
+{
+ "code": 200,
+ "data": [
+ {
+ "userId": 12345678,
+ "currency": "btc",
+ "address": "0xd476b0d77583fbda5180039f1f513b750cb4f527",
+ "addressTag": "",
+ "chain": "hbtc"
+ },
+ {
+ "userId": 12345678,
+ "currency": "btc",
+ "address": "16egzDeZiVDJ4D44UbWKN6snLYFjS1aEmJ",
+ "addressTag": "",
+ "chain": "btc"
+ },
+ {
+ "userId": 12345678,
+ "currency": "btc",
+ "address": "0xd476b0d77583fbda5180039f1f513b750cb4f527",
+ "addressTag": "",
+ "chain": "hrc20btc"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/GetPlatformValuation.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetPlatformValuation.txt
new file mode 100644
index 00000000..9dcafc4f
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetPlatformValuation.txt
@@ -0,0 +1,24 @@
+GET
+/v2/account/valuation
+true
+{
+ "code": 200,
+ "data": {
+ "updated": {
+ "success": true,
+ "time": 1629916724000
+ },
+ "todayProfitRate": "0.004638293764657609",
+ "totalBalance": "0.06276321",
+ "todayProfit": "0.00028977",
+ "profitAccountBalanceList": [
+ {
+ "distributionType": "11",
+ "balance": 0.05728808,
+ "success": true,
+ "accountBalance": "0.05728808"
+ }
+ ]
+ },
+ "success": true
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/GetPointBalance.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetPointBalance.txt
new file mode 100644
index 00000000..411f0847
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetPointBalance.txt
@@ -0,0 +1,19 @@
+GET
+/v2/point/account
+true
+{
+ "code": 200,
+ "data": {
+ "accountId": "14403739",
+ "groupIds": [
+ {
+ "groupId": 26,
+ "expiryDate": 1594396800000,
+ "remainAmt": "0.3"
+ }
+ ],
+ "acctBalance": "0.30000000",
+ "accountStatus": "working"
+ },
+ "success": true
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/GetTradingFees.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetTradingFees.txt
new file mode 100644
index 00000000..adfed261
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetTradingFees.txt
@@ -0,0 +1,30 @@
+GET
+/v2/reference/transact-fee-rate
+true
+{
+ "code": 200,
+ "data": [
+ {
+ "symbol": "btcusdt",
+ "actualMakerRate": "0.002",
+ "actualTakerRate": "0.002",
+ "takerFeeRate": "0.002",
+ "makerFeeRate": "0.002"
+ },
+ {
+ "symbol": "apnusdt",
+ "actualMakerRate": "0.002",
+ "actualTakerRate": "0.002",
+ "takerFeeRate": "0.002",
+ "makerFeeRate": "0.002"
+ },
+ {
+ "symbol": "htusdt",
+ "actualMakerRate": "0.002",
+ "actualTakerRate": "0.002",
+ "takerFeeRate": "0.002",
+ "makerFeeRate": "0.002"
+ }
+ ],
+ "success": true
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/GetUserDeductionInfo.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetUserDeductionInfo.txt
new file mode 100644
index 00000000..d9f0ca48
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetUserDeductionInfo.txt
@@ -0,0 +1,12 @@
+GET
+/v1/account/switch/user/info
+true
+{
+ "code": 200,
+ "data": {
+ "pointSwitch": "1",
+ "htxSwitch": "0",
+ "currency": "HTX"
+ },
+ "success": true
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/GetUserId.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetUserId.txt
new file mode 100644
index 00000000..3f6050ba
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetUserId.txt
@@ -0,0 +1,7 @@
+GET
+/v2/user/uid
+true
+{
+ "code": 200,
+ "data": 63628520
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/GetWithdrawDeposit.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetWithdrawDeposit.txt
new file mode 100644
index 00000000..33a7ae5b
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetWithdrawDeposit.txt
@@ -0,0 +1,40 @@
+GET
+/v1/query/deposit-withdraw
+true
+{
+ "status": "ok",
+ "data": [
+ {
+ "id": 45182894,
+ "type": "withdraw",
+ "sub-type": "FAST",
+ "currency": "usdt",
+ "chain": "trc20usdt",
+ "tx-hash": "",
+ "amount": 400,
+ "from-addr-tag": "",
+ "address": "TRwkUYHWgUh23jbKpgTcYHgE9CcBzhGno9",
+ "address-tag": "",
+ "fee": 0,
+ "state": "confirmed",
+ "created-at": 1612261330443,
+ "updated-at": 1612261389250
+ },
+ {
+ "id": 61003926,
+ "type": "withdraw",
+ "sub-type": "FAST",
+ "currency": "usdt",
+ "chain": "trc20usdt",
+ "tx-hash": "",
+ "amount": 2,
+ "from-addr-tag": "",
+ "address": "TYGvZSD1duPctGaMPSP12Fy8BrQMu2KCdp",
+ "address-tag": "",
+ "fee": 0,
+ "state": "confirmed",
+ "created-at": 1621416907639,
+ "updated-at": 1621416907788
+ }
+ ]
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/GetWithdrawalAddresses.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetWithdrawalAddresses.txt
new file mode 100644
index 00000000..af4a6958
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetWithdrawalAddresses.txt
@@ -0,0 +1,16 @@
+GET
+/v2/account/withdraw/address
+true
+{
+ "code": 200,
+ "data": [
+ {
+ "currency": "usdt",
+ "chain": "hrc20usdt",
+ "note": "tom",
+ "addressTag": "",
+ "address": "0x3b994f25c4c25e99d4d26364ffc014cce64600ca"
+ }
+ ],
+ "next-id": 30137790
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/GetWithdrawalByClientOrderId.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetWithdrawalByClientOrderId.txt
new file mode 100644
index 00000000..dc049a99
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetWithdrawalByClientOrderId.txt
@@ -0,0 +1,23 @@
+GET
+/v1/query/withdraw/client-order-id
+true
+{
+ "status": "ok",
+ "data": {
+ "id": 101123262,
+ "client-order-id": "1113",
+ "type": "withdraw",
+ "sub-type": "FAST",
+ "currency": "usdt",
+ "chain": "usdt",
+ "tx-hash": "",
+ "amount": 1.2,
+ "from-addr-tag": "",
+ "address": "1PL24EbWrNNrnMKw1cxAHPsebUz7DdhWTx",
+ "address-tag": "",
+ "fee": 0,
+ "state": "confirmed",
+ "created-at": 1637758163686,
+ "updated-at": 1637758251559
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/GetWithdrawalQuotas.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetWithdrawalQuotas.txt
new file mode 100644
index 00000000..cc4e8361
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/GetWithdrawalQuotas.txt
@@ -0,0 +1,51 @@
+GET
+/v2/account/withdraw/quota
+true
+{
+ "code": 200,
+ "data": {
+ "currency": "usdt",
+ "chains": [
+ {
+ "chain": "hrc20usdt",
+ "maxWithdrawAmt": "2000000.000000000000000000",
+ "withdrawQuotaPerDay": "4845303.99999991",
+ "remainWithdrawQuotaPerDay": "4845303.99999991",
+ "withdrawQuotaPerYear": "-1",
+ "remainWithdrawQuotaPerYear": "-1",
+ "withdrawQuotaTotal": "-1",
+ "remainWithdrawQuotaTotal": "-1"
+ },
+ {
+ "chain": "trc20usdt",
+ "maxWithdrawAmt": "1000000.000000000000000000",
+ "withdrawQuotaPerDay": "4845303.99999991",
+ "remainWithdrawQuotaPerDay": "4845303.99999991",
+ "withdrawQuotaPerYear": "-1",
+ "remainWithdrawQuotaPerYear": "-1",
+ "withdrawQuotaTotal": "-1",
+ "remainWithdrawQuotaTotal": "-1"
+ },
+ {
+ "chain": "usdt",
+ "maxWithdrawAmt": "600000.000000000000000000",
+ "withdrawQuotaPerDay": "4845303.99999991",
+ "remainWithdrawQuotaPerDay": "4845303.99999991",
+ "withdrawQuotaPerYear": "-1",
+ "remainWithdrawQuotaPerYear": "-1",
+ "withdrawQuotaTotal": "-1",
+ "remainWithdrawQuotaTotal": "-1"
+ },
+ {
+ "chain": "usdterc20",
+ "maxWithdrawAmt": "1000000.000000000000000000",
+ "withdrawQuotaPerDay": "4845303.99999991",
+ "remainWithdrawQuotaPerDay": "4845303.99999991",
+ "withdrawQuotaPerYear": "-1",
+ "remainWithdrawQuotaPerYear": "-1",
+ "withdrawQuotaTotal": "-1",
+ "remainWithdrawQuotaTotal": "-1"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/InternalTransfer.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/InternalTransfer.txt
new file mode 100644
index 00000000..25a141b7
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/InternalTransfer.txt
@@ -0,0 +1,10 @@
+POST
+/v1/account/transfer
+true
+{
+ "status": "ok",
+ "data": {
+ "transact-id": 220521190,
+ "transact-time": 1590662591832
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/SetDeductionSwitch.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/SetDeductionSwitch.txt
new file mode 100644
index 00000000..2e0aaf80
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/SetDeductionSwitch.txt
@@ -0,0 +1,8 @@
+POST
+/v1/account/fee/switch
+true
+{
+ "code": 200,
+ "data": {},
+ "success": true
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/Transfer.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/Transfer.txt
new file mode 100644
index 00000000..777e5a38
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/Transfer.txt
@@ -0,0 +1,9 @@
+POST
+/v2/account/transfer
+true
+{
+ "code": 200,
+ "data": 176104252,
+ "message": "Succeed",
+ "success": true
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/TransferPoints.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/TransferPoints.txt
new file mode 100644
index 00000000..2522732e
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/TransferPoints.txt
@@ -0,0 +1,11 @@
+POST
+/v2/point/transfer
+true
+{
+ "code": 200,
+ "data": {
+ "transactId": "74",
+ "transactTime": 1594370136458
+ },
+ "success": true
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/Account/Withdraw.txt b/HTX.Net.UnitTests/Endpoints/Spot/Account/Withdraw.txt
new file mode 100644
index 00000000..5825af68
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/Account/Withdraw.txt
@@ -0,0 +1,6 @@
+POST
+/v1/dw/withdraw/api/create
+true
+{
+ "data": 700
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Endpoints/Spot/ExchangeData/GetAssets.txt b/HTX.Net.UnitTests/Endpoints/Spot/ExchangeData/GetAssets.txt
new file mode 100644
index 00000000..96ffed0d
--- /dev/null
+++ b/HTX.Net.UnitTests/Endpoints/Spot/ExchangeData/GetAssets.txt
@@ -0,0 +1,38 @@
+GET
+/v2/settings/common/currencies
+false
+{
+ "status": "ok",
+ "data": [
+ {
+ "tags": "",
+ "cawt": false,
+ "fc": 12,
+ "sc": 12,
+ "dma": "1",
+ "wma": "10",
+ "ft": "eth",
+ "whe": false,
+ "cd": false,
+ "qc": true,
+ "sp": "8",
+ "wp": 6,
+ "fn": "Tether USDT",
+ "at": 1,
+ "cc": "usdt",
+ "v": true,
+ "de": true,
+ "wed": true,
+ "w": 10006,
+ "state": "online",
+ "dn": "USDT",
+ "dd": "Please don’t deposit any other digital assets except USDT to the above address. Otherwise, you may lose your assets permanently. !>______
-
+
diff --git a/Huobi.Net.UnitTests/HuobiClientTests.cs b/HTX.Net.UnitTests/HTXRestClientTests.cs
similarity index 79%
rename from Huobi.Net.UnitTests/HuobiClientTests.cs
rename to HTX.Net.UnitTests/HTXRestClientTests.cs
index ab59619a..e603b857 100644
--- a/Huobi.Net.UnitTests/HuobiClientTests.cs
+++ b/HTX.Net.UnitTests/HTXRestClientTests.cs
@@ -1,27 +1,19 @@
-using Huobi.Net.UnitTests.TestImplementations;
-using Newtonsoft.Json;
+using HTX.Net.UnitTests.TestImplementations;
using NUnit.Framework;
using System;
-using System.Linq;
using System.Threading.Tasks;
-using System.Reflection;
-using System.Diagnostics;
-using CryptoExchange.Net.Objects;
-using Huobi.Net.Clients;
-using Huobi.Net.Clients.SpotApi;
-using Huobi.Net.ExtensionMethods;
-using CryptoExchange.Net.Objects.Sockets;
+using HTX.Net.Clients;
using NUnit.Framework.Legacy;
using CryptoExchange.Net.Authentication;
using CryptoExchange.Net.Clients;
using System.Net.Http;
using System.Collections.Generic;
-using CryptoExchange.Net.Converters.JsonNet;
+using System.Text.Json;
-namespace Huobi.Net.UnitTests
+namespace HTX.Net.UnitTests
{
[TestFixture]
- public class HuobiClientTests
+ public class HTXRestClientTests
{
[TestCase]
public async Task ReceivingErrorResponse_Should_FailCall()
@@ -55,17 +47,17 @@ public async Task ReceivingHttpErrorResponse_Should_FailCall()
public string SerializeExpected(T data, bool tick)
{
- return $"{{\"status\": \"ok\", {(tick ? "tick" : "data")}: {JsonConvert.SerializeObject(data)}}}";
+ return $"{{\"status\": \"ok\", {(tick ? "tick" : "data")}: {JsonSerializer.Serialize(data)}}}";
}
[Test]
public void CheckSignatureExample()
{
- var authProvider = new HuobiAuthenticationProvider(
+ var authProvider = new HTXAuthenticationProvider(
new ApiCredentials("e2xxxxxx-99xxxxxx-84xxxxxx-7xxxx", "XXXXXXXXXX"),
false
);
- var client = (RestApiClient)new HuobiRestClient().SpotApi;
+ var client = (RestApiClient)new HTXRestClient().SpotApi;
CryptoExchange.Net.Testing.TestHelpers.CheckSignature(
client,
@@ -88,8 +80,8 @@ public void CheckSignatureExample()
[Test]
public void CheckInterfaces()
{
- CryptoExchange.Net.Testing.TestHelpers.CheckForMissingRestInterfaces();
- CryptoExchange.Net.Testing.TestHelpers.CheckForMissingSocketInterfaces();
+ CryptoExchange.Net.Testing.TestHelpers.CheckForMissingRestInterfaces();
+ CryptoExchange.Net.Testing.TestHelpers.CheckForMissingSocketInterfaces();
}
}
}
diff --git a/HTX.Net.UnitTests/HTXRestIntegrationTests.cs b/HTX.Net.UnitTests/HTXRestIntegrationTests.cs
new file mode 100644
index 00000000..2592bfa1
--- /dev/null
+++ b/HTX.Net.UnitTests/HTXRestIntegrationTests.cs
@@ -0,0 +1,179 @@
+using HTX.Net.Clients;
+using HTX.Net.Objects;
+using CryptoExchange.Net.Authentication;
+using CryptoExchange.Net.Testing;
+using Microsoft.Extensions.Logging;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using HTX.Net.Enums;
+
+namespace HTX.Net.UnitTests
+{
+ [NonParallelizable]
+ internal class HTXRestIntegrationTests : RestIntergrationTest
+ {
+ public override bool Run { get; set; } = true;
+
+ public HTXRestIntegrationTests()
+ {
+ }
+
+ public override HTXRestClient GetClient(ILoggerFactory loggerFactory)
+ {
+ var key = Environment.GetEnvironmentVariable("APIKEY");
+ var sec = Environment.GetEnvironmentVariable("APISECRET");
+
+ key = "1c8706b1-ed2htwf5tf-c1f359fd-2559d";
+ sec = "20c4ff8b-7c9a2297-d231b3a4-2f8b5";
+
+ Authenticated = key != null && sec != null;
+ return new HTXRestClient(null, loggerFactory, opts =>
+ {
+ opts.OutputOriginalData = true;
+ opts.ApiCredentials = Authenticated ? new ApiCredentials(key, sec) : null;
+ });
+ }
+
+ [Test]
+ public async Task TestErrorResponseParsing()
+ {
+ if (!ShouldRun())
+ return;
+
+ var result = await CreateClient().SpotApi.ExchangeData.GetKlinesAsync("TSTTST", Enums.KlineInterval.OneDay, default);
+
+ Assert.That(result.Success, Is.False);
+ Assert.That(result.Error.Message, Contains.Substring("invalid-parameter"));
+ }
+
+ [Test]
+ public async Task TestSpotApiAccount()
+ {
+ await RunAndCheckResult(client => client.SpotApi.Account.GetAccountsAsync(default), true);
+ await RunAndCheckResult(client => client.SpotApi.Account.GetPlatformValuationAsync(default, default, default), true);
+ await RunAndCheckResult(client => client.SpotApi.Account.GetAssetValuationAsync(Enums.AccountType.Spot, default, default, default), true);
+ await RunAndCheckResult(client => client.SpotApi.Account.GetPointBalanceAsync(default, default), true);
+ await RunAndCheckResult(client => client.SpotApi.Account.GetUserDeductionInfoAsync(default), true);
+ await RunAndCheckResult(client => client.SpotApi.Account.GetDeductAssetsAsync(default), true);
+ await RunAndCheckResult(client => client.SpotApi.Account.GetWithdrawalQuotasAsync("BTC", default), true);
+ await RunAndCheckResult(client => client.SpotApi.Account.GetWithdrawalAddressesAsync("ETH", default, default, default, default, default), true);
+ await RunAndCheckResult(client => client.SpotApi.Account.GetWithdrawDepositHistoryAsync(default, default, default, default, default, default), true);
+ await RunAndCheckResult(client => client.SpotApi.Account.GetTradingFeesAsync(new[] { "ETHUSDT" }, default), true);
+ await RunAndCheckResult(client => client.SpotApi.Account.GetUserIdAsync(default), true);
+ }
+
+ [Test]
+ public async Task TestSpotApiExchangeData()
+ {
+ await RunAndCheckResult(client => client.SpotApi.ExchangeData.GetSystemStatusAsync(default), false);
+ await RunAndCheckResult(client => client.SpotApi.ExchangeData.GetSymbolStatusAsync(default), false);
+ await RunAndCheckResult(client => client.SpotApi.ExchangeData.GetSymbolsAsync(default), false);
+ await RunAndCheckResult(client => client.SpotApi.ExchangeData.GetAssetsAsync(default), false);
+ await RunAndCheckResult(client => client.SpotApi.ExchangeData.GetSymbolConfigAsync(default, default), false);
+ await RunAndCheckResult(client => client.SpotApi.ExchangeData.GetAssetsAndNetworksAsync(default, default), false);
+ await RunAndCheckResult(client => client.SpotApi.ExchangeData.GetServerTimeAsync(default), false);
+ await RunAndCheckResult(client => client.SpotApi.ExchangeData.GetKlinesAsync("ETHUSDT", Enums.KlineInterval.OneDay, default, default), false);
+ await RunAndCheckResult(client => client.SpotApi.ExchangeData.GetTickerAsync("ETHUSDT", default), false);
+ await RunAndCheckResult(client => client.SpotApi.ExchangeData.GetTickersAsync(default), false);
+ await RunAndCheckResult(client => client.SpotApi.ExchangeData.GetOrderBookAsync("ETHUSDT", 0, default, default), false);
+ await RunAndCheckResult(client => client.SpotApi.ExchangeData.GetLastTradeAsync("ETHUSDT", default), false);
+ await RunAndCheckResult(client => client.SpotApi.ExchangeData.GetTradeHistoryAsync("ETHUSDT", default, default), false);
+ await RunAndCheckResult(client => client.SpotApi.ExchangeData.GetSymbolDetails24HAsync("ETHUSDT", default), false);
+ }
+
+ [Test]
+ public async Task TestSpotApiTrading()
+ {
+ await RunAndCheckResult(client => client.SpotApi.Trading.GetOpenOrdersAsync(default, default, default, default, default), true);
+ await RunAndCheckResult(client => client.SpotApi.Trading.GetClosedOrdersAsync("ETHUSDT", default, default, default, default, default, default, default, default), true);
+ await RunAndCheckResult(client => client.SpotApi.Trading.GetHistoricalOrdersAsync("ETHUSDT", default, default, default, default, default), true);
+ await RunAndCheckResult(client => client.SpotApi.Trading.GetUserTradesAsync("ETHUSDT", default, default, default, default, default, default, default), true);
+ await RunAndCheckResult(client => client.SpotApi.Trading.GetOpenConditionalOrdersAsync(default, default, default, default, default, default, default, default), true);
+ await RunAndCheckResult(client => client.SpotApi.Trading.GetClosedConditionalOrdersAsync("ETHUSDT", Enums.ConditionalOrderStatus.Triggered, default, default, default, default, default, default, default, default, default), true);
+ }
+
+ [Test]
+ public async Task TestUsdtFuturesApiAccount()
+ {
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Account.GetAssetValuationAsync(default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Account.GetIsolatedMarginAccountInfoAsync(default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Account.GetCrossMarginAccountInfoAsync(default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Account.GetIsolatedMarginPositionsAsync(default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Account.GetCrossMarginPositionsAsync(default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Account.GetIsolatedMarginAssetsAndPositionsAsync("ETH-USDT", default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Account.GetCrossMarginAssetsAndPositionsAsync("USDT", default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Account.GetFinancialRecordsAsync("USDT", default, default, default, default, default, default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Account.GetIsolatedMarginAvailableLeverageAsync(default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Account.GetCrossMarginAvailableLeverageAsync(default, default, default, default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Account.GetOrderLimitsAsync(Enums.OrderPriceType.BestOffer, "ETH-USDT", default, default, default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Account.GetTradingFeesAsync(default, default, default, default, default), true);
+ }
+
+ [Test]
+ public async Task TestUsdtFuturesApiExchangeData()
+ {
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetServerTimeAsync(default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetFundingRateAsync("ETH-USDT", default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetFundingRatesAsync("ETH-USDT", default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetHistoricalFundingRatesAsync("ETH-USDT", default, default, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetLiquidationOrdersAsync("ETH-USDT", Enums.LiquidationTradeType.FullyFilledLiquidationOrders, default, default, default, default, default, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetHistoricalSettlementRecordsAsync("ETH-USDT", default, default, default, default, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetTopTraderAccountSentimentAsync("ETH-USDT", Enums.Period.OneDay, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetTopTraderPositionSentimentAsync("ETH-USDT", Enums.Period.OneDay, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetIsolatedMarginStatusAsync("ETH-USDT", default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetCrossTieredMarginInfoAsync("ETH-USDT", default, default, default, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetIsolatedMarginTieredInfoAsync("ETH-USDT", default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetEstimatedSettlementPriceAsync("ETH-USDT", default, default, default, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetIsolatedMarginAdjustFactorInfoAsync("ETH-USDT", default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetCrossMarginAdjustFactorInfoAsync("ETH-USDT", default, default, default, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetInsuranceFundHistoryAsync("ETH-USDT", default, default, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetSwapRiskInfoAsync("ETH-USDT", default, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetSwapPriceLimitationAsync("ETH-USDT", default, default, default, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetSwapOpenInterestAsync("ETH-USDT", default, default, default, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetContractsAsync(default, default, default, default, default, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetSwapIndexPriceAsync(default, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetContractElementsAsync("ETH-USDT", default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetOrderBookAsync("ETH-USDT", default, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetBookTickerAsync(default, default, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetKlinesAsync("ETH-USDT", Enums.KlineInterval.OneDay, default, default, default, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetMarkPriceKlinesAsync("ETH-USDT", Enums.KlineInterval.OneDay, 100, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetTickerAsync("ETH-USDT", default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetTickersAsync(default, default, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetLastTradesAsync("ETH-USDT", default, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetRecentTradesAsync("ETH-USDT", 10, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetOpenInterestHistoryAsync(Enums.InterestPeriod.OneDay, Enums.Unit.Cont, "ETH-USDT", default ,default, default, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetPremiumIndexKlinesAsync("ETH-USDT", Enums.KlineInterval.OneDay, 10, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetEstimatedFundingRateKlinesAsync("ETH-USDT", Enums.KlineInterval.OneDay, 10, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetBasisDataAsync("ETH-USDT", Enums.KlineInterval.OneDay, 10 , default, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetCrossMarginTradeStatusAsync("ETH-USDT", default ,default, default, default), false);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.ExchangeData.GetCrossMarginTransferStatusAsync("USDT", default), false);
+ }
+
+ [Test]
+ public async Task TestUsdtFuturesApiTrading()
+ {
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Trading.GetIsolatedMarginOpenOrdersAsync("ETH-USDT", default, default, default, default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Trading.GetCrossMarginOpenOrdersAsync("ETH-USDT", default, default, default, default, default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Trading.GetIsolatedMarginClosedOrdersAsync("ETH-USDT", Enums.MarginTradeType.BuyShort, true, new[] { OrderStatusFilter.Canceled }, default, default, default, default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Trading.GetCrossMarginClosedOrdersAsync("ETH-USDT", Enums.MarginTradeType.BuyShort, true, new[] { OrderStatusFilter.Canceled }, default, default, default, default, default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Trading.GetIsolatedMarginUserTradesAsync("ETH-USDT", Enums.MarginTradeType.BuyShort, default, default, default, default, default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Trading.GetCrossMarginUserTradesAsync("ETH-USDT", Enums.MarginTradeType.BuyShort, default, default, default, default, default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Trading.GetIsolatedMarginOpenTriggerOrdersAsync("ETH-USDT", default, default, default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Trading.GetCrossMarginOpenTriggerOrdersAsync("ETH-USDT", default, default, default, default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Trading.GetIsolatedMarginTriggerOrderHistoryAsync("ETH-USDT", MarginTradeType.BuyLong, 90, OrderStatusFilter.FullyMatched, default, default, default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Trading.GetCrossMarginTriggerOrderHistoryAsync(MarginTradeType.BuyLong, 90, OrderStatusFilter.FullyMatched, "ETH-USDT", default, default, default, default, default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Trading.GetIsolatedMarginOpenTpSlOrdersAsync("ETH-USDT", default, default, default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Trading.GetCrossMarginOpenTpSlOrdersAsync("ETH-USDT", default, default, default, default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Trading.GetIsolatedMarginTpSlHistoryAsync("ETH-USDT", new[] { TpSlStatus.Canceled }, 90, default, default, default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Trading.GetCrossMarginTpSlHistoryAsync(new[] { TpSlStatus.Canceled }, 90, "ETH-USDT", default, default, default, default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Trading.GetOpenIsolatedMarginTrailingOrdersAsync("ETH-USDT", MarginTradeType.BuyLong, 90, default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Trading.GetOpenCrossMarginTrailingOrdersAsync("ETH-USDT", default, default, default, default, default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Trading.GetClosedIsolatedMarginTrailingOrdersAsync("ETH-USDT", new[] { TpSlStatus.Canceled }, MarginTradeType.BuyLong, 90, default, default, default, default), true);
+ await RunAndCheckResult(client => client.UsdtFuturesApi.Trading.GetClosedCrossMarginTrailingOrdersAsync(new[] { TpSlStatus.Canceled }, MarginTradeType.BuyLong, 90, "ETH-USDT", default, default, default, default, default, default), true);
+ }
+ }
+}
diff --git a/HTX.Net.UnitTests/HTXSocketClientTests.cs b/HTX.Net.UnitTests/HTXSocketClientTests.cs
new file mode 100644
index 00000000..b42f8666
--- /dev/null
+++ b/HTX.Net.UnitTests/HTXSocketClientTests.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using HTX.Net.UnitTests.TestImplementations;
+using NUnit.Framework;
+using NUnit.Framework.Legacy;
+using System.Text.Json;
+
+namespace HTX.Net.UnitTests
+{
+ [TestFixture]
+ public class HTXSocketClientTests
+ {
+ [Test]
+ public void SubscribeV1_Should_SucceedIfSubbedResponse()
+ {
+ // arrange
+ var socket = new TestSocket();
+ socket.CanConnect = true;
+ var client = TestHelpers.CreateSocketClient(socket);
+
+ // act
+ var subTask = client.SpotApi.SubscribeToPartialOrderBookUpdates1SecondAsync("ETHBTC", 1, test => { });
+ var id = JsonDocument.Parse(socket.LastSendMessage).RootElement.GetProperty("id").GetString();
+ socket.InvokeMessage($"{{\"subbed\": \"test\", \"id\":\"{id}\", \"status\": \"ok\"}}");
+ var subResult = subTask.Result;
+
+ // assert
+ Assert.That(subResult.Success);
+ }
+
+ [Test]
+ public async Task SubscribeV1_Should_FailIfNoResponse()
+ {
+ // arrange
+ var socket = new TestSocket();
+ socket.CanConnect = true;
+ var client = TestHelpers.CreateSocketClient(socket, x =>
+ {
+ x.RequestTimeout = TimeSpan.FromMilliseconds(10);
+ });
+
+ // act
+ var subResult = await client.SpotApi.SubscribeToPartialOrderBookUpdates1SecondAsync("ETHBTC", 1, test => { });
+
+ // assert
+ ClassicAssert.IsFalse(subResult.Success);
+ }
+
+ [Test]
+ public void SubscribeV1_Should_FailIfErrorResponse()
+ {
+ // arrange
+ var socket = new TestSocket();
+ socket.CanConnect = true;
+ var client = TestHelpers.CreateSocketClient(socket);
+
+ // act
+ var subTask = client.SpotApi.SubscribeToPartialOrderBookUpdates1SecondAsync("ETHBTC", 1, test => { });
+ var id = JsonDocument.Parse(socket.LastSendMessage).RootElement.GetProperty("id").GetString();
+ socket.InvokeMessage($"{{\"status\": \"error\", \"id\": \"{id}\", \"err-code\": \"Fail\", \"err-msg\": \"failed\"}}");
+ var subResult = subTask.Result;
+
+ // assert
+ ClassicAssert.IsFalse(subResult.Success);
+ }
+
+ [Test]
+ public void SubscribeV2_Should_SucceedIfSubbedResponse()
+ {
+ // arrange
+ var socket = new TestSocket();
+ socket.CanConnect = true;
+ var client = TestHelpers.CreateAuthenticatedSocketClient(socket);
+
+ // act
+ var subTask = client.SpotApi.SubscribeToAccountUpdatesAsync(test => { });
+ socket.InvokeMessage("{\"action\": \"req\", \"code\": 200, \"ch\": \"auth\"}");
+ Thread.Sleep(10);
+ socket.InvokeMessage("{\"action\": \"sub\", \"code\": 200, \"ch\": \"accounts.update#1\"}");
+ var subResult = subTask.Result;
+
+ // assert
+ Assert.That(subResult.Success);
+ }
+
+ [Test]
+ public void SubscribeV2_Should_FailIfAuthErrorResponse()
+ {
+ // arrange
+ var socket = new TestSocket();
+ socket.CanConnect = true;
+ var client = TestHelpers.CreateSocketClient(socket);
+
+ // act
+ var subTask = client.SpotApi.SubscribeToAccountUpdatesAsync(test => { });
+ socket.InvokeMessage("{ \"action\": \"req\", \"ch\": \"auth\", \"code\": 400}");
+ var subResult = subTask.Result;
+
+ // assert
+ ClassicAssert.IsFalse(subResult.Success);
+ }
+
+ [Test]
+ public void SubscribeV2_Should_FailIfNoResponse()
+ {
+ // arrange
+ var socket = new TestSocket();
+ socket.CanConnect = true;
+ var client = TestHelpers.CreateSocketClient(socket, x =>
+ {
+ x.RequestTimeout = TimeSpan.FromMilliseconds(10);
+ });
+
+ // act
+ var subTask = client.SpotApi.SubscribeToAccountUpdatesAsync(test => { });
+ var subResult = subTask.Result;
+
+ // assert
+ ClassicAssert.IsFalse(subResult.Success);
+ }
+ }
+}
diff --git a/HTX.Net.UnitTests/RestRequestTests.cs b/HTX.Net.UnitTests/RestRequestTests.cs
new file mode 100644
index 00000000..ac734ebe
--- /dev/null
+++ b/HTX.Net.UnitTests/RestRequestTests.cs
@@ -0,0 +1,333 @@
+using CryptoExchange.Net.Objects;
+using CryptoExchange.Net.Testing;
+using HTX.Net.Clients;
+using HTX.Net.Objects.Models;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace HTX.Net.UnitTests
+{
+ [TestFixture]
+ public class RestRequestTests
+ {
+ [Test]
+ public async Task ValidateSpotAccountCalls()
+ {
+ var client = new HTXRestClient(opts =>
+ {
+ opts.AutoTimestamp = false;
+ opts.ApiCredentials = new CryptoExchange.Net.Authentication.ApiCredentials("123", "456");
+ });
+ var tester = new RestRequestValidator(client, "Endpoints/Spot/Account", "https://api.huobi.pro", IsAuthenticated, stjCompare: true, nestedPropertyForCompare: "data");
+ await tester.ValidateAsync(client => client.SpotApi.Account.GetAccountsAsync(), "GetAccounts");
+ await tester.ValidateAsync(client => client.SpotApi.Account.GetBalancesAsync(1), "GetBalances", "data.list", ignoreProperties: new List { "debt", "available" });
+ await tester.ValidateAsync(client => client.SpotApi.Account.GetPlatformValuationAsync(), "GetPlatformValuation");
+ await tester.ValidateAsync(client => client.SpotApi.Account.GetAssetValuationAsync(Enums.AccountType.Spot), "GetAssetValuation");
+ await tester.ValidateAsync(client => client.SpotApi.Account.InternalTransferAsync(1, Enums.AccountType.Spot, 2, 3, Enums.AccountType.Spot, 4, "ETH", 1), "InternalTransfer");
+ await tester.ValidateAsync(client => client.SpotApi.Account.GetAccountHistoryAsync(1), "GetAccountHistory");
+ await tester.ValidateAsync(client => client.SpotApi.Account.GetAccountLedgerAsync(1), "GetAccountLedger");
+ await tester.ValidateAsync(client => client.SpotApi.Account.TransferAsync(Enums.TransferAccount.Spot, Enums.TransferAccount.Futures, "ETH", 1, "ETH"), "Transfer");
+ await tester.ValidateAsync(client => client.SpotApi.Account.GetPointBalanceAsync(), "GetPointBalance");
+ await tester.ValidateAsync(client => client.SpotApi.Account.TransferPointsAsync("1", "2", "3", 1), "TransferPoints");
+ await tester.ValidateAsync(client => client.SpotApi.Account.GetUserDeductionInfoAsync(), "GetUserDeductionInfo");
+ await tester.ValidateAsync(client => client.SpotApi.Account.GetDeductAssetsAsync(), "GetDeductAssets");
+ await tester.ValidateAsync(client => client.SpotApi.Account.SetDeductionSwitchAsync(Enums.DeductionSwitchType.AssetDeduction), "SetDeductionSwitch");
+ await tester.ValidateAsync(client => client.SpotApi.Account.GetDepositAddressesAsync("ETH"), "GetDepositAddresses");
+ await tester.ValidateAsync(client => client.SpotApi.Account.GetWithdrawalQuotasAsync("ETH"), "GetWithdrawalQuotas");
+ await tester.ValidateAsync(client => client.SpotApi.Account.GetWithdrawalAddressesAsync("ETH"), "GetWithdrawalAddresses");
+ await tester.ValidateAsync(client => client.SpotApi.Account.WithdrawAsync("123", "ETH", 1, 1), "Withdraw");
+ await tester.ValidateAsync(client => client.SpotApi.Account.GetWithdrawalByClientOrderIdAsync("123"), "GetWithdrawalByClientOrderId");
+ await tester.ValidateAsync(client => client.SpotApi.Account.CancelWithdrawalAsync(1), "CancelWithdrawal");
+ await tester.ValidateAsync(client => client.SpotApi.Account.GetWithdrawDepositHistoryAsync(Enums.WithdrawDepositType.Withdraw), "GetWithdrawDeposit");
+ await tester.ValidateAsync(client => client.SpotApi.Account.GetTradingFeesAsync(new[] { "ETHUSDT" }), "GetTradingFees");
+ await tester.ValidateAsync(client => client.SpotApi.Account.GetApiKeyInfoAsync(1), "GetApiKeyInfo");
+ await tester.ValidateAsync(client => client.SpotApi.Account.GetUserIdAsync(), "GetUserId");
+ }
+
+ [Test]
+ public async Task ValidateSpotExchangeDataCalls()
+ {
+ var client = new HTXRestClient(opts =>
+ {
+ opts.AutoTimestamp = false;
+ opts.ApiCredentials = new CryptoExchange.Net.Authentication.ApiCredentials("123", "456");
+ });
+ var tester = new RestRequestValidator(client, "Endpoints/Spot/ExchangeData", "https://api.huobi.pro", IsAuthenticated, stjCompare: true, nestedPropertyForCompare: "data");
+ await tester.ValidateAsync(client => client.SpotApi.ExchangeData.GetSymbolStatusAsync(), "GetSymbolStatus");
+ await tester.ValidateAsync(client => client.SpotApi.ExchangeData.GetSymbolsAsync(), "GetSymbols", ignoreProperties: new List { "p" });
+ await tester.ValidateAsync(client => client.SpotApi.ExchangeData.GetAssetsAsync(), "GetAssets");
+ await tester.ValidateAsync(client => client.SpotApi.ExchangeData.GetSymbolConfigAsync(), "GetSymbolConfig");
+ await tester.ValidateAsync(client => client.SpotApi.ExchangeData.GetAssetsAndNetworksAsync("ETH"), "GetAssetsAndNetworks");
+ await tester.ValidateAsync(client => client.SpotApi.ExchangeData.GetKlinesAsync("ETHUSDT", Enums.KlineInterval.OneDay), "GetKlines");
+ await tester.ValidateAsync(client => client.SpotApi.ExchangeData.GetTickerAsync("ETHUSDT"), "GetTicker", nestedJsonProperty: "tick");
+ await tester.ValidateAsync(client => client.SpotApi.ExchangeData.GetTickersAsync(), "GetTickers");
+ await tester.ValidateAsync(client => client.SpotApi.ExchangeData.GetOrderBookAsync("ETHUSDT", 0), "GetOrderBook", nestedJsonProperty: "tick");
+ await tester.ValidateAsync(client => client.SpotApi.ExchangeData.GetLastTradeAsync("ETHUSDT"), "GetLastTrade", nestedJsonProperty: "tick");
+ await tester.ValidateAsync(client => client.SpotApi.ExchangeData.GetTradeHistoryAsync("ETHUSDT"), "GetTradeHistory");
+ await tester.ValidateAsync(client => client.SpotApi.ExchangeData.GetSymbolDetails24HAsync("ETHUSDT"), "GetSymbolDetails24H", nestedJsonProperty: "tick");
+ }
+
+ [Test]
+ public async Task ValidateSpotMarginCalls()
+ {
+ var client = new HTXRestClient(opts =>
+ {
+ opts.AutoTimestamp = false;
+ opts.ApiCredentials = new CryptoExchange.Net.Authentication.ApiCredentials("123", "456");
+ });
+ var tester = new RestRequestValidator(client, "Endpoints/Spot/Margin", "https://api.huobi.pro", IsAuthenticated, stjCompare: true, nestedPropertyForCompare: "data");
+ await tester.ValidateAsync(client => client.SpotApi.Margin.RepayLoanAsync("123", "ETH", 1), "RepayLoan");
+ await tester.ValidateAsync(client => client.SpotApi.Margin.TransferSpotToIsolatedMarginAsync("123", "ETH", 1), "TransferSpotToIsolatedMargin");
+ await tester.ValidateAsync(client => client.SpotApi.Margin.TransferIsolatedMarginToSpotAsync("123", "ETH", 1), "TransferIsolatedMarginToSpot");
+ await tester.ValidateAsync(client => client.SpotApi.Margin.GetIsolatedLoanInterestRateAndQuotaAsync(), "GetIsolatedLoanInterestRateAndQuota");
+ await tester.ValidateAsync(client => client.SpotApi.Margin.RequestIsolatedMarginLoanAsync("ETHUSDT", "ETH", 1), "RequestIsolatedMarginLoan");
+ await tester.ValidateAsync(client => client.SpotApi.Margin.RepayIsolatedMarginLoanAsync("123", 1), "RepayIsolatedMarginLoan");
+ await tester.ValidateAsync(client => client.SpotApi.Margin.GetIsolatedMarginClosedOrdersAsync("123"), "GetIsolatedMarginClosedOrders");
+ await tester.ValidateAsync(client => client.SpotApi.Margin.GetIsolatedMarginBalanceAsync("ETHUSDT"), "GetIsolatedMarginBalance");
+ await tester.ValidateAsync(client => client.SpotApi.Margin.TransferSpotToCrossMarginAsync("ETH", 1), "TransferSpotToCrossMargin");
+ await tester.ValidateAsync(client => client.SpotApi.Margin.TransferCrossMarginToSpotAsync("ETH", 1), "TransferCrossMarginToSpot");
+ await tester.ValidateAsync(client => client.SpotApi.Margin.GetCrossLoanInterestRateAndQuotaAsync(), "GetCrossLoanInterestRateAndQuota");
+ await tester.ValidateAsync(client => client.SpotApi.Margin.RequestCrossMarginLoanAsync("ETH", 1), "RequestCrossMarginLoan");
+ await tester.ValidateAsync(client => client.SpotApi.Margin.RepayCrossMarginLoanAsync("123", 1), "RepayCrossMarginLoan");
+ await tester.ValidateAsync(client => client.SpotApi.Margin.GetCrossMarginClosedOrdersAsync(), "GetCrossMarginClosedOrders");
+ await tester.ValidateAsync(client => client.SpotApi.Margin.GetCrossMarginBalanceAsync(), "GetCrossMarginBalance");
+ await tester.ValidateAsync(client => client.SpotApi.Margin.GetCrossMarginLimitAsync(), "GetCrossMarginLimit");
+ await tester.ValidateAsync(client => client.SpotApi.Margin.GetRepaymentHistoryAsync(), "GetRepaymentHistory");
+ }
+
+ [Test]
+ public async Task ValidateSpotSubAccountCalls()
+ {
+ var client = new HTXRestClient(opts =>
+ {
+ opts.AutoTimestamp = false;
+ opts.ApiCredentials = new CryptoExchange.Net.Authentication.ApiCredentials("123", "456");
+ });
+ var tester = new RestRequestValidator(client, "Endpoints/Spot/SubAccount", "https://api.huobi.pro", IsAuthenticated, stjCompare: true, nestedPropertyForCompare: "data");
+ await tester.ValidateAsync(client => client.SpotApi.SubAccount.SetDeductModeAsync(new[] { "1" }, Enums.DeductMode.Sub), "SetDeductMode");
+ await tester.ValidateAsync(client => client.SpotApi.SubAccount.CreateSubAccountsAsync(new[] { new HTXSubAccountRequest { UserName = "123" } }), "CreateSubAccounts");
+ await tester.ValidateAsync(client => client.SpotApi.SubAccount.GetSubUserListAsync(), "GetSubUserList");
+ await tester.ValidateAsync(client => client.SpotApi.SubAccount.SetLockAsync(1, Enums.LockAction.Normal), "SetLock");
+ await tester.ValidateAsync(client => client.SpotApi.SubAccount.GetSubUserAsync(1), "GetSubUser");
+ await tester.ValidateAsync(client => client.SpotApi.SubAccount.SetTradableMarketAsync(new[] { "1" }, Enums.SubAccountMarketType.IsolatedMargin, true), "SetTradableMarket");
+ await tester.ValidateAsync(client => client.SpotApi.SubAccount.SetAssetTransferPermissionsAsync(new[] { "1" }, true), "SetAssetTransferPermissions");
+ await tester.ValidateAsync(client => client.SpotApi.SubAccount.GetSubUserAccountsAsync(1), "GetSubUserAccounts");
+ await tester.ValidateAsync(client => client.SpotApi.SubAccount.CreateApiKeyAsync("1", 1, "123", new[] { "" }, new[] { "" }), "CreateApiKey");
+ await tester.ValidateAsync(client => client.SpotApi.SubAccount.EditApiKeyAsync(1, "123", "123", new[] {""}, new[] {""}), "EditApiKey");
+ await tester.ValidateAsync(client => client.SpotApi.SubAccount.DeleteApiKeyAsync(1, "123"), "DeleteApiKey");
+ await tester.ValidateAsync(client => client.SpotApi.SubAccount.TransferWithSubAccountAsync(1, "ETH", 1, Enums.TransferType.PointFromSubAccount), "TransferWithSubAccount");
+ await tester.ValidateAsync(client => client.SpotApi.SubAccount.GetDepositAddressAsync(1, "ETH"), "GetDepositAddress");
+ await tester.ValidateAsync(client => client.SpotApi.SubAccount.GetDepositHistoryAsync(1, "ETH"), "GetDepositHistory");
+ await tester.ValidateAsync(client => client.SpotApi.SubAccount.GetAggregateBalancesAsync(), "GetAggregateBalances");
+ await tester.ValidateAsync(client => client.SpotApi.SubAccount.GetBalancesAsync(1), "GetBalances", ignoreProperties: new List { "debt", "available" });
+ }
+
+ [Test]
+ public async Task ValidateSpotTradingCalls()
+ {
+ var client = new HTXRestClient(opts =>
+ {
+ opts.AutoTimestamp = false;
+ opts.ApiCredentials = new CryptoExchange.Net.Authentication.ApiCredentials("123", "456");
+ });
+ var tester = new RestRequestValidator(client, "Endpoints/Spot/Trading", "https://api.huobi.pro", IsAuthenticated, stjCompare: true, nestedPropertyForCompare: "data");
+ await tester.ValidateAsync(client => client.SpotApi.Trading.PlaceOrderAsync(1, "ETHUSDT", Enums.OrderSide.Buy, Enums.OrderType.IOC, 1), "PlaceOrder");
+ await tester.ValidateAsync(client => client.SpotApi.Trading.PlaceMultipleOrderAsync(new [] { new HTXOrderRequest { } }), "PlaceMultipleOrder");
+ await tester.ValidateAsync(client => client.SpotApi.Trading.PlaceMarginOrderAsync(1, "ETHUSDT", Enums.OrderSide.Buy, Enums.OrderType.IOC, Enums.MarginPurpose.AutomaticLoan, Enums.SourceType.C2CMargin), "PlaceMarginOrder");
+ await tester.ValidateAsync(client => client.SpotApi.Trading.CancelOrderAsync(1), "CancelOrder");
+ await tester.ValidateAsync(client => client.SpotApi.Trading.CancelOrderByClientOrderIdAsync("1"), "CancelOrderByClientOrderId");
+ await tester.ValidateAsync(client => client.SpotApi.Trading.GetOpenOrdersAsync(), "GetOpenOrders");
+ await tester.ValidateAsync(client => client.SpotApi.Trading.CancelOrdersByCriteriaAsync(), "CancelOrdersByCriteria");
+ await tester.ValidateAsync(client => client.SpotApi.Trading.CancelOrdersAsync(new[] { 1L } ), "CancelOrders");
+ await tester.ValidateAsync(client => client.SpotApi.Trading.GetOrderAsync(1), "GetOrder");
+ await tester.ValidateAsync(client => client.SpotApi.Trading.GetOrderByClientOrderIdAsync("1"), "GetOrderByClientOrderId");
+ await tester.ValidateAsync(client => client.SpotApi.Trading.GetOrderTradesAsync(1), "GetOrderTrades");
+ await tester.ValidateAsync(client => client.SpotApi.Trading.GetClosedOrdersAsync("ETHUSDT"), "GetClosedOrders");
+ await tester.ValidateAsync(client => client.SpotApi.Trading.GetHistoricalOrdersAsync("ETHUSDT"), "GetHistoricalOrders");
+ await tester.ValidateAsync(client => client.SpotApi.Trading.GetUserTradesAsync("ETHUSDT"), "GetUserTrades");
+ await tester.ValidateAsync(client => client.SpotApi.Trading.PlaceConditionalOrderAsync(1, "ETHUSDT", Enums.OrderSide.Buy, Enums.ConditionalOrderType.Market, 1), "PlaceConditionalOrder");
+ await tester.ValidateAsync(client => client.SpotApi.Trading.CancelConditionalOrdersAsync(new[] { "1" }), "CancelConditionalOrders");
+ await tester.ValidateAsync(client => client.SpotApi.Trading.GetOpenConditionalOrdersAsync(), "GetOpenConditionalOrders");
+ await tester.ValidateAsync(client => client.SpotApi.Trading.GetClosedConditionalOrdersAsync("ETHUSDT", Enums.ConditionalOrderStatus.Created), "GetClosedConditionalOrders");
+ await tester.ValidateAsync(client => client.SpotApi.Trading.GetConditionalOrderAsync("1"), "GetConditionalOrder");
+ }
+
+ [Test]
+ public async Task ValidateUsdtMarginSwapAccountCalls()
+ {
+ var client = new HTXRestClient(opts =>
+ {
+ opts.AutoTimestamp = false;
+ opts.ApiCredentials = new CryptoExchange.Net.Authentication.ApiCredentials("123", "456");
+ });
+ var tester = new RestRequestValidator(client, "Endpoints/UsdtMarginSwap/Account", "https://api.hbdm.com", IsAuthenticated, stjCompare: true, nestedPropertyForCompare: "data");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetAssetValuationAsync(), "GetAssetValuation");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetIsolatedMarginAccountInfoAsync(), "GetIsolatedMarginAccountInfo");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetCrossMarginAccountInfoAsync(), "GetCrossMarginAccountInfo");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetIsolatedMarginPositionsAsync(), "GetIsolatedMarginPositions");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetCrossMarginPositionsAsync(), "GetCrossMarginPositions");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetIsolatedMarginAssetsAndPositionsAsync("ETH-USDT"), "GetIsolatedMarginAssetsAndPositions");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetCrossMarginAssetsAndPositionsAsync("ETH-USDT"), "GetCrossMarginAssetsAndPositions");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetFinancialRecordsAsync("ETH-USDT"), "GetFinancialRecords");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetIsolatedMarginAvailableLeverageAsync("ETH-USDT"), "GetIsolatedMarginAvailableLeverage");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetCrossMarginAvailableLeverageAsync("ETH-USDT"), "GetCrossMarginAvailableLeverage");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetOrderLimitsAsync(Enums.OrderPriceType.PostOnly), "GetOrderLimits");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetTradingFeesAsync(), "GetTradingFees");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetIsolatedMarginTransferLimitsAsync(), "GetIsolatedMarginTransferLimits");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetCrossMarginTransferLimitsAsync(), "GetCrossMarginTransferLimits");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetIsolatedMarginPositionLimitAsync("ETH-USDT"), "GetIsolatedMarginPositionLimit");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetCrossMarginPositionLimitsAsync("ETH-USDT"), "GetCrossMarginPositionLimits");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetIsolatedMarginLeveragePositionLimitsAsync("ETH-USDT"), "GetIsolatedMarginLeveragePositionLimits");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetCrossMarginLeveragePositionLimitsAsync(Enums.BusinessType.Futures), "GetCrossMarginLeveragePositionLimits");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.TransferMarginAccountsAsync("ETH", "123", "123", 1), "TransferMarginAccounts");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetTradingStatusAsync(), "GetTradingStatus");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.SetIsolatedMarginPositionModeAsync("ETH-USDT", Enums.PositionMode.SingleSide), "SetIsolatedMarginPositionMode");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.SetCrossMarginPositionModeAsync("ETH-USDT", Enums.PositionMode.SingleSide), "SetCrossMarginPositionMode");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetIsolatedMarginPositionModeAsync("ETH-USDT"), "GetIsolatedMarginPositionMode");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetCrossMarginPositionModeAsync("ETH-USDT"), "GetCrossMarginPositionMode");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetIsolatedMarginSettlementRecordsAsync("ETH-USDT"), "GetIsolatedMarginSettlementRecords");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Account.GetCrossMarginSettlementRecordsAsync("ETH-USDT"), "GetCrossMarginSettlementRecords");
+ }
+
+ [Test]
+ public async Task ValidateUsdtMarginSwapExchangeDataCalls()
+ {
+ var client = new HTXRestClient(opts =>
+ {
+ opts.AutoTimestamp = false;
+ opts.ApiCredentials = new CryptoExchange.Net.Authentication.ApiCredentials("123", "456");
+ });
+ var tester = new RestRequestValidator(client, "Endpoints/UsdtMarginSwap/ExchangeData", "https://api.hbdm.com", IsAuthenticated, stjCompare: true, nestedPropertyForCompare: "data");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetFundingRateAsync("ETH-USDT"), "GetFundingRate");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetFundingRatesAsync(), "GetFundingRates");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetHistoricalFundingRatesAsync("ETH-USDT"), "GetHistoricalFundingRates");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetLiquidationOrdersAsync("ETH-USDT", Enums.LiquidationTradeType.FullyFilledLiquidationOrders), "GetLiquidationOrders");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetHistoricalSettlementRecordsAsync("ETH-USDT"), "GetHistoricalSettlementRecords");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetTopTraderAccountSentimentAsync("ETH-USDT", Enums.Period.OneDay), "GetTopTraderAccountSentiment");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetTopTraderPositionSentimentAsync("ETH-USDT", Enums.Period.OneDay), "GetTopTraderPositionSentiment");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetIsolatedMarginStatusAsync("ETH-USDT"), "GetIsolatedMarginStatus");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetCrossTieredMarginInfoAsync("ETH-USDT"), "GetCrossTieredMarginInfo");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetIsolatedMarginTieredInfoAsync("ETH-USDT"), "GetIsolatedMarginTieredInfo");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetEstimatedSettlementPriceAsync("ETH-USDT"), "GetEstimatedSettlementPrice");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetIsolatedMarginAdjustFactorInfoAsync("ETH-USDT"), "GetIsolatedMarginAdjustFactorInfo");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetCrossMarginAdjustFactorInfoAsync("ETH-USDT"), "GetCrossMarginAdjustFactorInfo");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetInsuranceFundHistoryAsync("ETH-USDT"), "GetInsuranceFundHistory");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetSwapRiskInfoAsync("ETH-USDT"), "GetSwapRiskInfo");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetSwapPriceLimitationAsync("ETH-USDT"), "GetSwapPriceLimitation");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetSwapOpenInterestAsync("ETH-USDT"), "GetSwapOpenInterest");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetContractsAsync("ETH-USDT"), "GetContractInfo");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetSwapIndexPriceAsync("ETH-USDT"), "GetSwapIndexPrice");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetContractElementsAsync("ETH-USDT"), "GetContractElements");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetOrderBookAsync("ETH-USDT"), "GetOrderBook", nestedJsonProperty: "tick", ignoreProperties: new List { "ch", "id", "mrid" });
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetBookTickerAsync("ETH-USDT"), "GetBookTicker", nestedJsonProperty: "ticks");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetKlinesAsync("ETH-USDT", Enums.KlineInterval.OneDay), "GetKlines");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetMarkPriceKlinesAsync("ETH-USDT", Enums.KlineInterval.OneDay, 10), "GetMarkPriceKlines");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetTickerAsync("ETH-USDT"), "GetTicker", nestedJsonProperty: "tick");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetTickersAsync("ETH-USDT"), "GetTickers", nestedJsonProperty: "ticks");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetLastTradesAsync("ETH-USDT"), "GetLastTrades", nestedJsonProperty: "tick.data.0");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetRecentTradesAsync("ETH-USDT", 10), "GetRecentTrades", "data.0.data");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetOpenInterestHistoryAsync(Enums.InterestPeriod.OneDay, Enums.Unit.Cont), "GetOpenInterestHistory");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetPremiumIndexKlinesAsync("ETH-USDT", Enums.KlineInterval.OneDay, 10), "GetPremiumIndexKlines");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetEstimatedFundingRateKlinesAsync("ETH-USDT", Enums.KlineInterval.OneDay, 10), "GetEstimatedFundingRateKlines");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetBasisDataAsync("ETH-USDT", Enums.KlineInterval.OneDay, 10), "GetBasisData");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetCrossMarginTradeStatusAsync("ETH-USDT"), "GetCrossMarginTradeStatus");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.ExchangeData.GetCrossMarginTransferStatusAsync("ETH-USDT"), "GetCrossMarginTransferStatus");
+ }
+
+ [Test]
+ public async Task ValidateUsdtMarginSwapSubAccountCalls()
+ {
+ var client = new HTXRestClient(opts =>
+ {
+ opts.AutoTimestamp = false;
+ opts.ApiCredentials = new CryptoExchange.Net.Authentication.ApiCredentials("123", "456");
+ });
+ var tester = new RestRequestValidator(client, "Endpoints/UsdtMarginSwap/SubAccount", "https://api.hbdm.com", IsAuthenticated, stjCompare: true, nestedPropertyForCompare: "data");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.SubAccount.SetTradingPermissionsAsync(new[] { "1" }, true), "SetTradingPermissions");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.SubAccount.GetTradePermissionsAsync(new[] { "1" }), "GetTradePermissions");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.SubAccount.GetIsolatedMarginAssetsAsync(), "GetIsolatedMarginAssets");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.SubAccount.GetCrossMarginAssetsAsync(), "GetCrossMarginAssets");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.SubAccount.GetIsolatedMarginAssetInfoAsync(), "GetIsolatedMarginAssetInfo");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.SubAccount.GetCrossMarginAssetInfoAsync(), "GetCrossMarginAssetInfo");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.SubAccount.GetIsolatedMarginPositionsAsync(1), "GetIsolatedMarginPositions");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.SubAccount.GetCrossMarginPositionsAsync(1), "GetCrossMarginPositions");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.SubAccount.TransferMasterSubAsync("1", "ETH", "1", "2", 1, Enums.MasterSubTransferType.MasterToSub), "TransferMasterSub");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.SubAccount.GetMasterSubTransferRecordsAsync("1", 90), "GetMasterSubTransferRecords");
+ }
+
+ [Test]
+ public async Task ValidateUsdtMarginSwapTradingCalls()
+ {
+ var client = new HTXRestClient(opts =>
+ {
+ opts.AutoTimestamp = false;
+ opts.ApiCredentials = new CryptoExchange.Net.Authentication.ApiCredentials("123", "456");
+ });
+ var tester = new RestRequestValidator(client, "Endpoints/UsdtMarginSwap/Trading", "https://api.hbdm.com", IsAuthenticated, stjCompare: true, nestedPropertyForCompare: "data");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CancelOrdersAfterAsync(true, TimeSpan.FromSeconds(10)), "CancelOrdersAfter");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.PlaceIsolatedMarginOrderAsync("ETH-USDT", 1, Enums.OrderSide.Buy, 1, Enums.OrderPriceType.Limit), "PlaceIsolatedMarginOrder");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.PlaceCrossMarginOrderAsync(1, Enums.OrderSide.Sell, 1, Enums.OrderPriceType.Limit), "PlaceCrossMarginOrder");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CancelIsolatedMarginOrderAsync("ETH-USDT", 1), "CancelIsolatedMarginOrder");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CancelCrossMarginOrderAsync(1), "CancelCrossMarginOrder");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CancelIsolatedMarginOrdersAsync("ETH-USDT", new[] { 1L }, new[] { 1L }), "CancelIsolatedMarginOrders");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CancelCrossMarginOrdersAsync(new[] { 1L }, new[] { 1L }), "CancelCrossMarginOrders");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CancelAllIsolatedMarginOrdersAsync("ETH-USDT"), "CancelAllIsolatedMarginOrders");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CancelAllCrossMarginOrdersAsync("ETH-USDT"), "CancelAllCrossMarginOrders");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.SetIsolatedMarginLeverageAsync("ETH-USDT", 1), "SetIsolatedMarginLeverage");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.SetCrossMarginLeverageAsync(1), "SetCrossMarginLeverage");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetIsolatedMarginOrderAsync("ETH-USDT"), "GetIsolatedMarginOrder");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetCrossMarginOrderAsync("ETH-USDT"), "GetCrossMarginOrder");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetIsolatedMarginOrdersAsync("ETH-USDT", new[] { 1L }, new[] { 1L }), "GetIsolatedMarginOrders");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetCrossMarginOrdersAsync(new[] { 1L }, new[] { 1L }), "GetCrossMarginOrders");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetIsolatedMarginOrderDetailsAsync("ETH-USDT", 1), "GetIsolatedMarginOrderDetails", ignoreProperties: new List { "price" });
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetCrossMarginOrderDetailsAsync("ETH-USDT", 1), "GetCrossMarginOrderDetails", ignoreProperties: new List { "price" });
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetIsolatedMarginOpenOrdersAsync("ETH-USDT", 1), "GetIsolatedMarginOpenOrders");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetCrossMarginOpenOrdersAsync("ETH-USDT"), "GetCrossMarginOpenOrders");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetIsolatedMarginClosedOrdersAsync("ETH-USDT", Enums.MarginTradeType.LiquidateShort, true, new[] { Enums.OrderStatusFilter.Canceled }), "GetIsolatedMarginClosedOrders", ignoreProperties: new List { "query_id" });
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetCrossMarginClosedOrdersAsync("ETH-USDT", Enums.MarginTradeType.LiquidateShort, true, new[] { Enums.OrderStatusFilter.Canceled }), "GetCrossMarginClosedOrders", ignoreProperties: new List { "query_id" });
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetIsolatedMarginUserTradesAsync("ETH-USDT", Enums.MarginTradeType.LiquidateShort), "GetIsolatedMarginUserTrades");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetCrossMarginUserTradesAsync("ETH-USDT", Enums.MarginTradeType.LiquidateShort), "GetCrossMarginUserTrades");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CloseIsolatedMarginPositionAsync("ETH-USDT", Enums.OrderSide.Sell), "CloseIsolatedMarginPosition");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CloseCrossMarginPositionAsync(Enums.OrderSide.Sell), "CloseCrossMarginPosition");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.PlaceIsolatedMarginTriggerOrderAsync("ETH-USDT", Enums.TriggerType.LesserThanOrEqual, 1, 1, Enums.OrderSide.Buy), "PlaceIsolatedMarginTriggerOrder");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.PlaceCrossMarginTriggerOrderAsync(Enums.TriggerType.LesserThanOrEqual, 1, 1, Enums.OrderSide.Buy), "PlaceCrossMarginTriggerOrder");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CancelIsolatedMarginTriggerOrderAsync("ETH-USDT", "1"), "CancelIsolatedMarginTriggerOrder");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CancelCrossMarginTriggerOrderAsync("1"), "CancelCrossMarginTriggerOrder");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CancelAllIsolatedMarginTriggerOrdersAsync("ETH-USDT"), "CancelAllIsolatedMarginTriggerOrders");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CancelAllCrossMarginTriggerOrdersAsync("ETH-USDT"), "CancelAllCrossMarginTriggerOrdersAsync");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetIsolatedMarginOpenTriggerOrdersAsync("ETH-USDT"), "GetIsolatedMarginOpenTriggerOrders");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetCrossMarginOpenTriggerOrdersAsync("ETH-USDT"), "GetCrossMarginOpenTriggerOrders");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetIsolatedMarginTriggerOrderHistoryAsync("ETH-USDT", Enums.MarginTradeType.BuyShort, 90, Enums.OrderStatusFilter.FullyMatched), "GetIsolatedMarginTriggerOrderHistory");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetCrossMarginTriggerOrderHistoryAsync(Enums.MarginTradeType.BuyShort, 90, Enums.OrderStatusFilter.FullyMatched), "GetCrossMarginTriggerOrderHistory");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.SetIsolatedMarginTpSlAsync("ETH-USDT", Enums.OrderSide.Sell, 1), "SetIsolatedMarginTpSl");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.SetCrossMarginTpSlAsync(Enums.OrderSide.Sell, 1), "SetCrossMarginTpSl");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CancelIsolatedMarginTpSlAsync("ETH-USDT", "1"), "CancelIsolatedMarginTpSl");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CancelCrossMarginTpSlAsync("1"), "CancelCrossMarginTpSl");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CancelAllIsolatedMarginTpSlAsync("ETH-USDT"), "CancelAllIsolatedMarginTpSl");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CancelAllCrossMarginTpSlAsync("ETH-USDT"), "CancelAllCrossMarginTpSl");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetIsolatedMarginOpenTpSlOrdersAsync("ETH-USDT"), "GetIsolatedMarginOpenTpSlOrders");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetCrossMarginOpenTpSlOrdersAsync("ETH-USDT"), "GetCrossMarginOpenTpSlOrders");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetIsolatedMarginTpSlHistoryAsync("ETH-USDT", new[] { Enums.TpSlStatus.Canceled }, 90), "GetIsolatedMarginTpSlHistory");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetCrossMarginTpSlHistoryAsync(new[] { Enums.TpSlStatus.Canceled }, 90), "GetCrossMarginTpSlHistory");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetIsolatedMarginPositionOpenTpSlInfoAsync("ETH-UST", 1), "GetIsolatedMarginPositionOpenTpSlInfo");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetCrossMarginPositionOpenTpSlInfoAsync(1), "GetCrossMarginPositionOpenTpSlInfo");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.PlaceIsolatedMarginTrailingOrderAsync("ETH-USDT", false, Enums.OrderSide.Sell, Enums.Offset.Open, 1, 1, 1, 1, Enums.OrderPriceType.Limit), "PlaceIsolatedMarginTrailingOrder");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.PlaceCrossMarginTrailingOrderAsync(Enums.OrderSide.Sell, Enums.Offset.Open, 1, 1, 1, 1, Enums.OrderPriceType.Limit), "PlaceCrossMarginTrailingOrder");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CancelIsolatedMarginTrailingOrderAsync("ETH-USDT", "1"), "CancelIsolatedMarginTrailingOrder");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CancelCrossMarginTrailingOrderAsync("1"), "CancelCrossMarginTrailingOrder");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CancelAllIsolatedMarginTrailingOrdersAsync("ETH-USDT"), "CancelAllIsolatedMarginTrailingOrders");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.CancelAllCrossMarginTrailingOrdersAsync("ETH-USDT"), "CancelAllCrossMarginTrailingOrders");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetOpenIsolatedMarginTrailingOrdersAsync("ETH-USDT"), "GetOpenIsolatedMarginTrailingOrders");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetOpenCrossMarginTrailingOrdersAsync("ETH-USDT"), "GetOpenCrossMarginTrailingOrders");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetClosedIsolatedMarginTrailingOrdersAsync("ETH-USDT", new[] { Enums.TpSlStatus.Canceled }, Enums.MarginTradeType.BuyShort, 90), "GetClosedIsolatedMarginTrailingOrders");
+ await tester.ValidateAsync(client => client.UsdtFuturesApi.Trading.GetClosedCrossMarginTrailingOrdersAsync(new[] { Enums.TpSlStatus.Canceled }, Enums.MarginTradeType.BuyShort, 90), "GetClosedCrossMarginTrailingOrders");
+ }
+
+ private bool IsAuthenticated(WebCallResult result)
+ {
+ return result.RequestUrl.Contains("Signature");
+ }
+ }
+}
diff --git a/HTX.Net.UnitTests/SocketSubscriptionTests.cs b/HTX.Net.UnitTests/SocketSubscriptionTests.cs
new file mode 100644
index 00000000..a595f099
--- /dev/null
+++ b/HTX.Net.UnitTests/SocketSubscriptionTests.cs
@@ -0,0 +1,71 @@
+using HTX.Net.Clients;
+using CryptoExchange.Net.Testing;
+using NUnit.Framework;
+using System.Threading.Tasks;
+using HTX.Net.Objects.Models;
+using System.Collections.Generic;
+using HTX.Net.Objects.Models.Socket;
+
+namespace HTX.Net.UnitTests
+{
+ [TestFixture]
+ public class SocketSubscriptionTests
+ {
+ [Test]
+ public async Task ValidateSpotSubscriptions()
+ {
+ var client = new HTXSocketClient(opts =>
+ {
+ opts.ApiCredentials = new CryptoExchange.Net.Authentication.ApiCredentials("123", "456");
+ });
+ var tester = new SocketSubscriptionValidator(client, "Subscriptions/Spot", "wss://api.huobi.pro", "data", stjCompare: true);
+ await tester.ValidateAsync((client, handler) => client.SpotApi.SubscribeToKlineUpdatesAsync("ETHUSDT", Enums.KlineInterval.OneDay, handler), "Klines", nestedJsonProperty: "tick");
+ await tester.ValidateAsync((client, handler) => client.SpotApi.SubscribeToPartialOrderBookUpdates1SecondAsync("ETHUSDT", 0, handler), "OrderBook", nestedJsonProperty: "tick");
+ await tester.ValidateAsync((client, handler) => client.SpotApi.SubscribeToPartialOrderBookUpdates100MilisecondAsync("ETHUSDT", 20, handler), "OrderBookMbp", nestedJsonProperty: "tick");
+ await tester.ValidateAsync((client, handler) => client.SpotApi.SubscribeToOrderBookChangeUpdatesAsync("ETHUSDT", 5, handler), "OrderBookChange", nestedJsonProperty: "tick");
+ await tester.ValidateAsync((client, handler) => client.SpotApi.SubscribeToTradeUpdatesAsync("ETHUSDT", handler), "Trades", nestedJsonProperty: "tick");
+ await tester.ValidateAsync((client, handler) => client.SpotApi.SubscribeToSymbolDetailUpdatesAsync("ETHUSDT", handler), "Symbols", nestedJsonProperty: "tick");
+ await tester.ValidateAsync>((client, handler) => client.SpotApi.SubscribeToTickerUpdatesAsync(handler), "Tickers", nestedJsonProperty: "data");
+ await tester.ValidateAsync((client, handler) => client.SpotApi.SubscribeToTickerUpdatesAsync("ETHUSDT", handler), "Ticker", nestedJsonProperty: "tick");
+ await tester.ValidateAsync((client, handler) => client.SpotApi.SubscribeToBookTickerUpdatesAsync("ETHUSDT", handler), "BookTicker", nestedJsonProperty: "tick");
+ await tester.ValidateAsync((client, handler) => client.SpotApi.SubscribeToOrderUpdatesAsync(null, handler), "Order1", nestedJsonProperty: "data");
+ await tester.ValidateAsync((client, handler) => client.SpotApi.SubscribeToAccountUpdatesAsync(handler, null), "Account", nestedJsonProperty: "data");
+ await tester.ValidateAsync((client, handler) => client.SpotApi.SubscribeToOrderDetailsUpdatesAsync(null, handler), "OrderTrade", nestedJsonProperty: "data");
+ }
+
+ [Test]
+ public async Task ValidateUsdtMarginSwapSubscriptions()
+ {
+ var client = new HTXSocketClient(opts =>
+ {
+ opts.ApiCredentials = new CryptoExchange.Net.Authentication.ApiCredentials("123", "456");
+ });
+ var tester = new SocketSubscriptionValidator(client, "Subscriptions/UsdtMarginSwap", "wss://api.huobi.pro", stjCompare: true);
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToKlineUpdatesAsync("ETH-USDT", Enums.KlineInterval.OneDay, handler), "Klines", nestedJsonProperty: "tick");
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToOrderBookUpdatesAsync("ETH-USDT", 0, handler), "OrderBook", nestedJsonProperty: "tick", ignoreProperties: new List { "ch", "ts" });
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToIncrementalOrderBookUpdatesAsync("ETH-USDT", true, 20, handler), "OrderBookIncr", nestedJsonProperty: "tick", ignoreProperties: new List { "ch", "ts" });
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToTickerUpdatesAsync("ETH-USDT", handler), "Ticker", nestedJsonProperty: "tick");
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToBookTickerUpdatesAsync("ETH-USDT", handler), "BookTicker", nestedJsonProperty: "tick", ignoreProperties: new List { "ch" });
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToTradeUpdatesAsync("ETH-USDT", handler), "Trades", nestedJsonProperty: "tick", ignoreProperties: new List { "ch" });
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToIndexKlineUpdatesAsync("ETH-USDT", Enums.KlineInterval.OneDay, handler), "IndexKline", nestedJsonProperty: "tick", ignoreProperties: new List { "ch" });
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToPremiumIndexKlineUpdatesAsync("ETH-USDT", Enums.KlineInterval.OneDay, handler), "PremiumKline", nestedJsonProperty: "tick", ignoreProperties: new List { "ch" });
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToEstimatedFundingRateKlineUpdatesAsync("ETH-USDT", Enums.KlineInterval.OneDay, handler), "FundingKlines", nestedJsonProperty: "tick", ignoreProperties: new List { "ch" });
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToBasisUpdatesAsync("ETH-USDT", Enums.KlineInterval.OneDay, "open", handler), "Basis", nestedJsonProperty: "tick", ignoreProperties: new List { "ch" });
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToMarkPriceKlineUpdatesAsync("ETH-USDT", Enums.KlineInterval.OneDay, handler), "MarkKline", nestedJsonProperty: "tick", ignoreProperties: new List { "ch" });
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToLiquidationUpdatesAsync(handler), "Liquidation", nestedJsonProperty: "data");
+ await tester.ValidateAsync>((client, handler) => client.UsdtFuturesApi.SubscribeToFundingRateUpdatesAsync(handler), "Funding", nestedJsonProperty: "data");
+ await tester.ValidateAsync>((client, handler) => client.UsdtFuturesApi.SubscribeToContractUpdatesAsync(handler), "Contract", nestedJsonProperty: "data");
+ await tester.ValidateAsync>((client, handler) => client.UsdtFuturesApi.SubscribeToContractElementsUpdatesAsync(handler), "ContractElements", nestedJsonProperty: "data");
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToSystemStatusUpdatesAsync(handler), "Status");
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToOrderUpdatesAsync(Enums.MarginMode.Isolated, handler), "Order");
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToIsolatedMarginBalanceUpdatesAsync(handler), "IsolatedBalance");
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToCrossMarginBalanceUpdatesAsync(handler), "CrossBalance");
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToIsolatedMarginPositionUpdatesAsync(handler), "IsolatedPositions");
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToCrossMarginPositionUpdatesAsync(handler), "CrossPositions");
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToIsolatedMarginUserTradeUpdatesAsync(handler), "IsolatedTrades");
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToCrossMarginUserTradeUpdatesAsync(handler), "CrossTrades");
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToIsolatedMarginTriggerOrderUpdatesAsync(handler), "IsolatedTrigger");
+ await tester.ValidateAsync((client, handler) => client.UsdtFuturesApi.SubscribeToCrossMarginTriggerOrderUpdatesAsync(handler), "CrossTrigger");
+ }
+ }
+}
diff --git a/HTX.Net.UnitTests/Subscriptions/Spot/Account.txt b/HTX.Net.UnitTests/Subscriptions/Spot/Account.txt
new file mode 100644
index 00000000..2f347922
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/Spot/Account.txt
@@ -0,0 +1,18 @@
+> { "action": "req", "ch": "auth", "params": { "authType":"api", "accessKey": "|1|", "signatureMethod": "HmacSHA256", "signatureVersion": "2.1", "timestamp": "|2|", "signature": "|3|" } }
+< { "action": "req", "code": 200, "ch": "auth", "data": {} }
+> { "action": "sub", "ch": "accounts.update#1" }
+< { "action": "sub", "code": 200, "ch": "accounts.update#1", "data": {}}
+=
+{
+ "action": "push",
+ "ch": "accounts.update#1",
+ "data": {
+ "currency": "btc",
+ "accountId": 33385,
+ "available": "2028.699426619837209087",
+ "changeType": "order.match",
+ "accountType":"trade",
+ "seqNum": "86872993928",
+ "changeTime": 1574393385167
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/Spot/BookTicker.txt b/HTX.Net.UnitTests/Subscriptions/Spot/BookTicker.txt
new file mode 100644
index 00000000..f76815ef
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/Spot/BookTicker.txt
@@ -0,0 +1,16 @@
+> { "sub": "market.ethusdt.bbo", "id": "|1|" }
+< { "id": "|1|", "status": "ok", "subbed": "market.ethusdt.bbo", "ts": 1489474081631 }
+=
+{
+ "ch": "market.ethusdt.bbo",
+ "ts": 1630994555540,
+ "tick": {
+ "seqId": 137005210233,
+ "ask": 52665.02,
+ "askSize": 1.502181,
+ "bid": 52665.01,
+ "bidSize": 0.178567,
+ "quoteTime": 1630994555539,
+ "symbol": "btcusdt"
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/Spot/Klines.txt b/HTX.Net.UnitTests/Subscriptions/Spot/Klines.txt
new file mode 100644
index 00000000..01943b1e
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/Spot/Klines.txt
@@ -0,0 +1,17 @@
+> { "sub": "market.ethusdt.kline.1day", "id": "|1|" }
+< { "id": "|1|", "status": "ok", "subbed": "market.ethusdt.kline.1day", "ts": 1489474081631 }
+=
+{
+ "ch": "market.ethusdt.kline.1day",
+ "ts": 1630981694370,
+ "tick": {
+ "id": 1630981680,
+ "open": 0.074849,
+ "close": 0.074848,
+ "low": 0.074848,
+ "high": 0.074849,
+ "amount": 2.4448,
+ "vol": 0.1829884187,
+ "count": 3
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/Spot/Order1.txt b/HTX.Net.UnitTests/Subscriptions/Spot/Order1.txt
new file mode 100644
index 00000000..bb7e999a
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/Spot/Order1.txt
@@ -0,0 +1,24 @@
+> { "action": "req", "ch": "auth", "params": { "authType":"api", "accessKey": "|1|", "signatureMethod": "HmacSHA256", "signatureVersion": "2.1", "timestamp": "|2|", "signature": "|3|" } }
+< { "action": "req", "code": 200, "ch": "auth", "data": {} }
+> { "action": "sub", "ch": "orders#*" }
+< { "action": "sub", "code": 200, "ch": "orders#*", "data": {}}
+=
+{
+ "action":"push",
+ "ch":"orders#*",
+ "data":
+ {
+ "orderSize":"2.000000000000000000",
+ "orderCreateTime":1583853365586,
+ "accountId":992701,
+ "orderPrice":"77.000000000000000000",
+ "type":"sell-limit",
+ "orderId":27163533,
+ "clientOrderId":"abc123",
+ "orderSource":"spot-api",
+ "orderStatus":"submitted",
+ "symbol":"btcusdt",
+ "eventType":"creation"
+
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/Spot/OrderBook.txt b/HTX.Net.UnitTests/Subscriptions/Spot/OrderBook.txt
new file mode 100644
index 00000000..feae9a5e
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/Spot/OrderBook.txt
@@ -0,0 +1,31 @@
+> { "sub": "market.ethusdt.depth.step0", "id": "|1|" }
+< { "id": "|1|", "status": "ok", "subbed": "market.ethusdt.depth.step0", "ts": 1489474081631 }
+=
+{
+ "ch": "market.ethusdt.depth.step0",
+ "ts": 1630983549503,
+ "tick": {
+ "bids": [
+ [
+ 52690.69,
+ 0.36281
+ ],
+ [
+ 52690.68,
+ 0.2
+ ]
+ ],
+ "asks": [
+ [
+ 52690.7,
+ 0.372591
+ ],
+ [
+ 52691.26,
+ 0.13
+ ]
+ ],
+ "version": 136998124622,
+ "ts": 1630983549500
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/Spot/OrderBookChange.txt b/HTX.Net.UnitTests/Subscriptions/Spot/OrderBookChange.txt
new file mode 100644
index 00000000..db9ccc69
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/Spot/OrderBookChange.txt
@@ -0,0 +1,17 @@
+> { "sub": "market.ethusdt.mbp.5", "id": "|1|" }
+< { "id": "|1|", "status": "ok", "subbed": "market.ethusdt.mbp.5", "ts": 1489474081631 }
+=
+{
+ "ch": "market.ethusdt.mbp.5",
+ "ts": 1573199608679,
+ "tick": {
+ "seqNum": 100020146795,
+ "prevSeqNum": 100020146794,
+ "asks": [
+ [
+ 645.14,
+ 26.75597395914065
+ ]
+ ]
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/Spot/OrderBookMbp.txt b/HTX.Net.UnitTests/Subscriptions/Spot/OrderBookMbp.txt
new file mode 100644
index 00000000..41fe6cd4
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/Spot/OrderBookMbp.txt
@@ -0,0 +1,24 @@
+> { "sub": "market.ethusdt.mbp.refresh.20", "id": "|1|" }
+< { "id": "|1|", "status": "ok", "subbed": "market.ethusdt.mbp.refresh.20", "ts": 1489474081631 }
+=
+{
+ "ch": "market.ethusdt.mbp.refresh.20",
+ "ts": 1573199608679,
+ "tick": {
+ "seqNum": 100020142010,
+ "bids": [
+ [618.37, 71.594],
+ [423.33, 77.726],
+ [223.18, 47.997],
+ [219.34, 24.82],
+ [210.34, 94.463]
+ ],
+ "asks": [
+ [650.59, 14.909733438479636],
+ [650.63, 97.996],
+ [650.77, 97.465],
+ [651.23, 83.973],
+ [651.42, 34.465]
+ ]
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/Spot/OrderTrade.txt b/HTX.Net.UnitTests/Subscriptions/Spot/OrderTrade.txt
new file mode 100644
index 00000000..f0a76a11
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/Spot/OrderTrade.txt
@@ -0,0 +1,30 @@
+> { "action": "req", "ch": "auth", "params": { "authType":"api", "accessKey": "|1|", "signatureMethod": "HmacSHA256", "signatureVersion": "2.1", "timestamp": "|2|", "signature": "|3|" } }
+< { "action": "req", "code": 200, "ch": "auth", "data": {} }
+> { "action": "sub", "ch": "trade.clearing#*#1" }
+< { "action": "sub", "code": 200, "ch": "trade.clearing#*#1", "data": {}}
+=
+{
+ "ch": "trade.clearing#*#1",
+ "data": {
+ "eventType": "trade",
+ "symbol": "btcusdt",
+ "orderId": 99998888,
+ "tradePrice": "9999.99",
+ "tradeVolume": "0.96",
+ "orderSide": "buy",
+ "aggressor": true,
+ "tradeId": 919219323232,
+ "tradeTime": 998787897878,
+ "transactFee": "19.88",
+ "feeDeduct": "0",
+ "feeDeductType": "",
+ "feeCurrency": "btc",
+ "accountId": 9912791,
+ "source": "spot-api",
+ "orderPrice": "10000",
+ "orderSize": "1",
+ "clientOrderId": "a001",
+ "orderCreateTime": 998787897878,
+ "orderStatus": "partial-filled"
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/Spot/Symbols.txt b/HTX.Net.UnitTests/Subscriptions/Spot/Symbols.txt
new file mode 100644
index 00000000..789fe781
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/Spot/Symbols.txt
@@ -0,0 +1,18 @@
+> { "sub": "market.ethusdt.detail", "id": "|1|" }
+< { "id": "|1|", "status": "ok", "subbed": "market.ethusdt.detail", "ts": 1489474081631 }
+=
+{
+ "ch": "market.ethusdt.detail",
+ "ts": 1630998026649,
+ "tick": {
+ "id": 273956868110,
+ "low": 51000,
+ "high": 52924.14,
+ "open": 51823.62,
+ "close": 52379.99,
+ "vol": 727676440.200527,
+ "amount": 13991.028076056185,
+ "version": 273956868110,
+ "count": 471348
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/Spot/Ticker.txt b/HTX.Net.UnitTests/Subscriptions/Spot/Ticker.txt
new file mode 100644
index 00000000..3770a682
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/Spot/Ticker.txt
@@ -0,0 +1,22 @@
+> { "sub": "market.ethusdt.ticker", "id": "|1|" }
+< { "id": "|1|", "status": "ok", "subbed": "market.ethusdt.ticker", "ts": 1489474081631 }
+=
+{
+ "ch": "market.ethusdt.ticker",
+ "ts": 1630982370526,
+ "tick": {
+ "open": 51732,
+ "high": 52785.64,
+ "low": 51000,
+ "close": 52735.63,
+ "amount": 13259.24137056181,
+ "vol": 687640987.4125315,
+ "count": 448737,
+ "bid": 52732.88,
+ "bidSize": 0.036,
+ "ask": 52732.89,
+ "askSize": 0.583653,
+ "lastPrice": 52735.63,
+ "lastSize": 0.03
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/Spot/Tickers.txt b/HTX.Net.UnitTests/Subscriptions/Spot/Tickers.txt
new file mode 100644
index 00000000..89c6e72c
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/Spot/Tickers.txt
@@ -0,0 +1,4 @@
+> { "sub": "market.tickers", "id": "|1|" }
+< { "id": "|1|", "status": "ok", "subbed": "market.tickers", "ts": 1489474081631 }
+=
+{"ch":"market.tickers","status":"ok","ts":1722857569936,"data":[{"symbol":"sylousdt","open":9.7E-4,"high":0.001055,"low":8.29E-4,"close":8.97E-4,"amount":6.033648738732E8,"vol":566666.2460002552,"count":12651}]}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/Spot/Trades.txt b/HTX.Net.UnitTests/Subscriptions/Spot/Trades.txt
new file mode 100644
index 00000000..946f1eda
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/Spot/Trades.txt
@@ -0,0 +1,21 @@
+> { "sub": "market.ethusdt.trade.detail", "id": "|1|" }
+< { "id": "|1|", "status": "ok", "subbed": "market.ethusdt.trade.detail", "ts": 1489474081631 }
+=
+{
+ "ch": "market.ethusdt.trade.detail",
+ "ts": 1630994963175,
+ "tick": {
+ "id": 137005445109,
+ "ts": 1630994963173,
+ "data": [
+ {
+ "id": 1.3700544510935929e+26,
+ "ts": 1630994963173,
+ "tradeId": 102523573486,
+ "amount": 0.006754,
+ "price": 52648.62,
+ "direction": "buy"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Basis.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Basis.txt
new file mode 100644
index 00000000..26272e06
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Basis.txt
@@ -0,0 +1,14 @@
+> { "sub": "market.ETH-USDT.basis.1day.open", "id": "|1|" }
+< { "id": "|1|", "status": "ok", "subbed": "market.ETH-USDT.basis.1day.open", "ts": 1489474081631 }
+=
+{
+ "ch": "market.ETH-USDT.basis.1day.open",
+ "ts": 1617164081549,
+ "tick": {
+ "id": 1617164040,
+ "index_price": "58686.78333333333",
+ "contract_price": "58765",
+ "basis": "78.21666666667",
+ "basis_rate": "0.0013327816285723049700163397705562309"
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/BookTicker.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/BookTicker.txt
new file mode 100644
index 00000000..07b2914b
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/BookTicker.txt
@@ -0,0 +1,22 @@
+> { "sub": "market.ETH-USDT.bbo", "id": "|1|" }
+< { "id": "|1|", "status": "ok", "subbed": "market.ETH-USDT.bbo", "ts": 1489474081631 }
+=
+{
+ "ch": "market.ETH-USDT.bbo",
+ "ts": 1603707934525,
+ "tick": {
+ "mrid": 131599726,
+ "id": 1603707934,
+ "bid": [
+ 13064,
+ 38
+ ],
+ "ask": [
+ 13072.3,
+ 205
+ ],
+ "ts": 1603707934525,
+ "version": 131599726,
+ "ch": "market.BTC-USDT.bbo"
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Contract.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Contract.txt
new file mode 100644
index 00000000..0f375d80
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Contract.txt
@@ -0,0 +1,41 @@
+> { "op": "sub", "cid": "|1|", "topic": "public.*.contract_info" }
+< { "op": "sub", "cid": "|1|", "topic": "public.*.contract_info", "ts": 1670903745088, "err-code": 0 }
+=
+{
+ "op": "notify",
+ "topic": "public.*.contract_info",
+ "ts": 1639122053894,
+ "event": "init",
+ "data": [
+ {
+ "symbol": "MANA",
+ "contract_code": "MANA-USDT",
+ "contract_size": 10,
+ "price_tick": 0.0001,
+ "settlement_date": "1639123200000",
+ "create_date": "20210129",
+ "contract_status": 1,
+ "support_margin_mode": "all",
+ "delivery_time": "",
+ "contract_type": "swap",
+ "business_type": "swap",
+ "pair": "MANA-USDT",
+ "delivery_date": ""
+ },
+ {
+ "symbol": "NKN",
+ "contract_code": "NKN-USDT",
+ "contract_size": 10,
+ "price_tick": 0.00001,
+ "settlement_date": "1639123200000",
+ "create_date": "20210810",
+ "contract_status": 1,
+ "support_margin_mode": "all",
+ "delivery_time": "",
+ "contract_type": "swap",
+ "business_type": "swap",
+ "pair": "NKN-USDT",
+ "delivery_date": ""
+ }
+ ]
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/ContractElements.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/ContractElements.txt
new file mode 100644
index 00000000..014573a1
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/ContractElements.txt
@@ -0,0 +1,186 @@
+> { "op": "sub", "cid": "|1|", "topic": "public.*.contract_elements" }
+< { "op": "sub", "cid": "|1|", "topic": "public.*.contract_elements", "ts": 1670903745088, "err-code": 0 }
+=
+{
+ "op": "notify",
+ "event": "init",
+ "topic": "public.*.contract_elements",
+ "ts": 1712804933421,
+ "data": [{
+ "contract_code": "DOSE-USDT",
+ "funding_rate_cap": "0.007800000000000000",
+ "funding_rate_floor": "-0.007600000000000000",
+ "mode_type": 2,
+ "swap_delivery_type": 3,
+ "settle_period": 4,
+ "instrument_index_code": "DOSE-USDT",
+ "price_ticks": [
+ {
+ "business_type": 2,
+ "price": "0.000010000000000000"
+ },
+ {
+ "business_type": 1,
+ "price": "0.000000000100000000"
+ }
+ ],
+ "instrument_values": [
+ {
+ "business_type": 2,
+ "price": "0.000010000000000000"
+ },
+ {
+ "business_type": 1,
+ "price": "0.000010000000000000"
+ }
+ ],
+ "min_level": "1",
+ "max_level": "74",
+ "order_limits": [
+ {
+ "open_after_closing": "500000000000.000000000000000000",
+ "instrument_type": 1,
+ "open": "500000000000.000000000000000000",
+ "close": "500000000000.000000000000000000"
+ },
+ {
+ "open_after_closing": "500000000000.000000000000000000",
+ "instrument_type": 2,
+ "open": "500000000000.000000000000000000",
+ "close": "500000000000.000000000000000000"
+ },
+ {
+ "open_after_closing": "500000000000.000000000000000000",
+ "instrument_type": 3,
+ "open": "500000000000.000000000000000000",
+ "close": "500000000000.000000000000000000"
+ },
+ {
+ "open_after_closing": "500000000003.000000000000000000",
+ "instrument_type": 0,
+ "open": "500000000001.000000000000000000",
+ "close": "10005000000002.000000000000000000"
+ }
+ ],
+ "normal_limits": [
+ {
+ "instrument_type": 1,
+ "open": "999999.910000000000000000",
+ "close": "999999.920000000000000000"
+ },
+ {
+ "instrument_type": 2,
+ "open": "999999.990000000000000000",
+ "close": "999999.990000000000000000"
+ },
+ {
+ "instrument_type": 3,
+ "open": "999999.990000000000000000",
+ "close": "999999.990000000000000000"
+ },
+ {
+ "instrument_type": 0,
+ "open": "999999.910000000000000000",
+ "close": "999999.920000000000000000"
+ }
+ ],
+ "open_limits": [
+ {
+ "instrument_type": 1,
+ "open": "999999.930000000000000000",
+ "close": "999999.940000000000000000"
+ },
+ {
+ "instrument_type": 2,
+ "open": "999999.990000000000000000",
+ "close": "999999.990000000000000000"
+ },
+ {
+ "instrument_type": 3,
+ "open": "999999.990000000000000000",
+ "close": "999999.990000000000000000"
+ },
+ {
+ "instrument_type": 0,
+ "open": "999999.930000000000000000",
+ "close": "999999.940000000000000000"
+ }
+ ],
+ "trade_limits": [
+ {
+ "instrument_type": 1,
+ "open": "999999.950000000000000000",
+ "close": "999999.960000000000000000"
+ },
+ {
+ "instrument_type": 2,
+ "open": "999999.990000000000000000",
+ "close": "999999.990000000000000000"
+ },
+ {
+ "instrument_type": 3,
+ "open": "999999.990000000000000000",
+ "close": "999999.990000000000000000"
+ },
+ {
+ "instrument_type": 0,
+ "open": "999999.950000000000000000",
+ "close": "999999.960000000000000000"
+ }
+ ],
+ "real_time_settlement": 0,
+ "transfer_profit_ratio": 0,
+ "cross_transfer_profit_ratio": 1,
+ "instrument_type": [
+ 1,
+ 2,
+ 3,
+ 0
+ ],
+ "price_tick": "0.000000000100000000",
+ "instrument_value": "0.000010000000000000",
+ "trade_partition": "USDT",
+ "open_order_limit": "500000000001.000000000000000000",
+ "offset_order_limit": "10005000000002.000000000000000000",
+ "long_position_limit": "4000001.000000000000000000",
+ "short_position_limit": "4000002.000000000000000000",
+ "contract_infos": [
+ {
+ "contract_code": "DOSE-USDT-231027",
+ "instrument_type": 1,
+ "settlement_date": "1694592000000",
+ "delivery_time": "1698393600000",
+ "create_date": "20231024",
+ "contract_status": 1,
+ "delivery_date": "20231027"
+ },
+ {
+ "contract_code": "DOSE-USDT-231103",
+ "instrument_type": 2,
+ "settlement_date": "1694592000000",
+ "delivery_time": "1698998400000",
+ "create_date": "20231024",
+ "contract_status": 1,
+ "delivery_date": "20231103"
+ },
+ {
+ "contract_code": "DOSE-USDT-231229",
+ "instrument_type": 3,
+ "settlement_date": "1694592000000",
+ "delivery_time": "1703836800000",
+ "create_date": "20231024",
+ "contract_status": 1,
+ "delivery_date": "20231229"
+ },
+ {
+ "contract_code": "DOSE-USDT",
+ "instrument_type": 0,
+ "settlement_date": "1712822400000",
+ "delivery_time": "",
+ "create_date": "20231024",
+ "contract_status": 1,
+ "delivery_date": ""
+ }
+ ]
+ }]
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/CrossBalance.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/CrossBalance.txt
new file mode 100644
index 00000000..9cb5c62e
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/CrossBalance.txt
@@ -0,0 +1,60 @@
+> {"op": "auth","type": "api","AccessKeyId": "|1|","SignatureMethod": "HmacSHA256","SignatureVersion": "2","Timestamp": "|2|","Signature": "|3|"}
+< { "op": "auth", "type":"api", "ts": 1489474081631, "err-code": 0, "data": { "user-id": "12345678" } }
+> { "op": "sub", "cid": "|4|", "topic": "accounts_cross.*" }
+< { "op": "sub", "cid": "|4|", "topic": "accounts_cross.*", "ts": 1670903745088, "err-code": 0 }
+=
+{
+ "op": "notify",
+ "topic": "accounts_cross",
+ "ts": 1640756528985,
+ "event": "snapshot",
+ "data": [
+ {
+ "margin_mode": "cross",
+ "margin_account": "USDT",
+ "margin_asset": "USDT",
+ "margin_balance": 20.603401615553835,
+ "margin_static": 20.475701615553835,
+ "margin_position": 19.30352,
+ "margin_frozen": 0,
+ "profit_real": -0.01911684,
+ "profit_unreal": 0.1277,
+ "withdraw_available": 1.1721816155538354,
+ "risk_rate": 25.68347743773394,
+ "position_mode": "dual_side",
+ "contract_detail": [
+ {
+ "symbol": "BTC",
+ "contract_code": "BTC-USDT",
+ "margin_position": 9.55638,
+ "margin_frozen": 0,
+ "margin_available": 1.2998816155538353,
+ "profit_unreal": -0.0102,
+ "liquidation_price": 27790.709661740086,
+ "lever_rate": 5,
+ "adjust_factor": 0.04,
+ "contract_type": "swap",
+ "pair": "BTC-USDT",
+ "business_type": "swap"
+ }
+ ],
+ "futures_contract_detail": [
+ {
+ "symbol": "BTC",
+ "contract_code": "BTC-USDT-220325",
+ "margin_position": 9.74714,
+ "margin_frozen": 0,
+ "margin_available": 1.2998816155538353,
+ "profit_unreal": 0.1379,
+ "liquidation_price": 28744.509661740085,
+ "lever_rate": 5,
+ "adjust_factor": 0.04,
+ "contract_type": "quarter",
+ "pair": "BTC-USDT",
+ "business_type": "futures"
+ }
+ ]
+ }
+ ],
+ "uid": "123456789"
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/CrossPositions.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/CrossPositions.txt
new file mode 100644
index 00000000..acf12610
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/CrossPositions.txt
@@ -0,0 +1,38 @@
+> {"op": "auth","type": "api","AccessKeyId": "|1|","SignatureMethod": "HmacSHA256","SignatureVersion": "2","Timestamp": "|2|","Signature": "|3|"}
+< { "op": "auth", "type":"api", "ts": 1489474081631, "err-code": 0, "data": { "user-id": "12345678" } }
+> { "op": "sub", "cid": "|4|", "topic": "positions_cross.*" }
+< { "op": "sub", "cid": "|4|", "topic": "positions_cross.*", "ts": 1670903745088, "err-code": 0 }
+=
+{
+ "op": "notify",
+ "topic": "positions_cross",
+ "ts": 1639107468139,
+ "event": "order.match",
+ "data": [
+ {
+ "contract_type": "swap",
+ "pair": "BTC-USDT",
+ "business_type": "swap",
+ "symbol": "BTC",
+ "contract_code": "BTC-USDT",
+ "volume": 1,
+ "available": 1,
+ "frozen": 0,
+ "cost_open": 48284.9,
+ "cost_hold": 48284.9,
+ "profit_unreal": -0.0001,
+ "profit_rate": -0.000010355204214985,
+ "profit": -0.0001,
+ "margin_asset": "USDT",
+ "position_margin": 9.65696,
+ "lever_rate": 5,
+ "direction": "buy",
+ "last_price": 48284.8,
+ "margin_mode": "cross",
+ "margin_account": "USDT",
+ "position_mode": "dual_side",
+ "adl_risk_percent": "3"
+ }
+ ],
+ "uid": "123456789"
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/CrossTrades.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/CrossTrades.txt
new file mode 100644
index 00000000..ceea33c9
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/CrossTrades.txt
@@ -0,0 +1,45 @@
+> {"op": "auth","type": "api","AccessKeyId": "|1|","SignatureMethod": "HmacSHA256","SignatureVersion": "2","Timestamp": "|2|","Signature": "|3|"}
+< { "op": "auth", "type":"api", "ts": 1489474081631, "err-code": 0, "data": { "user-id": "12345678" } }
+> { "op": "sub", "cid": "|4|", "topic": "matchOrders_cross.*" }
+< { "op": "sub", "cid": "|4|", "topic": "matchOrders_cross.*", "ts": 1670903745088, "err-code": 0 }
+=
+{
+ "contract_type": "swap",
+ "pair": "BTC-USDT",
+ "business_type": "swap",
+ "op": "notify",
+ "topic": "matchOrders_cross",
+ "ts": 1639705640671,
+ "uid": "123456789",
+ "symbol": "BTC",
+ "contract_code": "BTC-USDT",
+ "status": 6,
+ "order_id": 921337601229725700,
+ "order_id_str": "921337601229725696",
+ "client_order_id": null,
+ "order_type": 1,
+ "volume": 1,
+ "trade_volume": 1,
+ "created_at": 1639705601752,
+ "direction": "sell",
+ "offset": "open",
+ "lever_rate": 5,
+ "price": 47800,
+ "order_source": "web",
+ "order_price_type": "limit",
+ "trade": [
+ {
+ "trade_id": 87890603387,
+ "id": "87890603387-921337601229725696-1",
+ "trade_volume": 1,
+ "trade_price": 47800,
+ "trade_turnover": 47.8,
+ "created_at": 1639705640641,
+ "role": "maker"
+ }
+ ],
+ "margin_mode": "cross",
+ "margin_account": "USDT",
+ "is_tpsl": 1,
+ "reduce_only": 0
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/CrossTrigger.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/CrossTrigger.txt
new file mode 100644
index 00000000..625fdf78
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/CrossTrigger.txt
@@ -0,0 +1,45 @@
+> {"op": "auth","type": "api","AccessKeyId": "|1|","SignatureMethod": "HmacSHA256","SignatureVersion": "2","Timestamp": "|2|","Signature": "|3|"}
+< { "op": "auth", "type":"api", "ts": 1489474081631, "err-code": 0, "data": { "user-id": "12345678" } }
+> { "op": "sub", "cid": "|4|", "topic": "trigger_order_cross.*" }
+< { "op": "sub", "cid": "|4|", "topic": "trigger_order_cross.*", "ts": 1670903745088, "err-code": 0 }
+=
+{
+ "op": "notify",
+ "topic": "trigger_order_cross.*",
+ "ts": 1639123353369,
+ "event": "order",
+ "uid": "123456789",
+ "data": [
+ {
+ "contract_type": "swap",
+ "pair": "BTC-USDT",
+ "business_type": "swap",
+ "symbol": "BTC",
+ "contract_code": "BTC-USDT",
+ "trigger_type": "le",
+ "volume": 1,
+ "order_type": 1,
+ "direction": "buy",
+ "offset": "open",
+ "lever_rate": 1,
+ "order_id": 918895474461802500,
+ "order_id_str": "918895474461802496",
+ "relation_order_id": "-1",
+ "order_price_type": "limit",
+ "status": 2,
+ "order_source": "api",
+ "trigger_price": 40000,
+ "triggered_price": null,
+ "order_price": 40000,
+ "created_at": 1639123353364,
+ "triggered_at": 0,
+ "order_insert_at": 0,
+ "canceled_at": 0,
+ "fail_code": null,
+ "fail_reason": null,
+ "margin_mode": "cross",
+ "margin_account": "USDT",
+ "reduce_only": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Funding.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Funding.txt
new file mode 100644
index 00000000..e0509e4a
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Funding.txt
@@ -0,0 +1,19 @@
+> { "op": "sub", "cid": "|1|", "topic": "public.*.funding_rate" }
+< { "op": "sub", "cid": "|1|", "topic": "public.*.funding_rate", "ts": 1670903745088, "err-code": 0 }
+=
+{
+ "op": "notify",
+ "topic": "public.*.funding_rate",
+ "ts": 1603778748166,
+ "data": [
+ {
+ "symbol": "BTC",
+ "contract_code": "BTC-USDT",
+ "fee_asset": "USDT",
+ "funding_time": "1603778700000",
+ "funding_rate": "-0.000220068774978695",
+ "settlement_time": "1603785600000",
+ "estimated_rate": null
+ }
+ ]
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/FundingKlines.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/FundingKlines.txt
new file mode 100644
index 00000000..ae858f38
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/FundingKlines.txt
@@ -0,0 +1,18 @@
+> { "sub": "market.ETH-USDT.estimated_rate.1day", "id": "|1|" }
+< { "id": "|1|", "status": "ok", "subbed": "market.ETH-USDT.estimated_rate.1day", "ts": 1489474081631 }
+=
+{
+ "ch": "market.ETH-USDT.estimated_rate.1day",
+ "ts": 1603708560233,
+ "tick": {
+ "id": 1603708560,
+ "open": "0.0001",
+ "close": "0.0001",
+ "high": "0.0001",
+ "low": "0.0001",
+ "amount": "0",
+ "vol": "0",
+ "count": "0",
+ "trade_turnover": "0"
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/IndexKline.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/IndexKline.txt
new file mode 100644
index 00000000..82fd3f7f
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/IndexKline.txt
@@ -0,0 +1,17 @@
+> { "sub": "market.ETH-USDT.index.1day", "id": "|1|" }
+< { "id": "|1|", "status": "ok", "subbed": "market.ETH-USDT.index.1day", "ts": 1489474081631 }
+=
+{
+ "ch": "market.ETH-USDT.index.1day",
+ "ts": 1607309592214,
+ "tick": {
+ "id": 1607309100,
+ "open": "19213.505",
+ "close": "19242.05",
+ "high": "19248.31",
+ "low": "19213.505",
+ "amount": "0",
+ "vol": "0",
+ "count": 0
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/IsolatedBalance.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/IsolatedBalance.txt
new file mode 100644
index 00000000..13c0c2fa
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/IsolatedBalance.txt
@@ -0,0 +1,34 @@
+> {"op": "auth","type": "api","AccessKeyId": "|1|","SignatureMethod": "HmacSHA256","SignatureVersion": "2","Timestamp": "|2|","Signature": "|3|"}
+< { "op": "auth", "type":"api", "ts": 1489474081631, "err-code": 0, "data": { "user-id": "12345678" } }
+> { "op": "sub", "cid": "|4|", "topic": "accounts.*" }
+< { "op": "sub", "cid": "|4|", "topic": "accounts.*", "ts": 1670903745088, "err-code": 0 }
+=
+{
+ "op": "notify",
+ "topic": "accounts",
+ "ts": 1603711370689,
+ "event": "order.open",
+ "data": [
+ {
+ "symbol": "BTC",
+ "contract_code": "BTC-USDT",
+ "margin_balance": 79.72434662,
+ "margin_static": 79.79484662,
+ "margin_position": 1.31303,
+ "margin_frozen": 4.0662,
+ "margin_available": 74.34511662,
+ "profit_real": 0.03405608,
+ "profit_unreal": -0.0705,
+ "withdraw_available": 74.34511662,
+ "risk_rate": 14.745772976801513,
+ "liquidation_price": 92163.42096277916,
+ "lever_rate": 10,
+ "adjust_factor": 0.075,
+ "margin_asset": "USDT",
+ "margin_mode": "isolated",
+ "margin_account": "BTC-USDT",
+ "position_mode": "dual_side"
+ }
+ ],
+ "uid": "123456789"
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/IsolatedPositions.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/IsolatedPositions.txt
new file mode 100644
index 00000000..f4422648
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/IsolatedPositions.txt
@@ -0,0 +1,35 @@
+> {"op": "auth","type": "api","AccessKeyId": "|1|","SignatureMethod": "HmacSHA256","SignatureVersion": "2","Timestamp": "|2|","Signature": "|3|"}
+< { "op": "auth", "type":"api", "ts": 1489474081631, "err-code": 0, "data": { "user-id": "12345678" } }
+> { "op": "sub", "cid": "|4|", "topic": "positions.*" }
+< { "op": "sub", "cid": "|4|", "topic": "positions.*", "ts": 1670903745088, "err-code": 0 }
+=
+{
+ "op": "notify",
+ "topic": "positions",
+ "ts": 1603711371803,
+ "event": "snapshot",
+ "data": [
+ {
+ "symbol": "BTC",
+ "contract_code": "BTC-USDT",
+ "volume": 1,
+ "available": 0,
+ "frozen": 1,
+ "cost_open": 13059.8,
+ "cost_hold": 13059.8,
+ "profit_unreal": -0.0705,
+ "profit_rate": -0.05398244996094886,
+ "profit": -0.0705,
+ "position_margin": 1.31303,
+ "lever_rate": 10,
+ "direction": "sell",
+ "last_price": 13130.3,
+ "margin_asset": "USDT",
+ "margin_mode": "isolated",
+ "margin_account": "BTC-USDT",
+ "position_mode": "dual_side",
+ "adl_risk_percent": "3"
+ }
+ ],
+ "uid": "123456789"
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/IsolatedTrades.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/IsolatedTrades.txt
new file mode 100644
index 00000000..9fc840ea
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/IsolatedTrades.txt
@@ -0,0 +1,42 @@
+> {"op": "auth","type": "api","AccessKeyId": "|1|","SignatureMethod": "HmacSHA256","SignatureVersion": "2","Timestamp": "|2|","Signature": "|3|"}
+< { "op": "auth", "type":"api", "ts": 1489474081631, "err-code": 0, "data": { "user-id": "12345678" } }
+> { "op": "sub", "cid": "|4|", "topic": "matchOrders.*" }
+< { "op": "sub", "cid": "|4|", "topic": "matchOrders.*", "ts": 1670903745088, "err-code": 0 }
+=
+{
+ "op": "notify",
+ "topic": "matchOrders",
+ "ts": 1600926986125,
+ "symbol": "BTC",
+ "contract_code": "BTC-USDT",
+ "status": 6,
+ "order_id": 758688290195656700,
+ "order_id_str": "758688290195656704",
+ "client_order_id": null,
+ "order_type": 1,
+ "created_at": 1600926984112,
+ "trade": [
+ {
+ "trade_id": 14470,
+ "id": "14470-758688290195656704-1",
+ "trade_volume": 1,
+ "trade_price": 10329.11,
+ "trade_turnover": 103.2911,
+ "created_at": 1600926986046,
+ "role": "taker"
+ }
+ ],
+ "uid": "123456789",
+ "volume": 1,
+ "trade_volume": 1,
+ "direction": "buy",
+ "offset": "open",
+ "lever_rate": 5,
+ "price": 10329.11,
+ "order_source": "web",
+ "order_price_type": "opponent",
+ "margin_mode": "isolated",
+ "margin_account": "BTC-USDT",
+ "is_tpsl": 0,
+ "reduce_only": 0
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/IsolatedTrigger.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/IsolatedTrigger.txt
new file mode 100644
index 00000000..e65cf213
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/IsolatedTrigger.txt
@@ -0,0 +1,42 @@
+> {"op": "auth","type": "api","AccessKeyId": "|1|","SignatureMethod": "HmacSHA256","SignatureVersion": "2","Timestamp": "|2|","Signature": "|3|"}
+< { "op": "auth", "type":"api", "ts": 1489474081631, "err-code": 0, "data": { "user-id": "12345678" } }
+> { "op": "sub", "cid": "|4|", "topic": "trigger_order.*" }
+< { "op": "sub", "cid": "|4|", "topic": "trigger_order.*", "ts": 1670903745088, "err-code": 0 }
+=
+{
+ "op": "notify",
+ "topic": "trigger_order",
+ "ts": 1603778055069,
+ "event": "order",
+ "uid": "123456789",
+ "data": [
+ {
+ "symbol": "BTC-USDT",
+ "contract_code": "BTC-USDT",
+ "trigger_type": "ge",
+ "volume": 1,
+ "order_type": 1,
+ "direction": "sell",
+ "offset": "open",
+ "lever_rate": 10,
+ "order_id": 5,
+ "order_id_str": "5",
+ "relation_order_id": "-1",
+ "order_price_type": "limit",
+ "status": 2,
+ "order_source": "web",
+ "trigger_price": 15000,
+ "triggered_price": null,
+ "order_price": 15000,
+ "created_at": 1603778055064,
+ "triggered_at": 0,
+ "order_insert_at": 0,
+ "canceled_at": 0,
+ "margin_mode": "isolated",
+ "margin_account": "BTC-USDT",
+ "fail_code": null,
+ "fail_reason": null,
+ "reduce_only": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Klines.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Klines.txt
new file mode 100644
index 00000000..48936593
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Klines.txt
@@ -0,0 +1,19 @@
+> { "sub": "market.ETH-USDT.kline.1day", "id": "|1|" }
+< { "id": "|1|", "status": "ok", "subbed": "market.ETH-USDT.kline.1day", "ts": 1489474081631 }
+=
+{
+ "ch": "market.ETH-USDT.kline.1day",
+ "ts": 1603707124366,
+ "tick": {
+ "id": 1603707120,
+ "mrid": 131592424,
+ "open": 13067.7,
+ "close": 13067.7,
+ "high": 13067.7,
+ "low": 13067.7,
+ "amount": 0.004,
+ "vol": 4,
+ "trade_turnover": 52.2708,
+ "count": 1
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Liquidation.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Liquidation.txt
new file mode 100644
index 00000000..a23e4cf9
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Liquidation.txt
@@ -0,0 +1,24 @@
+> { "op": "sub", "cid": "|1|", "topic": "public.*.liquidation_orders" }
+< { "op": "sub", "cid": "|1|", "topic": "public.*.liquidation_orders", "ts": 1670903745088, "err-code": 0 }
+=
+{
+ "op": "notify",
+ "topic": "public.*.liquidation_orders",
+ "ts": 1639122193214,
+ "data": [
+ {
+ "symbol": "O3",
+ "contract_code": "O3-USDT",
+ "direction": "sell",
+ "offset": "close",
+ "volume": 432,
+ "price": 0.7858,
+ "created_at": 1639122193172,
+ "amount": 432,
+ "trade_turnover": 339.4656,
+ "contract_type": "swap",
+ "pair": "O3-USDT",
+ "business_type": "swap"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/MarkKline.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/MarkKline.txt
new file mode 100644
index 00000000..8d4f1577
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/MarkKline.txt
@@ -0,0 +1,18 @@
+> { "sub": "market.ETH-USDT.mark_price.1day", "id": "|1|" }
+< { "id": "|1|", "status": "ok", "subbed": "market.ETH-USDT.mark_price.1day", "ts": 1489474081631 }
+=
+{
+ "ch": "market.ETH-USDT.mark_price.1day",
+ "ts": 1489474082831,
+ "tick": {
+ "vol": "0",
+ "close": "9800.12",
+ "count": "0",
+ "high": "9800.12",
+ "id": 1529898780,
+ "low": "9800.12",
+ "open": "9800.12",
+ "trade_turnover": "0",
+ "amount": "0"
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Order.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Order.txt
new file mode 100644
index 00000000..b81db333
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Order.txt
@@ -0,0 +1,58 @@
+> {"op": "auth","type": "api","AccessKeyId": "|1|","SignatureMethod": "HmacSHA256","SignatureVersion": "2","Timestamp": "|2|","Signature": "|3|"}
+< { "op": "auth", "type":"api", "ts": 1489474081631, "err-code": 0, "data": { "user-id": "12345678" } }
+> { "op": "sub", "cid": "|4|", "topic": "orders.*" }
+< { "op": "sub", "cid": "|4|", "topic": "orders.*", "ts": 1670903745088, "err-code": 0 }
+=
+{
+ "op": "notify",
+ "topic": "orders.BTC-USDT",
+ "ts": 1489474082831,
+ "uid": "123456789",
+ "symbol": "BTC",
+ "contract_code": "BTC-USDT",
+ "volume": 111,
+ "price": 1111,
+ "order_price_type": "limit",
+ "direction": "buy",
+ "offset": "open",
+ "status": 6,
+ "lever_rate": 10,
+ "order_id": 758684042347171800,
+ "order_id_str": "758684042347171840",
+ "client_order_id": 10683,
+ "order_source": "web",
+ "order_type": 1,
+ "created_at": 1408076414000,
+ "trade_volume": 1,
+ "trade_turnover": 1200,
+ "fee": 0,
+ "liquidation_type": "0",
+ "trade_avg_price": 10,
+ "margin_asset": "USDT",
+ "margin_frozen": 10,
+ "profit": 2,
+ "canceled_at": 1408076414000,
+ "fee_asset": "USDT",
+ "margin_mode": "isolated",
+ "margin_account": "BTC-USDT",
+ "is_tpsl": 0,
+ "real_profit": 0,
+ "reduce_only": 0,
+ "canceled_source": "timeout-canceled-order",
+ "trade": [
+ {
+ "trade_id": 14469,
+ "id": "14469-758684042347171840-1",
+ "trade_volume": 1,
+ "trade_price": 123.4555,
+ "trade_fee": 0.234,
+ "fee_asset": "USDT",
+ "price": "",
+ "trade_turnover": 34.123,
+ "created_at": 1490759594752,
+ "role": "maker",
+ "profit": 2,
+ "real_profit": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/OrderBook.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/OrderBook.txt
new file mode 100644
index 00000000..29880f70
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/OrderBook.txt
@@ -0,0 +1,34 @@
+> { "sub": "market.ETH-USDT.depth.step0", "id": "|1|" }
+< { "id": "|1|", "status": "ok", "subbed": "market.ETH-USDT.depth.step0", "ts": 1489474081631 }
+=
+{
+ "ch": "market.ETH-USDT.depth.step0",
+ "ts": 1603707576468,
+ "tick": {
+ "mrid": 131596447,
+ "id": 1603707576,
+ "bids": [
+ [
+ 13071.9,
+ 38
+ ],
+ [
+ 13068,
+ 5
+ ]
+ ],
+ "asks": [
+ [
+ 13081.9,
+ 197
+ ],
+ [
+ 13099.7,
+ 371
+ ]
+ ],
+ "ts": 1603707576467,
+ "version": 1603707576,
+ "ch": "market.BTC-USDT.depth.step6"
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/OrderBookIncr.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/OrderBookIncr.txt
new file mode 100644
index 00000000..f8bc6c1c
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/OrderBookIncr.txt
@@ -0,0 +1,35 @@
+> { "sub": "market.ETH-USDT.depth.size_20.high_freq", "id": "|1|" }
+< { "id": "|1|", "status": "ok", "subbed": "market.ETH-USDT.depth.size_20.high_freq", "ts": 1489474081631 }
+=
+{
+ "ch": "market.ETH-USDT.depth.size_20.high_freq",
+ "tick": {
+ "asks": [
+ [
+ 13081.9,
+ 206
+ ],
+ [
+ 13099.7,
+ 371
+ ]
+ ],
+ "bids": [
+ [
+ 13071.9,
+ 38
+ ],
+ [
+ 13060,
+ 400
+ ]
+ ],
+ "ch": "market.BTC-USDT.depth.size_20.high_freq",
+ "event": "snapshot",
+ "id": 131597620,
+ "mrid": 131597620,
+ "ts": 1603707712356,
+ "version": 1512467
+ },
+ "ts": 1603707712357
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/PremiumKline.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/PremiumKline.txt
new file mode 100644
index 00000000..d1654cd2
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/PremiumKline.txt
@@ -0,0 +1,17 @@
+> { "sub": "market.ETH-USDT.premium_index.1day", "id": "|1|" }
+< { "id": "|1|", "status": "ok", "subbed": "market.ETH-USDT.premium_index.1day", "ts": 1489474081631 }
+=
+{
+ "ch": "market.ETH-USDT.premium_index.1day",
+ "ts": 1603708380380,
+ "tick": {
+ "id": 1603708380,
+ "open": "0.000068125",
+ "close": "0.000068125",
+ "high": "0.000068125",
+ "low": "0.000068125",
+ "amount": "0",
+ "vol": "0",
+ "count": "0"
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Status.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Status.txt
new file mode 100644
index 00000000..567a1de0
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Status.txt
@@ -0,0 +1,13 @@
+> { "op": "sub", "cid": "|1|", "topic": "public.linear-swap.heartbeat" }
+< { "op": "sub", "cid": "|1|", "topic": "public.linear-swap.heartbeat", "ts": 1670903745088, "err-code": 0 }
+=
+{
+ "op": "notify",
+ "topic": "public.linear-swap.heartbeat",
+ "event": "init",
+ "ts": 1580815422403,
+ "data": {
+ "heartbeat": 0,
+ "estimated_recovery_time": 1408076414000
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Ticker.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Ticker.txt
new file mode 100644
index 00000000..e55b58d4
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Ticker.txt
@@ -0,0 +1,27 @@
+> { "sub": "market.ETH-USDT.detail", "id": "|1|" }
+< { "id": "|1|", "status": "ok", "subbed": "market.ETH-USDT.detail", "ts": 1489474081631 }
+=
+{
+ "ch": "market.ETH-USDT.detail",
+ "ts": 1603707870528,
+ "tick": {
+ "id": 1603707840,
+ "mrid": 131599205,
+ "open": 12916.2,
+ "close": 13065.8,
+ "high": 13205.3,
+ "low": 12852.8,
+ "amount": 30.316,
+ "vol": 30316,
+ "trade_turnover": 395073.4918,
+ "count": 2983,
+ "ask": [
+ 13081.9,
+ 206
+ ],
+ "bid": [
+ 13071.9,
+ 38
+ ]
+ }
+}
\ No newline at end of file
diff --git a/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Trades.txt b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Trades.txt
new file mode 100644
index 00000000..dbb3942f
--- /dev/null
+++ b/HTX.Net.UnitTests/Subscriptions/UsdtMarginSwap/Trades.txt
@@ -0,0 +1,4 @@
+> { "sub": "market.ETH-USDT.trade.detail", "id": "|1|" }
+< { "id": "|1|", "status": "ok", "subbed": "market.ETH-USDT.trade.detail", "ts": 1489474081631 }
+=
+{"ch":"market.ETH-USDT.trade.detail","ts":1722863196449,"tick":{"id":100021951274748,"ts":1722863196444,"data":[{"amount":196,"quantity":1.96,"trade_turnover":4403.1204,"ts":1722863196444,"id":1000219512747480000,"price":2246.49,"direction":"sell"}]}}
\ No newline at end of file
diff --git a/Huobi.Net.UnitTests/TestImplementations/TestHelpers.cs b/HTX.Net.UnitTests/TestImplementations/TestHelpers.cs
similarity index 77%
rename from Huobi.Net.UnitTests/TestImplementations/TestHelpers.cs
rename to HTX.Net.UnitTests/TestImplementations/TestHelpers.cs
index 1dc05acf..ee7d1c9c 100644
--- a/Huobi.Net.UnitTests/TestImplementations/TestHelpers.cs
+++ b/HTX.Net.UnitTests/TestImplementations/TestHelpers.cs
@@ -9,22 +9,18 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using CryptoExchange.Net;
using CryptoExchange.Net.Authentication;
using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Objects.Sockets;
-using CryptoExchange.Net.Sockets;
-using Huobi.Net.Clients;
-using Huobi.Net.Enums;
-using Huobi.Net.Interfaces;
-using Huobi.Net.Interfaces.Clients;
-using Huobi.Net.Objects;
-using Huobi.Net.Objects.Options;
+using HTX.Net.Clients;
+using HTX.Net.Enums;
+using HTX.Net.Interfaces.Clients;
+using HTX.Net.Objects.Options;
using Microsoft.Extensions.Logging;
using Moq;
using Newtonsoft.Json;
-namespace Huobi.Net.UnitTests.TestImplementations
+namespace HTX.Net.UnitTests.TestImplementations
{
public class TestHelpers
{
@@ -68,55 +64,55 @@ public static bool AreEqual(T self, T to, params string[] ignore) where T : c
return self == to;
}
- public static HuobiSocketClient CreateSocketClient(IWebsocket socket, Action options = null)
+ public static HTXSocketClient CreateSocketClient(IWebsocket socket, Action options = null)
{
- HuobiSocketClient client;
- client = options != null ? new HuobiSocketClient(options) : new HuobiSocketClient(x => { x.ApiCredentials = new ApiCredentials("Test", "Test"); });
+ HTXSocketClient client;
+ client = options != null ? new HTXSocketClient(options) : new HTXSocketClient(x => { x.ApiCredentials = new ApiCredentials("Test", "Test"); });
client.SpotApi.SocketFactory = Mock.Of();
Mock.Get(client.SpotApi.SocketFactory).Setup(f => f.CreateWebsocket(It.IsAny(), It.IsAny())).Returns(socket);
return client;
}
- public static HuobiSocketClient CreateAuthenticatedSocketClient(IWebsocket socket, Action options = null)
+ public static HTXSocketClient CreateAuthenticatedSocketClient(IWebsocket socket, Action options = null)
{
- HuobiSocketClient client;
- client = options != null ? new HuobiSocketClient(options) : new HuobiSocketClient(x => { x.ApiCredentials = new ApiCredentials("Test", "Test"); });
+ HTXSocketClient client;
+ client = options != null ? new HTXSocketClient(options) : new HTXSocketClient(x => { x.ApiCredentials = new ApiCredentials("Test", "Test"); });
client.SpotApi.SocketFactory = Mock.Of();
Mock.Get(client.SpotApi.SocketFactory).Setup(f => f.CreateWebsocket(It.IsAny(), It.IsAny())).Returns(socket);
return client;
}
- public static IHuobiRestClient CreateClient(Action options = null)
+ public static IHTXRestClient CreateClient(Action options = null)
{
- IHuobiRestClient client;
- client = options != null ? new HuobiRestClient(options) : new HuobiRestClient();
+ IHTXRestClient client;
+ client = options != null ? new HTXRestClient(options) : new HTXRestClient();
client.SpotApi.RequestFactory = Mock.Of();
return client;
}
- public static IHuobiRestClient CreateAuthResponseClient(string response, HttpStatusCode statusCode = HttpStatusCode.OK)
+ public static IHTXRestClient CreateAuthResponseClient(string response, HttpStatusCode statusCode = HttpStatusCode.OK)
{
- var client = (HuobiRestClient)CreateClient(x => { x.ApiCredentials = new ApiCredentials("Test", "test"); });
+ var client = (HTXRestClient)CreateClient(x => { x.ApiCredentials = new ApiCredentials("Test", "test"); });
SetResponse(client, response, statusCode);
return client;
}
- public static IHuobiRestClient CreateResponseClient(string response, Action options = null)
+ public static IHTXRestClient CreateResponseClient(string response, Action options = null)
{
- var client = (HuobiRestClient)CreateClient(options);
+ var client = (HTXRestClient)CreateClient(options);
SetResponse(client, response);
return client;
}
- public static IHuobiRestClient CreateResponseClient(T response, Action options = null)
+ public static IHTXRestClient CreateResponseClient(T response, Action options = null)
{
- var client = (HuobiRestClient)CreateClient(options);
+ var client = (HTXRestClient)CreateClient(options);
SetResponse(client, JsonConvert.SerializeObject(response));
return client;
}
- public static void SetResponse(HuobiRestClient client, string responseData, HttpStatusCode statusCode = HttpStatusCode.OK)
+ public static void SetResponse(HTXRestClient client, string responseData, HttpStatusCode statusCode = HttpStatusCode.OK)
{
var expectedBytes = Encoding.UTF8.GetBytes(responseData);
var responseStream = new MemoryStream();
@@ -176,8 +172,8 @@ public static object GetTestValue(Type type, int i)
if (type == typeof(IEnumerable))
return new[] { "string" + i };
- if (type == typeof(IEnumerable))
- return new[] { OrderState.Canceled };
+ if (type == typeof(IEnumerable))
+ return new[] { OrderStatus.Canceled };
if (type.IsEnum)
{
diff --git a/Huobi.Net.UnitTests/TestImplementations/TestSocket.cs b/HTX.Net.UnitTests/TestImplementations/TestSocket.cs
similarity index 98%
rename from Huobi.Net.UnitTests/TestImplementations/TestSocket.cs
rename to HTX.Net.UnitTests/TestImplementations/TestSocket.cs
index af9444db..e1fc7dd1 100644
--- a/Huobi.Net.UnitTests/TestImplementations/TestSocket.cs
+++ b/HTX.Net.UnitTests/TestImplementations/TestSocket.cs
@@ -1,5 +1,4 @@
using System;
-using System.IO;
using System.Net.WebSockets;
using System.Security.Authentication;
using System.Text;
@@ -8,7 +7,7 @@
using CryptoExchange.Net.Objects;
using Newtonsoft.Json;
-namespace Huobi.Net.UnitTests.TestImplementations
+namespace HTX.Net.UnitTests.TestImplementations
{
public class TestSocket: IWebsocket
{
diff --git a/Huobi.Net.sln b/HTX.Net.sln
similarity index 81%
rename from Huobi.Net.sln
rename to HTX.Net.sln
index 0780bd99..95eed85d 100644
--- a/Huobi.Net.sln
+++ b/HTX.Net.sln
@@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2002
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Huobi.Net", "Huobi.Net\Huobi.Net.csproj", "{A58E8B0F-D5E5-42D0-9BC1-9CFDDE192582}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HTX.Net", "HTX.Net\HTX.Net.csproj", "{A58E8B0F-D5E5-42D0-9BC1-9CFDDE192582}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Huobi.Net.UnitTests", "Huobi.Net.UnitTests\Huobi.Net.UnitTests.csproj", "{AC3A5D12-F046-45D4-8815-4E4267FA4EE0}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HTX.Net.UnitTests", "HTX.Net.UnitTests\HTX.Net.UnitTests.csproj", "{AC3A5D12-F046-45D4-8815-4E4267FA4EE0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/Huobi.Net/AssemblyInfo.cs b/HTX.Net/AssemblyInfo.cs
similarity index 72%
rename from Huobi.Net/AssemblyInfo.cs
rename to HTX.Net/AssemblyInfo.cs
index 4d3ae458..678de02f 100644
--- a/Huobi.Net/AssemblyInfo.cs
+++ b/HTX.Net/AssemblyInfo.cs
@@ -1 +1 @@
-[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Huobi.Net.UnitTests")]
\ No newline at end of file
+[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("HTX.Net.UnitTests")]
\ No newline at end of file
diff --git a/HTX.Net/Clients/HTXRestClient.cs b/HTX.Net/Clients/HTXRestClient.cs
new file mode 100644
index 00000000..69b99022
--- /dev/null
+++ b/HTX.Net/Clients/HTXRestClient.cs
@@ -0,0 +1,72 @@
+using HTX.Net.Clients.SpotApi;
+using HTX.Net.Interfaces.Clients;
+using HTX.Net.Interfaces.Clients.SpotApi;
+using HTX.Net.Objects.Options;
+using CryptoExchange.Net.Clients;
+using HTX.Net.Interfaces.Clients.UsdtFuturesApi;
+using HTX.Net.Clients.UsdtFutures;
+
+namespace HTX.Net.Clients
+{
+ ///
+ public class HTXRestClient : BaseRestClient, IHTXRestClient
+ {
+ #region Api clients
+
+ ///
+ public IHTXRestClientSpotApi SpotApi { get; }
+
+ ///
+ public IHTXRestClientUsdtFuturesApi UsdtFuturesApi { get; }
+
+ #endregion
+
+ #region constructor/destructor
+ ///
+ /// Create a new instance of the HTXRestClient using provided options
+ ///
+ /// Option configuration delegate
+ public HTXRestClient(Action? optionsDelegate = null) : this(null, null, optionsDelegate)
+ {
+ }
+
+ ///
+ /// Create a new instance of the HTXRestClient
+ ///
+ /// Option configuration delegate
+ /// The logger factory
+ /// Http client for this client
+ public HTXRestClient(HttpClient? httpClient, ILoggerFactory? loggerFactory, Action? optionsDelegate = null)
+ : base(loggerFactory, "HTX")
+ {
+ var options = HTXRestOptions.Default.Copy();
+ if (optionsDelegate != null)
+ optionsDelegate(options);
+ Initialize(options);
+
+ SpotApi = AddApiClient(new HTXRestClientSpotApi(_logger, httpClient, options));
+ UsdtFuturesApi = AddApiClient(new HTXRestClientUsdtFuturesApi(_logger, httpClient, options));
+ }
+ #endregion
+
+ #region methods
+ ///
+ /// Set the default options to be used when creating new clients
+ ///
+ /// Option configuration delegate
+ public static void SetDefaultOptions(Action optionsDelegate)
+ {
+ var options = HTXRestOptions.Default.Copy();
+ optionsDelegate(options);
+ HTXRestOptions.Default = options;
+ }
+
+ ///
+ public void SetApiCredentials(ApiCredentials apiCredentials)
+ {
+ SpotApi.SetApiCredentials(apiCredentials);
+ UsdtFuturesApi.SetApiCredentials(apiCredentials);
+ }
+ #endregion
+ }
+}
diff --git a/HTX.Net/Clients/HTXSocketClient.cs b/HTX.Net/Clients/HTXSocketClient.cs
new file mode 100644
index 00000000..fff8b4b0
--- /dev/null
+++ b/HTX.Net/Clients/HTXSocketClient.cs
@@ -0,0 +1,74 @@
+using CryptoExchange.Net.Clients;
+using HTX.Net.Clients.SpotApi;
+using HTX.Net.Clients.UsdtFutures;
+using HTX.Net.Interfaces.Clients;
+using HTX.Net.Interfaces.Clients.SpotApi;
+using HTX.Net.Interfaces.Clients.UsdtFuturesApi;
+using HTX.Net.Objects.Options;
+
+namespace HTX.Net.Clients
+{
+ ///
+ public class HTXSocketClient : BaseSocketClient, IHTXSocketClient
+ {
+ #region fields
+ ///
+ public IHTXSocketClientSpotApi SpotApi { get; }
+ ///
+ public IHTXSocketClientUsdtFuturesApi UsdtFuturesApi { get; }
+ #endregion
+
+ #region ctor
+ ///
+ /// Create a new instance of the HTXSocketClient
+ ///
+ /// The logger factory
+ public HTXSocketClient(ILoggerFactory? loggerFactory = null) : this((x) => { }, loggerFactory)
+ {
+ }
+
+ ///
+ /// Create a new instance of the HTXSocketClient
+ ///
+ /// Option configuration delegate
+ public HTXSocketClient(Action optionsDelegate) : this(optionsDelegate, null)
+ {
+ }
+
+ ///
+ /// Create a new instance of the HTXSocketClient
+ ///
+ /// The logger factory
+ /// Option configuration delegate
+ public HTXSocketClient(Action optionsDelegate, ILoggerFactory? loggerFactory = null) : base(loggerFactory, "HTX")
+ {
+ var options = HTXSocketOptions.Default.Copy();
+ optionsDelegate(options);
+ Initialize(options);
+
+ SpotApi = AddApiClient(new HTXSocketClientSpotApi(_logger, options));
+ UsdtFuturesApi = AddApiClient(new HTXSocketClientUsdtFuturesApi(_logger, options));
+ }
+ #endregion
+
+ #region methods
+ ///
+ /// Set the default options to be used when creating new clients
+ ///
+ /// Option configuration delegate
+ public static void SetDefaultOptions(Action optionsDelegate)
+ {
+ var options = HTXSocketOptions.Default.Copy();
+ optionsDelegate(options);
+ HTXSocketOptions.Default = options;
+ }
+
+ ///
+ public void SetApiCredentials(ApiCredentials apiCredentials)
+ {
+ SpotApi.SetApiCredentials(apiCredentials);
+ UsdtFuturesApi.SetApiCredentials(apiCredentials);
+ }
+ #endregion
+ }
+}
diff --git a/Huobi.Net/Clients/SpotApi/HuobiRestClientSpotApi.cs b/HTX.Net/Clients/SpotApi/HTXRestClientSpotApi.cs
similarity index 68%
rename from Huobi.Net/Clients/SpotApi/HuobiRestClientSpotApi.cs
rename to HTX.Net/Clients/SpotApi/HTXRestClientSpotApi.cs
index 0feccc35..138214fb 100644
--- a/Huobi.Net/Clients/SpotApi/HuobiRestClientSpotApi.cs
+++ b/HTX.Net/Clients/SpotApi/HTXRestClientSpotApi.cs
@@ -1,30 +1,19 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Net.Http;
-using System.Threading;
-using System.Threading.Tasks;
-using CryptoExchange.Net.Authentication;
-using CryptoExchange.Net.Clients;
+using CryptoExchange.Net.Clients;
using CryptoExchange.Net.CommonObjects;
using CryptoExchange.Net.Converters.MessageParsing;
using CryptoExchange.Net.Interfaces.CommonClients;
-using CryptoExchange.Net.Objects;
-using Huobi.Net.Enums;
-using Huobi.Net.Interfaces.Clients.SpotApi;
-using Huobi.Net.Objects.Internal;
-using Huobi.Net.Objects.Options;
-using Microsoft.Extensions.Logging;
-using Newtonsoft.Json.Linq;
-
-namespace Huobi.Net.Clients.SpotApi
+using HTX.Net.Enums;
+using HTX.Net.Interfaces.Clients.SpotApi;
+using HTX.Net.Objects.Internal;
+using HTX.Net.Objects.Options;
+
+namespace HTX.Net.Clients.SpotApi
{
///
- internal class HuobiRestClientSpotApi : RestApiClient, IHuobiRestClientSpotApi, ISpotClient
+ internal class HTXRestClientSpotApi : RestApiClient, IHTXRestClientSpotApi, ISpotClient
{
///
- public new HuobiRestOptions ClientOptions => (HuobiRestOptions)base.ClientOptions;
+ public new HTXRestOptions ClientOptions => (HTXRestOptions)base.ClientOptions;
internal static TimeSyncState _timeSyncState = new TimeSyncState("Spot Api");
@@ -38,45 +27,62 @@ internal class HuobiRestClientSpotApi : RestApiClient, IHuobiRestClientSpotApi,
public event Action? OnOrderCanceled;
///
- public string ExchangeName => "Huobi";
+ public string ExchangeName => "HTX";
internal readonly string _brokerId;
#region Api clients
///
- public IHuobiRestClientSpotApiAccount Account { get; }
+ public IHTXRestClientSpotApiAccount Account { get; }
+ ///
+ public IHTXRestClientSpotApiExchangeData ExchangeData { get; }
+ ///
+ public IHTXRestClientSpotApiMargin Margin { get; }
///
- public IHuobiRestClientSpotApiExchangeData ExchangeData { get; }
+ public IHTXRestClientSpotApiSubAccount SubAccount { get; }
///
- public IHuobiRestClientSpotApiTrading Trading { get; }
+ public IHTXRestClientSpotApiTrading Trading { get; }
#endregion
#region constructor/destructor
- internal HuobiRestClientSpotApi(ILogger logger, HttpClient? httpClient, HuobiRestOptions options)
+ internal HTXRestClientSpotApi(ILogger logger, HttpClient? httpClient, HTXRestOptions options)
: base(logger, httpClient, options.Environment.RestBaseAddress, options, options.SpotOptions)
{
- Account = new HuobiRestClientSpotApiAccount(this);
- ExchangeData = new HuobiRestClientSpotApiExchangeData(this);
- Trading = new HuobiRestClientSpotApiTrading(this);
+ Account = new HTXRestClientSpotApiAccount(this);
+ ExchangeData = new HTXRestClientSpotApiExchangeData(this);
+ SubAccount = new HTXRestClientSpotApiSubAccount(this);
+ Margin = new HTXRestClientSpotApiMargin(this);
+ Trading = new HTXRestClientSpotApiTrading(this);
_brokerId = !string.IsNullOrEmpty(options.BrokerId) ? options.BrokerId! : "AA1ef14811";
}
#endregion
+ protected override IStreamMessageAccessor CreateAccessor() => new SystemTextJsonStreamMessageAccessor();
+
+ protected override IMessageSerializer CreateSerializer() => new SystemTextJsonMessageSerializer();
+
///
public override string FormatSymbol(string baseAsset, string quoteAsset) => $"{baseAsset.ToLowerInvariant()}{quoteAsset.ToLowerInvariant()}";
///
protected override AuthenticationProvider CreateAuthenticationProvider(ApiCredentials credentials)
- => new HuobiAuthenticationProvider(credentials, ClientOptions.SignPublicRequests);
+ => new HTXAuthenticationProvider(credentials, ClientOptions.SignPublicRequests);
#region methods
+ internal async Task> SendToAddressRawAsync(string baseAddress, RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null) where T : class
+ {
+ return await base.SendAsync(baseAddress, definition, parameters, cancellationToken, null, weight).ConfigureAwait(false);
+ }
+
+ internal Task> SendAsync(RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null)
+ => SendToAddressAsync(BaseAddress, definition, parameters, cancellationToken, weight);
- internal async Task> SendHuobiV2Request(Uri uri, HttpMethod method, CancellationToken cancellationToken, Dictionary? parameters = null, bool signed = false)
+ internal async Task> SendToAddressAsync(string baseAddress, RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null)
{
- var result = await SendRequestAsync>(uri, method, cancellationToken, parameters, signed, requestWeight: 0).ConfigureAwait(false);
+ var result = await base.SendAsync>(baseAddress, definition, parameters, cancellationToken, null, weight).ConfigureAwait(false);
if (!result || result.Data == null)
return result.AsError(result.Error!);
@@ -86,9 +92,9 @@ internal async Task> SendHuobiV2Request(Uri uri, HttpMethod
return result.As(result.Data.Data);
}
- internal async Task> SendHuobiTimestampRequest(Uri uri, HttpMethod method, CancellationToken cancellationToken, Dictionary? parameters = null, bool signed = false)
+ internal async Task> SendHTXTimestampRequest(Uri uri, HttpMethod method, CancellationToken cancellationToken, Dictionary? parameters = null, bool signed = false)
{
- var result = await SendRequestAsync>(uri, method, cancellationToken, parameters, signed, requestWeight: 0).ConfigureAwait(false);
+ var result = await SendRequestAsync>(uri, method, cancellationToken, parameters, signed, requestWeight: 0).ConfigureAwait(false);
if (!result || result.Data == null)
return result.AsError<(T, DateTime)>(result.Error!);
@@ -98,14 +104,48 @@ internal async Task> SendHuobiV2Request(Uri uri, HttpMethod
return result.As((result.Data.Data, result.Data.Timestamp));
}
- internal async Task> SendHuobiRequest(Uri uri, HttpMethod method, CancellationToken cancellationToken, Dictionary? parameters = null, bool signed = false)
+ internal Task> SendTimestampAsync(RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null)
+ => SendTimestampToAddressAsync(BaseAddress, definition, parameters, cancellationToken, weight);
+
+ internal async Task> SendTimestampToAddressAsync(string baseAddress, RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null)
{
- var result = await SendRequestAsync>(uri, method, cancellationToken, parameters, signed, requestWeight: 0).ConfigureAwait(false);
+ var result = await base.SendAsync>(baseAddress, definition, parameters, cancellationToken, null, weight).ConfigureAwait(false);
if (!result || result.Data == null)
- return result.AsError(result.Error!);
+ return result.AsError<(T, DateTime)>(result.Error!);
if (result.Data.ErrorCode != null)
- return result.AsError(new ServerError(result.Data.ErrorCode, result.Data.ErrorMessage));
+ return result.AsError<(T, DateTime)>(new ServerError($"{result.Data.ErrorCode}-{result.Data.ErrorMessage}"));
+
+ return result.As((result.Data.Data, result.Data.Timestamp));
+ }
+
+ internal Task SendBasicAsync(RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null)
+ => SendBasicToAddressAsync(BaseAddress, definition, parameters, cancellationToken, weight);
+
+ internal async Task SendBasicToAddressAsync(string baseAddress, RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null)
+ {
+ var result = await base.SendAsync(baseAddress, definition, parameters, cancellationToken, null, weight).ConfigureAwait(false);
+ if (!result || result.Data == null)
+ return result.AsDatalessError(result.Error!);
+
+ if (!string.IsNullOrEmpty(result.Data.ErrorCode))
+ return result.AsDatalessError(new ServerError(result.Data.ErrorCode!, result.Data.ErrorMessage!));
+
+ return result.AsDataless();
+
+ }
+
+ internal Task> SendBasicAsync(RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null)
+ => SendBasicToAddressAsync(BaseAddress, definition, parameters, cancellationToken, weight);
+
+ internal async Task> SendBasicToAddressAsync(string baseAddress, RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null)
+ {
+ var result = await base.SendAsync>(baseAddress, definition, parameters, cancellationToken, null, weight).ConfigureAwait(false);
+ if (!result || result.Data == null)
+ return result.AsError(result.Error!);
+
+ if (!string.IsNullOrEmpty(result.Data.ErrorCode))
+ return result.AsError(new ServerError(result.Data.ErrorCode!, result.Data.ErrorMessage!));
return result.As(result.Data.Data);
}
@@ -131,26 +171,17 @@ protected override Error ParseErrorResponse(int httpStatusCode, IEnumerable(MessagePath.Get().Property("err-code"));
- var msg = accessor.GetValue(MessagePath.Get().Property("err-msg"));
+ var code = accessor.GetValue(MessagePath.Get().Property("code"));
+ var errCode = accessor.GetValue(MessagePath.Get().Property("err-code"));
+ var msg = accessor.GetValue(MessagePath.Get().Property("message")) ?? accessor.GetValue(MessagePath.Get().Property("err-msg"));
- if (code == null && msg == null)
- return null;
+ if (code > 0 && code != 200)
+ return new ServerError(code!.Value, msg!);
- return new ServerError($"{code}, {msg}");
- }
+ if (!string.IsNullOrEmpty(errCode))
+ return new ServerError($"{errCode}: {msg}");
- ///
- /// Construct url
- ///
- ///
- ///
- ///
- internal Uri GetUrl(string endpoint, string? version = null)
- {
- if (version == null)
- return new Uri(BaseAddress.AppendPath(endpoint));
- return new Uri(BaseAddress.AppendPath($"v{version}", endpoint));
+ return null;
}
internal void InvokeOrderPlaced(OrderId id)
@@ -166,7 +197,7 @@ internal void InvokeOrderCanceled(OrderId id)
#region common interface
///
- /// Get the name of a symbol for Huobi based on the base and quote asset
+ /// Get the name of a symbol for HTX based on the base and quote asset
///
///
///
@@ -175,15 +206,15 @@ internal void InvokeOrderCanceled(OrderId id)
async Task>> IBaseRestClient.GetSymbolsAsync(CancellationToken ct)
{
- var symbols = await ExchangeData.GetSymbolsAsync(ct: ct).ConfigureAwait(false);
+ var symbols = await ExchangeData.GetSymbolConfigAsync(ct: ct).ConfigureAwait(false);
if (!symbols)
return symbols.As>(null);
return symbols.As(symbols.Data.Select(d => new Symbol
{
SourceObject = d,
- Name = d.Name,
- MinTradeQuantity = d.MinLimitOrderQuantity,
+ Name = d.Symbol,
+ MinTradeQuantity = d.MinOrderQuantity,
PriceDecimals = d.PricePrecision,
QuantityDecimals = d.QuantityPrecision
}));
@@ -192,7 +223,7 @@ async Task>> IBaseRestClient.GetSymbolsAsync(C
async Task> IBaseRestClient.GetTickerAsync(string symbol, CancellationToken ct)
{
if (string.IsNullOrEmpty(symbol))
- throw new ArgumentException(nameof(symbol) + " required for Huobi " + nameof(ISpotClient.GetTickerAsync), nameof(symbol));
+ throw new ArgumentException(nameof(symbol) + " required for HTX " + nameof(ISpotClient.GetTickerAsync), nameof(symbol));
var tickers = await ExchangeData.GetTickersAsync(ct: ct).ConfigureAwait(false);
if (!tickers)
@@ -235,10 +266,10 @@ async Task>> IBaseRestClient.GetTickersAsync(C
async Task>> IBaseRestClient.GetKlinesAsync(string symbol, TimeSpan timespan, DateTime? startTime, DateTime? endTime, int? limit, CancellationToken ct)
{
if (string.IsNullOrEmpty(symbol))
- throw new ArgumentException(nameof(symbol) + " required for Huobi " + nameof(ISpotClient.GetKlinesAsync), nameof(symbol));
+ throw new ArgumentException(nameof(symbol) + " required for HTX " + nameof(ISpotClient.GetKlinesAsync), nameof(symbol));
if (startTime != null || endTime != null)
- throw new ArgumentException($"Huobi does not support the {nameof(startTime)}/{nameof(endTime)} parameters for the method {nameof(IBaseRestClient.GetKlinesAsync)}");
+ throw new ArgumentException($"HTX does not support the {nameof(startTime)}/{nameof(endTime)} parameters for the method {nameof(IBaseRestClient.GetKlinesAsync)}");
var klines = await ExchangeData.GetKlinesAsync(symbol, GetKlineIntervalFromTimespan(timespan), limit ?? 500, ct: ct).ConfigureAwait(false);
if (!klines)
@@ -259,7 +290,7 @@ async Task>> IBaseRestClient.GetKlinesAsync(str
async Task> IBaseRestClient.GetOrderBookAsync(string symbol, CancellationToken ct)
{
if (string.IsNullOrEmpty(symbol))
- throw new ArgumentException(nameof(symbol) + " required for Huobi " + nameof(ISpotClient.GetOrderBookAsync), nameof(symbol));
+ throw new ArgumentException(nameof(symbol) + " required for HTX " + nameof(ISpotClient.GetOrderBookAsync), nameof(symbol));
var book = await ExchangeData.GetOrderBookAsync(symbol, 0, ct: ct).ConfigureAwait(false);
if (!book)
@@ -276,7 +307,7 @@ async Task> IBaseRestClient.GetOrderBookAsync(string sy
async Task>> IBaseRestClient.GetRecentTradesAsync(string symbol, CancellationToken ct)
{
if (string.IsNullOrEmpty(symbol))
- throw new ArgumentException(nameof(symbol) + " required for Huobi " + nameof(ISpotClient.GetRecentTradesAsync), nameof(symbol));
+ throw new ArgumentException(nameof(symbol) + " required for HTX " + nameof(ISpotClient.GetRecentTradesAsync), nameof(symbol));
var trades = await ExchangeData.GetTradeHistoryAsync(symbol, 100, ct).ConfigureAwait(false);
if (!trades)
@@ -295,10 +326,10 @@ async Task>> IBaseRestClient.GetRecentTradesAsy
async Task> ISpotClient.PlaceOrderAsync(string symbol, CommonOrderSide side, CommonOrderType type, decimal quantity, decimal? price, string? accountId, string? clientOrderId, CancellationToken ct)
{
if (string.IsNullOrEmpty(symbol))
- throw new ArgumentException(nameof(symbol) + " required for Huobi " + nameof(ISpotClient.PlaceOrderAsync), nameof(symbol));
+ throw new ArgumentException(nameof(symbol) + " required for HTX " + nameof(ISpotClient.PlaceOrderAsync), nameof(symbol));
if (string.IsNullOrEmpty(accountId) || !long.TryParse(accountId, out var id))
- throw new ArgumentException(nameof(accountId) + " required for Huobi " + nameof(ISpotClient.PlaceOrderAsync), nameof(accountId));
+ throw new ArgumentException(nameof(accountId) + " required for HTX " + nameof(ISpotClient.PlaceOrderAsync), nameof(accountId));
var huobiType = GetOrderType(type);
var result = await Trading.PlaceOrderAsync(id, symbol, side == CommonOrderSide.Sell ? Enums.OrderSide.Sell: Enums.OrderSide.Buy, huobiType, quantity, price, clientOrderId: clientOrderId, ct: ct).ConfigureAwait(false);
@@ -314,7 +345,7 @@ async Task> ISpotClient.PlaceOrderAsync(string symbol, Co
async Task> IBaseRestClient.GetOrderAsync(string orderId, string? symbol, CancellationToken ct)
{
if(!long.TryParse(orderId, out var id))
- throw new ArgumentException("Invalid order id for Huobi " + nameof(ISpotClient.GetOrderAsync), nameof(orderId));
+ throw new ArgumentException("Invalid order id for HTX " + nameof(ISpotClient.GetOrderAsync), nameof(orderId));
var order = await Trading.GetOrderAsync(id, ct: ct).ConfigureAwait(false);
if (!order)
@@ -331,14 +362,14 @@ async Task> IBaseRestClient.GetOrderAsync(string orderId, s
Timestamp = order.Data.CreateTime,
Side = order.Data.Side == OrderSide.Buy ? CommonOrderSide.Buy: CommonOrderSide.Sell,
Type = order.Data.Type == OrderType.Limit ? CommonOrderType.Limit: order.Data.Type == OrderType.Market ? CommonOrderType.Market: CommonOrderType.Other,
- Status = order.Data.State == OrderState.Canceled || order.Data.State == OrderState.PartiallyCanceled ? CommonOrderStatus.Canceled: order.Data.State == OrderState.Filled ? CommonOrderStatus.Filled: CommonOrderStatus.Active
+ Status = order.Data.Status == OrderStatus.Canceled || order.Data.Status == OrderStatus.PartiallyCanceled ? CommonOrderStatus.Canceled: order.Data.Status == OrderStatus.Filled ? CommonOrderStatus.Filled: CommonOrderStatus.Active
});
}
async Task>> IBaseRestClient.GetOrderTradesAsync(string orderId, string? symbol, CancellationToken ct)
{
if (!long.TryParse(orderId, out var id))
- throw new ArgumentException("Invalid order id for Huobi " + nameof(ISpotClient.GetOrderAsync), nameof(orderId));
+ throw new ArgumentException("Invalid order id for HTX " + nameof(ISpotClient.GetOrderAsync), nameof(orderId));
var result = await Trading.GetOrderTradesAsync(id, ct: ct).ConfigureAwait(false);
if (!result)
@@ -376,7 +407,7 @@ async Task>> IBaseRestClient.GetOpenOrdersAsync
Timestamp = o.CreateTime,
Side = o.Side == OrderSide.Buy ? CommonOrderSide.Buy : CommonOrderSide.Sell,
Type = o.Type == OrderType.Limit ? CommonOrderType.Limit : o.Type == OrderType.Market ? CommonOrderType.Market : CommonOrderType.Other,
- Status = o.State == OrderState.Canceled || o.State == OrderState.PartiallyCanceled ? CommonOrderStatus.Canceled : o.State == OrderState.Filled ? CommonOrderStatus.Filled : CommonOrderStatus.Active
+ Status = o.Status == OrderStatus.Canceled || o.Status == OrderStatus.PartiallyCanceled ? CommonOrderStatus.Canceled : o.Status == OrderStatus.Filled ? CommonOrderStatus.Filled : CommonOrderStatus.Active
}
));
}
@@ -384,7 +415,7 @@ async Task>> IBaseRestClient.GetOpenOrdersAsync
async Task>> IBaseRestClient.GetClosedOrdersAsync(string? symbol, CancellationToken ct)
{
if (string.IsNullOrEmpty(symbol))
- throw new ArgumentException(nameof(symbol) + " required for Huobi " + nameof(ISpotClient.GetClosedOrdersAsync), nameof(symbol));
+ throw new ArgumentException(nameof(symbol) + " required for HTX " + nameof(ISpotClient.GetClosedOrdersAsync), nameof(symbol));
var result = await Trading.GetClosedOrdersAsync(symbol!, ct: ct).ConfigureAwait(false);
if (!result)
@@ -402,7 +433,7 @@ async Task>> IBaseRestClient.GetClosedOrdersAsy
Timestamp = o.CreateTime,
Side = o.Side == OrderSide.Buy ? CommonOrderSide.Buy : CommonOrderSide.Sell,
Type = o.Type == OrderType.Limit ? CommonOrderType.Limit : o.Type == OrderType.Market ? CommonOrderType.Market : CommonOrderType.Other,
- Status = o.State == OrderState.Canceled || o.State == OrderState.PartiallyCanceled ? CommonOrderStatus.Canceled : o.State == OrderState.Filled ? CommonOrderStatus.Filled : CommonOrderStatus.Active
+ Status = o.Status == OrderStatus.Canceled || o.Status == OrderStatus.PartiallyCanceled ? CommonOrderStatus.Canceled : o.Status == OrderStatus.Filled ? CommonOrderStatus.Filled : CommonOrderStatus.Active
}
));
}
@@ -410,7 +441,7 @@ async Task>> IBaseRestClient.GetClosedOrdersAsy
async Task> IBaseRestClient.CancelOrderAsync(string orderId, string? symbol, CancellationToken ct)
{
if (!long.TryParse(orderId, out var id))
- throw new ArgumentException("Invalid order id for Huobi " + nameof(ISpotClient.CancelOrderAsync), nameof(orderId));
+ throw new ArgumentException("Invalid order id for HTX " + nameof(ISpotClient.CancelOrderAsync), nameof(orderId));
var result = await Trading.CancelOrderAsync(id, ct: ct).ConfigureAwait(false);
if (!result)
@@ -426,7 +457,7 @@ async Task> IBaseRestClient.CancelOrderAsync(string order
async Task>> IBaseRestClient.GetBalancesAsync(string? accountId, CancellationToken ct)
{
if (string.IsNullOrEmpty(accountId) || !long.TryParse(accountId, out var id))
- throw new ArgumentException(nameof(accountId) + " required for Huobi " + nameof(ISpotClient.GetBalancesAsync), nameof(accountId));
+ throw new ArgumentException(nameof(accountId) + " required for HTX " + nameof(ISpotClient.GetBalancesAsync), nameof(accountId));
var balances = await Account.GetBalancesAsync(long.Parse(accountId), ct: ct).ConfigureAwait(false);
if (!balances)
@@ -479,7 +510,7 @@ private static KlineInterval GetKlineIntervalFromTimespan(TimeSpan timeSpan)
if (timeSpan == TimeSpan.FromDays(30) || timeSpan == TimeSpan.FromDays(31)) return KlineInterval.OneMonth;
if (timeSpan == TimeSpan.FromDays(365)) return KlineInterval.OneYear;
- throw new ArgumentException("Unsupported timespan for Huobi Klines, check supported intervals using Huobi.Net.Objects.HuobiPeriod");
+ throw new ArgumentException("Unsupported timespan for HTX Klines, check supported intervals using HTX.Net.Objects.HTXPeriod");
}
#endregion
diff --git a/HTX.Net/Clients/SpotApi/HTXRestClientSpotApiAccount.cs b/HTX.Net/Clients/SpotApi/HTXRestClientSpotApiAccount.cs
new file mode 100644
index 00000000..370641f8
--- /dev/null
+++ b/HTX.Net/Clients/SpotApi/HTXRestClientSpotApiAccount.cs
@@ -0,0 +1,435 @@
+using HTX.Net.Enums;
+using HTX.Net.Objects.Models;
+using HTX.Net.Interfaces.Clients.SpotApi;
+using HTX.Net.Objects.Internal;
+using CryptoExchange.Net.RateLimiting.Guards;
+
+namespace HTX.Net.Clients.SpotApi
+{
+ ///
+ internal class HTXRestClientSpotApiAccount : IHTXRestClientSpotApiAccount
+ {
+ private static readonly RequestDefinitionCache _definitions = new RequestDefinitionCache();
+ private readonly HTXRestClientSpotApi _baseClient;
+
+ internal HTXRestClientSpotApiAccount(HTXRestClientSpotApi baseClient)
+ {
+ _baseClient = baseClient;
+ }
+
+ #region Get Accounts
+
+ ///
+ public async Task>> GetAccountsAsync(CancellationToken ct = default)
+ {
+ var request = _definitions.GetOrCreate(HttpMethod.Get, "v1/account/accounts", HTXExchange.RateLimiter.EndpointLimit, 1, true,
+ new SingleLimitGuard(100, TimeSpan.FromSeconds(2), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey));
+ return await _baseClient.SendBasicAsync>(request, null, ct).ConfigureAwait(false);
+ }
+
+ #endregion
+
+ #region Get Balances
+
+ ///
+ public async Task>> GetBalancesAsync(long accountId, CancellationToken ct = default)
+ {
+ var request = _definitions.GetOrCreate(HttpMethod.Get, $"v1/account/accounts/{accountId}/balance", HTXExchange.RateLimiter.EndpointLimit, 1, true,
+ new SingleLimitGuard(100, TimeSpan.FromSeconds(2), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey));
+ var result = await _baseClient.SendBasicAsync(request, null, ct).ConfigureAwait(false);
+ if (!result)
+ return result.AsError>(result.Error!);
+
+ return result.As(result.Data.Data);
+ }
+
+ #endregion
+
+ #region Get Platform Valuation
+
+ ///
+ public async Task> GetPlatformValuationAsync(AccountType? accountType = null, string? valuationAsset = null, CancellationToken ct = default)
+ {
+ var parameters = new ParameterCollection();
+ parameters.AddOptionalEnum("accountType", accountType);
+ parameters.AddOptionalParameter("valuationCurrency", valuationAsset);
+
+ var request = _definitions.GetOrCreate(HttpMethod.Get, "v2/account/valuation", HTXExchange.RateLimiter.EndpointLimit, 1, true,
+ new SingleLimitGuard(3, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey));
+ return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false);
+ }
+
+ #endregion
+
+ #region Get Asset Valuation
+
+ ///
+ public async Task> GetAssetValuationAsync(AccountType accountType, string? valuationCurrency = null, long? subUserId = null, CancellationToken ct = default)
+ {
+ var parameters = new ParameterCollection();
+ parameters.AddEnum("accountType", accountType);
+ parameters.AddOptionalParameter("valuationCurrency", valuationCurrency);
+ parameters.AddOptionalParameter("subUid", subUserId);
+
+ var request = _definitions.GetOrCreate(HttpMethod.Get, "v2/account/asset-valuation", HTXExchange.RateLimiter.EndpointLimit, 1, true,
+ new SingleLimitGuard(100, TimeSpan.FromSeconds(2), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey));
+ return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false);
+ }
+
+ #endregion
+
+ #region Internal Transfer
+
+ ///
+ public async Task> InternalTransferAsync(long fromUserId, AccountType fromAccountType, long fromAccountId,
+ long toUserId, AccountType toAccountType, long toAccountId, string asset, decimal quantity, CancellationToken ct = default)
+ {
+ asset = asset.ToLowerInvariant();
+
+ var parameters = new ParameterCollection()
+ {
+ { "from-account-id", fromAccountId.ToString(CultureInfo.InvariantCulture)},
+ { "from-user", fromUserId.ToString(CultureInfo.InvariantCulture)},
+
+ { "to-account-id", toAccountId.ToString(CultureInfo.InvariantCulture)},
+ { "to-user", toUserId.ToString(CultureInfo.InvariantCulture)},
+
+ { "currency", asset },
+ { "amount", quantity.ToString(CultureInfo.InvariantCulture) },
+ };
+ parameters.AddEnum("from-account-type", fromAccountType);
+ parameters.AddEnum("to-account-type", toAccountType);
+
+ var request = _definitions.GetOrCreate(HttpMethod.Post, $"v1/account/transfer", HTXExchange.RateLimiter.EndpointLimit, 1, true);
+ return await _baseClient.SendBasicAsync(request, null, ct).ConfigureAwait(false);
+ }
+
+ #endregion
+
+ #region Get Account History
+
+ ///
+ public async Task>> GetAccountHistoryAsync(long accountId, string? asset = null, IEnumerable? transactionTypes = null, DateTime? startTime = null, DateTime? endTime = null, SortingType? sort = null, int? limit = null, CancellationToken ct = default)
+ {
+ asset = asset?.ToLowerInvariant();
+ limit?.ValidateIntBetween(nameof(limit), 1, 500);
+
+ var parameters = new ParameterCollection()
+ {
+ { "account-id", accountId }
+ };
+ parameters.AddOptionalParameter("currency", asset);
+ parameters.AddOptionalParameter("transact-types", transactionTypes == null ? null : string.Join(",", transactionTypes.Select(s => EnumConverter.GetString(s))));
+ parameters.AddOptionalParameter("start-time", DateTimeConverter.ConvertToMilliseconds(startTime));
+ parameters.AddOptionalParameter("end-time", DateTimeConverter.ConvertToMilliseconds(endTime));
+ parameters.AddOptionalParameter("size", limit);
+ parameters.AddOptionalEnum("sort", sort);
+
+ var request = _definitions.GetOrCreate(HttpMethod.Get, $"v1/account/history", HTXExchange.RateLimiter.EndpointLimit, 1, true,
+ new SingleLimitGuard(5, TimeSpan.FromSeconds(2), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey));
+ return await _baseClient.SendBasicAsync>(request, null, ct).ConfigureAwait(false);
+ }
+
+ #endregion
+
+ #region Get Account Ledger
+
+ ///
+ public async Task>> GetAccountLedgerAsync(long accountId, string? asset = null, IEnumerable? transactionTypes = null, DateTime? startTime = null, DateTime? endTime = null, SortingType? sort = null, int? limit = null, long? fromId = null, CancellationToken ct = default)
+ {
+ asset = asset?.ToLowerInvariant();
+ limit?.ValidateIntBetween(nameof(limit), 1, 500);
+
+ var parameters = new ParameterCollection()
+ {
+ { "accountId", accountId }
+ };
+ parameters.AddOptionalParameter("currency", asset);
+ parameters.AddOptionalParameter("transactTypes", transactionTypes == null ? null : string.Join(",", transactionTypes.Select(s => EnumConverter.GetString(s))));
+ parameters.AddOptionalParameter("startTime", DateTimeConverter.ConvertToMilliseconds(startTime));
+ parameters.AddOptionalParameter("endTime", DateTimeConverter.ConvertToMilliseconds(endTime));
+ parameters.AddOptionalParameter("limit", limit);
+ parameters.AddOptionalParameter("fromId", fromId?.ToString(CultureInfo.InvariantCulture));
+ parameters.AddOptionalEnum("sort", sort);
+
+ var request = _definitions.GetOrCreate(HttpMethod.Get, "v2/account/ledger", HTXExchange.RateLimiter.EndpointLimit, 1, true,
+ new SingleLimitGuard(20, TimeSpan.FromSeconds(2), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey));
+ return await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false);
+ }
+
+ #endregion
+
+ #region Transfer
+
+ ///
+ public async Task> TransferAsync(TransferAccount fromAccount, TransferAccount toAccount, string asset, decimal quantity, string marginAccount, CancellationToken ct = default)
+ {
+ asset = asset.ToLowerInvariant();
+
+ var parameters = new ParameterCollection();
+ parameters.AddEnum("from", fromAccount);
+ parameters.AddEnum("to", toAccount);
+ parameters.Add("currency", asset);
+ parameters.AddString("amount", quantity);
+ parameters.Add("margin-account", marginAccount);
+ var request = _definitions.GetOrCreate(HttpMethod.Post, "/v2/account/transfer", HTXExchange.RateLimiter.EndpointLimit, 1, true,
+ new SingleLimitGuard(10, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey));
+ var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false);
+ return result;
+ }
+
+ #endregion
+
+ #region Get Point Balance
+
+ ///
+ public async Task> GetPointBalanceAsync(string? subUserId = null, CancellationToken ct = default)
+ {
+ var parameters = new ParameterCollection();
+ parameters.AddOptional("subUid", subUserId);
+ var request = _definitions.GetOrCreate(HttpMethod.Get, "/v2/point/account", HTXExchange.RateLimiter.EndpointLimit, 1, true,
+ new SingleLimitGuard(2, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey));
+ var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false);
+ return result;
+ }
+
+ #endregion
+
+ #region Transfer Points
+
+ ///
+ public async Task> TransferPointsAsync(string fromUserId, string toUserId, string groupId, decimal quantity, CancellationToken ct = default)
+ {
+ var parameters = new ParameterCollection();
+ parameters.Add("fromUid", fromUserId);
+ parameters.Add("toUid", toUserId);
+ parameters.Add("groupId", groupId);
+ parameters.AddString("amount", quantity);
+ var request = _definitions.GetOrCreate(HttpMethod.Post, "/v2/point/transfer", HTXExchange.RateLimiter.EndpointLimit, 1, true,
+ new SingleLimitGuard(2, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey));
+ var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false);
+ return result;
+ }
+
+ #endregion
+
+ #region Get User Deduction Info
+
+ ///
+ public async Task> GetUserDeductionInfoAsync(CancellationToken ct = default)
+ {
+ var parameters = new ParameterCollection();
+ var request = _definitions.GetOrCreate(HttpMethod.Get, "/v1/account/switch/user/info", HTXExchange.RateLimiter.EndpointLimit, 1, true,
+ new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey));
+ var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false);
+ return result;
+ }
+
+ #endregion
+
+ #region Get Deduct Assets
+
+ ///
+ public async Task> GetDeductAssetsAsync(CancellationToken ct = default)
+ {
+ var parameters = new ParameterCollection();
+ var request = _definitions.GetOrCreate(HttpMethod.Get, "/v1/account/overview/info", HTXExchange.RateLimiter.EndpointLimit, 1, true,
+ new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey));
+ var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false);
+ return result;
+ }
+
+ #endregion
+
+ #region Set Deduction Switch
+
+ ///
+ public async Task SetDeductionSwitchAsync(DeductionSwitchType switchType, string? deductionAsset = null, CancellationToken ct = default)
+ {
+ var parameters = new ParameterCollection();
+ parameters.AddEnumAsInt("switchType", switchType);
+ parameters.AddOptional("deductionCurrency", deductionAsset);
+ var request = _definitions.GetOrCreate(HttpMethod.Post, "/v1/account/fee/switch", HTXExchange.RateLimiter.EndpointLimit, 1, true,
+ new SingleLimitGuard(2, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey));
+ var result = await _baseClient.SendAsync