mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-24 23:16:52 +00:00
Remove HADAX exchange support (#362)
* Remove Huobi HADAX * Remove Hadax from root_readme.tmpl
This commit is contained in:
@@ -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 |
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
const (
|
||||
// Default number of enabled exchanges. Modify this whenever an exchange is
|
||||
// added or removed
|
||||
defaultEnabledExchanges = 28
|
||||
defaultEnabledExchanges = 27
|
||||
)
|
||||
|
||||
func TestGetCurrencyConfig(t *testing.T) {
|
||||
|
||||
@@ -933,52 +933,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,
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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">
|
||||
|
||||
|
||||
[](https://travis-ci.org/thrasher-corp/gocryptotrader)
|
||||
[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
|
||||
[](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/huobihadax)
|
||||
[](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
|
||||
[](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.GetTickerPrice()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Fetches current orderbook information
|
||||
ob, err := h.GetOrderbookEx()
|
||||
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***
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,670 +0,0 @@
|
||||
package huobihadax
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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/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
|
||||
|
||||
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.AuthenticatedAPISupport = true
|
||||
hadaxConfig.AuthenticatedWebsocketAPISupport = true
|
||||
hadaxConfig.APIKey = apiKey
|
||||
hadaxConfig.APISecret = apiSecret
|
||||
|
||||
h.Setup(&hadaxConfig)
|
||||
}
|
||||
|
||||
func setupWsTests(t *testing.T) {
|
||||
if wsSetupRan {
|
||||
return
|
||||
}
|
||||
TestSetDefaults(t)
|
||||
TestSetup(t)
|
||||
if !h.Websocket.IsEnabled() && !h.AuthenticatedWebsocketAPISupport || !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 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.APIKey == "" || h.APISecret == "" || h.APIAuthPEMKey == "" {
|
||||
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.APIKey == "" || h.APISecret == "" || h.APIAuthPEMKey == "" {
|
||||
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.APIKey == "" || h.APISecret == "" || h.APIAuthPEMKey == "" {
|
||||
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.APIKey == "" || h.APISecret == "" || h.APIAuthPEMKey == "" {
|
||||
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 {
|
||||
fmt.Println(newOrderID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelExistingOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if h.APIKey == "" || h.APISecret == "" || h.APIAuthPEMKey == "" {
|
||||
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.APIKey == "" || h.APISecret == "" || h.APIAuthPEMKey == "" {
|
||||
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.APIKey == "" || h.APISecret == "" || h.APIAuthPEMKey == "" {
|
||||
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.APIKey == "" || h.APISecret == "" || h.APIAuthPEMKey == "" {
|
||||
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.APIKey == "" || h.APISecret == "" || h.APIAuthPEMKey == "" {
|
||||
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 {
|
||||
if h.APIKey != "" && h.APIKey != "Key" &&
|
||||
h.APISecret != "" && h.APISecret != "Secret" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestSubmitOrder(t *testing.T) {
|
||||
h.SetDefaults()
|
||||
TestSetup(t)
|
||||
|
||||
if areTestAPIKeysSet() && !canManipulateRealOrders {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
if (h.APIKey == "" || h.APIKey == "Key") &&
|
||||
(h.APISecret == "" || h.APISecret == "Secret") {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USDT,
|
||||
}
|
||||
|
||||
accounts, err := h.GetAccounts()
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get accounts. Err: %s", err)
|
||||
}
|
||||
|
||||
response, err := h.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, strconv.FormatInt(accounts[0].ID, 10))
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
t.Error("Expecting an error when no keys are set")
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
var withdrawCryptoRequest = exchange.WithdrawRequest{
|
||||
Amount: 100,
|
||||
Currency: currency.BTC,
|
||||
Address: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
|
||||
Description: "WITHDRAW IT ALL",
|
||||
}
|
||||
|
||||
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.WithdrawRequest{}
|
||||
|
||||
_, 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.WithdrawRequest{}
|
||||
|
||||
_, 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)
|
||||
}
|
||||
}
|
||||
@@ -1,535 +0,0 @@
|
||||
package huobihadax
|
||||
|
||||
import (
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
)
|
||||
|
||||
// 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"`
|
||||
Vol float64 `json:"vol"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// 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 currency.Pair `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 currency.Pair `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 currency.Pair `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 currency.Pair `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 currency.Pair `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"`
|
||||
}
|
||||
@@ -1,475 +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/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"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"
|
||||
|
||||
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("%v - authenticated dial failed: %v", h.Name, err)
|
||||
}
|
||||
err = h.wsLogin()
|
||||
if err != nil {
|
||||
log.Errorf("%v - authentication failed: %v", 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("%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(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if init.ErrorMessage == "api-signature-not-valid" {
|
||||
h.Websocket.SetCanUseAuthenticatedEndpoints(false)
|
||||
}
|
||||
if init.Op == "sub" {
|
||||
if h.Verbose {
|
||||
log.Debugf("%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 common.StringContains(init.Topic, "orders") &&
|
||||
common.StringContains(init.Topic, "update"):
|
||||
var response WsAuthenticatedOrdersUpdateResponse
|
||||
err := common.JSONDecode(resp.Raw, &response)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
}
|
||||
h.Websocket.DataHandler <- response
|
||||
case common.StringContains(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(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch {
|
||||
case common.StringContains(init.Channel, "depth"):
|
||||
var depth WsDepth
|
||||
err := common.JSONDecode(resp.Raw, &depth)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
return
|
||||
}
|
||||
data := common.SplitStrings(depth.Channel, ".")
|
||||
h.WsProcessOrderbook(&depth, data[1])
|
||||
case common.StringContains(init.Channel, "kline"):
|
||||
var kline WsKline
|
||||
err := common.JSONDecode(resp.Raw, &kline)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
return
|
||||
}
|
||||
data := common.SplitStrings(kline.Channel, ".")
|
||||
h.Websocket.DataHandler <- wshandler.KlineData{
|
||||
Timestamp: time.Unix(0, kline.Timestamp),
|
||||
Exchange: h.GetName(),
|
||||
AssetType: orderbook.Spot,
|
||||
Pair: currency.NewPairFromString(data[1]),
|
||||
OpenPrice: kline.Tick.Open,
|
||||
ClosePrice: kline.Tick.Close,
|
||||
HighPrice: kline.Tick.High,
|
||||
LowPrice: kline.Tick.Low,
|
||||
Volume: kline.Tick.Volume,
|
||||
}
|
||||
case common.StringContains(init.Channel, "trade"):
|
||||
var trade WsTrade
|
||||
err := common.JSONDecode(resp.Raw, &trade)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
return
|
||||
}
|
||||
data := common.SplitStrings(trade.Channel, ".")
|
||||
h.Websocket.DataHandler <- wshandler.TradeData{
|
||||
Exchange: h.GetName(),
|
||||
AssetType: orderbook.Spot,
|
||||
CurrencyPair: currency.NewPairFromString(data[1]),
|
||||
Timestamp: time.Unix(0, trade.Tick.Timestamp),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WsProcessOrderbook processes new orderbook data
|
||||
func (h *HUOBIHADAX) WsProcessOrderbook(update *WsDepth, symbol string) error {
|
||||
p := currency.NewPairFromString(symbol)
|
||||
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: orderbook.Spot,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions()
|
||||
func (h *HUOBIHADAX) GenerateDefaultSubscriptions() {
|
||||
var channels = []string{wsMarketKline, wsMarketDepth, wsMarketTrade}
|
||||
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.GetEnabledCurrencies()
|
||||
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 common.StringContains(channelToSubscribe.Channel, "orders.") ||
|
||||
common.StringContains(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 common.StringContains(channelToSubscribe.Channel, "orders.") ||
|
||||
common.StringContains(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.APIKey,
|
||||
SignatureMethod: signatureMethod,
|
||||
SignatureVersion: signatureVersion,
|
||||
Timestamp: timestamp,
|
||||
}
|
||||
hmac := h.wsGenerateSignature(timestamp, wsAccountsOrdersEndPoint)
|
||||
request.Signature = common.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.APIKey)
|
||||
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 common.GetHMAC(common.HashSHA256, []byte(payload), []byte(h.APISecret))
|
||||
}
|
||||
|
||||
func (h *HUOBIHADAX) wsAuthenticatedSubscribe(operation, endpoint, topic string) error {
|
||||
timestamp := time.Now().UTC().Format(wsDateTimeFormatting)
|
||||
request := WsAuthenticatedSubscriptionRequest{
|
||||
Op: operation,
|
||||
AccessKeyID: h.APIKey,
|
||||
SignatureMethod: signatureMethod,
|
||||
SignatureVersion: signatureVersion,
|
||||
Timestamp: timestamp,
|
||||
Topic: topic,
|
||||
}
|
||||
hmac := h.wsGenerateSignature(timestamp, endpoint)
|
||||
request.Signature = common.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.APIKey,
|
||||
SignatureMethod: signatureMethod,
|
||||
SignatureVersion: signatureVersion,
|
||||
Timestamp: timestamp,
|
||||
Topic: wsAccountsList,
|
||||
Symbol: pair,
|
||||
}
|
||||
hmac := h.wsGenerateSignature(timestamp, wsAccountListEndpoint)
|
||||
request.Signature = common.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.APIKey,
|
||||
SignatureMethod: signatureMethod,
|
||||
SignatureVersion: signatureVersion,
|
||||
Timestamp: timestamp,
|
||||
Topic: wsOrdersList,
|
||||
AccountID: accountID,
|
||||
Symbol: pair.Lower(),
|
||||
States: "submitted,partial-filled",
|
||||
}
|
||||
hmac := h.wsGenerateSignature(timestamp, wsOrdersListEndpoint)
|
||||
request.Signature = common.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.APIKey,
|
||||
SignatureMethod: signatureMethod,
|
||||
SignatureVersion: signatureVersion,
|
||||
Timestamp: timestamp,
|
||||
Topic: wsOrdersDetail,
|
||||
OrderID: orderID,
|
||||
}
|
||||
hmac := h.wsGenerateSignature(timestamp, wsOrdersDetailEndpoint)
|
||||
request.Signature = common.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
|
||||
}
|
||||
@@ -1,475 +0,0 @@
|
||||
package huobihadax
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
|
||||
log "github.com/thrasher-corp/gocryptotrader/logger"
|
||||
)
|
||||
|
||||
// Start starts the OKEX go routine
|
||||
func (h *HUOBIHADAX) Start(wg *sync.WaitGroup) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
h.Run()
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
// Run implements the OKEX wrapper
|
||||
func (h *HUOBIHADAX) Run() {
|
||||
if h.Verbose {
|
||||
log.Debugf("%s Websocket: %s. (url: %s).\n", h.GetName(), common.IsEnabled(h.Websocket.IsEnabled()), h.WebsocketURL)
|
||||
log.Debugf("%s polling delay: %ds.\n", h.GetName(), h.RESTPollingDelay)
|
||||
log.Debugf("%s %d currencies enabled: %s.\n", h.GetName(), len(h.EnabledPairs), h.EnabledPairs)
|
||||
}
|
||||
|
||||
exchangeProducts, err := h.GetSymbols()
|
||||
if err != nil {
|
||||
log.Debugf("%s Failed to get available symbols.\n", h.GetName())
|
||||
} else {
|
||||
var currencies currency.Pairs
|
||||
for x := range exchangeProducts {
|
||||
currencies = append(currencies,
|
||||
currency.NewPairWithDelimiter(exchangeProducts[x].BaseCurrency,
|
||||
exchangeProducts[x].QuoteCurrency,
|
||||
"-"))
|
||||
}
|
||||
|
||||
err = h.UpdateCurrencies(currencies, false, false)
|
||||
if err != nil {
|
||||
log.Debugf("%s Failed to update available currencies.\n", h.GetName())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (h *HUOBIHADAX) UpdateTicker(p currency.Pair, assetType string) (ticker.Price, error) {
|
||||
var tickerPrice ticker.Price
|
||||
tick, err := h.GetMarketDetailMerged(exchange.FormatExchangeCurrency(h.Name, p).String())
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
|
||||
tickerPrice.Pair = p
|
||||
tickerPrice.Low = tick.Low
|
||||
tickerPrice.Last = tick.Close
|
||||
tickerPrice.Volume = tick.Volume
|
||||
tickerPrice.High = tick.High
|
||||
|
||||
if len(tick.Ask) > 0 {
|
||||
tickerPrice.Ask = tick.Ask[0]
|
||||
}
|
||||
|
||||
if len(tick.Bid) > 0 {
|
||||
tickerPrice.Bid = tick.Bid[0]
|
||||
}
|
||||
|
||||
err = ticker.ProcessTicker(h.GetName(), &tickerPrice, assetType)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
|
||||
return ticker.GetTicker(h.Name, p, assetType)
|
||||
}
|
||||
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (h *HUOBIHADAX) GetTickerPrice(p currency.Pair, assetType string) (ticker.Price, error) {
|
||||
tickerNew, err := ticker.GetTicker(h.GetName(), p, assetType)
|
||||
if err != nil {
|
||||
return h.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
// GetOrderbookEx returns orderbook base on the currency pair
|
||||
func (h *HUOBIHADAX) GetOrderbookEx(p currency.Pair, assetType string) (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 string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
orderbookNew, err := h.GetDepth(exchange.FormatExchangeCurrency(h.Name, p).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 string) ([]exchange.TradeHistory, error) {
|
||||
var resp []exchange.TradeHistory
|
||||
|
||||
return resp, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (h *HUOBIHADAX) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
accountID, err := strconv.ParseInt(clientID, 0, 64)
|
||||
if err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
var formattedType SpotNewOrderRequestParamsType
|
||||
var params = SpotNewOrderRequestParams{
|
||||
Amount: amount,
|
||||
Source: "api",
|
||||
Symbol: common.StringToLower(p.String()),
|
||||
AccountID: int(accountID),
|
||||
}
|
||||
|
||||
switch {
|
||||
case side == exchange.BuyOrderSide && orderType == exchange.MarketOrderType:
|
||||
formattedType = SpotNewOrderRequestTypeBuyMarket
|
||||
case side == exchange.SellOrderSide && orderType == exchange.MarketOrderType:
|
||||
formattedType = SpotNewOrderRequestTypeSellMarket
|
||||
case side == exchange.BuyOrderSide && orderType == exchange.LimitOrderType:
|
||||
formattedType = SpotNewOrderRequestTypeBuyLimit
|
||||
params.Price = price
|
||||
case side == exchange.SellOrderSide && orderType == exchange.LimitOrderType:
|
||||
formattedType = SpotNewOrderRequestTypeSellLimit
|
||||
params.Price = price
|
||||
default:
|
||||
return submitOrderResponse, errors.New("unsupported order type")
|
||||
}
|
||||
|
||||
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.GetEnabledCurrencies() {
|
||||
resp, err := h.CancelOpenOrdersBatch(orderCancellation.AccountID, exchange.FormatExchangeCurrency(h.Name, currency).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.WithdrawRequest) (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.WithdrawRequest) (string, error) {
|
||||
return "", common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// WithdrawFiatFundsToInternationalBank returns a withdrawal ID when a
|
||||
// withdrawal is submitted
|
||||
func (h *HUOBIHADAX) WithdrawFiatFundsToInternationalBank(withdrawRequest *exchange.WithdrawRequest) (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.APIKey == "" || h.APISecret == "") && // 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.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.ConfigCurrencyPairFormat.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.ConfigCurrencyPairFormat.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()
|
||||
}
|
||||
46
testdata/configtest.json
vendored
46
testdata/configtest.json
vendored
@@ -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,
|
||||
|
||||
@@ -60,7 +60,6 @@ const (
|
||||
gemini = "..%s..%sexchanges%sgemini%s"
|
||||
hitbtc = "..%s..%sexchanges%shitbtc%s"
|
||||
huobi = "..%s..%sexchanges%shuobi%s"
|
||||
huobihadax = "..%s..%sexchanges%shuobihadax%s"
|
||||
itbit = "..%s..%sexchanges%sitbit%s"
|
||||
kraken = "..%s..%sexchanges%skraken%s"
|
||||
lakebtc = "..%s..%sexchanges%slakebtc%s"
|
||||
@@ -230,7 +229,6 @@ func addPaths() {
|
||||
codebasePaths["exchanges gemini"] = fmt.Sprintf(gemini, path, path, path, path)
|
||||
codebasePaths["exchanges hitbtc"] = fmt.Sprintf(hitbtc, path, path, path, path)
|
||||
codebasePaths["exchanges huobi"] = fmt.Sprintf(huobi, path, path, path, path)
|
||||
codebasePaths["exchanges huobihadax"] = fmt.Sprintf(huobihadax, path, path, path, path)
|
||||
codebasePaths["exchanges itbit"] = fmt.Sprintf(itbit, path, path, path, path)
|
||||
codebasePaths["exchanges kraken"] = fmt.Sprintf(kraken, path, path, path, path)
|
||||
codebasePaths["exchanges lakebtc"] = fmt.Sprintf(lakebtc, path, path, path, path)
|
||||
|
||||
@@ -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.GetTickerPrice()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Fetches current orderbook information
|
||||
ob, err := h.GetOrderbookEx()
|
||||
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}}
|
||||
@@ -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 |
|
||||
|
||||
Reference in New Issue
Block a user