mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
* drop common uuid v4 func and imported package as needed * removed common functions regarding json marshal and unmarshal and used the json package directly. WRT unmarshal it was calling reflect and converted to string which is also checked in the JSON package so it was doing a double up, this will be a tiny gain as it was directly used in the requester package for all our outbound requests. * add in string * explicitly throw away return error value * atleast return the error that websocket initialise returns * return error when not connected * fix comment * Adds comments * move package declarations * drop append whenever we call supported * remove unused import * Change incorrect spelling * fix tests * fix go import issue
687 lines
19 KiB
Go
687 lines
19 KiB
Go
package binance
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/thrasher-corp/gocryptotrader/common"
|
|
"github.com/thrasher-corp/gocryptotrader/common/convert"
|
|
"github.com/thrasher-corp/gocryptotrader/common/crypto"
|
|
"github.com/thrasher-corp/gocryptotrader/currency"
|
|
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
|
|
log "github.com/thrasher-corp/gocryptotrader/logger"
|
|
)
|
|
|
|
const (
|
|
apiURL = "https://api.binance.com"
|
|
|
|
// Public endpoints
|
|
exchangeInfo = "/api/v1/exchangeInfo"
|
|
orderBookDepth = "/api/v1/depth"
|
|
recentTrades = "/api/v1/trades"
|
|
historicalTrades = "/api/v1/historicalTrades"
|
|
aggregatedTrades = "/api/v1/aggTrades"
|
|
candleStick = "/api/v1/klines"
|
|
averagePrice = "/api/v3/avgPrice"
|
|
priceChange = "/api/v1/ticker/24hr"
|
|
symbolPrice = "/api/v3/ticker/price"
|
|
bestPrice = "/api/v3/ticker/bookTicker"
|
|
accountInfo = "/api/v3/account"
|
|
|
|
// Authenticated endpoints
|
|
newOrderTest = "/api/v3/order/test"
|
|
newOrder = "/api/v3/order"
|
|
cancelOrder = "/api/v3/order"
|
|
queryOrder = "/api/v3/order"
|
|
openOrders = "/api/v3/openOrders"
|
|
allOrders = "/api/v3/allOrders"
|
|
|
|
// Withdraw API endpoints
|
|
withdraw = "/wapi/v3/withdraw.html"
|
|
depositHistory = "/wapi/v3/depositHistory.html"
|
|
withdrawalHistory = "/wapi/v3/withdrawHistory.html"
|
|
depositAddress = "/wapi/v3/depositAddress.html"
|
|
accountStatus = "/wapi/v3/accountStatus.html"
|
|
systemStatus = "/wapi/v3/systemStatus.html"
|
|
dustLog = "/wapi/v3/userAssetDribbletLog.html"
|
|
tradeFee = "/wapi/v3/tradeFee.html"
|
|
assetDetail = "/wapi/v3/assetDetail.html"
|
|
|
|
// binance authenticated and unauthenticated limit rates
|
|
// to-do
|
|
binanceAuthRate = 0
|
|
binanceUnauthRate = 0
|
|
)
|
|
|
|
// Binance is the overarching type across the Bithumb package
|
|
type Binance struct {
|
|
exchange.Base
|
|
WebsocketConn *wshandler.WebsocketConnection
|
|
|
|
// Valid string list that is required by the exchange
|
|
validLimits []int
|
|
validIntervals []TimeInterval
|
|
}
|
|
|
|
// GetExchangeInfo returns exchange information. Check binance_types for more
|
|
// information
|
|
func (b *Binance) GetExchangeInfo() (ExchangeInfo, error) {
|
|
var resp ExchangeInfo
|
|
path := b.API.Endpoints.URL + exchangeInfo
|
|
|
|
return resp, b.SendHTTPRequest(path, &resp)
|
|
}
|
|
|
|
// GetOrderBook returns full orderbook information
|
|
//
|
|
// OrderBookDataRequestParams contains the following members
|
|
// symbol: string of currency pair
|
|
// limit: returned limit amount
|
|
func (b *Binance) GetOrderBook(obd OrderBookDataRequestParams) (OrderBook, error) {
|
|
var orderbook OrderBook
|
|
if err := b.CheckLimit(obd.Limit); err != nil {
|
|
return orderbook, err
|
|
}
|
|
|
|
params := url.Values{}
|
|
params.Set("symbol", strings.ToUpper(obd.Symbol))
|
|
params.Set("limit", fmt.Sprintf("%d", obd.Limit))
|
|
|
|
var resp OrderBookData
|
|
path := common.EncodeURLValues(b.API.Endpoints.URL+orderBookDepth, params)
|
|
if err := b.SendHTTPRequest(path, &resp); err != nil {
|
|
return orderbook, err
|
|
}
|
|
|
|
for x := range resp.Bids {
|
|
price, err := strconv.ParseFloat(resp.Bids[x][0], 64)
|
|
if err != nil {
|
|
return orderbook, err
|
|
}
|
|
|
|
amount, err := strconv.ParseFloat(resp.Bids[x][1], 64)
|
|
if err != nil {
|
|
return orderbook, err
|
|
}
|
|
|
|
orderbook.Bids = append(orderbook.Bids, OrderbookItem{
|
|
Price: price,
|
|
Quantity: amount,
|
|
})
|
|
}
|
|
|
|
for x := range resp.Asks {
|
|
price, err := strconv.ParseFloat(resp.Asks[x][0], 64)
|
|
if err != nil {
|
|
return orderbook, err
|
|
}
|
|
|
|
amount, err := strconv.ParseFloat(resp.Asks[x][1], 64)
|
|
if err != nil {
|
|
return orderbook, err
|
|
}
|
|
|
|
orderbook.Asks = append(orderbook.Asks, OrderbookItem{
|
|
Price: price,
|
|
Quantity: amount,
|
|
})
|
|
}
|
|
|
|
orderbook.LastUpdateID = resp.LastUpdateID
|
|
return orderbook, nil
|
|
}
|
|
|
|
// GetRecentTrades returns recent trade activity
|
|
// limit: Up to 500 results returned
|
|
func (b *Binance) GetRecentTrades(rtr RecentTradeRequestParams) ([]RecentTrade, error) {
|
|
var resp []RecentTrade
|
|
|
|
params := url.Values{}
|
|
params.Set("symbol", strings.ToUpper(rtr.Symbol))
|
|
params.Set("limit", fmt.Sprintf("%d", rtr.Limit))
|
|
|
|
path := fmt.Sprintf("%s%s?%s", b.API.Endpoints.URL, recentTrades, params.Encode())
|
|
|
|
return resp, b.SendHTTPRequest(path, &resp)
|
|
}
|
|
|
|
// GetHistoricalTrades returns historical trade activity
|
|
//
|
|
// symbol: string of currency pair
|
|
// limit: Optional. Default 500; max 1000.
|
|
// fromID:
|
|
func (b *Binance) GetHistoricalTrades(symbol string, limit int, fromID int64) ([]HistoricalTrade, error) {
|
|
// Dropping support due to response for market data is always
|
|
// {"code":-2014,"msg":"API-key format invalid."}
|
|
// TODO: replace with newer API vs REST endpoint
|
|
return nil, common.ErrFunctionNotSupported
|
|
}
|
|
|
|
// GetAggregatedTrades returns aggregated trade activity
|
|
//
|
|
// symbol: string of currency pair
|
|
// limit: Optional. Default 500; max 1000.
|
|
func (b *Binance) GetAggregatedTrades(symbol string, limit int) ([]AggregatedTrade, error) {
|
|
var resp []AggregatedTrade
|
|
|
|
if err := b.CheckLimit(limit); err != nil {
|
|
return resp, err
|
|
}
|
|
|
|
params := url.Values{}
|
|
params.Set("symbol", strings.ToUpper(symbol))
|
|
params.Set("limit", strconv.Itoa(limit))
|
|
|
|
path := fmt.Sprintf("%s%s?%s", b.API.Endpoints.URL, aggregatedTrades, params.Encode())
|
|
|
|
return resp, b.SendHTTPRequest(path, &resp)
|
|
}
|
|
|
|
// GetSpotKline returns kline data
|
|
//
|
|
// KlinesRequestParams supports 5 parameters
|
|
// symbol: the symbol to get the kline data for
|
|
// limit: optinal
|
|
// interval: the interval time for the data
|
|
// startTime: startTime filter for kline data
|
|
// endTime: endTime filter for the kline data
|
|
func (b *Binance) GetSpotKline(arg KlinesRequestParams) ([]CandleStick, error) {
|
|
var resp interface{}
|
|
var kline []CandleStick
|
|
|
|
params := url.Values{}
|
|
params.Set("symbol", arg.Symbol)
|
|
params.Set("interval", string(arg.Interval))
|
|
if arg.Limit != 0 {
|
|
params.Set("limit", strconv.Itoa(arg.Limit))
|
|
}
|
|
if arg.StartTime != 0 {
|
|
params.Set("startTime", strconv.FormatInt(arg.StartTime, 10))
|
|
}
|
|
if arg.EndTime != 0 {
|
|
params.Set("endTime", strconv.FormatInt(arg.EndTime, 10))
|
|
}
|
|
|
|
path := fmt.Sprintf("%s%s?%s", b.API.Endpoints.URL, candleStick, params.Encode())
|
|
|
|
if err := b.SendHTTPRequest(path, &resp); err != nil {
|
|
return kline, err
|
|
}
|
|
|
|
for _, responseData := range resp.([]interface{}) {
|
|
var candle CandleStick
|
|
for i, individualData := range responseData.([]interface{}) {
|
|
switch i {
|
|
case 0:
|
|
candle.OpenTime = individualData.(float64)
|
|
case 1:
|
|
candle.Open, _ = strconv.ParseFloat(individualData.(string), 64)
|
|
case 2:
|
|
candle.High, _ = strconv.ParseFloat(individualData.(string), 64)
|
|
case 3:
|
|
candle.Low, _ = strconv.ParseFloat(individualData.(string), 64)
|
|
case 4:
|
|
candle.Close, _ = strconv.ParseFloat(individualData.(string), 64)
|
|
case 5:
|
|
candle.Volume, _ = strconv.ParseFloat(individualData.(string), 64)
|
|
case 6:
|
|
candle.CloseTime = individualData.(float64)
|
|
case 7:
|
|
candle.QuoteAssetVolume, _ = strconv.ParseFloat(individualData.(string), 64)
|
|
case 8:
|
|
candle.TradeCount = individualData.(float64)
|
|
case 9:
|
|
candle.TakerBuyAssetVolume, _ = strconv.ParseFloat(individualData.(string), 64)
|
|
case 10:
|
|
candle.TakerBuyQuoteAssetVolume, _ = strconv.ParseFloat(individualData.(string), 64)
|
|
}
|
|
}
|
|
kline = append(kline, candle)
|
|
}
|
|
return kline, nil
|
|
}
|
|
|
|
// GetAveragePrice returns current average price for a symbol.
|
|
//
|
|
// symbol: string of currency pair
|
|
func (b *Binance) GetAveragePrice(symbol string) (AveragePrice, error) {
|
|
resp := AveragePrice{}
|
|
params := url.Values{}
|
|
params.Set("symbol", strings.ToUpper(symbol))
|
|
|
|
path := fmt.Sprintf("%s%s?%s", b.API.Endpoints.URL, averagePrice, params.Encode())
|
|
|
|
return resp, b.SendHTTPRequest(path, &resp)
|
|
}
|
|
|
|
// GetPriceChangeStats returns price change statistics for the last 24 hours
|
|
//
|
|
// symbol: string of currency pair
|
|
func (b *Binance) GetPriceChangeStats(symbol string) (PriceChangeStats, error) {
|
|
resp := PriceChangeStats{}
|
|
params := url.Values{}
|
|
params.Set("symbol", strings.ToUpper(symbol))
|
|
|
|
path := fmt.Sprintf("%s%s?%s", b.API.Endpoints.URL, priceChange, params.Encode())
|
|
|
|
return resp, b.SendHTTPRequest(path, &resp)
|
|
}
|
|
|
|
// GetTickers returns the ticker data for the last 24 hrs
|
|
func (b *Binance) GetTickers() ([]PriceChangeStats, error) {
|
|
var resp []PriceChangeStats
|
|
path := fmt.Sprintf("%s%s", b.API.Endpoints.URL, priceChange)
|
|
return resp, b.SendHTTPRequest(path, &resp)
|
|
}
|
|
|
|
// GetLatestSpotPrice returns latest spot price of symbol
|
|
//
|
|
// symbol: string of currency pair
|
|
func (b *Binance) GetLatestSpotPrice(symbol string) (SymbolPrice, error) {
|
|
resp := SymbolPrice{}
|
|
params := url.Values{}
|
|
params.Set("symbol", strings.ToUpper(symbol))
|
|
|
|
path := fmt.Sprintf("%s%s?%s", b.API.Endpoints.URL, symbolPrice, params.Encode())
|
|
|
|
return resp, b.SendHTTPRequest(path, &resp)
|
|
}
|
|
|
|
// GetBestPrice returns the latest best price for symbol
|
|
//
|
|
// symbol: string of currency pair
|
|
func (b *Binance) GetBestPrice(symbol string) (BestPrice, error) {
|
|
resp := BestPrice{}
|
|
params := url.Values{}
|
|
params.Set("symbol", strings.ToUpper(symbol))
|
|
|
|
path := fmt.Sprintf("%s%s?%s", b.API.Endpoints.URL, bestPrice, params.Encode())
|
|
|
|
return resp, b.SendHTTPRequest(path, &resp)
|
|
}
|
|
|
|
// NewOrder sends a new order to Binance
|
|
func (b *Binance) NewOrder(o *NewOrderRequest) (NewOrderResponse, error) {
|
|
var resp NewOrderResponse
|
|
|
|
path := fmt.Sprintf("%s%s", b.API.Endpoints.URL, newOrder)
|
|
|
|
params := url.Values{}
|
|
params.Set("symbol", o.Symbol)
|
|
params.Set("side", o.Side)
|
|
params.Set("type", string(o.TradeType))
|
|
params.Set("quantity", strconv.FormatFloat(o.Quantity, 'f', -1, 64))
|
|
if o.TradeType == BinanceRequestParamsOrderLimit {
|
|
params.Set("price", strconv.FormatFloat(o.Price, 'f', -1, 64))
|
|
}
|
|
if o.TimeInForce != "" {
|
|
params.Set("timeInForce", string(o.TimeInForce))
|
|
}
|
|
|
|
if o.NewClientOrderID != "" {
|
|
params.Set("newClientOrderID", o.NewClientOrderID)
|
|
}
|
|
|
|
if o.StopPrice != 0 {
|
|
params.Set("stopPrice", strconv.FormatFloat(o.StopPrice, 'f', -1, 64))
|
|
}
|
|
|
|
if o.IcebergQty != 0 {
|
|
params.Set("icebergQty", strconv.FormatFloat(o.IcebergQty, 'f', -1, 64))
|
|
}
|
|
|
|
if o.NewOrderRespType != "" {
|
|
params.Set("newOrderRespType", o.NewOrderRespType)
|
|
}
|
|
|
|
if err := b.SendAuthHTTPRequest(http.MethodPost, path, params, &resp); err != nil {
|
|
return resp, err
|
|
}
|
|
|
|
if resp.Code != 0 {
|
|
return resp, errors.New(resp.Msg)
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
// CancelExistingOrder sends a cancel order to Binance
|
|
func (b *Binance) CancelExistingOrder(symbol string, orderID int64, origClientOrderID string) (CancelOrderResponse, error) {
|
|
var resp CancelOrderResponse
|
|
|
|
path := fmt.Sprintf("%s%s", b.API.Endpoints.URL, cancelOrder)
|
|
|
|
params := url.Values{}
|
|
params.Set("symbol", symbol)
|
|
|
|
if orderID != 0 {
|
|
params.Set("orderId", strconv.FormatInt(orderID, 10))
|
|
}
|
|
|
|
if origClientOrderID != "" {
|
|
params.Set("origClientOrderId", origClientOrderID)
|
|
}
|
|
|
|
return resp, b.SendAuthHTTPRequest(http.MethodDelete, path, params, &resp)
|
|
}
|
|
|
|
// OpenOrders Current open orders. Get all open orders on a symbol.
|
|
// Careful when accessing this with no symbol: The number of requests counted against the rate limiter
|
|
// is equal to the number of symbols currently trading on the exchange.
|
|
func (b *Binance) OpenOrders(symbol string) ([]QueryOrderData, error) {
|
|
var resp []QueryOrderData
|
|
|
|
path := fmt.Sprintf("%s%s", b.API.Endpoints.URL, openOrders)
|
|
|
|
params := url.Values{}
|
|
|
|
if symbol != "" {
|
|
params.Set("symbol", strings.ToUpper(symbol))
|
|
}
|
|
|
|
if err := b.SendAuthHTTPRequest(http.MethodGet, path, params, &resp); err != nil {
|
|
return resp, err
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
// AllOrders Get all account orders; active, canceled, or filled.
|
|
// orderId optional param
|
|
// limit optional param, default 500; max 500
|
|
func (b *Binance) AllOrders(symbol, orderID, limit string) ([]QueryOrderData, error) {
|
|
var resp []QueryOrderData
|
|
|
|
path := fmt.Sprintf("%s%s", b.API.Endpoints.URL, allOrders)
|
|
|
|
params := url.Values{}
|
|
params.Set("symbol", strings.ToUpper(symbol))
|
|
if orderID != "" {
|
|
params.Set("orderId", orderID)
|
|
}
|
|
if limit != "" {
|
|
params.Set("limit", limit)
|
|
}
|
|
if err := b.SendAuthHTTPRequest(http.MethodGet, path, params, &resp); err != nil {
|
|
return resp, err
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
// QueryOrder returns information on a past order
|
|
func (b *Binance) QueryOrder(symbol, origClientOrderID string, orderID int64) (QueryOrderData, error) {
|
|
var resp QueryOrderData
|
|
|
|
path := fmt.Sprintf("%s%s", b.API.Endpoints.URL, queryOrder)
|
|
|
|
params := url.Values{}
|
|
params.Set("symbol", strings.ToUpper(symbol))
|
|
if origClientOrderID != "" {
|
|
params.Set("origClientOrderId", origClientOrderID)
|
|
}
|
|
if orderID != 0 {
|
|
params.Set("orderId", strconv.FormatInt(orderID, 10))
|
|
}
|
|
|
|
if err := b.SendAuthHTTPRequest(http.MethodGet, path, params, &resp); err != nil {
|
|
return resp, err
|
|
}
|
|
|
|
if resp.Code != 0 {
|
|
return resp, errors.New(resp.Msg)
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
// GetAccount returns binance user accounts
|
|
func (b *Binance) GetAccount() (*Account, error) {
|
|
type response struct {
|
|
Response
|
|
Account
|
|
}
|
|
|
|
var resp response
|
|
|
|
path := fmt.Sprintf("%s%s", b.API.Endpoints.URL, accountInfo)
|
|
params := url.Values{}
|
|
|
|
if err := b.SendAuthHTTPRequest(http.MethodGet, path, params, &resp); err != nil {
|
|
return &resp.Account, err
|
|
}
|
|
|
|
if resp.Code != 0 {
|
|
return &resp.Account, errors.New(resp.Msg)
|
|
}
|
|
|
|
return &resp.Account, nil
|
|
}
|
|
|
|
// SendHTTPRequest sends an unauthenticated request
|
|
func (b *Binance) SendHTTPRequest(path string, result interface{}) error {
|
|
return b.SendPayload(http.MethodGet,
|
|
path,
|
|
nil,
|
|
nil,
|
|
result,
|
|
false,
|
|
false,
|
|
b.Verbose,
|
|
b.HTTPDebugging,
|
|
b.HTTPRecording)
|
|
}
|
|
|
|
// SendAuthHTTPRequest sends an authenticated HTTP request
|
|
func (b *Binance) SendAuthHTTPRequest(method, path string, params url.Values, result interface{}) error {
|
|
if !b.AllowAuthenticatedRequest() {
|
|
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name)
|
|
}
|
|
|
|
if params == nil {
|
|
params = url.Values{}
|
|
}
|
|
params.Set("recvWindow", strconv.FormatInt(convert.RecvWindow(5*time.Second), 10))
|
|
params.Set("timestamp", strconv.FormatInt(time.Now().Unix()*1000, 10))
|
|
|
|
signature := params.Encode()
|
|
hmacSigned := crypto.GetHMAC(crypto.HashSHA256, []byte(signature), []byte(b.API.Credentials.Secret))
|
|
hmacSignedStr := crypto.HexEncodeToString(hmacSigned)
|
|
|
|
headers := make(map[string]string)
|
|
headers["X-MBX-APIKEY"] = b.API.Credentials.Key
|
|
|
|
if b.Verbose {
|
|
log.Debugf(log.ExchangeSys, "sent path: %s", path)
|
|
}
|
|
|
|
path = common.EncodeURLValues(path, params)
|
|
path += fmt.Sprintf("&signature=%s", hmacSignedStr)
|
|
|
|
interim := json.RawMessage{}
|
|
|
|
errCap := struct {
|
|
Success bool `json:"success"`
|
|
Message string `json:"msg"`
|
|
}{}
|
|
|
|
err := b.SendPayload(method,
|
|
path,
|
|
headers,
|
|
bytes.NewBuffer(nil),
|
|
&interim,
|
|
true,
|
|
false,
|
|
b.Verbose,
|
|
b.HTTPDebugging,
|
|
b.HTTPRecording)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := json.Unmarshal(interim, &errCap); err == nil {
|
|
if !errCap.Success && errCap.Message != "" {
|
|
return errors.New(errCap.Message)
|
|
}
|
|
}
|
|
|
|
return json.Unmarshal(interim, result)
|
|
}
|
|
|
|
// CheckLimit checks value against a variable list
|
|
func (b *Binance) CheckLimit(limit int) error {
|
|
for x := range b.validLimits {
|
|
if b.validLimits[x] == limit {
|
|
return nil
|
|
}
|
|
}
|
|
return errors.New("incorrect limit values - valid values are 5, 10, 20, 50, 100, 500, 1000")
|
|
}
|
|
|
|
// CheckSymbol checks value against a variable list
|
|
func (b *Binance) CheckSymbol(symbol string, assetType asset.Item) error {
|
|
enPairs := b.GetAvailablePairs(assetType)
|
|
for x := range enPairs {
|
|
if b.FormatExchangeCurrency(enPairs[x], assetType).String() == symbol {
|
|
return nil
|
|
}
|
|
}
|
|
return errors.New("incorrect symbol values - please check available pairs in configuration")
|
|
}
|
|
|
|
// CheckIntervals checks value against a variable list
|
|
func (b *Binance) CheckIntervals(interval string) error {
|
|
for x := range b.validIntervals {
|
|
if TimeInterval(interval) == b.validIntervals[x] {
|
|
return nil
|
|
}
|
|
}
|
|
return errors.New(`incorrect interval values - valid values are "1m","3m","5m","15m","30m","1h","2h","4h","6h","8h","12h","1d","3d","1w","1M"`)
|
|
}
|
|
|
|
// SetValues sets the default valid values
|
|
func (b *Binance) SetValues() {
|
|
b.validLimits = []int{5, 10, 20, 50, 100, 500, 1000}
|
|
b.validIntervals = []TimeInterval{
|
|
TimeIntervalMinute,
|
|
TimeIntervalThreeMinutes,
|
|
TimeIntervalFiveMinutes,
|
|
TimeIntervalFifteenMinutes,
|
|
TimeIntervalThirtyMinutes,
|
|
TimeIntervalHour,
|
|
TimeIntervalTwoHours,
|
|
TimeIntervalFourHours,
|
|
TimeIntervalSixHours,
|
|
TimeIntervalEightHours,
|
|
TimeIntervalTwelveHours,
|
|
TimeIntervalDay,
|
|
TimeIntervalThreeDays,
|
|
TimeIntervalWeek,
|
|
TimeIntervalMonth,
|
|
}
|
|
}
|
|
|
|
// GetFee returns an estimate of fee based on type of transaction
|
|
func (b *Binance) GetFee(feeBuilder *exchange.FeeBuilder) (float64, error) {
|
|
var fee float64
|
|
|
|
switch feeBuilder.FeeType {
|
|
case exchange.CryptocurrencyTradeFee:
|
|
multiplier, err := b.getMultiplier(feeBuilder.IsMaker)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount, multiplier)
|
|
case exchange.CryptocurrencyWithdrawalFee:
|
|
fee = getCryptocurrencyWithdrawalFee(feeBuilder.Pair.Base)
|
|
case exchange.OfflineTradeFee:
|
|
fee = getOfflineTradeFee(feeBuilder.PurchasePrice, feeBuilder.Amount)
|
|
}
|
|
if fee < 0 {
|
|
fee = 0
|
|
}
|
|
return fee, nil
|
|
}
|
|
|
|
// getOfflineTradeFee calculates the worst case-scenario trading fee
|
|
func getOfflineTradeFee(price, amount float64) float64 {
|
|
return 0.002 * price * amount
|
|
}
|
|
|
|
// getMultiplier retrieves account based taker/maker fees
|
|
func (b *Binance) getMultiplier(isMaker bool) (float64, error) {
|
|
var multiplier float64
|
|
account, err := b.GetAccount()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if isMaker {
|
|
multiplier = float64(account.MakerCommission)
|
|
} else {
|
|
multiplier = float64(account.TakerCommission)
|
|
}
|
|
return multiplier, nil
|
|
}
|
|
|
|
// calculateTradingFee returns the fee for trading any currency on Bittrex
|
|
func calculateTradingFee(purchasePrice, amount, multiplier float64) float64 {
|
|
return (multiplier / 100) * purchasePrice * amount
|
|
}
|
|
|
|
// getCryptocurrencyWithdrawalFee returns the fee for withdrawing from the exchange
|
|
func getCryptocurrencyWithdrawalFee(c currency.Code) float64 {
|
|
return WithdrawalFees[c]
|
|
}
|
|
|
|
// WithdrawCrypto sends cryptocurrency to the address of your choosing
|
|
func (b *Binance) WithdrawCrypto(asset, address, addressTag, name, amount string) (string, error) {
|
|
var resp WithdrawResponse
|
|
path := fmt.Sprintf("%s%s", b.API.Endpoints.URL, withdraw)
|
|
|
|
params := url.Values{}
|
|
params.Set("asset", asset)
|
|
params.Set("address", address)
|
|
params.Set("amount", amount)
|
|
if len(name) > 0 {
|
|
params.Set("name", name)
|
|
}
|
|
if len(addressTag) > 0 {
|
|
params.Set("addressTag", addressTag)
|
|
}
|
|
|
|
if err := b.SendAuthHTTPRequest(http.MethodPost, path, params, &resp); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if !resp.Success {
|
|
return resp.ID, errors.New(resp.Msg)
|
|
}
|
|
|
|
return resp.ID, nil
|
|
}
|
|
|
|
// GetDepositAddressForCurrency retrieves the wallet address for a given currency
|
|
func (b *Binance) GetDepositAddressForCurrency(currency string) (string, error) {
|
|
path := fmt.Sprintf("%s%s", b.API.Endpoints.URL, depositAddress)
|
|
|
|
resp := struct {
|
|
Address string `json:"address"`
|
|
Success bool `json:"success"`
|
|
AddressTag string `json:"addressTag"`
|
|
}{}
|
|
|
|
params := url.Values{}
|
|
params.Set("asset", currency)
|
|
params.Set("status", "true")
|
|
|
|
return resp.Address,
|
|
b.SendAuthHTTPRequest(http.MethodGet, path, params, &resp)
|
|
}
|