mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-18 07:26:50 +00:00
Added support for Bithumb exchange
This commit is contained in:
@@ -21,6 +21,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
|
||||
| Alphapoint | Yes | Yes | NA |
|
||||
| ANXPRO | Yes | No | NA |
|
||||
| Bitfinex | Yes | Yes | NA |
|
||||
| Bithumb | Yes | NA | NA |
|
||||
| Bitstamp | Yes | Yes | NA |
|
||||
| Bittrex | Yes | No | NA |
|
||||
| BTCC | Yes | Yes | No |
|
||||
@@ -103,7 +104,7 @@ copy %GOPATH%\src\github.com\thrasher-\gocryptotrader\config_example.json %GOPAT
|
||||
|
||||
<img src="https://github.com/thrasher-/gocryptotrader/blob/master/web/src/assets/early-dumb-donate.png?raw=true" hspace="70">
|
||||
|
||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||
|
||||
***1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB***
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ func TestGetEnabledExchanges(t *testing.T) {
|
||||
}
|
||||
|
||||
exchanges := cfg.GetEnabledExchanges()
|
||||
if len(exchanges) != 20 {
|
||||
if len(exchanges) != 21 {
|
||||
t.Error(
|
||||
"Test failed. TestGetEnabledExchanges. Enabled exchanges value mismatch",
|
||||
)
|
||||
@@ -141,7 +141,7 @@ func TestGetDisabledExchanges(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCountEnabledExchanges(t *testing.T) {
|
||||
defaultEnabledExchanges := 20
|
||||
defaultEnabledExchanges := 21
|
||||
GetConfigEnabledExchanges := GetConfig()
|
||||
err := GetConfigEnabledExchanges.LoadConfig(ConfigTestFile)
|
||||
if err != nil {
|
||||
|
||||
@@ -101,6 +101,28 @@
|
||||
"Uppercase": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Bithumb",
|
||||
"Enabled": true,
|
||||
"Verbose": false,
|
||||
"Websocket": false,
|
||||
"UseSandbox": false,
|
||||
"RESTPollingDelay": 10,
|
||||
"AuthenticatedAPISupport": false,
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"ClientID": "ClientID",
|
||||
"AvailablePairs": "BTCKRW,ETHKRW,DASHKRW,LTCKRW,ETCKRW,XRPKRW,BCHKRW,XMRKRW,ZECKRW,QTUMKRW,BTGKRW,EOSKRW",
|
||||
"EnabledPairs": "BTCKRW,ETHKRW,DASHKRW,LTCKRW,ETCKRW,XRPKRW,BCHKRW,XMRKRW,ZECKRW,QTUMKRW,BTGKRW,EOSKRW",
|
||||
"BaseCurrencies": "KRW",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Bitstamp",
|
||||
"Enabled": true,
|
||||
@@ -501,4 +523,4 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/anx"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/bitfinex"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/bithumb"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/bitstamp"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/bittrex"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/btcc"
|
||||
@@ -129,6 +130,8 @@ func LoadExchange(name string) error {
|
||||
exch = new(anx.ANX)
|
||||
case "bitfinex":
|
||||
exch = new(bitfinex.Bitfinex)
|
||||
case "bithumb":
|
||||
exch = new(bithumb.Bithumb)
|
||||
case "bitstamp":
|
||||
exch = new(bitstamp.Bitstamp)
|
||||
case "bittrex":
|
||||
|
||||
464
exchanges/bithumb/bithumb.go
Normal file
464
exchanges/bithumb/bithumb.go
Normal file
@@ -0,0 +1,464 @@
|
||||
package bithumb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
apiURL = "https://api.bithumb.com"
|
||||
|
||||
noError = "0000"
|
||||
|
||||
// Public API
|
||||
requestsPerSecondPublicAPI = 20
|
||||
|
||||
publicTicker = "/public/ticker/"
|
||||
publicOrderBook = "/public/orderbook/"
|
||||
publicRecentTransaction = "/public/recent_transactions/"
|
||||
|
||||
// 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"
|
||||
)
|
||||
|
||||
// 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.Websocket = false
|
||||
b.RESTPollingDelay = 10
|
||||
b.RequestCurrencyPairFormat.Delimiter = ""
|
||||
b.RequestCurrencyPairFormat.Uppercase = true
|
||||
b.ConfigCurrencyPairFormat.Delimiter = ""
|
||||
b.ConfigCurrencyPairFormat.Uppercase = true
|
||||
b.AssetTypes = []string{ticker.Spot}
|
||||
}
|
||||
|
||||
// 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.RESTPollingDelay = exch.RESTPollingDelay
|
||||
b.Verbose = exch.Verbose
|
||||
b.Websocket = 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetTicker returns ticker information
|
||||
//
|
||||
// symbol e.g. "btc"
|
||||
func (b *Bithumb) GetTicker(symbol string) (Ticker, error) {
|
||||
response := Ticker{}
|
||||
path := fmt.Sprintf("%s%s%s", apiURL, publicTicker, common.StringToUpper(symbol))
|
||||
|
||||
return response, common.SendHTTPGetRequest(path, true, b.Verbose, &response)
|
||||
}
|
||||
|
||||
// GetOrderBook returns current orderbook
|
||||
//
|
||||
// symbol e.g. "btc"
|
||||
func (b *Bithumb) GetOrderBook(symbol string) (Orderbook, error) {
|
||||
response := Orderbook{}
|
||||
path := fmt.Sprintf("%s%s%s", apiURL, publicOrderBook, common.StringToUpper(symbol))
|
||||
|
||||
return response, common.SendHTTPGetRequest(path, true, b.Verbose, &response)
|
||||
}
|
||||
|
||||
// GetRecentTransactions returns recent transactions
|
||||
//
|
||||
// symbol e.g. "btc"
|
||||
func (b *Bithumb) GetRecentTransactions(symbol string) (RecentTransactions, error) {
|
||||
response := RecentTransactions{}
|
||||
path := fmt.Sprintf("%s%s%s", apiURL, publicRecentTransaction, common.StringToUpper(symbol))
|
||||
|
||||
return response, common.SendHTTPGetRequest(path, true, b.Verbose, &response)
|
||||
}
|
||||
|
||||
// GetAccountInfo returns account information
|
||||
func (b *Bithumb) GetAccountInfo() (Account, error) {
|
||||
response := Account{}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(privateAccInfo, nil, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
if response.Status != noError {
|
||||
return response, errors.New(response.Message)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// GetAccountBalance returns customer wallet information
|
||||
func (b *Bithumb) GetAccountBalance() (Balance, error) {
|
||||
response := Balance{}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(privateAccBalance, nil, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
if response.Status != noError {
|
||||
return response, errors.New(response.Message)
|
||||
}
|
||||
return response, 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.Status != noError {
|
||||
return response, errors.New(response.Message)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// GetLastTransaction returns customer last transaction
|
||||
func (b *Bithumb) GetLastTransaction() (LastTransactionTicker, error) {
|
||||
response := LastTransactionTicker{}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(privateTicker, nil, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
if response.Status != noError {
|
||||
return response, errors.New(response.Message)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// 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{}
|
||||
params.Set("order_id", orderID)
|
||||
params.Set("type", transactionType)
|
||||
params.Set("count", count)
|
||||
params.Set("after", after)
|
||||
params.Set("currency", common.StringToUpper(currency))
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(privateOrders, params, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
if response.Status != noError {
|
||||
return response, errors.New(response.Message)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// GetUserTransactions returns customer transactions
|
||||
func (b *Bithumb) GetUserTransactions() (UserTransactions, error) {
|
||||
response := UserTransactions{}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(privateUserTrans, nil, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
if response.Status != noError {
|
||||
return response, errors.New(response.Message)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// 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))
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(privatePlaceTrade, params, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
if response.Status != noError {
|
||||
return response, errors.New(response.Message)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// 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", common.StringToUpper(transactionType))
|
||||
params.Set("currency", common.StringToUpper(currency))
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(privateOrderDetail, params, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
if response.Status != noError {
|
||||
return response, errors.New(response.Message)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// 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))
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(privateCancelTrade, nil, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
if response.Status != noError {
|
||||
return response, errors.New(response.Message)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
params.Set("destination", destination)
|
||||
params.Set("currency", common.StringToUpper(currency))
|
||||
params.Set("units", strconv.FormatFloat(units, 'f', -1, 64))
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(privateBTCWithdraw, params, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
if response.Status != noError {
|
||||
return response, errors.New(response.Message)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// RequestKRWDepositDetails returns Bithumb banking details for deposit
|
||||
// information
|
||||
func (b *Bithumb) RequestKRWDepositDetails() (KRWDeposit, error) {
|
||||
response := KRWDeposit{}
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(privateKRWDeposit, nil, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
if response.Status != noError {
|
||||
return response, errors.New(response.Message)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// 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))
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(privateKRWWithdraw, params, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
if response.Status != noError {
|
||||
return response, errors.New(response.Message)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// 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))
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(privateMarketBuy, params, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
if response.Status != noError {
|
||||
return response, errors.New(response.Message)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// 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))
|
||||
|
||||
err := b.SendAuthenticatedHTTPRequest(privateMarketSell, params, &response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
if response.Status != noError {
|
||||
return response, errors.New(response.Message)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// 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"
|
||||
|
||||
resp, err := common.SendHTTPRequest(
|
||||
"POST", apiURL+path, headers, bytes.NewBufferString(payload),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if b.Verbose {
|
||||
log.Printf("Received raw: \n%s\n", resp)
|
||||
}
|
||||
|
||||
if err = common.JSONDecode([]byte(resp), &result); err != nil {
|
||||
return errors.New("sendAuthenticatedHTTPRequest: Unable to JSON Unmarshal response." + err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
202
exchanges/bithumb/bithumb_test.go
Normal file
202
exchanges/bithumb/bithumb_test.go
Normal file
@@ -0,0 +1,202 @@
|
||||
package bithumb
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
)
|
||||
|
||||
// Please supply your own keys here for due diligence testing
|
||||
const (
|
||||
testAPIKey = ""
|
||||
testAPISecret = ""
|
||||
)
|
||||
|
||||
var b Bithumb
|
||||
|
||||
func TestSetDefaults(t *testing.T) {
|
||||
b.SetDefaults()
|
||||
}
|
||||
|
||||
func TestSetup(t *testing.T) {
|
||||
cfg := config.GetConfig()
|
||||
cfg.LoadConfig("../../testdata/configtest.json")
|
||||
bitConfig, err := cfg.GetExchangeConfig("Bithumb")
|
||||
if err != nil {
|
||||
t.Error("Test Failed - Bithumb Setup() init error")
|
||||
}
|
||||
|
||||
bitConfig.AuthenticatedAPISupport = true
|
||||
bitConfig.APIKey = testAPIKey
|
||||
bitConfig.APISecret = testAPISecret
|
||||
|
||||
b.Setup(bitConfig)
|
||||
}
|
||||
|
||||
func TestGetTicker(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetTicker("btc")
|
||||
if err != nil {
|
||||
t.Error("test failed - Bithumb GetTicker() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderBook(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetOrderBook("btc")
|
||||
if err != nil {
|
||||
t.Error("test failed - Bithumb GetOrderBook() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRecentTransactions(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetRecentTransactions("btc")
|
||||
if err != nil {
|
||||
t.Error("test failed - Bithumb GetRecentTransactions() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAccountInfo(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetAccountInfo()
|
||||
if err == nil {
|
||||
t.Error("test failed - Bithumb GetAccountInfo() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAccountBalance(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetAccountBalance()
|
||||
if err == nil {
|
||||
t.Error("test failed - Bithumb GetAccountBalance() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetWalletAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetWalletAddress("")
|
||||
if err == nil {
|
||||
t.Error("test failed - Bithumb GetWalletAddress() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLastTransaction(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetLastTransaction()
|
||||
if err == nil {
|
||||
t.Error("test failed - Bithumb GetLastTransaction() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrders(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetOrders("1337", "bid", "100", "", "BTC")
|
||||
if err == nil {
|
||||
t.Error("test failed - Bithumb GetOrders() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUserTransactions(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetUserTransactions()
|
||||
if err == nil {
|
||||
t.Error("test failed - Bithumb GetUserTransactions() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlaceTrade(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.PlaceTrade("btc", "bid", 0, 0)
|
||||
if err == nil {
|
||||
t.Error("test failed - Bithumb PlaceTrade() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderDetails(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetOrderDetails("1337", "bid", "btc")
|
||||
if err == nil {
|
||||
t.Error("test failed - Bithumb GetOrderDetails() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelTrade(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.CancelTrade("", "", "")
|
||||
if err == nil {
|
||||
t.Error("test failed - Bithumb CancelTrade() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithdrawCrypto(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.WithdrawCrypto("LQxiDhKU7idKiWQhx4ALKYkBx8xKEQVxJR", "", "ltc", 0)
|
||||
if err == nil {
|
||||
t.Error("test failed - Bithumb WithdrawCrypto() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestKRWDepositDetails(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.RequestKRWDepositDetails()
|
||||
if err == nil {
|
||||
t.Error("test failed - Bithumb RequestKRWDepositDetails() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestKRWWithdraw(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.RequestKRWWithdraw("102_bank", "1337", 1000)
|
||||
if err == nil {
|
||||
t.Error("test failed - Bithumb RequestKRWWithdraw() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarketBuyOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.MarketBuyOrder("btc", 0)
|
||||
if err == nil {
|
||||
t.Error("test failed - Bithumb MarketBuyOrder() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarketSellOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.MarketSellOrder("btc", 0)
|
||||
if err == nil {
|
||||
t.Error("test failed - Bithumb MarketSellOrder() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
t.Parallel()
|
||||
b.Run()
|
||||
}
|
||||
|
||||
func TestUpdateTicker(t *testing.T) {
|
||||
t.Parallel()
|
||||
pair := b.GetEnabledCurrencies()[0]
|
||||
_, err := b.UpdateTicker(pair, b.AssetTypes[0])
|
||||
if err != nil {
|
||||
t.Error("test failed - Bithumb UpdateTicker() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTickerPrice(t *testing.T) {
|
||||
t.Parallel()
|
||||
pair := b.GetEnabledCurrencies()[0]
|
||||
_, err := b.GetTickerPrice(pair, b.AssetTypes[0])
|
||||
if err != nil {
|
||||
t.Error("test failed - Bithumb GetTickerPrice() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderbookEx(t *testing.T) {
|
||||
t.Parallel()
|
||||
pair := b.GetEnabledCurrencies()[0]
|
||||
_, err := b.GetOrderbookEx(pair, b.AssetTypes[0])
|
||||
if err != nil {
|
||||
t.Error("test failed - Bithumb GetOrderbookEx() error", err)
|
||||
}
|
||||
}
|
||||
217
exchanges/bithumb/bithumb_types.go
Normal file
217
exchanges/bithumb/bithumb_types.go
Normal file
@@ -0,0 +1,217 @@
|
||||
package bithumb
|
||||
|
||||
// Ticker holds the standard ticker information
|
||||
type Ticker struct {
|
||||
Status string `json:"status"`
|
||||
Data struct {
|
||||
OpeningPrice float64 `json:"opening_price,string"`
|
||||
ClosingPrice float64 `json:"closing_price,string"`
|
||||
MinPrice float64 `json:"min_price,string"`
|
||||
MaxPrice float64 `json:"max_price,string"`
|
||||
AveragePrice float64 `json:"average_price,string"`
|
||||
UnitsTraded float64 `json:"units_traded,string"`
|
||||
Volume1Day float64 `json:"volume_1day,string"`
|
||||
Volume7Day float64 `json:"volume_7day,string"`
|
||||
BuyPrice float64 `json:"buy_price,string"`
|
||||
SellPrice float64 `json:"sell_price,string"`
|
||||
Date int64 `json:"date,string"`
|
||||
} `json:"data"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// Orderbook holds full range of order book information
|
||||
type Orderbook struct {
|
||||
Status string `json:"status"`
|
||||
Data struct {
|
||||
Timestamp int64 `json:"timestamp,string"`
|
||||
OrderCurrency string `json:"order_currency"`
|
||||
PaymentCurrency string `json:"payment_currency"`
|
||||
Bids []struct {
|
||||
Quantity float64 `json:"quantity,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
} `json:"bids"`
|
||||
Asks []struct {
|
||||
Quantity float64 `json:"quantity,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
} `json:"asks"`
|
||||
} `json:"data"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// RecentTransactions holds history of completed transaction data
|
||||
type RecentTransactions struct {
|
||||
Status string `json:"status"`
|
||||
Data []struct {
|
||||
TransactionDate string `json:"transaction_date"`
|
||||
Type string `json:"type"`
|
||||
UnitsTraded float64 `json:"units_traded,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Total float64 `json:"total,string"`
|
||||
} `json:"data"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// Account holds account details
|
||||
type Account struct {
|
||||
Status string `json:"status"`
|
||||
Data struct {
|
||||
Created int64 `json:"created,string"`
|
||||
AccountID string `json:"account_id"`
|
||||
TradeFee float64 `json:"trade_fee,string"`
|
||||
Balance float64 `json:"balance,string"`
|
||||
} `json:"data"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// Balance holds balance details
|
||||
type Balance struct {
|
||||
Status string `json:"status"`
|
||||
Data struct {
|
||||
TotalBTC float64 `json:"total_btc,string"`
|
||||
TotalKRW float64 `json:"total_krw"`
|
||||
InUseBTC float64 `json:"in_use_btc,string"`
|
||||
InUseKRW float64 `json:"in_use_krw"`
|
||||
AvailableBTC float64 `json:"available_btc,string"`
|
||||
AvailableKRW float64 `json:"available_krw"`
|
||||
MisuKRW float64 `json:"misu_krw"`
|
||||
MisuBTC float64 `json:"misu_btc,string"`
|
||||
XcoinLast float64 `json:"xcoin_last,string"`
|
||||
} `json:"data"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// WalletAddressRes contains wallet address information
|
||||
type WalletAddressRes struct {
|
||||
Status string `json:"status"`
|
||||
Data struct {
|
||||
WalletAddress string `json:"wallet_address"`
|
||||
Currency string `json:"currency"`
|
||||
} `json:"data"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// LastTransactionTicker holds customer last transaction information
|
||||
type LastTransactionTicker struct {
|
||||
Status string `json:"status"`
|
||||
Data struct {
|
||||
OpeningPrice float64 `json:"opening_price,string"`
|
||||
ClosingPrice float64 `json:"closing_price,string"`
|
||||
MinPrice float64 `json:"min_price,string"`
|
||||
MaxPrice float64 `json:"max_price,string"`
|
||||
AveragePrice float64 `json:"average_price,string"`
|
||||
UnitsTraded float64 `json:"units_traded,string"`
|
||||
Volume1Day float64 `json:"volume_1day,string"`
|
||||
Volume7Day float64 `json:"volume_7day,string"`
|
||||
BuyPrice int64 `json:"buy_price,string"`
|
||||
SellPrice int64 `json:"sell_price,string"`
|
||||
Date int64 `json:"date,string"`
|
||||
} `json:"data"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// Orders contains information about your current orders
|
||||
type Orders struct {
|
||||
Status string `json:"status"`
|
||||
Data []struct {
|
||||
OrderID string `json:"order_id"`
|
||||
OrderCurrency string `json:"order_currency"`
|
||||
OrderDate int64 `json:"order_date"`
|
||||
PaymentCurrency string `json:"payment_currency"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
Units float64 `json:"units,string"`
|
||||
UnitsRemaining float64 `json:"units_remaining,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
Total float64 `json:"total,string"`
|
||||
DateCompleted int64 `json:"date_completed"`
|
||||
} `json:"data"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// UserTransactions holds users full transaction list
|
||||
type UserTransactions struct {
|
||||
Status string `json:"status"`
|
||||
Data []struct {
|
||||
Search string `json:"search"`
|
||||
TransferDate int64 `json:"transfer_date"`
|
||||
Units string `json:"units"`
|
||||
Price float64 `json:"price,string"`
|
||||
BTC1KRW float64 `json:"btc1krw,string"`
|
||||
Fee string `json:"fee"`
|
||||
BTCRemain float64 `json:"btc_remain,string"`
|
||||
KRWRemain float64 `json:"krw_remain,string"`
|
||||
} `json:"data"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// OrderPlace contains order information
|
||||
type OrderPlace struct {
|
||||
Status string `json:"status"`
|
||||
Data []struct {
|
||||
ContID string `json:"cont_id"`
|
||||
Units float64 `json:"units,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Total float64 `json:"total,string"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
} `json:"data"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// OrderDetails contains specific order information
|
||||
type OrderDetails struct {
|
||||
Status string `json:"status"`
|
||||
Data []struct {
|
||||
TransactionDate int64 `json:"transaction_date,string"`
|
||||
Type string `json:"type"`
|
||||
OrderCurrency string `json:"order_currency"`
|
||||
PaymentCurrency string `json:"payment_currency"`
|
||||
UnitsTraded float64 `json:"units_traded,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Total float64 `json:"total,string"`
|
||||
} `json:"data"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// ActionStatus holds the return status
|
||||
type ActionStatus struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// KRWDeposit resp type for a KRW deposit
|
||||
type KRWDeposit struct {
|
||||
Status string `json:"status"`
|
||||
Account string `json:"account"`
|
||||
Bank string `json:"bank"`
|
||||
BankUser string `json:"BankUser"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// MarketBuy holds market buy order information
|
||||
type MarketBuy struct {
|
||||
Status string `json:"status"`
|
||||
OrderID string `json:"order_id"`
|
||||
Data []struct {
|
||||
ContID string `json:"cont_id"`
|
||||
Units float64 `json:"units,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Total float64 `json:"total,string"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
} `json:"data"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// MarketSell holds market buy order information
|
||||
type MarketSell struct {
|
||||
Status string `json:"status"`
|
||||
OrderID string `json:"order_id"`
|
||||
Data []struct {
|
||||
ContID string `json:"cont_id"`
|
||||
Units float64 `json:"units,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Total float64 `json:"total,string"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
} `json:"data"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
94
exchanges/bithumb/bithumb_wrapper.go
Normal file
94
exchanges/bithumb/bithumb_wrapper.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package bithumb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// Start starts the OKEX go routine
|
||||
func (b *Bithumb) Start() {
|
||||
go b.Run()
|
||||
}
|
||||
|
||||
// Run implements the OKEX wrapper
|
||||
func (b *Bithumb) Run() {
|
||||
if b.Verbose {
|
||||
log.Printf("%s Websocket: %s. (url: %s).\n", b.GetName(), common.IsEnabled(b.Websocket), b.WebsocketURL)
|
||||
log.Printf("%s polling delay: %ds.\n", b.GetName(), b.RESTPollingDelay)
|
||||
log.Printf("%s %d currencies enabled: %s.\n", b.GetName(), len(b.EnabledPairs), b.EnabledPairs)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (b *Bithumb) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
var tickerPrice ticker.Price
|
||||
item := p.GetFirstCurrency().String()
|
||||
tick, err := b.GetTicker(item)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
|
||||
tickerPrice.Pair = p
|
||||
tickerPrice.Ask = tick.Data.SellPrice
|
||||
tickerPrice.Bid = tick.Data.BuyPrice
|
||||
tickerPrice.Low = tick.Data.MinPrice
|
||||
tickerPrice.Last = tick.Data.ClosingPrice
|
||||
tickerPrice.Volume = tick.Data.Volume1Day
|
||||
tickerPrice.High = tick.Data.MaxPrice
|
||||
ticker.ProcessTicker(b.GetName(), p, tickerPrice, assetType)
|
||||
|
||||
return ticker.GetTicker(b.Name, p, assetType)
|
||||
}
|
||||
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (b *Bithumb) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tickerNew, err := ticker.GetTicker(b.GetName(), p, assetType)
|
||||
if err != nil {
|
||||
return b.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
// GetOrderbookEx returns orderbook base on the currency pair
|
||||
func (b *Bithumb) GetOrderbookEx(currency pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(b.GetName(), currency, assetType)
|
||||
if err != nil {
|
||||
return b.UpdateOrderbook(currency, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (b *Bithumb) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
currency := p.GetFirstCurrency().String()
|
||||
|
||||
orderbookNew, err := b.GetOrderBook(currency)
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
for _, bids := range orderbookNew.Data.Bids {
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: bids.Quantity, Price: bids.Price})
|
||||
}
|
||||
|
||||
for _, asks := range orderbookNew.Data.Asks {
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: asks.Quantity, Price: asks.Price})
|
||||
}
|
||||
|
||||
orderbook.ProcessOrderbook(b.GetName(), p, orderBook, assetType)
|
||||
return orderbook.GetOrderbook(b.Name, p, assetType)
|
||||
}
|
||||
|
||||
// GetExchangeAccountInfo retrieves balances for all enabled currencies for the
|
||||
// Bithumb exchange
|
||||
func (b *Bithumb) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
return response, errors.New("not implemented")
|
||||
}
|
||||
22
testdata/configtest.json
vendored
22
testdata/configtest.json
vendored
@@ -101,6 +101,28 @@
|
||||
"Uppercase": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Bithumb",
|
||||
"Enabled": true,
|
||||
"Verbose": false,
|
||||
"Websocket": false,
|
||||
"UseSandbox": false,
|
||||
"RESTPollingDelay": 10,
|
||||
"AuthenticatedAPISupport": false,
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"ClientID": "ClientID",
|
||||
"AvailablePairs": "BTCKRW,ETHKRW,DASHKRW,LTCKRW,ETCKRW,XRPKRW,BCHKRW,XMRKRW,ZECKRW,QTUMKRW,BTGKRW,EOSKRW",
|
||||
"EnabledPairs": "BTCKRW,ETHKRW,DASHKRW,LTCKRW,ETCKRW,XRPKRW,BCHKRW,XMRKRW,ZECKRW,QTUMKRW,BTGKRW,EOSKRW",
|
||||
"BaseCurrencies": "KRW",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Bitstamp",
|
||||
"Enabled": true,
|
||||
|
||||
Reference in New Issue
Block a user