mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-04 07:26:47 +00:00
Merge branch 'master' into engine
This commit is contained in:
@@ -1,12 +1,16 @@
|
||||
package hitbtc
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/sharedtestvalues"
|
||||
)
|
||||
|
||||
var h HitBTC
|
||||
@@ -30,6 +34,7 @@ func TestSetup(t *testing.T) {
|
||||
t.Error("Test Failed - HitBTC Setup() init error")
|
||||
}
|
||||
hitbtcConfig.API.AuthenticatedSupport = true
|
||||
hitbtcConfig.API.AuthenticatedWebsocketSupport = true
|
||||
hitbtcConfig.API.Credentials.Key = apiKey
|
||||
hitbtcConfig.API.Credentials.Secret = apiSecret
|
||||
|
||||
@@ -99,7 +104,7 @@ func TestGetFee(t *testing.T) {
|
||||
// CryptocurrencyTradeFee Basic
|
||||
if resp, err := h.GetFee(feeBuilder); resp != float64(0.001) || err != nil {
|
||||
t.Error(err)
|
||||
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0.001), resp)
|
||||
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0.002), resp)
|
||||
}
|
||||
|
||||
// CryptocurrencyTradeFee High quantity
|
||||
@@ -107,7 +112,7 @@ func TestGetFee(t *testing.T) {
|
||||
feeBuilder.Amount = 1000
|
||||
feeBuilder.PurchasePrice = 1000
|
||||
if resp, err := h.GetFee(feeBuilder); resp != float64(1000) || err != nil {
|
||||
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(1000), resp)
|
||||
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(2000), resp)
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
@@ -115,7 +120,7 @@ func TestGetFee(t *testing.T) {
|
||||
feeBuilder = setFeeBuilder()
|
||||
feeBuilder.IsMaker = true
|
||||
if resp, err := h.GetFee(feeBuilder); resp != float64(-0.0001) || err != nil {
|
||||
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(-0.0001), resp)
|
||||
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0.001), resp)
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
@@ -123,7 +128,7 @@ func TestGetFee(t *testing.T) {
|
||||
feeBuilder = setFeeBuilder()
|
||||
feeBuilder.PurchasePrice = -1000
|
||||
if resp, err := h.GetFee(feeBuilder); resp != float64(-1) || err != nil {
|
||||
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(-1), resp)
|
||||
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
@@ -131,7 +136,7 @@ func TestGetFee(t *testing.T) {
|
||||
feeBuilder = setFeeBuilder()
|
||||
feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
|
||||
if resp, err := h.GetFee(feeBuilder); resp != float64(0.009580) || err != nil {
|
||||
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0.009580), resp)
|
||||
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0.042800), resp)
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
@@ -383,3 +388,107 @@ func TestGetDepositAddress(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
func setupWsAuth(t *testing.T) {
|
||||
TestSetDefaults(t)
|
||||
TestSetup(t)
|
||||
if !h.Websocket.IsEnabled() && !h.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
|
||||
t.Skip(exchange.WebsocketNotEnabled)
|
||||
}
|
||||
var err error
|
||||
var dialer websocket.Dialer
|
||||
h.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
|
||||
h.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride()
|
||||
h.WebsocketConn, _, err = dialer.Dial(hitbtcWebsocketAddress, http.Header{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
go h.WsHandleData()
|
||||
h.wsLogin()
|
||||
timer := time.NewTimer(time.Second)
|
||||
select {
|
||||
case loginError := <-h.Websocket.DataHandler:
|
||||
t.Fatal(loginError)
|
||||
case <-timer.C:
|
||||
}
|
||||
timer.Stop()
|
||||
}
|
||||
|
||||
// TestWsCancelOrder dials websocket, sends cancel request.
|
||||
func TestWsCancelOrder(t *testing.T) {
|
||||
setupWsAuth(t)
|
||||
err := h.wsCancelOrder("ImNotARealOrderID")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
timer := time.NewTimer(sharedtestvalues.WebsocketResponseDefaultTimeout)
|
||||
select {
|
||||
case <-h.Websocket.DataHandler:
|
||||
case <-timer.C:
|
||||
t.Error("Expecting response")
|
||||
}
|
||||
timer.Stop()
|
||||
}
|
||||
|
||||
// TestWsPlaceOrder dials websocket, sends order submission.
|
||||
func TestWsPlaceOrder(t *testing.T) {
|
||||
setupWsAuth(t)
|
||||
err := h.wsPlaceOrder(currency.NewPair(currency.LTC, currency.BTC), exchange.BuyOrderSide.ToString(), 1, 1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
timer := time.NewTimer(sharedtestvalues.WebsocketResponseDefaultTimeout)
|
||||
select {
|
||||
case <-h.Websocket.DataHandler:
|
||||
case <-timer.C:
|
||||
t.Error("Expecting response")
|
||||
}
|
||||
timer.Stop()
|
||||
}
|
||||
|
||||
// TestWsReplaceOrder dials websocket, sends replace order request.
|
||||
func TestWsReplaceOrder(t *testing.T) {
|
||||
setupWsAuth(t)
|
||||
err := h.wsReplaceOrder("ImNotARealOrderID", 1, 1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
timer := time.NewTimer(sharedtestvalues.WebsocketResponseDefaultTimeout)
|
||||
select {
|
||||
case <-h.Websocket.DataHandler:
|
||||
case <-timer.C:
|
||||
t.Error("Expecting response")
|
||||
}
|
||||
timer.Stop()
|
||||
}
|
||||
|
||||
// TestWsGetActiveOrders dials websocket, sends get active orders request.
|
||||
func TestWsGetActiveOrders(t *testing.T) {
|
||||
setupWsAuth(t)
|
||||
err := h.wsGetActiveOrders()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
timer := time.NewTimer(sharedtestvalues.WebsocketResponseDefaultTimeout)
|
||||
select {
|
||||
case <-h.Websocket.DataHandler:
|
||||
case <-timer.C:
|
||||
t.Error("Expecting response")
|
||||
}
|
||||
timer.Stop()
|
||||
}
|
||||
|
||||
// TestWsGetTradingBalance dials websocket, sends get trading balance request.
|
||||
func TestWsGetTradingBalance(t *testing.T) {
|
||||
setupWsAuth(t)
|
||||
err := h.wsGetTradingBalance()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
timer := time.NewTimer(sharedtestvalues.WebsocketResponseDefaultTimeout)
|
||||
select {
|
||||
case <-h.Websocket.DataHandler:
|
||||
case <-timer.C:
|
||||
t.Error("Expecting response")
|
||||
}
|
||||
timer.Stop()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package hitbtc
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/currency"
|
||||
)
|
||||
|
||||
// Ticker holds ticker information
|
||||
type Ticker struct {
|
||||
@@ -186,19 +190,19 @@ type AuthenticatedTradeHistoryResponse struct {
|
||||
|
||||
// OrderHistoryResponse used for GetOrderHistory
|
||||
type OrderHistoryResponse struct {
|
||||
ID string `json:"id"`
|
||||
ClientOrderID string `json:"clientOrderId"`
|
||||
Symbol string `json:"symbol"`
|
||||
Side string `json:"side"`
|
||||
Status string `json:"status"`
|
||||
Type string `json:"type"`
|
||||
TimeInForce string `json:"timeInForce"`
|
||||
Price float64 `json:"price,string"`
|
||||
Quantity float64 `json:"quantity,string"`
|
||||
PostOnly bool `json:"postOnly"`
|
||||
CumQuantity float64 `json:"cumQuantity,string"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
ID string `json:"id"`
|
||||
ClientOrderID string `json:"clientOrderId"`
|
||||
Symbol string `json:"symbol"`
|
||||
Side string `json:"side"`
|
||||
Status string `json:"status"`
|
||||
Type string `json:"type"`
|
||||
TimeInForce string `json:"timeInForce"`
|
||||
Price float64 `json:"price,string"`
|
||||
Quantity float64 `json:"quantity,string"`
|
||||
PostOnly bool `json:"postOnly"`
|
||||
CumQuantity float64 `json:"cumQuantity,string"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
// ResultingTrades holds resulting trade information
|
||||
@@ -295,12 +299,13 @@ type LendingHistory struct {
|
||||
}
|
||||
|
||||
type capture struct {
|
||||
Method string `json:"method"`
|
||||
Result bool `json:"result"`
|
||||
Method string `json:"method,omitempty"`
|
||||
Result interface{} `json:"result"`
|
||||
Error struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
} `json:"error"`
|
||||
ID int64 `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
// WsRequest defines a request obj for the JSON-RPC and gets a websocket
|
||||
@@ -314,13 +319,13 @@ type WsRequest struct {
|
||||
// WsNotification defines a notification obj for the JSON-RPC this does not get
|
||||
// a websocket response
|
||||
type WsNotification struct {
|
||||
JSONRPCVersion string `json:"jsonrpc"`
|
||||
JSONRPCVersion string `json:"jsonrpc,omitempty"`
|
||||
Method string `json:"method"`
|
||||
Params interface{} `json:"params"`
|
||||
}
|
||||
|
||||
type params struct {
|
||||
Symbol string `json:"symbol"`
|
||||
Symbol string `json:"symbol,omitempty"`
|
||||
Period string `json:"period,omitempty"`
|
||||
Limit int64 `json:"limit,omitempty"`
|
||||
}
|
||||
@@ -370,3 +375,234 @@ type WsTrade struct {
|
||||
Symbol string `json:"symbol"`
|
||||
} `json:"params"`
|
||||
}
|
||||
|
||||
// WsLoginRequest defines login requirements for ws
|
||||
type WsLoginRequest struct {
|
||||
Method string `json:"method"`
|
||||
Params WsLoginData `json:"params"`
|
||||
}
|
||||
|
||||
// WsLoginData sets credentials for WsLoginRequest
|
||||
type WsLoginData struct {
|
||||
Algo string `json:"algo"`
|
||||
PKey string `json:"pKey"`
|
||||
Nonce string `json:"nonce"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
// WsActiveOrdersResponse Active order response for auth subscription to reports
|
||||
type WsActiveOrdersResponse struct {
|
||||
Params []WsActiveOrdersResponseData `json:"params"`
|
||||
}
|
||||
|
||||
// WsActiveOrdersResponseData Active order data for WsActiveOrdersResponse
|
||||
type WsActiveOrdersResponseData struct {
|
||||
ID string `json:"id"`
|
||||
ClientOrderID string `json:"clientOrderId"`
|
||||
Symbol currency.Pair `json:"symbol"`
|
||||
Side string `json:"side"`
|
||||
Status string `json:"status"`
|
||||
Type string `json:"type"`
|
||||
TimeInForce string `json:"timeInForce"`
|
||||
Quantity float64 `json:"quantity,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
CumQuantity float64 `json:"cumQuantity,string"`
|
||||
PostOnly bool `json:"postOnly"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
ReportType string `json:"reportType"`
|
||||
}
|
||||
|
||||
// WsReportResponse report response for auth subscription to reports
|
||||
type WsReportResponse struct {
|
||||
Params WsReportResponseData `json:"params"`
|
||||
}
|
||||
|
||||
// WsReportResponseData Report data for WsReportResponse
|
||||
type WsReportResponseData struct {
|
||||
ID string `json:"id"`
|
||||
ClientOrderID string `json:"clientOrderId"`
|
||||
Symbol currency.Pair `json:"symbol"`
|
||||
Side string `json:"side"`
|
||||
Status string `json:"status"`
|
||||
Type string `json:"type"`
|
||||
TimeInForce string `json:"timeInForce"`
|
||||
Quantity float64 `json:"quantity,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
CumQuantity float64 `json:"cumQuantity,string"`
|
||||
PostOnly bool `json:"postOnly"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
ReportType string `json:"reportType"`
|
||||
TradeQuantity float64 `json:"tradeQuantity,string"`
|
||||
TradePrice float64 `json:"tradePrice,string"`
|
||||
TradeID int64 `json:"tradeId"`
|
||||
TradeFee float64 `json:"tradeFee,string"`
|
||||
}
|
||||
|
||||
// WsSubmitOrderRequest WS request
|
||||
type WsSubmitOrderRequest struct {
|
||||
Method string `json:"method"`
|
||||
Params WsSubmitOrderRequestData `json:"params"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
// WsSubmitOrderRequestData WS request data
|
||||
type WsSubmitOrderRequestData struct {
|
||||
ClientOrderID string `json:"clientOrderId"`
|
||||
Symbol currency.Pair `json:"symbol"`
|
||||
Side string `json:"side"`
|
||||
Price float64 `json:"price,string"`
|
||||
Quantity float64 `json:"quantity,string"`
|
||||
}
|
||||
|
||||
// WsSubmitOrderSuccessResponse WS response
|
||||
type WsSubmitOrderSuccessResponse struct {
|
||||
Result WsSubmitOrderSuccessResponseData `json:"result"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
// WsSubmitOrderSuccessResponseData WS response data
|
||||
type WsSubmitOrderSuccessResponseData struct {
|
||||
ID string `json:"id"`
|
||||
ClientOrderID string `json:"clientOrderId"`
|
||||
Symbol string `json:"symbol"`
|
||||
Side string `json:"side"`
|
||||
Status string `json:"status"`
|
||||
Type string `json:"type"`
|
||||
TimeInForce string `json:"timeInForce"`
|
||||
Quantity float64 `json:"quantity,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
CumQuantity float64 `json:"cumQuantity,string"`
|
||||
PostOnly bool `json:"postOnly"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
ReportType string `json:"reportType"`
|
||||
}
|
||||
|
||||
// WsSubmitOrderErrorResponse WS error response
|
||||
type WsSubmitOrderErrorResponse struct {
|
||||
Error WsSubmitOrderErrorResponseData `json:"error"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
// WsSubmitOrderErrorResponseData WS error response data
|
||||
type WsSubmitOrderErrorResponseData struct {
|
||||
Code int64 `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// WsCancelOrderResponse WS response
|
||||
type WsCancelOrderResponse struct {
|
||||
Result WsCancelOrderResponseData `json:"result"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
// WsCancelOrderResponseData WS response data
|
||||
type WsCancelOrderResponseData struct {
|
||||
ID string `json:"id"`
|
||||
ClientOrderID string `json:"clientOrderId"`
|
||||
Symbol currency.Pair `json:"symbol"`
|
||||
Side string `json:"side"`
|
||||
Status string `json:"status"`
|
||||
Type string `json:"type"`
|
||||
TimeInForce string `json:"timeInForce"`
|
||||
Quantity float64 `json:"quantity,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
CumQuantity float64 `json:"cumQuantity,string"`
|
||||
PostOnly bool `json:"postOnly"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
ReportType string `json:"reportType"`
|
||||
}
|
||||
|
||||
// WsReplaceOrderResponse WS response
|
||||
type WsReplaceOrderResponse struct {
|
||||
Result WsReplaceOrderResponseData `json:"result"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
// WsReplaceOrderResponseData WS response data
|
||||
type WsReplaceOrderResponseData struct {
|
||||
ID string `json:"id"`
|
||||
ClientOrderID string `json:"clientOrderId"`
|
||||
Symbol currency.Pair `json:"symbol"`
|
||||
Side string `json:"side"`
|
||||
Status string `json:"status"`
|
||||
Type string `json:"type"`
|
||||
TimeInForce string `json:"timeInForce"`
|
||||
Quantity float64 `json:"quantity,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
CumQuantity float64 `json:"cumQuantity,string"`
|
||||
PostOnly bool `json:"postOnly"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
ReportType string `json:"reportType"`
|
||||
OriginalRequestClientOrderID string `json:"originalRequestClientOrderId"`
|
||||
}
|
||||
|
||||
// WsGetActiveOrdersResponse WS response
|
||||
type WsGetActiveOrdersResponse struct {
|
||||
Result []WsGetActiveOrdersResponseData `json:"result"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
// WsGetActiveOrdersResponseData WS response data
|
||||
type WsGetActiveOrdersResponseData struct {
|
||||
ID string `json:"id"`
|
||||
ClientOrderID string `json:"clientOrderId"`
|
||||
Symbol currency.Pair `json:"symbol"`
|
||||
Side string `json:"side"`
|
||||
Status string `json:"status"`
|
||||
Type string `json:"type"`
|
||||
TimeInForce string `json:"timeInForce"`
|
||||
Quantity float64 `json:"quantity,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
CumQuantity float64 `json:"cumQuantity,string"`
|
||||
PostOnly bool `json:"postOnly"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
ReportType string `json:"reportType"`
|
||||
OriginalRequestClientOrderID string `json:"originalRequestClientOrderId"`
|
||||
}
|
||||
|
||||
// WsGetTradingBalanceResponse WS response
|
||||
type WsGetTradingBalanceResponse struct {
|
||||
Result []WsGetTradingBalanceResponseData `json:"result"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
// WsGetTradingBalanceResponseData WS response data
|
||||
type WsGetTradingBalanceResponseData struct {
|
||||
Currency currency.Code `json:"currency"`
|
||||
Available float64 `json:"available,string"`
|
||||
Reserved float64 `json:"reserved,string"`
|
||||
}
|
||||
|
||||
// WsCancelOrderRequest WS request
|
||||
type WsCancelOrderRequest struct {
|
||||
Method string `json:"method"`
|
||||
Params WsCancelOrderRequestData `json:"params"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
// WsCancelOrderRequestData WS request data
|
||||
type WsCancelOrderRequestData struct {
|
||||
ClientOrderID string `json:"clientOrderId"`
|
||||
}
|
||||
|
||||
// WsReplaceOrderRequest WS request
|
||||
type WsReplaceOrderRequest struct {
|
||||
Method string `json:"method"`
|
||||
Params WsReplaceOrderRequestData `json:"params"`
|
||||
ID int64 `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
// WsReplaceOrderRequestData WS request data
|
||||
type WsReplaceOrderRequestData struct {
|
||||
ClientOrderID string `json:"clientOrderId,omitempty"`
|
||||
RequestClientID string `json:"requestClientId,omitempty"`
|
||||
Quantity float64 `json:"quantity,string,omitempty"`
|
||||
Price float64 `json:"price,string,omitempty"`
|
||||
}
|
||||
|
||||
@@ -10,9 +10,11 @@ import (
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/common/crypto"
|
||||
"github.com/thrasher-/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/nonce"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
log "github.com/thrasher-/gocryptotrader/logger"
|
||||
)
|
||||
@@ -22,6 +24,8 @@ const (
|
||||
rpcVersion = "2.0"
|
||||
)
|
||||
|
||||
var requestID nonce.Nonce
|
||||
|
||||
// WsConnect starts a new connection with the websocket API
|
||||
func (h *HitBTC) WsConnect() error {
|
||||
if !h.Websocket.IsEnabled() || !h.IsEnabled() {
|
||||
@@ -46,6 +50,11 @@ func (h *HitBTC) WsConnect() error {
|
||||
}
|
||||
|
||||
go h.WsHandleData()
|
||||
err = h.wsLogin()
|
||||
if err != nil {
|
||||
log.Errorf("%v - authentication failed: %v", h.Name, err)
|
||||
}
|
||||
|
||||
h.GenerateDefaultSubscriptions()
|
||||
|
||||
return nil
|
||||
@@ -90,86 +99,146 @@ func (h *HitBTC) WsHandleData() {
|
||||
}
|
||||
|
||||
if init.Error.Message != "" || init.Error.Code != 0 {
|
||||
if init.Error.Code == 1002 {
|
||||
h.Websocket.SetCanUseAuthenticatedEndpoints(false)
|
||||
}
|
||||
h.Websocket.DataHandler <- fmt.Errorf("hitbtc.go error - Code: %d, Message: %s",
|
||||
init.Error.Code,
|
||||
init.Error.Message)
|
||||
continue
|
||||
}
|
||||
|
||||
if init.Result {
|
||||
if _, ok := init.Result.(bool); ok {
|
||||
continue
|
||||
}
|
||||
|
||||
switch init.Method {
|
||||
case "ticker":
|
||||
var ticker WsTicker
|
||||
err := common.JSONDecode(resp.Raw, &ticker)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
continue
|
||||
}
|
||||
|
||||
ts, err := time.Parse(time.RFC3339, ticker.Params.Timestamp)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
continue
|
||||
}
|
||||
|
||||
h.Websocket.DataHandler <- exchange.TickerData{
|
||||
Exchange: h.GetName(),
|
||||
AssetType: asset.Spot,
|
||||
Pair: currency.NewPairFromString(ticker.Params.Symbol),
|
||||
Quantity: ticker.Params.Volume,
|
||||
Timestamp: ts,
|
||||
OpenPrice: ticker.Params.Open,
|
||||
HighPrice: ticker.Params.High,
|
||||
LowPrice: ticker.Params.Low,
|
||||
}
|
||||
|
||||
case "snapshotOrderbook":
|
||||
var obSnapshot WsOrderbook
|
||||
err := common.JSONDecode(resp.Raw, &obSnapshot)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
continue
|
||||
}
|
||||
|
||||
err = h.WsProcessOrderbookSnapshot(obSnapshot)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
continue
|
||||
}
|
||||
|
||||
case "updateOrderbook":
|
||||
var obUpdate WsOrderbook
|
||||
err := common.JSONDecode(resp.Raw, &obUpdate)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
continue
|
||||
}
|
||||
|
||||
h.WsProcessOrderbookUpdate(obUpdate)
|
||||
|
||||
case "snapshotTrades":
|
||||
var tradeSnapshot WsTrade
|
||||
err := common.JSONDecode(resp.Raw, &tradeSnapshot)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
continue
|
||||
}
|
||||
|
||||
case "updateTrades":
|
||||
var tradeUpdates WsTrade
|
||||
err := common.JSONDecode(resp.Raw, &tradeUpdates)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
continue
|
||||
}
|
||||
if init.Method != "" {
|
||||
h.handleSubscriptionUpdates(resp, init)
|
||||
} else {
|
||||
h.handleCommandResponses(resp, init)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HitBTC) handleSubscriptionUpdates(resp exchange.WebsocketResponse, init capture) {
|
||||
switch init.Method {
|
||||
case "ticker":
|
||||
var ticker WsTicker
|
||||
err := common.JSONDecode(resp.Raw, &ticker)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
return
|
||||
}
|
||||
ts, err := time.Parse(time.RFC3339, ticker.Params.Timestamp)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
return
|
||||
}
|
||||
h.Websocket.DataHandler <- exchange.TickerData{
|
||||
Exchange: h.GetName(),
|
||||
AssetType: asset.Spot,
|
||||
Pair: currency.NewPairFromString(ticker.Params.Symbol),
|
||||
Quantity: ticker.Params.Volume,
|
||||
Timestamp: ts,
|
||||
OpenPrice: ticker.Params.Open,
|
||||
HighPrice: ticker.Params.High,
|
||||
LowPrice: ticker.Params.Low,
|
||||
}
|
||||
case "snapshotOrderbook":
|
||||
var obSnapshot WsOrderbook
|
||||
err := common.JSONDecode(resp.Raw, &obSnapshot)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
}
|
||||
err = h.WsProcessOrderbookSnapshot(obSnapshot)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
}
|
||||
case "updateOrderbook":
|
||||
var obUpdate WsOrderbook
|
||||
err := common.JSONDecode(resp.Raw, &obUpdate)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
}
|
||||
h.WsProcessOrderbookUpdate(obUpdate)
|
||||
case "snapshotTrades":
|
||||
var tradeSnapshot WsTrade
|
||||
err := common.JSONDecode(resp.Raw, &tradeSnapshot)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
}
|
||||
case "updateTrades":
|
||||
var tradeUpdates WsTrade
|
||||
err := common.JSONDecode(resp.Raw, &tradeUpdates)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
}
|
||||
case "activeOrders":
|
||||
var activeOrders WsActiveOrdersResponse
|
||||
err := common.JSONDecode(resp.Raw, &activeOrders)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
}
|
||||
h.Websocket.DataHandler <- activeOrders
|
||||
case "report":
|
||||
var reportData WsReportResponse
|
||||
err := common.JSONDecode(resp.Raw, &reportData)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
}
|
||||
h.Websocket.DataHandler <- reportData
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HitBTC) handleCommandResponses(resp exchange.WebsocketResponse, init capture) {
|
||||
switch resultType := init.Result.(type) {
|
||||
case map[string]interface{}:
|
||||
switch resultType["reportType"].(string) {
|
||||
case "new":
|
||||
var response WsSubmitOrderSuccessResponse
|
||||
err := common.JSONDecode(resp.Raw, &response)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
}
|
||||
h.Websocket.DataHandler <- response
|
||||
case "canceled":
|
||||
var response WsCancelOrderResponse
|
||||
err := common.JSONDecode(resp.Raw, &response)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
}
|
||||
h.Websocket.DataHandler <- response
|
||||
case "replaced":
|
||||
var response WsReplaceOrderResponse
|
||||
err := common.JSONDecode(resp.Raw, &response)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
}
|
||||
h.Websocket.DataHandler <- response
|
||||
}
|
||||
case []interface{}:
|
||||
if len(resultType) == 0 {
|
||||
h.Websocket.DataHandler <- fmt.Sprintf("No data returned. ID: %v", init.ID)
|
||||
return
|
||||
}
|
||||
data := resultType[0].(map[string]interface{})
|
||||
if _, ok := data["clientOrderId"]; ok {
|
||||
var response WsActiveOrdersResponse
|
||||
err := common.JSONDecode(resp.Raw, &response)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
}
|
||||
h.Websocket.DataHandler <- response
|
||||
} else if _, ok := data["available"]; ok {
|
||||
var response WsGetTradingBalanceResponse
|
||||
err := common.JSONDecode(resp.Raw, &response)
|
||||
if err != nil {
|
||||
h.Websocket.DataHandler <- err
|
||||
}
|
||||
h.Websocket.DataHandler <- response
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WsProcessOrderbookSnapshot processes a full orderbook snapshot to a local cache
|
||||
func (h *HitBTC) WsProcessOrderbookSnapshot(ob WsOrderbook) error {
|
||||
if len(ob.Params.Bid) == 0 || len(ob.Params.Ask) == 0 {
|
||||
@@ -242,7 +311,12 @@ func (h *HitBTC) WsProcessOrderbookUpdate(ob WsOrderbook) error {
|
||||
// GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions()
|
||||
func (h *HitBTC) GenerateDefaultSubscriptions() {
|
||||
var channels = []string{"subscribeTicker", "subscribeOrderbook", "subscribeTrades", "subscribeCandles"}
|
||||
subscriptions := []exchange.WebsocketChannelSubscription{}
|
||||
var subscriptions []exchange.WebsocketChannelSubscription
|
||||
if h.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
subscriptions = append(subscriptions, exchange.WebsocketChannelSubscription{
|
||||
Channel: "subscribeReports",
|
||||
})
|
||||
}
|
||||
enabledCurrencies := h.GetEnabledPairs(asset.Spot)
|
||||
for i := range channels {
|
||||
for j := range enabledCurrencies {
|
||||
@@ -259,11 +333,12 @@ func (h *HitBTC) GenerateDefaultSubscriptions() {
|
||||
// Subscribe sends a websocket message to receive data from the channel
|
||||
func (h *HitBTC) Subscribe(channelToSubscribe exchange.WebsocketChannelSubscription) error {
|
||||
subscribe := WsNotification{
|
||||
JSONRPCVersion: rpcVersion,
|
||||
Method: channelToSubscribe.Channel,
|
||||
Params: params{
|
||||
Method: channelToSubscribe.Channel,
|
||||
}
|
||||
if channelToSubscribe.Currency.String() != "" {
|
||||
subscribe.Params = params{
|
||||
Symbol: channelToSubscribe.Currency.String(),
|
||||
},
|
||||
}
|
||||
}
|
||||
if strings.EqualFold(channelToSubscribe.Channel, "subscribeTrades") {
|
||||
subscribe.Params = params{
|
||||
@@ -316,7 +391,111 @@ func (h *HitBTC) wsSend(data interface{}) error {
|
||||
return err
|
||||
}
|
||||
if h.Verbose {
|
||||
log.Debugf("%v sending message to websocket %v", h.Name, data)
|
||||
log.Debugf("%v sending message to websocket %v", h.Name, string(json))
|
||||
}
|
||||
return h.WebsocketConn.WriteMessage(websocket.TextMessage, json)
|
||||
}
|
||||
|
||||
// Unsubscribe sends a websocket message to stop receiving data from the channel
|
||||
func (h *HitBTC) wsLogin() error {
|
||||
if !h.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) {
|
||||
return fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", h.Name)
|
||||
}
|
||||
h.Websocket.SetCanUseAuthenticatedEndpoints(true)
|
||||
nonce := fmt.Sprintf("%v", time.Now().Unix())
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(nonce), []byte(h.API.Credentials.Secret))
|
||||
request := WsLoginRequest{
|
||||
Method: "login",
|
||||
Params: WsLoginData{
|
||||
Algo: "HS256",
|
||||
PKey: h.API.Credentials.Key,
|
||||
Nonce: nonce,
|
||||
Signature: crypto.HexEncodeToString(hmac),
|
||||
},
|
||||
}
|
||||
|
||||
err := h.wsSend(request)
|
||||
if err != nil {
|
||||
h.Websocket.SetCanUseAuthenticatedEndpoints(false)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// wsPlaceOrder sends a websocket message to submit an order
|
||||
func (h *HitBTC) wsPlaceOrder(pair currency.Pair, side string, price, quantity float64) error {
|
||||
if !h.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
return fmt.Errorf("%v not authenticated, cannot place order", h.Name)
|
||||
}
|
||||
request := WsSubmitOrderRequest{
|
||||
Method: "newOrder",
|
||||
Params: WsSubmitOrderRequestData{
|
||||
ClientOrderID: fmt.Sprintf("%v", time.Now().Unix()),
|
||||
Symbol: pair,
|
||||
Side: strings.ToLower(side),
|
||||
Price: price,
|
||||
Quantity: quantity,
|
||||
},
|
||||
ID: int64(requestID.GetInc()),
|
||||
}
|
||||
return h.wsSend(request)
|
||||
}
|
||||
|
||||
// wsCancelOrder sends a websocket message to cancel an order
|
||||
func (h *HitBTC) wsCancelOrder(clientOrderID string) error {
|
||||
if !h.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
return fmt.Errorf("%v not authenticated, cannot place order", h.Name)
|
||||
}
|
||||
request := WsCancelOrderRequest{
|
||||
Method: "cancelOrder",
|
||||
Params: WsCancelOrderRequestData{
|
||||
ClientOrderID: clientOrderID,
|
||||
},
|
||||
ID: int64(requestID.GetInc()),
|
||||
}
|
||||
return h.wsSend(request)
|
||||
}
|
||||
|
||||
// wsReplaceOrder sends a websocket message to replace an order
|
||||
func (h *HitBTC) wsReplaceOrder(clientOrderID string, quantity, price float64) error {
|
||||
if !h.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
return fmt.Errorf("%v not authenticated, cannot place order", h.Name)
|
||||
}
|
||||
request := WsReplaceOrderRequest{
|
||||
Method: "cancelReplaceOrder",
|
||||
Params: WsReplaceOrderRequestData{
|
||||
ClientOrderID: clientOrderID,
|
||||
RequestClientID: fmt.Sprintf("%v", time.Now().Unix()),
|
||||
Quantity: quantity,
|
||||
Price: price,
|
||||
},
|
||||
ID: int64(requestID.GetInc()),
|
||||
}
|
||||
return h.wsSend(request)
|
||||
}
|
||||
|
||||
// wsGetActiveOrders sends a websocket message to get all active orders
|
||||
func (h *HitBTC) wsGetActiveOrders() error {
|
||||
if !h.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
return fmt.Errorf("%v not authenticated, cannot place order", h.Name)
|
||||
}
|
||||
request := WsReplaceOrderRequest{
|
||||
Method: "getOrders",
|
||||
Params: WsReplaceOrderRequestData{},
|
||||
ID: int64(requestID.GetInc()),
|
||||
}
|
||||
return h.wsSend(request)
|
||||
}
|
||||
|
||||
// wsGetTradingBalance sends a websocket message to get trading balance
|
||||
func (h *HitBTC) wsGetTradingBalance() error {
|
||||
if !h.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
return fmt.Errorf("%v not authenticated, cannot place order", h.Name)
|
||||
}
|
||||
request := WsReplaceOrderRequest{
|
||||
Method: "getTradingBalance",
|
||||
Params: WsReplaceOrderRequestData{},
|
||||
ID: int64(requestID.GetInc()),
|
||||
}
|
||||
return h.wsSend(request)
|
||||
}
|
||||
|
||||
@@ -427,18 +427,12 @@ func (h *HitBTC) GetActiveOrders(getOrdersRequest *exchange.GetOrdersRequest) ([
|
||||
symbol := currency.NewPairDelimiter(allOrders[i].Symbol,
|
||||
h.CurrencyPairs.Get(asset.Spot).ConfigFormat.Delimiter)
|
||||
side := exchange.OrderSide(strings.ToUpper(allOrders[i].Side))
|
||||
orderDate, err := time.Parse(time.RFC3339, allOrders[i].CreatedAt)
|
||||
if err != nil {
|
||||
log.Warnf("Exchange %v Func %v Order %v Could not parse date to unix with value of %v",
|
||||
h.Name, "GetActiveOrders", allOrders[i].ID, allOrders[i].CreatedAt)
|
||||
}
|
||||
|
||||
orders = append(orders, exchange.OrderDetail{
|
||||
ID: allOrders[i].ID,
|
||||
Amount: allOrders[i].Quantity,
|
||||
Exchange: h.Name,
|
||||
Price: allOrders[i].Price,
|
||||
OrderDate: orderDate,
|
||||
OrderDate: allOrders[i].CreatedAt,
|
||||
OrderSide: side,
|
||||
CurrencyPair: symbol,
|
||||
})
|
||||
@@ -471,18 +465,12 @@ func (h *HitBTC) GetOrderHistory(getOrdersRequest *exchange.GetOrdersRequest) ([
|
||||
symbol := currency.NewPairDelimiter(allOrders[i].Symbol,
|
||||
h.CurrencyPairs.Get(asset.Spot).ConfigFormat.Delimiter)
|
||||
side := exchange.OrderSide(strings.ToUpper(allOrders[i].Side))
|
||||
orderDate, err := time.Parse(time.RFC3339, allOrders[i].CreatedAt)
|
||||
if err != nil {
|
||||
log.Warnf("Exchange %v Func %v Order %v Could not parse date to unix with value of %v",
|
||||
h.Name, "GetOrderHistory", allOrders[i].ID, allOrders[i].CreatedAt)
|
||||
}
|
||||
|
||||
orders = append(orders, exchange.OrderDetail{
|
||||
ID: allOrders[i].ID,
|
||||
Amount: allOrders[i].Quantity,
|
||||
Exchange: h.Name,
|
||||
Price: allOrders[i].Price,
|
||||
OrderDate: orderDate,
|
||||
OrderDate: allOrders[i].CreatedAt,
|
||||
OrderSide: side,
|
||||
CurrencyPair: symbol,
|
||||
})
|
||||
@@ -506,3 +494,13 @@ func (h *HitBTC) UnsubscribeToWebsocketChannels(channels []exchange.WebsocketCha
|
||||
h.Websocket.UnsubscribeToChannels(channels)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSubscriptions returns a copied list of subscriptions
|
||||
func (h *HitBTC) GetSubscriptions() ([]exchange.WebsocketChannelSubscription, error) {
|
||||
return h.Websocket.GetSubscriptions(), nil
|
||||
}
|
||||
|
||||
// AuthenticateWebsocket sends an authentication message to the websocket
|
||||
func (h *HitBTC) AuthenticateWebsocket() error {
|
||||
return h.wsLogin()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user