mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-03 15:10:49 +00:00
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
This commit is contained in:
@@ -6,16 +6,15 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
"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/request"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/wshandler"
|
||||
log "github.com/thrasher-/gocryptotrader/logger"
|
||||
)
|
||||
|
||||
@@ -47,9 +46,8 @@ const (
|
||||
// COINUT is the overarching type across the coinut package
|
||||
type COINUT struct {
|
||||
exchange.Base
|
||||
WebsocketConn *websocket.Conn
|
||||
WebsocketConn *wshandler.WebsocketConnection
|
||||
InstrumentMap map[string]int
|
||||
wsRequestMtx sync.Mutex
|
||||
}
|
||||
|
||||
// SetDefaults sets current default values
|
||||
@@ -76,15 +74,18 @@ func (c *COINUT) SetDefaults() {
|
||||
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
|
||||
c.APIUrlDefault = coinutAPIURL
|
||||
c.APIUrl = c.APIUrlDefault
|
||||
c.WebsocketInit()
|
||||
c.Websocket.Functionality = exchange.WebsocketTickerSupported |
|
||||
exchange.WebsocketOrderbookSupported |
|
||||
exchange.WebsocketTradeDataSupported |
|
||||
exchange.WebsocketSubscribeSupported |
|
||||
exchange.WebsocketUnsubscribeSupported |
|
||||
exchange.WebsocketAuthenticatedEndpointsSupported |
|
||||
exchange.WebsocketSubmitOrderSupported |
|
||||
exchange.WebsocketCancelOrderSupported
|
||||
c.Websocket = wshandler.New()
|
||||
c.Websocket.Functionality = wshandler.WebsocketTickerSupported |
|
||||
wshandler.WebsocketOrderbookSupported |
|
||||
wshandler.WebsocketTradeDataSupported |
|
||||
wshandler.WebsocketSubscribeSupported |
|
||||
wshandler.WebsocketUnsubscribeSupported |
|
||||
wshandler.WebsocketAuthenticatedEndpointsSupported |
|
||||
wshandler.WebsocketSubmitOrderSupported |
|
||||
wshandler.WebsocketCancelOrderSupported |
|
||||
wshandler.WebsocketMessageCorrelationSupported
|
||||
c.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
|
||||
c.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
|
||||
}
|
||||
|
||||
// Setup sets the current exchange configuration
|
||||
@@ -125,17 +126,27 @@ func (c *COINUT) Setup(exch *config.ExchangeConfig) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = c.WebsocketSetup(c.WsConnect,
|
||||
err = c.Websocket.Setup(c.WsConnect,
|
||||
c.Subscribe,
|
||||
c.Unsubscribe,
|
||||
exch.Name,
|
||||
exch.Websocket,
|
||||
exch.Verbose,
|
||||
coinutWebsocketURL,
|
||||
exch.WebsocketURL)
|
||||
exch.WebsocketURL,
|
||||
exch.AuthenticatedWebsocketAPISupport)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
c.WebsocketConn = &wshandler.WebsocketConnection{
|
||||
ExchangeName: c.Name,
|
||||
URL: c.Websocket.GetWebsocketURL(),
|
||||
ProxyURL: c.Websocket.GetProxyAddress(),
|
||||
Verbose: c.Verbose,
|
||||
ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
|
||||
ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
|
||||
RateLimit: coinutWebsocketRateLimit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/sharedtestvalues"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/wshandler"
|
||||
)
|
||||
|
||||
var c COINUT
|
||||
@@ -55,12 +56,18 @@ func setupWSTestAuth(t *testing.T) {
|
||||
c.SetDefaults()
|
||||
TestSetup(t)
|
||||
if !c.Websocket.IsEnabled() && !c.AuthenticatedWebsocketAPISupport || !areTestAPIKeysSet() {
|
||||
t.Skip(exchange.WebsocketNotEnabled)
|
||||
t.Skip(wshandler.WebsocketNotEnabled)
|
||||
}
|
||||
c.WebsocketConn = &wshandler.WebsocketConnection{
|
||||
ExchangeName: c.Name,
|
||||
URL: coinutWebsocketURL,
|
||||
Verbose: c.Verbose,
|
||||
RateLimit: coinutWebsocketRateLimit,
|
||||
ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit,
|
||||
ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout,
|
||||
}
|
||||
var err error
|
||||
var dialer websocket.Dialer
|
||||
c.WebsocketConn, _, err = dialer.Dial(c.Websocket.GetWebsocketURL(),
|
||||
http.Header{})
|
||||
err := c.WebsocketConn.Dial(&dialer, http.Header{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -72,17 +79,6 @@ func setupWSTestAuth(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
timer := time.NewTimer(5 * time.Second)
|
||||
select {
|
||||
case resp := <-c.Websocket.DataHandler:
|
||||
if resp.(WsLoginResponse).Username != clientID {
|
||||
t.Fatal("Unsuccessful login")
|
||||
}
|
||||
case <-timer.C:
|
||||
t.Fatal("Expected response")
|
||||
}
|
||||
timer.Stop()
|
||||
time.Sleep(2 * time.Second)
|
||||
instrumentListByString = make(map[string]int64)
|
||||
instrumentListByString[currency.NewPair(currency.LTC, currency.BTC).String()] = 1
|
||||
wsSetupRan = true
|
||||
@@ -448,28 +444,21 @@ func TestGetDepositAddress(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestWsAuthGetAccountBalance dials websocket, sends login request.
|
||||
// TestWsAuthGetAccountBalance dials websocket, retrieves account balance
|
||||
func TestWsAuthGetAccountBalance(t *testing.T) {
|
||||
setupWSTestAuth(t)
|
||||
err := c.wsGetAccountBalance()
|
||||
_, err := c.wsGetAccountBalance()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
timer := time.NewTimer(sharedtestvalues.WebsocketResponseExtendedTimeout)
|
||||
select {
|
||||
case resp := <-c.Websocket.DataHandler:
|
||||
if resp.(WsUserBalanceResponse).Status[0] != "OK" {
|
||||
t.Error("Expected successful response")
|
||||
}
|
||||
case <-timer.C:
|
||||
t.Error("Expected response")
|
||||
}
|
||||
timer.Stop()
|
||||
}
|
||||
|
||||
// TestWsAuthSubmitOrders dials websocket, sends login request.
|
||||
func TestWsAuthSubmitOrders(t *testing.T) {
|
||||
// TestWsAuthSubmitOrder dials websocket, submit order
|
||||
func TestWsAuthSubmitOrder(t *testing.T) {
|
||||
setupWSTestAuth(t)
|
||||
if !canManipulateRealOrders {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
order := WsSubmitOrderParameters{
|
||||
Amount: 1,
|
||||
Currency: currency.NewPair(currency.LTC, currency.BTC),
|
||||
@@ -477,42 +466,64 @@ func TestWsAuthSubmitOrders(t *testing.T) {
|
||||
Price: 1,
|
||||
Side: exchange.BuyOrderSide,
|
||||
}
|
||||
err := c.wsSubmitOrders([]WsSubmitOrderParameters{order, order})
|
||||
_, err := c.wsSubmitOrder(&order)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
timer := time.NewTimer(sharedtestvalues.WebsocketResponseExtendedTimeout)
|
||||
select {
|
||||
case <-c.Websocket.DataHandler:
|
||||
case <-timer.C:
|
||||
t.Error("Expected response")
|
||||
}
|
||||
timer.Stop()
|
||||
}
|
||||
|
||||
// TestWsAuthCancelOrders dials websocket, sends login request.
|
||||
// TestWsAuthCancelOrders dials websocket, submit orders
|
||||
func TestWsAuthSubmitOrders(t *testing.T) {
|
||||
setupWSTestAuth(t)
|
||||
if !canManipulateRealOrders {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
order1 := WsSubmitOrderParameters{
|
||||
Amount: 1,
|
||||
Currency: currency.NewPair(currency.LTC, currency.BTC),
|
||||
OrderID: 1,
|
||||
Price: 1,
|
||||
Side: exchange.BuyOrderSide,
|
||||
}
|
||||
order2 := WsSubmitOrderParameters{
|
||||
Amount: 3,
|
||||
Currency: currency.NewPair(currency.LTC, currency.BTC),
|
||||
OrderID: 2,
|
||||
Price: 2,
|
||||
Side: exchange.BuyOrderSide,
|
||||
}
|
||||
_, err := c.wsSubmitOrders([]WsSubmitOrderParameters{order1, order2})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestWsAuthCancelOrders dials websocket, cancels orders
|
||||
func TestWsAuthCancelOrders(t *testing.T) {
|
||||
setupWSTestAuth(t)
|
||||
if !canManipulateRealOrders {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
order := WsCancelOrderParameters{
|
||||
Currency: currency.NewPair(currency.LTC, currency.BTC),
|
||||
OrderID: 1,
|
||||
}
|
||||
err := c.wsCancelOrders([]WsCancelOrderParameters{order, order})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
order2 := WsCancelOrderParameters{
|
||||
Currency: currency.NewPair(currency.LTC, currency.BTC),
|
||||
OrderID: 2,
|
||||
}
|
||||
timer := time.NewTimer(sharedtestvalues.WebsocketResponseExtendedTimeout)
|
||||
select {
|
||||
case <-c.Websocket.DataHandler:
|
||||
case <-timer.C:
|
||||
t.Error("Expected response")
|
||||
_, errs := c.wsCancelOrders([]WsCancelOrderParameters{order, order2})
|
||||
if len(errs) > 0 {
|
||||
t.Error(errs)
|
||||
}
|
||||
timer.Stop()
|
||||
}
|
||||
|
||||
// TestWsAuthCancelOrder dials websocket, sends login request.
|
||||
// TestWsAuthCancelOrder dials websocket, cancels order
|
||||
func TestWsAuthCancelOrder(t *testing.T) {
|
||||
setupWSTestAuth(t)
|
||||
if !canManipulateRealOrders {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
order := WsCancelOrderParameters{
|
||||
Currency: currency.NewPair(currency.LTC, currency.BTC),
|
||||
OrderID: 1,
|
||||
@@ -521,27 +532,13 @@ func TestWsAuthCancelOrder(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
timer := time.NewTimer(sharedtestvalues.WebsocketResponseExtendedTimeout)
|
||||
select {
|
||||
case <-c.Websocket.DataHandler:
|
||||
case <-timer.C:
|
||||
t.Error("Expected response")
|
||||
}
|
||||
timer.Stop()
|
||||
}
|
||||
|
||||
// TestWsAuthGetOpenOrders dials websocket, sends login request.
|
||||
// TestWsAuthGetOpenOrders dials websocket, retrieves open orders
|
||||
func TestWsAuthGetOpenOrders(t *testing.T) {
|
||||
setupWSTestAuth(t)
|
||||
err := c.wsGetOpenOrders(currency.NewPair(currency.LTC, currency.BTC))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
timer := time.NewTimer(sharedtestvalues.WebsocketResponseExtendedTimeout)
|
||||
select {
|
||||
case <-c.Websocket.DataHandler:
|
||||
case <-timer.C:
|
||||
t.Error("Expected response")
|
||||
}
|
||||
timer.Stop()
|
||||
}
|
||||
|
||||
@@ -263,11 +263,12 @@ type wsRequest struct {
|
||||
SecType string `json:"sec_type,omitempty"`
|
||||
InstID int64 `json:"inst_id,omitempty"`
|
||||
TopN int64 `json:"top_n,omitempty"`
|
||||
Subscribe bool `json:"subscribe"`
|
||||
Nonce int64 `json:"nonce"`
|
||||
Subscribe bool `json:"subscribe,omitempty"`
|
||||
Nonce int64 `json:"nonce,omitempty"`
|
||||
}
|
||||
|
||||
type wsResponse struct {
|
||||
Nonce int64 `json:"nonce,omitempty"`
|
||||
Reply string `json:"reply"`
|
||||
}
|
||||
|
||||
@@ -400,14 +401,14 @@ type WsCancelOrderParameters struct {
|
||||
OrderID int64
|
||||
}
|
||||
|
||||
// WsCancelOrderRequest ws request
|
||||
// WsCancelOrderRequest data required for cancelling an order
|
||||
type WsCancelOrderRequest struct {
|
||||
InstID int64 `json:"inst_id"`
|
||||
OrderID int64 `json:"order_id"`
|
||||
WsRequest
|
||||
}
|
||||
|
||||
// WsCancelOrderResponse ws response
|
||||
// WsCancelOrderResponse contains cancelled order data
|
||||
type WsCancelOrderResponse struct {
|
||||
Nonce int64 `json:"nonce"`
|
||||
Reply string `json:"reply"`
|
||||
@@ -416,16 +417,20 @@ type WsCancelOrderResponse struct {
|
||||
Status []string `json:"status"`
|
||||
}
|
||||
|
||||
// WsCancelOrdersResponse ws response
|
||||
// WsCancelOrdersResponse contains all cancelled order data
|
||||
type WsCancelOrdersResponse struct {
|
||||
WsRequest
|
||||
Entries []WsCancelOrdersResponseEntry `json:"entries"`
|
||||
Nonce int64 `json:"nonce"`
|
||||
Reply string `json:"reply"`
|
||||
Results []WsCancelOrdersResponseData `json:"results"`
|
||||
Status []string `json:"status"`
|
||||
TransID int64 `json:"trans_id"`
|
||||
}
|
||||
|
||||
// WsCancelOrdersResponseEntry ws response entry
|
||||
type WsCancelOrdersResponseEntry struct {
|
||||
InstID int64 `json:"inst_id"`
|
||||
OrderID int64 `json:"order_id"`
|
||||
// WsCancelOrdersResponseData individual cancellation response data
|
||||
type WsCancelOrdersResponseData struct {
|
||||
InstID int64 `json:"inst_id"`
|
||||
OrderID int64 `json:"order_id"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// WsGetOpenOrdersRequest ws request
|
||||
@@ -547,6 +552,25 @@ type WsOrderRejectedResponse struct {
|
||||
TransID int64 `json:"trans_id"`
|
||||
}
|
||||
|
||||
// WsStandardOrderResponse a standardised order
|
||||
type WsStandardOrderResponse struct {
|
||||
InstID int64
|
||||
OrderID int64
|
||||
ClientOrdID int64
|
||||
TransID int64
|
||||
Nonce int64
|
||||
Status []string
|
||||
Qty float64
|
||||
OpenQty float64
|
||||
Price float64
|
||||
Side string
|
||||
Reasons []string
|
||||
Timestamp int64
|
||||
OrderType string
|
||||
CommissionAmount float64
|
||||
CommissionCurrency currency.Pair
|
||||
}
|
||||
|
||||
// WsUserOpenOrdersResponse ws response
|
||||
type WsUserOpenOrdersResponse struct {
|
||||
Nonce int64 `json:"nonce"`
|
||||
@@ -612,3 +636,25 @@ type WsNewOrderResponse struct {
|
||||
Reply string `json:"reply"`
|
||||
Status []string `json:"status"`
|
||||
}
|
||||
|
||||
// WsGetAccountBalanceResponse contains values of each currency
|
||||
type WsGetAccountBalanceResponse struct {
|
||||
BCH string `json:"BCH"`
|
||||
BTC string `json:"BTC"`
|
||||
BTG string `json:"BTG"`
|
||||
CAD string `json:"CAD"`
|
||||
ETC string `json:"ETC"`
|
||||
ETH string `json:"ETH"`
|
||||
LCH string `json:"LCH"`
|
||||
LTC string `json:"LTC"`
|
||||
MYR string `json:"MYR"`
|
||||
SGD string `json:"SGD"`
|
||||
USD string `json:"USD"`
|
||||
USDT string `json:"USDT"`
|
||||
XMR string `json:"XMR"`
|
||||
ZEC string `json:"ZEC"`
|
||||
Nonce int64 `json:"nonce"`
|
||||
Reply string `json:"reply"`
|
||||
Status []string `json:"status"`
|
||||
TransID int64 `json:"trans_id"`
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -13,11 +12,11 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
log "github.com/thrasher-/gocryptotrader/logger"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/wshandler"
|
||||
)
|
||||
|
||||
const coinutWebsocketURL = "wss://wsapi.coinut.com"
|
||||
const coinutWebsocketRateLimit = 30 * time.Millisecond
|
||||
const coinutWebsocketRateLimit = 30
|
||||
|
||||
var nNonce map[int64]string
|
||||
var channels map[string]chan []byte
|
||||
@@ -33,32 +32,18 @@ var populatedList bool
|
||||
// WsConnect intiates a websocket connection
|
||||
func (c *COINUT) WsConnect() error {
|
||||
if !c.Websocket.IsEnabled() || !c.IsEnabled() {
|
||||
return errors.New(exchange.WebsocketNotEnabled)
|
||||
return errors.New(wshandler.WebsocketNotEnabled)
|
||||
}
|
||||
|
||||
var Dialer websocket.Dialer
|
||||
|
||||
if c.Websocket.GetProxyAddress() != "" {
|
||||
proxy, err := url.Parse(c.Websocket.GetProxyAddress())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Dialer.Proxy = http.ProxyURL(proxy)
|
||||
}
|
||||
|
||||
var err error
|
||||
c.WebsocketConn, _, err = Dialer.Dial(c.Websocket.GetWebsocketURL(),
|
||||
http.Header{})
|
||||
|
||||
var dialer websocket.Dialer
|
||||
err := c.WebsocketConn.Dial(&dialer, http.Header{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go c.WsHandleData()
|
||||
|
||||
if !populatedList {
|
||||
instrumentListByString = make(map[string]int64)
|
||||
instrumentListByCode = make(map[int64]string)
|
||||
|
||||
err = c.WsSetInstrumentList()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -72,21 +57,9 @@ func (c *COINUT) WsConnect() error {
|
||||
channels = make(map[string]chan []byte)
|
||||
channels["hb"] = make(chan []byte, 1)
|
||||
|
||||
go c.WsHandleData()
|
||||
return nil
|
||||
}
|
||||
|
||||
// WsReadData reads data from the websocket connection
|
||||
func (c *COINUT) WsReadData() (exchange.WebsocketResponse, error) {
|
||||
_, resp, err := c.WebsocketConn.ReadMessage()
|
||||
if err != nil {
|
||||
return exchange.WebsocketResponse{}, err
|
||||
}
|
||||
|
||||
c.Websocket.TrafficAlert <- struct{}{}
|
||||
return exchange.WebsocketResponse{Raw: resp}, nil
|
||||
}
|
||||
|
||||
// WsHandleData handles read data
|
||||
func (c *COINUT) WsHandleData() {
|
||||
c.Websocket.Wg.Add(1)
|
||||
@@ -101,11 +74,12 @@ func (c *COINUT) WsHandleData() {
|
||||
return
|
||||
|
||||
default:
|
||||
resp, err := c.WsReadData()
|
||||
resp, err := c.WebsocketConn.ReadMessage()
|
||||
if err != nil {
|
||||
c.Websocket.DataHandler <- err
|
||||
return
|
||||
}
|
||||
c.Websocket.TrafficAlert <- struct{}{}
|
||||
|
||||
if strings.HasPrefix(string(resp.Raw), "[") {
|
||||
var incoming []wsResponse
|
||||
@@ -115,6 +89,10 @@ func (c *COINUT) WsHandleData() {
|
||||
continue
|
||||
}
|
||||
for i := range incoming {
|
||||
if incoming[i].Nonce > 0 {
|
||||
c.WebsocketConn.AddResponseWithID(incoming[i].Nonce, resp.Raw)
|
||||
break
|
||||
}
|
||||
var individualJSON []byte
|
||||
individualJSON, err = common.JSONEncode(incoming[i])
|
||||
if err != nil {
|
||||
@@ -131,6 +109,7 @@ func (c *COINUT) WsHandleData() {
|
||||
c.Websocket.DataHandler <- err
|
||||
continue
|
||||
}
|
||||
|
||||
c.wsProcessResponse(resp.Raw)
|
||||
}
|
||||
|
||||
@@ -146,15 +125,6 @@ func (c *COINUT) wsProcessResponse(resp []byte) {
|
||||
return
|
||||
}
|
||||
switch incoming.Reply {
|
||||
case "login":
|
||||
var login WsLoginResponse
|
||||
err := common.JSONDecode(resp, &login)
|
||||
if err != nil {
|
||||
c.Websocket.DataHandler <- err
|
||||
return
|
||||
}
|
||||
c.Websocket.SetCanUseAuthenticatedEndpoints(login.Username == c.ClientID)
|
||||
c.Websocket.DataHandler <- login
|
||||
case "hb":
|
||||
channels["hb"] <- resp
|
||||
case "inst_tick":
|
||||
@@ -164,7 +134,7 @@ func (c *COINUT) wsProcessResponse(resp []byte) {
|
||||
c.Websocket.DataHandler <- err
|
||||
return
|
||||
}
|
||||
c.Websocket.DataHandler <- exchange.TickerData{
|
||||
c.Websocket.DataHandler <- wshandler.TickerData{
|
||||
Timestamp: time.Unix(0, ticker.Timestamp),
|
||||
Exchange: c.GetName(),
|
||||
AssetType: "SPOT",
|
||||
@@ -187,7 +157,7 @@ func (c *COINUT) wsProcessResponse(resp []byte) {
|
||||
return
|
||||
}
|
||||
currencyPair := instrumentListByCode[orderbooksnapshot.InstID]
|
||||
c.Websocket.DataHandler <- exchange.WebsocketOrderbookUpdate{
|
||||
c.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{
|
||||
Exchange: c.GetName(),
|
||||
Asset: "SPOT",
|
||||
Pair: currency.NewPairFromString(currencyPair),
|
||||
@@ -205,7 +175,7 @@ func (c *COINUT) wsProcessResponse(resp []byte) {
|
||||
return
|
||||
}
|
||||
currencyPair := instrumentListByCode[orderbookUpdate.InstID]
|
||||
c.Websocket.DataHandler <- exchange.WebsocketOrderbookUpdate{
|
||||
c.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{
|
||||
Exchange: c.GetName(),
|
||||
Asset: "SPOT",
|
||||
Pair: currency.NewPairFromString(currencyPair),
|
||||
@@ -226,7 +196,7 @@ func (c *COINUT) wsProcessResponse(resp []byte) {
|
||||
return
|
||||
}
|
||||
currencyPair := instrumentListByCode[tradeUpdate.InstID]
|
||||
c.Websocket.DataHandler <- exchange.TradeData{
|
||||
c.Websocket.DataHandler <- wshandler.TradeData{
|
||||
Timestamp: time.Unix(tradeUpdate.Timestamp, 0),
|
||||
CurrencyPair: currency.NewPairFromString(currencyPair),
|
||||
AssetType: "SPOT",
|
||||
@@ -234,78 +204,12 @@ func (c *COINUT) wsProcessResponse(resp []byte) {
|
||||
Price: tradeUpdate.Price,
|
||||
Side: tradeUpdate.Side,
|
||||
}
|
||||
case "user_balance":
|
||||
var userBalance WsUserBalanceResponse
|
||||
err := common.JSONDecode(resp, &userBalance)
|
||||
if err != nil {
|
||||
c.Websocket.DataHandler <- err
|
||||
default:
|
||||
if incoming.Nonce > 0 {
|
||||
c.WebsocketConn.AddResponseWithID(incoming.Nonce, resp)
|
||||
return
|
||||
}
|
||||
c.Websocket.DataHandler <- userBalance
|
||||
case "new_order":
|
||||
var newOrder WsNewOrderResponse
|
||||
err := common.JSONDecode(resp, &newOrder)
|
||||
if err != nil {
|
||||
c.Websocket.DataHandler <- err
|
||||
return
|
||||
}
|
||||
c.Websocket.DataHandler <- newOrder
|
||||
case "order_accepted":
|
||||
var orderAccepted WsOrderAcceptedResponse
|
||||
err := common.JSONDecode(resp, &orderAccepted)
|
||||
if err != nil {
|
||||
c.Websocket.DataHandler <- err
|
||||
return
|
||||
}
|
||||
c.Websocket.DataHandler <- orderAccepted
|
||||
case "order_filled":
|
||||
var orderFilled WsOrderFilledResponse
|
||||
err := common.JSONDecode(resp, &orderFilled)
|
||||
if err != nil {
|
||||
c.Websocket.DataHandler <- err
|
||||
return
|
||||
}
|
||||
c.Websocket.DataHandler <- orderFilled
|
||||
case "order_rejected":
|
||||
var orderRejected WsOrderRejectedResponse
|
||||
err := common.JSONDecode(resp, &orderRejected)
|
||||
if err != nil {
|
||||
c.Websocket.DataHandler <- err
|
||||
return
|
||||
}
|
||||
c.Websocket.DataHandler <- orderRejected
|
||||
case "user_open_orders":
|
||||
var openOrders WsUserOpenOrdersResponse
|
||||
err := common.JSONDecode(resp, &openOrders)
|
||||
if err != nil {
|
||||
c.Websocket.DataHandler <- err
|
||||
return
|
||||
}
|
||||
c.Websocket.DataHandler <- openOrders
|
||||
case "trade_history":
|
||||
var tradeHistory WsTradeHistoryResponse
|
||||
err := common.JSONDecode(resp, &tradeHistory)
|
||||
if err != nil {
|
||||
c.Websocket.DataHandler <- err
|
||||
return
|
||||
}
|
||||
c.Websocket.DataHandler <- tradeHistory
|
||||
case "cancel_orders":
|
||||
var cancelOrders WsCancelOrdersResponse
|
||||
err := common.JSONDecode(resp, &cancelOrders)
|
||||
if err != nil {
|
||||
c.Websocket.DataHandler <- err
|
||||
return
|
||||
}
|
||||
c.Websocket.DataHandler <- cancelOrders
|
||||
case "cancel_order":
|
||||
var cancelOrder WsCancelOrderResponse
|
||||
err := common.JSONDecode(resp, &cancelOrder)
|
||||
if err != nil {
|
||||
c.Websocket.DataHandler <- err
|
||||
return
|
||||
}
|
||||
c.Websocket.DataHandler <- cancelOrder
|
||||
c.Websocket.DataHandler <- fmt.Errorf("%v unhandled websocket response: %s", c.Name, resp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,37 +226,27 @@ func (c *COINUT) GetNonce() int64 {
|
||||
|
||||
// WsSetInstrumentList fetches instrument list and propagates a local cache
|
||||
func (c *COINUT) WsSetInstrumentList() error {
|
||||
err := c.wsSend(wsRequest{
|
||||
request := wsRequest{
|
||||
Request: "inst_list",
|
||||
SecType: "SPOT",
|
||||
Nonce: c.GetNonce(),
|
||||
})
|
||||
Nonce: c.WebsocketConn.GenerateMessageID(false),
|
||||
}
|
||||
resp, err := c.WebsocketConn.SendMessageReturnResponse(request.Nonce, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, resp, err := c.WebsocketConn.ReadMessage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Websocket.TrafficAlert <- struct{}{}
|
||||
|
||||
var list WsInstrumentList
|
||||
err = common.JSONDecode(resp, &list)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for currency, data := range list.Spot {
|
||||
instrumentListByString[currency] = data[0].InstID
|
||||
instrumentListByCode[data[0].InstID] = currency
|
||||
}
|
||||
|
||||
if len(instrumentListByString) == 0 || len(instrumentListByCode) == 0 {
|
||||
return errors.New("instrument lists failed to populate")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -409,11 +303,11 @@ func (c *COINUT) WsProcessOrderbookUpdate(ob *WsOrderbookUpdate) error {
|
||||
// GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions()
|
||||
func (c *COINUT) GenerateDefaultSubscriptions() {
|
||||
var channels = []string{"inst_tick", "inst_order_book"}
|
||||
var subscriptions []exchange.WebsocketChannelSubscription
|
||||
var subscriptions []wshandler.WebsocketChannelSubscription
|
||||
enabledCurrencies := c.GetEnabledCurrencies()
|
||||
for i := range channels {
|
||||
for j := range enabledCurrencies {
|
||||
subscriptions = append(subscriptions, exchange.WebsocketChannelSubscription{
|
||||
subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{
|
||||
Channel: channels[i],
|
||||
Currency: enabledCurrencies[j],
|
||||
})
|
||||
@@ -423,42 +317,37 @@ func (c *COINUT) GenerateDefaultSubscriptions() {
|
||||
}
|
||||
|
||||
// Subscribe sends a websocket message to receive data from the channel
|
||||
func (c *COINUT) Subscribe(channelToSubscribe exchange.WebsocketChannelSubscription) error {
|
||||
func (c *COINUT) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error {
|
||||
subscribe := wsRequest{
|
||||
Request: channelToSubscribe.Channel,
|
||||
InstID: instrumentListByString[channelToSubscribe.Currency.String()],
|
||||
Subscribe: true,
|
||||
Nonce: c.GetNonce(),
|
||||
Nonce: c.WebsocketConn.GenerateMessageID(false),
|
||||
}
|
||||
return c.wsSend(subscribe)
|
||||
return c.WebsocketConn.SendMessage(subscribe)
|
||||
}
|
||||
|
||||
// Unsubscribe sends a websocket message to stop receiving data from the channel
|
||||
func (c *COINUT) Unsubscribe(channelToSubscribe exchange.WebsocketChannelSubscription) error {
|
||||
func (c *COINUT) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error {
|
||||
subscribe := wsRequest{
|
||||
Request: channelToSubscribe.Channel,
|
||||
InstID: instrumentListByString[channelToSubscribe.Currency.String()],
|
||||
Subscribe: false,
|
||||
Nonce: c.GetNonce(),
|
||||
Nonce: c.WebsocketConn.GenerateMessageID(false),
|
||||
}
|
||||
return c.wsSend(subscribe)
|
||||
}
|
||||
|
||||
// WsSend sends data to the websocket server
|
||||
func (c *COINUT) wsSend(data interface{}) error {
|
||||
c.wsRequestMtx.Lock()
|
||||
defer c.wsRequestMtx.Unlock()
|
||||
|
||||
json, err := common.JSONEncode(data)
|
||||
resp, err := c.WebsocketConn.SendMessageReturnResponse(subscribe.Nonce, subscribe)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.Verbose {
|
||||
log.Debugf("%v sending message to websocket %v", c.Name, string(json))
|
||||
var response map[string]interface{}
|
||||
err = common.JSONDecode(resp, &response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Basic rate limiter
|
||||
time.Sleep(coinutWebsocketRateLimit)
|
||||
return c.WebsocketConn.WriteMessage(websocket.TextMessage, json)
|
||||
if response["status"].([]interface{})[0] != "OK" {
|
||||
return fmt.Errorf("%v unsubscribe failed for channel %v", c.Name, channelToSubscribe.Channel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *COINUT) wsAuthenticate() error {
|
||||
@@ -466,7 +355,7 @@ func (c *COINUT) wsAuthenticate() error {
|
||||
return fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", c.Name)
|
||||
}
|
||||
timestamp := time.Now().Unix()
|
||||
nonce := c.GetNonce()
|
||||
nonce := c.WebsocketConn.GenerateMessageID(false)
|
||||
payload := fmt.Sprintf("%v|%v|%v", c.ClientID, timestamp, nonce)
|
||||
hmac := common.GetHMAC(common.HashSHA256, []byte(payload), []byte(c.APIKey))
|
||||
loginRequest := struct {
|
||||
@@ -483,34 +372,54 @@ func (c *COINUT) wsAuthenticate() error {
|
||||
Timestamp: timestamp,
|
||||
}
|
||||
|
||||
err := c.wsSend(loginRequest)
|
||||
resp, err := c.WebsocketConn.SendMessageReturnResponse(loginRequest.Nonce, loginRequest)
|
||||
if err != nil {
|
||||
c.Websocket.SetCanUseAuthenticatedEndpoints(false)
|
||||
return err
|
||||
}
|
||||
var response map[string]interface{}
|
||||
err = common.JSONDecode(resp, &response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if response["status"].([]interface{})[0] != "OK" {
|
||||
c.Websocket.SetCanUseAuthenticatedEndpoints(false)
|
||||
return fmt.Errorf("%v failed to authenticate", c.Name)
|
||||
}
|
||||
c.Websocket.SetCanUseAuthenticatedEndpoints(true)
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (c *COINUT) wsGetAccountBalance() error {
|
||||
func (c *COINUT) wsGetAccountBalance() (*WsGetAccountBalanceResponse, error) {
|
||||
if !c.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
return fmt.Errorf("%v not authorised to submit order", c.Name)
|
||||
return nil, fmt.Errorf("%v not authorised to submit order", c.Name)
|
||||
}
|
||||
accBalance := wsRequest{
|
||||
Request: "user_balance",
|
||||
Nonce: c.GetNonce(),
|
||||
Nonce: c.WebsocketConn.GenerateMessageID(false),
|
||||
}
|
||||
return c.wsSend(accBalance)
|
||||
resp, err := c.WebsocketConn.SendMessageReturnResponse(accBalance.Nonce, accBalance)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var response WsGetAccountBalanceResponse
|
||||
err = common.JSONDecode(resp, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if response.Status[0] != "OK" {
|
||||
return &response, fmt.Errorf("%v get account balance failed", c.Name)
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func (c *COINUT) wsSubmitOrder(order *WsSubmitOrderParameters) error {
|
||||
func (c *COINUT) wsSubmitOrder(order *WsSubmitOrderParameters) (*WsStandardOrderResponse, error) {
|
||||
if !c.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
return fmt.Errorf("%v not authorised to submit order", c.Name)
|
||||
return nil, fmt.Errorf("%v not authorised to submit order", c.Name)
|
||||
}
|
||||
currency := exchange.FormatExchangeCurrency(c.Name, order.Currency).String()
|
||||
var orderSubmissionRequest WsSubmitOrderRequest
|
||||
orderSubmissionRequest.Request = "new_order"
|
||||
orderSubmissionRequest.Nonce = c.GetNonce()
|
||||
orderSubmissionRequest.Nonce = c.WebsocketConn.GenerateMessageID(false)
|
||||
orderSubmissionRequest.InstID = instrumentListByString[currency]
|
||||
orderSubmissionRequest.Qty = order.Amount
|
||||
orderSubmissionRequest.Price = order.Price
|
||||
@@ -519,12 +428,100 @@ func (c *COINUT) wsSubmitOrder(order *WsSubmitOrderParameters) error {
|
||||
if order.OrderID > 0 {
|
||||
orderSubmissionRequest.OrderID = order.OrderID
|
||||
}
|
||||
return c.wsSend(orderSubmissionRequest)
|
||||
resp, err := c.WebsocketConn.SendMessageReturnResponse(orderSubmissionRequest.Nonce, orderSubmissionRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var standardOrder WsStandardOrderResponse
|
||||
standardOrder, err = c.wsStandardiseOrderResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if standardOrder.Status[0] != "OK" {
|
||||
return &standardOrder, fmt.Errorf("%v order submission failed. %v", c.Name, standardOrder)
|
||||
}
|
||||
if len(standardOrder.Reasons) > 0 && standardOrder.Reasons[0] != "" {
|
||||
return &standardOrder, fmt.Errorf("%v order submission failed. %v", c.Name, standardOrder.Reasons[0])
|
||||
}
|
||||
return &standardOrder, nil
|
||||
}
|
||||
|
||||
func (c *COINUT) wsSubmitOrders(orders []WsSubmitOrderParameters) error {
|
||||
func (c *COINUT) wsStandardiseOrderResponse(resp []byte) (WsStandardOrderResponse, error) {
|
||||
var response WsStandardOrderResponse
|
||||
var incoming wsResponse
|
||||
err := common.JSONDecode(resp, &incoming)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
switch incoming.Reply {
|
||||
case "order_accepted":
|
||||
var orderAccepted WsOrderAcceptedResponse
|
||||
err := common.JSONDecode(resp, &orderAccepted)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
response = WsStandardOrderResponse{
|
||||
InstID: orderAccepted.InstID,
|
||||
Nonce: orderAccepted.Nonce,
|
||||
OpenQty: orderAccepted.OpenQty,
|
||||
OrderID: orderAccepted.OrderID,
|
||||
OrderType: orderAccepted.Reply,
|
||||
Price: orderAccepted.OrderPrice,
|
||||
Qty: orderAccepted.Qty,
|
||||
Side: orderAccepted.Side,
|
||||
Status: orderAccepted.Status,
|
||||
TransID: orderAccepted.TransID,
|
||||
ClientOrdID: orderAccepted.ClientOrdID,
|
||||
}
|
||||
case "order_filled":
|
||||
var orderFilled WsOrderFilledResponse
|
||||
err := common.JSONDecode(resp, &orderFilled)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
response = WsStandardOrderResponse{
|
||||
InstID: orderFilled.Order.InstID,
|
||||
Nonce: orderFilled.Nonce,
|
||||
OpenQty: orderFilled.Order.OpenQty,
|
||||
OrderID: orderFilled.Order.OrderID,
|
||||
OrderType: orderFilled.Reply,
|
||||
Price: orderFilled.Order.Price,
|
||||
Qty: orderFilled.Order.Qty,
|
||||
Side: orderFilled.Order.Side,
|
||||
Status: orderFilled.Status,
|
||||
TransID: orderFilled.TransID,
|
||||
ClientOrdID: orderFilled.Order.ClientOrdID,
|
||||
}
|
||||
case "order_rejected":
|
||||
var orderRejected WsOrderRejectedResponse
|
||||
err := common.JSONDecode(resp, &orderRejected)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
response = WsStandardOrderResponse{
|
||||
InstID: orderRejected.InstID,
|
||||
Nonce: orderRejected.Nonce,
|
||||
OpenQty: orderRejected.OpenQty,
|
||||
OrderID: orderRejected.OrderID,
|
||||
OrderType: orderRejected.Reply,
|
||||
Price: orderRejected.Price,
|
||||
Qty: orderRejected.Qty,
|
||||
Side: orderRejected.Side,
|
||||
Status: orderRejected.Status,
|
||||
TransID: orderRejected.TransID,
|
||||
ClientOrdID: orderRejected.ClientOrdID,
|
||||
Reasons: orderRejected.Reasons,
|
||||
}
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *COINUT) wsSubmitOrders(orders []WsSubmitOrderParameters) ([]WsStandardOrderResponse, []error) {
|
||||
var errors []error
|
||||
var ordersResponse []WsStandardOrderResponse
|
||||
if !c.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
return fmt.Errorf("%v not authorised to submit orders", c.Name)
|
||||
errors = append(errors, fmt.Errorf("%v not authorised to submit orders", c.Name))
|
||||
return nil, errors
|
||||
}
|
||||
orderRequest := WsSubmitOrdersRequest{}
|
||||
for i := range orders {
|
||||
@@ -539,9 +536,48 @@ func (c *COINUT) wsSubmitOrders(orders []WsSubmitOrderParameters) error {
|
||||
})
|
||||
}
|
||||
|
||||
orderRequest.Nonce = c.GetNonce()
|
||||
orderRequest.Nonce = c.WebsocketConn.GenerateMessageID(false)
|
||||
orderRequest.Request = "new_orders"
|
||||
return c.wsSend(orderRequest)
|
||||
resp, err := c.WebsocketConn.SendMessageReturnResponse(orderRequest.Nonce, orderRequest)
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
return nil, errors
|
||||
}
|
||||
var incoming []interface{}
|
||||
err = common.JSONDecode(resp, &incoming)
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
return nil, errors
|
||||
}
|
||||
for i := range incoming {
|
||||
var individualJSON []byte
|
||||
individualJSON, err = common.JSONEncode(incoming[i])
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
continue
|
||||
}
|
||||
standardOrder, err := c.wsStandardiseOrderResponse(individualJSON)
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
continue
|
||||
}
|
||||
if standardOrder.Status[0] != "OK" {
|
||||
errors = append(errors, fmt.Errorf("%v order submission failed. %v", c.Name, standardOrder))
|
||||
continue
|
||||
}
|
||||
if len(standardOrder.Reasons) > 0 && standardOrder.Reasons[0] != "" {
|
||||
errors = append(errors, fmt.Errorf("%v order submission failed for currency %v and orderID %v, message %v ",
|
||||
c.Name,
|
||||
instrumentListByCode[standardOrder.InstID],
|
||||
standardOrder.OrderID,
|
||||
standardOrder.Reasons[0]))
|
||||
|
||||
continue
|
||||
}
|
||||
ordersResponse = append(ordersResponse, standardOrder)
|
||||
}
|
||||
|
||||
return ordersResponse, errors
|
||||
}
|
||||
|
||||
func (c *COINUT) wsGetOpenOrders(p currency.Pair) error {
|
||||
@@ -551,10 +587,24 @@ func (c *COINUT) wsGetOpenOrders(p currency.Pair) error {
|
||||
currency := exchange.FormatExchangeCurrency(c.Name, p).String()
|
||||
var openOrdersRequest WsGetOpenOrdersRequest
|
||||
openOrdersRequest.Request = "user_open_orders"
|
||||
openOrdersRequest.Nonce = c.GetNonce()
|
||||
openOrdersRequest.Nonce = c.WebsocketConn.GenerateMessageID(false)
|
||||
openOrdersRequest.InstID = instrumentListByString[currency]
|
||||
|
||||
return c.wsSend(openOrdersRequest)
|
||||
resp, err := c.WebsocketConn.SendMessageReturnResponse(openOrdersRequest.Nonce, openOrdersRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var response map[string]interface{}
|
||||
err = common.JSONDecode(resp, &response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if response["status"].([]interface{})[0] != "OK" {
|
||||
return fmt.Errorf("%v get open orders failed for currency %v",
|
||||
c.Name,
|
||||
p)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *COINUT) wsCancelOrder(cancellation WsCancelOrderParameters) error {
|
||||
@@ -566,14 +616,31 @@ func (c *COINUT) wsCancelOrder(cancellation WsCancelOrderParameters) error {
|
||||
cancellationRequest.Request = "cancel_order"
|
||||
cancellationRequest.InstID = instrumentListByString[currency]
|
||||
cancellationRequest.OrderID = cancellation.OrderID
|
||||
cancellationRequest.Nonce = c.GetNonce()
|
||||
cancellationRequest.Nonce = c.WebsocketConn.GenerateMessageID(false)
|
||||
|
||||
return c.wsSend(cancellationRequest)
|
||||
resp, err := c.WebsocketConn.SendMessageReturnResponse(cancellationRequest.Nonce, cancellationRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var response map[string]interface{}
|
||||
err = common.JSONDecode(resp, &response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if response["status"].([]interface{})[0] != "OK" {
|
||||
return fmt.Errorf("%v order cancellation failed for currency %v and orderID %v, message %v",
|
||||
c.Name,
|
||||
cancellation.Currency,
|
||||
cancellation.OrderID,
|
||||
response["status"])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *COINUT) wsCancelOrders(cancellations []WsCancelOrderParameters) error {
|
||||
func (c *COINUT) wsCancelOrders(cancellations []WsCancelOrderParameters) (*WsCancelOrdersResponse, []error) {
|
||||
var errors []error
|
||||
if !c.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
return fmt.Errorf("%v not authorised to cancel orders", c.Name)
|
||||
return nil, errors
|
||||
}
|
||||
cancelOrderRequest := WsCancelOrdersRequest{}
|
||||
for i := range cancellations {
|
||||
@@ -585,8 +652,29 @@ func (c *COINUT) wsCancelOrders(cancellations []WsCancelOrderParameters) error {
|
||||
}
|
||||
|
||||
cancelOrderRequest.Request = "cancel_orders"
|
||||
cancelOrderRequest.Nonce = c.GetNonce()
|
||||
return c.wsSend(cancelOrderRequest)
|
||||
cancelOrderRequest.Nonce = c.WebsocketConn.GenerateMessageID(false)
|
||||
resp, err := c.WebsocketConn.SendMessageReturnResponse(cancelOrderRequest.Nonce, cancelOrderRequest)
|
||||
if err != nil {
|
||||
return nil, []error{err}
|
||||
}
|
||||
var response WsCancelOrdersResponse
|
||||
err = common.JSONDecode(resp, &response)
|
||||
if err != nil {
|
||||
return nil, []error{err}
|
||||
}
|
||||
if response.Status[0] != "OK" {
|
||||
return &response, []error{err}
|
||||
}
|
||||
for i := range response.Results {
|
||||
if response.Results[i].Status != "OK" {
|
||||
errors = append(errors, fmt.Errorf("%v order cancellation failed for currency %v and orderID %v, message %v",
|
||||
c.Name,
|
||||
instrumentListByCode[response.Results[i].InstID],
|
||||
response.Results[i].OrderID,
|
||||
response.Results[i].Status))
|
||||
}
|
||||
}
|
||||
return &response, errors
|
||||
}
|
||||
|
||||
func (c *COINUT) wsGetTradeHistory(p currency.Pair, start, limit int64) error {
|
||||
@@ -597,9 +685,23 @@ func (c *COINUT) wsGetTradeHistory(p currency.Pair, start, limit int64) error {
|
||||
var request WsTradeHistoryRequest
|
||||
request.Request = "trade_history"
|
||||
request.InstID = instrumentListByString[currency]
|
||||
request.Nonce = c.GetNonce()
|
||||
request.Nonce = c.WebsocketConn.GenerateMessageID(false)
|
||||
request.Start = start
|
||||
request.Limit = limit
|
||||
|
||||
return c.wsSend(request)
|
||||
resp, err := c.WebsocketConn.SendMessageReturnResponse(request.Nonce, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var response map[string]interface{}
|
||||
err = common.JSONDecode(resp, &response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if response["status"].([]interface{})[0] != "OK" {
|
||||
return fmt.Errorf("%v get trade history failed for %v",
|
||||
c.Name,
|
||||
request)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/wshandler"
|
||||
log "github.com/thrasher-/gocryptotrader/logger"
|
||||
)
|
||||
|
||||
@@ -377,7 +378,7 @@ func (c *COINUT) WithdrawFiatFundsToInternationalBank(withdrawRequest *exchange.
|
||||
}
|
||||
|
||||
// GetWebsocket returns a pointer to the exchange websocket
|
||||
func (c *COINUT) GetWebsocket() (*exchange.Websocket, error) {
|
||||
func (c *COINUT) GetWebsocket() (*wshandler.Websocket, error) {
|
||||
return c.Websocket, nil
|
||||
}
|
||||
|
||||
@@ -506,20 +507,20 @@ func (c *COINUT) GetOrderHistory(getOrdersRequest *exchange.GetOrdersRequest) ([
|
||||
|
||||
// SubscribeToWebsocketChannels appends to ChannelsToSubscribe
|
||||
// which lets websocket.manageSubscriptions handle subscribing
|
||||
func (c *COINUT) SubscribeToWebsocketChannels(channels []exchange.WebsocketChannelSubscription) error {
|
||||
func (c *COINUT) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error {
|
||||
c.Websocket.SubscribeToChannels(channels)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe
|
||||
// which lets websocket.manageSubscriptions handle unsubscribing
|
||||
func (c *COINUT) UnsubscribeToWebsocketChannels(channels []exchange.WebsocketChannelSubscription) error {
|
||||
c.Websocket.UnsubscribeToChannels(channels)
|
||||
func (c *COINUT) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error {
|
||||
c.Websocket.RemoveSubscribedChannels(channels)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSubscriptions returns a copied list of subscriptions
|
||||
func (c *COINUT) GetSubscriptions() ([]exchange.WebsocketChannelSubscription, error) {
|
||||
func (c *COINUT) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) {
|
||||
return c.Websocket.GetSubscriptions(), nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user