diff --git a/src/ExchangeSharp/API/Exchanges/BinanceGroup/BinanceGroupCommon.cs b/src/ExchangeSharp/API/Exchanges/BinanceGroup/BinanceGroupCommon.cs
index 392a0019..3693c145 100644
--- a/src/ExchangeSharp/API/Exchanges/BinanceGroup/BinanceGroupCommon.cs
+++ b/src/ExchangeSharp/API/Exchanges/BinanceGroup/BinanceGroupCommon.cs
@@ -22,14 +22,9 @@ namespace ExchangeSharp.BinanceGroup
{
public abstract class BinanceGroupCommon : ExchangeAPI
{
- public abstract string BaseUrlPrivate { get; set; }
- public abstract string WithdrawalUrlPrivate { get; set; }
- ///
- /// base address for APIs used by the Binance website and not published in the API docs
- ///
- public abstract string BaseWebUrl { get; set; }
+ public string BaseUrlApi => $"{BaseUrl}/api/v3";
- public const string GetCurrenciesUrl = "/assetWithdraw/getAllAsset.html";
+ public string BaseUrlSApi => $"{BaseUrl}/sapi/v1";
protected async Task GetWebSocketStreamUrlForSymbolsAsync(string suffix, params string[] marketSymbols)
{
@@ -90,6 +85,25 @@ public async Task> GetMyTradesAsync(string? mar
return await OnGetMyTradesAsync(marketSymbol, afterDate);
}
+ protected override async Task> OnGetCurrenciesAsync()
+ {
+ var result = await MakeJsonRequestAsync>("/capital/config/getall", BaseUrlSApi);
+
+ return result.ToDictionary(x => x.Coin.ToUpper(), x => {
+ var network = x.NetworkList.FirstOrDefault(x => x.IsDefault);
+ return new ExchangeCurrency
+ {
+ Name = x.Coin,
+ FullName = x.Name,
+ DepositEnabled = network?.DepositEnable ?? x.DepositAllEnable,
+ WithdrawalEnabled = network?.WithdrawEnable ?? x.WithdrawAllEnable,
+ MinConfirmations = network?.MinConfirm ?? 0,
+ MinWithdrawalSize = decimal.Parse(network?.WithdrawMin ?? "0"),
+ TxFee = decimal.Parse(network?.WithdrawFee ?? "0")
+ };
+ });
+ }
+
protected override async Task> OnGetMarketSymbolsAsync()
{
List symbols = new List();
@@ -189,30 +203,6 @@ protected internal override async Task> OnGetMarketS
return markets;
}
- protected override async Task> OnGetCurrenciesAsync()
- {
- // https://www.binance.com/assetWithdraw/getAllAsset.html
- Dictionary allCoins = new Dictionary(StringComparer.OrdinalIgnoreCase);
-
- List currencies = await MakeJsonRequestAsync>(GetCurrenciesUrl, BaseWebUrl);
- foreach (Currency coin in currencies)
- {
- allCoins[coin.AssetCode] = new ExchangeCurrency
- {
- CoinType = coin.ParentCode,
- DepositEnabled = coin.EnableCharge,
- FullName = coin.AssetName,
- MinConfirmations = coin.ConfirmTimes.ConvertInvariant(),
- Name = coin.AssetCode,
- TxFee = coin.TransactionFee,
- WithdrawalEnabled = coin.EnableWithdraw,
- MinWithdrawalSize = coin.MinProductWithdraw.ConvertInvariant(),
- };
- }
-
- return allCoins;
- }
-
protected override async Task OnGetTickerAsync(string marketSymbol)
{
JToken obj = await MakeJsonRequestAsync("/ticker/24hr?symbol=" + marketSymbol);
@@ -517,7 +507,7 @@ protected override async Task> OnGetCandlesAsync(strin
protected override async Task> OnGetAmountsAsync()
{
- JToken token = await MakeJsonRequestAsync("/account", BaseUrlPrivate, await GetNoncePayloadAsync());
+ JToken token = await MakeJsonRequestAsync("/account", BaseUrlApi, await GetNoncePayloadAsync());
Dictionary balances = new Dictionary(StringComparer.OrdinalIgnoreCase);
foreach (JToken balance in token["balances"])
{
@@ -532,7 +522,7 @@ protected override async Task> OnGetAmountsAsync()
protected override async Task> OnGetAmountsAvailableToTradeAsync()
{
- JToken token = await MakeJsonRequestAsync("/account", BaseUrlPrivate, await GetNoncePayloadAsync());
+ JToken token = await MakeJsonRequestAsync("/account", BaseUrlApi, await GetNoncePayloadAsync());
Dictionary balances = new Dictionary(StringComparer.OrdinalIgnoreCase);
foreach (JToken balance in token["balances"])
{
@@ -577,7 +567,7 @@ protected override async Task OnPlaceOrderAsync(ExchangeOrd
}
order.ExtraParameters.CopyTo(payload);
- JToken? token = await MakeJsonRequestAsync("/order", BaseUrlPrivate, payload, "POST");
+ JToken? token = await MakeJsonRequestAsync("/order", BaseUrlApi, payload, "POST");
if (token is null)
{
return null;
@@ -599,13 +589,13 @@ protected override async Task OnGetOrderDetailsAsync(string
else
payload["orderId"] = orderId;
- JToken token = await MakeJsonRequestAsync("/order", BaseUrlPrivate, payload);
+ JToken token = await MakeJsonRequestAsync("/order", BaseUrlApi, payload);
ExchangeOrderResult result = ParseOrder(token);
// Add up the fees from each trade in the order
Dictionary feesPayload = await GetNoncePayloadAsync();
feesPayload["symbol"] = marketSymbol!;
- JToken feesToken = await MakeJsonRequestAsync("/myTrades", BaseUrlPrivate, feesPayload);
+ JToken feesToken = await MakeJsonRequestAsync("/myTrades", BaseUrlApi, feesPayload);
ParseFees(feesToken, result);
return result;
@@ -640,7 +630,7 @@ protected override async Task> OnGetOpenOrderDe
{
payload["symbol"] = marketSymbol!;
}
- JToken token = await MakeJsonRequestAsync("/openOrders", BaseUrlPrivate, payload);
+ JToken token = await MakeJsonRequestAsync("/openOrders", BaseUrlApi, payload);
foreach (JToken order in token)
{
orders.Add(ParseOrder(order));
@@ -703,74 +693,13 @@ protected override async Task> OnGetCompletedOr
{
payload["startTime"] = afterDate.Value.UnixTimestampFromDateTimeMilliseconds();
}
- JToken token = await MakeJsonRequestAsync("/myTrades", BaseUrlPrivate, payload);
+ JToken token = await MakeJsonRequestAsync("/myTrades", BaseUrlApi, payload);
foreach (JToken trade in token)
{
trades.Add(ParseTrade(trade, marketSymbol!));
}
}
return trades;
-
- //old way
-
- //List orders = new List();
- //if (string.IsNullOrWhiteSpace(marketSymbol))
- //{
- // orders.AddRange(await GetCompletedOrdersForAllSymbolsAsync(afterDate));
- //}
- //else
- //{
- // Dictionary payload = await GetNoncePayloadAsync();
- // payload["symbol"] = marketSymbol;
- // if (afterDate != null)
- // {
- // payload["startTime"] = Math.Round(afterDate.Value.UnixTimestampFromDateTimeMilliseconds());
- // }
- // JToken token = await MakeJsonRequestAsync("/allOrders", BaseUrlPrivate, payload);
- // foreach (JToken order in token)
- // {
- // orders.Add(ParseOrder(order));
- // }
- //}
- //return orders;
- }
-
- private async Task> GetMyTradesForAllSymbols(DateTime? afterDate)
- {
- // TODO: This is a HACK, Binance API needs to add a single API call to get all orders for all symbols, terrible...
- List trades = new List();
- Exception? ex = null;
- string? failedSymbol = null;
- Parallel.ForEach((await GetMarketSymbolsAsync()).Where(s => s.IndexOf("BTC", StringComparison.OrdinalIgnoreCase) >= 0), async (s) =>
- {
- try
- {
- foreach (ExchangeOrderResult trade in (await GetMyTradesAsync(s, afterDate)))
- {
- lock (trades)
- {
- trades.Add(trade);
- }
- }
- }
- catch (Exception _ex)
- {
- failedSymbol = s;
- ex = _ex;
- }
- });
-
- if (ex != null)
- {
- throw new APIException("Failed to get my trades for symbol " + failedSymbol, ex);
- }
-
- // sort timestamp desc
- trades.Sort((o1, o2) =>
- {
- return o2.OrderDate.CompareTo(o1.OrderDate);
- });
- return trades;
}
private async Task> OnGetMyTradesAsync(string? marketSymbol = null, DateTime? afterDate = null)
@@ -788,7 +717,7 @@ private async Task> OnGetMyTradesAsync(string?
{
payload["timestamp"] = afterDate.Value.UnixTimestampFromDateTimeMilliseconds();
}
- JToken token = await MakeJsonRequestAsync("/myTrades", BaseUrlPrivate, payload);
+ JToken token = await MakeJsonRequestAsync("/myTrades", BaseUrlApi, payload);
foreach (JToken trade in token)
{
trades.Add(ParseTrade(trade, marketSymbol!));
@@ -806,7 +735,7 @@ protected override async Task OnCancelOrderAsync(string orderId, string? marketS
}
payload["symbol"] = marketSymbol!;
payload["orderId"] = orderId;
- _ = await MakeJsonRequestAsync("/order", BaseUrlPrivate, payload, "DELETE");
+ _ = await MakeJsonRequestAsync("/order", BaseUrlApi, payload, "DELETE");
}
/// A withdrawal request. Fee is automatically subtracted from the amount.
@@ -828,17 +757,21 @@ protected override async Task OnWithdrawAsync(Exchan
}
Dictionary payload = await GetNoncePayloadAsync();
- payload["asset"] = withdrawalRequest.Currency;
+ payload["coin"] = withdrawalRequest.Currency;
payload["address"] = withdrawalRequest.Address;
payload["amount"] = withdrawalRequest.Amount;
- payload["name"] = withdrawalRequest.Description ?? "apiwithdrawal"; // Contrary to what the API docs say, name is required
+
+ if (!string.IsNullOrWhiteSpace(withdrawalRequest.Description))
+ {
+ payload["name"] = withdrawalRequest.Description;
+ }
if (!string.IsNullOrWhiteSpace(withdrawalRequest.AddressTag))
{
payload["addressTag"] = withdrawalRequest.AddressTag;
}
- JToken response = await MakeJsonRequestAsync("/withdraw.html", WithdrawalUrlPrivate, payload, "POST");
+ JToken response = await MakeJsonRequestAsync("/capital/withdraw/apply", BaseUrlSApi, payload, "POST");
ExchangeWithdrawalResponse withdrawalResponse = new ExchangeWithdrawalResponse
{
Id = response["id"].ToStringInvariant(),
@@ -1079,14 +1012,14 @@ protected override async Task OnGetDepositAddressAsync(s
*/
Dictionary payload = await GetNoncePayloadAsync();
- payload["asset"] = currency;
+ payload["coin"] = currency;
- JToken response = await MakeJsonRequestAsync("/depositAddress.html", WithdrawalUrlPrivate, payload);
+ JToken response = await MakeJsonRequestAsync("/capital/deposit/address", BaseUrlSApi, payload);
ExchangeDepositDetails depositDetails = new ExchangeDepositDetails
{
- Currency = response["asset"].ToStringInvariant(),
+ Currency = response["coin"].ToStringInvariant(),
Address = response["address"].ToStringInvariant(),
- AddressTag = response["addressTag"].ToStringInvariant()
+ AddressTag = response["tag"].ToStringInvariant()
};
return depositDetails;
@@ -1098,44 +1031,33 @@ protected override async Task OnGetDepositAddressAsync(s
protected override async Task> OnGetDepositHistoryAsync(string currency)
{
// TODO: API supports searching on status, startTime, endTime
- Dictionary payload = await GetNoncePayloadAsync();
+ var payload = await GetNoncePayloadAsync();
+
if (!string.IsNullOrWhiteSpace(currency))
{
- payload["asset"] = currency;
+ payload["coin"] = currency;
}
- JToken response = await MakeJsonRequestAsync("/depositHistory.html", WithdrawalUrlPrivate, payload);
+ var response = await MakeJsonRequestAsync>("/capital/deposit/hisrec", BaseUrlSApi, payload);
var transactions = new List();
- foreach (JToken token in response["depositList"])
+
+ foreach (var item in response)
{
- var transaction = new ExchangeTransaction
- {
- Timestamp = token["insertTime"].ConvertInvariant().UnixTimeStampToDateTimeMilliseconds(),
- Amount = token["amount"].ConvertInvariant(),
- Currency = token["asset"].ToStringUpperInvariant(),
- Address = token["address"].ToStringInvariant(),
- AddressTag = token["addressTag"].ToStringInvariant(),
- BlockchainTxId = token["txId"].ToStringInvariant()
- };
- int status = token["status"].ConvertInvariant();
- switch (status)
+ transactions.Add(new ExchangeTransaction
{
- case 0:
- transaction.Status = TransactionStatus.Processing;
- break;
-
- case 1:
- transaction.Status = TransactionStatus.Complete;
- break;
-
- default:
- // If new states are added, see https://github.com/binance-exchange/binance-official-api-docs/blob/master/wapi-api.md
- transaction.Status = TransactionStatus.Unknown;
- transaction.Notes = "Unknown transaction status: " + status;
- break;
- }
-
- transactions.Add(transaction);
+ Timestamp = item.InsertTime.UnixTimeStampToDateTimeMilliseconds(),
+ Amount = decimal.Parse(item.Amount),
+ Currency = item.Coin.ToUpperInvariant(),
+ Address = item.Address,
+ AddressTag = item.AddressTag,
+ BlockchainTxId = item.TxId,
+ Status = item.Status switch
+ {
+ 0 => TransactionStatus.Processing,
+ 1 => TransactionStatus.Complete,
+ _ => TransactionStatus.Unknown
+ }
+ });
}
return transactions;
@@ -1168,7 +1090,7 @@ protected override async Task OnUserDataWebSocketAsync(Action