@@ -22,14 +22,9 @@ namespace ExchangeSharp.BinanceGroup
2222{
2323 public abstract class BinanceGroupCommon : ExchangeAPI
2424 {
25- public abstract string BaseUrlPrivate { get ; set ; }
26- public abstract string WithdrawalUrlPrivate { get ; set ; }
27- /// <summary>
28- /// base address for APIs used by the Binance website and not published in the API docs
29- /// </summary>
30- public abstract string BaseWebUrl { get ; set ; }
25+ public string BaseUrlApi => $ "{ BaseUrl } /api/v3";
3126
32- public const string GetCurrenciesUrl = "/assetWithdraw/getAllAsset.html ";
27+ public string BaseUrlSApi => $ " { BaseUrl } /sapi/v1 ";
3328
3429 protected async Task < string > GetWebSocketStreamUrlForSymbolsAsync ( string suffix , params string [ ] marketSymbols )
3530 {
@@ -90,6 +85,25 @@ public async Task<IEnumerable<ExchangeOrderResult>> GetMyTradesAsync(string? mar
9085 return await OnGetMyTradesAsync ( marketSymbol , afterDate ) ;
9186 }
9287
88+ protected override async Task < IReadOnlyDictionary < string , ExchangeCurrency > > OnGetCurrenciesAsync ( )
89+ {
90+ var result = await MakeJsonRequestAsync < List < Currency > > ( "/capital/config/getall" , BaseUrlSApi ) ;
91+
92+ return result . ToDictionary ( x => x . Coin . ToUpper ( ) , x => {
93+ var network = x . NetworkList . FirstOrDefault ( x => x . IsDefault ) ;
94+ return new ExchangeCurrency
95+ {
96+ Name = x . Coin ,
97+ FullName = x . Name ,
98+ DepositEnabled = network ? . DepositEnable ?? x . DepositAllEnable ,
99+ WithdrawalEnabled = network ? . WithdrawEnable ?? x . WithdrawAllEnable ,
100+ MinConfirmations = network ? . MinConfirm ?? 0 ,
101+ MinWithdrawalSize = decimal . Parse ( network ? . WithdrawMin ?? "0" ) ,
102+ TxFee = decimal . Parse ( network ? . WithdrawFee ?? "0" )
103+ } ;
104+ } ) ;
105+ }
106+
93107 protected override async Task < IEnumerable < string > > OnGetMarketSymbolsAsync ( )
94108 {
95109 List < string > symbols = new List < string > ( ) ;
@@ -189,30 +203,6 @@ protected internal override async Task<IEnumerable<ExchangeMarket>> OnGetMarketS
189203 return markets ;
190204 }
191205
192- protected override async Task < IReadOnlyDictionary < string , ExchangeCurrency > > OnGetCurrenciesAsync ( )
193- {
194- // https://www.binance.com/assetWithdraw/getAllAsset.html
195- Dictionary < string , ExchangeCurrency > allCoins = new Dictionary < string , ExchangeCurrency > ( StringComparer . OrdinalIgnoreCase ) ;
196-
197- List < Currency > currencies = await MakeJsonRequestAsync < List < Currency > > ( GetCurrenciesUrl , BaseWebUrl ) ;
198- foreach ( Currency coin in currencies )
199- {
200- allCoins [ coin . AssetCode ] = new ExchangeCurrency
201- {
202- CoinType = coin . ParentCode ,
203- DepositEnabled = coin . EnableCharge ,
204- FullName = coin . AssetName ,
205- MinConfirmations = coin . ConfirmTimes . ConvertInvariant < int > ( ) ,
206- Name = coin . AssetCode ,
207- TxFee = coin . TransactionFee ,
208- WithdrawalEnabled = coin . EnableWithdraw ,
209- MinWithdrawalSize = coin . MinProductWithdraw . ConvertInvariant < decimal > ( ) ,
210- } ;
211- }
212-
213- return allCoins ;
214- }
215-
216206 protected override async Task < ExchangeTicker > OnGetTickerAsync ( string marketSymbol )
217207 {
218208 JToken obj = await MakeJsonRequestAsync < JToken > ( "/ticker/24hr?symbol=" + marketSymbol ) ;
@@ -517,7 +507,7 @@ protected override async Task<IEnumerable<MarketCandle>> OnGetCandlesAsync(strin
517507
518508 protected override async Task < Dictionary < string , decimal > > OnGetAmountsAsync ( )
519509 {
520- JToken token = await MakeJsonRequestAsync < JToken > ( "/account" , BaseUrlPrivate , await GetNoncePayloadAsync ( ) ) ;
510+ JToken token = await MakeJsonRequestAsync < JToken > ( "/account" , BaseUrlApi , await GetNoncePayloadAsync ( ) ) ;
521511 Dictionary < string , decimal > balances = new Dictionary < string , decimal > ( StringComparer . OrdinalIgnoreCase ) ;
522512 foreach ( JToken balance in token [ "balances" ] )
523513 {
@@ -532,7 +522,7 @@ protected override async Task<Dictionary<string, decimal>> OnGetAmountsAsync()
532522
533523 protected override async Task < Dictionary < string , decimal > > OnGetAmountsAvailableToTradeAsync ( )
534524 {
535- JToken token = await MakeJsonRequestAsync < JToken > ( "/account" , BaseUrlPrivate , await GetNoncePayloadAsync ( ) ) ;
525+ JToken token = await MakeJsonRequestAsync < JToken > ( "/account" , BaseUrlApi , await GetNoncePayloadAsync ( ) ) ;
536526 Dictionary < string , decimal > balances = new Dictionary < string , decimal > ( StringComparer . OrdinalIgnoreCase ) ;
537527 foreach ( JToken balance in token [ "balances" ] )
538528 {
@@ -578,7 +568,7 @@ protected override async Task<ExchangeOrderResult> OnPlaceOrderAsync(ExchangeOrd
578568 }
579569 order . ExtraParameters . CopyTo ( payload ) ;
580570
581- JToken ? token = await MakeJsonRequestAsync < JToken > ( "/order" , BaseUrlPrivate , payload , "POST" ) ;
571+ JToken ? token = await MakeJsonRequestAsync < JToken > ( "/order" , BaseUrlApi , payload , "POST" ) ;
582572 if ( token is null )
583573 {
584574 return null ;
@@ -600,13 +590,13 @@ protected override async Task<ExchangeOrderResult> OnGetOrderDetailsAsync(string
600590 else
601591 payload [ "orderId" ] = orderId ;
602592
603- JToken token = await MakeJsonRequestAsync < JToken > ( "/order" , BaseUrlPrivate , payload ) ;
593+ JToken token = await MakeJsonRequestAsync < JToken > ( "/order" , BaseUrlApi , payload ) ;
604594 ExchangeOrderResult result = ParseOrder ( token ) ;
605595
606596 // Add up the fees from each trade in the order
607597 Dictionary < string , object > feesPayload = await GetNoncePayloadAsync ( ) ;
608598 feesPayload [ "symbol" ] = marketSymbol ! ;
609- JToken feesToken = await MakeJsonRequestAsync < JToken > ( "/myTrades" , BaseUrlPrivate , feesPayload ) ;
599+ JToken feesToken = await MakeJsonRequestAsync < JToken > ( "/myTrades" , BaseUrlApi , feesPayload ) ;
610600 ParseFees ( feesToken , result ) ;
611601
612602 return result ;
@@ -641,7 +631,7 @@ protected override async Task<IEnumerable<ExchangeOrderResult>> OnGetOpenOrderDe
641631 {
642632 payload [ "symbol" ] = marketSymbol ! ;
643633 }
644- JToken token = await MakeJsonRequestAsync < JToken > ( "/openOrders" , BaseUrlPrivate , payload ) ;
634+ JToken token = await MakeJsonRequestAsync < JToken > ( "/openOrders" , BaseUrlApi , payload ) ;
645635 foreach ( JToken order in token )
646636 {
647637 orders . Add ( ParseOrder ( order ) ) ;
@@ -704,74 +694,13 @@ protected override async Task<IEnumerable<ExchangeOrderResult>> OnGetCompletedOr
704694 {
705695 payload [ "startTime" ] = afterDate . Value . UnixTimestampFromDateTimeMilliseconds ( ) ;
706696 }
707- JToken token = await MakeJsonRequestAsync < JToken > ( "/myTrades" , BaseUrlPrivate , payload ) ;
697+ JToken token = await MakeJsonRequestAsync < JToken > ( "/myTrades" , BaseUrlApi , payload ) ;
708698 foreach ( JToken trade in token )
709699 {
710700 trades . Add ( ParseTrade ( trade , marketSymbol ! ) ) ;
711701 }
712702 }
713703 return trades ;
714-
715- //old way
716-
717- //List<ExchangeOrderResult> orders = new List<ExchangeOrderResult>();
718- //if (string.IsNullOrWhiteSpace(marketSymbol))
719- //{
720- // orders.AddRange(await GetCompletedOrdersForAllSymbolsAsync(afterDate));
721- //}
722- //else
723- //{
724- // Dictionary<string, object> payload = await GetNoncePayloadAsync();
725- // payload["symbol"] = marketSymbol;
726- // if (afterDate != null)
727- // {
728- // payload["startTime"] = Math.Round(afterDate.Value.UnixTimestampFromDateTimeMilliseconds());
729- // }
730- // JToken token = await MakeJsonRequestAsync<JToken>("/allOrders", BaseUrlPrivate, payload);
731- // foreach (JToken order in token)
732- // {
733- // orders.Add(ParseOrder(order));
734- // }
735- //}
736- //return orders;
737- }
738-
739- private async Task < IEnumerable < ExchangeOrderResult > > GetMyTradesForAllSymbols ( DateTime ? afterDate )
740- {
741- // TODO: This is a HACK, Binance API needs to add a single API call to get all orders for all symbols, terrible...
742- List < ExchangeOrderResult > trades = new List < ExchangeOrderResult > ( ) ;
743- Exception ? ex = null ;
744- string ? failedSymbol = null ;
745- Parallel . ForEach ( ( await GetMarketSymbolsAsync ( ) ) . Where ( s => s . IndexOf ( "BTC" , StringComparison . OrdinalIgnoreCase ) >= 0 ) , async ( s ) =>
746- {
747- try
748- {
749- foreach ( ExchangeOrderResult trade in ( await GetMyTradesAsync ( s , afterDate ) ) )
750- {
751- lock ( trades )
752- {
753- trades . Add ( trade ) ;
754- }
755- }
756- }
757- catch ( Exception _ex )
758- {
759- failedSymbol = s ;
760- ex = _ex ;
761- }
762- } ) ;
763-
764- if ( ex != null )
765- {
766- throw new APIException ( "Failed to get my trades for symbol " + failedSymbol , ex ) ;
767- }
768-
769- // sort timestamp desc
770- trades . Sort ( ( o1 , o2 ) =>
771- {
772- return o2 . OrderDate . CompareTo ( o1 . OrderDate ) ;
773- } ) ;
774- return trades ;
775704 }
776705
777706 private async Task < IEnumerable < ExchangeOrderResult > > OnGetMyTradesAsync ( string ? marketSymbol = null , DateTime ? afterDate = null )
@@ -789,7 +718,7 @@ private async Task<IEnumerable<ExchangeOrderResult>> OnGetMyTradesAsync(string?
789718 {
790719 payload [ "timestamp" ] = afterDate . Value . UnixTimestampFromDateTimeMilliseconds ( ) ;
791720 }
792- JToken token = await MakeJsonRequestAsync < JToken > ( "/myTrades" , BaseUrlPrivate , payload ) ;
721+ JToken token = await MakeJsonRequestAsync < JToken > ( "/myTrades" , BaseUrlApi , payload ) ;
793722 foreach ( JToken trade in token )
794723 {
795724 trades . Add ( ParseTrade ( trade , marketSymbol ! ) ) ;
@@ -807,7 +736,7 @@ protected override async Task OnCancelOrderAsync(string orderId, string? marketS
807736 }
808737 payload [ "symbol" ] = marketSymbol ! ;
809738 payload [ "orderId" ] = orderId ;
810- _ = await MakeJsonRequestAsync < JToken > ( "/order" , BaseUrlPrivate , payload , "DELETE" ) ;
739+ _ = await MakeJsonRequestAsync < JToken > ( "/order" , BaseUrlApi , payload , "DELETE" ) ;
811740 }
812741
813742 /// <summary>A withdrawal request. Fee is automatically subtracted from the amount.</summary>
@@ -829,17 +758,21 @@ protected override async Task<ExchangeWithdrawalResponse> OnWithdrawAsync(Exchan
829758 }
830759
831760 Dictionary < string , object > payload = await GetNoncePayloadAsync ( ) ;
832- payload [ "asset " ] = withdrawalRequest . Currency ;
761+ payload [ "coin " ] = withdrawalRequest . Currency ;
833762 payload [ "address" ] = withdrawalRequest . Address ;
834763 payload [ "amount" ] = withdrawalRequest . Amount ;
835- payload [ "name" ] = withdrawalRequest . Description ?? "apiwithdrawal" ; // Contrary to what the API docs say, name is required
764+
765+ if ( ! string . IsNullOrWhiteSpace ( withdrawalRequest . Description ) )
766+ {
767+ payload [ "name" ] = withdrawalRequest . Description ;
768+ }
836769
837770 if ( ! string . IsNullOrWhiteSpace ( withdrawalRequest . AddressTag ) )
838771 {
839772 payload [ "addressTag" ] = withdrawalRequest . AddressTag ;
840773 }
841774
842- JToken response = await MakeJsonRequestAsync < JToken > ( "/withdraw.html " , WithdrawalUrlPrivate , payload , "POST" ) ;
775+ JToken response = await MakeJsonRequestAsync < JToken > ( "/capital/ withdraw/apply " , BaseUrlSApi , payload , "POST" ) ;
843776 ExchangeWithdrawalResponse withdrawalResponse = new ExchangeWithdrawalResponse
844777 {
845778 Id = response [ "id" ] . ToStringInvariant ( ) ,
@@ -1081,14 +1014,14 @@ protected override async Task<ExchangeDepositDetails> OnGetDepositAddressAsync(s
10811014 */
10821015
10831016 Dictionary < string , object > payload = await GetNoncePayloadAsync ( ) ;
1084- payload [ "asset " ] = currency ;
1017+ payload [ "coin " ] = currency ;
10851018
1086- JToken response = await MakeJsonRequestAsync < JToken > ( "/depositAddress.html " , WithdrawalUrlPrivate , payload ) ;
1019+ JToken response = await MakeJsonRequestAsync < JToken > ( "/capital/deposit/address " , BaseUrlSApi , payload ) ;
10871020 ExchangeDepositDetails depositDetails = new ExchangeDepositDetails
10881021 {
1089- Currency = response [ "asset " ] . ToStringInvariant ( ) ,
1022+ Currency = response [ "coin " ] . ToStringInvariant ( ) ,
10901023 Address = response [ "address" ] . ToStringInvariant ( ) ,
1091- AddressTag = response [ "addressTag " ] . ToStringInvariant ( )
1024+ AddressTag = response [ "tag " ] . ToStringInvariant ( )
10921025 } ;
10931026
10941027 return depositDetails ;
@@ -1100,44 +1033,33 @@ protected override async Task<ExchangeDepositDetails> OnGetDepositAddressAsync(s
11001033 protected override async Task < IEnumerable < ExchangeTransaction > > OnGetDepositHistoryAsync ( string currency )
11011034 {
11021035 // TODO: API supports searching on status, startTime, endTime
1103- Dictionary < string , object > payload = await GetNoncePayloadAsync ( ) ;
1036+ var payload = await GetNoncePayloadAsync ( ) ;
1037+
11041038 if ( ! string . IsNullOrWhiteSpace ( currency ) )
11051039 {
1106- payload [ "asset " ] = currency ;
1040+ payload [ "coin " ] = currency ;
11071041 }
11081042
1109- JToken response = await MakeJsonRequestAsync < JToken > ( "/depositHistory.html " , WithdrawalUrlPrivate , payload ) ;
1043+ var response = await MakeJsonRequestAsync < List < HistoryRecord > > ( "/capital/deposit/hisrec " , BaseUrlSApi , payload ) ;
11101044 var transactions = new List < ExchangeTransaction > ( ) ;
1111- foreach ( JToken token in response [ "depositList" ] )
1045+
1046+ foreach ( var item in response )
11121047 {
1113- var transaction = new ExchangeTransaction
1114- {
1115- Timestamp = token [ "insertTime" ] . ConvertInvariant < double > ( ) . UnixTimeStampToDateTimeMilliseconds ( ) ,
1116- Amount = token [ "amount" ] . ConvertInvariant < decimal > ( ) ,
1117- Currency = token [ "asset" ] . ToStringUpperInvariant ( ) ,
1118- Address = token [ "address" ] . ToStringInvariant ( ) ,
1119- AddressTag = token [ "addressTag" ] . ToStringInvariant ( ) ,
1120- BlockchainTxId = token [ "txId" ] . ToStringInvariant ( )
1121- } ;
1122- int status = token [ "status" ] . ConvertInvariant < int > ( ) ;
1123- switch ( status )
1048+ transactions . Add ( new ExchangeTransaction
11241049 {
1125- case 0 :
1126- transaction . Status = TransactionStatus . Processing ;
1127- break ;
1128-
1129- case 1 :
1130- transaction . Status = TransactionStatus . Complete ;
1131- break ;
1132-
1133- default :
1134- // If new states are added, see https://github.com/binance-exchange/binance-official-api-docs/blob/master/wapi-api.md
1135- transaction . Status = TransactionStatus . Unknown ;
1136- transaction . Notes = "Unknown transaction status: " + status ;
1137- break ;
1138- }
1139-
1140- transactions . Add ( transaction ) ;
1050+ Timestamp = item . InsertTime . UnixTimeStampToDateTimeMilliseconds ( ) ,
1051+ Amount = decimal . Parse ( item . Amount ) ,
1052+ Currency = item . Coin . ToUpperInvariant ( ) ,
1053+ Address = item . Address ,
1054+ AddressTag = item . AddressTag ,
1055+ BlockchainTxId = item . TxId ,
1056+ Status = item . Status switch
1057+ {
1058+ 0 => TransactionStatus . Processing ,
1059+ 1 => TransactionStatus . Complete ,
1060+ _ => TransactionStatus . Unknown
1061+ }
1062+ } ) ;
11411063 }
11421064
11431065 return transactions ;
@@ -1170,7 +1092,7 @@ protected override async Task<IWebSocket> OnUserDataWebSocketAsync(Action<object
11701092
11711093 public async Task < string > GetListenKeyAsync ( )
11721094 {
1173- JToken response = await MakeJsonRequestAsync < JToken > ( "/userDataStream" , BaseUrl , null , "POST" ) ;
1095+ JToken response = await MakeJsonRequestAsync < JToken > ( "/userDataStream" , BaseUrlApi , null , "POST" ) ;
11741096 var listenKey = response [ "listenKey" ] . ToStringInvariant ( ) ;
11751097 return listenKey ;
11761098 }
0 commit comments