mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-30 23:16:52 +00:00
Merge branch 'master' into engine
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
# GoCryptoTrader package Gateio
|
||||
|
||||
<img src="https://github.com/thrasher-/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70">
|
||||
<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-/gocryptotrader)
|
||||
[](https://github.com/thrasher-/gocryptotrader/blob/master/LICENSE)
|
||||
[](https://godoc.org/github.com/thrasher-/gocryptotrader/exchanges/gateio)
|
||||
[](http://codecov.io/github/thrasher-/gocryptotrader?branch=master)
|
||||
[](https://goreportcard.com/report/github.com/thrasher-/gocryptotrader)
|
||||
[](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/gateio)
|
||||
[](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
|
||||
[](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
|
||||
|
||||
|
||||
This gateio package is part of the GoCryptoTrader codebase.
|
||||
@@ -26,7 +26,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
|
||||
|
||||
### How to enable
|
||||
|
||||
+ [Enable via configuration](https://github.com/thrasher-/gocryptotrader/tree/master/config#enable-exchange-via-config-example)
|
||||
+ [Enable via configuration](https://github.com/thrasher-corp/gocryptotrader/tree/master/config#enable-exchange-via-config-example)
|
||||
|
||||
+ Individual package example below:
|
||||
|
||||
@@ -127,12 +127,12 @@ 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-/gocryptotrader/blob/master/doc/coding_style.md).
|
||||
+ 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-/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
||||
<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:
|
||||
|
||||
|
||||
@@ -7,14 +7,13 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/common/convert"
|
||||
"github.com/thrasher-/gocryptotrader/common/crypto"
|
||||
"github.com/thrasher-/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/convert"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/crypto"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/wshandler"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -45,9 +44,8 @@ const (
|
||||
|
||||
// Gateio is the overarching type across this package
|
||||
type Gateio struct {
|
||||
WebsocketConn *websocket.Conn
|
||||
WebsocketConn *wshandler.WebsocketConnection
|
||||
exchange.Base
|
||||
wsRequestMtx sync.Mutex
|
||||
}
|
||||
|
||||
// GetSymbols returns all supported symbols
|
||||
|
||||
@@ -2,16 +2,15 @@ package gateio
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"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"
|
||||
"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/wshandler"
|
||||
)
|
||||
|
||||
// Please supply your own APIKEYS here for due diligence testing
|
||||
@@ -23,6 +22,7 @@ const (
|
||||
)
|
||||
|
||||
var g Gateio
|
||||
var wsSetupRan bool
|
||||
|
||||
func TestSetDefaults(t *testing.T) {
|
||||
g.SetDefaults()
|
||||
@@ -500,47 +500,126 @@ func TestGetOrderInfo(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestWsAuth dials websocket, sends login request.
|
||||
func TestWsAuth(t *testing.T) {
|
||||
// TestWsGetBalance dials websocket, sends balance request.
|
||||
func TestWsGetBalance(t *testing.T) {
|
||||
g.SetDefaults()
|
||||
TestSetup(t)
|
||||
if !g.Websocket.IsEnabled() && !g.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
|
||||
t.Skip(exchange.WebsocketNotEnabled)
|
||||
t.Skip(wshandler.WebsocketNotEnabled)
|
||||
}
|
||||
g.WebsocketConn = &wshandler.WebsocketConnection{
|
||||
ExchangeName: g.Name,
|
||||
URL: gateioWebsocketEndpoint,
|
||||
Verbose: g.Verbose,
|
||||
RateLimit: gateioWebsocketRateLimit,
|
||||
ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit,
|
||||
ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout,
|
||||
}
|
||||
var err error
|
||||
var dialer websocket.Dialer
|
||||
g.WebsocketConn, _, err = dialer.Dial(g.Websocket.GetWebsocketURL(),
|
||||
http.Header{})
|
||||
err := g.WebsocketConn.Dial(&dialer, http.Header{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
go g.WsHandleData()
|
||||
g.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
|
||||
g.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride()
|
||||
go g.WsHandleData()
|
||||
defer g.WebsocketConn.Close()
|
||||
err = g.wsServerSignIn()
|
||||
resp, err := g.wsServerSignIn()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp.Result.Status != "success" {
|
||||
t.Fatal("Unsuccessful login")
|
||||
}
|
||||
_, err = g.wsGetBalance([]string{"EOS", "BTC"})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestWsGetOrderInfo dials websocket, sends order info request.
|
||||
func TestWsGetOrderInfo(t *testing.T) {
|
||||
g.SetDefaults()
|
||||
TestSetup(t)
|
||||
if !g.Websocket.IsEnabled() && !g.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
|
||||
t.Skip(wshandler.WebsocketNotEnabled)
|
||||
}
|
||||
g.WebsocketConn = &wshandler.WebsocketConnection{
|
||||
ExchangeName: g.Name,
|
||||
URL: gateioWebsocketEndpoint,
|
||||
Verbose: g.Verbose,
|
||||
RateLimit: gateioWebsocketRateLimit,
|
||||
ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit,
|
||||
ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout,
|
||||
}
|
||||
var dialer websocket.Dialer
|
||||
err := g.WebsocketConn.Dial(&dialer, http.Header{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
go g.WsHandleData()
|
||||
g.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
|
||||
g.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride()
|
||||
resp, err := g.wsServerSignIn()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp.Result.Status != "success" {
|
||||
t.Fatal("Unsuccessful login")
|
||||
}
|
||||
_, err = g.wsGetOrderInfo("EOS_USDT", 0, 10)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func setupWSTestAuth(t *testing.T) {
|
||||
if wsSetupRan {
|
||||
return
|
||||
}
|
||||
g.SetDefaults()
|
||||
TestSetup(t)
|
||||
if !g.Websocket.IsEnabled() && !g.API.AuthenticatedWebsocketSupport {
|
||||
t.Skip(wshandler.WebsocketNotEnabled)
|
||||
}
|
||||
g.WebsocketConn = &wshandler.WebsocketConnection{
|
||||
ExchangeName: g.Name,
|
||||
URL: gateioWebsocketEndpoint,
|
||||
Verbose: g.Verbose,
|
||||
RateLimit: gateioWebsocketRateLimit,
|
||||
ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit,
|
||||
ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout,
|
||||
}
|
||||
var dialer websocket.Dialer
|
||||
err := g.WebsocketConn.Dial(&dialer, http.Header{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
go g.WsHandleData()
|
||||
g.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
|
||||
g.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride()
|
||||
wsSetupRan = true
|
||||
}
|
||||
|
||||
// TestWsSubscribe dials websocket, sends a subscribe request.
|
||||
func TestWsSubscribe(t *testing.T) {
|
||||
setupWSTestAuth(t)
|
||||
err := g.Subscribe(wshandler.WebsocketChannelSubscription{
|
||||
Channel: "ticker.subscribe",
|
||||
Currency: currency.NewPairWithDelimiter(currency.BTC.String(), currency.USDT.String(), "_"),
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestWsUnsubscribe dials websocket, sends an unsubscribe request.
|
||||
func TestWsUnsubscribe(t *testing.T) {
|
||||
setupWSTestAuth(t)
|
||||
err := g.Unsubscribe(wshandler.WebsocketChannelSubscription{
|
||||
Channel: "ticker.subscribe",
|
||||
Currency: currency.NewPairWithDelimiter(currency.BTC.String(), currency.USDT.String(), "_"),
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
timer := time.NewTimer(sharedtestvalues.WebsocketResponseDefaultTimeout)
|
||||
select {
|
||||
case resultString := <-g.Websocket.DataHandler:
|
||||
if !strings.Contains(resultString.(string), "success") {
|
||||
t.Error("Authentication failed")
|
||||
}
|
||||
case <-timer.C:
|
||||
t.Error("Expected response")
|
||||
}
|
||||
timer.Stop()
|
||||
err = g.wsGetBalance()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
timer = time.NewTimer(sharedtestvalues.WebsocketResponseDefaultTimeout)
|
||||
select {
|
||||
case <-g.Websocket.DataHandler:
|
||||
case <-timer.C:
|
||||
t.Error("Expected response")
|
||||
}
|
||||
timer.Stop()
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
)
|
||||
|
||||
// TimeInterval Interval represents interval enum.
|
||||
@@ -24,14 +24,6 @@ var (
|
||||
TimeIntervalDay = TimeInterval(60 * 60 * 24)
|
||||
)
|
||||
|
||||
// IDs for requests
|
||||
const (
|
||||
IDGeneric = 0000
|
||||
IDSignIn = 1010
|
||||
IDBalance = 2000
|
||||
IDOrderQuery = 3001
|
||||
)
|
||||
|
||||
// MarketInfoResponse holds the market info data
|
||||
type MarketInfoResponse struct {
|
||||
Result string `json:"result"`
|
||||
@@ -467,3 +459,32 @@ type WebSocketOrderQueryRecords struct {
|
||||
FilledAmount string `json:"filledAmount"`
|
||||
FilledTotal string `json:"filledTotal"`
|
||||
}
|
||||
|
||||
// WebsocketAuthenticationResponse contains the result of a login request
|
||||
type WebsocketAuthenticationResponse struct {
|
||||
Error string `json:"error"`
|
||||
Result struct {
|
||||
Status string `json:"status"`
|
||||
} `json:"result"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
// wsGetBalanceRequest
|
||||
type wsGetBalanceRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
Method string `json:"method"`
|
||||
Params []string `json:"params,omitempty"`
|
||||
}
|
||||
|
||||
// WsGetBalanceResponse stores WS GetBalance response
|
||||
type WsGetBalanceResponse struct {
|
||||
Error interface{} `json:"error"`
|
||||
Result map[currency.Code]WsGetBalanceResponseData `json:"result,omitempty"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
// WsGetBalanceResponseData contains currency data
|
||||
type WsGetBalanceResponseData struct {
|
||||
Available float64 `json:"available,string"`
|
||||
Freeze float64 `json:"freeze,string"`
|
||||
}
|
||||
|
||||
@@ -1,56 +1,42 @@
|
||||
package gateio
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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/orderbook"
|
||||
log "github.com/thrasher-/gocryptotrader/logger"
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/crypto"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/wshandler"
|
||||
log "github.com/thrasher-corp/gocryptotrader/logger"
|
||||
)
|
||||
|
||||
const (
|
||||
gateioWebsocketEndpoint = "wss://ws.gate.io/v3/"
|
||||
gatioWsMethodPing = "ping"
|
||||
gateioWebsocketRateLimit = 120 * time.Millisecond
|
||||
gateioWebsocketRateLimit = 120
|
||||
)
|
||||
|
||||
// WsConnect initiates a websocket connection
|
||||
func (g *Gateio) WsConnect() error {
|
||||
if !g.Websocket.IsEnabled() || !g.IsEnabled() {
|
||||
return errors.New(exchange.WebsocketNotEnabled)
|
||||
return errors.New(wshandler.WebsocketNotEnabled)
|
||||
}
|
||||
|
||||
var dialer websocket.Dialer
|
||||
if g.Websocket.GetProxyAddress() != "" {
|
||||
proxy, err := url.Parse(g.Websocket.GetProxyAddress())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dialer.Proxy = http.ProxyURL(proxy)
|
||||
}
|
||||
|
||||
var err error
|
||||
g.WebsocketConn, _, err = dialer.Dial(g.Websocket.GetWebsocketURL(),
|
||||
http.Header{})
|
||||
err := g.WebsocketConn.Dial(&dialer, http.Header{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go g.WsHandleData()
|
||||
|
||||
err = g.wsServerSignIn()
|
||||
_, err = g.wsServerSignIn()
|
||||
if err != nil {
|
||||
log.Errorf(log.ExchangeSys, "%v - authentication failed: %v\n", g.Name, err)
|
||||
}
|
||||
@@ -59,37 +45,33 @@ func (g *Gateio) WsConnect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Gateio) wsServerSignIn() error {
|
||||
func (g *Gateio) wsServerSignIn() (*WebsocketAuthenticationResponse, error) {
|
||||
if !g.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) {
|
||||
return fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", g.Name)
|
||||
return nil, fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", g.Name)
|
||||
}
|
||||
nonce := int(time.Now().Unix() * 1000)
|
||||
sigTemp := g.GenerateSignature(strconv.Itoa(nonce))
|
||||
signature := crypto.Base64Encode(sigTemp)
|
||||
signinWsRequest := WebsocketRequest{
|
||||
ID: IDSignIn,
|
||||
ID: g.WebsocketConn.GenerateMessageID(true),
|
||||
Method: "server.sign",
|
||||
Params: []interface{}{g.API.Credentials.Key, signature, nonce},
|
||||
}
|
||||
err := g.wsSend(signinWsRequest)
|
||||
resp, err := g.WebsocketConn.SendMessageReturnResponse(signinWsRequest.ID, signinWsRequest)
|
||||
if err != nil {
|
||||
g.Websocket.SetCanUseAuthenticatedEndpoints(false)
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
time.Sleep(time.Second * 2) // sleep to allow server to complete sign-on if further authenticated requests are sent prior to this they will fail
|
||||
return nil
|
||||
}
|
||||
|
||||
// WsReadData reads from the websocket connection and returns the websocket
|
||||
// response
|
||||
func (g *Gateio) WsReadData() (exchange.WebsocketResponse, error) {
|
||||
_, resp, err := g.WebsocketConn.ReadMessage()
|
||||
var response WebsocketAuthenticationResponse
|
||||
err = common.JSONDecode(resp, &response)
|
||||
if err != nil {
|
||||
return exchange.WebsocketResponse{}, err
|
||||
g.Websocket.SetCanUseAuthenticatedEndpoints(false)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g.Websocket.TrafficAlert <- struct{}{}
|
||||
return exchange.WebsocketResponse{Raw: resp}, nil
|
||||
if response.Result.Status == "success" {
|
||||
g.Websocket.SetCanUseAuthenticatedEndpoints(true)
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// WsHandleData handles all the websocket data coming from the websocket
|
||||
@@ -107,25 +89,28 @@ func (g *Gateio) WsHandleData() {
|
||||
return
|
||||
|
||||
default:
|
||||
resp, err := g.WsReadData()
|
||||
resp, err := g.WebsocketConn.ReadMessage()
|
||||
if err != nil {
|
||||
g.Websocket.DataHandler <- err
|
||||
// Read data error messages can overwhelm and panic the application
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
return
|
||||
}
|
||||
|
||||
g.Websocket.TrafficAlert <- struct{}{}
|
||||
var result WebsocketResponse
|
||||
err = common.JSONDecode(resp.Raw, &result)
|
||||
if err != nil {
|
||||
g.Websocket.DataHandler <- err
|
||||
continue
|
||||
}
|
||||
|
||||
if result.ID > 0 {
|
||||
g.WebsocketConn.AddResponseWithID(result.ID, resp.Raw)
|
||||
continue
|
||||
}
|
||||
|
||||
if result.Error.Code != 0 {
|
||||
if strings.Contains(result.Error.Message, "authentication") {
|
||||
g.Websocket.DataHandler <- fmt.Errorf("%v - authentication failed: %v", g.Name, err)
|
||||
g.Websocket.SetCanUseAuthenticatedEndpoints(false)
|
||||
|
||||
continue
|
||||
}
|
||||
g.Websocket.DataHandler <- fmt.Errorf("%v error %s",
|
||||
@@ -133,49 +118,6 @@ func (g *Gateio) WsHandleData() {
|
||||
continue
|
||||
}
|
||||
|
||||
switch result.ID {
|
||||
case IDSignIn:
|
||||
g.Websocket.SetCanUseAuthenticatedEndpoints(true)
|
||||
g.Websocket.DataHandler <- string(result.Result)
|
||||
case IDBalance:
|
||||
var balance WebsocketBalance
|
||||
var balanceInterface interface{}
|
||||
err = json.Unmarshal(result.Result, &balanceInterface)
|
||||
if err != nil {
|
||||
g.Websocket.DataHandler <- err
|
||||
}
|
||||
var p WebsocketBalanceCurrency
|
||||
switch x := balanceInterface.(type) {
|
||||
case map[string]interface{}:
|
||||
for xx := range x {
|
||||
switch kk := x[xx].(type) {
|
||||
case map[string]interface{}:
|
||||
p = WebsocketBalanceCurrency{
|
||||
Currency: xx,
|
||||
Available: kk["available"].(string),
|
||||
Locked: kk["freeze"].(string),
|
||||
}
|
||||
balance.Currency = append(balance.Currency, p)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
g.Websocket.DataHandler <- balance
|
||||
case IDOrderQuery:
|
||||
var orderQuery WebSocketOrderQueryResult
|
||||
err = common.JSONDecode(result.Result, &orderQuery)
|
||||
if err != nil {
|
||||
g.Websocket.DataHandler <- err
|
||||
continue
|
||||
}
|
||||
g.Websocket.DataHandler <- orderQuery
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.Contains(result.Method, "ticker"):
|
||||
var ticker WebsocketTicker
|
||||
@@ -192,7 +134,7 @@ func (g *Gateio) WsHandleData() {
|
||||
continue
|
||||
}
|
||||
|
||||
g.Websocket.DataHandler <- exchange.TickerData{
|
||||
g.Websocket.DataHandler <- wshandler.TickerData{
|
||||
Timestamp: time.Now(),
|
||||
Pair: currency.NewPairFromString(c),
|
||||
AssetType: asset.Spot,
|
||||
@@ -220,7 +162,7 @@ func (g *Gateio) WsHandleData() {
|
||||
}
|
||||
|
||||
for _, trade := range trades {
|
||||
g.Websocket.DataHandler <- exchange.TradeData{
|
||||
g.Websocket.DataHandler <- wshandler.TradeData{
|
||||
Timestamp: time.Now(),
|
||||
CurrencyPair: currency.NewPairFromString(c),
|
||||
AssetType: asset.Spot,
|
||||
@@ -313,7 +255,7 @@ func (g *Gateio) WsHandleData() {
|
||||
}
|
||||
}
|
||||
|
||||
g.Websocket.DataHandler <- exchange.WebsocketOrderbookUpdate{
|
||||
g.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{
|
||||
Pair: currency.NewPairFromString(c),
|
||||
Asset: asset.Spot,
|
||||
Exchange: g.GetName(),
|
||||
@@ -333,7 +275,7 @@ func (g *Gateio) WsHandleData() {
|
||||
low, _ := strconv.ParseFloat(data[4].(string), 64)
|
||||
volume, _ := strconv.ParseFloat(data[5].(string), 64)
|
||||
|
||||
g.Websocket.DataHandler <- exchange.KlineData{
|
||||
g.Websocket.DataHandler <- wshandler.KlineData{
|
||||
Timestamp: time.Now(),
|
||||
Pair: currency.NewPairFromString(data[7].(string)),
|
||||
AssetType: asset.Spot,
|
||||
@@ -355,11 +297,11 @@ func (g *Gateio) GenerateAuthenticatedSubscriptions() {
|
||||
return
|
||||
}
|
||||
var channels = []string{"balance.subscribe", "order.subscribe"}
|
||||
var subscriptions []exchange.WebsocketChannelSubscription
|
||||
var subscriptions []wshandler.WebsocketChannelSubscription
|
||||
enabledCurrencies := g.GetEnabledPairs(asset.Spot)
|
||||
for i := range channels {
|
||||
for j := range enabledCurrencies {
|
||||
subscriptions = append(subscriptions, exchange.WebsocketChannelSubscription{
|
||||
subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{
|
||||
Channel: channels[i],
|
||||
Currency: enabledCurrencies[j],
|
||||
})
|
||||
@@ -371,7 +313,7 @@ func (g *Gateio) GenerateAuthenticatedSubscriptions() {
|
||||
// GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions()
|
||||
func (g *Gateio) GenerateDefaultSubscriptions() {
|
||||
var channels = []string{"ticker.subscribe", "trades.subscribe", "depth.subscribe", "kline.subscribe"}
|
||||
var subscriptions []exchange.WebsocketChannelSubscription
|
||||
var subscriptions []wshandler.WebsocketChannelSubscription
|
||||
enabledCurrencies := g.GetEnabledPairs(asset.Spot)
|
||||
for i := range channels {
|
||||
for j := range enabledCurrencies {
|
||||
@@ -382,7 +324,7 @@ func (g *Gateio) GenerateDefaultSubscriptions() {
|
||||
} else if strings.EqualFold(channels[i], "kline.subscribe") {
|
||||
params["interval"] = 1800
|
||||
}
|
||||
subscriptions = append(subscriptions, exchange.WebsocketChannelSubscription{
|
||||
subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{
|
||||
Channel: channels[i],
|
||||
Currency: enabledCurrencies[j],
|
||||
Params: params,
|
||||
@@ -393,54 +335,84 @@ func (g *Gateio) GenerateDefaultSubscriptions() {
|
||||
}
|
||||
|
||||
// Subscribe sends a websocket message to receive data from the channel
|
||||
func (g *Gateio) Subscribe(channelToSubscribe exchange.WebsocketChannelSubscription) error {
|
||||
func (g *Gateio) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error {
|
||||
params := []interface{}{channelToSubscribe.Currency.String()}
|
||||
for _, paramValue := range channelToSubscribe.Params {
|
||||
params = append(params, paramValue)
|
||||
}
|
||||
|
||||
subscribe := WebsocketRequest{
|
||||
ID: IDGeneric,
|
||||
ID: g.WebsocketConn.GenerateMessageID(true),
|
||||
Method: channelToSubscribe.Channel,
|
||||
Params: params,
|
||||
}
|
||||
|
||||
if strings.EqualFold(channelToSubscribe.Channel, "balance.subscribe") {
|
||||
subscribe.ID = IDBalance
|
||||
resp, err := g.WebsocketConn.SendMessageReturnResponse(subscribe.ID, subscribe)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return g.wsSend(subscribe)
|
||||
var response WebsocketAuthenticationResponse
|
||||
err = common.JSONDecode(resp, &response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if response.Result.Status != "success" {
|
||||
return fmt.Errorf("%v could not subscribe to %v", g.Name, channelToSubscribe.Channel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unsubscribe sends a websocket message to stop receiving data from the channel
|
||||
func (g *Gateio) Unsubscribe(channelToSubscribe exchange.WebsocketChannelSubscription) error {
|
||||
func (g *Gateio) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error {
|
||||
unsbuscribeText := strings.Replace(channelToSubscribe.Channel, "subscribe", "unsubscribe", 1)
|
||||
subscribe := WebsocketRequest{
|
||||
ID: IDGeneric,
|
||||
ID: g.WebsocketConn.GenerateMessageID(true),
|
||||
Method: unsbuscribeText,
|
||||
Params: []interface{}{channelToSubscribe.Currency.String(), 1800},
|
||||
}
|
||||
return g.wsSend(subscribe)
|
||||
resp, err := g.WebsocketConn.SendMessageReturnResponse(subscribe.ID, subscribe)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var response WebsocketAuthenticationResponse
|
||||
err = common.JSONDecode(resp, &response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if response.Result.Status != "success" {
|
||||
return fmt.Errorf("%v could not subscribe to %v", g.Name, channelToSubscribe.Channel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Gateio) wsGetBalance() error {
|
||||
func (g *Gateio) wsGetBalance(currencies []string) (*WsGetBalanceResponse, error) {
|
||||
if !g.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
return fmt.Errorf("%v not authorised to get balance", g.Name)
|
||||
return nil, fmt.Errorf("%v not authorised to get balance", g.Name)
|
||||
}
|
||||
balanceWsRequest := WebsocketRequest{
|
||||
ID: IDBalance,
|
||||
balanceWsRequest := wsGetBalanceRequest{
|
||||
ID: g.WebsocketConn.GenerateMessageID(false),
|
||||
Method: "balance.query",
|
||||
Params: []interface{}{},
|
||||
Params: currencies,
|
||||
}
|
||||
return g.wsSend(balanceWsRequest)
|
||||
resp, err := g.WebsocketConn.SendMessageReturnResponse(balanceWsRequest.ID, balanceWsRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var balance WsGetBalanceResponse
|
||||
err = common.JSONDecode(resp, &balance)
|
||||
if err != nil {
|
||||
return &balance, err
|
||||
}
|
||||
|
||||
return &balance, nil
|
||||
}
|
||||
|
||||
func (g *Gateio) wsGetOrderInfo(market string, offset, limit int) error {
|
||||
func (g *Gateio) wsGetOrderInfo(market string, offset, limit int) (*WebSocketOrderQueryResult, error) {
|
||||
if !g.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
return fmt.Errorf("%v not authorised to get order info", g.Name)
|
||||
return nil, fmt.Errorf("%v not authorised to get order info", g.Name)
|
||||
}
|
||||
order := WebsocketRequest{
|
||||
ID: IDOrderQuery,
|
||||
ID: g.WebsocketConn.GenerateMessageID(true),
|
||||
Method: "order.query",
|
||||
Params: []interface{}{
|
||||
market,
|
||||
@@ -448,17 +420,14 @@ func (g *Gateio) wsGetOrderInfo(market string, offset, limit int) error {
|
||||
limit,
|
||||
},
|
||||
}
|
||||
return g.wsSend(order)
|
||||
}
|
||||
|
||||
// WsSend sends data to the websocket server
|
||||
func (g *Gateio) wsSend(data interface{}) error {
|
||||
g.wsRequestMtx.Lock()
|
||||
defer g.wsRequestMtx.Unlock()
|
||||
if g.Verbose {
|
||||
log.Debugf(log.ExchangeSys, "%v sending message to websocket %v", g.Name, data)
|
||||
resp, err := g.WebsocketConn.SendMessageReturnResponse(order.ID, order)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Basic rate limiter
|
||||
time.Sleep(gateioWebsocketRateLimit)
|
||||
return g.WebsocketConn.WriteJSON(data)
|
||||
var orderQuery WebSocketOrderQueryResult
|
||||
err = common.JSONDecode(resp, &orderQuery)
|
||||
if err != nil {
|
||||
return &orderQuery, err
|
||||
}
|
||||
return &orderQuery, nil
|
||||
}
|
||||
|
||||
@@ -8,15 +8,16 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"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/asset"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
log "github.com/thrasher-/gocryptotrader/logger"
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/config"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/wshandler"
|
||||
log "github.com/thrasher-corp/gocryptotrader/logger"
|
||||
)
|
||||
|
||||
// GetDefaultConfig returns a default exchange config
|
||||
@@ -90,13 +91,17 @@ func (g *Gateio) SetDefaults() {
|
||||
g.API.Endpoints.URLSecondaryDefault = gateioMarketURL
|
||||
g.API.Endpoints.URLSecondary = g.API.Endpoints.URLSecondaryDefault
|
||||
g.API.Endpoints.WebsocketURL = gateioWebsocketEndpoint
|
||||
g.WebsocketInit()
|
||||
g.Websocket.Functionality = exchange.WebsocketTickerSupported |
|
||||
exchange.WebsocketTradeDataSupported |
|
||||
exchange.WebsocketOrderbookSupported |
|
||||
exchange.WebsocketKlineSupported |
|
||||
exchange.WebsocketSubscribeSupported |
|
||||
exchange.WebsocketUnsubscribeSupported
|
||||
g.Websocket = wshandler.New()
|
||||
g.Websocket.Functionality = wshandler.WebsocketTickerSupported |
|
||||
wshandler.WebsocketTradeDataSupported |
|
||||
wshandler.WebsocketOrderbookSupported |
|
||||
wshandler.WebsocketKlineSupported |
|
||||
wshandler.WebsocketSubscribeSupported |
|
||||
wshandler.WebsocketUnsubscribeSupported |
|
||||
wshandler.WebsocketAuthenticatedEndpointsSupported |
|
||||
wshandler.WebsocketMessageCorrelationSupported
|
||||
g.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
|
||||
g.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
|
||||
}
|
||||
|
||||
// Setup sets user configuration
|
||||
@@ -111,14 +116,29 @@ func (g *Gateio) Setup(exch *config.ExchangeConfig) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return g.WebsocketSetup(g.WsConnect,
|
||||
err = g.Websocket.Setup(g.WsConnect,
|
||||
g.Subscribe,
|
||||
g.Unsubscribe,
|
||||
exch.Name,
|
||||
exch.Features.Enabled.Websocket,
|
||||
exch.Verbose,
|
||||
gateioWebsocketEndpoint,
|
||||
exch.API.Endpoints.WebsocketURL)
|
||||
exch.API.Endpoints.WebsocketURL,
|
||||
exch.API.AuthenticatedWebsocketSupport)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g.WebsocketConn = &wshandler.WebsocketConnection{
|
||||
ExchangeName: g.Name,
|
||||
URL: g.Websocket.GetWebsocketURL(),
|
||||
ProxyURL: g.Websocket.GetProxyAddress(),
|
||||
Verbose: g.Verbose,
|
||||
ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
|
||||
ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
|
||||
RateLimit: gateioWebsocketRateLimit,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start starts the GateIO go routine
|
||||
@@ -473,7 +493,7 @@ func (g *Gateio) WithdrawFiatFundsToInternationalBank(withdrawRequest *exchange.
|
||||
}
|
||||
|
||||
// GetWebsocket returns a pointer to the exchange websocket
|
||||
func (g *Gateio) GetWebsocket() (*exchange.Websocket, error) {
|
||||
func (g *Gateio) GetWebsocket() (*wshandler.Websocket, error) {
|
||||
return g.Websocket, nil
|
||||
}
|
||||
|
||||
@@ -564,24 +584,25 @@ func (g *Gateio) GetOrderHistory(getOrdersRequest *exchange.GetOrdersRequest) ([
|
||||
|
||||
// SubscribeToWebsocketChannels appends to ChannelsToSubscribe
|
||||
// which lets websocket.manageSubscriptions handle subscribing
|
||||
func (g *Gateio) SubscribeToWebsocketChannels(channels []exchange.WebsocketChannelSubscription) error {
|
||||
func (g *Gateio) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error {
|
||||
g.Websocket.SubscribeToChannels(channels)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe
|
||||
// which lets websocket.manageSubscriptions handle unsubscribing
|
||||
func (g *Gateio) UnsubscribeToWebsocketChannels(channels []exchange.WebsocketChannelSubscription) error {
|
||||
g.Websocket.UnsubscribeToChannels(channels)
|
||||
func (g *Gateio) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error {
|
||||
g.Websocket.RemoveSubscribedChannels(channels)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSubscriptions returns a copied list of subscriptions
|
||||
func (g *Gateio) GetSubscriptions() ([]exchange.WebsocketChannelSubscription, error) {
|
||||
func (g *Gateio) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) {
|
||||
return g.Websocket.GetSubscriptions(), nil
|
||||
}
|
||||
|
||||
// AuthenticateWebsocket sends an authentication message to the websocket
|
||||
func (g *Gateio) AuthenticateWebsocket() error {
|
||||
return g.wsServerSignIn()
|
||||
_, err := g.wsServerSignIn()
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user