Files
gocryptotrader/exchanges/bithumb/bithumb.go
Scott 6a15ecc65c OKEX/OKCoin API combine via OKGroup (#252)
* Initial commit

* Successful authenticated request implementation.

* Removes data from okcoin, okex. Implements some account okgroup endpoints. Adds tests

* Finishes account API endpoint implementations.

* Implements and adds tests for the following okgroup v3 API endpoints: GetSpotTradingAccounts, GetSpotTradingAccountForCurrency, GetSpotBillDetailsForCurrency, PlaceSpotOrder, PlaceMultipleSpotOrders, CancelSpotOrder, CancelMultipleSpotOrders, GetSpotOrders, GetSpotOpenOrders, GetSpotOrder, GetSpotTransactionDetails, GetSpotTokenPairDetails, GetSpotOrderBook, GetSpotAllTokenPairsInformation, GetSpotAllTokenPairsInformationForCurrency, GetSpotFilledOrdersInformation, GetSpotMarketData

* Implements and adds tests for all margin api endpoints: GetMarginTradingAccounts, GetMarginTradingAccountsForCurrency, GetMarginBillDetails, GetMarginAccountSettings, GetMarginLoanHistory, OpenMarginLoan, RepayMarginLoan, PlaceMarginOrder, PlaceMultipleMarginOrders, CancelMarginOrder, CancelMultipleMarginOrders, GetMarginOrders, GetMarginOpenOrders, GetMarginOrder, GetMarginTransactionDetails. Simplifies some Spot endpoints combining ForCurrency funcs where possible

* Adds all 29 Futures endpoints with tests. Updates comments and naming conventions. Adds standard realordertest func. Adds ability to make public API requests. Adds test purpose comments

* Adds 29 endpoints for SWAP API support. Adds tests for each endpoint. Declares response variables in function declaration. Simplifies URL parameter formatting

* Adds all ETT endpoints with tests

* Creates OKCoin and OKEX struct types. Moves okgroup tests to okcoin and okex exchanges. Updates withdraw fee calculation. Updates exchange.go exchange declaration to point to new types. Streamlines wrapper tests. Begins websocket integration

* Rebase fixes

* Deletes okcoin_types.go, okcoin_wrapper.go, okex_types.go, okex_wrapper.go. Combines okex,okcoin wrappers in okgroup_wrapper.go. Removes boilerplate url.values with new request struct type parsing. Adds github.com/google/go-querystring to go modules. Replaces USDT with USD for OKCoin tests. Moves OKEX specific endpoints (futures, swap & ett) to okex.go. Fixes recieving receiving again

* Maps the wrapper

* Parses json into time.Time instead of string + conversion

* Renames websocket.SetEnabled to websocket.SetWsStatusAndConnection. Maps main spot websocket functions for okgroup. Adds some basic ws tests

* Updates websocket default URLS, adds checksum tests, removes setdefaults from okgroup, adds WebsocketDataWrapper to wrap all okgroup websocket data responses, handles spot, swap, index and futures ticker, candle, trade, orderbook, funding fee websocket responses. Partially implements checksum validation on orderbook data. Fixes all linting issues

* Handles the calculation of okgroup websocket checksums. Adds tests

* Now all orderbook checksums are validated. Cleans up implementations and removes invalid checksum calculator functions. Adds function to parse ordertype. Puts verbose logs behind verbose check

* Removes parallel from okgroup tests. Removes unused code. Adds GetWsChannelWithoutOrderType. Improves handling of WS data types. Adds types for more ws channels. Simplifies update orderbook handling

* updates btse func name

* linting

* Fixes websocket connection issue with tests. Removes test verbosity

* Updates checksum calculation to handle more than 7 decimal places. Adds rate limiters. Uses != "" instead of len > 0. Adds new test to handle checksum calculation with 8 decimal places.

* Removes logging. Fixes orderbook wrapper references

* Adds slightly more robust resubscribe func. Adds websocket tests that can detect websocket errors

* Fixes linting issues

* Adds new WS func IsConnected() to expose ws status. Tests protect against channel timeout

* Adds test comments. Fixes parallel issues for futures tests

* Fixes error output for wrapper function
2019-03-18 15:18:36 +11:00

684 lines
19 KiB
Go

package bithumb
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"reflect"
"strconv"
"time"
"github.com/thrasher-/gocryptotrader/common"
"github.com/thrasher-/gocryptotrader/config"
"github.com/thrasher-/gocryptotrader/currency/symbol"
exchange "github.com/thrasher-/gocryptotrader/exchanges"
"github.com/thrasher-/gocryptotrader/exchanges/request"
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
log "github.com/thrasher-/gocryptotrader/logger"
)
const (
apiURL = "https://api.bithumb.com"
noError = "0000"
// Public API
requestsPerSecondPublicAPI = 20
publicTicker = "/public/ticker/"
publicOrderBook = "/public/orderbook/"
publicTransactionHistory = "/public/transaction_history/"
// Private API
requestsPerSecondPrivateAPI = 10
privateAccInfo = "/info/account"
privateAccBalance = "/info/balance"
privateWalletAdd = "/info/wallet_address"
privateTicker = "/info/ticker"
privateOrders = "/info/orders"
privateUserTrans = "/info/user_transactions"
privatePlaceTrade = "/trade/place"
privateOrderDetail = "/info/order_detail"
privateCancelTrade = "/trade/cancel"
privateBTCWithdraw = "/trade/btc_withdrawal"
privateKRWDeposit = "/trade/krw_deposit"
privateKRWWithdraw = "/trade/krw_withdrawal"
privateMarketBuy = "/trade/market_buy"
privateMarketSell = "/trade/market_sell"
bithumbAuthRate = 10
bithumbUnauthRate = 20
)
// Bithumb is the overarching type across the Bithumb package
type Bithumb struct {
exchange.Base
}
// SetDefaults sets the basic defaults for Bithumb
func (b *Bithumb) SetDefaults() {
b.Name = "Bithumb"
b.Enabled = false
b.Verbose = false
b.RESTPollingDelay = 10
b.APIWithdrawPermissions = exchange.AutoWithdrawCrypto |
exchange.AutoWithdrawFiat
b.RequestCurrencyPairFormat.Delimiter = ""
b.RequestCurrencyPairFormat.Uppercase = true
b.ConfigCurrencyPairFormat.Delimiter = ""
b.ConfigCurrencyPairFormat.Uppercase = true
b.ConfigCurrencyPairFormat.Index = "KRW"
b.AssetTypes = []string{ticker.Spot}
b.SupportsAutoPairUpdating = true
b.SupportsRESTTickerBatching = true
b.Requester = request.New(b.Name,
request.NewRateLimit(time.Second, bithumbAuthRate),
request.NewRateLimit(time.Second, bithumbUnauthRate),
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 *Bithumb) 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.SetWsStatusAndConnection(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)
}
}
}
// GetTradablePairs returns a list of tradable currencies
func (b *Bithumb) GetTradablePairs() ([]string, error) {
result, err := b.GetAllTickers()
if err != nil {
return nil, err
}
var currencies []string
for x := range result {
currencies = append(currencies, x)
}
return currencies, nil
}
// GetTicker returns ticker information
//
// symbol e.g. "btc"
func (b *Bithumb) GetTicker(symbol string) (Ticker, error) {
response := Ticker{}
path := fmt.Sprintf("%s%s%s", b.APIUrl, publicTicker, common.StringToUpper(symbol))
err := b.SendHTTPRequest(path, &response)
if err != nil {
return response, err
}
if response.Status != noError {
return response, errors.New(response.Message)
}
return response, nil
}
// GetAllTickers returns all ticker information
func (b *Bithumb) GetAllTickers() (map[string]Ticker, error) {
type Response struct {
ActionStatus
Data map[string]interface{}
}
response := Response{}
path := fmt.Sprintf("%s%s%s", b.APIUrl, publicTicker, "all")
err := b.SendHTTPRequest(path, &response)
if err != nil {
return nil, err
}
if response.Status != noError {
return nil, errors.New(response.Message)
}
result := make(map[string]Ticker)
for k, v := range response.Data {
if k == "date" {
continue
}
if reflect.TypeOf(v).String() != "map[string]interface {}" {
continue
}
data := v.(map[string]interface{})
var t Ticker
t.AveragePrice, _ = strconv.ParseFloat(data["average_price"].(string), 64)
t.BuyPrice, _ = strconv.ParseFloat(data["buy_price"].(string), 64)
t.ClosingPrice, _ = strconv.ParseFloat(data["closing_price"].(string), 64)
t.MaxPrice, _ = strconv.ParseFloat(data["max_price"].(string), 64)
t.MinPrice, _ = strconv.ParseFloat(data["min_price"].(string), 64)
t.OpeningPrice, _ = strconv.ParseFloat(data["opening_price"].(string), 64)
t.SellPrice, _ = strconv.ParseFloat(data["sell_price"].(string), 64)
t.UnitsTraded, _ = strconv.ParseFloat(data["units_traded"].(string), 64)
t.Volume1Day, _ = strconv.ParseFloat(data["volume_1day"].(string), 64)
t.Volume7Day, _ = strconv.ParseFloat(data["volume_7day"].(string), 64)
result[k] = t
}
return result, nil
}
// GetOrderBook returns current orderbook
//
// symbol e.g. "btc"
func (b *Bithumb) GetOrderBook(symbol string) (Orderbook, error) {
response := Orderbook{}
path := fmt.Sprintf("%s%s%s", b.APIUrl, publicOrderBook, common.StringToUpper(symbol))
err := b.SendHTTPRequest(path, &response)
if err != nil {
return response, err
}
if response.Status != noError {
return response, errors.New(response.Message)
}
return response, nil
}
// GetTransactionHistory returns recent transactions
//
// symbol e.g. "btc"
func (b *Bithumb) GetTransactionHistory(symbol string) (TransactionHistory, error) {
response := TransactionHistory{}
path := fmt.Sprintf("%s%s%s", b.APIUrl, publicTransactionHistory, common.StringToUpper(symbol))
err := b.SendHTTPRequest(path, &response)
if err != nil {
return response, err
}
if response.Status != noError {
return response, errors.New(response.Message)
}
return response, nil
}
// GetAccountInformation returns account information by singular currency
func (b *Bithumb) GetAccountInformation(currency string) (Account, error) {
response := Account{}
val := url.Values{}
if currency != "" {
val.Set("currency", currency)
}
return response,
b.SendAuthenticatedHTTPRequest(privateAccInfo, val, &response)
}
// GetAccountBalance returns customer wallet information
func (b *Bithumb) GetAccountBalance(c string) (FullBalance, error) {
var response Balance
var fullBalance = FullBalance{
make(map[string]float64),
make(map[string]float64),
make(map[string]float64),
make(map[string]float64),
make(map[string]float64),
}
vals := url.Values{}
if c != "" {
vals.Set("currency", c)
}
err := b.SendAuthenticatedHTTPRequest(privateAccBalance, vals, &response)
if err != nil {
return fullBalance, err
}
// Added due to increasing of the usuable currencies on exchange, usually
// without notificatation, so we dont need to update structs later on
for tag, datum := range response.Data {
splitTag := common.SplitStrings(tag, "_")
c := splitTag[len(splitTag)-1]
var val float64
if reflect.TypeOf(datum).String() != "float64" {
val, err = strconv.ParseFloat(datum.(string), 64)
if err != nil {
return fullBalance, err
}
} else {
val = datum.(float64)
}
switch splitTag[0] {
case "available":
fullBalance.Available[c] = val
case "in":
fullBalance.InUse[c] = val
case "total":
fullBalance.Total[c] = val
case "misu":
fullBalance.Misu[c] = val
case "xcoin":
fullBalance.Xcoin[c] = val
default:
return fullBalance, fmt.Errorf("getaccountbalance error tag name %s unhandled",
splitTag)
}
}
return fullBalance, nil
}
// GetWalletAddress returns customer wallet address
//
// currency e.g. btc, ltc or "", will default to btc without currency specified
func (b *Bithumb) GetWalletAddress(currency string) (WalletAddressRes, error) {
response := WalletAddressRes{}
params := url.Values{}
params.Set("currency", common.StringToUpper(currency))
err := b.SendAuthenticatedHTTPRequest(privateWalletAdd, params, &response)
if err != nil {
return response, err
}
if response.Data.WalletAddress == "" {
return response,
fmt.Errorf("deposit address needs to be created via the Bithumb website before retreival for currency %s",
currency)
}
return response, nil
}
// GetLastTransaction returns customer last transaction
func (b *Bithumb) GetLastTransaction() (LastTransactionTicker, error) {
response := LastTransactionTicker{}
return response,
b.SendAuthenticatedHTTPRequest(privateTicker, nil, &response)
}
// GetOrders returns order list
//
// orderID: order number registered for purchase/sales
// transactionType: transaction type(bid : purchase, ask : sell)
// count: Value : 1 ~1000 (default : 100)
// after: YYYY-MM-DD hh:mm:ss's UNIX Timestamp
// (2014-11-28 16:40:01 = 1417160401000)
func (b *Bithumb) GetOrders(orderID, transactionType, count, after, currency string) (Orders, error) {
response := Orders{}
params := url.Values{}
if len(orderID) > 0 {
params.Set("order_id", orderID)
}
if len(transactionType) > 0 {
params.Set("type", transactionType)
}
if len(count) > 0 {
params.Set("count", count)
}
if len(after) > 0 {
params.Set("after", after)
}
if len(currency) > 0 {
params.Set("currency", common.StringToUpper(currency))
}
return response,
b.SendAuthenticatedHTTPRequest(privateOrders, params, &response)
}
// GetUserTransactions returns customer transactions
func (b *Bithumb) GetUserTransactions() (UserTransactions, error) {
response := UserTransactions{}
return response,
b.SendAuthenticatedHTTPRequest(privateUserTrans, nil, &response)
}
// PlaceTrade executes a trade order
//
// orderCurrency: BTC, ETH, DASH, LTC, ETC, XRP, BCH, XMR, ZEC, QTUM, BTG, EOS
// (default value: BTC)
// transactionType: Transaction type(bid : purchase, ask : sales)
// units: Order quantity
// price: Transaction amount per currency
func (b *Bithumb) PlaceTrade(orderCurrency, transactionType string, units float64, price int64) (OrderPlace, error) {
response := OrderPlace{}
params := url.Values{}
params.Set("order_currency", common.StringToUpper(orderCurrency))
params.Set("Payment_currency", "KRW")
params.Set("type", common.StringToUpper(transactionType))
params.Set("units", strconv.FormatFloat(units, 'f', -1, 64))
params.Set("price", strconv.FormatInt(price, 10))
return response,
b.SendAuthenticatedHTTPRequest(privatePlaceTrade, params, &response)
}
// ModifyTrade modifies an order already on the exchange books
func (b *Bithumb) ModifyTrade(orderID, orderCurrency, transactionType string, units float64, price int64) (OrderPlace, error) {
response := OrderPlace{}
params := url.Values{}
params.Set("order_currency", common.StringToUpper(orderCurrency))
params.Set("Payment_currency", "KRW")
params.Set("type", common.StringToUpper(transactionType))
params.Set("units", strconv.FormatFloat(units, 'f', -1, 64))
params.Set("price", strconv.FormatInt(price, 10))
params.Set("order_id", orderID)
return response,
b.SendAuthenticatedHTTPRequest(privatePlaceTrade, params, &response)
}
// GetOrderDetails returns specific order details
//
// orderID: Order number registered for purchase/sales
// transactionType: Transaction type(bid : purchase, ask : sales)
// currency: BTC, ETH, DASH, LTC, ETC, XRP, BCH, XMR, ZEC, QTUM, BTG, EOS
// (default value: BTC)
func (b *Bithumb) GetOrderDetails(orderID, transactionType, currency string) (OrderDetails, error) {
response := OrderDetails{}
params := url.Values{}
params.Set("order_id", common.StringToUpper(orderID))
params.Set("type", transactionType)
params.Set("currency", common.StringToUpper(currency))
return response,
b.SendAuthenticatedHTTPRequest(privateOrderDetail, params, &response)
}
// CancelTrade cancels a customer purchase/sales transaction
// transactionType: Transaction type(bid : purchase, ask : sales)
// orderID: Order number registered for purchase/sales
// currency: BTC, ETH, DASH, LTC, ETC, XRP, BCH, XMR, ZEC, QTUM, BTG, EOS
// (default value: BTC)
func (b *Bithumb) CancelTrade(transactionType, orderID, currency string) (ActionStatus, error) {
response := ActionStatus{}
params := url.Values{}
params.Set("order_id", common.StringToUpper(orderID))
params.Set("type", common.StringToUpper(transactionType))
params.Set("currency", common.StringToUpper(currency))
return response,
b.SendAuthenticatedHTTPRequest(privateCancelTrade, nil, &response)
}
// WithdrawCrypto withdraws a customer currency to an address
//
// address: Currency withdrawing address
// destination: Currency withdrawal Destination Tag (when withdraw XRP) OR
// Currency withdrawal Payment Id (when withdraw XMR)
// currency: BTC, ETH, DASH, LTC, ETC, XRP, BCH, XMR, ZEC, QTUM
// (default value: BTC)
// units: Quantity to withdraw currency
func (b *Bithumb) WithdrawCrypto(address, destination, currency string, units float64) (ActionStatus, error) {
response := ActionStatus{}
params := url.Values{}
params.Set("address", address)
if len(destination) > 0 {
params.Set("destination", destination)
}
params.Set("currency", common.StringToUpper(currency))
params.Set("units", strconv.FormatFloat(units, 'f', -1, 64))
return response,
b.SendAuthenticatedHTTPRequest(privateBTCWithdraw, params, &response)
}
// RequestKRWDepositDetails returns Bithumb banking details for deposit
// information
func (b *Bithumb) RequestKRWDepositDetails() (KRWDeposit, error) {
response := KRWDeposit{}
return response,
b.SendAuthenticatedHTTPRequest(privateKRWDeposit, nil, &response)
}
// RequestKRWWithdraw allows a customer KRW withdrawal request
//
// bank: Bankcode with bank name e.g. (bankcode)_(bankname)
// account: Withdrawing bank account number
// price: Withdrawing amount
func (b *Bithumb) RequestKRWWithdraw(bank, account string, price int64) (ActionStatus, error) {
response := ActionStatus{}
params := url.Values{}
params.Set("bank", bank)
params.Set("account", account)
params.Set("price", strconv.FormatInt(price, 10))
return response,
b.SendAuthenticatedHTTPRequest(privateKRWWithdraw, params, &response)
}
// MarketBuyOrder initiates a buy order through available order books
//
// currency: BTC, ETH, DASH, LTC, ETC, XRP, BCH, XMR, ZEC, QTUM, BTG, EOS
// (default value: BTC)
// units: Order quantity
func (b *Bithumb) MarketBuyOrder(currency string, units float64) (MarketBuy, error) {
response := MarketBuy{}
params := url.Values{}
params.Set("currency", common.StringToUpper(currency))
params.Set("units", strconv.FormatFloat(units, 'f', -1, 64))
return response,
b.SendAuthenticatedHTTPRequest(privateMarketBuy, params, &response)
}
// MarketSellOrder initiates a sell order through available order books
//
// currency: BTC, ETH, DASH, LTC, ETC, XRP, BCH, XMR, ZEC, QTUM, BTG, EOS
// (default value: BTC)
// units: Order quantity
func (b *Bithumb) MarketSellOrder(currency string, units float64) (MarketSell, error) {
response := MarketSell{}
params := url.Values{}
params.Set("currency", common.StringToUpper(currency))
params.Set("units", strconv.FormatFloat(units, 'f', -1, 64))
return response,
b.SendAuthenticatedHTTPRequest(privateMarketSell, params, &response)
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (b *Bithumb) SendHTTPRequest(path string, result interface{}) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, b.Verbose)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to bithumb
func (b *Bithumb) SendAuthenticatedHTTPRequest(path string, params url.Values, result interface{}) error {
if !b.AuthenticatedAPISupport {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name)
}
if params == nil {
params = url.Values{}
}
if b.Nonce.Get() == 0 {
b.Nonce.Set(time.Now().UnixNano() / int64(time.Millisecond))
} else {
b.Nonce.Inc()
}
params.Set("endpoint", path)
payload := params.Encode()
hmacPayload := path + string(0) + payload + string(0) + b.Nonce.String()
hmac := common.GetHMAC(common.HashSHA512,
[]byte(hmacPayload),
[]byte(b.APISecret))
hmacStr := common.HexEncodeToString(hmac)
headers := make(map[string]string)
headers["Api-Key"] = b.APIKey
headers["Api-Sign"] = common.Base64Encode([]byte(hmacStr))
headers["Api-Nonce"] = b.Nonce.String()
headers["Content-Type"] = "application/x-www-form-urlencoded"
var intermediary json.RawMessage
errCapture := struct {
Status string `json:"status"`
Message string `json:"message"`
}{}
err := b.SendPayload(http.MethodPost,
b.APIUrl+path,
headers,
bytes.NewBufferString(payload),
&intermediary,
true,
b.Verbose)
if err != nil {
return err
}
err = common.JSONDecode(intermediary, &errCapture)
if err == nil {
if errCapture.Status != "" && errCapture.Status != "0000" {
return fmt.Errorf("sendAuthenticatedAPIRequest error code: %s message:%s",
errCapture.Status,
errCode[errCapture.Status])
}
}
return common.JSONDecode(intermediary, result)
}
// GetFee returns an estimate of fee based on type of transaction
func (b *Bithumb) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) {
var fee float64
switch feeBuilder.FeeType {
case exchange.CryptocurrencyTradeFee:
fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount)
case exchange.CyptocurrencyDepositFee:
fee = getDepositFee(feeBuilder.FirstCurrency, feeBuilder.Amount)
case exchange.CryptocurrencyWithdrawalFee:
fee = getWithdrawalFee(feeBuilder.FirstCurrency)
case exchange.InternationalBankWithdrawalFee:
fee = getWithdrawalFee(feeBuilder.CurrencyItem)
}
if fee < 0 {
fee = 0
}
return fee, nil
}
// calculateTradingFee returns fee when performing a trade
func calculateTradingFee(purchasePrice, amount float64) float64 {
fee := 0.0015
return fee * amount * purchasePrice
}
// getDepositFee returns fee on a currency when depositing small amounts to bithumb
func getDepositFee(currency string, amount float64) float64 {
var fee float64
switch currency {
case symbol.BTC:
if amount <= 0.005 {
fee = 0.001
}
case symbol.LTC:
if amount <= 0.3 {
fee = 0.01
}
case symbol.DASH:
if amount <= 0.04 {
fee = 0.01
}
case symbol.BCH:
if amount <= 0.03 {
fee = 0.001
}
case symbol.ZEC:
if amount <= 0.02 {
fee = 0.001
}
case symbol.BTG:
if amount <= 0.15 {
fee = 0.001
}
}
return fee
}
// getWithdrawalFee returns fee on a currency when withdrawing out of bithumb
func getWithdrawalFee(currency string) float64 {
return WithdrawalFees[currency]
}
var errCode = map[string]string{
"5100": "Bad Request",
"5200": "Not Member",
"5300": "Invalid Apikey",
"5302": "Method Not Allowed",
"5400": "Database Fail",
"5500": "Invalid Parameter",
"5600": "CUSTOM NOTICE (상황별 에러 메시지 출력) usually means transaction not allowed",
"5900": "Unknown Error",
}