|
|
|
using Binance.Net.Clients;
|
|
|
|
using Binance.Net.Objects;
|
|
|
|
using Binance.TradeRobot.Model.Base;
|
|
|
|
using CryptoExchange.Net.Authentication;
|
|
|
|
using SDKAdapter.Model;
|
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
|
|
|
|
|
|
|
namespace SDKAdapter.APIClient
|
|
|
|
{
|
|
|
|
public class BinanceAPIClient : BaseAPIClient
|
|
|
|
{
|
|
|
|
private BinanceClient binanceClient;
|
|
|
|
|
|
|
|
public BinanceAPIClient(long uid, string apiKey, string secret) : base(uid, apiKey, secret)
|
|
|
|
{
|
|
|
|
var spotClientOption = new BinanceApiClientOptions()
|
|
|
|
{
|
|
|
|
BaseAddress = "https://api.binance.com",
|
|
|
|
ApiCredentials = new ApiCredentials(apiKey, secret)
|
|
|
|
};
|
|
|
|
var usdFuturesClientOption = new BinanceApiClientOptions()
|
|
|
|
{
|
|
|
|
BaseAddress = "https://fapi.binance.com",
|
|
|
|
ApiCredentials = new ApiCredentials(apiKey, secret)
|
|
|
|
};
|
|
|
|
binanceClient = new BinanceClient(new BinanceClientOptions()
|
|
|
|
{
|
|
|
|
UsdFuturesApiOptions = usdFuturesClientOption,
|
|
|
|
SpotApiOptions = spotClientOption
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public override IList<IsolatedMarginAccountAsset> GetIsolatedMarginAccountAssets()
|
|
|
|
{
|
|
|
|
var r = binanceClient.SpotApi.Account.GetIsolatedMarginAccountAsync().Result;
|
|
|
|
if (!r.Success)
|
|
|
|
throw new Exception($"获取逐仓杠杆账户信息失败 {r.Error?.Message}");
|
|
|
|
|
|
|
|
return r.Data.Assets.Select(asset => new IsolatedMarginAccountAsset()
|
|
|
|
{
|
|
|
|
Symbol = asset.Symbol,
|
|
|
|
|
|
|
|
BaseAsset = asset.BaseAsset.Asset,
|
|
|
|
BaseBorrowed = asset.BaseAsset.Borrowed,
|
|
|
|
BaseFree = asset.BaseAsset.Free,
|
|
|
|
BaseInterest = asset.BaseAsset.Interest,
|
|
|
|
BaseLocked = asset.BaseAsset.Locked,
|
|
|
|
BaseNetAsset = asset.BaseAsset.NetAsset,
|
|
|
|
|
|
|
|
QuoteAsset = asset.QuoteAsset.Asset,
|
|
|
|
QuoteBorrowed = asset.QuoteAsset.Borrowed,
|
|
|
|
QuoteFree = asset.QuoteAsset.Free,
|
|
|
|
QuoteInterest = asset.QuoteAsset.Interest,
|
|
|
|
QuoteLocked = asset.QuoteAsset.Locked,
|
|
|
|
QuoteNetAsset = asset.QuoteAsset.NetAsset
|
|
|
|
}).ToList();
|
|
|
|
}
|
|
|
|
|
|
|
|
public override decimal QueryMaxLoanAmount(string symbol)
|
|
|
|
{
|
|
|
|
var r = binanceClient.SpotApi.Account.GetMarginMaxBorrowAmountAsync("USDT", symbol).Result;
|
|
|
|
if (!r.Success)
|
|
|
|
throw new Exception($"查询最大借币额度失败 {r.Error?.Message}");
|
|
|
|
return r.Data.Quantity;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override IsolatedMarginLoanResponse IsolatedMarginLoan(string symbol, decimal loanAmount)
|
|
|
|
{
|
|
|
|
var r = binanceClient.SpotApi.Account.MarginBorrowAsync("USDT", loanAmount, true, symbol).Result;
|
|
|
|
if (!r.Success)
|
|
|
|
throw new Exception($"借币失败 {r.Error?.Message}");
|
|
|
|
|
|
|
|
var isolatedMarginAccountAssetList = GetIsolatedMarginAccountAssets();
|
|
|
|
var currentAsset = isolatedMarginAccountAssetList.FirstOrDefault(s => s.Symbol == symbol);
|
|
|
|
if (currentAsset == null)
|
|
|
|
throw new Exception($"借币已完成,但未查询到{symbol}的资产信息 {r.Error?.Message}");
|
|
|
|
return new IsolatedMarginLoanResponse()
|
|
|
|
{
|
|
|
|
CurrentLoanAmount = loanAmount,
|
|
|
|
AccountLoanAmount = currentAsset.QuoteBorrowed
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
public override decimal IsolatedMarginRepay(string symbol, decimal repayAmount)
|
|
|
|
{
|
|
|
|
var r = binanceClient.SpotApi.Account.MarginRepayAsync("USDT", repayAmount, true, symbol).Result;
|
|
|
|
if (!r.Success)
|
|
|
|
throw new Exception($"还币失败 {r.Error?.Message}");
|
|
|
|
var txId = r.Data.TransactionId;
|
|
|
|
|
|
|
|
var r1 = binanceClient.SpotApi.Account.GetMarginRepaysAsync("USDT", txId, isolatedSymbol: symbol).Result;
|
|
|
|
if (!r1.Success)
|
|
|
|
throw new Exception($"还币成功,查询还贷记录失败 {r1.Error?.Message}");
|
|
|
|
try
|
|
|
|
{
|
|
|
|
return r1.Data.Rows.ToList().Sum(x => x.Interest);
|
|
|
|
}
|
|
|
|
catch
|
|
|
|
{
|
|
|
|
return 0M;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public override long IsolatedMarginPlaceOrder(string symbol,
|
|
|
|
Enums.TradeDirection tradeDirection,
|
|
|
|
Enums.OrderType orderType,
|
|
|
|
decimal? quantity,
|
|
|
|
decimal? quoteAmount,
|
|
|
|
decimal? price,
|
|
|
|
decimal? stopPrice,
|
|
|
|
string newClientOrderId)
|
|
|
|
{
|
|
|
|
var binanceOrderSite = (Binance.Net.Enums.OrderSide)(int)tradeDirection;
|
|
|
|
var binanceOrderType = (Binance.Net.Enums.SpotOrderType)(int)orderType;
|
|
|
|
Binance.Net.Enums.TimeInForce? timeInForce = null;
|
|
|
|
if (orderType == Enums.OrderType.STOP_LOSS_LIMIT)
|
|
|
|
timeInForce = Binance.Net.Enums.TimeInForce.GoodTillCanceled;
|
|
|
|
|
|
|
|
var r = binanceClient.SpotApi.Trading.PlaceMarginOrderAsync(symbol,
|
|
|
|
binanceOrderSite,
|
|
|
|
binanceOrderType,
|
|
|
|
quantity,
|
|
|
|
quoteAmount,
|
|
|
|
newClientOrderId,
|
|
|
|
price: price,
|
|
|
|
timeInForce: timeInForce,
|
|
|
|
stopPrice: stopPrice,
|
|
|
|
isIsolated: true,
|
|
|
|
orderResponseType: Binance.Net.Enums.OrderResponseType.Acknowledge).Result;
|
|
|
|
if (!r.Success)
|
|
|
|
throw new Exception($"下单失败 {orderType} {tradeDirection} {r.Error?.Message}");
|
|
|
|
return r.Data.Id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|