mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-05 23:16:53 +00:00
Bittrex: Update API v1.1 to v3 and add websocket support (#646)
* Update Bittrex API from v1.1 to v3 V1.1 has been retired as of 9/30/2020 - Update REST to V3 - Add initial websocket support * Bittrex update - enable websockets in testdata config file * Update Bittrex - add Websocket capability to docs * Update Bittrex connector - AppVeyor warnings - Update tests - Generate documentation - Fix nits * Update Bittrex - add websocket order processing * Update Bittrex connector * Bittrex connector - fix ineffectual err assignment * Fix nits * Orderbook synchronization * Remove redundant nil * Log WS fetch orderbook message as debug message instead of as warning * Update after rebase * Add tests * Add allowed candle interval values * Replace literals with declared constants * Replace variable name 'request' with 'req' * Add check and update for deprecated REST URL * Nits and some cleaning up * Change ParseInt bit size to 64 * [FIX] Remove several shadow declarations * Do not export constructTicker * Remove parseTime() * Update GetHistoricCandles() * [FIX] Address gocritic nits * [FIX] Address gocritic nits * Use SendMessageReturnResponse() instead of local map * Rate limit subscribing and unsubscribing * [FIX] use go routine for subscribing and unsubscribing * [FIX] Set correct index for map * [FIX] Address unused vars, literals, time format * Adjusted timing when subscribing to many order books * Cache partial updates to tickers instead of calling REST function * [FIX] Update sequence nr when multiple updates are queued * Address golint issues * Fix nits
This commit is contained in:
@@ -1,425 +1,366 @@
|
||||
package bittrex
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"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/request"
|
||||
)
|
||||
|
||||
const (
|
||||
spotURL = "spotAPIURL"
|
||||
spotWSURL = "spotWSURL"
|
||||
bittrexAPIURL = "https://bittrex.com/api/v1.1"
|
||||
bittrexAPIVersion = "v1.1"
|
||||
bittrexMaxOpenOrders = 500
|
||||
bittrexMaxOrderCountPerDay = 200000
|
||||
|
||||
// Returned messages from Bittrex API
|
||||
bittrexAddressGenerating = "ADDRESS_GENERATING"
|
||||
bittrexErrorMarketNotProvided = "MARKET_NOT_PROVIDED"
|
||||
bittrexErrorInvalidMarket = "INVALID_MARKET"
|
||||
bittrexErrorAPIKeyInvalid = "APIKEY_INVALID"
|
||||
bittrexErrorInvalidPermission = "INVALID_PERMISSION"
|
||||
|
||||
// Public requests
|
||||
bittrexAPIGetMarkets = "public/getmarkets"
|
||||
bittrexAPIGetCurrencies = "public/getcurrencies"
|
||||
bittrexAPIGetTicker = "public/getticker"
|
||||
bittrexAPIGetMarketSummaries = "public/getmarketsummaries"
|
||||
bittrexAPIGetMarketSummary = "public/getmarketsummary"
|
||||
bittrexAPIGetOrderbook = "public/getorderbook"
|
||||
bittrexAPIGetMarketHistory = "public/getmarkethistory"
|
||||
|
||||
// Market requests
|
||||
bittrexAPIBuyLimit = "market/buylimit"
|
||||
bittrexAPISellLimit = "market/selllimit"
|
||||
bittrexAPICancel = "market/cancel"
|
||||
bittrexAPIGetOpenOrders = "market/getopenorders"
|
||||
|
||||
// Account requests
|
||||
bittrexAPIGetBalances = "account/getbalances"
|
||||
bittrexAPIGetBalance = "account/getbalance"
|
||||
bittrexAPIGetDepositAddress = "account/getdepositaddress"
|
||||
bittrexAPIWithdraw = "account/withdraw"
|
||||
bittrexAPIGetOrder = "account/getorder"
|
||||
bittrexAPIGetOrderHistory = "account/getorderhistory"
|
||||
bittrexAPIGetWithdrawalHistory = "account/getwithdrawalhistory"
|
||||
bittrexAPIGetDepositHistory = "account/getdeposithistory"
|
||||
|
||||
bittrexRateInterval = time.Minute
|
||||
bittrexRequestRate = 60
|
||||
bittrexTimeLayout = "2006-01-02T15:04:05"
|
||||
)
|
||||
|
||||
// Bittrex is the overaching type across the bittrex methods
|
||||
type Bittrex struct {
|
||||
exchange.Base
|
||||
WsSequenceOrders int64
|
||||
|
||||
obm *orderbookManager
|
||||
tickerCache *TickerCache
|
||||
}
|
||||
|
||||
const (
|
||||
bittrexAPIRestURL = "https://api.bittrex.com/v3"
|
||||
bittrexAPIDeprecatedURL = "https://bittrex.com/api/v1.1"
|
||||
|
||||
// Public endpoints
|
||||
getMarkets = "/markets"
|
||||
getMarketSummaries = "/markets/summaries"
|
||||
getTicker = "/markets/%s/ticker"
|
||||
getMarketSummary = "/markets/%s/summary"
|
||||
getMarketTrades = "/markets/%s/trades"
|
||||
getOrderbook = "/markets/%s/orderbook?depth=%s"
|
||||
getRecentCandles = "/markets/%s/candles/%s/%s/recent"
|
||||
getHistoricalCandles = "/markets/%s/candles/%s/%s/historical/%s"
|
||||
getCurrencies = "/currencies"
|
||||
|
||||
// Authenticated endpoints
|
||||
getBalances = "/balances"
|
||||
getBalance = "/balances/%s"
|
||||
getDepositAddress = "/addresses/%s"
|
||||
getAllOpenOrders = "/orders/open"
|
||||
getOpenOrders = "/orders/open?marketSymbol=%s"
|
||||
getOrder = "/orders/%s"
|
||||
getClosedOrders = "/orders/closed?marketSymbol=%s"
|
||||
cancelOrder = "/orders/%s"
|
||||
cancelOpenOrders = "/orders/open"
|
||||
getClosedWithdrawals = "/withdrawals/closed"
|
||||
getOpenWithdrawals = "/withdrawals/open"
|
||||
submitWithdrawal = "/transfers"
|
||||
getClosedDeposits = "/deposits/closed"
|
||||
getOpenDeposits = "/deposits/open"
|
||||
submitOrder = "/orders"
|
||||
|
||||
// Other Consts
|
||||
ratePeriod = time.Minute
|
||||
rateLimit = 60
|
||||
orderbookDepth = 500 // ws uses REST snapshots and needs identical depths
|
||||
)
|
||||
|
||||
// GetMarkets is used to get the open and available trading markets at Bittrex
|
||||
// along with other meta data.
|
||||
func (b *Bittrex) GetMarkets() (Market, error) {
|
||||
var markets Market
|
||||
|
||||
if err := b.SendHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetMarkets+"/", &markets); err != nil {
|
||||
return markets, err
|
||||
}
|
||||
|
||||
if !markets.Success {
|
||||
return markets, errors.New(markets.Message)
|
||||
}
|
||||
return markets, nil
|
||||
func (b *Bittrex) GetMarkets() ([]MarketData, error) {
|
||||
var resp []MarketData
|
||||
return resp, b.SendHTTPRequest(exchange.RestSpot, getMarkets, &resp, nil)
|
||||
}
|
||||
|
||||
// GetCurrencies is used to get all supported currencies at Bittrex
|
||||
func (b *Bittrex) GetCurrencies() (Currency, error) {
|
||||
var currencies Currency
|
||||
|
||||
if err := b.SendHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetCurrencies+"/", ¤cies); err != nil {
|
||||
return currencies, err
|
||||
}
|
||||
|
||||
if !currencies.Success {
|
||||
return currencies, errors.New(currencies.Message)
|
||||
}
|
||||
return currencies, nil
|
||||
func (b *Bittrex) GetCurrencies() ([]CurrencyData, error) {
|
||||
var resp []CurrencyData
|
||||
return resp, b.SendHTTPRequest(exchange.RestSpot, getCurrencies, &resp, nil)
|
||||
}
|
||||
|
||||
// GetTicker sends a public get request and returns current ticker information
|
||||
// on the supplied currency. Example currency input param "btc-ltc".
|
||||
func (b *Bittrex) GetTicker(currencyPair string) (Ticker, error) {
|
||||
tick := Ticker{}
|
||||
path := "/" + bittrexAPIGetTicker + "?market=" + strings.ToUpper(currencyPair)
|
||||
|
||||
if err := b.SendHTTPRequest(exchange.RestSpot, path, &tick); err != nil {
|
||||
return tick, err
|
||||
}
|
||||
|
||||
if !tick.Success {
|
||||
return tick, errors.New(tick.Message)
|
||||
}
|
||||
return tick, nil
|
||||
// on the supplied currency. Example currency input param "ltc-btc".
|
||||
func (b *Bittrex) GetTicker(marketName string) (TickerData, error) {
|
||||
var resp TickerData
|
||||
return resp, b.SendHTTPRequest(exchange.RestSpot, fmt.Sprintf(getTicker, marketName), &resp, nil)
|
||||
}
|
||||
|
||||
// GetMarketSummaries is used to get the last 24 hour summary of all active
|
||||
// exchanges
|
||||
func (b *Bittrex) GetMarketSummaries() (MarketSummary, error) {
|
||||
var summaries MarketSummary
|
||||
|
||||
if err := b.SendHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetMarketSummaries+"/", &summaries); err != nil {
|
||||
return summaries, err
|
||||
}
|
||||
|
||||
if !summaries.Success {
|
||||
return summaries, errors.New(summaries.Message)
|
||||
}
|
||||
return summaries, nil
|
||||
func (b *Bittrex) GetMarketSummaries() ([]MarketSummaryData, error) {
|
||||
var resp []MarketSummaryData
|
||||
return resp, b.SendHTTPRequest(exchange.RestSpot, getMarketSummaries, &resp, nil)
|
||||
}
|
||||
|
||||
// GetMarketSummary is used to get the last 24 hour summary of all active
|
||||
// exchanges by currency pair (btc-ltc).
|
||||
func (b *Bittrex) GetMarketSummary(currencyPair string) (MarketSummary, error) {
|
||||
var summary MarketSummary
|
||||
if err := b.SendHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetMarketSummary+"?market="+strings.ToLower(currencyPair), &summary); err != nil {
|
||||
return summary, err
|
||||
}
|
||||
|
||||
if !summary.Success {
|
||||
return summary, errors.New(summary.Message)
|
||||
}
|
||||
return summary, nil
|
||||
// exchanges by currency pair (ltc-btc).
|
||||
func (b *Bittrex) GetMarketSummary(marketName string) (MarketSummaryData, error) {
|
||||
var resp MarketSummaryData
|
||||
return resp, b.SendHTTPRequest(exchange.RestSpot, fmt.Sprintf(getMarketSummary, marketName), &resp, nil)
|
||||
}
|
||||
|
||||
// GetOrderbook method returns current order book information by currency, type
|
||||
// & depth.
|
||||
// "Currency Pair" ie btc-ltc
|
||||
// "Category" either "buy", "sell" or "both"; for ease of use and reduced
|
||||
// complexity this function is set to "both"
|
||||
// "Depth" max depth is 50 but you can literally set it any integer you want and
|
||||
// it returns full depth. So depth default is 50.
|
||||
func (b *Bittrex) GetOrderbook(currencyPair string) (OrderBooks, error) {
|
||||
var orderbooks OrderBooks
|
||||
path := "/" + bittrexAPIGetOrderbook + "?market=" + strings.ToLower(currencyPair) + "&type=both&depth=50"
|
||||
if err := b.SendHTTPRequest(exchange.RestSpot, path, &orderbooks); err != nil {
|
||||
return orderbooks, err
|
||||
// GetOrderbook method returns current order book information by currency and depth.
|
||||
// "marketSymbol" ie ltc-btc
|
||||
// "depth" is either 1, 25 or 500. Server side, the depth defaults to 25.
|
||||
func (b *Bittrex) GetOrderbook(marketName string, depth int64) (OrderbookData, int64, error) {
|
||||
strDepth := strconv.FormatInt(depth, 10)
|
||||
|
||||
var resp OrderbookData
|
||||
var sequence int64
|
||||
resultHeader := http.Header{}
|
||||
err := b.SendHTTPRequest(exchange.RestSpot, fmt.Sprintf(getOrderbook, marketName, strDepth), &resp, &resultHeader)
|
||||
if err != nil {
|
||||
return OrderbookData{}, 0, err
|
||||
}
|
||||
sequence, err = strconv.ParseInt(resultHeader.Get("sequence"), 10, 64)
|
||||
if err != nil {
|
||||
return OrderbookData{}, 0, err
|
||||
}
|
||||
|
||||
if !orderbooks.Success {
|
||||
return orderbooks, errors.New(orderbooks.Message)
|
||||
}
|
||||
return orderbooks, nil
|
||||
return resp, sequence, nil
|
||||
}
|
||||
|
||||
// GetMarketHistory retrieves the latest trades that have occurred for a specific
|
||||
// market
|
||||
func (b *Bittrex) GetMarketHistory(currencyPair string) (MarketHistory, error) {
|
||||
var marketHistoriae MarketHistory
|
||||
path := "/" + bittrexAPIGetMarketHistory + "?market=" + strings.ToUpper(currencyPair)
|
||||
if err := b.SendHTTPRequest(exchange.RestSpot, path, &marketHistoriae); err != nil {
|
||||
return marketHistoriae, err
|
||||
}
|
||||
|
||||
if !marketHistoriae.Success {
|
||||
return marketHistoriae, errors.New(marketHistoriae.Message)
|
||||
}
|
||||
return marketHistoriae, nil
|
||||
// GetMarketHistory retrieves the latest trades that have occurred for a specific market
|
||||
func (b *Bittrex) GetMarketHistory(currency string) ([]TradeData, error) {
|
||||
var resp []TradeData
|
||||
return resp, b.SendHTTPRequest(exchange.RestSpot, fmt.Sprintf(getMarketTrades, currency), &resp, nil)
|
||||
}
|
||||
|
||||
// PlaceBuyLimit is used to place a buy order in a specific market. Use buylimit
|
||||
// to place limit orders. Make sure you have the proper permissions set on your
|
||||
// API keys for this call to work.
|
||||
// "Currency" ie "btc-ltc"
|
||||
// "Quantity" is the amount to purchase
|
||||
// "Rate" is the rate at which to purchase
|
||||
func (b *Bittrex) PlaceBuyLimit(currencyPair string, quantity, rate float64) (UUID, error) {
|
||||
var id UUID
|
||||
values := url.Values{}
|
||||
values.Set("market", currencyPair)
|
||||
values.Set("quantity", strconv.FormatFloat(quantity, 'E', -1, 64))
|
||||
values.Set("rate", strconv.FormatFloat(rate, 'E', -1, 64))
|
||||
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPIBuyLimit, values, &id); err != nil {
|
||||
return id, err
|
||||
// Order places an order
|
||||
func (b *Bittrex) Order(marketName, side, orderType string, timeInForce TimeInForce, price, amount, ceiling float64) (OrderData, error) {
|
||||
req := make(map[string]interface{})
|
||||
req["marketSymbol"] = marketName
|
||||
req["direction"] = side
|
||||
req["type"] = orderType
|
||||
req["quantity"] = strconv.FormatFloat(amount, 'f', -1, 64)
|
||||
if orderType == "CEILING_LIMIT" || orderType == "CEILING_MARKET" {
|
||||
req["ceiling"] = strconv.FormatFloat(ceiling, 'f', -1, 64)
|
||||
}
|
||||
|
||||
if !id.Success {
|
||||
return id, errors.New(id.Message)
|
||||
if orderType == "LIMIT" {
|
||||
req["limit"] = strconv.FormatFloat(price, 'f', -1, 64)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// PlaceSellLimit is used to place a sell order in a specific market. Use
|
||||
// selllimit to place limit orders. Make sure you have the proper permissions
|
||||
// set on your API keys for this call to work.
|
||||
// "Currency" ie "btc-ltc"
|
||||
// "Quantity" is the amount to purchase
|
||||
// "Rate" is the rate at which to purchase
|
||||
func (b *Bittrex) PlaceSellLimit(currencyPair string, quantity, rate float64) (UUID, error) {
|
||||
var id UUID
|
||||
values := url.Values{}
|
||||
values.Set("market", currencyPair)
|
||||
values.Set("quantity", strconv.FormatFloat(quantity, 'E', -1, 64))
|
||||
values.Set("rate", strconv.FormatFloat(rate, 'E', -1, 64))
|
||||
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPISellLimit, values, &id); err != nil {
|
||||
return id, err
|
||||
if timeInForce != "" {
|
||||
req["timeInForce"] = timeInForce
|
||||
} else {
|
||||
req["timeInForce"] = GoodTilCancelled
|
||||
}
|
||||
|
||||
if !id.Success {
|
||||
return id, errors.New(id.Message)
|
||||
}
|
||||
return id, nil
|
||||
var resp OrderData
|
||||
return resp, b.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost, submitOrder, nil, req, &resp, nil)
|
||||
}
|
||||
|
||||
// GetOpenOrders returns all orders that you currently have opened.
|
||||
// A specific market can be requested for example "btc-ltc"
|
||||
func (b *Bittrex) GetOpenOrders(currencyPair string) (Order, error) {
|
||||
var orders Order
|
||||
values := url.Values{}
|
||||
if !(currencyPair == "" || currencyPair == " ") {
|
||||
values.Set("market", currencyPair)
|
||||
// A specific market can be requested for example "ltc-btc"
|
||||
func (b *Bittrex) GetOpenOrders(marketName string) ([]OrderData, int64, error) {
|
||||
var path string
|
||||
if marketName == "" || marketName == " " {
|
||||
path = getAllOpenOrders
|
||||
} else {
|
||||
path = fmt.Sprintf(getOpenOrders, marketName)
|
||||
}
|
||||
|
||||
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetOpenOrders, values, &orders); err != nil {
|
||||
return orders, err
|
||||
var resp []OrderData
|
||||
var sequence int64
|
||||
resultHeader := http.Header{}
|
||||
err := b.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, path, nil, nil, &resp, &resultHeader)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if !orders.Success {
|
||||
return orders, errors.New(orders.Message)
|
||||
sequence, err = strconv.ParseInt(resultHeader.Get("sequence"), 10, 64)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return orders, nil
|
||||
return resp, sequence, err
|
||||
}
|
||||
|
||||
// CancelExistingOrder is used to cancel a buy or sell order.
|
||||
func (b *Bittrex) CancelExistingOrder(uuid string) (Balances, error) {
|
||||
var balances Balances
|
||||
values := url.Values{}
|
||||
values.Set("uuid", uuid)
|
||||
|
||||
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPICancel, values, &balances); err != nil {
|
||||
return balances, err
|
||||
}
|
||||
|
||||
if !balances.Success {
|
||||
return balances, errors.New(balances.Message)
|
||||
}
|
||||
return balances, nil
|
||||
func (b *Bittrex) CancelExistingOrder(uuid string) (OrderData, error) {
|
||||
var resp OrderData
|
||||
return resp, b.SendAuthHTTPRequest(exchange.RestSpot, http.MethodDelete, fmt.Sprintf(cancelOrder, uuid), nil, nil, &resp, nil)
|
||||
}
|
||||
|
||||
// GetAccountBalances is used to retrieve all balances from your account
|
||||
func (b *Bittrex) GetAccountBalances() (Balances, error) {
|
||||
var balances Balances
|
||||
// CancelOpenOrders is used to cancel all open orders for a specific market
|
||||
// Or cancel all orders for all markets if the parameter `markets` is set to ""
|
||||
func (b *Bittrex) CancelOpenOrders(market string) ([]BulkCancelResultData, error) {
|
||||
var resp []BulkCancelResultData
|
||||
|
||||
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetBalances, url.Values{}, &balances); err != nil {
|
||||
return balances, err
|
||||
params := url.Values{}
|
||||
if market != "" {
|
||||
params.Set("marketSymbol", market)
|
||||
}
|
||||
|
||||
if !balances.Success {
|
||||
return balances, errors.New(balances.Message)
|
||||
return resp, b.SendAuthHTTPRequest(exchange.RestSpot, http.MethodDelete, cancelOpenOrders, params, nil, &resp, nil)
|
||||
}
|
||||
|
||||
// GetRecentCandles retrieves recent candles;
|
||||
// Interval: MINUTE_1, MINUTE_5, HOUR_1, or DAY_1
|
||||
// Type: TRADE or MIDPOINT
|
||||
func (b *Bittrex) GetRecentCandles(marketName, candleInterval, candleType string) ([]CandleData, error) {
|
||||
var resp []CandleData
|
||||
|
||||
return resp, b.SendHTTPRequest(exchange.RestSpot, fmt.Sprintf(getRecentCandles, marketName, candleType, candleInterval), &resp, nil)
|
||||
}
|
||||
|
||||
// GetHistoricalCandles retrieves recent candles
|
||||
// Type: TRADE or MIDPOINT
|
||||
func (b *Bittrex) GetHistoricalCandles(marketName, candleInterval, candleType string, year, month, day int) ([]CandleData, error) {
|
||||
var resp []CandleData
|
||||
|
||||
var start string
|
||||
switch candleInterval {
|
||||
case "MINUTE_1", "MINUTE_5":
|
||||
// Retrieve full day
|
||||
start = fmt.Sprintf("%d/%d/%d", year, month, day)
|
||||
case "HOUR_1":
|
||||
// Retrieve full month
|
||||
start = fmt.Sprintf("%d/%d", year, month)
|
||||
case "DAY_1":
|
||||
// Retrieve full year
|
||||
start = fmt.Sprintf("%d", year)
|
||||
default:
|
||||
return resp, fmt.Errorf("invalid interval %v, not supported", candleInterval)
|
||||
}
|
||||
return balances, nil
|
||||
|
||||
return resp, b.SendHTTPRequest(exchange.RestSpot, fmt.Sprintf(getHistoricalCandles, marketName, candleType, candleInterval, start), &resp, nil)
|
||||
}
|
||||
|
||||
// GetBalances is used to retrieve all balances from your account
|
||||
func (b *Bittrex) GetBalances() ([]BalanceData, error) {
|
||||
var resp []BalanceData
|
||||
return resp, b.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getBalances, nil, nil, &resp, nil)
|
||||
}
|
||||
|
||||
// GetAccountBalanceByCurrency is used to retrieve the balance from your account
|
||||
// for a specific currency. ie. "btc" or "ltc"
|
||||
func (b *Bittrex) GetAccountBalanceByCurrency(currency string) (Balance, error) {
|
||||
var balance Balance
|
||||
values := url.Values{}
|
||||
values.Set("currency", currency)
|
||||
|
||||
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetBalance, values, &balance); err != nil {
|
||||
return balance, err
|
||||
}
|
||||
|
||||
if !balance.Success {
|
||||
return balance, errors.New(balance.Message)
|
||||
}
|
||||
return balance, nil
|
||||
func (b *Bittrex) GetAccountBalanceByCurrency(currency string) (BalanceData, error) {
|
||||
var resp BalanceData
|
||||
return resp, b.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, fmt.Sprintf(getBalance, currency), nil, nil, &resp, nil)
|
||||
}
|
||||
|
||||
// GetCryptoDepositAddress is used to retrieve or generate an address for a specific
|
||||
// currency. If one does not exist, the call will fail and return
|
||||
// ADDRESS_GENERATING until one is available.
|
||||
func (b *Bittrex) GetCryptoDepositAddress(currency string) (DepositAddress, error) {
|
||||
var address DepositAddress
|
||||
values := url.Values{}
|
||||
values.Set("currency", currency)
|
||||
|
||||
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetDepositAddress, values, &address); err != nil {
|
||||
return address, err
|
||||
}
|
||||
|
||||
if !address.Success {
|
||||
return address, errors.New(address.Message)
|
||||
}
|
||||
return address, nil
|
||||
// GetCryptoDepositAddress is used to retrieve an address for a specific currency
|
||||
func (b *Bittrex) GetCryptoDepositAddress(currency string) (AddressData, error) {
|
||||
var resp AddressData
|
||||
return resp, b.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, fmt.Sprintf(getDepositAddress, currency), nil, nil, &resp, nil)
|
||||
}
|
||||
|
||||
// Withdraw is used to withdraw funds from your account.
|
||||
// note: Please account for transaction fee.
|
||||
func (b *Bittrex) Withdraw(currency, paymentID, address string, quantity float64) (UUID, error) {
|
||||
var id UUID
|
||||
values := url.Values{}
|
||||
values.Set("currency", currency)
|
||||
values.Set("quantity", strconv.FormatFloat(quantity, 'f', -1, 64))
|
||||
values.Set("address", address)
|
||||
func (b *Bittrex) Withdraw(currency, paymentID, address string, quantity float64) (WithdrawalData, error) {
|
||||
req := make(map[string]interface{})
|
||||
req["currencySymbol"] = currency
|
||||
req["quantity"] = strconv.FormatFloat(quantity, 'f', -1, 64)
|
||||
req["cryptoAddress"] = address
|
||||
if len(paymentID) > 0 {
|
||||
values.Set("paymentid", paymentID)
|
||||
req["cryptoAddressTag"] = paymentID
|
||||
}
|
||||
|
||||
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPIWithdraw, values, &id); err != nil {
|
||||
return id, err
|
||||
}
|
||||
|
||||
if !id.Success {
|
||||
return id, errors.New(id.Message)
|
||||
}
|
||||
return id, nil
|
||||
var resp WithdrawalData
|
||||
return resp, b.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost, submitWithdrawal, nil, req, &resp, nil)
|
||||
}
|
||||
|
||||
// GetOrder is used to retrieve a single order by UUID.
|
||||
func (b *Bittrex) GetOrder(uuid string) (Order, error) {
|
||||
var order Order
|
||||
values := url.Values{}
|
||||
values.Set("uuid", uuid)
|
||||
|
||||
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetOrder, values, &order); err != nil {
|
||||
return order, err
|
||||
}
|
||||
|
||||
if !order.Success {
|
||||
return order, errors.New(order.Message)
|
||||
}
|
||||
return order, nil
|
||||
func (b *Bittrex) GetOrder(uuid string) (OrderData, error) {
|
||||
var resp OrderData
|
||||
return resp, b.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, fmt.Sprintf(getOrder, uuid), nil, nil, &resp, nil)
|
||||
}
|
||||
|
||||
// GetOrderHistoryForCurrency is used to retrieve your order history. If currencyPair
|
||||
// omitted it will return the entire order History.
|
||||
func (b *Bittrex) GetOrderHistoryForCurrency(currencyPair string) (Order, error) {
|
||||
var orders Order
|
||||
values := url.Values{}
|
||||
|
||||
if !(currencyPair == "" || currencyPair == " ") {
|
||||
values.Set("market", currencyPair)
|
||||
}
|
||||
|
||||
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetOrderHistory, values, &orders); err != nil {
|
||||
return orders, err
|
||||
}
|
||||
|
||||
if !orders.Success {
|
||||
return orders, errors.New(orders.Message)
|
||||
}
|
||||
return orders, nil
|
||||
// GetOrderHistoryForCurrency is used to retrieve your order history. If marketName
|
||||
// is omitted it will return the entire order History.
|
||||
func (b *Bittrex) GetOrderHistoryForCurrency(currency string) ([]OrderData, error) {
|
||||
var resp []OrderData
|
||||
return resp, b.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, fmt.Sprintf(getClosedOrders, currency), nil, nil, &resp, nil)
|
||||
}
|
||||
|
||||
// GetWithdrawalHistory is used to retrieve your withdrawal history. If currency
|
||||
// GetClosedWithdrawals is used to retrieve your withdrawal history.
|
||||
func (b *Bittrex) GetClosedWithdrawals() ([]WithdrawalData, error) {
|
||||
var resp []WithdrawalData
|
||||
|
||||
return resp, b.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getClosedWithdrawals, nil, nil, &resp, nil)
|
||||
}
|
||||
|
||||
// GetClosedWithdrawalsForCurrency is used to retrieve your withdrawal history for the specified currency.
|
||||
func (b *Bittrex) GetClosedWithdrawalsForCurrency(currency string) ([]WithdrawalData, error) {
|
||||
var resp []WithdrawalData
|
||||
|
||||
params := url.Values{}
|
||||
params.Set("currencySymbol", currency)
|
||||
|
||||
return resp, b.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getClosedWithdrawals, params, nil, &resp, nil)
|
||||
}
|
||||
|
||||
// GetOpenWithdrawals is used to retrieve your withdrawal history. If currency
|
||||
// omitted it will return the entire history
|
||||
func (b *Bittrex) GetWithdrawalHistory(currency string) (WithdrawalHistory, error) {
|
||||
var history WithdrawalHistory
|
||||
values := url.Values{}
|
||||
|
||||
if !(currency == "" || currency == " ") {
|
||||
values.Set("currency", currency)
|
||||
}
|
||||
|
||||
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetWithdrawalHistory, values, &history); err != nil {
|
||||
return history, err
|
||||
}
|
||||
|
||||
if !history.Success {
|
||||
return history, errors.New(history.Message)
|
||||
}
|
||||
return history, nil
|
||||
func (b *Bittrex) GetOpenWithdrawals() ([]WithdrawalData, error) {
|
||||
var resp []WithdrawalData
|
||||
return resp, b.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getOpenWithdrawals, nil, nil, &resp, nil)
|
||||
}
|
||||
|
||||
// GetDepositHistory is used to retrieve your deposit history. If currency is
|
||||
// is omitted it will return the entire deposit history
|
||||
func (b *Bittrex) GetDepositHistory(currency string) (DepositHistory, error) {
|
||||
var history DepositHistory
|
||||
values := url.Values{}
|
||||
// GetClosedDeposits is used to retrieve your deposit history.
|
||||
func (b *Bittrex) GetClosedDeposits() ([]DepositData, error) {
|
||||
var resp []DepositData
|
||||
return resp, b.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getClosedDeposits, nil, nil, &resp, nil)
|
||||
}
|
||||
|
||||
if !(currency == "" || currency == " ") {
|
||||
values.Set("currency", currency)
|
||||
// GetClosedDepositsForCurrency is used to retrieve your deposit history for the specified currency
|
||||
func (b *Bittrex) GetClosedDepositsForCurrency(currency string) ([]DepositData, error) {
|
||||
var resp []DepositData
|
||||
|
||||
params := url.Values{}
|
||||
params.Set("currencySymbol", currency)
|
||||
|
||||
return resp, b.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getClosedDeposits, params, nil, &resp, nil)
|
||||
}
|
||||
|
||||
// GetClosedDepositsPaginated is used to retrieve your deposit history.
|
||||
// The maximum page size is 200 and it defaults to 100.
|
||||
// PreviousPageToken is the unique identifier of the item that the resulting
|
||||
// query result should end before, in the sort order of the given endpoint. Used
|
||||
// for traversing a paginated set in the reverse direction.
|
||||
func (b *Bittrex) GetClosedDepositsPaginated(pageSize int, previousPageTokenOptional ...string) ([]DepositData, error) {
|
||||
var resp []DepositData
|
||||
|
||||
params := url.Values{}
|
||||
params.Set("pageSize", strconv.Itoa(pageSize))
|
||||
|
||||
if len(previousPageTokenOptional) > 0 {
|
||||
params.Set("previousPageToken", previousPageTokenOptional[0])
|
||||
}
|
||||
|
||||
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetDepositHistory, values, &history); err != nil {
|
||||
return history, err
|
||||
}
|
||||
return resp, b.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getClosedDeposits, params, nil, &resp, nil)
|
||||
}
|
||||
|
||||
if !history.Success {
|
||||
return history, errors.New(history.Message)
|
||||
}
|
||||
return history, nil
|
||||
// GetOpenDeposits is used to retrieve your open deposits.
|
||||
func (b *Bittrex) GetOpenDeposits() ([]DepositData, error) {
|
||||
var resp []DepositData
|
||||
return resp, b.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getOpenDeposits, nil, nil, &resp, nil)
|
||||
}
|
||||
|
||||
// GetOpenDepositsForCurrency is used to retrieve your open deposits for the specified currency
|
||||
func (b *Bittrex) GetOpenDepositsForCurrency(currency string) ([]DepositData, error) {
|
||||
var resp []DepositData
|
||||
|
||||
params := url.Values{}
|
||||
params.Set("currencySymbol", currency)
|
||||
|
||||
return resp, b.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getOpenDeposits, params, nil, &resp, nil)
|
||||
}
|
||||
|
||||
// SendHTTPRequest sends an unauthenticated HTTP request
|
||||
func (b *Bittrex) SendHTTPRequest(ep exchange.URL, path string, result interface{}) error {
|
||||
func (b *Bittrex) SendHTTPRequest(ep exchange.URL, path string, result interface{}, resultHeader *http.Header) error {
|
||||
endpoint, err := b.API.Endpoints.GetURL(ep)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.SendPayload(context.Background(), &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: result,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
})
|
||||
requestItem := request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: result,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
HeaderResponse: resultHeader,
|
||||
}
|
||||
return b.SendPayload(context.Background(), &requestItem)
|
||||
}
|
||||
|
||||
// SendAuthenticatedHTTPRequest sends an authenticated http request to a desired
|
||||
// path
|
||||
func (b *Bittrex) SendAuthenticatedHTTPRequest(ep exchange.URL, path string, values url.Values, result interface{}) (err error) {
|
||||
// SendAuthHTTPRequest sends an authenticated request
|
||||
func (b *Bittrex) SendAuthHTTPRequest(ep exchange.URL, method, action string, params url.Values, data, result interface{}, resultHeader *http.Header) error {
|
||||
if !b.AllowAuthenticatedRequest() {
|
||||
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name)
|
||||
}
|
||||
@@ -427,27 +368,46 @@ func (b *Bittrex) SendAuthenticatedHTTPRequest(ep exchange.URL, path string, val
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n := b.Requester.GetNonce(true).String()
|
||||
|
||||
values.Set("apikey", b.API.Credentials.Key)
|
||||
values.Set("nonce", n)
|
||||
rawQuery := endpoint + path + "?" + values.Encode()
|
||||
hmac := crypto.GetHMAC(
|
||||
crypto.HashSHA512, []byte(rawQuery), []byte(b.API.Credentials.Secret),
|
||||
)
|
||||
ts := strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
|
||||
|
||||
path := common.EncodeURLValues(action, params)
|
||||
|
||||
var body io.Reader
|
||||
var hmac, payload []byte
|
||||
var contentHash string
|
||||
if data == nil {
|
||||
payload = []byte("")
|
||||
} else {
|
||||
var err error
|
||||
payload, err = json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
body = bytes.NewBuffer(payload)
|
||||
contentHash = crypto.HexEncodeToString(crypto.GetSHA512(payload))
|
||||
sigPayload := ts + endpoint + path + method + contentHash
|
||||
hmac = crypto.GetHMAC(crypto.HashSHA512, []byte(sigPayload), []byte(b.API.Credentials.Secret))
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["apisign"] = crypto.HexEncodeToString(hmac)
|
||||
|
||||
headers["Api-Key"] = b.API.Credentials.Key
|
||||
headers["Api-Timestamp"] = ts
|
||||
headers["Api-Content-Hash"] = contentHash
|
||||
headers["Api-Signature"] = crypto.HexEncodeToString(hmac)
|
||||
headers["Content-Type"] = "application/json"
|
||||
headers["Accept"] = "application/json"
|
||||
return b.SendPayload(context.Background(), &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: rawQuery,
|
||||
Headers: headers,
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
Method: method,
|
||||
Path: endpoint + path,
|
||||
Headers: headers,
|
||||
Body: body,
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
HeaderResponse: resultHeader,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -478,9 +438,9 @@ func (b *Bittrex) GetWithdrawalFee(c currency.Code) (float64, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, result := range currencies.Result {
|
||||
if result.Currency == c.String() {
|
||||
fee = result.TxFee
|
||||
for i := range currencies {
|
||||
if currencies[i].Symbol == c.String() {
|
||||
fee = currencies[i].TxFee
|
||||
}
|
||||
}
|
||||
return fee, nil
|
||||
@@ -490,7 +450,3 @@ func (b *Bittrex) GetWithdrawalFee(c currency.Code) (float64, error) {
|
||||
func calculateTradingFee(price, amount float64) float64 {
|
||||
return 0.0025 * price * amount
|
||||
}
|
||||
|
||||
func parseTime(t string) (time.Time, error) {
|
||||
return time.Parse(bittrexTimeLayout, t)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user