Update Huobi REST authenticated and unauthenticated requests

This commit is contained in:
Adrian Gallagher
2018-01-30 13:11:36 +11:00
parent 784e9e48f5
commit 1e92cab53a
7 changed files with 1052 additions and 160 deletions

View File

@@ -1,11 +1,12 @@
package huobi
import (
"bytes"
"errors"
"fmt"
"log"
"net/url"
"strconv"
"strings"
"time"
"github.com/thrasher-/gocryptotrader/common"
@@ -15,8 +16,35 @@ import (
)
const (
huobiAPIURL = "https://api.huobi.com/apiv2.php"
huobiAPIVersion = "2"
huobiAPIURL = "https://api.huobi.pro"
huobiAPIVersion = "1"
huobiMarketHistoryKline = "market/history/kline"
huobiMarketDetail = "market/detail"
huobiMarketDetailMerged = "market/detail/merged"
huobiMarketDepth = "market/depth"
huobiMarketTrade = "market/trade"
huobiMarketTradeHistory = "market/history/trade"
huobiSymbols = "common/symbols"
huobiCurrencies = "common/currencys"
huobiTimestamp = "common/timestamp"
huobiAccounts = "account/accounts"
huobiAccountBalance = "account/accounts/%s/balance"
huobiOrderPlace = "order/orders/place"
huobiOrderCancel = "order/orders/%s/submitcancel"
huobiOrderCancelBatch = "order/orders/batchcancel"
huobiGetOrder = "order/orders/%s"
huobiGetOrderMatch = "order/orders/%s/matchresults"
huobiGetOrders = "order/orders"
huobiGetOrdersMatch = "orders/matchresults"
huobiMarginTransferIn = "dw/transfer-in/margin"
huobiMarginTransferOut = "dw/transfer-out/margin"
huobiMarginOrders = "margin/orders"
huobiMarginRepay = "margin/orders/%s/repay"
huobiMarginLoanOrders = "margin/loan-orders"
huobiMarginAccountBalance = "margin/accounts/balance"
huobiWithdrawCreate = "dw/withdraw/api/create"
huobiWithdrawCancel = "dw/withdraw-virtual/%s/cancel"
)
// HUOBI is the overarching type across this package
@@ -34,7 +62,7 @@ func (h *HUOBI) SetDefaults() {
h.RESTPollingDelay = 10
h.RequestCurrencyPairFormat.Delimiter = ""
h.RequestCurrencyPairFormat.Uppercase = false
h.ConfigCurrencyPairFormat.Delimiter = ""
h.ConfigCurrencyPairFormat.Delimiter = "-"
h.ConfigCurrencyPairFormat.Uppercase = true
h.AssetTypes = []string{ticker.Spot}
}
@@ -69,163 +97,628 @@ func (h *HUOBI) GetFee() float64 {
return h.Fee
}
// GetTicker returns the Huobi ticker
func (h *HUOBI) GetTicker(symbol string) (Ticker, error) {
resp := TickerResponse{}
path := fmt.Sprintf("https://api.huobi.com/staticmarket/ticker_%s_json.js", symbol)
// GetKline returns kline data
func (h *HUOBI) GetKline(symbol, period, size string) ([]Klines, error) {
vals := url.Values{}
vals.Set("symbol", symbol)
return resp.Ticker, common.SendHTTPGetRequest(path, true, h.Verbose, &resp)
if period != "" {
vals.Set("period", period)
}
if size != "" {
vals.Set("size", size)
}
type response struct {
Response
Data []Klines `json:"data"`
}
var result response
url := fmt.Sprintf("%s/%s", huobiAPIURL, huobiMarketHistoryKline)
err := common.SendHTTPGetRequest(common.EncodeURLValues(url, vals), true, h.Verbose, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.Data, err
}
// GetOrderBook returns the Huobi current orderbook for a currency pair
func (h *HUOBI) GetOrderBook(symbol string) (Orderbook, error) {
path := fmt.Sprintf("https://api.huobi.com/staticmarket/depth_%s_json.js", symbol)
resp := Orderbook{}
// GetMarketDetailMerged returns the ticker for the specified symbol
func (h *HUOBI) GetMarketDetailMerged(symbol string) (DetailMerged, error) {
vals := url.Values{}
vals.Set("symbol", symbol)
return resp, common.SendHTTPGetRequest(path, true, h.Verbose, &resp)
type response struct {
Response
Tick DetailMerged `json:"tick"`
}
var result response
url := fmt.Sprintf("%s/%s", huobiAPIURL, huobiMarketDetailMerged)
err := common.SendHTTPGetRequest(common.EncodeURLValues(url, vals), true, h.Verbose, &result)
if result.ErrorMessage != "" {
return result.Tick, errors.New(result.ErrorMessage)
}
return result.Tick, err
}
// GetAccountInfo returns account information
func (h *HUOBI) GetAccountInfo() {
err := h.SendAuthenticatedRequest("get_account_info", url.Values{})
// GetDepth returns the depth for the specified symbol
func (h *HUOBI) GetDepth(symbol, depthType string) (Orderbook, error) {
vals := url.Values{}
vals.Set("symbol", symbol)
if err != nil {
log.Println(err)
if depthType != "" {
vals.Set("type", depthType)
}
type response struct {
Response
Depth Orderbook `json:"tick"`
}
var result response
url := fmt.Sprintf("%s/%s", huobiAPIURL, huobiMarketDepth)
err := common.SendHTTPGetRequest(common.EncodeURLValues(url, vals), true, h.Verbose, &result)
if result.ErrorMessage != "" {
return result.Depth, errors.New(result.ErrorMessage)
}
return result.Depth, err
}
// GetOrders returns full list of orders
func (h *HUOBI) GetOrders(coinType int) {
values := url.Values{}
values.Set("coin_type", strconv.Itoa(coinType))
err := h.SendAuthenticatedRequest("get_orders", values)
// GetTrades returns the trades for the specified symbol
func (h *HUOBI) GetTrades(symbol string) ([]Trade, error) {
vals := url.Values{}
vals.Set("symbol", symbol)
if err != nil {
log.Println(err)
type response struct {
Response
tick struct {
Data []Trade `json:"data"`
} `json:"tick"`
}
var result response
url := fmt.Sprintf("%s/%s", huobiAPIURL, huobiMarketTrade)
err := common.SendHTTPGetRequest(common.EncodeURLValues(url, vals), true, h.Verbose, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.tick.Data, err
}
// GetOrderInfo returns specific info on an order
func (h *HUOBI) GetOrderInfo(orderID, coinType int) {
values := url.Values{}
values.Set("id", strconv.Itoa(orderID))
values.Set("coin_type", strconv.Itoa(coinType))
err := h.SendAuthenticatedRequest("order_info", values)
// GetTradeHistory returns the trades for the specified symbol
func (h *HUOBI) GetTradeHistory(symbol, size string) ([]TradeHistory, error) {
vals := url.Values{}
vals.Set("symbol", symbol)
if err != nil {
log.Println(err)
if size != "" {
vals.Set("size", size)
}
type response struct {
Response
TradeHistory []TradeHistory `json:"data"`
}
var result response
url := fmt.Sprintf("%s/%s", huobiAPIURL, huobiMarketTradeHistory)
err := common.SendHTTPGetRequest(common.EncodeURLValues(url, vals), true, h.Verbose, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.TradeHistory, err
}
// Trade opens a trade on the Huobi exchange
func (h *HUOBI) Trade(orderType string, coinType int, price, amount float64) {
values := url.Values{}
if orderType != "buy" {
orderType = "sell"
}
values.Set("coin_type", strconv.Itoa(coinType))
values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
values.Set("price", strconv.FormatFloat(price, 'f', -1, 64))
err := h.SendAuthenticatedRequest(orderType, values)
// GetMarketDetail returns the ticker for the specified symbol
func (h *HUOBI) GetMarketDetail(symbol string) (Detail, error) {
vals := url.Values{}
vals.Set("symbol", symbol)
if err != nil {
log.Println(err)
type response struct {
Response
tick Detail `json:"tick"`
}
var result response
url := fmt.Sprintf("%s/%s", huobiAPIURL, huobiMarketDetail)
err := common.SendHTTPGetRequest(common.EncodeURLValues(url, vals), true, h.Verbose, &result)
if result.ErrorMessage != "" {
return result.tick, errors.New(result.ErrorMessage)
}
return result.tick, err
}
// MarketTrade initiates a market trade
func (h *HUOBI) MarketTrade(orderType string, coinType int, price, amount float64) {
values := url.Values{}
if orderType != "buy_market" {
orderType = "sell_market"
// GetSymbols returns an array of symbols supported by Huobi
func (h *HUOBI) GetSymbols() ([]Symbol, error) {
type response struct {
Response
Symbols []Symbol `json:"data"`
}
values.Set("coin_type", strconv.Itoa(coinType))
values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
values.Set("price", strconv.FormatFloat(price, 'f', -1, 64))
err := h.SendAuthenticatedRequest(orderType, values)
if err != nil {
log.Println(err)
var result response
url := fmt.Sprintf("%s/v%s/%s", huobiAPIURL, huobiAPIVersion, huobiSymbols)
err := common.SendHTTPGetRequest(url, true, h.Verbose, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.Symbols, err
}
// CancelOrder cancels order by order ID
func (h *HUOBI) CancelOrder(orderID, coinType int) {
values := url.Values{}
values.Set("coin_type", strconv.Itoa(coinType))
values.Set("id", strconv.Itoa(orderID))
err := h.SendAuthenticatedRequest("cancel_order", values)
if err != nil {
log.Println(err)
// GetCurrencies returns a list of currencies supported by Huobi
func (h *HUOBI) GetCurrencies() ([]string, error) {
type response struct {
Response
Currencies []string `json:"data"`
}
var result response
url := fmt.Sprintf("%s/v%s/%s", huobiAPIURL, huobiAPIVersion, huobiCurrencies)
err := common.SendHTTPGetRequest(url, true, h.Verbose, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.Currencies, err
}
// ModifyOrder modifies an order
func (h *HUOBI) ModifyOrder(orderType string, coinType, orderID int, price, amount float64) {
values := url.Values{}
values.Set("coin_type", strconv.Itoa(coinType))
values.Set("id", strconv.Itoa(orderID))
values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
values.Set("price", strconv.FormatFloat(price, 'f', -1, 64))
err := h.SendAuthenticatedRequest("modify_order", values)
if err != nil {
log.Println(err)
// GetTimestamp returns the Huobi server time
func (h *HUOBI) GetTimestamp() (int64, error) {
type response struct {
Response
Timestamp int64 `json:"data"`
}
var result response
url := fmt.Sprintf("%s/v%s/%s", huobiAPIURL, huobiAPIVersion, huobiTimestamp)
err := common.SendHTTPGetRequest(url, true, h.Verbose, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
}
return result.Timestamp, err
}
// GetNewDealOrders creates a new deal
func (h *HUOBI) GetNewDealOrders(coinType int) {
values := url.Values{}
values.Set("coin_type", strconv.Itoa(coinType))
err := h.SendAuthenticatedRequest("get_new_deal_orders", values)
if err != nil {
log.Println(err)
// GetAccounts returns the Huobi user accounts
func (h *HUOBI) GetAccounts() ([]Account, error) {
type response struct {
Response
AccountData []Account `json:"data"`
}
var result response
err := h.SendAuthenticatedHTTPRequest("GET", huobiAccounts, url.Values{}, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.AccountData, err
}
// GetOrderIDByTradeID returns ORDERID by Trade ID
func (h *HUOBI) GetOrderIDByTradeID(coinType, orderID int) {
values := url.Values{}
values.Set("coin_type", strconv.Itoa(coinType))
values.Set("trade_id", strconv.Itoa(orderID))
err := h.SendAuthenticatedRequest("get_order_id_by_trade_id", values)
if err != nil {
log.Println(err)
// GetAccountBalance returns the users Huobi account balance
func (h *HUOBI) GetAccountBalance(accountID string) ([]AccountBalance, error) {
type response struct {
Response
AccountData []AccountBalance `json:"list"`
}
var result response
endpoint := fmt.Sprintf(huobiAccountBalance, accountID)
err := h.SendAuthenticatedHTTPRequest("GET", endpoint, url.Values{}, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.AccountData, err
}
// SendAuthenticatedRequest sends an autheticated HTTP request to Huobi
func (h *HUOBI) SendAuthenticatedRequest(method string, v url.Values) error {
// PlaceOrder submits an order to Huobi
func (h *HUOBI) PlaceOrder(symbol, source, accountID, orderType string, amount, price float64) (int64, error) {
vals := url.Values{}
vals.Set("account-id", accountID)
vals.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
// Only set price if order type is not equal to buy-market or sell-market
if orderType != "buy-market" && orderType != "sell-market" {
vals.Set("price", strconv.FormatFloat(price, 'f', -1, 64))
}
if source != "" {
vals.Set("source", source)
}
vals.Set("symbol", symbol)
vals.Set("type", orderType)
type response struct {
Response
OrderID int64 `json:"data,string"`
}
var result response
err := h.SendAuthenticatedHTTPRequest("POST", huobiOrderPlace, vals, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
}
return result.OrderID, err
}
// CancelOrder cancels an order on Huobi
func (h *HUOBI) CancelOrder(orderID int64) (int64, error) {
type response struct {
Response
OrderID int64 `json:"data,string"`
}
var result response
endpoint := fmt.Sprintf(huobiOrderCancel, strconv.FormatInt(orderID, 10))
err := h.SendAuthenticatedHTTPRequest("POST", endpoint, url.Values{}, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
}
return result.OrderID, err
}
// CancelOrderBatch cancels a batch of orders -- to-do
func (h *HUOBI) CancelOrderBatch(orderIDs []int64) ([]CancelOrderBatch, error) {
type response struct {
Response
Data []CancelOrderBatch `json:"data"`
}
var result response
err := h.SendAuthenticatedHTTPRequest("POST", huobiOrderCancelBatch, url.Values{}, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.Data, err
}
// GetOrder returns order information for the specified order
func (h *HUOBI) GetOrder(orderID int64) (OrderInfo, error) {
type response struct {
Response
Order OrderInfo `json:"data"`
}
var result response
endpoint := fmt.Sprintf(huobiGetOrder, strconv.FormatInt(orderID, 10))
err := h.SendAuthenticatedHTTPRequest("GET", endpoint, url.Values{}, &result)
if result.ErrorMessage != "" {
return result.Order, errors.New(result.ErrorMessage)
}
return result.Order, err
}
// GetOrderMatchResults returns matched order info for the specified order
func (h *HUOBI) GetOrderMatchResults(orderID int64) ([]OrderMatchInfo, error) {
type response struct {
Response
Orders []OrderMatchInfo `json:"data"`
}
var result response
endpoint := fmt.Sprintf(huobiGetOrderMatch, strconv.FormatInt(orderID, 10))
err := h.SendAuthenticatedHTTPRequest("GET", endpoint, url.Values{}, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.Orders, err
}
// GetOrders returns a list of orders
func (h *HUOBI) GetOrders(symbol, types, start, end, states, from, direct, size string) ([]OrderInfo, error) {
type response struct {
Response
Orders []OrderInfo `json:"data"`
}
vals := url.Values{}
vals.Set("symbol", symbol)
vals.Set("states", states)
if types != "" {
vals.Set("types", types)
}
if start != "" {
vals.Set("start-date", start)
}
if end != "" {
vals.Set("end-date", end)
}
if from != "" {
vals.Set("from", from)
}
if direct != "" {
vals.Set("direct", direct)
}
if size != "" {
vals.Set("size", size)
}
var result response
err := h.SendAuthenticatedHTTPRequest("GET", huobiGetOrders, vals, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.Orders, err
}
// GetOrdersMatch returns a list of matched orders
func (h *HUOBI) GetOrdersMatch(symbol, types, start, end, from, direct, size string) ([]OrderMatchInfo, error) {
type response struct {
Response
Orders []OrderMatchInfo `json:"data"`
}
vals := url.Values{}
vals.Set("symbol", symbol)
if types != "" {
vals.Set("types", types)
}
if start != "" {
vals.Set("start-date", start)
}
if end != "" {
vals.Set("end-date", end)
}
if from != "" {
vals.Set("from", from)
}
if direct != "" {
vals.Set("direct", direct)
}
if size != "" {
vals.Set("size", size)
}
var result response
err := h.SendAuthenticatedHTTPRequest("GET", huobiGetOrdersMatch, vals, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.Orders, err
}
// MarginTransfer transfers assets into or out of the margin account
func (h *HUOBI) MarginTransfer(symbol, currency string, amount float64, in bool) (int64, error) {
vals := url.Values{}
vals.Set("symbol", symbol)
vals.Set("currency", currency)
vals.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
path := huobiMarginTransferIn
if !in {
path = huobiMarginTransferOut
}
type response struct {
Response
TransferID int64 `json:"data"`
}
var result response
err := h.SendAuthenticatedHTTPRequest("POST", path, vals, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
}
return result.TransferID, err
}
// MarginOrder submits a margin order application
func (h *HUOBI) MarginOrder(symbol, currency string, amount float64) (int64, error) {
vals := url.Values{}
vals.Set("symbol", symbol)
vals.Set("currency", currency)
vals.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
type response struct {
Response
MarginOrderID int64 `json:"data"`
}
var result response
err := h.SendAuthenticatedHTTPRequest("POST", huobiMarginOrders, vals, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
}
return result.MarginOrderID, err
}
// MarginRepayment repays a margin amount for a margin ID
func (h *HUOBI) MarginRepayment(orderID int64, amount float64) (int64, error) {
vals := url.Values{}
vals.Set("order-id", strconv.FormatInt(orderID, 10))
vals.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
type response struct {
Response
MarginOrderID int64 `json:"data"`
}
var result response
endpoint := fmt.Sprintf(huobiMarginRepay, strconv.FormatInt(orderID, 10))
err := h.SendAuthenticatedHTTPRequest("POST", endpoint, vals, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
}
return result.MarginOrderID, err
}
// GetMarginLoanOrders returns the margin loan orders
func (h *HUOBI) GetMarginLoanOrders(symbol, currency, start, end, states, from, direct, size string) ([]MarginOrder, error) {
vals := url.Values{}
vals.Set("symbol", symbol)
vals.Set("currency", currency)
if start != "" {
vals.Set("start-date", start)
}
if end != "" {
vals.Set("end-date", end)
}
if states != "" {
vals.Set("states", states)
}
if from != "" {
vals.Set("from", from)
}
if direct != "" {
vals.Set("direct", direct)
}
if size != "" {
vals.Set("size", size)
}
type response struct {
Response
MarginLoanOrders []MarginOrder `json:"data"`
}
var result response
err := h.SendAuthenticatedHTTPRequest("GET", huobiMarginLoanOrders, vals, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.MarginLoanOrders, err
}
// GetMarginAccountBalance returns the margin account balances
func (h *HUOBI) GetMarginAccountBalance(symbol string) ([]MarginAccountBalance, error) {
type response struct {
Response
Balances []MarginAccountBalance `json:"data"`
}
vals := url.Values{}
if symbol != "" {
vals.Set("symbol", symbol)
}
var result response
err := h.SendAuthenticatedHTTPRequest("GET", huobiMarginAccountBalance, vals, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.Balances, err
}
// Withdraw withdraws the desired amount and currency
func (h *HUOBI) Withdraw(address, currency, addrTag string, amount, fee float64) (int64, error) {
type response struct {
Response
WithdrawID int64 `json:"data"`
}
vals := url.Values{}
vals.Set("address", address)
vals.Set("currency", currency)
vals.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
if fee != 0 {
vals.Set("fee", strconv.FormatFloat(fee, 'f', -1, 64))
}
if currency == "XRP" {
vals.Set("addr-tag", addrTag)
}
var result response
err := h.SendAuthenticatedHTTPRequest("POST", huobiWithdrawCreate, vals, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
}
return result.WithdrawID, err
}
// CancelWithdraw cancels a withdraw request
func (h *HUOBI) CancelWithdraw(withdrawID int64) (int64, error) {
type response struct {
Response
WithdrawID int64 `json:"data"`
}
vals := url.Values{}
vals.Set("withdraw-id", strconv.FormatInt(withdrawID, 10))
var result response
endpoint := fmt.Sprintf(huobiWithdrawCancel, strconv.FormatInt(withdrawID, 10))
err := h.SendAuthenticatedHTTPRequest("POST", endpoint, vals, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
}
return result.WithdrawID, err
}
// SendAuthenticatedHTTPRequest sends authenticated requests to the HUOBI API
func (h *HUOBI) SendAuthenticatedHTTPRequest(method, endpoint string, values url.Values, result interface{}) error {
if !h.AuthenticatedAPISupport {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, h.Name)
}
v.Set("access_key", h.APIKey)
v.Set("created", strconv.FormatInt(time.Now().Unix(), 10))
v.Set("method", method)
hash := common.GetMD5([]byte(v.Encode() + "&secret_key=" + h.APISecret))
v.Set("sign", common.StringToLower(common.HexEncodeToString(hash)))
encoded := v.Encode()
values.Set("AccessKeyId", h.APIKey)
values.Set("SignatureMethod", "HmacSHA256")
values.Set("SignatureVersion", "2")
values.Set("Timestamp", time.Now().UTC().Format("2006-01-02T15:04:05"))
if h.Verbose {
log.Printf("Sending POST request to %s with params %s\n", huobiAPIURL, encoded)
}
endpoint = fmt.Sprintf("/v%s/%s", huobiAPIVersion, endpoint)
payload := fmt.Sprintf("%s\napi.huobi.pro\n%s\n%s",
method, endpoint, values.Encode())
headers := make(map[string]string)
headers["Content-Type"] = "application/x-www-form-urlencoded"
resp, err := common.SendHTTPRequest("POST", huobiAPIURL, headers, strings.NewReader(encoded))
hmac := common.GetHMAC(common.HashSHA256, []byte(payload), []byte(h.APISecret))
values.Set("Signature", common.Base64Encode(hmac))
url := fmt.Sprintf("%s%s", huobiAPIURL, endpoint)
url = common.EncodeURLValues(url, values)
resp, err := common.SendHTTPRequest(method, url, headers, bytes.NewBufferString(""))
if err != nil {
return err
}
if h.Verbose {
log.Printf("Received raw: %s\n", resp)
err = common.JSONDecode([]byte(resp), &result)
if err != nil {
return errors.New("unable to JSON Unmarshal response")
}
return nil

View File

@@ -1,6 +1,7 @@
package huobi
import (
"strconv"
"testing"
"github.com/thrasher-/gocryptotrader/config"
@@ -37,14 +38,216 @@ func TestSetup(t *testing.T) {
func TestGetFee(t *testing.T) {
t.Parallel()
if h.GetFee() != 0 {
t.Error("test failed - Huobi GetFee() error")
t.Errorf("test failed - Huobi GetFee() error")
}
}
func TestGetTicker(t *testing.T) {
func TestGetKline(t *testing.T) {
t.Parallel()
_, err := h.GetTicker("btcusd")
if err == nil {
t.Error("test failed - Huobi GetTicker() error", err)
_, err := h.GetKline("btcusdt", "1week", "")
if err != nil {
t.Errorf("Test failed - Huobi TestGetKline: %s", err)
}
}
func TestGetMarketDetailMerged(t *testing.T) {
t.Parallel()
_, err := h.GetMarketDetailMerged("btcusdt")
if err != nil {
t.Errorf("Test failed - Huobi TestGetMarketDetailMerged: %s", err)
}
}
func TestGetDepth(t *testing.T) {
t.Parallel()
_, err := h.GetDepth("btcusdt", "step1")
if err != nil {
t.Errorf("Test failed - Huobi TestGetDepth: %s", err)
}
}
func TestGetTrades(t *testing.T) {
t.Parallel()
_, err := h.GetTrades("btcusdt")
if err != nil {
t.Errorf("Test failed - Huobi TestGetTrades: %s", err)
}
}
func TestGetTradeHistory(t *testing.T) {
t.Parallel()
_, err := h.GetTradeHistory("btcusdt", "50")
if err != nil {
t.Errorf("Test failed - Huobi TestGetTradeHistory: %s", err)
}
}
func TestGetMarketDetail(t *testing.T) {
t.Parallel()
_, err := h.GetMarketDetail("btcusdt")
if err != nil {
t.Errorf("Test failed - Huobi TestGetTradeHistory: %s", err)
}
}
func TestGetSymbols(t *testing.T) {
t.Parallel()
_, err := h.GetSymbols()
if err != nil {
t.Errorf("Test failed - Huobi TestGetSymbols: %s", err)
}
}
func TestGetCurrencies(t *testing.T) {
t.Parallel()
_, err := h.GetCurrencies()
if err != nil {
t.Errorf("Test failed - Huobi TestGetCurrencies: %s", err)
}
}
func TestGetTimestamp(t *testing.T) {
t.Parallel()
_, err := h.GetTimestamp()
if err != nil {
t.Errorf("Test failed - Huobi TestGetTimestamp: %s", err)
}
}
func TestGetAccounts(t *testing.T) {
t.Parallel()
if apiKey == "" && apiSecret == "" {
t.Skip()
}
h.APIKey = apiKey
h.APISecret = apiSecret
h.AuthenticatedAPISupport = true
_, err := h.GetAccounts()
if err != nil {
t.Errorf("Test failed - Huobi GetAccounts: %s", err)
}
}
func TestGetAccountBalance(t *testing.T) {
t.Parallel()
if apiKey == "" && apiSecret == "" {
t.Skip()
}
h.APIKey = apiKey
h.APISecret = apiSecret
h.AuthenticatedAPISupport = true
result, err := h.GetAccounts()
if err != nil {
t.Errorf("Test failed - Huobi GetAccounts: %s", err)
}
userID := strconv.FormatInt(result[0].ID, 10)
_, err = h.GetAccountBalance(userID)
if err != nil {
t.Errorf("Test failed - Huobi GetAccountBalance: %s", err)
}
}
func TestPlaceOrder(t *testing.T) {
t.Parallel()
if apiKey == "" && apiSecret == "" {
t.Skip()
}
h.APIKey = apiKey
h.APISecret = apiSecret
h.AuthenticatedAPISupport = true
_, err := h.GetAccounts()
if err != nil {
t.Errorf("Test failed - Huobi GetAccounts: %s", err)
}
/*
userID := strconv.FormatInt(result[0].ID, 10)
_, err = h.PlaceOrder("ethusdt", "api", userID, "buy-limit", 10.1, 100.1)
if err != nil {
t.Errorf("Test failed - Huobi TestPlaceOrder: %s", err)
}
*/
}
func TestCancelOrder(t *testing.T) {
t.Parallel()
if apiKey == "" && apiSecret == "" {
t.Skip()
}
h.APIKey = apiKey
h.APISecret = apiSecret
h.AuthenticatedAPISupport = true
_, err := h.CancelOrder(1337)
if err == nil {
t.Error("Test failed - Huobi TestCancelOrder: Invalid orderID returned true")
}
}
func TestGetOrder(t *testing.T) {
t.Parallel()
if apiKey == "" && apiSecret == "" {
t.Skip()
}
h.APIKey = apiKey
h.APISecret = apiSecret
h.AuthenticatedAPISupport = true
_, err := h.GetOrder(1337)
if err == nil {
t.Error("Test failed - Huobi TestCancelOrder: Invalid orderID returned true")
}
}
func TestGetMarginLoanOrders(t *testing.T) {
t.Parallel()
if apiKey == "" && apiSecret == "" {
t.Skip()
}
h.APIKey = apiKey
h.APISecret = apiSecret
h.AuthenticatedAPISupport = true
_, err := h.GetMarginLoanOrders("btcusdt", "", "", "", "", "", "", "")
if err != nil {
t.Errorf("Test failed - Huobi TestGetMarginLoanOrders: %s", err)
}
}
func TestGetMarginAccountBalance(t *testing.T) {
t.Parallel()
if apiKey == "" && apiSecret == "" {
t.Skip()
}
h.APIKey = apiKey
h.APISecret = apiSecret
h.AuthenticatedAPISupport = true
_, err := h.GetMarginAccountBalance("btcusdt")
if err != nil {
t.Errorf("Test failed - Huobi TestGetMarginAccountBalance: %s", err)
}
}
func TestCancelWithdraw(t *testing.T) {
t.Parallel()
if apiKey == "" && apiSecret == "" {
t.Skip()
}
h.APIKey = apiKey
h.APISecret = apiSecret
h.AuthenticatedAPISupport = true
_, err := h.CancelWithdraw(1337)
if err == nil {
t.Error("Test failed - Huobi TestCancelWithdraw: Invalid withdraw-ID was valid")
}
}

View File

@@ -1,26 +1,171 @@
package huobi
// Ticker holds ticker information
type Ticker struct {
High float64
Low float64
Last float64
Vol float64
Buy float64
Sell float64
// Response stores the Huobi response information
type Response struct {
Status string `json:"status"`
Channel string `json:"ch"`
Timestamp int64 `json:"ts"`
ErrorCode string `json:"err-code"`
ErrorMessage string `json:"err-msg"`
}
// TickerResponse holds the initial response type
type TickerResponse struct {
Time string
Ticker Ticker
// KlineItem stores a kline item
type KlineItem struct {
ID int `json:"id"`
Open float64 `json:"open"`
Close float64 `json:"close"`
Low float64 `json:"low"`
High float64 `json:"high"`
Amount float64 `json:"amount"`
Vol float64 `json:"vol"`
Count int `json:"count"`
}
// Orderbook holds the order book information
// Klines stores tan array of kline items
type Klines struct {
Klines []KlineItem `json:"data"`
}
// DetailMerged stores the ticker detail merged data
type DetailMerged struct {
Detail
Version int `json:"version"`
Ask []float64 `json:"ask"`
Bid []float64 `json:"bid"`
}
// Orderbook stores the orderbook data
type Orderbook struct {
ID float64
TS float64
Bids [][]float64 `json:"bids"`
Asks [][]float64 `json:"asks"`
Symbol string `json:"string"`
ID int64 `json:"id"`
Timetstamp int64 `json:"ts"`
Bids [][]float64 `json:"bids"`
Asks [][]float64 `json:"asks"`
}
// Trade stores the trade data
type Trade struct {
ID float64 `json:"id"`
Price float64 `json:"price"`
Amount float64 `json:"amount"`
Direction string `json:"direction"`
Timestamp int64 `json:"ts"`
}
// TradeHistory stores the the trade history data
type TradeHistory struct {
ID int64 `json:"id"`
Timestamp int64 `json:"ts"`
Trades []Trade `json:"data"`
}
// Detail stores the ticker detail data
type Detail struct {
Amount float64 `json:"amount"`
Open float64 `json:"open"`
Close float64 `json:"close"`
High float64 `json:"high"`
Timestamp int64 `json:"id"`
ID int `json:"id"`
Count int `json:"count"`
Low float64 `json:"low"`
Volume float64 `json:"vol"`
}
// Symbol stores the symbol data
type Symbol struct {
BaseCurrency string `json:"base-currency"`
QuoteCurrency string `json:"quote-currency"`
PricePrecision int `json:"price-precision"`
AmountPrecision int `json:"amount-precision"`
SymbolPartition string `json:"symbol-partition"`
}
// Account stores the account data
type Account struct {
ID int64 `json:"id"`
Type string `json:"type"`
State string `json:"working"`
UserID int64 `json:"user-id"`
}
// AccountBalance stores the user account balance
type AccountBalance struct {
Currency string `json:"currency"`
Type string `json:"type"`
Balance float64 `json:"balance,string"`
}
// CancelOrderBatch stores the cancel order batch data
type CancelOrderBatch struct {
Success []string `json:"success"`
Failed []struct {
OrderID int64 `json:"order-id,string"`
ErrorCode string `json:"err-code"`
ErrorMessage string `json:"err-msg"`
} `json:"failed"`
}
// OrderInfo stores the order info
type OrderInfo struct {
ID int `json:"id"`
Symbol string `json:"symbol"`
AccountID int `json:"account-id"`
Amount string `json:"amount"`
Price string `json:"price"`
CreatedAt int64 `json:"created-at"`
Type string `json:"type"`
FieldAmount string `json:"field-amount"`
FieldCashAmount string `json:"field-cash-amount"`
FieldFees string `json:"field-fees"`
FinishedAt int64 `json:"finished-at"`
UserID int `json:"user-id"`
Source string `json:"source"`
State string `json:"state"`
CanceledAt int `json:"canceled-at"`
Exchange string `json:"exchange"`
Batch string `json:"batch"`
}
// OrderMatchInfo stores the order match info
type OrderMatchInfo struct {
ID int `json:"id"`
OrderID int `json:"order-id"`
MatchID int `json:"match-id"`
Symbol string `json:"symbol"`
Type string `json:"type"`
Source string `json:"source"`
Price string `json:"price"`
FilledAmount string `json:"filled-amount"`
FilledFees string `json:"filled-fees"`
CreatedAt int64 `json:"created-at"`
}
// MarginOrder stores the margin order info
type MarginOrder struct {
Currency string `json:"currency"`
Symbol string `json:"symbol"`
AccruedAt int64 `json:"accrued-at"`
LoanAmount string `json:"loan-amount"`
LoanBalance string `json:"loan-balance"`
InterestBalance string `json:"interest-balance"`
CreatedAt int64 `json:"created-at"`
InterestAmount string `json:"interest-amount"`
InterestRate string `json:"interest-rate"`
AccountID int `json:"account-id"`
UserID int `json:"user-id"`
UpdatedAt int64 `json:"updated-at"`
ID int `json:"id"`
State string `json:"state"`
}
// MarginAccountBalance stores the margin account balance info
type MarginAccountBalance struct {
ID int `json:"id"`
Type string `json:"type"`
State string `json:"state"`
Symbol string `json:"symbol"`
FlPrice string `json:"fl-price"`
FlType string `json:"fl-type"`
RiskRate string `json:"risk-rate"`
List []AccountBalance `json:"list"`
}

View File

@@ -4,6 +4,7 @@ import (
"log"
"github.com/thrasher-/gocryptotrader/common"
"github.com/thrasher-/gocryptotrader/config"
"github.com/thrasher-/gocryptotrader/currency/pair"
"github.com/thrasher-/gocryptotrader/exchanges"
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
@@ -26,22 +27,70 @@ func (h *HUOBI) Run() {
if h.Websocket {
go h.WebsocketClient()
}
exchangeProducts, err := h.GetSymbols()
if err != nil {
log.Printf("%s Failed to get available symbols.\n", h.GetName())
} else {
forceUpgrade := false
if common.DataContains(h.EnabledPairs, "CNY") || common.DataContains(h.AvailablePairs, "CNY") {
forceUpgrade = true
}
if common.DataContains(h.BaseCurrencies, "CNY") {
cfg := config.GetConfig()
exchCfg, err := cfg.GetExchangeConfig(h.Name)
if err != nil {
log.Printf("%s failed to get exchange config. %s\n", h.Name, err)
return
}
exchCfg.BaseCurrencies = "USD"
h.BaseCurrencies = []string{"USD"}
err = cfg.UpdateExchangeConfig(exchCfg)
if err != nil {
log.Printf("%s failed to update config. %s\n", h.Name, err)
return
}
}
var currencies []string
for x := range exchangeProducts {
newCurrency := exchangeProducts[x].BaseCurrency + "-" + exchangeProducts[x].QuoteCurrency
currencies = append(currencies, newCurrency)
}
if forceUpgrade {
enabledPairs := []string{"btc-usdt"}
log.Println("WARNING: Available and enabled pairs for Huobi reset due to config upgrade, please enable the ones you would like again")
err = h.UpdateEnabledCurrencies(enabledPairs, true)
if err != nil {
log.Printf("%s Failed to update enabled currencies.\n", h.GetName())
}
}
err = h.UpdateAvailableCurrencies(currencies, forceUpgrade)
if err != nil {
log.Printf("%s Failed to update available currencies.\n", h.GetName())
}
}
}
// UpdateTicker updates and returns the ticker for a currency pair
func (h *HUOBI) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
var tickerPrice ticker.Price
tick, err := h.GetTicker(p.GetFirstCurrency().Lower().String())
tick, err := h.GetMarketDetailMerged(exchange.FormatExchangeCurrency(h.Name, p).String())
if err != nil {
return tickerPrice, err
}
tickerPrice.Pair = p
tickerPrice.Ask = tick.Sell
tickerPrice.Bid = tick.Buy
tickerPrice.Low = tick.Low
tickerPrice.Last = tick.Last
tickerPrice.Volume = tick.Vol
tickerPrice.Last = tick.Close
tickerPrice.Volume = tick.Volume
tickerPrice.High = tick.High
tickerPrice.Ask = tick.Ask[0]
tickerPrice.Bid = tick.Bid[0]
ticker.ProcessTicker(h.GetName(), p, tickerPrice, assetType)
return ticker.GetTicker(h.Name, p, assetType)
}
@@ -67,7 +116,7 @@ func (h *HUOBI) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook
// UpdateOrderbook updates and returns the orderbook for a currency pair
func (h *HUOBI) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
var orderBook orderbook.Base
orderbookNew, err := h.GetOrderBook(p.GetFirstCurrency().Lower().String())
orderbookNew, err := h.GetDepth(exchange.FormatExchangeCurrency(h.Name, p).String(), "step1")
if err != nil {
return orderBook, err
}