Files
gocryptotrader/tools/websocket_client/main.go
Scott e209d85d0d Websocket request-response correlation (#328)
* Establishes new websocket functionality. Begins the creation of the websocket request

* Adding a wrapper over gorilla websocket connect,send,receive to handle ID messages. Doesn't work

* Successfully moved exchange_websocket into its own wshandler namespace. oof

* Sets up ZB to use a round trip WS request system

* Adds Kraken ID support to subscriptions. Renames duplicate func name UnsubscribeToChannels to RemoveSubscribedChannels. Adds some helper methods in the WebsocketConn to reduce duplicate code. Cleans up ZB implementation

* Fixes double locking which caused no websocket data to be read. Fixes requestid for kraken subscriptions

* Completes Huobi and Hadax implementation. Extends ZB error handling. Adds GZip support for reading messages

* Adds HitBTC support. Adds GetCurrencies, GetSymbols, GetTrades WS funcs. Adds super fun new parameter to GenerateMessageID for Unix and UnixNano

* Adds GateIO id support

* Adds Coinut support. Prevents nil reference error in constatus when there isnt one

* Standardises all Exchange websockets to use the wshandler websocket. Removes the wsRequestMtx as wshandler handles that now. Makes the Dialer a dialer, its not externally referenced that I can see.

* Fixes issue with coinut implementation. Updates bitmex currencies. Removes redundant log messages which are used to log messages

* Starts testing. Renames files

* Adds tests for websocket connection

* Reverts request.go change

* Linting everything

* Fixes rebase issue

* Final changes. Fixes variable names, removes log.Debug, removes lines, rearranges test types, removes order correlation websocket type

* Final final commit, fixing ZB issues.

* Adds traffic alerts where missed. Changes empty struct pointer addresses to nil instead. Removes empty lines

* Fixed string conversion

* Fixes issue with ZB not sending success codes

* Fixes issue with coinut processing due to nonce handling with subscriptions

* Fixes issue where ZB test failure was not caught. Removes unnecessary error handling from other ZB tests

* Removes unused interface

* Renames wshandler.Init() to wshandler.Run()

* Updates template file

* Capitalises cryptocurrencies in struct. Moves websocketResponseCheckTimeout and websocketResponseMaxLimit into config options. Moves connection configuration to main exchange Setup (where appropriate). Reverts currencylastupdated ticks. Improves reader close error checking

* Fixes two inconsistent websocket delay times

* Creates a default variable for websocket ResponseMaxLimit and ResponseCheckTimeout, then applies it to setdefaults and all tests

* Updates exchange template to set and use default websocket response limits
2019-08-07 15:15:01 +10:00

192 lines
4.3 KiB
Go

package main
import (
"errors"
"fmt"
"log"
"net/http"
"github.com/gorilla/websocket"
"github.com/thrasher-/gocryptotrader/common"
"github.com/thrasher-/gocryptotrader/config"
)
// Vars for the websocket client
var (
WSConn *websocket.Conn
)
// WebsocketEvent is the struct used for websocket events
type WebsocketEvent struct {
Exchange string `json:"exchange,omitempty"`
AssetType string `json:"assetType,omitempty"`
Event string
Data interface{}
}
// WebsocketAuth is the struct used for a websocket auth request
type WebsocketAuth struct {
Username string `json:"username"`
Password string `json:"password"`
}
// WebsocketEventResponse is the struct used for websocket event responses
type WebsocketEventResponse struct {
Event string `json:"event"`
Data interface{} `json:"data"`
Error string `json:"error"`
}
// WebsocketOrderbookTickerRequest is a struct used for ticker and orderbook
// requests
type WebsocketOrderbookTickerRequest struct {
Exchange string `json:"exchangeName"`
Currency string `json:"currency"`
AssetType string `json:"assetType"`
}
// SendWebsocketEvent sends a websocket event message
func SendWebsocketEvent(event string, reqData interface{}, result *WebsocketEventResponse) error {
req := WebsocketEvent{
Event: event,
}
if reqData != nil {
req.Data = reqData
}
err := WSConn.WriteJSON(req)
if err != nil {
return err
}
err = WSConn.ReadJSON(&result)
if err != nil {
return err
}
if result.Error != "" {
return errors.New(result.Error)
}
return nil
}
func main() {
cfg := config.GetConfig()
err := cfg.LoadConfig(config.ConfigFile)
if err != nil {
log.Fatalf("Failed to load config file: %s", err)
}
listenAddr := cfg.Webserver.ListenAddress
wsHost := fmt.Sprintf("ws://%s:%d/ws", common.ExtractHost(listenAddr),
common.ExtractPort(listenAddr))
log.Printf("Connecting to websocket host: %s", wsHost)
var dialer websocket.Dialer
WSConn, _, err = dialer.Dial(wsHost, http.Header{})
if err != nil {
log.Println("Unable to connect to websocket server")
return
}
log.Println("Connected to websocket!")
log.Println("Authenticating..")
var wsResp WebsocketEventResponse
reqData := WebsocketAuth{
Username: cfg.Webserver.AdminUsername,
Password: common.HexEncodeToString(common.GetSHA256([]byte(cfg.Webserver.AdminPassword))),
}
err = SendWebsocketEvent("auth", reqData, &wsResp)
if err != nil {
log.Fatal(err)
}
log.Println("Authenticated successfully")
log.Println("Getting config..")
err = SendWebsocketEvent("GetConfig", nil, &wsResp)
if err != nil {
log.Fatal(err)
}
log.Printf("Fetched config.")
dataJSON, err := common.JSONEncode(&wsResp.Data)
if err != nil {
log.Fatal(err)
}
var resultCfg config.Config
err = common.JSONDecode(dataJSON, &resultCfg)
if err != nil {
log.Fatal(err)
}
log.Println("Saving config..")
origBotName := resultCfg.Name
resultCfg.Name = "TEST"
err = SendWebsocketEvent("SaveConfig", resultCfg, &wsResp)
if err != nil {
log.Fatal(err)
}
log.Println("Saved config!")
resultCfg.Name = origBotName
err = SendWebsocketEvent("SaveConfig", resultCfg, &wsResp)
if err != nil {
log.Fatal(err)
}
log.Println("Saved config (restored original bot name)!")
log.Println("Getting account info..")
err = SendWebsocketEvent("GetAccountInfo", nil, &wsResp)
if err != nil {
log.Fatal(err)
}
log.Println("Got account info!")
log.Println("Getting tickers..")
err = SendWebsocketEvent("GetTickers", nil, &wsResp)
if err != nil {
log.Fatal(err)
}
log.Println("Got tickers!")
log.Println("Getting specific ticker..")
dataReq := WebsocketOrderbookTickerRequest{
Exchange: "Bitfinex",
Currency: "BTCUSD",
AssetType: "SPOT",
}
err = SendWebsocketEvent("GetTicker", dataReq, &wsResp)
if err != nil {
log.Fatal(err)
}
log.Println("Got ticker!")
log.Println("Getting orderbooks..")
err = SendWebsocketEvent("GetOrderbooks", nil, &wsResp)
if err != nil {
log.Fatal(err)
}
log.Println("Got orderbooks!")
log.Println("Getting specific orderbook..")
err = SendWebsocketEvent("GetOrderbook", dataReq, &wsResp)
if err != nil {
log.Fatal(err)
}
log.Println("Got orderbook!")
for {
var wsEvent WebsocketEvent
err = WSConn.ReadJSON(&wsEvent)
if err != nil {
break
}
log.Printf("Recv'd: %s", wsEvent.Event)
}
WSConn.Close()
}