Merge branch 'master' into engine

This commit is contained in:
Adrian Gallagher
2019-09-26 11:35:18 +10:00
15 changed files with 1 additions and 3693 deletions

View File

@@ -36,7 +36,6 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
| Gemini | Yes | Yes | No |
| HitBTC | Yes | Yes | No |
| Huobi.Pro | Yes | Yes | NA |
| Huobi.Hadax | Yes | Yes | NA |
| ItBit | Yes | NA | No |
| Kraken | Yes | Yes | NA |
| Lbank | Yes | No | NA |

View File

@@ -1,105 +0,0 @@
{{define "exchanges huobihadax" -}}
{{template "header" .}}
## HuobiHadax Exchange
### Current Features
+ REST functions
### How to enable
+ [Enable via configuration](https://github.com/thrasher-corp/gocryptotrader/tree/master/config#enable-exchange-via-config-example)
+ Individual package example below:
```go
// Exchanges will be abstracted out in further updates and examples will be
// supplied then
```
### How to do REST public/private calls
+ If enabled via "configuration".json file the exchange will be added to the
IBotExchange array in the ```go var bot Bot``` and you will only be able to use
the wrapper interface functions for accessing exchange data. View routines.go
for an example of integration usage with GoCryptoTrader. Rudimentary example
below:
main.go
```go
var h exchange.IBotExchange
for i := range bot.Exchanges {
if bot.Exchanges[i].GetName() == "HuobiHadax" {
h = bot.Exchanges[i]
}
}
// Public calls - wrapper functions
// Fetches current ticker information
tick, err := h.FetchTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := h.FetchOrderbook()
if err != nil {
// Handle error
}
// Private calls - wrapper functions - make sure your APIKEY and APISECRET are
// set and AuthenticatedAPISupport is set to true
// Fetches current account information
accountInfo, err := h.GetAccountInfo()
if err != nil {
// Handle error
}
```
+ If enabled via individually importing package, rudimentary example below:
```go
// Public calls
// Fetches current ticker information
ticker, err := h.GetTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := h.GetOrderBook()
if err != nil {
// Handle error
}
// Private calls - make sure your APIKEY and APISECRET are set and
// AuthenticatedAPISupport is set to true
// GetUserInfo returns account info
accountInfo, err := h.GetUserInfo(...)
if err != nil {
// Handle error
}
// Submits an order and the exchange and returns its tradeID
tradeID, err := h.Trade(...)
if err != nil {
// Handle error
}
```
### How to do LongPolling public/private calls
```go
// Exchanges will be abstracted out in further updates and examples will be
// supplied then
```
### Please click GoDocs chevron above to view current GoDoc information for this package
{{template "contributions"}}
{{template "donations"}}
{{end}}

View File

@@ -37,7 +37,6 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
| Gemini | Yes | Yes | No |
| HitBTC | Yes | Yes | No |
| Huobi.Pro | Yes | Yes | NA |
| Huobi.Hadax | Yes | Yes | NA |
| ItBit | Yes | NA | No |
| Kraken | Yes | Yes | NA |
| Lbank | Yes | No | NA |

View File

@@ -121,11 +121,6 @@
"secret": "Secret",
"otpSecret": "-"
},
"huobihadax": {
"key": "Key",
"secret": "Secret",
"otpSecret": "-"
},
"itbit": {
"secret": "Secret",
"clientID": "ClientID",

View File

@@ -16,7 +16,7 @@ import (
const (
// Default number of enabled exchanges. Modify this whenever an exchange is
// added or removed
defaultEnabledExchanges = 28
defaultEnabledExchanges = 27
testFakeExchangeName = "Stampbit"
)

View File

@@ -959,52 +959,6 @@
}
]
},
{
"name": "HuobiHadax",
"enabled": true,
"verbose": false,
"websocket": false,
"useSandbox": false,
"restPollingDelay": 10,
"httpTimeout": 15000000000,
"websocketResponseCheckTimeout": 30000000,
"websocketResponseMaxLimit": 7000000000,
"websocketOrderbookBufferLimit": 5,
"httpUserAgent": "",
"httpDebugging": false,
"authenticatedApiSupport": false,
"authenticatedWebsocketApiSupport": false,
"apiKey": "Key",
"apiSecret": "Secret",
"apiAuthPemKey": "-----BEGIN EC PRIVATE KEY-----\nJUSTADUMMY\n-----END EC PRIVATE KEY-----\n",
"apiUrl": "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API",
"apiUrlSecondary": "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API",
"proxyAddress": "",
"websocketUrl": "NON_DEFAULT_HTTP_LINK_TO_WEBSOCKET_EXCHANGE_API",
"availablePairs": "EGCC-ETH,BKBT-BTC,YCC-ETH,SEELE-BTC,INC-BTC,BUT-ETH,UIP-BTC,PC-BTC,TRIO-ETH,GSC-BTC,DAC-BTC,HOT-BTC,UC-ETH,REN-ETH,GET-ETH,LYM-BTC,IDT-ETH,MEX-BTC,MUSK-BTC,UUU-BTC,MEX-ETH,ZJLT-ETH,SSP-ETH,XMX-ETH,18C-ETH,CDC-ETH,SEXC-ETH,HPT-BTC,HPT-HT,TOS-ETH,UC-BTC,EKT-BTC,CVCOIN-ETH,BCV-BTC,EKT-ETH,CDC-BTC,MAN-ETH,CVCOIN-BTC,MAN-BTC,SEXC-BTC,HPT-USDT,LXT-ETH,GVE-BTC,TOS-BTC,18C-BTC,YCC-BTC,CNN-BTC,RTE-ETH,GVE-ETH,CNN-ETH,PORTAL-BTC,LXT-BTC,PORTAL-ETH,ZJLT-BTC,DAC-ETH,REN-BTC,PC-ETH,GSC-ETH,RTE-BTC,BKBT-ETH,TRIO-BTC,GTC-BTC,IIC-BTC,BUT-BTC,SEELE-ETH,SHE-BTC,UIP-ETH,NCC-ETH,EGCC-BTC,MUSK-ETH,BCV-ETH,RCCC-BTC,UUU-ETH,IDT-BTC,XMX-BTC,SSP-BTC,AE-ETH,MT-HT,NCC-BTC,AAC-BTC,KCASH-BTC,DATX-BTC,HOT-ETH,LYM-ETH,GET-BTC,FAIR-ETH,GTC-ETH,IIC-ETH,INC-ETH,RCCC-ETH,FAIR-BTC,AE-BTC,KCASH-HT,MT-ETH,MT-BTC,SHE-ETH,PNT-ETH,DATX-ETH,AAC-ETH,PNT-BTC,KCASH-ETH,FTI-ETH,FTI-BTC",
"enabledPairs": "NCC-BTC",
"baseCurrencies": "USD",
"assetTypes": "SPOT",
"supportsAutoPairUpdates": true,
"configCurrencyPairFormat": {
"uppercase": true,
"delimiter": "-"
},
"requestCurrencyPairFormat": {
"uppercase": false
},
"bankAccounts": [
{
"bankName": "",
"bankAddress": "",
"accountName": "",
"accountNumber": "",
"swiftCode": "",
"iban": "",
"supportedCurrencies": ""
}
]
},
{
"name": "ITBIT",
"enabled": true,

View File

@@ -24,7 +24,6 @@ import (
"github.com/thrasher-corp/gocryptotrader/exchanges/gemini"
"github.com/thrasher-corp/gocryptotrader/exchanges/hitbtc"
"github.com/thrasher-corp/gocryptotrader/exchanges/huobi"
"github.com/thrasher-corp/gocryptotrader/exchanges/huobihadax"
"github.com/thrasher-corp/gocryptotrader/exchanges/itbit"
"github.com/thrasher-corp/gocryptotrader/exchanges/kraken"
"github.com/thrasher-corp/gocryptotrader/exchanges/lakebtc"
@@ -166,8 +165,6 @@ func LoadExchange(name string, useWG bool, wg *sync.WaitGroup) error {
exch = new(hitbtc.HitBTC)
case "huobi":
exch = new(huobi.HUOBI)
case "huobihadax":
exch = new(huobihadax.HUOBIHADAX)
case "itbit":
exch = new(itbit.ItBit)
case "kraken":

View File

@@ -1,140 +0,0 @@
# GoCryptoTrader package Huobihadax
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70">
[![Build Status](https://travis-ci.org/thrasher-corp/gocryptotrader.svg?branch=master)](https://travis-ci.org/thrasher-corp/gocryptotrader)
[![Software License](https://img.shields.io/badge/License-MIT-orange.svg?style=flat-square)](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
[![GoDoc](https://godoc.org/github.com/thrasher-corp/gocryptotrader?status.svg)](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/huobihadax)
[![Coverage Status](http://codecov.io/github/thrasher-corp/gocryptotrader/coverage.svg?branch=master)](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/thrasher-corp/gocryptotrader)](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
This huobihadax package is part of the GoCryptoTrader codebase.
## This is still in active development
You can track ideas, planned features and what's in progresss on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader).
Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTc5ZDE1ZTNiOGM3ZGMyMmY1NTAxYWZhODE0MWM5N2JlZDk1NDU0YTViYzk4NTk3OTRiMDQzNGQ1YTc4YmRlMTk)
## HuobiHadax Exchange
### Current Features
+ REST functions
### How to enable
+ [Enable via configuration](https://github.com/thrasher-corp/gocryptotrader/tree/master/config#enable-exchange-via-config-example)
+ Individual package example below:
```go
// Exchanges will be abstracted out in further updates and examples will be
// supplied then
```
### How to do REST public/private calls
+ If enabled via "configuration".json file the exchange will be added to the
IBotExchange array in the ```go var bot Bot``` and you will only be able to use
the wrapper interface functions for accessing exchange data. View routines.go
for an example of integration usage with GoCryptoTrader. Rudimentary example
below:
main.go
```go
var h exchange.IBotExchange
for i := range bot.Exchanges {
if bot.Exchanges[i].GetName() == "HuobiHadax" {
h = bot.Exchanges[i]
}
}
// Public calls - wrapper functions
// Fetches current ticker information
tick, err := h.FetchTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := h.FetchOrderbook()
if err != nil {
// Handle error
}
// Private calls - wrapper functions - make sure your APIKEY and APISECRET are
// set and AuthenticatedAPISupport is set to true
// Fetches current account information
accountInfo, err := h.GetAccountInfo()
if err != nil {
// Handle error
}
```
+ If enabled via individually importing package, rudimentary example below:
```go
// Public calls
// Fetches current ticker information
ticker, err := h.GetTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := h.GetOrderBook()
if err != nil {
// Handle error
}
// Private calls - make sure your APIKEY and APISECRET are set and
// AuthenticatedAPISupport is set to true
// GetUserInfo returns account info
accountInfo, err := h.GetUserInfo(...)
if err != nil {
// Handle error
}
// Submits an order and the exchange and returns its tradeID
tradeID, err := h.Trade(...)
if err != nil {
// Handle error
}
```
### How to do LongPolling public/private calls
```go
// Exchanges will be abstracted out in further updates and examples will be
// supplied then
```
### Please click GoDocs chevron above to view current GoDoc information for this package
## Contribution
Please feel free to submit any pull requests or suggest any desired features to be added.
When submitting a PR, please abide by our coding guidelines:
+ Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
+ Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
+ Code must adhere to our [coding style](https://github.com/thrasher-corp/gocryptotrader/blob/master/doc/coding_style.md).
+ Pull requests need to be based on and opened against the `master` branch.
## Donations
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/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:
***1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB***

View File

@@ -1,916 +0,0 @@
package huobihadax
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
log "github.com/thrasher-corp/gocryptotrader/logger"
)
const (
huobihadaxAPIURL = "https://api.hadax.com"
huobihadaxAPIVersion = "1"
huobihadaxAPIName = "hadax"
huobihadaxMarketHistoryKline = "market/history/kline"
huobihadaxMarketDetail = "market/detail"
huobihadaxMarketDetailMerged = "market/detail/merged"
huobihadaxMarketDepth = "market/depth"
huobihadaxMarketTicker = "market/tickers"
huobihadaxMarketTrade = "market/trade"
huobihadaxMarketTradeHistory = "market/history/trade"
huobihadaxSymbols = "common/symbols"
huobihadaxCurrencies = "common/currencys"
huobihadaxTimestamp = "common/timestamp"
huobihadaxAccounts = "account/accounts"
huobihadaxAggregatedBalance = "subuser/aggregate-balance"
huobihadaxAccountBalance = "account/accounts/%s/balance"
huobihadaxOrderPlace = "order/orders/place"
huobihadaxOrderCancel = "order/orders/%s/submitcancel"
huobihadaxGetOpenOrders = "order/openOrders"
huobihadaxOrderCancelBatch = "order/orders/batchcancel"
huobiHadaxBatchCancelOpenOrders = "order/orders/batchCancelOpenOrders"
huobihadaxGetOrder = "order/orders/%s"
huobihadaxGetOrderMatch = "order/orders/%s/matchresults"
huobihadaxGetOrders = "order/orders"
huobihadaxGetOrdersMatch = "orders/matchresults"
huobihadaxMarginTransferIn = "dw/transfer-in/margin"
huobihadaxMarginTransferOut = "dw/transfer-out/margin"
huobihadaxMarginOrders = "margin/orders"
huobihadaxMarginRepay = "margin/orders/%s/repay"
huobihadaxMarginLoanOrders = "margin/loan-orders"
huobihadaxMarginAccountBalance = "margin/accounts/balance"
huobihadaxWithdrawCreate = "dw/withdraw/api/create"
huobihadaxWithdrawCancel = "dw/withdraw-virtual/%s/cancel"
huobiHadaxDepositAddress = "query/deposit-withdraw"
huobihadaxAuthRate = 100
huobihadaxUnauthRate = 100
)
// HUOBIHADAX is the overarching type across this package
type HUOBIHADAX struct {
WebsocketConn *wshandler.WebsocketConnection
AuthenticatedWebsocketConn *wshandler.WebsocketConnection
exchange.Base
}
// GetSpotKline returns kline data
// KlinesRequestParams holds the Kline request params
func (h *HUOBIHADAX) GetSpotKline(arg KlinesRequestParams) ([]KlineItem, error) {
vals := url.Values{}
vals.Set("symbol", arg.Symbol)
vals.Set("period", string(arg.Period))
if arg.Size != 0 {
vals.Set("size", strconv.Itoa(arg.Size))
}
type response struct {
Response
Data []KlineItem `json:"data"`
}
var result response
urlPath := fmt.Sprintf("%s/%s", h.API.Endpoints.URL, huobihadaxMarketHistoryKline)
err := h.SendHTTPRequest(common.EncodeURLValues(urlPath, vals), &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.Data, err
}
// GetMarketDetailMerged returns the ticker for the specified symbol
func (h *HUOBIHADAX) GetMarketDetailMerged(symbol string) (DetailMerged, error) {
vals := url.Values{}
vals.Set("symbol", symbol)
type response struct {
Response
Tick DetailMerged `json:"tick"`
}
var result response
urlPath := fmt.Sprintf("%s/%s", h.API.Endpoints.URL, huobihadaxMarketDetailMerged)
err := h.SendHTTPRequest(common.EncodeURLValues(urlPath, vals), &result)
if result.ErrorMessage != "" {
return result.Tick, errors.New(result.ErrorMessage)
}
return result.Tick, err
}
// GetDepth returns the depth for the specified symbol
func (h *HUOBIHADAX) GetDepth(symbol, depthType string) (Orderbook, error) {
vals := url.Values{}
vals.Set("symbol", symbol)
if depthType != "" {
vals.Set("type", depthType)
}
type response struct {
Response
Depth Orderbook `json:"tick"`
}
var result response
urlPath := fmt.Sprintf("%s/%s", h.API.Endpoints.URL, huobihadaxMarketDepth)
err := h.SendHTTPRequest(common.EncodeURLValues(urlPath, vals), &result)
if result.ErrorMessage != "" {
return result.Depth, errors.New(result.ErrorMessage)
}
return result.Depth, err
}
// GetTrades returns the trades for the specified symbol
func (h *HUOBIHADAX) GetTrades(symbol string) ([]Trade, error) {
vals := url.Values{}
vals.Set("symbol", symbol)
type response struct {
Response
Tick struct {
Data []Trade `json:"data"`
} `json:"tick"`
}
var result response
urlPath := fmt.Sprintf("%s/%s", h.API.Endpoints.URL, huobihadaxMarketTrade)
err := h.SendHTTPRequest(common.EncodeURLValues(urlPath, vals), &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.Tick.Data, err
}
// GetLatestSpotPrice returns latest spot price of symbol
//
// symbol: string of currency pair
func (h *HUOBIHADAX) GetLatestSpotPrice(symbol string) (float64, error) {
list, err := h.GetTradeHistory(symbol, "1")
if err != nil {
return 0, err
}
if len(list) == 0 {
return 0, errors.New("the length of the list is 0")
}
return list[0].Trades[0].Price, nil
}
// GetTradeHistory returns the trades for the specified symbol
func (h *HUOBIHADAX) GetTradeHistory(symbol, size string) ([]TradeHistory, error) {
vals := url.Values{}
vals.Set("symbol", symbol)
if size != "" {
vals.Set("size", size)
}
type response struct {
Response
TradeHistory []TradeHistory `json:"data"`
}
var result response
urlPath := fmt.Sprintf("%s/%s", h.API.Endpoints.URL, huobihadaxMarketTradeHistory)
err := h.SendHTTPRequest(common.EncodeURLValues(urlPath, vals), &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.TradeHistory, err
}
// GetMarketDetail returns the ticker for the specified symbol
func (h *HUOBIHADAX) GetMarketDetail(symbol string) (Detail, error) {
vals := url.Values{}
vals.Set("symbol", symbol)
type response struct {
Response
Tick Detail `json:"tick"`
}
var result response
urlPath := fmt.Sprintf("%s/%s", h.API.Endpoints.URL, huobihadaxMarketDetail)
err := h.SendHTTPRequest(common.EncodeURLValues(urlPath, vals), &result)
if result.ErrorMessage != "" {
return result.Tick, errors.New(result.ErrorMessage)
}
return result.Tick, err
}
// GetSymbols returns an array of symbols supported by Huobi
func (h *HUOBIHADAX) GetSymbols() ([]Symbol, error) {
type response struct {
Response
Symbols []Symbol `json:"data"`
}
var result response
urlPath := fmt.Sprintf("%s/v%s/%s/%s", h.API.Endpoints.URL, huobihadaxAPIVersion, huobihadaxAPIName, huobihadaxSymbols)
err := h.SendHTTPRequest(urlPath, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.Symbols, err
}
// GetCurrencies returns a list of currencies supported by Huobi
func (h *HUOBIHADAX) GetCurrencies() ([]string, error) {
type response struct {
Response
Currencies []string `json:"data"`
}
var result response
urlPath := fmt.Sprintf("%s/v%s/%s/%s", h.API.Endpoints.URL, huobihadaxAPIVersion, huobihadaxAPIName, huobihadaxCurrencies)
err := h.SendHTTPRequest(urlPath, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.Currencies, err
}
// GetTickers returns the ticker for the specified symbol
func (h *HUOBIHADAX) GetTickers() (Tickers, error) {
var result Tickers
urlPath := fmt.Sprintf("%s/%s", h.API.Endpoints.URL, huobihadaxMarketTicker)
return result, h.SendHTTPRequest(urlPath, &result)
}
// GetTimestamp returns the Huobi server time
func (h *HUOBIHADAX) GetTimestamp() (int64, error) {
type response struct {
Response
Timestamp int64 `json:"data"`
}
var result response
urlPath := fmt.Sprintf("%s/v%s/%s", h.API.Endpoints.URL, huobihadaxAPIVersion, huobihadaxTimestamp)
err := h.SendHTTPRequest(urlPath, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
}
return result.Timestamp, err
}
// GetAccounts returns the Huobi user accounts
func (h *HUOBIHADAX) GetAccounts() ([]Account, error) {
type response struct {
Response
AccountData []Account `json:"data"`
}
var result response
err := h.SendAuthenticatedHTTPRequest(http.MethodGet, huobihadaxAccounts, url.Values{}, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.AccountData, err
}
// GetAccountBalance returns the users Huobi account balance
func (h *HUOBIHADAX) GetAccountBalance(accountID string) ([]AccountBalanceDetail, error) {
type response struct {
Response
AccountBalanceData AccountBalance `json:"data"`
}
var result response
endpoint := fmt.Sprintf("%s/%s", huobihadaxAPIName, fmt.Sprintf(huobihadaxAccountBalance, accountID))
err := h.SendAuthenticatedHTTPRequest(http.MethodGet, endpoint, url.Values{}, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.AccountBalanceData.AccountBalanceDetails, err
}
// GetAggregatedBalance returns the balances of all the sub-account aggregated.
func (h *HUOBIHADAX) GetAggregatedBalance() ([]AggregatedBalance, error) {
type response struct {
Response
AggregatedBalances []AggregatedBalance `json:"data"`
}
var result response
err := h.SendAuthenticatedHTTPRequest(
http.MethodGet,
huobihadaxAggregatedBalance,
url.Values{},
&result,
)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.AggregatedBalances, err
}
// SpotNewOrder submits an order to Huobi
func (h *HUOBIHADAX) SpotNewOrder(arg SpotNewOrderRequestParams) (int64, error) {
vals := make(map[string]string)
vals["account-id"] = fmt.Sprintf("%d", arg.AccountID)
vals["amount"] = strconv.FormatFloat(arg.Amount, 'f', -1, 64)
// Only set price if order type is not equal to buy-market or sell-market
if arg.Type != SpotNewOrderRequestTypeBuyMarket && arg.Type != SpotNewOrderRequestTypeSellMarket {
vals["price"] = strconv.FormatFloat(arg.Price, 'f', -1, 64)
}
if arg.Source != "" {
vals["source"] = arg.Source
}
vals["symbol"] = arg.Symbol
vals["type"] = string(arg.Type)
type response struct {
Response
OrderID int64 `json:"data,string"`
}
// The API indicates that for the POST request, the parameters of each method are not signed and authenticated. That is, only the AccessKeyId, SignatureMethod, SignatureVersion, and Timestamp parameters are required for the POST request. The other parameters are placed in the body.
// So re-encode the Post parameter
bytesParams, _ := json.Marshal(vals)
postBodyParams := string(bytesParams)
if h.Verbose {
log.Debugf(log.ExchangeSys, "Post params: %v", postBodyParams)
}
var result response
endpoint := fmt.Sprintf("%s/%s", huobihadaxAPIName, huobihadaxOrderPlace)
err := h.SendAuthenticatedHTTPPostRequest(http.MethodPost, endpoint, postBodyParams, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
}
return result.OrderID, err
}
// CancelExistingOrder cancels an order on Huobi
func (h *HUOBIHADAX) CancelExistingOrder(orderID int64) (int64, error) {
type response struct {
Response
OrderID int64 `json:"data,string"`
}
var result response
endpoint := fmt.Sprintf(huobihadaxOrderCancel, strconv.FormatInt(orderID, 10))
err := h.SendAuthenticatedHTTPRequest(http.MethodPost, 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 *HUOBIHADAX) CancelOrderBatch(orderIDs []int64) (CancelOrderBatch, error) {
type response struct {
Status string `json:"status"`
Data CancelOrderBatch `json:"data"`
}
// Used to send param formatting
type postBody struct {
List []int64 `json:"order-ids"`
}
// Format to JSON
bytesParams, _ := common.JSONEncode(&postBody{List: orderIDs})
postBodyParams := string(bytesParams)
var result response
err := h.SendAuthenticatedHTTPPostRequest(http.MethodPost, huobihadaxOrderCancelBatch, postBodyParams, &result)
if len(result.Data.Failed) != 0 {
errJSON, _ := common.JSONEncode(result.Data.Failed)
return CancelOrderBatch{}, errors.New(string(errJSON))
}
return result.Data, err
}
// CancelOpenOrdersBatch cancels a batch of orders -- to-do
func (h *HUOBIHADAX) CancelOpenOrdersBatch(accountID, symbol string) (CancelOpenOrdersBatch, error) {
params := url.Values{}
params.Set("account-id", accountID)
var result CancelOpenOrdersBatch
data := struct {
AccountID string `json:"account-id"`
Symbol string `json:"symbol"`
}{
AccountID: accountID,
Symbol: symbol,
}
bytesParams, _ := common.JSONEncode(data)
postBodyParams := string(bytesParams)
err := h.SendAuthenticatedHTTPPostRequest(http.MethodPost, huobiHadaxBatchCancelOpenOrders, postBodyParams, &result)
if result.Data.FailedCount > 0 {
return result, fmt.Errorf("there were %v failed order cancellations", result.Data.FailedCount)
}
return result, err
}
// GetOpenOrders returns a list of orders
func (h *HUOBIHADAX) GetOpenOrders(accountID, symbol, side string, size int) ([]OrderInfo, error) {
type response struct {
Response
Orders []OrderInfo `json:"data"`
}
vals := url.Values{}
vals.Set("symbol", symbol)
vals.Set("accountID", accountID)
if len(side) > 0 {
vals.Set("side", side)
}
vals.Set("size", fmt.Sprintf("%v", size))
var result response
err := h.SendAuthenticatedHTTPRequest(http.MethodGet, huobihadaxGetOpenOrders, vals, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.Orders, err
}
// GetOrder returns order information for the specified order
func (h *HUOBIHADAX) GetOrder(orderID int64) (OrderInfo, error) {
type response struct {
Response
Order OrderInfo `json:"data"`
}
var result response
endpoint := fmt.Sprintf(huobihadaxGetOrder, strconv.FormatInt(orderID, 10))
err := h.SendAuthenticatedHTTPRequest(http.MethodGet, 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 *HUOBIHADAX) GetOrderMatchResults(orderID int64) ([]OrderMatchInfo, error) {
type response struct {
Response
Orders []OrderMatchInfo `json:"data"`
}
var result response
endpoint := fmt.Sprintf(huobihadaxGetOrderMatch, strconv.FormatInt(orderID, 10))
err := h.SendAuthenticatedHTTPRequest(http.MethodGet, 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 *HUOBIHADAX) 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(http.MethodGet, huobihadaxGetOrders, vals, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.Orders, err
}
// GetOrdersMatch returns a list of matched orders
func (h *HUOBIHADAX) 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(http.MethodGet, huobihadaxGetOrdersMatch, 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 *HUOBIHADAX) 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 := huobihadaxMarginTransferIn
if !in {
path = huobihadaxMarginTransferOut
}
type response struct {
Response
TransferID int64 `json:"data"`
}
var result response
err := h.SendAuthenticatedHTTPRequest(http.MethodPost, path, vals, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
}
return result.TransferID, err
}
// MarginOrder submits a margin order application
func (h *HUOBIHADAX) 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(http.MethodPost, huobihadaxMarginOrders, 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 *HUOBIHADAX) 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(huobihadaxMarginRepay, strconv.FormatInt(orderID, 10))
err := h.SendAuthenticatedHTTPRequest(http.MethodPost, endpoint, vals, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
}
return result.MarginOrderID, err
}
// GetMarginLoanOrders returns the margin loan orders
func (h *HUOBIHADAX) 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(http.MethodGet, huobihadaxMarginLoanOrders, vals, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.MarginLoanOrders, err
}
// GetMarginAccountBalance returns the margin account balances
func (h *HUOBIHADAX) 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(http.MethodGet, huobihadaxMarginAccountBalance, vals, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.Balances, err
}
// Withdraw withdraws the desired amount and currency
func (h *HUOBIHADAX) Withdraw(c currency.Code, address, addrTag string, amount, fee float64) (int64, error) {
type response struct {
Response
WithdrawID int64 `json:"data"`
}
data := struct {
Address string `json:"address"`
Amount string `json:"amount"`
Currency string `json:"currency"`
Fee string `json:"fee,omitempty"`
AddrTag string `json:"addr-tag,omitempty"`
}{
Address: address,
Currency: c.Lower().String(),
Amount: strconv.FormatFloat(amount, 'f', -1, 64),
}
if fee > 0 {
data.Fee = strconv.FormatFloat(fee, 'f', -1, 64)
}
if c == currency.XRP {
data.AddrTag = addrTag
}
var result response
bytesParams, _ := common.JSONEncode(data)
postBodyParams := string(bytesParams)
err := h.SendAuthenticatedHTTPPostRequest(http.MethodPost, huobihadaxWithdrawCreate, postBodyParams, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
}
return result.WithdrawID, err
}
// CancelWithdraw cancels a withdraw request
func (h *HUOBIHADAX) 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(huobihadaxWithdrawCancel, strconv.FormatInt(withdrawID, 10))
err := h.SendAuthenticatedHTTPRequest(http.MethodPost, endpoint, vals, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
}
return result.WithdrawID, err
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (h *HUOBIHADAX) SendHTTPRequest(path string, result interface{}) error {
return h.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
h.Verbose,
h.HTTPDebugging,
h.HTTPRecording)
}
// SendAuthenticatedHTTPPostRequest sends authenticated requests to the HUOBI API
func (h *HUOBIHADAX) SendAuthenticatedHTTPPostRequest(method, endpoint, postBodyValues string, result interface{}) error {
if !h.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, h.Name)
}
signatureParams := url.Values{}
signatureParams.Set("AccessKeyId", h.API.Credentials.Key)
signatureParams.Set("SignatureMethod", "HmacSHA256")
signatureParams.Set("SignatureVersion", "2")
signatureParams.Set("Timestamp", time.Now().UTC().Format("2006-01-02T15:04:05"))
endpoint = fmt.Sprintf("/v%s/%s", huobihadaxAPIVersion, endpoint)
payload := fmt.Sprintf("%s\napi.hadax.com\n%s\n%s",
method, endpoint, signatureParams.Encode())
headers := make(map[string]string)
headers["Content-Type"] = "application/json"
headers["Accept-Language"] = "zh-cn"
hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(payload), []byte(h.API.Credentials.Secret))
signatureParams.Set("Signature", crypto.Base64Encode(hmac))
urlPath := common.EncodeURLValues(fmt.Sprintf("%s%s", h.API.Endpoints.URL, endpoint),
signatureParams)
return h.SendPayload(method,
urlPath,
headers,
bytes.NewBufferString(postBodyValues),
result,
true,
false,
h.Verbose,
h.HTTPDebugging,
h.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends authenticated requests to the HUOBI API
func (h *HUOBIHADAX) SendAuthenticatedHTTPRequest(method, endpoint string, values url.Values, result interface{}) error {
if !h.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, h.Name)
}
values.Set("AccessKeyId", h.API.Credentials.Key)
values.Set("SignatureMethod", "HmacSHA256")
values.Set("SignatureVersion", "2")
values.Set("Timestamp", time.Now().UTC().Format("2006-01-02T15:04:05"))
endpoint = fmt.Sprintf("/v%s/%s", huobihadaxAPIVersion, endpoint)
payload := fmt.Sprintf("%s\napi.hadax.com\n%s\n%s",
method, endpoint, values.Encode())
headers := make(map[string]string)
headers["Content-Type"] = "application/x-www-form-urlencoded"
hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(payload), []byte(h.API.Credentials.Secret))
values.Set("Signature", crypto.Base64Encode(hmac))
urlPath := common.EncodeURLValues(fmt.Sprintf("%s%s", h.API.Endpoints.URL, endpoint),
values)
return h.SendPayload(method,
urlPath,
headers,
bytes.NewBufferString(""),
result,
true,
false,
h.Verbose,
h.HTTPDebugging,
h.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction
func (h *HUOBIHADAX) GetFee(feeBuilder *exchange.FeeBuilder) (float64, error) {
var fee float64
if feeBuilder.FeeType == exchange.OfflineTradeFee || feeBuilder.FeeType == exchange.CryptocurrencyTradeFee {
fee = calculateTradingFee(feeBuilder.Pair, feeBuilder.PurchasePrice, feeBuilder.Amount)
}
if fee < 0 {
fee = 0
}
return fee, nil
}
func calculateTradingFee(c currency.Pair, price, amount float64) float64 {
if c.IsCryptoFiatPair() {
return 0.001 * price * amount
}
return 0.002 * price * amount
}
// GetDepositWithdrawalHistory returns deposit or withdrawal data
func (h *HUOBIHADAX) GetDepositWithdrawalHistory(associatedID, currency string, isDeposit bool, size int64) ([]History, error) {
var resp = struct {
Response
Data []History `json:"data"`
}{}
vals := url.Values{}
if isDeposit {
vals.Set("type", "deposit")
} else {
vals.Set("type", "withdraw")
}
vals.Set("from", associatedID)
vals.Set("size", strconv.FormatInt(size, 10))
vals.Set("currency", strings.ToLower(currency))
err := h.SendAuthenticatedHTTPRequest(http.MethodGet,
huobiHadaxDepositAddress,
vals,
&resp)
if resp.ErrorMessage != "" {
return resp.Data, errors.New(resp.ErrorMessage)
}
return resp.Data, err
}

View File

@@ -1,729 +0,0 @@
package huobihadax
import (
"strconv"
"testing"
"github.com/gorilla/websocket"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
)
// Please supply your own APIKEYS here for due diligence testing
const (
apiKey = ""
apiSecret = ""
canManipulateRealOrders = false
testSymbol = "btcusdt"
)
var h HUOBIHADAX
var wsSetupRan bool
// getDefaultConfig returns a default hadax config
func getDefaultConfig() config.ExchangeConfig {
exchCfg := config.ExchangeConfig{
Name: "Huobi",
Enabled: true,
Verbose: true,
Features: &config.FeaturesConfig{
Supports: config.FeaturesSupportedConfig{
REST: true,
Websocket: false,
RESTCapabilities: config.ProtocolFeaturesConfig{
AutoPairUpdates: false,
},
},
Enabled: config.FeaturesEnabledConfig{
AutoPairUpdates: false,
Websocket: false,
},
},
API: config.APIConfig{
AuthenticatedSupport: false,
},
HTTPTimeout: 15000000000,
CurrencyPairs: &currency.PairsManager{
AssetTypes: asset.Items{
asset.Spot,
},
UseGlobalFormat: true,
ConfigFormat: &currency.PairFormat{
Uppercase: true,
Delimiter: "-",
},
RequestFormat: &currency.PairFormat{
Uppercase: false,
},
},
BaseCurrencies: currency.NewCurrenciesFromStringArray([]string{"USD"}),
}
exchCfg.CurrencyPairs.StorePairs(asset.Spot,
currency.NewPairsFromStrings([]string{"BTC-USDT", "BCH-USDT"}),
false)
exchCfg.CurrencyPairs.StorePairs(asset.Spot,
currency.NewPairsFromStrings([]string{"BTC-USDT"}),
true)
return exchCfg
}
func TestSetDefaults(t *testing.T) {
h.SetDefaults()
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
hadaxConfig, err := cfg.GetExchangeConfig("HuobiHadax")
if err != nil {
t.Error("Test Failed - HuobiHadax Setup() init error")
}
hadaxConfig.API.AuthenticatedSupport = true
hadaxConfig.API.AuthenticatedWebsocketSupport = true
hadaxConfig.API.Credentials.Key = apiKey
hadaxConfig.API.Credentials.Secret = apiSecret
h.Setup(hadaxConfig)
}
func setupWsTests(t *testing.T) {
if wsSetupRan {
return
}
TestSetDefaults(t)
TestSetup(t)
if !h.Websocket.IsEnabled() && !h.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip(wshandler.WebsocketNotEnabled)
}
comms = make(chan WsMessage, sharedtestvalues.WebsocketChannelOverrideCapacity)
h.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
h.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride()
go h.WsHandleData()
h.AuthenticatedWebsocketConn = &wshandler.WebsocketConnection{
ExchangeName: h.Name,
URL: wsAccountsOrdersURL,
Verbose: h.Verbose,
ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit,
ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout,
}
h.WebsocketConn = &wshandler.WebsocketConnection{
ExchangeName: h.Name,
URL: HuobiHadaxSocketIOAddress,
Verbose: h.Verbose,
ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit,
ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout,
}
var dialer websocket.Dialer
err := h.wsAuthenticatedDial(&dialer)
if err != nil {
t.Fatal(err)
}
err = h.wsLogin()
if err != nil {
t.Fatal(err)
}
wsSetupRan = true
}
func TestGetSpotKline(t *testing.T) {
t.Parallel()
_, err := h.GetSpotKline(KlinesRequestParams{
Symbol: testSymbol,
Period: TimeIntervalHour,
Size: 0,
})
if err != nil {
t.Errorf("Test failed - Huobi TestGetSpotKline: %s", err)
}
}
func TestGetMarketDetailMerged(t *testing.T) {
t.Parallel()
_, err := h.GetMarketDetailMerged(testSymbol)
if err != nil {
t.Errorf("Test failed - Huobi TestGetMarketDetailMerged: %s", err)
}
}
func TestGetDepth(t *testing.T) {
t.Parallel()
_, err := h.GetDepth(testSymbol, "step1")
if err != nil {
t.Errorf("Test failed - Huobi TestGetDepth: %s", err)
}
}
func TestGetTrades(t *testing.T) {
t.Parallel()
_, err := h.GetTrades(testSymbol)
if err != nil {
t.Errorf("Test failed - Huobi TestGetTrades: %s", err)
}
}
func TestGetLatestSpotPrice(t *testing.T) {
t.Parallel()
_, err := h.GetLatestSpotPrice(testSymbol)
if err != nil {
t.Errorf("Test failed - Huobi GetLatestSpotPrice: %s", err)
}
}
func TestGetTradeHistory(t *testing.T) {
t.Parallel()
_, err := h.GetTradeHistory(testSymbol, "50")
if err != nil {
t.Errorf("Test failed - Huobi TestGetTradeHistory: %s", err)
}
}
func TestGetMarketDetail(t *testing.T) {
t.Parallel()
_, err := h.GetMarketDetail(testSymbol)
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 TestGetTicker(t *testing.T) {
_, err := h.GetTickers()
if err != nil {
t.Error(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 !h.ValidateAPICredentials() {
t.Skip()
}
_, err := h.GetAccounts()
if err != nil {
t.Errorf("Test failed - Huobi GetAccounts: %s", err)
}
}
func TestGetAccountBalance(t *testing.T) {
t.Parallel()
if !h.ValidateAPICredentials() {
t.Skip()
}
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 TestGetAggregatedBalance(t *testing.T) {
t.Parallel()
if !h.ValidateAPICredentials() {
t.Skip()
}
_, err := h.GetAggregatedBalance()
if err != nil {
t.Errorf("Test failed - Huobi GetAggregatedBalance: %s", err)
}
}
func TestSpotNewOrder(t *testing.T) {
t.Parallel()
if !h.ValidateAPICredentials() {
t.Skip()
}
arg := SpotNewOrderRequestParams{
Symbol: testSymbol,
AccountID: 000000,
Amount: 0.01,
Price: 10.1,
Type: SpotNewOrderRequestTypeBuyLimit,
}
newOrderID, err := h.SpotNewOrder(arg)
if err != nil {
t.Errorf("Test failed - Huobi SpotNewOrder: %s", err)
} else {
t.Log(newOrderID)
}
}
func TestCancelExistingOrder(t *testing.T) {
t.Parallel()
if !h.ValidateAPICredentials() {
t.Skip()
}
_, err := h.CancelExistingOrder(1337)
if err == nil {
t.Error("Test failed - Huobi TestCancelExistingOrder: Invalid orderID returned true")
}
}
func TestGetOrder(t *testing.T) {
t.Parallel()
if !h.ValidateAPICredentials() {
t.Skip()
}
_, 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 !h.ValidateAPICredentials() {
t.Skip()
}
_, err := h.GetMarginLoanOrders(testSymbol, "", "", "", "", "", "", "")
if err != nil {
t.Errorf("Test failed - Huobi TestGetMarginLoanOrders: %s", err)
}
}
func TestGetMarginAccountBalance(t *testing.T) {
t.Parallel()
if !h.ValidateAPICredentials() {
t.Skip()
}
_, err := h.GetMarginAccountBalance(testSymbol)
if err != nil {
t.Errorf("Test failed - Huobi TestGetMarginAccountBalance: %s", err)
}
}
func TestCancelWithdraw(t *testing.T) {
t.Parallel()
if !h.ValidateAPICredentials() {
t.Skip()
}
_, err := h.CancelWithdraw(1337)
if err == nil {
t.Error("Test failed - Huobi TestCancelWithdraw: Invalid withdraw-ID was valid")
}
}
func setFeeBuilder() *exchange.FeeBuilder {
return &exchange.FeeBuilder{
Amount: 1,
FeeType: exchange.CryptocurrencyTradeFee,
Pair: currency.NewPairWithDelimiter(currency.BTC.String(),
currency.LTC.String(),
"_"),
PurchasePrice: 1,
FiatCurrency: currency.USD,
BankTransactionType: exchange.WireTransfer,
}
}
// TestGetFeeByTypeOfflineTradeFee logic test
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
var feeBuilder = setFeeBuilder()
h.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
} else {
if feeBuilder.FeeType != exchange.CryptocurrencyTradeFee {
t.Errorf("Expected %v, received %v", exchange.CryptocurrencyTradeFee, feeBuilder.FeeType)
}
}
}
func TestGetFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
// CryptocurrencyTradeFee Basic
if resp, err := h.GetFee(feeBuilder); resp != float64(0.002) || err != nil {
t.Error(err)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0.002), resp)
}
// CryptocurrencyTradeFee High quantity
feeBuilder = setFeeBuilder()
feeBuilder.Amount = 1000
feeBuilder.PurchasePrice = 1000
if resp, err := h.GetFee(feeBuilder); resp != float64(2000) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(2000), resp)
t.Error(err)
}
// CryptocurrencyTradeFee IsMaker
feeBuilder = setFeeBuilder()
feeBuilder.IsMaker = true
if resp, err := h.GetFee(feeBuilder); resp != float64(0.002) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0.002), resp)
t.Error(err)
}
// CryptocurrencyTradeFee Negative purchase price
feeBuilder = setFeeBuilder()
feeBuilder.PurchasePrice = -1000
if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Error(err)
}
// CryptocurrencyWithdrawalFee Basic
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Error(err)
}
// CryptocurrencyWithdrawalFee Invalid currency
feeBuilder = setFeeBuilder()
feeBuilder.Pair.Base = currency.NewCode("hello")
feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Error(err)
}
// CyptocurrencyDepositFee Basic
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.CyptocurrencyDepositFee
if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Error(err)
}
// InternationalBankDepositFee Basic
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.InternationalBankDepositFee
if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Error(err)
}
// InternationalBankWithdrawalFee Basic
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee
feeBuilder.FiatCurrency = currency.USD
if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Error(err)
}
}
func TestFormatWithdrawPermissions(t *testing.T) {
h.SetDefaults()
expectedResult := exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.NoFiatWithdrawalsText
withdrawPermissions := h.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
h.SetDefaults()
TestSetup(t)
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
Currencies: []currency.Pair{currency.NewPair(currency.BTC, currency.USDT)},
}
_, err := h.GetActiveOrders(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not get open orders: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
}
func TestGetOrderHistory(t *testing.T) {
h.SetDefaults()
TestSetup(t)
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
Currencies: []currency.Pair{currency.NewPair(currency.BTC, currency.USDT)},
}
_, err := h.GetOrderHistory(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not get order history: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
}
// Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them
// ----------------------------------------------------------------------------------------------------------------------------
func areTestAPIKeysSet() bool {
return h.ValidateAPICredentials()
}
func TestSubmitOrder(t *testing.T) {
h.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
if !h.ValidateAPICredentials() {
t.Skip()
}
accounts, err := h.GetAccounts()
if err != nil {
t.Fatalf("Failed to get accounts. Err: %s", err)
}
var orderSubmission = &exchange.OrderSubmission{
Pair: currency.Pair{
Base: currency.BTC,
Quote: currency.USDT,
},
OrderSide: exchange.BuyOrderSide,
OrderType: exchange.LimitOrderType,
Price: 1,
Amount: 1,
ClientID: strconv.FormatInt(accounts[0].ID, 10),
}
response, err := h.SubmitOrder(orderSubmission)
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
t.Errorf("Order failed to be placed: %v", err)
}
}
func TestCancelExchangeOrder(t *testing.T) {
h.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &exchange.OrderCancellation{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
AccountID: "1",
CurrencyPair: currencyPair,
}
err := h.CancelOrder(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not cancel orders: %v", err)
}
}
func TestCancelAllExchangeOrders(t *testing.T) {
h.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &exchange.OrderCancellation{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
AccountID: "1",
CurrencyPair: currencyPair,
}
resp, err := h.CancelAllOrders(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not cancel orders: %v", err)
}
if len(resp.OrderStatus) > 0 {
t.Errorf("%v orders failed to cancel", len(resp.OrderStatus))
}
}
func TestGetAccountInfo(t *testing.T) {
if apiKey == "" || apiSecret == "" {
_, err := h.GetAccountInfo()
if err == nil {
t.Error("Test Failed - GetAccountInfo() error")
}
} else {
_, err := h.GetAccountInfo()
if err != nil {
t.Error("Test Failed - GetAccountInfo() error", err)
}
}
}
func TestModifyOrder(t *testing.T) {
_, err := h.ModifyOrder(&exchange.ModifyOrder{})
if err == nil {
t.Error("Test failed - ModifyOrder() error")
}
}
func TestWithdraw(t *testing.T) {
h.SetDefaults()
TestSetup(t)
withdrawCryptoRequest := exchange.CryptoWithdrawRequest{
GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{
Amount: -1,
Currency: currency.BTC,
Description: "WITHDRAW IT ALL",
},
Address: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
}
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := h.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
t.Errorf("Withdraw failed to be placed: %v", err)
}
}
func TestWithdrawFiat(t *testing.T) {
h.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var withdrawFiatRequest = exchange.FiatWithdrawRequest{}
_, err := h.WithdrawFiatFunds(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
}
}
func TestWithdrawInternationalBank(t *testing.T) {
h.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var withdrawFiatRequest = exchange.FiatWithdrawRequest{}
_, err := h.WithdrawFiatFundsToInternationalBank(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
}
}
func TestGetDepositAddress(t *testing.T) {
_, err := h.GetDepositAddress(currency.BTC, "")
if err == nil {
t.Error("Test Failed - GetDepositAddress() error cannot be nil")
}
}
// TestWsGetAccountsList connects to WS, logs in, gets account list
func TestWsGetAccountsList(t *testing.T) {
setupWsTests(t)
resp, err := h.wsGetAccountsList(currency.NewPairFromString("ethbtc"))
if err != nil {
t.Fatal(err)
}
if resp.ErrorCode > 0 {
t.Error(resp.ErrorMessage)
}
}
// TestWsGetOrderList connects to WS, logs in, gets order list
func TestWsGetOrderList(t *testing.T) {
setupWsTests(t)
resp, err := h.wsGetOrdersList(1, currency.NewPairFromString("ethbtc"))
if err != nil {
t.Fatal(err)
}
if resp.ErrorCode > 0 {
t.Error(resp.ErrorMessage)
}
}
// TestWsGetOrderDetails connects to WS, logs in, gets order details
func TestWsGetOrderDetails(t *testing.T) {
setupWsTests(t)
orderID := "123"
resp, err := h.wsGetOrderDetails(orderID)
if err != nil {
t.Fatal(err)
}
if resp.ErrorCode > 0 && (orderID == "123" && resp.ErrorCode != 10022) {
t.Error(resp.ErrorMessage)
}
}

View File

@@ -1,566 +0,0 @@
package huobihadax
// 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"`
}
// KlineItem stores a kline item
type KlineItem struct {
ID int64 `json:"id"`
Open float64 `json:"open"`
Close float64 `json:"close"`
Low float64 `json:"low"`
High float64 `json:"high"`
Amount float64 `json:"amount"`
Volume float64 `json:"vol"`
Count int `json:"count"`
}
// WsTick stores websocket ticker data
type WsTick struct {
Channel string `json:"ch"`
Timestamp int64 `json:"ts"`
Tick struct {
Amount float64 `json:"amount"`
Close float64 `json:"close"`
Count float64 `json:"count"`
High float64 `json:"high"`
ID float64 `json:"id"`
Low float64 `json:"low"`
Open float64 `json:"open"`
Timestamp float64 `json:"ts"`
Volume float64 `json:"vol"`
} `json:"tick"`
}
// Tickers contain all tickers
type Tickers struct {
Data []Ticker `json:"data"`
}
// Ticker latest ticker data
type Ticker struct {
Amount float64 `json:"amount"`
Close float64 `json:"close"`
Count int64 `json:"count"`
High float64 `json:"high"`
Low float64 `json:"low"`
Open float64 `json:"open"`
Symbol string `json:"symbol"`
Volume float64 `json:"vol"`
}
// 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 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"`
}
// CancelOpenOrdersBatch stores open order batch response data
type CancelOpenOrdersBatch struct {
Data struct {
FailedCount int `json:"failed-count"`
NextID int `json:"next-id"`
SuccessCount int `json:"success-count"`
} `json:"data"`
Status string `json:"status"`
ErrorMessage string `json:"err-msg"`
}
// 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:"timestamp"`
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"`
Symbol string `json:"innovation"`
}
// Account stores the account data
type Account struct {
ID int64 `json:"id"`
Type string `json:"type"`
SubType string `json:"subtype"`
State string `json:"state"`
}
// AccountBalance stores the user all account balance
type AccountBalance struct {
ID int64 `json:"id"`
Type string `json:"type"`
State string `json:"state"`
AccountBalanceDetails []AccountBalanceDetail `json:"list"`
}
// AccountBalanceDetail stores the user account balance
type AccountBalanceDetail struct {
Currency string `json:"currency"`
Type string `json:"type"`
Balance float64 `json:"balance,string"`
}
// AggregatedBalance stores balances of all the sub-account
type AggregatedBalance struct {
Currency string `json:"currency"`
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 float64 `json:"account-id"`
Amount float64 `json:"amount,string"`
Price float64 `json:"price,string"`
CreatedAt int64 `json:"created-at"`
Type string `json:"type"`
FieldAmount float64 `json:"field-amount,string"`
FieldCashAmount float64 `json:"field-cash-amount,string"`
Fieldees float64 `json:"field-fees,string"`
FilledAmount float64 `json:"filled-amount,string"`
FilledCashAmount float64 `json:"filled-cash-amount,string"`
FilledFees float64 `json:"filled-fees,string"`
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"`
}
// SpotNewOrderRequestParams holds the params required to place
// an order
type SpotNewOrderRequestParams struct {
AccountID int `json:"account-id"` // Account ID, obtained using the accounts method. Curency trades use the accountid of the spot account; for loan asset transactions, please use the accountid of the margin account.
Amount float64 `json:"amount"` // The limit price indicates the quantity of the order, the market price indicates how much to buy when the order is paid, and the market price indicates how much the coin is sold when the order is sold.
Price float64 `json:"price"` // Order price, market price does not use this parameter
Source string `json:"source"` // Order source, api: API call, margin-api: loan asset transaction
Symbol string `json:"symbol"` // The symbol to use; example btcusdt, bccbtc......
Type SpotNewOrderRequestParamsType `json:"type"` // Order type as listed below (buy-market, sell-market etc)
}
// SpotNewOrderRequestParamsType order types
type SpotNewOrderRequestParamsType string
var (
// SpotNewOrderRequestTypeBuyMarket buy market order
SpotNewOrderRequestTypeBuyMarket = SpotNewOrderRequestParamsType("buy-market")
// SpotNewOrderRequestTypeSellMarket sell market order
SpotNewOrderRequestTypeSellMarket = SpotNewOrderRequestParamsType("sell-market")
// SpotNewOrderRequestTypeBuyLimit buy limit order
SpotNewOrderRequestTypeBuyLimit = SpotNewOrderRequestParamsType("buy-limit")
// SpotNewOrderRequestTypeSellLimit sell limit order
SpotNewOrderRequestTypeSellLimit = SpotNewOrderRequestParamsType("sell-limit")
)
// KlinesRequestParams represents Klines request data.
type KlinesRequestParams struct {
Symbol string // Symbol; btcusdt, bccbtc......
Period TimeInterval // Kline data time interval; 1min, 5min, 15min......
Size int // Size [1-2000]
}
// TimeInterval base value
type TimeInterval string
// TimeInterval vars
var (
TimeIntervalMinute = TimeInterval("1min")
TimeIntervalFiveMinutes = TimeInterval("5min")
TimeIntervalFifteenMinutes = TimeInterval("15min")
TimeIntervalThirtyMinutes = TimeInterval("30min")
TimeIntervalHour = TimeInterval("60min")
TimeIntervalDay = TimeInterval("1day")
TimeIntervalWeek = TimeInterval("1week")
TimeIntervalMohth = TimeInterval("1mon")
TimeIntervalYear = TimeInterval("1year")
)
// History defines currency deposit or withdrawal data
type History struct {
ID int64 `json:"id"`
Type string `json:"type"`
Currency string `json:"currency"`
TxHash string `json:"tx-hash"`
Amount float64 `json:"amount"`
Address string `json:"address"`
Fee float64 `json:"fee"`
State string `json:"state"`
CreatedAt int64 `json:"created-at"`
UpdatedAt int64 `json:"Updated-at"`
}
// WsRequest defines a request data structure
type WsRequest struct {
Topic string `json:"req,omitempty"`
Subscribe string `json:"sub,omitempty"`
Unsubscribe string `json:"unsub,omitempty"`
ClientGeneratedID string `json:"id,omitempty"`
}
// WsResponse defines a response from the websocket connection when there
// is an error
type WsResponse struct {
TS int64 `json:"ts"`
Status string `json:"status"`
ErrorCode interface{} `json:"err-code"`
ErrorMessage string `json:"err-msg"`
Ping int64 `json:"ping"`
Channel string `json:"ch"`
Subscribed string `json:"subbed"`
}
// WsHeartBeat defines a heartbeat request
type WsHeartBeat struct {
ClientNonce int64 `json:"ping"`
}
// WsDepth defines market depth websocket response
type WsDepth struct {
Channel string `json:"ch"`
Timestamp int64 `json:"ts"`
Tick struct {
Bids []interface{} `json:"bids"`
Asks []interface{} `json:"asks"`
Timestamp int64 `json:"ts"`
Version int64 `json:"version"`
} `json:"tick"`
}
// WsKline defines market kline websocket response
type WsKline struct {
Channel string `json:"ch"`
Timestamp int64 `json:"ts"`
Tick struct {
ID int64 `json:"id"`
Open float64 `json:"open"`
Close float64 `json:"close"`
Low float64 `json:"low"`
High float64 `json:"high"`
Amount float64 `json:"amount"`
Volume float64 `json:"vol"`
Count int64 `json:"count"`
}
}
// WsTrade defines market trade websocket response
type WsTrade struct {
Channel string `json:"ch"`
Timestamp int64 `json:"ts"`
Tick struct {
ID int64 `json:"id"`
Timestamp int64 `json:"ts"`
Data []struct {
Amount float64 `json:"amount"`
Timestamp int64 `json:"ts"`
ID float64 `json:"id"`
Price float64 `json:"price"`
Direction string `json:"direction"`
} `json:"data"`
}
}
// WsAuthenticationRequest data for login
type WsAuthenticationRequest struct {
Op string `json:"op"`
AccessKeyID string `json:"AccessKeyId"`
SignatureMethod string `json:"SignatureMethod"`
SignatureVersion string `json:"SignatureVersion"`
Timestamp string `json:"Timestamp"`
Signature string `json:"Signature"`
ClientID int64 `json:"cid,string,omitempty"`
}
// WsMessage defines read data from the websocket connection
type WsMessage struct {
Raw []byte
URL string
}
// WsAuthenticatedSubscriptionRequest request for subscription on authenticated connection
type WsAuthenticatedSubscriptionRequest struct {
Op string `json:"op"`
AccessKeyID string `json:"AccessKeyId"`
SignatureMethod string `json:"SignatureMethod"`
SignatureVersion string `json:"SignatureVersion"`
Timestamp string `json:"Timestamp"`
Signature string `json:"Signature"`
Topic string `json:"topic"`
ClientID int64 `json:"cid,string,omitempty"`
}
// WsAuthenticatedAccountsListRequest request for account list authenticated connection
type WsAuthenticatedAccountsListRequest struct {
Op string `json:"op"`
AccessKeyID string `json:"AccessKeyId"`
SignatureMethod string `json:"SignatureMethod"`
SignatureVersion string `json:"SignatureVersion"`
Timestamp string `json:"Timestamp"`
Signature string `json:"Signature"`
Topic string `json:"topic"`
Symbol string `json:"symbol"`
ClientID int64 `json:"cid,string,omitempty"`
}
// WsAuthenticatedOrderDetailsRequest request for order details authenticated connection
type WsAuthenticatedOrderDetailsRequest struct {
Op string `json:"op"`
AccessKeyID string `json:"AccessKeyId"`
SignatureMethod string `json:"SignatureMethod"`
SignatureVersion string `json:"SignatureVersion"`
Timestamp string `json:"Timestamp"`
Signature string `json:"Signature"`
Topic string `json:"topic"`
OrderID string `json:"order-id"`
ClientID int64 `json:"cid,string,omitempty"`
}
// WsAuthenticatedOrdersListRequest request for orderslist authenticated connection
type WsAuthenticatedOrdersListRequest struct {
Op string `json:"op"`
AccessKeyID string `json:"AccessKeyId"`
SignatureMethod string `json:"SignatureMethod"`
SignatureVersion string `json:"SignatureVersion"`
Timestamp string `json:"Timestamp"`
Signature string `json:"Signature"`
Topic string `json:"topic"`
States string `json:"states"`
AccountID int64 `json:"account-id"`
Symbol string `json:"symbol"`
ClientID int64 `json:"cid,string,omitempty"`
}
// WsAuthenticatedDataResponse response from authenticated connection
type WsAuthenticatedDataResponse struct {
Op string `json:"op,omitempty"`
Ts int64 `json:"ts,omitempty"`
Topic string `json:"topic,omitempty"`
ErrorCode int64 `json:"err-code,omitempty"`
ErrorMessage string `json:"err-msg,omitempty"`
Ping int64 `json:"ping,omitempty"`
ClientID int64 `json:"cid,string,omitempty"`
}
// WsAuthenticatedAccountsResponse response from Accounts authenticated subscription
type WsAuthenticatedAccountsResponse struct {
WsAuthenticatedDataResponse
Data WsAuthenticatedAccountsResponseData `json:"data"`
}
// WsAuthenticatedAccountsResponseData account data
type WsAuthenticatedAccountsResponseData struct {
Event string `json:"event"`
List []WsAuthenticatedAccountsResponseDataList `json:"list"`
}
// WsAuthenticatedAccountsResponseDataList detailed account data
type WsAuthenticatedAccountsResponseDataList struct {
AccountID int64 `json:"account-id"`
Currency string `json:"currency"`
Type string `json:"type"`
Balance float64 `json:"balance,string"`
}
// WsAuthenticatedOrdersUpdateResponse response from OrdersUpdate authenticated subscription
type WsAuthenticatedOrdersUpdateResponse struct {
WsAuthenticatedDataResponse
Data WsAuthenticatedOrdersUpdateResponseData `json:"data"`
}
// WsAuthenticatedOrdersUpdateResponseData order updatedata
type WsAuthenticatedOrdersUpdateResponseData struct {
UnfilledAmount float64 `json:"unfilled-amount,string"`
FilledAmount float64 `json:"filled-amount,string"`
Price float64 `json:"price,string"`
OrderID int64 `json:"order-id"`
Symbol string `json:"symbol"`
MatchID int64 `json:"match-id"`
FilledCashAmount float64 `json:"filled-cash-amount,string"`
Role string `json:"role"`
OrderState string `json:"order-state"`
}
// WsAuthenticatedOrdersResponse response from Orders authenticated subscription
type WsAuthenticatedOrdersResponse struct {
WsAuthenticatedDataResponse
Data []WsAuthenticatedOrdersResponseData `json:"data"`
}
// WsAuthenticatedOrdersResponseData order data
type WsAuthenticatedOrdersResponseData struct {
SeqID int64 `json:"seq-id"`
OrderID int64 `json:"order-id"`
Symbol string `json:"symbol"`
AccountID int64 `json:"account-id"`
OrderAmount float64 `json:"order-amount,string"`
OrderPrice float64 `json:"order-price,string"`
CreatedAt int64 `json:"created-at"`
OrderType string `json:"order-type"`
OrderSource string `json:"order-source"`
OrderState string `json:"order-state"`
Role string `json:"role"`
Price float64 `json:"price,string"`
FilledAmount float64 `json:"filled-amount,string"`
UnfilledAmount float64 `json:"unfilled-amount,string"`
FilledCashAmount float64 `json:"filled-cash-amount,string"`
FilledFees float64 `json:"filled-fees,string"`
}
// WsAuthenticatedAccountsListResponse response from AccountsList authenticated endpoint
type WsAuthenticatedAccountsListResponse struct {
WsAuthenticatedDataResponse
Data []WsAuthenticatedAccountsListResponseData `json:"data"`
}
// WsAuthenticatedAccountsListResponseData account data
type WsAuthenticatedAccountsListResponseData struct {
ID int64 `json:"id"`
Type string `json:"type"`
State string `json:"state"`
List []WsAuthenticatedAccountsListResponseDataList `json:"list"`
}
// WsAuthenticatedAccountsListResponseDataList detailed account data
type WsAuthenticatedAccountsListResponseDataList struct {
Currency string `json:"currency"`
Type string `json:"type"`
Balance float64 `json:"balance,string"`
}
// WsAuthenticatedOrdersListResponse response from OrdersList authenticated endpoint
type WsAuthenticatedOrdersListResponse struct {
WsAuthenticatedDataResponse
Data []WsAuthenticatedOrdersListResponseData `json:"data"`
}
// WsAuthenticatedOrdersListResponseData contains order details
type WsAuthenticatedOrdersListResponseData struct {
ID int64 `json:"id"`
Symbol string `json:"symbol"`
AccountID int64 `json:"account-id"`
Amount float64 `json:"amount,string"`
Price float64 `json:"price,string"`
CreatedAt int64 `json:"created-at"`
Type string `json:"type"`
FilledAmount float64 `json:"filled-amount,string"`
FilledCashAmount float64 `json:"filled-cash-amount,string"`
FilledFees float64 `json:"filled-fees,string"`
FinishedAt int64 `json:"finished-at"`
Source string `json:"source"`
State string `json:"state"`
CanceledAt int64 `json:"canceled-at"`
}
// WsAuthenticatedOrderDetailResponse response from OrderDetail authenticated endpoint
type WsAuthenticatedOrderDetailResponse struct {
WsAuthenticatedDataResponse
Data WsAuthenticatedOrdersListResponseData `json:"data"`
}
// WsPong sent for pong messages
type WsPong struct {
Pong int64 `json:"pong"`
}

