mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-14 07:26:47 +00:00
* Initial commit * Updates signature for all withdrawal methods to use new withdrawRequest struct type * Implements crypto withdraw features & tests for Alphapoint, ANX, Binance, Bitfinex, Bitflyer, Bithumb, Bitmex, Bitstamp, Bittrex, BTCC, BTCmarkets, CoinbasePro, Coinut. Updates WithdrawRequest type with more members. Breaking change to update real order testing for increased code coverage * Updates all realOrder tests to run when no API key is present. Updates exchange functions to handle errors better * Implements crypto withdrawals for Exmo, GateIO, Gemini, HitBTC, Huobi, HuobiHadax, Kraken, LakeBTC, Liqui, Localbitcoins, OKCoin, OKEX, Poloniex, Wex, Yobit and ZB. Updates real order test formatting for all real order tests * Update alphapoint. Fixes anx typos. Adds function WithdrawFiatFundsToInternationalBank to exchange wrapper interface. Adds WithdrawFiatFundsToInternationalBank to alphapoint, bitmex, coinbasepro. Updates Kraken to use TradePassword property * Reverts alphapoint to use ErrNotYetImplemented * Fixes line spacing and removes unnecessary line
758 lines
21 KiB
Go
758 lines
21 KiB
Go
package binance
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"net/url"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/gorilla/websocket"
|
|
"github.com/thrasher-/gocryptotrader/common"
|
|
"github.com/thrasher-/gocryptotrader/config"
|
|
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
|
"github.com/thrasher-/gocryptotrader/exchanges/request"
|
|
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
|
)
|
|
|
|
// Binance is the overarching type across the Bithumb package
|
|
type Binance struct {
|
|
exchange.Base
|
|
WebsocketConn *websocket.Conn
|
|
|
|
// Valid string list that is required by the exchange
|
|
validLimits []int
|
|
validIntervals []TimeInterval
|
|
}
|
|
|
|
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
|
|
)
|
|
|
|
// SetDefaults sets the basic defaults for Binance
|
|
func (b *Binance) SetDefaults() {
|
|
b.Name = "Binance"
|
|
b.Enabled = false
|
|
b.Verbose = false
|
|
b.RESTPollingDelay = 10
|
|
b.RequestCurrencyPairFormat.Delimiter = ""
|
|
b.RequestCurrencyPairFormat.Uppercase = true
|
|
b.ConfigCurrencyPairFormat.Delimiter = "-"
|
|
b.ConfigCurrencyPairFormat.Uppercase = true
|
|
b.AssetTypes = []string{ticker.Spot}
|
|
b.SupportsAutoPairUpdating = true
|
|
b.SupportsRESTTickerBatching = true
|
|
b.APIWithdrawPermissions = exchange.AutoWithdrawCrypto
|
|
b.SetValues()
|
|
b.Requester = request.New(b.Name,
|
|
request.NewRateLimit(time.Second, binanceAuthRate),
|
|
request.NewRateLimit(time.Second, binanceUnauthRate),
|
|
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
|
|
b.APIUrlDefault = apiURL
|
|
b.APIUrl = b.APIUrlDefault
|
|
b.WebsocketInit()
|
|
}
|
|
|
|
// Setup takes in the supplied exchange configuration details and sets params
|
|
func (b *Binance) Setup(exch config.ExchangeConfig) {
|
|
if !exch.Enabled {
|
|
b.SetEnabled(false)
|
|
} else {
|
|
b.Enabled = true
|
|
b.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
|
b.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
|
|
b.SetHTTPClientTimeout(exch.HTTPTimeout)
|
|
b.SetHTTPClientUserAgent(exch.HTTPUserAgent)
|
|
b.RESTPollingDelay = exch.RESTPollingDelay
|
|
b.Verbose = exch.Verbose
|
|
b.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
|
b.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
|
b.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
|
err := b.SetCurrencyPairFormat()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = b.SetAssetTypes()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = b.SetAutoPairDefaults()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = b.SetAPIURL(exch)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = b.SetClientProxyAddress(exch.ProxyAddress)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = b.WebsocketSetup(b.WSConnect,
|
|
exch.Name,
|
|
exch.Websocket,
|
|
binanceDefaultWebsocketURL,
|
|
exch.WebsocketURL)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// GetExchangeValidCurrencyPairs returns the full pair list from the exchange
|
|
// at the moment do not integrate with config currency pairs automatically
|
|
func (b *Binance) GetExchangeValidCurrencyPairs() ([]string, error) {
|
|
var validCurrencyPairs []string
|
|
|
|
info, err := b.GetExchangeInfo()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, symbol := range info.Symbols {
|
|
if symbol.Status == "TRADING" {
|
|
validCurrencyPairs = append(validCurrencyPairs, symbol.BaseAsset+"-"+symbol.QuoteAsset)
|
|
}
|
|
}
|
|
return validCurrencyPairs, nil
|
|
}
|
|
|
|
// GetExchangeInfo returns exchange information. Check binance_types for more
|
|
// information
|
|
func (b *Binance) GetExchangeInfo() (ExchangeInfo, error) {
|
|
var resp ExchangeInfo
|
|
path := b.APIUrl + 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) {
|
|
orderbook, resp := OrderBook{}, OrderBookData{}
|
|
|
|
if err := b.CheckLimit(obd.Limit); err != nil {
|
|
return orderbook, err
|
|
}
|
|
if err := b.CheckSymbol(obd.Symbol); err != nil {
|
|
return orderbook, err
|
|
}
|
|
|
|
params := url.Values{}
|
|
params.Set("symbol", common.StringToUpper(obd.Symbol))
|
|
params.Set("limit", fmt.Sprintf("%d", obd.Limit))
|
|
|
|
path := fmt.Sprintf("%s%s?%s", b.APIUrl, orderBookDepth, params.Encode())
|
|
|
|
if err := b.SendHTTPRequest(path, &resp); err != nil {
|
|
return orderbook, err
|
|
}
|
|
|
|
for _, asks := range resp.Asks {
|
|
var ASK struct {
|
|
Price float64
|
|
Quantity float64
|
|
}
|
|
for i, ask := range asks.([]interface{}) {
|
|
switch i {
|
|
case 0:
|
|
ASK.Price, _ = strconv.ParseFloat(ask.(string), 64)
|
|
case 1:
|
|
ASK.Quantity, _ = strconv.ParseFloat(ask.(string), 64)
|
|
orderbook.Asks = append(orderbook.Asks, ASK)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, bids := range resp.Bids {
|
|
var BID struct {
|
|
Price float64
|
|
Quantity float64
|
|
}
|
|
for i, bid := range bids.([]interface{}) {
|
|
switch i {
|
|
case 0:
|
|
BID.Price, _ = strconv.ParseFloat(bid.(string), 64)
|
|
case 1:
|
|
BID.Quantity, _ = strconv.ParseFloat(bid.(string), 64)
|
|
orderbook.Bids = append(orderbook.Bids, BID)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
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) {
|
|
resp := []RecentTrade{}
|
|
|
|
params := url.Values{}
|
|
params.Set("symbol", common.StringToUpper(rtr.Symbol))
|
|
params.Set("limit", fmt.Sprintf("%d", rtr.Limit))
|
|
|
|
path := fmt.Sprintf("%s%s?%s", b.APIUrl, 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) {
|
|
resp := []HistoricalTrade{}
|
|
|
|
if err := b.CheckLimit(limit); err != nil {
|
|
return resp, err
|
|
}
|
|
|
|
params := url.Values{}
|
|
params.Set("symbol", common.StringToUpper(symbol))
|
|
params.Set("limit", strconv.Itoa(limit))
|
|
params.Set("fromid", strconv.FormatInt(fromID, 10))
|
|
|
|
path := fmt.Sprintf("%s%s?%s", b.APIUrl, historicalTrades, params.Encode())
|
|
|
|
return resp, b.SendHTTPRequest(path, &resp)
|
|
}
|
|
|
|
// 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) {
|
|
resp := []AggregatedTrade{}
|
|
|
|
if err := b.CheckLimit(limit); err != nil {
|
|
return resp, err
|
|
}
|
|
if err := b.CheckSymbol(symbol); err != nil {
|
|
return resp, err
|
|
}
|
|
|
|
params := url.Values{}
|
|
params.Set("symbol", common.StringToUpper(symbol))
|
|
params.Set("limit", strconv.Itoa(limit))
|
|
|
|
path := fmt.Sprintf("%s%s?%s", b.APIUrl, 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.APIUrl, 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{}
|
|
|
|
if err := b.CheckSymbol(symbol); err != nil {
|
|
return resp, err
|
|
}
|
|
|
|
params := url.Values{}
|
|
params.Set("symbol", common.StringToUpper(symbol))
|
|
|
|
path := fmt.Sprintf("%s%s?%s", b.APIUrl, 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{}
|
|
|
|
if err := b.CheckSymbol(symbol); err != nil {
|
|
return resp, err
|
|
}
|
|
|
|
params := url.Values{}
|
|
params.Set("symbol", common.StringToUpper(symbol))
|
|
|
|
path := fmt.Sprintf("%s%s?%s", b.APIUrl, 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.APIUrl, 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{}
|
|
|
|
if err := b.CheckSymbol(symbol); err != nil {
|
|
return resp, err
|
|
}
|
|
|
|
params := url.Values{}
|
|
params.Set("symbol", common.StringToUpper(symbol))
|
|
|
|
path := fmt.Sprintf("%s%s?%s", b.APIUrl, 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{}
|
|
|
|
if err := b.CheckSymbol(symbol); err != nil {
|
|
return resp, err
|
|
}
|
|
|
|
params := url.Values{}
|
|
params.Set("symbol", common.StringToUpper(symbol))
|
|
|
|
path := fmt.Sprintf("%s%s?%s", b.APIUrl, 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.APIUrl, newOrder)
|
|
|
|
params := url.Values{}
|
|
params.Set("symbol", o.Symbol)
|
|
params.Set("side", string(o.Side))
|
|
params.Set("type", string(o.TradeType))
|
|
params.Set("quantity", strconv.FormatFloat(o.Quantity, 'f', -1, 64))
|
|
if o.TradeType == "LIMIT" {
|
|
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("POST", 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.APIUrl, 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("DELETE", path, params, &resp)
|
|
}
|
|
|
|
// OpenOrders Current open orders
|
|
// Get all open orders on a symbol. Careful when accessing this with no symbol.
|
|
func (b *Binance) OpenOrders(symbol string) ([]QueryOrderData, error) {
|
|
var resp []QueryOrderData
|
|
path := fmt.Sprintf("%s%s", b.APIUrl, openOrders)
|
|
params := url.Values{}
|
|
|
|
if symbol != "" {
|
|
params.Set("symbol", common.StringToUpper(symbol))
|
|
}
|
|
|
|
if err := b.SendAuthHTTPRequest("GET", 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.APIUrl, allOrders)
|
|
|
|
params := url.Values{}
|
|
params.Set("symbol", common.StringToUpper(symbol))
|
|
if orderID != "" {
|
|
params.Set("orderId", orderID)
|
|
}
|
|
if limit != "" {
|
|
params.Set("limit", limit)
|
|
}
|
|
if err := b.SendAuthHTTPRequest("GET", 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.APIUrl, queryOrder)
|
|
|
|
params := url.Values{}
|
|
params.Set("symbol", common.StringToUpper(symbol))
|
|
if origClientOrderID != "" {
|
|
params.Set("origClientOrderId", origClientOrderID)
|
|
}
|
|
if orderID != 0 {
|
|
params.Set("orderId", strconv.FormatInt(orderID, 10))
|
|
}
|
|
|
|
if err := b.SendAuthHTTPRequest("GET", 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.APIUrl, accountInfo)
|
|
params := url.Values{}
|
|
|
|
if err := b.SendAuthHTTPRequest("GET", 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("GET", path, nil, nil, result, false, b.Verbose)
|
|
}
|
|
|
|
// SendAuthHTTPRequest sends an authenticated HTTP request
|
|
func (b *Binance) SendAuthHTTPRequest(method, path string, params url.Values, result interface{}) error {
|
|
if !b.AuthenticatedAPISupport {
|
|
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name)
|
|
}
|
|
|
|
if params == nil {
|
|
params = url.Values{}
|
|
}
|
|
params.Set("recvWindow", strconv.FormatInt(common.RecvWindow(5*time.Second), 10))
|
|
params.Set("timestamp", strconv.FormatInt(time.Now().Unix()*1000, 10))
|
|
|
|
signature := params.Encode()
|
|
hmacSigned := common.GetHMAC(common.HashSHA256, []byte(signature), []byte(b.APISecret))
|
|
hmacSignedStr := common.HexEncodeToString(hmacSigned)
|
|
params.Set("signature", hmacSignedStr)
|
|
|
|
headers := make(map[string]string)
|
|
headers["X-MBX-APIKEY"] = b.APIKey
|
|
|
|
if b.Verbose {
|
|
log.Printf("sent path: \n%s\n", path)
|
|
}
|
|
path = common.EncodeURLValues(path, params)
|
|
|
|
return b.SendPayload(method, path, headers, bytes.NewBufferString(""), result, true, b.Verbose)
|
|
}
|
|
|
|
// 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) error {
|
|
enPairs := b.GetAvailableCurrencies()
|
|
for x := range enPairs {
|
|
if exchange.FormatExchangeCurrency(b.Name, enPairs[x]).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.FirstCurrency, feeBuilder.PurchasePrice, feeBuilder.Amount)
|
|
}
|
|
if fee < 0 {
|
|
fee = 0
|
|
}
|
|
return fee, nil
|
|
}
|
|
|
|
// 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(currency string, purchasePrice, amount float64) float64 {
|
|
return WithdrawalFees[currency]
|
|
}
|
|
|
|
// WithdrawCrypto sends cryptocurrency to the address of your choosing
|
|
func (b *Binance) WithdrawCrypto(asset, address, addressTag, name, amount string) (int64, error) {
|
|
var resp WithdrawResponse
|
|
path := fmt.Sprintf("%s%s", b.APIUrl, withdraw)
|
|
|
|
params := url.Values{}
|
|
params.Set("asset", asset)
|
|
params.Set("address", string(address))
|
|
params.Set("amount", string(amount))
|
|
if len(name) > 0 {
|
|
params.Set("name", string(name))
|
|
}
|
|
if len(addressTag) > 0 {
|
|
params.Set("addressTag", string(addressTag))
|
|
}
|
|
|
|
if err := b.SendAuthHTTPRequest("POST", path, params, &resp); err != nil {
|
|
return -1, 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) error {
|
|
path := fmt.Sprintf("%s%s", b.APIUrl, depositAddress)
|
|
var resp interface{}
|
|
params := url.Values{}
|
|
params.Set("asset", currency)
|
|
|
|
if err := b.SendAuthHTTPRequest("GET", path, params, &resp); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|