mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
* Changes method signature for cancelling all orders (experitmental). Implements cancelAllOrders wrapper for alphapoint, anx, binance * Implements cancel all wrapper for bitfinex, bitmex, bitstamp, bittrex, btcmarkets, coinbasepro and hilariously coinut * Changes method signature to only use one OrderCancellation type. Adds support for Exmo, gateio, gemini, itbit, lakebtc * Adds/updates support for hitbtc, huobi, hadax, itbit and kraken * Adds support for liqui, localbitcoins, okcoin, poloniex, wex and yobit. Splits up open order methods for poloniex * Adds bithumb, okex and zb support. BTCC for another PR * Updates bitflyer, bithumb, bitmex, coinut, okex and zb cancelAllOrders method to cancel via enabled currency pairs rather than a singular currency * Adds tests to all exchanges to test wrapper function CancelAllOrders * Fixes OKEX and huobi, btcmarkets, kraken, okCoin cancel order implementations * Fixes coinut, hitbtc and okex api for authenticated requests * Fixes comment and spacing * Changes the CancelAllOrders signature to return orderids and errors along with a generic error. * Fixes OKEX delimiter * Removes spacing and test verbosity * Removes more spacing * Removes space * Fixes okex rebasing issue. Also makes the maps instead of assuming they just work
1002 lines
31 KiB
Go
1002 lines
31 KiB
Go
package bitfinex
|
|
|
|
import (
|
|
"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"
|
|
)
|
|
|
|
const (
|
|
bitfinexAPIURLBase = "https://api.bitfinex.com"
|
|
bitfinexAPIVersion = "/v1/"
|
|
bitfinexAPIVersion2 = "2"
|
|
bitfinexTickerV2 = "ticker"
|
|
bitfinexTickersV2 = "tickers"
|
|
bitfinexTicker = "pubticker/"
|
|
bitfinexStats = "stats/"
|
|
bitfinexLendbook = "lendbook/"
|
|
bitfinexOrderbookV2 = "book"
|
|
bitfinexOrderbook = "book/"
|
|
bitfinexTrades = "trades/"
|
|
bitfinexTradesV2 = "https://api.bitfinex.com/v2/trades/%s/hist?limit=1000&start=%s&end=%s"
|
|
bitfinexKeyPermissions = "key_info"
|
|
bitfinexLends = "lends/"
|
|
bitfinexSymbols = "symbols/"
|
|
bitfinexSymbolsDetails = "symbols_details/"
|
|
bitfinexAccountInfo = "account_infos"
|
|
bitfinexAccountFees = "account_fees"
|
|
bitfinexAccountSummary = "summary"
|
|
bitfinexDeposit = "deposit/new"
|
|
bitfinexOrderNew = "order/new"
|
|
bitfinexOrderNewMulti = "order/new/multi"
|
|
bitfinexOrderCancel = "order/cancel"
|
|
bitfinexOrderCancelMulti = "order/cancel/multi"
|
|
bitfinexOrderCancelAll = "order/cancel/all"
|
|
bitfinexOrderCancelReplace = "order/cancel/replace"
|
|
bitfinexOrderStatus = "order/status"
|
|
bitfinexOrders = "orders"
|
|
bitfinexPositions = "positions"
|
|
bitfinexClaimPosition = "position/claim"
|
|
bitfinexHistory = "history"
|
|
bitfinexHistoryMovements = "history/movements"
|
|
bitfinexTradeHistory = "mytrades"
|
|
bitfinexOfferNew = "offer/new"
|
|
bitfinexOfferCancel = "offer/cancel"
|
|
bitfinexOfferStatus = "offer/status"
|
|
bitfinexOffers = "offers"
|
|
bitfinexMarginActiveFunds = "taken_funds"
|
|
bitfinexMarginTotalFunds = "total_taken_funds"
|
|
bitfinexMarginUnusedFunds = "unused_taken_funds"
|
|
bitfinexMarginClose = "funding/close"
|
|
bitfinexBalances = "balances"
|
|
bitfinexMarginInfo = "margin_infos"
|
|
bitfinexTransfer = "transfer"
|
|
bitfinexWithdrawal = "withdraw"
|
|
bitfinexActiveCredits = "credits"
|
|
bitfinexPlatformStatus = "platform/status"
|
|
|
|
// requests per minute
|
|
bitfinexAuthRate = 10
|
|
bitfinexUnauthRate = 10
|
|
|
|
// Bitfinex platform status values
|
|
// When the platform is marked in maintenance mode bots should stop trading
|
|
// activity. Cancelling orders will be still possible.
|
|
bitfinexMaintenanceMode = 0
|
|
bitfinexOperativeMode = 1
|
|
)
|
|
|
|
// Bitfinex is the overarching type across the bitfinex package
|
|
// Notes: Bitfinex has added a rate limit to the number of REST requests.
|
|
// Rate limit policy can vary in a range of 10 to 90 requests per minute
|
|
// depending on some factors (e.g. servers load, endpoint, etc.).
|
|
type Bitfinex struct {
|
|
exchange.Base
|
|
WebsocketConn *websocket.Conn
|
|
WebsocketSubdChannels map[int]WebsocketChanInfo
|
|
}
|
|
|
|
// SetDefaults sets the basic defaults for bitfinex
|
|
func (b *Bitfinex) SetDefaults() {
|
|
b.Name = "Bitfinex"
|
|
b.Enabled = false
|
|
b.Verbose = false
|
|
b.RESTPollingDelay = 10
|
|
b.WebsocketSubdChannels = make(map[int]WebsocketChanInfo)
|
|
b.APIWithdrawPermissions = exchange.AutoWithdrawCryptoWithAPIPermission | exchange.AutoWithdrawFiatWithAPIPermission
|
|
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.Requester = request.New(b.Name,
|
|
request.NewRateLimit(time.Second*60, bitfinexAuthRate),
|
|
request.NewRateLimit(time.Second*60, bitfinexUnauthRate),
|
|
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
|
|
b.APIUrlDefault = bitfinexAPIURLBase
|
|
b.APIUrl = b.APIUrlDefault
|
|
b.WebsocketInit()
|
|
}
|
|
|
|
// Setup takes in the supplied exchange configuration details and sets params
|
|
func (b *Bitfinex) 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.Websocket.SetEnabled(exch.Websocket)
|
|
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,
|
|
bitfinexWebsocket,
|
|
exch.WebsocketURL)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// GetPlatformStatus returns the Bifinex platform status
|
|
func (b *Bitfinex) GetPlatformStatus() (int, error) {
|
|
var response []interface{}
|
|
path := fmt.Sprintf("%s/v%s/%s", b.APIUrl, bitfinexAPIVersion2,
|
|
bitfinexPlatformStatus)
|
|
|
|
err := b.SendHTTPRequest(path, &response, b.Verbose)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
if (len(response)) != 1 {
|
|
return 0, errors.New("unexpected platform status value")
|
|
}
|
|
|
|
return int(response[0].(float64)), nil
|
|
}
|
|
|
|
// GetLatestSpotPrice returns latest spot price of symbol
|
|
//
|
|
// symbol: string of currency pair
|
|
func (b *Bitfinex) GetLatestSpotPrice(symbol string) (float64, error) {
|
|
res, err := b.GetTicker(symbol)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return res.Mid, nil
|
|
}
|
|
|
|
// GetTicker returns ticker information
|
|
func (b *Bitfinex) GetTicker(symbol string) (Ticker, error) {
|
|
response := Ticker{}
|
|
path := common.EncodeURLValues(b.APIUrl+bitfinexAPIVersion+bitfinexTicker+symbol, url.Values{})
|
|
|
|
if err := b.SendHTTPRequest(path, &response, b.Verbose); err != nil {
|
|
return response, err
|
|
}
|
|
|
|
if response.Message != "" {
|
|
return response, errors.New(response.Message)
|
|
}
|
|
|
|
return response, nil
|
|
}
|
|
|
|
// GetTickerV2 returns ticker information
|
|
func (b *Bitfinex) GetTickerV2(symbol string) (Tickerv2, error) {
|
|
var response []interface{}
|
|
var ticker Tickerv2
|
|
|
|
path := fmt.Sprintf("%s/v%s/%s/%s", b.APIUrl, bitfinexAPIVersion2, bitfinexTickerV2, symbol)
|
|
err := b.SendHTTPRequest(path, &response, b.Verbose)
|
|
if err != nil {
|
|
return ticker, err
|
|
}
|
|
|
|
if len(response) > 10 {
|
|
ticker.FlashReturnRate = response[0].(float64)
|
|
ticker.Bid = response[1].(float64)
|
|
ticker.BidSize = response[2].(float64)
|
|
ticker.BidPeriod = int64(response[3].(float64))
|
|
ticker.Ask = response[4].(float64)
|
|
ticker.AskSize = response[5].(float64)
|
|
ticker.AskPeriod = int64(response[6].(float64))
|
|
ticker.DailyChange = response[7].(float64)
|
|
ticker.DailyChangePerc = response[8].(float64)
|
|
ticker.Last = response[9].(float64)
|
|
ticker.Volume = response[10].(float64)
|
|
ticker.High = response[11].(float64)
|
|
ticker.Low = response[12].(float64)
|
|
} else {
|
|
ticker.Bid = response[0].(float64)
|
|
ticker.BidSize = response[1].(float64)
|
|
ticker.Ask = response[2].(float64)
|
|
ticker.AskSize = response[3].(float64)
|
|
ticker.DailyChange = response[4].(float64)
|
|
ticker.DailyChangePerc = response[5].(float64)
|
|
ticker.Last = response[6].(float64)
|
|
ticker.Volume = response[7].(float64)
|
|
ticker.High = response[8].(float64)
|
|
ticker.Low = response[9].(float64)
|
|
}
|
|
return ticker, nil
|
|
}
|
|
|
|
// GetTickersV2 returns ticker information for multiple symbols
|
|
func (b *Bitfinex) GetTickersV2(symbols string) ([]Tickersv2, error) {
|
|
var response [][]interface{}
|
|
var tickers []Tickersv2
|
|
|
|
v := url.Values{}
|
|
v.Set("symbols", symbols)
|
|
|
|
path := common.EncodeURLValues(fmt.Sprintf("%s/v%s/%s",
|
|
b.APIUrl,
|
|
bitfinexAPIVersion2,
|
|
bitfinexTickersV2), v)
|
|
|
|
err := b.SendHTTPRequest(path, &response, b.Verbose)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for x := range response {
|
|
var tick Tickersv2
|
|
data := response[x]
|
|
if len(data) > 11 {
|
|
tick.Symbol = data[0].(string)
|
|
tick.FlashReturnRate = data[1].(float64)
|
|
tick.Bid = data[2].(float64)
|
|
tick.BidSize = data[3].(float64)
|
|
tick.BidPeriod = int64(data[4].(float64))
|
|
tick.Ask = data[5].(float64)
|
|
tick.AskSize = data[6].(float64)
|
|
tick.AskPeriod = int64(data[7].(float64))
|
|
tick.DailyChange = data[8].(float64)
|
|
tick.DailyChangePerc = data[9].(float64)
|
|
tick.Last = data[10].(float64)
|
|
tick.Volume = data[11].(float64)
|
|
tick.High = data[12].(float64)
|
|
tick.Low = data[13].(float64)
|
|
} else {
|
|
tick.Symbol = data[0].(string)
|
|
tick.Bid = data[1].(float64)
|
|
tick.BidSize = data[2].(float64)
|
|
tick.Ask = data[3].(float64)
|
|
tick.AskSize = data[4].(float64)
|
|
tick.DailyChange = data[5].(float64)
|
|
tick.DailyChangePerc = data[6].(float64)
|
|
tick.Last = data[7].(float64)
|
|
tick.Volume = data[8].(float64)
|
|
tick.High = data[9].(float64)
|
|
tick.Low = data[10].(float64)
|
|
}
|
|
tickers = append(tickers, tick)
|
|
}
|
|
return tickers, nil
|
|
}
|
|
|
|
// GetStats returns various statistics about the requested pair
|
|
func (b *Bitfinex) GetStats(symbol string) ([]Stat, error) {
|
|
response := []Stat{}
|
|
path := fmt.Sprint(b.APIUrl + bitfinexAPIVersion + bitfinexStats + symbol)
|
|
|
|
return response, b.SendHTTPRequest(path, &response, b.Verbose)
|
|
}
|
|
|
|
// GetFundingBook the entire margin funding book for both bids and asks sides
|
|
// per currency string
|
|
// symbol - example "USD"
|
|
func (b *Bitfinex) GetFundingBook(symbol string) (FundingBook, error) {
|
|
response := FundingBook{}
|
|
path := fmt.Sprint(b.APIUrl + bitfinexAPIVersion + bitfinexLendbook + symbol)
|
|
|
|
if err := b.SendHTTPRequest(path, &response, b.Verbose); err != nil {
|
|
return response, err
|
|
}
|
|
|
|
if response.Message != "" {
|
|
return response, errors.New(response.Message)
|
|
}
|
|
|
|
return response, nil
|
|
}
|
|
|
|
// GetOrderbook retieves the orderbook bid and ask price points for a currency
|
|
// pair - By default the response will return 25 bid and 25 ask price points.
|
|
// CurrencyPair - Example "BTCUSD"
|
|
// Values can contain limit amounts for both the asks and bids - Example
|
|
// "limit_bids" = 1000
|
|
func (b *Bitfinex) GetOrderbook(currencyPair string, values url.Values) (Orderbook, error) {
|
|
response := Orderbook{}
|
|
path := common.EncodeURLValues(
|
|
b.APIUrl+bitfinexAPIVersion+bitfinexOrderbook+currencyPair,
|
|
values,
|
|
)
|
|
return response, b.SendHTTPRequest(path, &response, b.Verbose)
|
|
}
|
|
|
|
// GetOrderbookV2 retieves the orderbook bid and ask price points for a currency
|
|
// pair - By default the response will return 25 bid and 25 ask price points.
|
|
// symbol - Example "tBTCUSD"
|
|
// precision - P0,P1,P2,P3,R0
|
|
// Values can contain limit amounts for both the asks and bids - Example
|
|
// "len" = 1000
|
|
func (b *Bitfinex) GetOrderbookV2(symbol, precision string, values url.Values) (OrderbookV2, error) {
|
|
var response [][]interface{}
|
|
var book OrderbookV2
|
|
path := common.EncodeURLValues(fmt.Sprintf("%s/v%s/%s/%s/%s", b.APIUrl,
|
|
bitfinexAPIVersion2, bitfinexOrderbookV2, symbol, precision), values)
|
|
err := b.SendHTTPRequest(path, &response, b.Verbose)
|
|
if err != nil {
|
|
return book, err
|
|
}
|
|
|
|
for x := range response {
|
|
data := response[x]
|
|
bookItem := BookV2{}
|
|
|
|
if len(data) > 3 {
|
|
bookItem.Rate = data[0].(float64)
|
|
bookItem.Price = data[1].(float64)
|
|
bookItem.Count = int64(data[2].(float64))
|
|
bookItem.Amount = data[3].(float64)
|
|
} else {
|
|
bookItem.Price = data[0].(float64)
|
|
bookItem.Count = int64(data[1].(float64))
|
|
bookItem.Amount = data[2].(float64)
|
|
}
|
|
|
|
if symbol[0] == 't' {
|
|
if bookItem.Amount > 0 {
|
|
book.Bids = append(book.Bids, bookItem)
|
|
} else {
|
|
book.Asks = append(book.Asks, bookItem)
|
|
}
|
|
} else {
|
|
if bookItem.Amount > 0 {
|
|
book.Asks = append(book.Asks, bookItem)
|
|
} else {
|
|
book.Bids = append(book.Bids, bookItem)
|
|
}
|
|
}
|
|
}
|
|
return book, nil
|
|
}
|
|
|
|
// GetTrades returns a list of the most recent trades for the given curencyPair
|
|
// By default the response will return 100 trades
|
|
// CurrencyPair - Example "BTCUSD"
|
|
// Values can contain limit amounts for the number of trades returned - Example
|
|
// "limit_trades" = 1000
|
|
func (b *Bitfinex) GetTrades(currencyPair string, values url.Values) ([]TradeStructure, error) {
|
|
response := []TradeStructure{}
|
|
path := common.EncodeURLValues(
|
|
b.APIUrl+bitfinexAPIVersion+bitfinexTrades+currencyPair,
|
|
values,
|
|
)
|
|
return response, b.SendHTTPRequest(path, &response, b.Verbose)
|
|
}
|
|
|
|
// GetTradesV2 uses the V2 API to get historic trades that occurred on the
|
|
// exchange
|
|
//
|
|
// currencyPair e.g. "tBTCUSD" v2 prefixes currency pairs with t. (?)
|
|
// timestampStart is an int64 unix epoch time
|
|
// timestampEnd is an int64 unix epoch time, make sure this is always there or
|
|
// you will get the most recent trades.
|
|
// reOrderResp reorders the returned data.
|
|
func (b *Bitfinex) GetTradesV2(currencyPair string, timestampStart, timestampEnd int64, reOrderResp bool) ([]TradeStructureV2, error) {
|
|
var resp [][]interface{}
|
|
var actualHistory []TradeStructureV2
|
|
|
|
path := fmt.Sprintf(bitfinexTradesV2,
|
|
currencyPair,
|
|
strconv.FormatInt(timestampStart, 10),
|
|
strconv.FormatInt(timestampEnd, 10))
|
|
|
|
err := b.SendHTTPRequest(path, &resp, b.Verbose)
|
|
if err != nil {
|
|
return actualHistory, err
|
|
}
|
|
|
|
var tempHistory TradeStructureV2
|
|
for _, data := range resp {
|
|
tempHistory.TID = int64(data[0].(float64))
|
|
tempHistory.Timestamp = int64(data[1].(float64))
|
|
tempHistory.Amount = data[2].(float64)
|
|
tempHistory.Price = data[3].(float64)
|
|
tempHistory.Exchange = b.Name
|
|
tempHistory.Type = "BUY"
|
|
|
|
if tempHistory.Amount < 0 {
|
|
tempHistory.Type = "SELL"
|
|
tempHistory.Amount = tempHistory.Amount * -1
|
|
}
|
|
|
|
actualHistory = append(actualHistory, tempHistory)
|
|
}
|
|
|
|
//re-order index
|
|
if reOrderResp {
|
|
orderedHistory := make([]TradeStructureV2, len(actualHistory))
|
|
for i, quickRange := range actualHistory {
|
|
orderedHistory[len(actualHistory)-i-1] = quickRange
|
|
}
|
|
return orderedHistory, nil
|
|
}
|
|
return actualHistory, nil
|
|
}
|
|
|
|
// GetLendbook returns a list of the most recent funding data for the given
|
|
// currency: total amount provided and Flash Return Rate (in % by 365 days) over
|
|
// time
|
|
// Symbol - example "USD"
|
|
func (b *Bitfinex) GetLendbook(symbol string, values url.Values) (Lendbook, error) {
|
|
response := Lendbook{}
|
|
if len(symbol) == 6 {
|
|
symbol = symbol[:3]
|
|
}
|
|
path := common.EncodeURLValues(b.APIUrl+bitfinexAPIVersion+bitfinexLendbook+symbol, values)
|
|
|
|
return response, b.SendHTTPRequest(path, &response, b.Verbose)
|
|
}
|
|
|
|
// GetLends returns a list of the most recent funding data for the given
|
|
// currency: total amount provided and Flash Return Rate (in % by 365 days)
|
|
// over time
|
|
// Symbol - example "USD"
|
|
func (b *Bitfinex) GetLends(symbol string, values url.Values) ([]Lends, error) {
|
|
response := []Lends{}
|
|
path := common.EncodeURLValues(b.APIUrl+bitfinexAPIVersion+bitfinexLends+symbol, values)
|
|
|
|
return response, b.SendHTTPRequest(path, &response, b.Verbose)
|
|
}
|
|
|
|
// GetSymbols returns the available currency pairs on the exchange
|
|
func (b *Bitfinex) GetSymbols() ([]string, error) {
|
|
products := []string{}
|
|
path := fmt.Sprint(b.APIUrl + bitfinexAPIVersion + bitfinexSymbols)
|
|
|
|
return products, b.SendHTTPRequest(path, &products, b.Verbose)
|
|
}
|
|
|
|
// GetSymbolsDetails a list of valid symbol IDs and the pair details
|
|
func (b *Bitfinex) GetSymbolsDetails() ([]SymbolDetails, error) {
|
|
response := []SymbolDetails{}
|
|
path := fmt.Sprint(b.APIUrl + bitfinexAPIVersion + bitfinexSymbolsDetails)
|
|
|
|
return response, b.SendHTTPRequest(path, &response, b.Verbose)
|
|
}
|
|
|
|
// GetAccountInformation returns information about your account incl. trading fees
|
|
func (b *Bitfinex) GetAccountInformation() ([]AccountInfo, error) {
|
|
|
|
var responses []AccountInfo
|
|
err := b.SendAuthenticatedHTTPRequest("POST", bitfinexAccountInfo, nil, &responses)
|
|
|
|
if err != nil {
|
|
return responses, err
|
|
}
|
|
|
|
return responses, nil
|
|
}
|
|
|
|
// GetAccountFees - Gets all fee rates for all currencies
|
|
func (b *Bitfinex) GetAccountFees() (AccountFees, error) {
|
|
response := AccountFees{}
|
|
|
|
err := b.SendAuthenticatedHTTPRequest("POST", bitfinexAccountFees, nil, &response)
|
|
if err != nil {
|
|
return response, err
|
|
}
|
|
return response, nil
|
|
}
|
|
|
|
// GetAccountSummary returns a 30-day summary of your trading volume and return
|
|
// on margin funding
|
|
func (b *Bitfinex) GetAccountSummary() (AccountSummary, error) {
|
|
response := AccountSummary{}
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest(
|
|
"POST", bitfinexAccountSummary, nil, &response,
|
|
)
|
|
}
|
|
|
|
// NewDeposit returns a new deposit address
|
|
// Method - Example methods accepted: “bitcoin”, “litecoin”, “ethereum”,
|
|
//“tethers", "ethereumc", "zcash", "monero", "iota", "bcash"
|
|
// WalletName - accepted: “trading”, “exchange”, “deposit”
|
|
// renew - Default is 0. If set to 1, will return a new unused deposit address
|
|
func (b *Bitfinex) NewDeposit(method, walletName string, renew int) (DepositResponse, error) {
|
|
response := DepositResponse{}
|
|
request := make(map[string]interface{})
|
|
request["method"] = method
|
|
request["wallet_name"] = walletName
|
|
request["renew"] = renew
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexDeposit, request, &response)
|
|
}
|
|
|
|
// GetKeyPermissions checks the permissions of the key being used to generate
|
|
// this request.
|
|
func (b *Bitfinex) GetKeyPermissions() (KeyPermissions, error) {
|
|
response := KeyPermissions{}
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexKeyPermissions, nil, &response)
|
|
}
|
|
|
|
// GetMarginInfo shows your trading wallet information for margin trading
|
|
func (b *Bitfinex) GetMarginInfo() ([]MarginInfo, error) {
|
|
response := []MarginInfo{}
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexMarginInfo, nil, &response)
|
|
}
|
|
|
|
// GetAccountBalance returns full wallet balance information
|
|
func (b *Bitfinex) GetAccountBalance() ([]Balance, error) {
|
|
response := []Balance{}
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexBalances, nil, &response)
|
|
}
|
|
|
|
// WalletTransfer move available balances between your wallets
|
|
// Amount - Amount to move
|
|
// Currency - example "BTC"
|
|
// WalletFrom - example "exchange"
|
|
// WalletTo - example "deposit"
|
|
func (b *Bitfinex) WalletTransfer(amount float64, currency, walletFrom, walletTo string) ([]WalletTransfer, error) {
|
|
response := []WalletTransfer{}
|
|
request := make(map[string]interface{})
|
|
request["amount"] = amount
|
|
request["currency"] = currency
|
|
request["walletfrom"] = walletFrom
|
|
request["walletTo"] = walletTo
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexTransfer, request, &response)
|
|
}
|
|
|
|
// Withdrawal requests a withdrawal from one of your wallets.
|
|
// Major Upgrade needed on this function to include all query params
|
|
func (b *Bitfinex) Withdrawal(withdrawType, wallet, address string, amount float64) ([]Withdrawal, error) {
|
|
response := []Withdrawal{}
|
|
request := make(map[string]interface{})
|
|
request["withdrawal_type"] = withdrawType
|
|
request["walletselected"] = wallet
|
|
request["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
|
|
request["address"] = address
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexWithdrawal, request, &response)
|
|
}
|
|
|
|
// NewOrder submits a new order and returns a order information
|
|
// Major Upgrade needed on this function to include all query params
|
|
func (b *Bitfinex) NewOrder(currencyPair string, amount float64, price float64, buy bool, Type string, hidden bool) (Order, error) {
|
|
response := Order{}
|
|
request := make(map[string]interface{})
|
|
request["symbol"] = currencyPair
|
|
request["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
|
|
request["price"] = strconv.FormatFloat(price, 'f', -1, 64)
|
|
request["exchange"] = "bitfinex"
|
|
request["type"] = Type
|
|
request["is_hidden"] = hidden
|
|
|
|
if buy {
|
|
request["side"] = "buy"
|
|
} else {
|
|
request["side"] = "sell"
|
|
}
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexOrderNew, request, &response)
|
|
}
|
|
|
|
// NewOrderMulti allows several new orders at once
|
|
func (b *Bitfinex) NewOrderMulti(orders []PlaceOrder) (OrderMultiResponse, error) {
|
|
response := OrderMultiResponse{}
|
|
request := make(map[string]interface{})
|
|
request["orders"] = orders
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexOrderNewMulti, request, &response)
|
|
}
|
|
|
|
// CancelExistingOrder cancels a single order by OrderID
|
|
func (b *Bitfinex) CancelExistingOrder(OrderID int64) (Order, error) {
|
|
response := Order{}
|
|
request := make(map[string]interface{})
|
|
request["order_id"] = OrderID
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexOrderCancel, request, &response)
|
|
}
|
|
|
|
// CancelMultipleOrders cancels multiple orders
|
|
func (b *Bitfinex) CancelMultipleOrders(OrderIDs []int64) (string, error) {
|
|
response := GenericResponse{}
|
|
request := make(map[string]interface{})
|
|
request["order_ids"] = OrderIDs
|
|
|
|
return response.Result,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexOrderCancelMulti, request, nil)
|
|
}
|
|
|
|
// CancelAllExistingOrders cancels all active and open orders
|
|
func (b *Bitfinex) CancelAllExistingOrders() (string, error) {
|
|
response := GenericResponse{}
|
|
|
|
return response.Result,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexOrderCancelAll, nil, nil)
|
|
}
|
|
|
|
// ReplaceOrder replaces an older order with a new order
|
|
func (b *Bitfinex) ReplaceOrder(OrderID int64, Symbol string, Amount float64, Price float64, Buy bool, Type string, Hidden bool) (Order, error) {
|
|
response := Order{}
|
|
request := make(map[string]interface{})
|
|
request["order_id"] = OrderID
|
|
request["symbol"] = Symbol
|
|
request["amount"] = strconv.FormatFloat(Amount, 'f', -1, 64)
|
|
request["price"] = strconv.FormatFloat(Price, 'f', -1, 64)
|
|
request["exchange"] = "bitfinex"
|
|
request["type"] = Type
|
|
request["is_hidden"] = Hidden
|
|
|
|
if Buy {
|
|
request["side"] = "buy"
|
|
} else {
|
|
request["side"] = "sell"
|
|
}
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexOrderCancelReplace, request, &response)
|
|
}
|
|
|
|
// GetOrderStatus returns order status information
|
|
func (b *Bitfinex) GetOrderStatus(OrderID int64) (Order, error) {
|
|
orderStatus := Order{}
|
|
request := make(map[string]interface{})
|
|
request["order_id"] = OrderID
|
|
|
|
return orderStatus,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexOrderStatus, request, &orderStatus)
|
|
}
|
|
|
|
// GetActiveOrders returns all active orders and statuses
|
|
func (b *Bitfinex) GetActiveOrders() ([]Order, error) {
|
|
response := []Order{}
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexOrders, nil, &response)
|
|
}
|
|
|
|
// GetActivePositions returns an array of active positions
|
|
func (b *Bitfinex) GetActivePositions() ([]Position, error) {
|
|
response := []Position{}
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexPositions, nil, &response)
|
|
}
|
|
|
|
// ClaimPosition allows positions to be claimed
|
|
func (b *Bitfinex) ClaimPosition(PositionID int) (Position, error) {
|
|
response := Position{}
|
|
request := make(map[string]interface{})
|
|
request["position_id"] = PositionID
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexClaimPosition, nil, nil)
|
|
}
|
|
|
|
// GetBalanceHistory returns balance history for the account
|
|
func (b *Bitfinex) GetBalanceHistory(symbol string, timeSince, timeUntil time.Time, limit int, wallet string) ([]BalanceHistory, error) {
|
|
response := []BalanceHistory{}
|
|
request := make(map[string]interface{})
|
|
request["currency"] = symbol
|
|
|
|
if !timeSince.IsZero() {
|
|
request["since"] = timeSince
|
|
}
|
|
if !timeUntil.IsZero() {
|
|
request["until"] = timeUntil
|
|
}
|
|
if limit > 0 {
|
|
request["limit"] = limit
|
|
}
|
|
if len(wallet) > 0 {
|
|
request["wallet"] = wallet
|
|
}
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexHistory, request, &response)
|
|
}
|
|
|
|
// GetMovementHistory returns an array of past deposits and withdrawals
|
|
func (b *Bitfinex) GetMovementHistory(symbol, method string, timeSince, timeUntil time.Time, limit int) ([]MovementHistory, error) {
|
|
response := []MovementHistory{}
|
|
request := make(map[string]interface{})
|
|
request["currency"] = symbol
|
|
|
|
if len(method) > 0 {
|
|
request["method"] = method
|
|
}
|
|
if !timeSince.IsZero() {
|
|
request["since"] = timeSince
|
|
}
|
|
if !timeUntil.IsZero() {
|
|
request["until"] = timeUntil
|
|
}
|
|
if limit > 0 {
|
|
request["limit"] = limit
|
|
}
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexHistoryMovements, request, &response)
|
|
}
|
|
|
|
// GetTradeHistory returns past executed trades
|
|
func (b *Bitfinex) GetTradeHistory(currencyPair string, timestamp, until time.Time, limit, reverse int) ([]TradeHistory, error) {
|
|
response := []TradeHistory{}
|
|
request := make(map[string]interface{})
|
|
request["currency"] = currencyPair
|
|
request["timestamp"] = timestamp
|
|
|
|
if !until.IsZero() {
|
|
request["until"] = until
|
|
}
|
|
if limit > 0 {
|
|
request["limit"] = limit
|
|
}
|
|
if reverse > 0 {
|
|
request["reverse"] = reverse
|
|
}
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexTradeHistory, request, &response)
|
|
}
|
|
|
|
// NewOffer submits a new offer
|
|
func (b *Bitfinex) NewOffer(symbol string, amount, rate float64, period int64, direction string) (Offer, error) {
|
|
response := Offer{}
|
|
request := make(map[string]interface{})
|
|
request["currency"] = symbol
|
|
request["amount"] = amount
|
|
request["rate"] = rate
|
|
request["period"] = period
|
|
request["direction"] = direction
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexOfferNew, request, &response)
|
|
}
|
|
|
|
// CancelOffer cancels offer by offerID
|
|
func (b *Bitfinex) CancelOffer(OfferID int64) (Offer, error) {
|
|
response := Offer{}
|
|
request := make(map[string]interface{})
|
|
request["offer_id"] = OfferID
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexOfferCancel, request, &response)
|
|
}
|
|
|
|
// GetOfferStatus checks offer status whether it has been cancelled, execute or
|
|
// is still active
|
|
func (b *Bitfinex) GetOfferStatus(OfferID int64) (Offer, error) {
|
|
response := Offer{}
|
|
request := make(map[string]interface{})
|
|
request["offer_id"] = OfferID
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexOrderStatus, request, &response)
|
|
}
|
|
|
|
// GetActiveCredits returns all available credits
|
|
func (b *Bitfinex) GetActiveCredits() ([]Offer, error) {
|
|
response := []Offer{}
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexActiveCredits, nil, &response)
|
|
}
|
|
|
|
// GetActiveOffers returns all current active offers
|
|
func (b *Bitfinex) GetActiveOffers() ([]Offer, error) {
|
|
response := []Offer{}
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexOffers, nil, &response)
|
|
}
|
|
|
|
// GetActiveMarginFunding returns an array of active margin funds
|
|
func (b *Bitfinex) GetActiveMarginFunding() ([]MarginFunds, error) {
|
|
response := []MarginFunds{}
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexMarginActiveFunds, nil, &response)
|
|
}
|
|
|
|
// GetUnusedMarginFunds returns an array of funding borrowed but not currently
|
|
// used
|
|
func (b *Bitfinex) GetUnusedMarginFunds() ([]MarginFunds, error) {
|
|
response := []MarginFunds{}
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexMarginUnusedFunds, nil, &response)
|
|
}
|
|
|
|
// GetMarginTotalTakenFunds returns an array of active funding used in a
|
|
// position
|
|
func (b *Bitfinex) GetMarginTotalTakenFunds() ([]MarginTotalTakenFunds, error) {
|
|
response := []MarginTotalTakenFunds{}
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexMarginTotalFunds, nil, &response)
|
|
}
|
|
|
|
// CloseMarginFunding closes an unused or used taken fund
|
|
func (b *Bitfinex) CloseMarginFunding(SwapID int64) (Offer, error) {
|
|
response := Offer{}
|
|
request := make(map[string]interface{})
|
|
request["swap_id"] = SwapID
|
|
|
|
return response,
|
|
b.SendAuthenticatedHTTPRequest("POST", bitfinexMarginClose, request, &response)
|
|
}
|
|
|
|
// SendHTTPRequest sends an unauthenticated request
|
|
func (b *Bitfinex) SendHTTPRequest(path string, result interface{}, verbose bool) error {
|
|
return b.SendPayload("GET", path, nil, nil, result, false, verbose)
|
|
}
|
|
|
|
// SendAuthenticatedHTTPRequest sends an autheticated http request and json
|
|
// unmarshals result to a supplied variable
|
|
func (b *Bitfinex) SendAuthenticatedHTTPRequest(method, path string, params map[string]interface{}, result interface{}) error {
|
|
if !b.AuthenticatedAPISupport {
|
|
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name)
|
|
}
|
|
|
|
if b.Nonce.Get() == 0 {
|
|
b.Nonce.Set(time.Now().UnixNano())
|
|
} else {
|
|
b.Nonce.Inc()
|
|
}
|
|
|
|
request := make(map[string]interface{})
|
|
request["request"] = fmt.Sprintf("%s%s", bitfinexAPIVersion, path)
|
|
request["nonce"] = b.Nonce.String()
|
|
|
|
if params != nil {
|
|
for key, value := range params {
|
|
request[key] = value
|
|
}
|
|
}
|
|
|
|
PayloadJSON, err := common.JSONEncode(request)
|
|
if err != nil {
|
|
return errors.New("SendAuthenticatedHTTPRequest: Unable to JSON request")
|
|
}
|
|
|
|
if b.Verbose {
|
|
log.Printf("Request JSON: %s\n", PayloadJSON)
|
|
}
|
|
|
|
PayloadBase64 := common.Base64Encode(PayloadJSON)
|
|
hmac := common.GetHMAC(common.HashSHA512_384, []byte(PayloadBase64), []byte(b.APISecret))
|
|
headers := make(map[string]string)
|
|
headers["X-BFX-APIKEY"] = b.APIKey
|
|
headers["X-BFX-PAYLOAD"] = PayloadBase64
|
|
headers["X-BFX-SIGNATURE"] = common.HexEncodeToString(hmac)
|
|
|
|
err = b.SendPayload(method, b.APIUrl+bitfinexAPIVersion+path, headers, nil, result, true, b.Verbose)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetFee returns an estimate of fee based on type of transaction
|
|
func (b *Bitfinex) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) {
|
|
var fee float64
|
|
|
|
switch feeBuilder.FeeType {
|
|
case exchange.CryptocurrencyTradeFee:
|
|
accountInfos, err := b.GetAccountInformation()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
fee, err = b.CalculateTradingFee(accountInfos, feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.FirstCurrency, feeBuilder.IsMaker)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
case exchange.CyptocurrencyDepositFee:
|
|
//TODO: fee is charged when < $1000USD is transferred, need to infer value in some way
|
|
fee = 0
|
|
case exchange.CryptocurrencyWithdrawalFee:
|
|
accountFees, err := b.GetAccountFees()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
fee, err = b.GetCryptocurrencyWithdrawalFee(feeBuilder.FirstCurrency, accountFees)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
case exchange.InternationalBankDepositFee:
|
|
fee = getInternationalBankDepositFee(feeBuilder.Amount)
|
|
case exchange.InternationalBankWithdrawalFee:
|
|
fee = getInternationalBankWithdrawalFee(feeBuilder.Amount)
|
|
}
|
|
if fee < 0 {
|
|
fee = 0
|
|
}
|
|
return fee, nil
|
|
}
|
|
|
|
// GetCryptocurrencyWithdrawalFee returns an estimate of fee based on type of transaction
|
|
func (b *Bitfinex) GetCryptocurrencyWithdrawalFee(currency string, accountFees AccountFees) (fee float64, err error) {
|
|
switch accountFees.Withdraw[currency].(type) {
|
|
case string:
|
|
fee, err = strconv.ParseFloat(accountFees.Withdraw[currency].(string), 64)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
case float64:
|
|
fee = accountFees.Withdraw[currency].(float64)
|
|
}
|
|
|
|
return fee, nil
|
|
}
|
|
|
|
func getInternationalBankDepositFee(amount float64) float64 {
|
|
return 0.001 * amount
|
|
}
|
|
|
|
func getInternationalBankWithdrawalFee(amount float64) float64 {
|
|
return 0.001 * amount
|
|
}
|
|
|
|
// CalculateTradingFee returns an estimate of fee based on type of whether is maker or taker fee
|
|
func (b *Bitfinex) CalculateTradingFee(accountInfos []AccountInfo, purchasePrice, amount float64, currency string, isMaker bool) (fee float64, err error) {
|
|
for _, i := range accountInfos {
|
|
for _, j := range i.Fees {
|
|
if currency == j.Pairs {
|
|
if isMaker {
|
|
fee = j.MakerFees
|
|
} else {
|
|
fee = j.TakerFees
|
|
}
|
|
break
|
|
}
|
|
}
|
|
if fee > 0 {
|
|
break
|
|
}
|
|
}
|
|
return (fee / 100) * purchasePrice * amount, err
|
|
}
|