View File

@@ -1,502 +0,0 @@
package huobihadax
import (
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"time"
"github.com/gorilla/websocket"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
log "github.com/thrasher-corp/gocryptotrader/logger"
)
// WS URL values
const (
HuobiHadaxSocketIOAddress = "wss://api.hadax.com/ws"
wsMarketKline = "market.%s.kline.1min"
wsMarketDepth = "market.%s.depth.step0"
wsMarketTrade = "market.%s.trade.detail"
wsMarketTicker = "market.%s.detail"
wsAccountsOrdersBaseURL = "wss://api.huobi.pro"
wsAccountsOrdersEndPoint = "/ws/v1"
wsAccountsList = "accounts.list"
wsOrdersList = "orders.list"
wsOrdersDetail = "orders.detail"
wsAccountsOrdersURL = wsAccountsOrdersBaseURL + wsAccountsOrdersEndPoint
wsAccountListEndpoint = wsAccountsOrdersEndPoint + "/" + wsAccountsList
wsOrdersListEndpoint = wsAccountsOrdersEndPoint + "/" + wsOrdersList
wsOrdersDetailEndpoint = wsAccountsOrdersEndPoint + "/" + wsOrdersDetail
wsDateTimeFormatting = "2006-01-02T15:04:05"
signatureMethod = "HmacSHA256"
signatureVersion = "2"
requestOp = "req"
authOp = "auth"
loginDelay = 50 * time.Millisecond
rateLimit = 20
)
// Instantiates a communications channel between websocket connections
var comms = make(chan WsMessage, 1)
// WsConnect initiates a new websocket connection
func (h *HUOBIHADAX) WsConnect() error {
if !h.Websocket.IsEnabled() || !h.IsEnabled() {
return errors.New(wshandler.WebsocketNotEnabled)
}
var dialer websocket.Dialer
err := h.wsDial(&dialer)
if err != nil {
return err
}
err = h.wsAuthenticatedDial(&dialer)
if err != nil {
log.Errorf(log.ExchangeSys, "%v - authenticated dial failed: %v\n", h.Name, err)
}
err = h.wsLogin()
if err != nil {
log.Errorf(log.ExchangeSys, "%v - authentication failed: %v\n", h.Name, err)
}
go h.WsHandleData()
h.GenerateDefaultSubscriptions()
return nil
}
func (h *HUOBIHADAX) wsDial(dialer *websocket.Dialer) error {
err := h.WebsocketConn.Dial(dialer, http.Header{})
if err != nil {
return err
}
go h.wsMultiConnectionFunnel(h.WebsocketConn, HuobiHadaxSocketIOAddress)
return nil
}
func (h *HUOBIHADAX) wsAuthenticatedDial(dialer *websocket.Dialer) error {
if !h.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) {
return fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", h.Name)
}
err := h.AuthenticatedWebsocketConn.Dial(dialer, http.Header{})
if err != nil {
return err
}
go h.wsMultiConnectionFunnel(h.AuthenticatedWebsocketConn, wsAccountsOrdersURL)
return nil
}
// wsMultiConnectionFunnel manages data from multiple endpoints and passes it to a channel
func (h *HUOBIHADAX) wsMultiConnectionFunnel(ws *wshandler.WebsocketConnection, url string) {
h.Websocket.Wg.Add(1)
defer h.Websocket.Wg.Done()
for {
select {
case <-h.Websocket.ShutdownC:
return
default:
resp, err := ws.ReadMessage()
if err != nil {
h.Websocket.DataHandler <- err
return
}
h.Websocket.TrafficAlert <- struct{}{}
comms <- WsMessage{Raw: resp.Raw, URL: url}
}
}
}
// WsHandleData handles data read from the websocket connection
func (h *HUOBIHADAX) WsHandleData() {
h.Websocket.Wg.Add(1)
defer h.Websocket.Wg.Done()
for {
select {
case <-h.Websocket.ShutdownC:
return
case resp := <-comms:
if h.Verbose {
log.Debugf(log.ExchangeSys, "%v: %v: %v", h.Name, resp.URL, string(resp.Raw))
}
switch resp.URL {
case HuobiHadaxSocketIOAddress:
h.wsHandleMarketData(resp)
case wsAccountsOrdersURL:
h.wsHandleAuthenticatedData(resp)
}
}
}
}
func (h *HUOBIHADAX) wsHandleAuthenticatedData(resp WsMessage) {
var init WsAuthenticatedDataResponse
err := common.JSONDecode(resp.Raw, &init)
if err != nil {
h.Websocket.DataHandler <- err
return
}
if init.Ping != 0 {
err = h.WebsocketConn.SendMessage(WsPong{Pong: init.Ping})
if err != nil {
log.Error(log.ExchangeSys, err)
}
return
}
if init.ErrorMessage == "api-signature-not-valid" {
h.Websocket.SetCanUseAuthenticatedEndpoints(false)
}
if init.Op == "sub" {
if h.Verbose {
log.Debugf(log.ExchangeSys, "%v: %v: Successfully subscribed to %v", h.Name, resp.URL, init.Topic)
}
return
}
if init.ClientID > 0 {
h.AuthenticatedWebsocketConn.AddResponseWithID(init.ClientID, resp.Raw)
return
}
switch {
case strings.EqualFold(init.Op, authOp):
h.Websocket.SetCanUseAuthenticatedEndpoints(true)
var response WsAuthenticatedDataResponse
err := common.JSONDecode(resp.Raw, &response)
if err != nil {
h.Websocket.DataHandler <- err
}
h.Websocket.DataHandler <- response
case strings.EqualFold(init.Topic, "accounts"):
var response WsAuthenticatedAccountsResponse
err := common.JSONDecode(resp.Raw, &response)
if err != nil {
h.Websocket.DataHandler <- err
}
h.Websocket.DataHandler <- response
case strings.Contains(init.Topic, "orders") &&
strings.Contains(init.Topic, "update"):
var response WsAuthenticatedOrdersUpdateResponse
err := common.JSONDecode(resp.Raw, &response)
if err != nil {
h.Websocket.DataHandler <- err
}
h.Websocket.DataHandler <- response
case strings.Contains(init.Topic, "orders"):
var response WsAuthenticatedOrdersResponse
err := common.JSONDecode(resp.Raw, &response)
if err != nil {
h.Websocket.DataHandler <- err
}
h.Websocket.DataHandler <- response
}
}
func (h *HUOBIHADAX) wsHandleMarketData(resp WsMessage) {
var init WsResponse
err := common.JSONDecode(resp.Raw, &init)
if err != nil {
h.Websocket.DataHandler <- err
return
}
if init.Status == "error" {
h.Websocket.DataHandler <- fmt.Errorf("%v %v Websocket error %s %s",
h.Name,
resp.URL,
init.ErrorCode,
init.ErrorMessage)
return
}
if init.Subscribed != "" {
return
}
if init.Ping != 0 {
err = h.WebsocketConn.SendMessage(WsPong{Pong: init.Ping})
if err != nil {
log.Error(log.ExchangeSys, err)
}
return
}
switch {
case strings.Contains(init.Channel, "depth"):
var depth WsDepth
err := common.JSONDecode(resp.Raw, &depth)
if err != nil {
h.Websocket.DataHandler <- err
return
}
data := strings.Split(depth.Channel, ".")
h.WsProcessOrderbook(&depth, data[1])
case strings.Contains(init.Channel, "kline"):
var kline WsKline
err := common.JSONDecode(resp.Raw, &kline)
if err != nil {
h.Websocket.DataHandler <- err
return
}
data := strings.Split(kline.Channel, ".")
h.Websocket.DataHandler <- wshandler.KlineData{
Timestamp: time.Unix(0, kline.Timestamp),
Exchange: h.GetName(),
AssetType: asset.Spot,
Pair: currency.NewPairFromFormattedPairs(data[1],
h.GetEnabledPairs(asset.Spot), h.GetPairFormat(asset.Spot, true)),
OpenPrice: kline.Tick.Open,
ClosePrice: kline.Tick.Close,
HighPrice: kline.Tick.High,
LowPrice: kline.Tick.Low,
Volume: kline.Tick.Volume,
}
case strings.Contains(init.Channel, "trade.detail"):
var trade WsTrade
err := common.JSONDecode(resp.Raw, &trade)
if err != nil {
h.Websocket.DataHandler <- err
return
}
data := strings.Split(trade.Channel, ".")
h.Websocket.DataHandler <- wshandler.TradeData{
Exchange: h.GetName(),
AssetType: asset.Spot,
CurrencyPair: currency.NewPairFromFormattedPairs(data[1],
h.GetEnabledPairs(asset.Spot), h.GetPairFormat(asset.Spot, true)),
Timestamp: time.Unix(0, trade.Tick.Timestamp),
}
case strings.Contains(init.Channel, "detail"):
var ticker WsTick
err := common.JSONDecode(resp.Raw, &ticker)
if err != nil {
h.Websocket.DataHandler <- err
return
}
data := strings.Split(ticker.Channel, ".")
h.Websocket.DataHandler <- wshandler.TickerData{
Exchange: h.Name,
Open: ticker.Tick.Open,
Close: ticker.Tick.Close,
Volume: ticker.Tick.Amount,
QuoteVolume: ticker.Tick.Volume,
High: ticker.Tick.High,
Low: ticker.Tick.Low,
Timestamp: time.Unix(0, ticker.Timestamp),
AssetType: asset.Spot,
Pair: currency.NewPairFromFormattedPairs(data[1],
h.GetEnabledPairs(asset.Spot), h.GetPairFormat(asset.Spot, true)),
}
}
}
// WsProcessOrderbook processes new orderbook data
func (h *HUOBIHADAX) WsProcessOrderbook(update *WsDepth, symbol string) error {
p := currency.NewPairFromFormattedPairs(symbol,
h.GetEnabledPairs(asset.Spot), h.GetPairFormat(asset.Spot, true))
var bids, asks []orderbook.Item
for i := 0; i < len(update.Tick.Bids); i++ {
bidLevel := update.Tick.Bids[i].([]interface{})
bids = append(bids, orderbook.Item{Price: bidLevel[0].(float64),
Amount: bidLevel[0].(float64)})
}
for i := 0; i < len(update.Tick.Asks); i++ {
askLevel := update.Tick.Asks[i].([]interface{})
asks = append(asks, orderbook.Item{Price: askLevel[0].(float64),
Amount: askLevel[0].(float64)})
}
var newOrderBook orderbook.Base
newOrderBook.Asks = asks
newOrderBook.Bids = bids
newOrderBook.Pair = p
err := h.Websocket.Orderbook.LoadSnapshot(&newOrderBook, true)
if err != nil {
return err
}
h.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{
Pair: p,
Exchange: h.GetName(),
Asset: asset.Spot,
}
return nil
}
// GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions()
func (h *HUOBIHADAX) GenerateDefaultSubscriptions() {
var channels = []string{wsMarketKline, wsMarketDepth, wsMarketTrade, wsMarketTicker}
var subscriptions []wshandler.WebsocketChannelSubscription
if h.Websocket.CanUseAuthenticatedEndpoints() {
channels = append(channels, "orders.%v", "orders.%v.update")
subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{
Channel: "accounts",
})
}
enabledCurrencies := h.GetEnabledPairs(asset.Spot)
for i := range channels {
for j := range enabledCurrencies {
enabledCurrencies[j].Delimiter = ""
channel := fmt.Sprintf(channels[i], enabledCurrencies[j].Lower().String())
subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{
Channel: channel,
Currency: enabledCurrencies[j],
})
}
}
h.Websocket.SubscribeToChannels(subscriptions)
}
// Subscribe sends a websocket message to receive data from the channel
func (h *HUOBIHADAX) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error {
if strings.Contains(channelToSubscribe.Channel, "orders.") ||
strings.Contains(channelToSubscribe.Channel, "accounts") {
return h.wsAuthenticatedSubscribe("sub", wsAccountsOrdersEndPoint+channelToSubscribe.Channel, channelToSubscribe.Channel)
}
return h.WebsocketConn.SendMessage(WsRequest{Subscribe: channelToSubscribe.Channel})
}
// Unsubscribe sends a websocket message to stop receiving data from the channel
func (h *HUOBIHADAX) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error {
if strings.Contains(channelToSubscribe.Channel, "orders.") ||
strings.Contains(channelToSubscribe.Channel, "accounts") {
return h.wsAuthenticatedSubscribe("unsub", wsAccountsOrdersEndPoint+channelToSubscribe.Channel, channelToSubscribe.Channel)
}
return h.WebsocketConn.SendMessage(WsRequest{Unsubscribe: channelToSubscribe.Channel})
}
func (h *HUOBIHADAX) wsLogin() error {
if !h.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) {
return fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", h.Name)
}
h.Websocket.SetCanUseAuthenticatedEndpoints(true)
timestamp := time.Now().UTC().Format(wsDateTimeFormatting)
request := WsAuthenticationRequest{
Op: authOp,
AccessKeyID: h.API.Credentials.Key,
SignatureMethod: signatureMethod,
SignatureVersion: signatureVersion,
Timestamp: timestamp,
}
hmac := h.wsGenerateSignature(timestamp, wsAccountsOrdersEndPoint)
request.Signature = crypto.Base64Encode(hmac)
err := h.AuthenticatedWebsocketConn.SendMessage(request)
if err != nil {
h.Websocket.SetCanUseAuthenticatedEndpoints(false)
return err
}
time.Sleep(loginDelay)
return nil
}
func (h *HUOBIHADAX) wsGenerateSignature(timestamp, endpoint string) []byte {
values := url.Values{}
values.Set("AccessKeyId", h.API.Credentials.Key)
values.Set("SignatureMethod", signatureMethod)
values.Set("SignatureVersion", signatureVersion)
values.Set("Timestamp", timestamp)
host := "api.huobi.pro"
payload := fmt.Sprintf("%s\n%s\n%s\n%s",
"GET", host, endpoint, values.Encode())
return crypto.GetHMAC(crypto.HashSHA256, []byte(payload), []byte(h.API.Credentials.Secret))
}
func (h *HUOBIHADAX) wsAuthenticatedSubscribe(operation, endpoint, topic string) error {
timestamp := time.Now().UTC().Format(wsDateTimeFormatting)
request := WsAuthenticatedSubscriptionRequest{
Op: operation,
AccessKeyID: h.API.Credentials.Key,
SignatureMethod: signatureMethod,
SignatureVersion: signatureVersion,
Timestamp: timestamp,
Topic: topic,
}
hmac := h.wsGenerateSignature(timestamp, endpoint)
request.Signature = crypto.Base64Encode(hmac)
return h.AuthenticatedWebsocketConn.SendMessage(request)
}
func (h *HUOBIHADAX) wsGetAccountsList(pair currency.Pair) (*WsAuthenticatedAccountsListResponse, error) {
if !h.Websocket.CanUseAuthenticatedEndpoints() {
return nil, fmt.Errorf("%v not authenticated cannot get accounts list", h.Name)
}
timestamp := time.Now().UTC().Format(wsDateTimeFormatting)
request := WsAuthenticatedAccountsListRequest{
Op: requestOp,
AccessKeyID: h.API.Credentials.Key,
SignatureMethod: signatureMethod,
SignatureVersion: signatureVersion,
Timestamp: timestamp,
Topic: wsAccountsList,
Symbol: h.FormatExchangeCurrency(pair, asset.Spot).String(),
}
hmac := h.wsGenerateSignature(timestamp, wsAccountListEndpoint)
request.Signature = crypto.Base64Encode(hmac)
request.ClientID = h.AuthenticatedWebsocketConn.GenerateMessageID(true)
resp, err := h.AuthenticatedWebsocketConn.SendMessageReturnResponse(request.ClientID, request)
if err != nil {
return nil, err
}
var response WsAuthenticatedAccountsListResponse
err = common.JSONDecode(resp, &response)
return &response, err
}
func (h *HUOBIHADAX) wsGetOrdersList(accountID int64, pair currency.Pair) (*WsAuthenticatedOrdersResponse, error) {
if !h.Websocket.CanUseAuthenticatedEndpoints() {
return nil, fmt.Errorf("%v not authenticated cannot get orders list", h.Name)
}
timestamp := time.Now().UTC().Format(wsDateTimeFormatting)
request := WsAuthenticatedOrdersListRequest{
Op: requestOp,
AccessKeyID: h.API.Credentials.Key,
SignatureMethod: signatureMethod,
SignatureVersion: signatureVersion,
Timestamp: timestamp,
Topic: wsOrdersList,
AccountID: accountID,
Symbol: h.FormatExchangeCurrency(pair, asset.Spot).String(),
States: "submitted,partial-filled",
}
hmac := h.wsGenerateSignature(timestamp, wsOrdersListEndpoint)
request.Signature = crypto.Base64Encode(hmac)
request.ClientID = h.AuthenticatedWebsocketConn.GenerateMessageID(true)
resp, err := h.AuthenticatedWebsocketConn.SendMessageReturnResponse(request.ClientID, request)
if err != nil {
return nil, err
}
var response WsAuthenticatedOrdersResponse
err = common.JSONDecode(resp, &response)
return &response, err
}
func (h *HUOBIHADAX) wsGetOrderDetails(orderID string) (*WsAuthenticatedOrderDetailResponse, error) {
if !h.Websocket.CanUseAuthenticatedEndpoints() {
return nil, fmt.Errorf("%v not authenticated cannot get order details", h.Name)
}
timestamp := time.Now().UTC().Format(wsDateTimeFormatting)
request := WsAuthenticatedOrderDetailsRequest{
Op: requestOp,
AccessKeyID: h.API.Credentials.Key,
SignatureMethod: signatureMethod,
SignatureVersion: signatureVersion,
Timestamp: timestamp,
Topic: wsOrdersDetail,
OrderID: orderID,
}
hmac := h.wsGenerateSignature(timestamp, wsOrdersDetailEndpoint)
request.Signature = crypto.Base64Encode(hmac)
request.ClientID = h.AuthenticatedWebsocketConn.GenerateMessageID(true)
resp, err := h.AuthenticatedWebsocketConn.SendMessageReturnResponse(request.ClientID, request)
if err != nil {
return nil, err
}
var response WsAuthenticatedOrderDetailResponse
err = common.JSONDecode(resp, &response)
return &response, err
}

