Fix Huobi's POST requests (#186)

* Fix Huobi's POST request content-type

If we try to send a POST request to Huobi w/ a content-type of
"application/x-www-form-urlencoded" we will receive an error:

Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported

Their english documentation is incorrect, it does not specify this atm,
translating their chinese documentation, it states:

"Content-Type: application/json must be declared in the POST request header;
Content-Type: application/x-www-form-urlencoded must be declared in the GET
request header. (Chinese users recommend setting Accept-Language: zh-cn)"

* Fix Huobi's place new order request (send as json)

We should not send the order details through url parameters, this
needs to be sent as a json payload via the request body.

Documentation:

https://github.com/huobiapi/API_Docs_en/wiki/REST_Reference#post-v1orderordersplace--make-an-order-in-huobipro

* Fix Huobi's margin transfer/margin order/withdraw requests

This requests data need to be sent as json, not as a query string.

Docs:

https://github.com/huobiapi/API_Docs_en/wiki/REST_Reference#post-v1dwtransfer-inmargin--transfer-asset-from-spot-account-to-margin-account

https://github.com/huobiapi/API_Docs_en/wiki/REST_Reference#post-v1marginorders--margin-application

https://github.com/huobiapi/API_Docs_en/wiki/REST_Reference#post-v1dwwithdrawapicreate---create-a-withdraw-application

* Fix Huobi's margin repayment request

This request data needs to be sent a json, not via the query string.

Also note, that the "order-id" parameter is already sent through the
url path, it does not need to be included in the request body.
This commit is contained in:
herenow
2018-10-02 21:58:32 -03:00
committed by Adrian Gallagher
parent 7186ac2602
commit 604443fbbb

View File

