mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-06 15:10:59 +00:00
Merge branch 'master' into engine
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
# GoCryptoTrader package Coinut
|
||||
|
||||
<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/coinut)
|
||||
[](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/coinut)
|
||||
[](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
|
||||
[](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
|
||||
|
||||
|
||||
This coinut package is part of the GoCryptoTrader codebase.
|
||||
@@ -27,7 +27,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:
|
||||
|
||||
@@ -128,12 +128,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,15 +7,14 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"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"
|
||||
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/wshandler"
|
||||
log "github.com/thrasher-corp/gocryptotrader/logger"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -46,9 +45,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
|
||||
}
|
||||
|
||||
// GetInstruments returns instruments
|
||||
|
||||
@@ -3,14 +3,14 @@ package coinut
|
||||
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"
|
||||
"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"
|
||||
)
|
||||
|
||||
var c COINUT
|
||||
@@ -54,12 +54,18 @@ func setupWSTestAuth(t *testing.T) {
|
||||
c.SetDefaults()
|
||||
TestSetup(t)
|
||||
if !c.Websocket.IsEnabled() && !c.API.AuthenticatedWebsocketSupport || !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)
|
||||
}
|
||||
@@ -71,17 +77,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
|
||||
@@ -450,28 +445,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),
|
||||
@@ -479,42 +467,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,
|
||||
@@ -523,27 +533,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()
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package coinut
|
||||
|
||||
import (
|
||||
"github.com/thrasher-/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
)
|
||||
|
||||
// GenericResponse is the generic response you will get from coinut
|
||||
@@ -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,22 +4,21 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"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"
|
||||
)
|
||||
|
||||
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
|
||||
@@ -35,32 +34,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
|
||||
@@ -74,21 +59,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)
|
||||
@@ -103,11 +76,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
|
||||
@@ -117,6 +91,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 {
|
||||
@@ -133,6 +111,7 @@ func (c *COINUT) WsHandleData() {
|
||||
c.Websocket.DataHandler <- err
|
||||
continue
|
||||
}
|
||||
|
||||
c.wsProcessResponse(resp.Raw)
|
||||
}
|
||||
|
||||
@@ -148,15 +127,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.API.Credentials.ClientID)
|
||||
c.Websocket.DataHandler <- login
|
||||
case "hb":
|
||||
channels["hb"] <- resp
|
||||
case "inst_tick":
|
||||
@@ -167,7 +137,7 @@ func (c *COINUT) wsProcessResponse(resp []byte) {
|
||||
return
|
||||
}
|
||||
currencyPair := instrumentListByCode[ticker.InstID]
|
||||
c.Websocket.DataHandler <- exchange.TickerData{
|
||||
c.Websocket.DataHandler <- wshandler.TickerData{
|
||||
Timestamp: time.Unix(0, ticker.Timestamp),
|
||||
Pair: currency.NewPairFromString(currencyPair),
|
||||
Exchange: c.GetName(),
|
||||
@@ -191,7 +161,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: asset.Spot,
|
||||
Pair: currency.NewPairFromString(currencyPair),
|
||||
@@ -209,7 +179,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: asset.Spot,
|
||||
Pair: currency.NewPairFromString(currencyPair),
|
||||
@@ -230,7 +200,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: asset.Spot,
|
||||
@@ -238,78 +208,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,37 +230,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: strings.ToUpper(asset.Spot.String()),
|
||||
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
|
||||
}
|
||||
|
||||
@@ -414,11 +308,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.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],
|
||||
})
|
||||
@@ -428,42 +322,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(log.ExchangeSys, "%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 {
|
||||
@@ -471,7 +360,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.API.Credentials.ClientID, timestamp, nonce)
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(payload), []byte(c.API.Credentials.Key))
|
||||
loginRequest := struct {
|
||||
@@ -488,34 +377,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 := c.FormatExchangeCurrency(order.Currency, asset.Spot).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
|
||||
@@ -524,12 +433,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 {
|
||||
@@ -544,9 +541,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 {
|
||||
@@ -556,10 +592,24 @@ func (c *COINUT) wsGetOpenOrders(p currency.Pair) error {
|
||||
currency := c.FormatExchangeCurrency(p, asset.Spot).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 {
|
||||
@@ -571,14 +621,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 {
|
||||
@@ -590,8 +657,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 {
|
||||
@@ -602,9 +690,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
|
||||
}
|
||||
|
||||
@@ -7,15 +7,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
|
||||
@@ -86,12 +87,18 @@ func (c *COINUT) SetDefaults() {
|
||||
c.API.Endpoints.URLDefault = coinutAPIURL
|
||||
c.API.Endpoints.URL = c.API.Endpoints.URLDefault
|
||||
c.API.Endpoints.WebsocketURL = coinutWebsocketURL
|
||||
c.WebsocketInit()
|
||||
c.Websocket.Functionality = exchange.WebsocketTickerSupported |
|
||||
exchange.WebsocketOrderbookSupported |
|
||||
exchange.WebsocketTradeDataSupported |
|
||||
exchange.WebsocketSubscribeSupported |
|
||||
exchange.WebsocketUnsubscribeSupported
|
||||
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
|
||||
@@ -106,14 +113,28 @@ func (c *COINUT) Setup(exch *config.ExchangeConfig) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.WebsocketSetup(c.WsConnect,
|
||||
err = c.Websocket.Setup(c.WsConnect,
|
||||
c.Subscribe,
|
||||
c.Unsubscribe,
|
||||
exch.Name,
|
||||
exch.Features.Enabled.Websocket,
|
||||
exch.Verbose,
|
||||
coinutWebsocketURL,
|
||||
exch.API.Endpoints.WebsocketURL)
|
||||
exch.API.Endpoints.WebsocketURL,
|
||||
exch.API.AuthenticatedWebsocketSupport)
|
||||
if err != nil {
|
||||
return 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,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start starts the COINUT go routine
|
||||
@@ -496,7 +517,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
|
||||
}
|
||||
|
||||
@@ -621,20 +642,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