View File

@@ -1,631 +0,0 @@
package huobihadax
import (
"errors"
"fmt"
"strconv"
"strings"
"sync"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
log "github.com/thrasher-corp/gocryptotrader/logger"
)
// GetDefaultConfig returns a default exchange config
func (h *HUOBIHADAX) GetDefaultConfig() (*config.ExchangeConfig, error) {
h.SetDefaults()
exchCfg := new(config.ExchangeConfig)
exchCfg.Name = h.Name
exchCfg.HTTPTimeout = exchange.DefaultHTTPTimeout
exchCfg.BaseCurrencies = h.BaseCurrencies
err := h.SetupDefaults(exchCfg)
if err != nil {
return nil, err
}
if h.Features.Supports.RESTCapabilities.AutoPairUpdates {
err = h.UpdateTradablePairs(true)
if err != nil {
return nil, err
}
}
return exchCfg, nil
}
// SetDefaults sets default values for the exchange
func (h *HUOBIHADAX) SetDefaults() {
h.Name = "HuobiHadax"
h.Enabled = true
h.Verbose = true
h.API.CredentialsValidator.RequiresKey = true
h.API.CredentialsValidator.RequiresSecret = true
h.CurrencyPairs = currency.PairsManager{
AssetTypes: asset.Items{
asset.Spot,
},
UseGlobalFormat: true,
RequestFormat: &currency.PairFormat{
Uppercase: false,
},
ConfigFormat: &currency.PairFormat{
Delimiter: "-",
Uppercase: true,
},
}
h.Features = exchange.Features{
Supports: exchange.FeaturesSupported{
REST: true,
Websocket: true,
RESTCapabilities: exchange.ProtocolFeatures{
AutoPairUpdates: true,
TickerBatching: true,
},
WithdrawPermissions: exchange.AutoWithdrawCryptoWithSetup |
exchange.NoFiatWithdrawals,
},
Enabled: exchange.FeaturesEnabled{
AutoPairUpdates: true,
},
}
h.Requester = request.New(h.Name,
request.NewRateLimit(time.Second*10, huobihadaxAuthRate),
request.NewRateLimit(time.Second*10, huobihadaxUnauthRate),
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
h.API.Endpoints.URLDefault = huobihadaxAPIURL
h.API.Endpoints.URL = h.API.Endpoints.URLDefault
h.Websocket = wshandler.New()
h.Websocket.Functionality = wshandler.WebsocketKlineSupported |
wshandler.WebsocketTradeDataSupported |
wshandler.WebsocketOrderbookSupported |
wshandler.WebsocketSubscribeSupported |
wshandler.WebsocketUnsubscribeSupported |
wshandler.WebsocketAuthenticatedEndpointsSupported |
wshandler.WebsocketAccountDataSupported |
wshandler.WebsocketMessageCorrelationSupported
h.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
h.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
h.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit
}
// Setup sets user configuration
func (h *HUOBIHADAX) Setup(exch *config.ExchangeConfig) error {
if !exch.Enabled {
h.SetEnabled(false)
return nil
}
err := h.SetupDefaults(exch)
if err != nil {
return err
}
err = h.Websocket.Setup(h.WsConnect,
h.Subscribe,
h.Unsubscribe,
exch.Name,
exch.Features.Enabled.Websocket,
exch.Verbose,
HuobiHadaxSocketIOAddress,
exch.API.Endpoints.WebsocketURL,
exch.API.AuthenticatedWebsocketSupport)
if err != nil {
return err
}
h.WebsocketConn = &wshandler.WebsocketConnection{
ExchangeName: h.Name,
URL: HuobiHadaxSocketIOAddress,
ProxyURL: h.Websocket.GetProxyAddress(),
Verbose: h.Verbose,
RateLimit: rateLimit,
ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
}
h.AuthenticatedWebsocketConn = &wshandler.WebsocketConnection{
ExchangeName: h.Name,
URL: wsAccountsOrdersURL,
ProxyURL: h.Websocket.GetProxyAddress(),
Verbose: h.Verbose,
RateLimit: rateLimit,
ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
}
h.Websocket.Orderbook.Setup(
exch.WebsocketOrderbookBufferLimit,
false,
false,
false,
false,
exch.Name)
return nil
}
// Start starts the HUOBIHADAX go routine
func (h *HUOBIHADAX) Start(wg *sync.WaitGroup) {
wg.Add(1)
go func() {
h.Run()
wg.Done()
}()
}
// Run implements the HUOBIHADAX wrapper
func (h *HUOBIHADAX) Run() {
if h.Verbose {
h.PrintEnabledPairs()
}
if !h.GetEnabledFeatures().AutoPairUpdates {
return
}
err := h.UpdateTradablePairs(false)
if err != nil {
log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", h.Name, err)
}
}
// FetchTradablePairs returns a list of the exchanges tradable pairs
func (h *HUOBIHADAX) FetchTradablePairs(asset asset.Item) ([]string, error) {
symbols, err := h.GetSymbols()
if err != nil {
return nil, err
}
var pairs []string
for x := range symbols {
pairs = append(pairs, fmt.Sprintf("%v%v%v", symbols[x].BaseCurrency,
h.GetPairFormat(asset, false).Delimiter, symbols[x].QuoteCurrency))
}
return pairs, nil
}
// UpdateTradablePairs updates the exchanges available pairs and stores
// them in the exchanges config
func (h *HUOBIHADAX) UpdateTradablePairs(forceUpdate bool) error {
pairs, err := h.FetchTradablePairs(asset.Spot)
if err != nil {
return err
}
return h.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpdate)
}
// UpdateTicker updates and returns the ticker for a currency pair
func (h *HUOBIHADAX) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) {
var tickerPrice ticker.Price
tickers, err := h.GetTickers()
if err != nil {
return tickerPrice, err
}
pairs := h.GetEnabledPairs(assetType)
for i := range pairs {
for j := range tickers.Data {
pairFmt := h.FormatExchangeCurrency(pairs[i], assetType).String()
if pairFmt != tickers.Data[j].Symbol {
continue
}
tickerPrice := ticker.Price{
High: tickers.Data[j].High,
Low: tickers.Data[j].Low,
Volume: tickers.Data[j].Volume,
Open: tickers.Data[j].Open,
Close: tickers.Data[j].Close,
Pair: pairs[i],
}
err = ticker.ProcessTicker(h.GetName(), &tickerPrice, assetType)
if err != nil {
log.Error(log.Ticker, err)
}
}
}
return ticker.GetTicker(h.Name, p, assetType)
}
// FetchTicker returns the ticker for a currency pair
func (h *HUOBIHADAX) FetchTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) {
tickerNew, err := ticker.GetTicker(h.GetName(), p, assetType)
if err != nil {
return h.UpdateTicker(p, assetType)
}
return tickerNew, nil
}
// FetchOrderbook returns orderbook base on the currency pair
func (h *HUOBIHADAX) FetchOrderbook(p currency.Pair, assetType asset.Item) (orderbook.Base, error) {
ob, err := orderbook.Get(h.GetName(), p, assetType)
if err != nil {
return h.UpdateOrderbook(p, assetType)
}
return ob, nil
}
// UpdateOrderbook updates and returns the orderbook for a currency pair
func (h *HUOBIHADAX) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbook.Base, error) {
var orderBook orderbook.Base
orderbookNew, err := h.GetDepth(h.FormatExchangeCurrency(p, assetType).String(), "step1")
if err != nil {
return orderBook, err
}
for x := range orderbookNew.Bids {
data := orderbookNew.Bids[x]
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data[1], Price: data[0]})
}
for x := range orderbookNew.Asks {
data := orderbookNew.Asks[x]
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data[1], Price: data[0]})
}
orderBook.Pair = p
orderBook.ExchangeName = h.GetName()
orderBook.AssetType = assetType
err = orderBook.Process()
if err != nil {
return orderBook, err
}
return orderbook.Get(h.Name, p, assetType)
}
// GetAccountID returns the account info
func (h *HUOBIHADAX) GetAccountID() ([]Account, error) {
acc, err := h.GetAccounts()
if err != nil {
return nil, err
}
if len(acc) < 1 {
return nil, errors.New("no account returned")
}
return acc, nil
}
// GetAccountInfo retrieves balances for all enabled currencies for the
// HUOBIHADAX exchange
func (h *HUOBIHADAX) GetAccountInfo() (exchange.AccountInfo, error) {
var info exchange.AccountInfo
info.Exchange = h.GetName()
accounts, err := h.GetAccountID()
if err != nil {
return info, err
}
for _, account := range accounts {
var acc exchange.Account
acc.ID = strconv.FormatInt(account.ID, 10)
balances, err := h.GetAccountBalance(acc.ID)
if err != nil {
return info, err
}
var currencyDetails []exchange.AccountCurrencyInfo
for _, balance := range balances {
var frozen bool
if balance.Type == "frozen" {
frozen = true
}
var updated bool
for i := range currencyDetails {
if currencyDetails[i].CurrencyName == currency.NewCode(balance.Currency) {
if frozen {
currencyDetails[i].Hold = balance.Balance
} else {
currencyDetails[i].TotalValue = balance.Balance
}
updated = true
}
}
if updated {
continue
}
if frozen {
currencyDetails = append(currencyDetails,
exchange.AccountCurrencyInfo{
CurrencyName: currency.NewCode(balance.Currency),
Hold: balance.Balance,
})
} else {
currencyDetails = append(currencyDetails,
exchange.AccountCurrencyInfo{
CurrencyName: currency.NewCode(balance.Currency),
TotalValue: balance.Balance,
})
}
}
acc.Currencies = currencyDetails
info.Accounts = append(info.Accounts, acc)
}
return info, nil
}
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (h *HUOBIHADAX) GetFundingHistory() ([]exchange.FundHistory, error) {
var fundHistory []exchange.FundHistory
return fundHistory, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.
func (h *HUOBIHADAX) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
// SubmitOrder submits a new order
func (h *HUOBIHADAX) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
var submitOrderResponse exchange.SubmitOrderResponse
if order == nil {
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
}
if err := order.Validate(); err != nil {
return submitOrderResponse, err
}
accountID, err := strconv.ParseInt(order.ClientID, 10, 64)
if err != nil {
return submitOrderResponse, err
}
var formattedType SpotNewOrderRequestParamsType
var params = SpotNewOrderRequestParams{
Amount: order.Amount,
Source: "api",
Symbol: strings.ToLower(order.Pair.String()),
AccountID: int(accountID),
}
switch {
case order.OrderSide == exchange.BuyOrderSide && order.OrderType == exchange.MarketOrderType:
formattedType = SpotNewOrderRequestTypeBuyMarket
case order.OrderSide == exchange.SellOrderSide && order.OrderType == exchange.MarketOrderType:
formattedType = SpotNewOrderRequestTypeSellMarket
case order.OrderSide == exchange.BuyOrderSide && order.OrderType == exchange.LimitOrderType:
formattedType = SpotNewOrderRequestTypeBuyLimit
params.Price = order.Price
case order.OrderSide == exchange.SellOrderSide && order.OrderType == exchange.LimitOrderType:
formattedType = SpotNewOrderRequestTypeSellLimit
params.Price = order.Price
}
params.Type = formattedType
response, err := h.SpotNewOrder(params)
if response > 0 {
submitOrderResponse.OrderID = fmt.Sprintf("%v", response)
}
if err == nil {
submitOrderResponse.IsOrderPlaced = true
}
return submitOrderResponse, err
}
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (h *HUOBIHADAX) ModifyOrder(action *exchange.ModifyOrder) (string, error) {
return "", common.ErrFunctionNotSupported
}
// CancelOrder cancels an order by its corresponding ID number
func (h *HUOBIHADAX) CancelOrder(order *exchange.OrderCancellation) error {
orderIDInt, err := strconv.ParseInt(order.OrderID, 10, 64)
if err != nil {
return err
}
_, err = h.CancelExistingOrder(orderIDInt)
return err
}
// CancelAllOrders cancels all orders associated with a currency pair
func (h *HUOBIHADAX) CancelAllOrders(orderCancellation *exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) {
var cancelAllOrdersResponse exchange.CancelAllOrdersResponse
for _, currency := range h.GetEnabledPairs(asset.Spot) {
resp, err := h.CancelOpenOrdersBatch(orderCancellation.AccountID,
h.FormatExchangeCurrency(currency, asset.Spot).String())
if err != nil {
return cancelAllOrdersResponse, err
}
if resp.Data.FailedCount > 0 {
return cancelAllOrdersResponse, fmt.Errorf("%v orders failed to cancel", resp.Data.FailedCount)
}
if resp.Status == "error" {
return cancelAllOrdersResponse, errors.New(resp.ErrorMessage)
}
}
return cancelAllOrdersResponse, nil
}
// GetOrderInfo returns information on a current open order
func (h *HUOBIHADAX) GetOrderInfo(orderID string) (exchange.OrderDetail, error) {
var orderDetail exchange.OrderDetail
return orderDetail, common.ErrNotYetImplemented
}
// GetDepositAddress returns a deposit address for a specified currency
func (h *HUOBIHADAX) GetDepositAddress(cryptocurrency currency.Code, accountID string) (string, error) {
return "", common.ErrFunctionNotSupported
}
// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is
// submitted
func (h *HUOBIHADAX) WithdrawCryptocurrencyFunds(withdrawRequest *exchange.CryptoWithdrawRequest) (string, error) {
resp, err := h.Withdraw(withdrawRequest.Currency, withdrawRequest.Address, withdrawRequest.AddressTag, withdrawRequest.Amount, withdrawRequest.FeeAmount)
return fmt.Sprintf("%v", resp), err
}
// WithdrawFiatFunds returns a withdrawal ID when a
// withdrawal is submitted
func (h *HUOBIHADAX) WithdrawFiatFunds(withdrawRequest *exchange.FiatWithdrawRequest) (string, error) {
return "", common.ErrFunctionNotSupported
}
// WithdrawFiatFundsToInternationalBank returns a withdrawal ID when a
// withdrawal is submitted
func (h *HUOBIHADAX) WithdrawFiatFundsToInternationalBank(withdrawRequest *exchange.FiatWithdrawRequest) (string, error) {
return "", common.ErrFunctionNotSupported
}
// GetWebsocket returns a pointer to the exchange websocket
func (h *HUOBIHADAX) GetWebsocket() (*wshandler.Websocket, error) {
return h.Websocket, nil
}
// GetFeeByType returns an estimate of fee based on type of transaction
func (h *HUOBIHADAX) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) {
if !h.AllowAuthenticatedRequest() && // Todo check connection status
feeBuilder.FeeType == exchange.CryptocurrencyTradeFee {
feeBuilder.FeeType = exchange.OfflineTradeFee
}
return h.GetFee(feeBuilder)
}
// GetActiveOrders retrieves any orders that are active/open
func (h *HUOBIHADAX) GetActiveOrders(getOrdersRequest *exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) {
if len(getOrdersRequest.Currencies) == 0 {
return nil, errors.New("currency must be supplied")
}
side := ""
if getOrdersRequest.OrderSide == exchange.AnyOrderSide || getOrdersRequest.OrderSide == "" {
side = ""
} else if getOrdersRequest.OrderSide == exchange.SellOrderSide {
side = strings.ToLower(string(getOrdersRequest.OrderSide))
}
var allOrders []OrderInfo
for _, currency := range getOrdersRequest.Currencies {
resp, err := h.GetOpenOrders(h.API.Credentials.ClientID,
currency.Lower().String(),
side,
500)
if err != nil {
return nil, err
}
allOrders = append(allOrders, resp...)
}
var orders []exchange.OrderDetail
for i := range allOrders {
symbol := currency.NewPairDelimiter(allOrders[i].Symbol,
h.GetPairFormat(asset.Spot, false).Delimiter)
orderDate := time.Unix(0, allOrders[i].CreatedAt*int64(time.Millisecond))
orders = append(orders, exchange.OrderDetail{
ID: fmt.Sprintf("%v", allOrders[i].ID),
Exchange: h.Name,
Amount: allOrders[i].Amount,
Price: allOrders[i].Price,
OrderDate: orderDate,
ExecutedAmount: allOrders[i].FilledAmount,
RemainingAmount: (allOrders[i].Amount - allOrders[i].FilledAmount),
CurrencyPair: symbol,
})
}
exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks,
getOrdersRequest.EndTicks)
return orders, nil
}
// GetOrderHistory retrieves account order information
// Can Limit response to specific order status
func (h *HUOBIHADAX) GetOrderHistory(getOrdersRequest *exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) {
if len(getOrdersRequest.Currencies) == 0 {
return nil, errors.New("currency must be supplied")
}
states := "partial-canceled,filled,canceled"
var allOrders []OrderInfo
for _, currency := range getOrdersRequest.Currencies {
resp, err := h.GetOrders(currency.Lower().String(),
"",
"",
"",
states,
"",
"",
"")
if err != nil {
return nil, err
}
allOrders = append(allOrders, resp...)
}
var orders []exchange.OrderDetail
for i := range allOrders {
symbol := currency.NewPairDelimiter(allOrders[i].Symbol,
h.GetPairFormat(asset.Spot, false).Delimiter)
orderDate := time.Unix(0, allOrders[i].CreatedAt*int64(time.Millisecond))
orders = append(orders, exchange.OrderDetail{
ID: fmt.Sprintf("%v", allOrders[i].ID),
Exchange: h.Name,
Amount: allOrders[i].Amount,
Price: allOrders[i].Price,
OrderDate: orderDate,
CurrencyPair: symbol,
})
}
exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks,
getOrdersRequest.EndTicks)
return orders, nil
}
// SubscribeToWebsocketChannels appends to ChannelsToSubscribe
// which lets websocket.manageSubscriptions handle subscribing
func (h *HUOBIHADAX) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error {
h.Websocket.SubscribeToChannels(channels)
return nil
}
// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe
// which lets websocket.manageSubscriptions handle unsubscribing
func (h *HUOBIHADAX) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error {
h.Websocket.RemoveSubscribedChannels(channels)
return nil
}
// GetSubscriptions returns a copied list of subscriptions
func (h *HUOBIHADAX) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) {
return h.Websocket.GetSubscriptions(), nil
}
// AuthenticateWebsocket sends an authentication message to the websocket
func (h *HUOBIHADAX) AuthenticateWebsocket() error {
return h.wsLogin()
}