@@ -5,11 +5,13 @@ import (
"crypto/ecdsa"
"crypto/rand"
"crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"strconv"
"strings"
@@ -338,7 +340,7 @@ func (h *HUOBI) GetAccounts() ([]Account, error) {
}
var result response
err := h.SendAuthenticatedHTTPRequest("GET", huobiAccounts, url.Values{}, &result)
err := h.SendAuthenticatedHTTPRequest("GET", huobiAccounts, url.Values{}, nil, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
@@ -355,7 +357,7 @@ func (h *HUOBI) GetAccountBalance(accountID string) ([]AccountBalanceDetail, err
var result response
endpoint := fmt.Sprintf(huobiAccountBalance, accountID)
err := h.SendAuthenticatedHTTPRequest("GET", endpoint, url.Values{}, &result)
err := h.SendAuthenticatedHTTPRequest("GET", endpoint, url.Values{}, nil, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
@@ -365,29 +367,36 @@ func (h *HUOBI) GetAccountBalance(accountID string) ([]AccountBalanceDetail, err
// SpotNewOrder submits an order to Huobi
func (h *HUOBI) SpotNewOrder(arg SpotNewOrderRequestParams) (int64, error) {
vals := url.Values{}
vals.Set("account-id", fmt.Sprintf("%d", arg.AccountID))
vals.Set("amount", strconv.FormatFloat(arg.Amount, 'f', -1, 64))
data := struct {
AccountID int `json:"account-id,string"`
Amount string `json:"amount"`
Price string `json:"price"`
Source string `json:"source"`
Symbol string `json:"symbol"`
Type string `json:"type"`
}{
AccountID: arg.AccountID,
Amount: strconv.FormatFloat(arg.Amount, 'f', -1, 64),
Symbol: arg.Symbol,
Type: string(arg.Type),
}
// Only set price if order type is not equal to buy-market or sell-market
if arg.Type != SpotNewOrderRequestTypeBuyMarket && arg.Type != SpotNewOrderRequestTypeSellMarket {
vals.Set("price", strconv.FormatFloat(arg.Price, 'f', -1, 64))
data.Price = strconv.FormatFloat(arg.Price, 'f', -1, 64)
}
if arg.Source != "" {
vals.Set("source", arg.Source)
data.Source = arg.Source
}
vals.Set("symbol", arg.Symbol)
vals.Set("type", string(arg.Type))
type response struct {
Response
OrderID int64 `json:"data,string"`
}
var result response
err := h.SendAuthenticatedHTTPRequest("POST", huobiOrderPlace, vals, &result)
err := h.SendAuthenticatedHTTPRequest("POST", huobiOrderPlace, nil, data, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
@@ -404,7 +413,7 @@ func (h *HUOBI) CancelOrder(orderID int64) (int64, error) {
var result response
endpoint := fmt.Sprintf(huobiOrderCancel, strconv.FormatInt(orderID, 10))
err := h.SendAuthenticatedHTTPRequest("POST", endpoint, url.Values{}, &result)
err := h.SendAuthenticatedHTTPRequest("POST", endpoint, url.Values{}, nil, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
@@ -420,7 +429,7 @@ func (h *HUOBI) CancelOrderBatch(orderIDs []int64) ([]CancelOrderBatch, error) {
}
var result response
err := h.SendAuthenticatedHTTPRequest("POST", huobiOrderCancelBatch, url.Values{}, &result)
err := h.SendAuthenticatedHTTPRequest("POST", huobiOrderCancelBatch, url.Values{}, nil, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
@@ -437,7 +446,7 @@ func (h *HUOBI) GetOrder(orderID int64) (OrderInfo, error) {
var result response
endpoint := fmt.Sprintf(huobiGetOrder, strconv.FormatInt(orderID, 10))
err := h.SendAuthenticatedHTTPRequest("GET", endpoint, url.Values{}, &result)
err := h.SendAuthenticatedHTTPRequest("GET", endpoint, url.Values{}, nil, &result)
if result.ErrorMessage != "" {
return result.Order, errors.New(result.ErrorMessage)
@@ -454,7 +463,7 @@ func (h *HUOBI) GetOrderMatchResults(orderID int64) ([]OrderMatchInfo, error) {
var result response
endpoint := fmt.Sprintf(huobiGetOrderMatch, strconv.FormatInt(orderID, 10))
err := h.SendAuthenticatedHTTPRequest("GET", endpoint, url.Values{}, &result)
err := h.SendAuthenticatedHTTPRequest("GET", endpoint, url.Values{}, nil, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
@@ -498,7 +507,7 @@ func (h *HUOBI) GetOrders(symbol, types, start, end, states, from, direct, size
}
var result response
err := h.SendAuthenticatedHTTPRequest("GET", huobiGetOrders, vals, &result)
err := h.SendAuthenticatedHTTPRequest("GET", huobiGetOrders, vals, nil, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
@@ -541,7 +550,7 @@ func (h *HUOBI) GetOrdersMatch(symbol, types, start, end, from, direct, size str
}
var result response
err := h.SendAuthenticatedHTTPRequest("GET", huobiGetOrdersMatch, vals, &result)
err := h.SendAuthenticatedHTTPRequest("GET", huobiGetOrdersMatch, vals, nil, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
@@ -551,10 +560,15 @@ func (h *HUOBI) GetOrdersMatch(symbol, types, start, end, from, direct, size str
// 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))
data := struct {
Symbol string `json:"symbol"`
Currency string `json:"currency"`
Amount string `json:"amount"`
}{
Symbol: symbol,
Currency: currency,
Amount: strconv.FormatFloat(amount, 'f', -1, 64),
}
path := huobiMarginTransferIn
if !in {
@@ -567,7 +581,7 @@ func (h *HUOBI) MarginTransfer(symbol, currency string, amount float64, in bool)
}
var result response
err := h.SendAuthenticatedHTTPRequest("POST", path, vals, &result)
err := h.SendAuthenticatedHTTPRequest("POST", path, nil, data, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
@@ -577,10 +591,15 @@ func (h *HUOBI) MarginTransfer(symbol, currency string, amount float64, in bool)
// 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))
data := struct {
Symbol string `json:"symbol"`
Currency string `json:"currency"`
Amount string `json:"amount"`
}{
Symbol: symbol,
Currency: currency,
Amount: strconv.FormatFloat(amount, 'f', -1, 64),
}
type response struct {
Response
@@ -588,7 +607,7 @@ func (h *HUOBI) MarginOrder(symbol, currency string, amount float64) (int64, err
}
var result response
err := h.SendAuthenticatedHTTPRequest("POST", huobiMarginOrders, vals, &result)
err := h.SendAuthenticatedHTTPRequest("POST", huobiMarginOrders, nil, data, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
@@ -598,9 +617,11 @@ func (h *HUOBI) MarginOrder(symbol, currency string, amount float64) (int64, 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))
data := struct {
Amount string `json:"amount"`
}{
Amount: strconv.FormatFloat(amount, 'f', -1, 64),
}
type response struct {
Response
@@ -609,7 +630,7 @@ func (h *HUOBI) MarginRepayment(orderID int64, amount float64) (int64, error) {
var result response
endpoint := fmt.Sprintf(huobiMarginRepay, strconv.FormatInt(orderID, 10))
err := h.SendAuthenticatedHTTPRequest("POST", endpoint, vals, &result)
err := h.SendAuthenticatedHTTPRequest("POST", endpoint, nil, data, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
@@ -653,7 +674,7 @@ func (h *HUOBI) GetMarginLoanOrders(symbol, currency, start, end, states, from,
}
var result response
err := h.SendAuthenticatedHTTPRequest("GET", huobiMarginLoanOrders, vals, &result)
err := h.SendAuthenticatedHTTPRequest("GET", huobiMarginLoanOrders, vals, nil, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
@@ -674,7 +695,7 @@ func (h *HUOBI) GetMarginAccountBalance(symbol string) ([]MarginAccountBalance,
}
var result response
err := h.SendAuthenticatedHTTPRequest("GET", huobiMarginAccountBalance, vals, &result)
err := h.SendAuthenticatedHTTPRequest("GET", huobiMarginAccountBalance, vals, nil, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
@@ -689,21 +710,28 @@ func (h *HUOBI) Withdraw(address, currency, addrTag string, amount, fee float64)
WithdrawID int64 `json:"data"`
}
vals := url.Values{}
vals.Set("address", address)
vals.Set("currency", currency)
vals.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
data := struct {
Address string `json:"address"`
Amount string `json:"amount"`
Currency string `json:"currency"`
Fee string `json:"fee"`
AddrTag string `json:"addr-tag"`
}{
Address: address,
Currency: currency,
Amount: strconv.FormatFloat(amount, 'f', -1, 64),
}
if fee != 0 {
vals.Set("fee", strconv.FormatFloat(fee, 'f', -1, 64))
data.Fee = strconv.FormatFloat(fee, 'f', -1, 64)
}
if currency == "XRP" {
vals.Set("addr-tag", addrTag)
data.AddrTag = addrTag
}
var result response
err := h.SendAuthenticatedHTTPRequest("POST", huobiWithdrawCreate, vals, &result)
err := h.SendAuthenticatedHTTPRequest("POST", huobiWithdrawCreate, nil, data, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
@@ -723,7 +751,7 @@ func (h *HUOBI) CancelWithdraw(withdrawID int64) (int64, error) {
var result response
endpoint := fmt.Sprintf(huobiWithdrawCancel, strconv.FormatInt(withdrawID, 10))
err := h.SendAuthenticatedHTTPRequest("POST", endpoint, vals, &result)
err := h.SendAuthenticatedHTTPRequest("POST", endpoint, vals, nil, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
@@ -737,11 +765,15 @@ func (h *HUOBI) SendHTTPRequest(path string, result interface{}) error {
}
// SendAuthenticatedHTTPRequest sends authenticated requests to the HUOBI API
func (h *HUOBI) SendAuthenticatedHTTPRequest(method, endpoint string, values url.Values, result interface{}) error {
func (h *HUOBI) SendAuthenticatedHTTPRequest(method, endpoint string, values url.Values, data interface{}, result interface{}) error {
if !h.AuthenticatedAPISupport {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, h.Name)
}
if values == nil {
values = url.Values{}
}
values.Set("AccessKeyId", h.APIKey)
values.Set("SignatureMethod", "HmacSHA256")
values.Set("SignatureVersion", "2")
@@ -752,7 +784,12 @@ func (h *HUOBI) SendAuthenticatedHTTPRequest(method, endpoint string, values url
method, endpoint, values.Encode())
headers := make(map[string]string)
headers["Content-Type"] = "application/x-www-form-urlencoded"
if method == http.MethodGet {
headers["Content-Type"] = "application/x-www-form-urlencoded"
} else {
headers["Content-Type"] = "application/json"
}
hmac := common.GetHMAC(common.HashSHA256, []byte(payload), []byte(h.APISecret))
signature := common.Base64Encode(hmac)
@@ -789,5 +826,16 @@ func (h *HUOBI) SendAuthenticatedHTTPRequest(method, endpoint string, values url
url := fmt.Sprintf("%s%s", h.APIUrl, endpoint)
url = common.EncodeURLValues(url, values)
return h.SendPayload(method, url, headers, bytes.NewBufferString(""), result, true, h.Verbose)
var body []byte
if data != nil {
encoded, err := json.Marshal(data)
if err != nil {
return fmt.Errorf("Huobi unable to marshal data: %s", err)
}
body = encoded
}
return h.SendPayload(method, url, headers, bytes.NewReader(body), result, true, h.Verbose)
}