View File

@@ -31,7 +31,6 @@ var Exchanges = []string{
"gemini",
"hitbtc",
"huobi",
"huobihadax",
"itbit",
"kraken",
"lakebtc",

View File

@@ -945,52 +945,6 @@
}
]
},
{
"name": "HuobiHadax",
"enabled": true,
"verbose": false,
"websocket": false,
"useSandbox": false,
"restPollingDelay": 10,
"httpTimeout": 15000000000,
"websocketResponseCheckTimeout": 30000000,
"websocketResponseMaxLimit": 7000000000,
"websocketOrderbookBufferLimit": 5,
"httpUserAgent": "",
"httpDebugging": false,
"authenticatedApiSupport": false,
"authenticatedWebsocketApiSupport": false,
"apiKey": "Key",
"apiSecret": "Secret",
"apiAuthPemKey": "-----BEGIN EC PRIVATE KEY-----\nJUSTADUMMY\n-----END EC PRIVATE KEY-----\n",
"apiUrl": "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API",
"apiUrlSecondary": "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API",
"proxyAddress": "",
"websocketUrl": "NON_DEFAULT_HTTP_LINK_TO_WEBSOCKET_EXCHANGE_API",
"availablePairs": "TOS-ETH,XMX-ETH,MAN-ETH,MEX-BTC,AE-BTC,DATX-BTC,HPT-HT,PNT-ETH,NCC-BTC,SHE-BTC,18C-ETH,BUT-ETH,DAC-BTC,TRIO-BTC,CNN-BTC,LXT-BTC,PC-BTC,UIP-BTC,EKT-ETH,SEELE-BTC,GSC-ETH,CNN-ETH,UIP-ETH,LYM-BTC,CVCOIN-BTC,SEXC-ETH,KCASH-HT,RCCC-ETH,REN-BTC,ZJLT-ETH,GTC-BTC,AAC-BTC,PC-ETH,KCASH-BTC,BUT-BTC,MUSK-BTC,PORTAL-ETH,UUU-BTC,TOS-BTC,GET-ETH,SSP-BTC,AAC-ETH,SEXC-BTC,MT-ETH,MT-HT,EGCC-ETH,CDC-ETH,GTC-ETH,CDC-BTC,HPT-BTC,RTE-ETH,FAIR-ETH,REN-ETH,FAIR-BTC,RTE-BTC,FTI-ETH,PORTAL-BTC,LXT-ETH,KCASH-ETH,EGCC-BTC,18C-BTC,UUU-ETH,NCC-ETH,MT-BTC,BKBT-ETH,GET-BTC,DAC-ETH,MUSK-ETH,SSP-ETH,SEELE-ETH,MEX-ETH,MAN-BTC,HPT-USDT,IDT-ETH,AE-ETH,PNT-BTC,RCCC-BTC,ZJLT-BTC,DATX-ETH,IDT-BTC,GSC-BTC,GVE-BTC,LYM-ETH,XMX-BTC,CVCOIN-ETH,EKT-BTC,GVE-ETH,YCC-ETH,HOT-ETH,TRIO-ETH,INC-ETH,UC-ETH,BCV-BTC,INC-BTC,SHE-ETH,UC-BTC,BKBT-BTC,BCV-ETH,YCC-BTC,HOT-BTC,FTI-BTC,IIC-ETH,IIC-BTC",
"enabledPairs": "HOT-BTC",
"baseCurrencies": "USD",
"assetTypes": "SPOT",
"supportsAutoPairUpdates": true,
"configCurrencyPairFormat": {
"uppercase": true,
"delimiter": "-"
},
"requestCurrencyPairFormat": {
"uppercase": false
},
"bankAccounts": [
{
"bankName": "",
"bankAddress": "",
"accountName": "",
"accountNumber": "",
"swiftCode": "",
"iban": "",
"supportedCurrencies": ""
}
]
},
{
"name": "ITBIT",
"enabled": true,