diff --git a/exchanges/bitfinex/bitfinex.go b/exchanges/bitfinex/bitfinex.go
index 45ebcdef..970bc05a 100644
--- a/exchanges/bitfinex/bitfinex.go
+++ b/exchanges/bitfinex/bitfinex.go
@@ -129,7 +129,7 @@ func (b *Bitfinex) Setup(exch config.ExchangeConfig) {
b.SetHTTPClientUserAgent(exch.HTTPUserAgent)
b.RESTPollingDelay = exch.RESTPollingDelay
b.Verbose = exch.Verbose
- b.Websocket.SetEnabled(exch.Websocket)
+ b.Websocket.SetWsStatusAndConnection(exch.Websocket)
b.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
b.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
b.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
diff --git a/exchanges/bitflyer/bitflyer.go b/exchanges/bitflyer/bitflyer.go
index ce9aa633..00cfeb6b 100644
--- a/exchanges/bitflyer/bitflyer.go
+++ b/exchanges/bitflyer/bitflyer.go
@@ -114,7 +114,7 @@ func (b *Bitflyer) Setup(exch config.ExchangeConfig) {
b.SetHTTPClientUserAgent(exch.HTTPUserAgent)
b.RESTPollingDelay = exch.RESTPollingDelay
b.Verbose = exch.Verbose
- b.Websocket.SetEnabled(exch.Websocket)
+ b.Websocket.SetWsStatusAndConnection(exch.Websocket)
b.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
b.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
b.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
diff --git a/exchanges/bithumb/bithumb.go b/exchanges/bithumb/bithumb.go
index bb63f441..d8edccb9 100644
--- a/exchanges/bithumb/bithumb.go
+++ b/exchanges/bithumb/bithumb.go
@@ -96,7 +96,7 @@ func (b *Bithumb) Setup(exch config.ExchangeConfig) {
b.SetHTTPClientUserAgent(exch.HTTPUserAgent)
b.RESTPollingDelay = exch.RESTPollingDelay
b.Verbose = exch.Verbose
- b.Websocket.SetEnabled(exch.Websocket)
+ b.Websocket.SetWsStatusAndConnection(exch.Websocket)
b.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
b.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
b.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
diff --git a/exchanges/bitmex/bitmex.go b/exchanges/bitmex/bitmex.go
index 258d1f6a..f5738045 100644
--- a/exchanges/bitmex/bitmex.go
+++ b/exchanges/bitmex/bitmex.go
@@ -148,7 +148,7 @@ func (b *Bitmex) Setup(exch config.ExchangeConfig) {
b.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
b.RESTPollingDelay = exch.RESTPollingDelay
b.Verbose = exch.Verbose
- b.Websocket.SetEnabled(exch.Websocket)
+ b.Websocket.SetWsStatusAndConnection(exch.Websocket)
b.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
b.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
b.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
diff --git a/exchanges/bitstamp/bitstamp.go b/exchanges/bitstamp/bitstamp.go
index 5cf036aa..a827150b 100644
--- a/exchanges/bitstamp/bitstamp.go
+++ b/exchanges/bitstamp/bitstamp.go
@@ -103,7 +103,7 @@ func (b *Bitstamp) Setup(exch config.ExchangeConfig) {
b.SetHTTPClientUserAgent(exch.HTTPUserAgent)
b.RESTPollingDelay = exch.RESTPollingDelay
b.Verbose = exch.Verbose
- b.Websocket.SetEnabled(exch.Websocket)
+ b.Websocket.SetWsStatusAndConnection(exch.Websocket)
b.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
b.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
b.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
diff --git a/exchanges/btcc/btcc.go b/exchanges/btcc/btcc.go
index 08f59ee4..2801603a 100644
--- a/exchanges/btcc/btcc.go
+++ b/exchanges/btcc/btcc.go
@@ -59,7 +59,7 @@ func (b *BTCC) Setup(exch config.ExchangeConfig) {
b.SetHTTPClientUserAgent(exch.HTTPUserAgent)
b.RESTPollingDelay = exch.RESTPollingDelay
b.Verbose = exch.Verbose
- b.Websocket.SetEnabled(exch.Websocket)
+ b.Websocket.SetWsStatusAndConnection(exch.Websocket)
b.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
b.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
b.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
diff --git a/exchanges/btse/btse.go b/exchanges/btse/btse.go
index d9db0cde..0203eb2a 100644
--- a/exchanges/btse/btse.go
+++ b/exchanges/btse/btse.go
@@ -81,7 +81,7 @@ func (b *BTSE) Setup(exch config.ExchangeConfig) {
b.SetHTTPClientUserAgent(exch.HTTPUserAgent)
b.RESTPollingDelay = exch.RESTPollingDelay
b.Verbose = exch.Verbose
- b.Websocket.SetEnabled(exch.Websocket)
+ b.Websocket.SetWsStatusAndConnection(exch.Websocket)
b.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
b.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
b.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
diff --git a/exchanges/coinbasepro/coinbasepro.go b/exchanges/coinbasepro/coinbasepro.go
index 3880e871..ad4be0d9 100644
--- a/exchanges/coinbasepro/coinbasepro.go
+++ b/exchanges/coinbasepro/coinbasepro.go
@@ -103,7 +103,7 @@ func (c *CoinbasePro) Setup(exch config.ExchangeConfig) {
c.SetHTTPClientUserAgent(exch.HTTPUserAgent)
c.RESTPollingDelay = exch.RESTPollingDelay
c.Verbose = exch.Verbose
- c.Websocket.SetEnabled(exch.Websocket)
+ c.Websocket.SetWsStatusAndConnection(exch.Websocket)
c.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
c.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
c.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
diff --git a/exchanges/coinut/coinut.go b/exchanges/coinut/coinut.go
index 7f8db663..d3d057aa 100644
--- a/exchanges/coinut/coinut.go
+++ b/exchanges/coinut/coinut.go
@@ -94,7 +94,7 @@ func (c *COINUT) Setup(exch config.ExchangeConfig) {
c.SetHTTPClientUserAgent(exch.HTTPUserAgent)
c.RESTPollingDelay = exch.RESTPollingDelay
c.Verbose = exch.Verbose
- c.Websocket.SetEnabled(exch.Websocket)
+ c.Websocket.SetWsStatusAndConnection(exch.Websocket)
c.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
c.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
c.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
diff --git a/exchanges/exchange.go b/exchanges/exchange.go
index fd04d2c5..577cbd85 100644
--- a/exchanges/exchange.go
+++ b/exchanges/exchange.go
@@ -240,9 +240,9 @@ type OrderDetail struct {
type FundHistory struct {
ExchangeName string
Status string
- TransferID int64
+ TransferID string
Description string
- Timestamp int64
+ Timestamp time.Time
Currency string
Amount float64
Fee float64
diff --git a/exchanges/exchange_websocket.go b/exchanges/exchange_websocket.go
index e54f9400..b50b9377 100644
--- a/exchanges/exchange_websocket.go
+++ b/exchanges/exchange_websocket.go
@@ -66,7 +66,7 @@ func (e *Base) WebsocketSetup(connector func() error,
e.Websocket.Disconnected = make(chan struct{}, 1)
e.Websocket.TrafficAlert = make(chan struct{}, 1)
- err := e.Websocket.SetEnabled(wsEnabled)
+ err := e.Websocket.SetWsStatusAndConnection(wsEnabled)
if err != nil {
return err
}
@@ -212,6 +212,11 @@ func (w *Websocket) Connect() error {
return nil
}
+// IsConnected exposes websocket connection status
+func (w *Websocket) IsConnected() bool {
+ return w.connected
+}
+
// Shutdown attempts to shut down a websocket connection and associated routines
// by using a package defined shutdown function
func (w *Websocket) Shutdown() error {
@@ -259,8 +264,9 @@ func (w *Websocket) GetWebsocketURL() string {
return w.runningURL
}
-// SetEnabled sets if websocket is enabled
-func (w *Websocket) SetEnabled(enabled bool) error {
+// SetWsStatusAndConnection sets if websocket is enabled
+// it will also connect/disconnect the websocket connection
+func (w *Websocket) SetWsStatusAndConnection(enabled bool) error {
if w.enabled == enabled {
if w.init {
return nil
diff --git a/exchanges/exchange_websocket_test.go b/exchanges/exchange_websocket_test.go
index 6ba5494c..ee7a0549 100644
--- a/exchanges/exchange_websocket_test.go
+++ b/exchanges/exchange_websocket_test.go
@@ -93,19 +93,19 @@ func TestWebsocket(t *testing.T) {
wsTest.Websocket.SetWebsocketURL("")
// -- Set true when already true
- err = wsTest.Websocket.SetEnabled(true)
+ err = wsTest.Websocket.SetWsStatusAndConnection(true)
if err == nil {
t.Fatal("test failed - setting enabled should not work")
}
// -- Set false normal
- err = wsTest.Websocket.SetEnabled(false)
+ err = wsTest.Websocket.SetWsStatusAndConnection(false)
if err != nil {
t.Fatal("test failed - setting enabled should not work")
}
// -- Set true normal
- err = wsTest.Websocket.SetEnabled(true)
+ err = wsTest.Websocket.SetWsStatusAndConnection(true)
if err != nil {
t.Fatal("test failed - setting enabled should not work")
}
diff --git a/exchanges/hitbtc/hitbtc.go b/exchanges/hitbtc/hitbtc.go
index 360ce145..8d5bb84f 100644
--- a/exchanges/hitbtc/hitbtc.go
+++ b/exchanges/hitbtc/hitbtc.go
@@ -95,7 +95,7 @@ func (h *HitBTC) Setup(exch config.ExchangeConfig) {
h.SetHTTPClientUserAgent(exch.HTTPUserAgent)
h.RESTPollingDelay = exch.RESTPollingDelay // Max 60000ms
h.Verbose = exch.Verbose
- h.Websocket.SetEnabled(exch.Websocket)
+ h.Websocket.SetWsStatusAndConnection(exch.Websocket)
h.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
h.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
h.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
diff --git a/exchanges/huobi/huobi.go b/exchanges/huobi/huobi.go
index f8176aa6..81ab0f7f 100644
--- a/exchanges/huobi/huobi.go
+++ b/exchanges/huobi/huobi.go
@@ -112,7 +112,7 @@ func (h *HUOBI) Setup(exch config.ExchangeConfig) {
h.SetHTTPClientUserAgent(exch.HTTPUserAgent)
h.RESTPollingDelay = exch.RESTPollingDelay
h.Verbose = exch.Verbose
- h.Websocket.SetEnabled(exch.Websocket)
+ h.Websocket.SetWsStatusAndConnection(exch.Websocket)
h.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
h.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
h.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
diff --git a/exchanges/okcoin/okcoin.go b/exchanges/okcoin/okcoin.go
index 6bf18b54..c541eb1f 100644
--- a/exchanges/okcoin/okcoin.go
+++ b/exchanges/okcoin/okcoin.go
@@ -1,1044 +1,58 @@
package okcoin
import (
- "errors"
- "fmt"
- "net/http"
- "net/url"
- "strconv"
- "strings"
"time"
- "github.com/gorilla/websocket"
"github.com/thrasher-/gocryptotrader/common"
- "github.com/thrasher-/gocryptotrader/config"
- "github.com/thrasher-/gocryptotrader/currency/symbol"
exchange "github.com/thrasher-/gocryptotrader/exchanges"
+ "github.com/thrasher-/gocryptotrader/exchanges/okgroup"
"github.com/thrasher-/gocryptotrader/exchanges/request"
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
- log "github.com/thrasher-/gocryptotrader/logger"
)
const (
- okcoinAPIURL = "https://www.okcoin.com/api/v1/"
- okcoinAPIURLBase = "https://www.okcoin.com/api/"
- okcoinAPIVersion = "1"
- okcoinWebsocketURL = "wss://real.okcoin.com:10440/websocket/okcoinapi"
- okcoinInstruments = "instruments"
- okcoinTicker = "ticker.do"
- okcoinDepth = "depth.do"
- okcoinTrades = "trades.do"
- okcoinKline = "kline.do"
- okcoinUserInfo = "userinfo.do"
- okcoinTrade = "trade.do"
- okcoinTradeHistory = "trade_history.do"
- okcoinTradeBatch = "batch_trade.do"
- okcoinOrderCancel = "cancel_order.do"
- okcoinOrderInfo = "order_info.do"
- okcoinOrdersInfo = "orders_info.do"
- okcoinOrderHistory = "order_history.do"
- okcoinWithdraw = "withdraw.do"
- okcoinWithdrawCancel = "cancel_withdraw.do"
- okcoinWithdrawInfo = "withdraw_info.do"
- okcoinOrderFee = "order_fee.do"
- okcoinLendDepth = "lend_depth.do"
- okcoinBorrowsInfo = "borrows_info.do"
- okcoinBorrowMoney = "borrow_money.do"
- okcoinBorrowCancel = "cancel_borrow.do"
- okcoinBorrowOrderInfo = "borrow_order_info.do"
- okcoinRepayment = "repayment.do"
- okcoinUnrepaymentsInfo = "unrepayments_info.do"
- okcoinAccountRecords = "account_records.do"
- okcoinFuturesTicker = "future_ticker.do"
- okcoinFuturesDepth = "future_depth.do"
- okcoinFuturesTrades = "future_trades.do"
- okcoinFuturesIndex = "future_index.do"
- okcoinExchangeRate = "exchange_rate.do"
- okcoinFuturesEstimatedPrice = "future_estimated_price.do"
- okcoinFuturesKline = "future_kline.do"
- okcoinFuturesHoldAmount = "future_hold_amount.do"
- okcoinFuturesUserInfo = "future_userinfo.do"
- okcoinFuturesPosition = "future_position.do"
- okcoinFuturesTrade = "future_trade.do"
- okcoinFuturesTradeHistory = "future_trades_history.do"
- okcoinFuturesTradeBatch = "future_batch_trade.do"
- okcoinFuturesCancel = "future_cancel.do"
- okcoinFuturesOrderInfo = "future_order_info.do"
- okcoinFuturesOrdersInfo = "future_orders_info.do"
- okcoinFuturesUserInfo4Fix = "future_userinfo_4fix.do"
- okcoinFuturesposition4Fix = "future_position_4fix.do"
- okcoinFuturesExplosive = "future_explosive.do"
- okcoinFuturesDevolve = "future_devolve.do"
-
- okcoinAuthRate = 0
- okcoinUnauthRate = 0
+ okCoinAuthRate = 600
+ okCoinUnauthRate = 600
+ okCoinAPIPath = "api/"
+ okCoinAPIURL = "https://www.okcoin.com/" + okCoinAPIPath
+ okCoinAPIVersion = "/v3/"
+ okCoinExchangeName = "OKCOIN International"
+ okCoinWebsocketURL = "wss://real.okcoin.com:10442/ws/v3"
)
-// OKCoin is the overarching type across this package
+// OKCoin bases all methods off okgroup implementation
type OKCoin struct {
- exchange.Base
- RESTErrors map[string]string
- WebsocketErrors map[string]string
- FuturesValues []string
- WebsocketConn *websocket.Conn
+ okgroup.OKGroup
}
-// SetDefaults sets current default values for this package
+// SetDefaults method assignes the default values for OKEX
func (o *OKCoin) SetDefaults() {
o.SetErrorDefaults()
- o.SetWebsocketErrorDefaults()
+ o.SetCheckVarDefaults()
+ o.Name = okCoinExchangeName
o.Enabled = false
o.Verbose = false
o.RESTPollingDelay = 10
- o.AssetTypes = []string{ticker.Spot}
o.APIWithdrawPermissions = exchange.AutoWithdrawCrypto |
- exchange.WithdrawFiatViaWebsiteOnly
- o.SupportsAutoPairUpdating = false
+ exchange.NoFiatWithdrawals
+ o.RequestCurrencyPairFormat.Delimiter = "_"
+ o.RequestCurrencyPairFormat.Uppercase = false
+ o.ConfigCurrencyPairFormat.Delimiter = "_"
+ o.ConfigCurrencyPairFormat.Uppercase = true
+ o.SupportsAutoPairUpdating = true
o.SupportsRESTTickerBatching = false
+ o.Requester = request.New(o.Name,
+ request.NewRateLimit(time.Second, okCoinAuthRate),
+ request.NewRateLimit(time.Second, okCoinUnauthRate),
+ common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
+ o.APIUrlDefault = okCoinAPIURL
+ o.APIUrl = okCoinAPIURL
+ o.AssetTypes = []string{ticker.Spot}
o.WebsocketInit()
+ o.WebsocketURL = okCoinWebsocketURL
+ o.APIVersion = okCoinAPIVersion
o.Websocket.Functionality = exchange.WebsocketTickerSupported |
- exchange.WebsocketOrderbookSupported |
- exchange.WebsocketKlineSupported
-}
-
-// Setup sets exchange configuration parameters
-func (o *OKCoin) Setup(exch config.ExchangeConfig) {
- if !exch.Enabled {
- o.SetEnabled(false)
- } else {
- o.Name = "OKCOIN International"
- o.Enabled = true
- o.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
- o.AssetTypes = append(o.AssetTypes, o.FuturesValues...)
- o.APIUrlDefault = okcoinAPIURL
- o.APIUrl = o.APIUrlDefault
- o.WebsocketURL = okcoinWebsocketURL
- o.Requester = request.New(o.Name,
- request.NewRateLimit(time.Second, okcoinAuthRate),
- request.NewRateLimit(time.Second, okcoinUnauthRate),
- common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
- o.ConfigCurrencyPairFormat.Delimiter = "_"
- o.ConfigCurrencyPairFormat.Uppercase = true
- o.RequestCurrencyPairFormat.Uppercase = false
- o.RequestCurrencyPairFormat.Delimiter = "_"
- o.SupportsAutoPairUpdating = true
- o.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
- o.SetHTTPClientTimeout(exch.HTTPTimeout)
- o.SetHTTPClientUserAgent(exch.HTTPUserAgent)
- o.RESTPollingDelay = exch.RESTPollingDelay
- o.Verbose = exch.Verbose
- o.Websocket.SetEnabled(exch.Websocket)
- o.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
- o.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
- o.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
- err := o.SetCurrencyPairFormat()
- if err != nil {
- log.Fatal(err)
- }
- err = o.SetAssetTypes()
- if err != nil {
- log.Fatal(err)
- }
- err = o.SetAutoPairDefaults()
- if err != nil {
- log.Fatal(err)
- }
- err = o.SetAPIURL(exch)
- if err != nil {
- log.Fatal(err)
- }
- err = o.SetClientProxyAddress(exch.ProxyAddress)
- if err != nil {
- log.Fatal(err)
- }
- err = o.WebsocketSetup(o.WsConnect,
- exch.Name,
- exch.Websocket,
- okcoinWebsocketURL,
- o.WebsocketURL)
- if err != nil {
- log.Fatal(err)
- }
- }
-}
-
-// GetSpotInstruments returns a list of tradable spot instruments and their properties
-func (o *OKCoin) GetSpotInstruments() ([]SpotInstrument, error) {
- var resp []SpotInstrument
-
- path := fmt.Sprintf("%sspot/v3/%s", okcoinAPIURLBase, okcoinInstruments)
- err := o.SendHTTPRequest(path, &resp)
-
- if err != nil {
- return nil, err
- }
-
- return resp, nil
-}
-
-// GetTicker returns the current ticker
-func (o *OKCoin) GetTicker(symbol string) (Ticker, error) {
- resp := TickerResponse{}
- vals := url.Values{}
- vals.Set("symbol", symbol)
- path := common.EncodeURLValues(o.APIUrl+okcoinTicker, vals)
-
- return resp.Ticker, o.SendHTTPRequest(path, &resp)
-}
-
-// GetOrderBook returns the current order book by size
-func (o *OKCoin) GetOrderBook(symbol string, size int64, merge bool) (Orderbook, error) {
- resp := Orderbook{}
- vals := url.Values{}
- vals.Set("symbol", symbol)
- if size != 0 {
- vals.Set("size", strconv.FormatInt(size, 10))
- }
- if merge {
- vals.Set("merge", "1")
- }
-
- path := common.EncodeURLValues(o.APIUrl+okcoinDepth, vals)
- return resp, o.SendHTTPRequest(path, &resp)
-}
-
-// GetTrades returns historic trades since a timestamp
-func (o *OKCoin) GetTrades(symbol string, since int64) ([]Trades, error) {
- result := []Trades{}
- vals := url.Values{}
- vals.Set("symbol", symbol)
- if since != 0 {
- vals.Set("since", strconv.FormatInt(since, 10))
- }
-
- path := common.EncodeURLValues(o.APIUrl+okcoinTrades, vals)
- return result, o.SendHTTPRequest(path, &result)
-}
-
-// GetKline returns kline data
-func (o *OKCoin) GetKline(symbol, klineType string, size, since int64) ([]interface{}, error) {
- resp := []interface{}{}
- vals := url.Values{}
- vals.Set("symbol", symbol)
- vals.Set("type", klineType)
-
- if size != 0 {
- vals.Set("size", strconv.FormatInt(size, 10))
- }
-
- if since != 0 {
- vals.Set("since", strconv.FormatInt(since, 10))
- }
-
- path := common.EncodeURLValues(o.APIUrl+okcoinKline, vals)
- return resp, o.SendHTTPRequest(path, &resp)
-}
-
-// GetFuturesTicker returns a current ticker for the futures market
-func (o *OKCoin) GetFuturesTicker(symbol, contractType string) (FuturesTicker, error) {
- resp := FuturesTickerResponse{}
- vals := url.Values{}
- vals.Set("symbol", symbol)
- vals.Set("contract_type", contractType)
- path := common.EncodeURLValues(o.APIUrl+okcoinFuturesTicker, vals)
-
- return resp.Ticker, o.SendHTTPRequest(path, &resp)
-}
-
-// GetFuturesDepth returns current depth for the futures market
-func (o *OKCoin) GetFuturesDepth(symbol, contractType string, size int64, merge bool) (Orderbook, error) {
- result := Orderbook{}
- vals := url.Values{}
- vals.Set("symbol", symbol)
- vals.Set("contract_type", contractType)
-
- if size != 0 {
- vals.Set("size", strconv.FormatInt(size, 10))
- }
- if merge {
- vals.Set("merge", "1")
- }
-
- path := common.EncodeURLValues(o.APIUrl+okcoinFuturesDepth, vals)
- return result, o.SendHTTPRequest(path, &result)
-}
-
-// GetFuturesTrades returns historic trades for the futures market
-func (o *OKCoin) GetFuturesTrades(symbol, contractType string) ([]FuturesTrades, error) {
- result := []FuturesTrades{}
- vals := url.Values{}
- vals.Set("symbol", symbol)
- vals.Set("contract_type", contractType)
-
- path := common.EncodeURLValues(o.APIUrl+okcoinFuturesTrades, vals)
- return result, o.SendHTTPRequest(path, &result)
-}
-
-// GetFuturesIndex returns an index for the futures market
-func (o *OKCoin) GetFuturesIndex(symbol string) (float64, error) {
- type Response struct {
- Index float64 `json:"future_index"`
- }
-
- result := Response{}
- vals := url.Values{}
- vals.Set("symbol", symbol)
-
- path := common.EncodeURLValues(o.APIUrl+okcoinFuturesIndex, vals)
- return result.Index, o.SendHTTPRequest(path, &result)
-}
-
-// GetFuturesExchangeRate returns the exchange rate for the futures market
-func (o *OKCoin) GetFuturesExchangeRate() (float64, error) {
- type Response struct {
- Rate float64 `json:"rate"`
- }
-
- result := Response{}
- return result.Rate, o.SendHTTPRequest(o.APIUrl+okcoinExchangeRate, &result)
-}
-
-// GetFuturesEstimatedPrice returns a current estimated futures price for a
-// currency
-func (o *OKCoin) GetFuturesEstimatedPrice(symbol string) (float64, error) {
- type Response struct {
- Price float64 `json:"forecast_price"`
- }
-
- result := Response{}
- vals := url.Values{}
- vals.Set("symbol", symbol)
- path := common.EncodeURLValues(o.APIUrl+okcoinFuturesEstimatedPrice, vals)
-
- return result.Price, o.SendHTTPRequest(path, &result)
-}
-
-// GetFuturesKline returns kline data for a specific currency on the futures
-// market
-func (o *OKCoin) GetFuturesKline(symbol, klineType, contractType string, size, since int64) ([]interface{}, error) {
- resp := []interface{}{}
- vals := url.Values{}
- vals.Set("symbol", symbol)
- vals.Set("type", klineType)
- vals.Set("contract_type", contractType)
-
- if size != 0 {
- vals.Set("size", strconv.FormatInt(size, 10))
- }
- if since != 0 {
- vals.Set("since", strconv.FormatInt(since, 10))
- }
-
- path := common.EncodeURLValues(o.APIUrl+okcoinFuturesKline, vals)
- return resp, o.SendHTTPRequest(path, &resp)
-}
-
-// GetFuturesHoldAmount returns the hold amount for a futures trade
-func (o *OKCoin) GetFuturesHoldAmount(symbol, contractType string) ([]FuturesHoldAmount, error) {
- resp := []FuturesHoldAmount{}
- vals := url.Values{}
- vals.Set("symbol", symbol)
- vals.Set("contract_type", contractType)
-
- path := common.EncodeURLValues(o.APIUrl+okcoinFuturesHoldAmount, vals)
- return resp, o.SendHTTPRequest(path, &resp)
-}
-
-// GetFuturesExplosive returns the explosive for a futures contract
-func (o *OKCoin) GetFuturesExplosive(symbol, contractType string, status, currentPage, pageLength int64) ([]FuturesExplosive, error) {
- type Response struct {
- Data []FuturesExplosive `json:"data"`
- }
- resp := Response{}
- vals := url.Values{}
- vals.Set("symbol", symbol)
- vals.Set("contract_type", contractType)
- vals.Set("status", strconv.FormatInt(status, 10))
- vals.Set("current_page", strconv.FormatInt(currentPage, 10))
- vals.Set("page_length", strconv.FormatInt(pageLength, 10))
-
- path := common.EncodeURLValues(o.APIUrl+okcoinFuturesExplosive, vals)
-
- return resp.Data, o.SendHTTPRequest(path, &resp)
-}
-
-// GetUserInfo returns user information associated with the calling APIkeys
-func (o *OKCoin) GetUserInfo() (UserInfo, error) {
- result := UserInfo{}
-
- return result,
- o.SendAuthenticatedHTTPRequest(okcoinUserInfo, url.Values{}, &result)
-}
-
-// Trade initiates a new trade
-func (o *OKCoin) Trade(amount, price float64, symbol, orderType string) (int64, error) {
- type Response struct {
- Result bool `json:"result"`
- OrderID int64 `json:"order_id"`
- }
- v := url.Values{}
- v.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
- v.Set("price", strconv.FormatFloat(price, 'f', -1, 64))
- v.Set("symbol", symbol)
- v.Set("type", orderType)
-
- result := Response{}
-
- err := o.SendAuthenticatedHTTPRequest(okcoinTrade, v, &result)
-
- if err != nil {
- return 0, err
- }
-
- if !result.Result {
- return 0, errors.New("unable to place order")
- }
-
- return result.OrderID, nil
-}
-
-// GetTradeHistory returns client trade history
-func (o *OKCoin) GetTradeHistory(symbol string, tradeID int64) ([]Trades, error) {
- result := []Trades{}
- v := url.Values{}
- v.Set("symbol", symbol)
- v.Set("since", strconv.FormatInt(tradeID, 10))
-
- err := o.SendAuthenticatedHTTPRequest(okcoinTradeHistory, v, &result)
-
- if err != nil {
- return nil, err
- }
-
- return result, nil
-}
-
-// BatchTrade initiates a trade by batch order
-func (o *OKCoin) BatchTrade(orderData, symbol, orderType string) (BatchTrade, error) {
- v := url.Values{}
- v.Set("orders_data", orderData)
- v.Set("symbol", symbol)
- v.Set("type", orderType)
-
- result := BatchTrade{}
- return result, o.SendAuthenticatedHTTPRequest(okcoinTradeBatch, v, &result)
-}
-
-// CancelExistingOrder cancels a specific order or list of orders by orderID
-func (o *OKCoin) CancelExistingOrder(orderID []int64, symbol string) (CancelOrderResponse, error) {
- v := url.Values{}
- orders := []string{}
- result := CancelOrderResponse{}
-
- orderStr := strconv.FormatInt(orderID[0], 10)
-
- if len(orderID) > 1 {
- for x := range orderID {
- orders = append(orders, strconv.FormatInt(orderID[x], 10))
- }
- orderStr = common.JoinStrings(orders, ",")
- }
-
- v.Set("order_id", orderStr)
- v.Set("symbol", symbol)
-
- return result, o.SendAuthenticatedHTTPRequest(okcoinOrderCancel, v, &result)
-}
-
-// GetOrderInformation returns order information by orderID
-func (o *OKCoin) GetOrderInformation(orderID int64, symbol string) ([]OrderInfo, error) {
- type Response struct {
- Result bool `json:"result"`
- Orders []OrderInfo `json:"orders"`
- }
- v := url.Values{}
- v.Set("symbol", symbol)
- v.Set("order_id", strconv.FormatInt(orderID, 10))
- result := Response{}
-
- err := o.SendAuthenticatedHTTPRequest(okcoinOrderInfo, v, &result)
-
- if err != nil {
- return nil, err
- }
-
- if !result.Result {
- return nil, errors.New("unable to retrieve order info")
- }
-
- return result.Orders, nil
-}
-
-// GetOrderInfoBatch returns order info on a batch of orders
-func (o *OKCoin) GetOrderInfoBatch(orderID []int64, symbol string) ([]OrderInfo, error) {
- type Response struct {
- Result bool `json:"result"`
- Orders []OrderInfo `json:"orders"`
- }
-
- orders := []string{}
- for x := range orderID {
- orders = append(orders, strconv.FormatInt(orderID[x], 10))
- }
-
- v := url.Values{}
- v.Set("symbol", symbol)
- v.Set("order_id", common.JoinStrings(orders, ","))
- result := Response{}
-
- err := o.SendAuthenticatedHTTPRequest(okcoinOrderInfo, v, &result)
-
- if err != nil {
- return nil, err
- }
-
- if !result.Result {
- return nil, errors.New("unable to retrieve order info")
- }
-
- return result.Orders, nil
-}
-
-// GetOrderHistoryForCurrency returns a history of orders
-func (o *OKCoin) GetOrderHistoryForCurrency(pageLength, currentPage, status int64, symbol string) (OrderHistory, error) {
- v := url.Values{}
- v.Set("symbol", symbol)
- v.Set("status", strconv.FormatInt(status, 10))
- v.Set("current_page", strconv.FormatInt(currentPage, 10))
- v.Set("page_length", strconv.FormatInt(pageLength, 10))
-
- result := OrderHistory{}
- return result, o.SendAuthenticatedHTTPRequest(okcoinOrderHistory, v, &result)
-}
-
-// Withdrawal withdraws a cryptocurrency to a supplied address
-func (o *OKCoin) Withdrawal(symbol string, fee float64, tradePWD, address string, amount float64) (int, error) {
- v := url.Values{}
- v.Set("symbol", symbol)
-
- if fee != 0 {
- v.Set("chargefee", strconv.FormatFloat(fee, 'f', -1, 64))
- }
- v.Set("trade_pwd", tradePWD)
- v.Set("withdraw_address", address)
- v.Set("withdraw_amount", strconv.FormatFloat(amount, 'f', -1, 64))
- v.Set("target", "address")
- result := WithdrawalResponse{}
-
- err := o.SendAuthenticatedHTTPRequest(okcoinWithdraw, v, &result)
- if err != nil {
- return 0, err
- }
-
- if !result.Result {
- return 0, errors.New("unable to process withdrawal request")
- }
-
- return result.WithdrawID, nil
-}
-
-// CancelWithdrawal cancels a withdrawal
-func (o *OKCoin) CancelWithdrawal(symbol string, withdrawalID int64) (int, error) {
- v := url.Values{}
- v.Set("symbol", symbol)
- v.Set("withdrawal_id", strconv.FormatInt(withdrawalID, 10))
- result := WithdrawalResponse{}
-
- err := o.SendAuthenticatedHTTPRequest(okcoinWithdrawCancel, v, &result)
-
- if err != nil {
- return 0, err
- }
-
- if !result.Result {
- return 0, errors.New("unable to process withdrawal cancel request")
- }
-
- return result.WithdrawID, nil
-}
-
-// GetWithdrawalInfo returns withdrawal information
-func (o *OKCoin) GetWithdrawalInfo(symbol string, withdrawalID int64) ([]WithdrawInfo, error) {
- type Response struct {
- Result bool
- Withdraw []WithdrawInfo `json:"withdraw"`
- }
- v := url.Values{}
- v.Set("symbol", symbol)
- v.Set("withdrawal_id", strconv.FormatInt(withdrawalID, 10))
- result := Response{}
-
- err := o.SendAuthenticatedHTTPRequest(okcoinWithdrawInfo, v, &result)
-
- if err != nil {
- return nil, err
- }
-
- if !result.Result {
- return nil, errors.New("unable to process withdrawal cancel request")
- }
-
- return result.Withdraw, nil
-}
-
-// GetOrderFeeInfo returns order fee information
-func (o *OKCoin) GetOrderFeeInfo(symbol string, orderID int64) (OrderFeeInfo, error) {
- type Response struct {
- Data OrderFeeInfo `json:"data"`
- Result bool `json:"result"`
- }
-
- v := url.Values{}
- v.Set("symbol", symbol)
- v.Set("order_id", strconv.FormatInt(orderID, 10))
- result := Response{}
-
- err := o.SendAuthenticatedHTTPRequest(okcoinOrderFee, v, &result)
-
- if err != nil {
- return result.Data, err
- }
-
- if !result.Result {
- return result.Data, errors.New("unable to get order fee info")
- }
-
- return result.Data, nil
-}
-
-// GetLendDepth returns the depth of lends
-func (o *OKCoin) GetLendDepth(symbol string) ([]LendDepth, error) {
- type Response struct {
- LendDepth []LendDepth `json:"lend_depth"`
- }
-
- v := url.Values{}
- v.Set("symbol", symbol)
- result := Response{}
-
- err := o.SendAuthenticatedHTTPRequest(okcoinLendDepth, v, &result)
-
- if err != nil {
- return nil, err
- }
-
- return result.LendDepth, nil
-}
-
-// GetBorrowInfo returns borrow information
-func (o *OKCoin) GetBorrowInfo(symbol string) (BorrowInfo, error) {
- v := url.Values{}
- v.Set("symbol", symbol)
- result := BorrowInfo{}
-
- err := o.SendAuthenticatedHTTPRequest(okcoinBorrowsInfo, v, &result)
-
- if err != nil {
- return result, nil
- }
-
- return result, nil
-}
-
-// Borrow initiates a borrow request
-func (o *OKCoin) Borrow(symbol, days string, amount, rate float64) (int, error) {
- v := url.Values{}
- v.Set("symbol", symbol)
- v.Set("days", days)
- v.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
- v.Set("rate", strconv.FormatFloat(rate, 'f', -1, 64))
- result := BorrowResponse{}
-
- err := o.SendAuthenticatedHTTPRequest(okcoinBorrowMoney, v, &result)
-
- if err != nil {
- return 0, err
- }
-
- if !result.Result {
- return 0, errors.New("unable to borrow")
- }
-
- return result.BorrowID, nil
-}
-
-// CancelBorrow cancels a borrow request
-func (o *OKCoin) CancelBorrow(symbol string, borrowID int64) (bool, error) {
- v := url.Values{}
- v.Set("symbol", symbol)
- v.Set("borrow_id", strconv.FormatInt(borrowID, 10))
- result := BorrowResponse{}
-
- err := o.SendAuthenticatedHTTPRequest(okcoinBorrowCancel, v, &result)
-
- if err != nil {
- return false, err
- }
-
- if !result.Result {
- return false, errors.New("unable to cancel borrow")
- }
-
- return true, nil
-}
-
-// GetBorrowOrderInfo returns information about a borrow order
-func (o *OKCoin) GetBorrowOrderInfo(borrowID int64) (BorrowInfo, error) {
- type Response struct {
- Result bool `json:"result"`
- BorrowOrder BorrowInfo `json:"borrow_order"`
- }
-
- v := url.Values{}
- v.Set("borrow_id", strconv.FormatInt(borrowID, 10))
- result := Response{}
- err := o.SendAuthenticatedHTTPRequest(okcoinBorrowOrderInfo, v, &result)
-
- if err != nil {
- return result.BorrowOrder, err
- }
-
- if !result.Result {
- return result.BorrowOrder, errors.New("unable to get borrow info")
- }
-
- return result.BorrowOrder, nil
-}
-
-// GetRepaymentInfo returns information on a repayment
-func (o *OKCoin) GetRepaymentInfo(borrowID int64) (bool, error) {
- v := url.Values{}
- v.Set("borrow_id", strconv.FormatInt(borrowID, 10))
- result := BorrowResponse{}
-
- err := o.SendAuthenticatedHTTPRequest(okcoinRepayment, v, &result)
-
- if err != nil {
- return false, err
- }
-
- if !result.Result {
- return false, errors.New("unable to get repayment info")
- }
-
- return true, nil
-}
-
-// GetUnrepaymentsInfo returns information on an unrepayment
-func (o *OKCoin) GetUnrepaymentsInfo(symbol string, currentPage, pageLength int) ([]BorrowOrder, error) {
- type Response struct {
- Unrepayments []BorrowOrder `json:"unrepayments"`
- Result bool `json:"result"`
- }
- v := url.Values{}
- v.Set("symbol", symbol)
- v.Set("current_page", strconv.Itoa(currentPage))
- v.Set("page_length", strconv.Itoa(pageLength))
- result := Response{}
- err := o.SendAuthenticatedHTTPRequest(okcoinUnrepaymentsInfo, v, &result)
-
- if err != nil {
- return nil, err
- }
-
- if !result.Result {
- return nil, errors.New("unable to get unrepayments info")
- }
-
- return result.Unrepayments, nil
-}
-
-// GetAccountRecords returns account records
-func (o *OKCoin) GetAccountRecords(symbol string, recType, currentPage, pageLength int) ([]AccountRecords, error) {
- type Response struct {
- Records []AccountRecords `json:"records"`
- Symbol string `json:"symbol"`
- }
- v := url.Values{}
- v.Set("symbol", symbol)
- v.Set("type", strconv.Itoa(recType))
- v.Set("current_page", strconv.Itoa(currentPage))
- v.Set("page_length", strconv.Itoa(pageLength))
- result := Response{}
-
- err := o.SendAuthenticatedHTTPRequest(okcoinAccountRecords, v, &result)
-
- if err != nil {
- return nil, err
- }
-
- return result.Records, nil
-}
-
-// GetFuturesUserInfo returns information on a users futures
-func (o *OKCoin) GetFuturesUserInfo() {
- err := o.SendAuthenticatedHTTPRequest(okcoinFuturesUserInfo, url.Values{}, nil)
-
- if err != nil {
- log.Error(err)
- }
-}
-
-// GetFuturesPosition returns position on a futures contract
-func (o *OKCoin) GetFuturesPosition(symbol, contractType string) {
- v := url.Values{}
- v.Set("symbol", symbol)
- v.Set("contract_type", contractType)
- err := o.SendAuthenticatedHTTPRequest(okcoinFuturesPosition, v, nil)
-
- if err != nil {
- log.Error(err)
- }
-}
-
-// FuturesTrade initiates a new futures trade
-func (o *OKCoin) FuturesTrade(amount, price float64, matchPrice, leverage int64, symbol, contractType, orderType string) {
- v := url.Values{}
- v.Set("symbol", symbol)
- v.Set("contract_type", contractType)
- v.Set("price", strconv.FormatFloat(price, 'f', -1, 64))
- v.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
- v.Set("type", orderType)
- v.Set("match_price", strconv.FormatInt(matchPrice, 10))
- v.Set("lever_rate", strconv.FormatInt(leverage, 10))
-
- err := o.SendAuthenticatedHTTPRequest(okcoinFuturesTrade, v, nil)
-
- if err != nil {
- log.Error(err)
- }
-}
-
-// FuturesBatchTrade initiates a batch of futures contract trades
-func (o *OKCoin) FuturesBatchTrade(orderData, symbol, contractType string, leverage int64, _ string) {
- v := url.Values{} // to-do batch trade support for orders_data)
- v.Set("symbol", symbol)
- v.Set("contract_type", contractType)
- v.Set("orders_data", orderData)
- v.Set("lever_rate", strconv.FormatInt(leverage, 10))
-
- err := o.SendAuthenticatedHTTPRequest(okcoinFuturesTradeBatch, v, nil)
-
- if err != nil {
- log.Error(err)
- }
-}
-
-// CancelFuturesOrder cancels a futures contract order
-func (o *OKCoin) CancelFuturesOrder(orderID int64, symbol, contractType string) {
- v := url.Values{}
- v.Set("symbol", symbol)
- v.Set("contract_type", contractType)
- v.Set("order_id", strconv.FormatInt(orderID, 10))
-
- err := o.SendAuthenticatedHTTPRequest(okcoinFuturesCancel, v, nil)
-
- if err != nil {
- log.Error(err)
- }
-}
-
-// GetFuturesOrderInfo returns information on a specific futures contract order
-func (o *OKCoin) GetFuturesOrderInfo(orderID, status, currentPage, pageLength int64, symbol, contractType string) {
- v := url.Values{}
- v.Set("symbol", symbol)
- v.Set("contract_type", contractType)
- v.Set("status", strconv.FormatInt(status, 10))
- v.Set("order_id", strconv.FormatInt(orderID, 10))
- v.Set("current_page", strconv.FormatInt(currentPage, 10))
- v.Set("page_length", strconv.FormatInt(pageLength, 10))
-
- err := o.SendAuthenticatedHTTPRequest(okcoinFuturesOrderInfo, v, nil)
-
- if err != nil {
- log.Error(err)
- }
-}
-
-// GetFutureOrdersInfo returns information on a range of futures orders
-func (o *OKCoin) GetFutureOrdersInfo(orderID int64, contractType, symbol string) {
- v := url.Values{}
- v.Set("order_id", strconv.FormatInt(orderID, 10))
- v.Set("contract_type", contractType)
- v.Set("symbol", symbol)
-
- err := o.SendAuthenticatedHTTPRequest(okcoinFuturesOrdersInfo, v, nil)
-
- if err != nil {
- log.Error(err)
- }
-}
-
-// GetFuturesUserInfo4Fix returns futures user info fix rate
-func (o *OKCoin) GetFuturesUserInfo4Fix() {
- v := url.Values{}
-
- err := o.SendAuthenticatedHTTPRequest(okcoinFuturesUserInfo4Fix, v, nil)
-
- if err != nil {
- log.Error(err)
- }
-}
-
-// GetFuturesUserPosition4Fix returns futures user info on a fixed position
-func (o *OKCoin) GetFuturesUserPosition4Fix(symbol, contractType string) {
- v := url.Values{}
- v.Set("symbol", symbol)
- v.Set("contract_type", contractType)
- v.Set("type", strconv.FormatInt(1, 10))
-
- err := o.SendAuthenticatedHTTPRequest(okcoinFuturesUserInfo4Fix, v, nil)
-
- if err != nil {
- log.Error(err)
- }
-}
-
-// SendHTTPRequest sends an unauthenticated HTTP request
-func (o *OKCoin) SendHTTPRequest(path string, result interface{}) error {
- return o.SendPayload(http.MethodGet, path, nil, nil, result, false, o.Verbose)
-}
-
-// SendAuthenticatedHTTPRequest sends an authenticated HTTP request
-func (o *OKCoin) SendAuthenticatedHTTPRequest(method string, v url.Values, result interface{}) (err error) {
- if !o.AuthenticatedAPISupport {
- return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, o.Name)
- }
-
- v.Set("api_key", o.APIKey)
- hasher := common.GetMD5([]byte(v.Encode() + "&secret_key=" + o.APISecret))
- v.Set("sign", strings.ToUpper(common.HexEncodeToString(hasher)))
-
- encoded := v.Encode()
- path := o.APIUrl + method
-
- if o.Verbose {
- log.Debugf("Sending POST request to %s with params %s\n", path, encoded)
- }
-
- headers := make(map[string]string)
- headers["Content-Type"] = "application/x-www-form-urlencoded"
-
- return o.SendPayload(http.MethodPost, path, headers, strings.NewReader(encoded), result, true, o.Verbose)
-}
-
-// SetErrorDefaults sets default error map
-func (o *OKCoin) SetErrorDefaults() {
- o.RESTErrors = map[string]string{
- "10000": "Required field, can not be null",
- "10001": "Request frequency too high",
- "10002": "System error",
- "10003": "Not in reqest list, please try again later",
- "10004": "IP not allowed to access the resource",
- "10005": "'secretKey' does not exist",
- "10006": "'partner' does not exist",
- "10007": "Signature does not match",
- "10008": "Illegal parameter",
- "10009": "Order does not exist",
- "10010": "Insufficient funds",
- "10011": "Amount too low",
- "10012": "Only btc_usd/btc_cny ltc_usd,ltc_cny supported",
- "10013": "Only support https request",
- "10014": "Order price must be between 0 and 1,000,000",
- "10015": "Order price differs from current market price too much",
- "10016": "Insufficient coins balance",
- "10017": "API authorization error",
- "10018": "Borrow amount less than lower limit [usd/cny:100,btc:0.1,ltc:1]",
- "10019": "Loan agreement not checked",
- "10020": `Rate cannot exceed 1%`,
- "10021": `Rate cannot less than 0.01%`,
- "10023": "Fail to get latest ticker",
- "10024": "Balance not sufficient",
- "10025": "Quota is full, cannot borrow temporarily",
- "10026": "Loan (including reserved loan) and margin cannot be withdrawn",
- "10027": "Cannot withdraw within 24 hrs of authentication information modification",
- "10028": "Withdrawal amount exceeds daily limit",
- "10029": "Account has unpaid loan, please cancel/pay off the loan before withdraw",
- "10031": "Deposits can only be withdrawn after 6 confirmations",
- "10032": "Please enabled phone/google authenticator",
- "10033": "Fee higher than maximum network transaction fee",
- "10034": "Fee lower than minimum network transaction fee",
- "10035": "Insufficient BTC/LTC",
- "10036": "Withdrawal amount too low",
- "10037": "Trade password not set",
- "10040": "Withdrawal cancellation fails",
- "10041": "Withdrawal address not approved",
- "10042": "Admin password error",
- "10043": "Account equity error, withdrawal failure",
- "10044": "fail to cancel borrowing order",
- "10047": "This function is disabled for sub-account",
- "10100": "User account frozen",
- "10216": "Non-available API",
- "20001": "User does not exist",
- "20002": "Account frozen",
- "20003": "Account frozen due to liquidation",
- "20004": "Futures account frozen",
- "20005": "User futures account does not exist",
- "20006": "Required field missing",
- "20007": "Illegal parameter",
- "20008": "Futures account balance is too low",
- "20009": "Future contract status error",
- "20010": "Risk rate ratio does not exist",
- "20011": `Risk rate higher than 90% before opening position`,
- "20012": `Risk rate higher than 90% after opening position`,
- "20013": "Temporally no counter party price",
- "20014": "System error",
- "20015": "Order does not exist",
- "20016": "Close amount bigger than your open positions",
- "20017": "Not authorized/illegal operation",
- "20018": `Order price differ more than 5% from the price in the last minute`,
- "20019": "IP restricted from accessing the resource",
- "20020": "secretKey does not exist",
- "20021": "Index information does not exist",
- "20022": "Wrong API interface (Cross margin mode shall call cross margin API, fixed margin mode shall call fixed margin API)",
- "20023": "Account in fixed-margin mode",
- "20024": "Signature does not match",
- "20025": "Leverage rate error",
- "20026": "API Permission Error",
- "20027": "No transaction record",
- "20028": "No such contract",
- }
-}
-
-// GetFee returns an estimate of fee based on type of transaction
-func (o *OKCoin) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) {
- var fee float64
- switch feeBuilder.FeeType {
- case exchange.CryptocurrencyTradeFee:
- fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.IsMaker)
- case exchange.InternationalBankWithdrawalFee:
- fee = calculateInternationalBankWithdrawalFee(feeBuilder.CurrencyItem, feeBuilder.PurchasePrice, feeBuilder.Amount)
- case exchange.CryptocurrencyWithdrawalFee:
- fee = getWithdrawalFee(feeBuilder.FirstCurrency)
- }
- if fee < 0 {
- fee = 0
- }
-
- return fee, nil
-}
-
-func calculateTradingFee(purchasePrice, amount float64, isMaker bool) (fee float64) {
- // TODO volume based fees
- if isMaker {
- fee = 0.0005
- } else {
- fee = 0.0015
- }
- return fee * amount * purchasePrice
-}
-
-func calculateInternationalBankWithdrawalFee(currency string, purchasePrice, amount float64) (fee float64) {
- if currency == symbol.USD {
- if purchasePrice*amount*0.001 < 15 {
- fee = 15
- } else {
- fee = purchasePrice * amount * 0.001
- }
- }
- return fee
-}
-
-func getWithdrawalFee(currency string) float64 {
- return WithdrawalFees[currency]
+ exchange.WebsocketTradeDataSupported |
+ exchange.WebsocketKlineSupported |
+ exchange.WebsocketOrderbookSupported
}
diff --git a/exchanges/okcoin/okcoin_test.go b/exchanges/okcoin/okcoin_test.go
index c7bff661..2c1c07d2 100644
--- a/exchanges/okcoin/okcoin_test.go
+++ b/exchanges/okcoin/okcoin_test.go
@@ -1,43 +1,1045 @@
package okcoin
import (
+ "strings"
"testing"
+ "time"
"github.com/thrasher-/gocryptotrader/common"
"github.com/thrasher-/gocryptotrader/config"
"github.com/thrasher-/gocryptotrader/currency/pair"
"github.com/thrasher-/gocryptotrader/currency/symbol"
exchange "github.com/thrasher-/gocryptotrader/exchanges"
+ "github.com/thrasher-/gocryptotrader/exchanges/okgroup"
)
-var o OKCoin
-
-// Please supply your own APIKEYS here for due diligence testing
-
+// Please supply you own test keys here for due diligence testing.
const (
apiKey = ""
apiSecret = ""
+ passphrase = ""
+ OKGroupExchange = "OKCOIN International"
canManipulateRealOrders = false
)
+var o = OKCoin{}
+var testSetupRan bool
+var spotCurrency = pair.NewCurrencyPairWithDelimiter(symbol.BTC, symbol.USD, "-").Pair().Lower().String()
+
+// TestSetDefaults Sets standard default settings for running a test
func TestSetDefaults(t *testing.T) {
- o.SetDefaults()
+ if o.Name != OKGroupExchange {
+ o.SetDefaults()
+ }
+ if o.GetName() != OKGroupExchange {
+ t.Errorf("Test Failed - %v - SetDefaults() error", OKGroupExchange)
+ }
+ TestSetup(t)
+ t.Parallel()
}
+// TestSetWsDefaults Sets websocket defaults
+func TestSetWsDefaults(t *testing.T) {
+ if o.Name != OKGroupExchange {
+ o.SetDefaults()
+ }
+ if o.GetName() != OKGroupExchange {
+ t.Errorf("Test Failed - %v - SetDefaults() error", OKGroupExchange)
+ }
+ TestSetup(t)
+}
+
+// TestSetRealOrderDefaults Sets test defaults when test can impact real money/orders
+func TestSetRealOrderDefaults(t *testing.T) {
+ TestSetDefaults(t)
+ if areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
+ }
+}
+
+// TestSetup Sets defaults for test environment
func TestSetup(t *testing.T) {
+ if testSetupRan {
+ return
+ }
+ if o.APIKey == apiKey && o.APISecret == apiSecret &&
+ o.ClientID == passphrase && apiKey != "" && apiSecret != "" && passphrase != "" {
+ return
+ }
+ o.ExchangeName = OKGroupExchange
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
- okcoinConfig, err := cfg.GetExchangeConfig("OKCOIN International")
+
+ okcoinConfig, err := cfg.GetExchangeConfig(OKGroupExchange)
if err != nil {
- t.Error("Test Failed - OKCoin Setup() init error")
+ t.Fatalf("Test Failed - %v Setup() init error", OKGroupExchange)
}
+
okcoinConfig.AuthenticatedAPISupport = true
okcoinConfig.APIKey = apiKey
okcoinConfig.APISecret = apiSecret
-
+ okcoinConfig.ClientID = passphrase
+ okcoinConfig.WebsocketURL = o.WebsocketURL
o.Setup(okcoinConfig)
+ testSetupRan = true
}
+func areTestAPIKeysSet() bool {
+ if o.APIKey != "" && o.APIKey != "Key" &&
+ o.APISecret != "" && o.APISecret != "Secret" {
+ return true
+ }
+ return false
+}
+
+func testStandardErrorHandling(t *testing.T, err error) {
+ if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Errorf("Encountered error: %v", err)
+ }
+}
+
+// setupWSConnection Connect to WS, but pass back error so test can handle it if needed
+func setupWSConnection() error {
+ o.Enabled = true
+ err := o.WebsocketSetup(o.WsConnect,
+ o.Name,
+ true,
+ o.WebsocketURL,
+ o.WebsocketURL)
+ o.Websocket.DataHandler = make(chan interface{}, 500)
+ if err != nil {
+ return err
+ }
+ o.Websocket.SetWsStatusAndConnection(true)
+ return nil
+}
+
+// disconnectFromWS disconnect to WS, but pass back error so test can handle it if needed
+func disconnectFromWS() error {
+ return o.Websocket.Shutdown()
+}
+
+// TestGetAccountCurrencies API endpoint test
+func TestGetAccountCurrencies(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetAccountCurrencies()
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetAccountWalletInformation API endpoint test
+func TestGetAccountWalletInformation(t *testing.T) {
+ TestSetDefaults(t)
+ resp, err := o.GetAccountWalletInformation("")
+ if areTestAPIKeysSet() {
+ if err != nil {
+ t.Error(err)
+ }
+ if len(resp) == 0 {
+ t.Error("No wallets returned")
+ }
+ } else if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+}
+
+// TestGetAccountWalletInformationForCurrency API endpoint test
+func TestGetAccountWalletInformationForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ resp, err := o.GetAccountWalletInformation(symbol.BTC)
+ if areTestAPIKeysSet() {
+ if err != nil {
+ t.Error(err)
+ }
+ if len(resp) != 1 {
+ t.Errorf("Error receiving wallet information for currency: %v", symbol.BTC)
+ }
+ } else if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+}
+
+// TestTransferAccountFunds API endpoint test
+func TestTransferAccountFunds(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ request := okgroup.TransferAccountFundsRequest{
+ Amount: 10,
+ Currency: symbol.BTC,
+ From: 6,
+ To: 1,
+ }
+ _, err := o.TransferAccountFunds(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestBaseWithdraw API endpoint test
+func TestAccountWithdrawRequest(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ request := okgroup.AccountWithdrawRequest{
+ Amount: 10,
+ Currency: symbol.BTC,
+ TradePwd: "1234",
+ Destination: 4,
+ ToAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
+ Fee: 1,
+ }
+ _, err := o.AccountWithdraw(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetAccountWithdrawalFee API endpoint test
+func TestGetAccountWithdrawalFee(t *testing.T) {
+ TestSetDefaults(t)
+ resp, err := o.GetAccountWithdrawalFee("")
+ if areTestAPIKeysSet() {
+ if err != nil {
+ t.Error(err)
+ }
+ if len(resp) == 0 {
+ t.Error("Expected fees")
+ }
+ } else if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+}
+
+// TestGetWithdrawalFeeForCurrency API endpoint test
+func TestGetAccountWithdrawalFeeForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ resp, err := o.GetAccountWithdrawalFee(symbol.BTC)
+ if areTestAPIKeysSet() {
+ if err != nil {
+ t.Error(err)
+ }
+ if len(resp) != 1 {
+ t.Error("Expected fee for one currency")
+ }
+ } else if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+}
+
+// TestGetAccountWithdrawalHistory API endpoint test
+func TestGetAccountWithdrawalHistory(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetAccountWithdrawalHistory("")
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetAccountWithdrawalHistoryForCurrency API endpoint test
+func TestGetAccountWithdrawalHistoryForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetAccountWithdrawalHistory(symbol.BTC)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetAccountBillDetails API endpoint test
+func TestGetAccountBillDetails(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetAccountBillDetails(okgroup.GetAccountBillDetailsRequest{})
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetAccountDepositAddressForCurrency API endpoint test
+func TestGetAccountDepositAddressForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetAccountDepositAddressForCurrency(symbol.BTC)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetAccountDepositHistory API endpoint test
+func TestGetAccountDepositHistory(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetAccountDepositHistory("")
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetAccountDepositHistoryForCurrency API endpoint test
+func TestGetAccountDepositHistoryForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetAccountDepositHistory(symbol.BTC)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSpotTradingAccounts API endpoint test
+func TestGetSpotTradingAccounts(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetSpotTradingAccounts()
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSpotTradingAccountsForCurrency API endpoint test
+func TestGetSpotTradingAccountsForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetSpotTradingAccountForCurrency(symbol.BTC)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSpotBillDetailsForCurrency API endpoint test
+func TestGetSpotBillDetailsForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ request := okgroup.GetSpotBillDetailsForCurrencyRequest{
+ Currency: symbol.BTC,
+ Limit: 100,
+ }
+ _, err := o.GetSpotBillDetailsForCurrency(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSpotBillDetailsForCurrencyBadLimit API logic test
+func TestGetSpotBillDetailsForCurrencyBadLimit(t *testing.T) {
+ TestSetDefaults(t)
+ request := okgroup.GetSpotBillDetailsForCurrencyRequest{
+ Currency: symbol.BTC,
+ Limit: -1,
+ }
+ _, err := o.GetSpotBillDetailsForCurrency(request)
+ if areTestAPIKeysSet() && err == nil {
+ t.Errorf("Expecting an error when invalid request sent")
+ }
+}
+
+// TestPlaceSpotOrderLimit API endpoint test
+func TestPlaceSpotOrderLimit(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ request := okgroup.PlaceSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ Type: "limit",
+ Side: "buy",
+ MarginTrading: "1",
+ Price: "100",
+ Size: "100",
+ }
+
+ _, err := o.PlaceSpotOrder(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestPlaceSpotOrderMarket API endpoint test
+func TestPlaceSpotOrderMarket(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ request := okgroup.PlaceSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ Type: "market",
+ Side: "buy",
+ MarginTrading: "1",
+ Size: "100",
+ Notional: "100",
+ }
+
+ _, err := o.PlaceSpotOrder(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestPlaceMultipleSpotOrders API endpoint test
+func TestPlaceMultipleSpotOrders(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ order := okgroup.PlaceSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ Type: "market",
+ Side: "buy",
+ MarginTrading: "1",
+ Size: "100",
+ Notional: "100",
+ }
+
+ request := []okgroup.PlaceSpotOrderRequest{
+ order,
+ }
+
+ _, errs := o.PlaceMultipleSpotOrders(request)
+ if len(errs) > 0 {
+ testStandardErrorHandling(t, errs[0])
+ }
+}
+
+// TestPlaceMultipleSpotOrdersOverCurrencyLimits API logic test
+func TestPlaceMultipleSpotOrdersOverCurrencyLimits(t *testing.T) {
+ TestSetDefaults(t)
+ order := okgroup.PlaceSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ Type: "market",
+ Side: "buy",
+ MarginTrading: "1",
+ Size: "100",
+ Notional: "100",
+ }
+
+ request := []okgroup.PlaceSpotOrderRequest{
+ order,
+ order,
+ order,
+ order,
+ order,
+ }
+
+ _, errs := o.PlaceMultipleSpotOrders(request)
+ if errs[0].Error() != "maximum 4 orders for each pair" {
+ t.Error("Expecting an error when more than 4 orders for a pair supplied", errs[0])
+ }
+}
+
+// TestPlaceMultipleSpotOrdersOverPairLimits API logic test
+func TestPlaceMultipleSpotOrdersOverPairLimits(t *testing.T) {
+ TestSetDefaults(t)
+ order := okgroup.PlaceSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ Type: "market",
+ Side: "buy",
+ MarginTrading: "1",
+ Size: "100",
+ Notional: "100",
+ }
+
+ request := []okgroup.PlaceSpotOrderRequest{
+ order,
+ }
+
+ order.InstrumentID = pair.NewCurrencyPairWithDelimiter(symbol.LTC, symbol.USD, "-").Pair().Lower().String()
+ request = append(request, order)
+ order.InstrumentID = pair.NewCurrencyPairWithDelimiter(symbol.DOGE, symbol.USD, "-").Pair().Lower().String()
+ request = append(request, order)
+ order.InstrumentID = pair.NewCurrencyPairWithDelimiter(symbol.XMR, symbol.USD, "-").Pair().Lower().String()
+ request = append(request, order)
+ order.InstrumentID = pair.NewCurrencyPairWithDelimiter(symbol.BCH, symbol.USD, "-").Pair().Lower().String()
+ request = append(request, order)
+
+ _, errs := o.PlaceMultipleSpotOrders(request)
+ if errs[0].Error() != "up to 4 trading pairs" {
+ t.Error("Expecting an error when more than 4 trading pairs supplied", errs[0])
+ }
+}
+
+// TestCancelSpotOrder API endpoint test
+func TestCancelSpotOrder(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ request := okgroup.CancelSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ OrderID: 1234,
+ }
+
+ _, err := o.CancelSpotOrder(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestCancelMultipleSpotOrders API endpoint test
+func TestCancelMultipleSpotOrders(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ request := okgroup.CancelMultipleSpotOrdersRequest{
+ InstrumentID: spotCurrency,
+ OrderIDs: []int64{1, 2, 3, 4},
+ }
+
+ cancellations, err := o.CancelMultipleSpotOrders(request)
+ testStandardErrorHandling(t, err)
+ for _, cancellationsPerCurrency := range cancellations {
+ for _, cancellation := range cancellationsPerCurrency {
+ if !cancellation.Result {
+ t.Error(cancellation.Error)
+ }
+ }
+ }
+}
+
+// TestCancelMultipleSpotOrdersOverCurrencyLimits API logic test
+func TestCancelMultipleSpotOrdersOverCurrencyLimits(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ request := okgroup.CancelMultipleSpotOrdersRequest{
+ InstrumentID: spotCurrency,
+ OrderIDs: []int64{1, 2, 3, 4, 5},
+ }
+
+ _, err := o.CancelMultipleSpotOrders(request)
+ if err.Error() != "maximum 4 order cancellations for each pair" {
+ t.Error("Expecting an error when more than 4 orders for a pair supplied", err)
+ }
+}
+
+// TestGetSpotOrders API endpoint test
+func TestGetSpotOrders(t *testing.T) {
+ TestSetDefaults(t)
+ request := okgroup.GetSpotOrdersRequest{
+ InstrumentID: spotCurrency,
+ Status: "all",
+ }
+ _, err := o.GetSpotOrders(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSpotOpenOrders API endpoint test
+func TestGetSpotOpenOrders(t *testing.T) {
+ TestSetDefaults(t)
+ request := okgroup.GetSpotOpenOrdersRequest{}
+ _, err := o.GetSpotOpenOrders(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSpotOrder API endpoint test
+func TestGetSpotOrder(t *testing.T) {
+ TestSetDefaults(t)
+ request := okgroup.GetSpotOrderRequest{
+ OrderID: "-1234",
+ InstrumentID: pair.NewCurrencyPairWithDelimiter(symbol.BTC, symbol.USD, "-").Pair().Upper().String(),
+ }
+ _, err := o.GetSpotOrder(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSpotTransactionDetails API endpoint test
+func TestGetSpotTransactionDetails(t *testing.T) {
+ TestSetDefaults(t)
+ request := okgroup.GetSpotTransactionDetailsRequest{
+ OrderID: 1234,
+ InstrumentID: spotCurrency,
+ }
+ _, err := o.GetSpotTransactionDetails(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSpotTokenPairDetails API endpoint test
+func TestGetSpotTokenPairDetails(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetSpotTokenPairDetails()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetSpotOrderBook API endpoint test
+func TestGetSpotOrderBook(t *testing.T) {
+ TestSetDefaults(t)
+ request := okgroup.GetSpotOrderBookRequest{
+ InstrumentID: spotCurrency,
+ }
+ _, err := o.GetSpotOrderBook(request)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetSpotAllTokenPairsInformation API endpoint test
+func TestGetSpotAllTokenPairsInformation(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetSpotAllTokenPairsInformation()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetSpotAllTokenPairsInformationForCurrency API endpoint test
+func TestGetSpotAllTokenPairsInformationForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetSpotAllTokenPairsInformationForCurrency(spotCurrency)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetSpotFilledOrdersInformation API endpoint test
+func TestGetSpotFilledOrdersInformation(t *testing.T) {
+ TestSetDefaults(t)
+ request := okgroup.GetSpotFilledOrdersInformationRequest{
+ InstrumentID: spotCurrency,
+ }
+ _, err := o.GetSpotFilledOrdersInformation(request)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetSpotMarketData API endpoint test
+func TestGetSpotMarketData(t *testing.T) {
+ TestSetDefaults(t)
+ request := okgroup.GetSpotMarketDataRequest{
+ InstrumentID: spotCurrency,
+ Granularity: 604800,
+ }
+ _, err := o.GetSpotMarketData(request)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetMarginTradingAccounts API endpoint test
+func TestGetMarginTradingAccounts(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetMarginTradingAccounts()
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetMarginTradingAccountsForCurrency API endpoint test
+func TestGetMarginTradingAccountsForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetMarginTradingAccountsForCurrency(spotCurrency)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetMarginBillDetails API endpoint test
+func TestGetMarginBillDetails(t *testing.T) {
+ TestSetDefaults(t)
+ request := okgroup.GetMarginBillDetailsRequest{
+ InstrumentID: spotCurrency,
+ Limit: 100,
+ }
+ _, err := o.GetMarginBillDetails(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetMarginAccountSettings API endpoint test
+func TestGetMarginAccountSettings(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetMarginAccountSettings("")
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetMarginAccountSettingsForCurrency API endpoint test
+func TestGetMarginAccountSettingsForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetMarginAccountSettings(spotCurrency)
+ testStandardErrorHandling(t, err)
+}
+
+// TestOpenMarginLoan API endpoint test
+func TestOpenMarginLoan(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ request := okgroup.OpenMarginLoanRequest{
+ Amount: 100,
+ InstrumentID: spotCurrency,
+ QuoteCurrency: symbol.USD,
+ }
+
+ _, err := o.OpenMarginLoan(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestRepayMarginLoan API endpoint test
+func TestRepayMarginLoan(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ request := okgroup.RepayMarginLoanRequest{
+ Amount: 100,
+ InstrumentID: spotCurrency,
+ QuoteCurrency: symbol.USD,
+ BorrowID: 1,
+ }
+
+ _, err := o.RepayMarginLoan(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestPlaceMarginOrderLimit API endpoint test
+func TestPlaceMarginOrderLimit(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ request := okgroup.PlaceSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ Type: "limit",
+ Side: "buy",
+ MarginTrading: "2",
+ Price: "100",
+ Size: "100",
+ }
+
+ _, err := o.PlaceMarginOrder(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestPlaceMarginOrderMarket API endpoint test
+func TestPlaceMarginOrderMarket(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ request := okgroup.PlaceSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ Type: "market",
+ Side: "buy",
+ MarginTrading: "2",
+ Size: "100",
+ Notional: "100",
+ }
+
+ _, err := o.PlaceMarginOrder(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestPlaceMultipleMarginOrders API endpoint test
+func TestPlaceMultipleMarginOrders(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ order := okgroup.PlaceSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ Type: "market",
+ Side: "buy",
+ MarginTrading: "1",
+ Size: "100",
+ Notional: "100",
+ }
+
+ request := []okgroup.PlaceSpotOrderRequest{
+ order,
+ }
+
+ _, errs := o.PlaceMultipleMarginOrders(request)
+ if len(errs) > 0 {
+ testStandardErrorHandling(t, errs[0])
+ }
+}
+
+// TestPlaceMultipleMarginOrdersOverCurrencyLimits API logic test
+func TestPlaceMultipleMarginOrdersOverCurrencyLimits(t *testing.T) {
+ TestSetDefaults(t)
+ order := okgroup.PlaceSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ Type: "market",
+ Side: "buy",
+ MarginTrading: "1",
+ Size: "100",
+ Notional: "100",
+ }
+
+ request := []okgroup.PlaceSpotOrderRequest{
+ order,
+ order,
+ order,
+ order,
+ order,
+ }
+
+ _, errs := o.PlaceMultipleMarginOrders(request)
+ if errs[0].Error() != "maximum 4 orders for each pair" {
+ t.Error("Expecting an error when more than 4 orders for a pair supplied", errs[0])
+ }
+}
+
+// TestPlaceMultipleMarginOrdersOverPairLimits API logic test
+func TestPlaceMultipleMarginOrdersOverPairLimits(t *testing.T) {
+ TestSetDefaults(t)
+ order := okgroup.PlaceSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ Type: "market",
+ Side: "buy",
+ MarginTrading: "1",
+ Size: "100",
+ Notional: "100",
+ }
+
+ request := []okgroup.PlaceSpotOrderRequest{
+ order,
+ }
+
+ order.InstrumentID = pair.NewCurrencyPairWithDelimiter(symbol.LTC, symbol.USD, "-").Pair().Lower().String()
+ request = append(request, order)
+ order.InstrumentID = pair.NewCurrencyPairWithDelimiter(symbol.DOGE, symbol.USD, "-").Pair().Lower().String()
+ request = append(request, order)
+ order.InstrumentID = pair.NewCurrencyPairWithDelimiter(symbol.XMR, symbol.USD, "-").Pair().Lower().String()
+ request = append(request, order)
+ order.InstrumentID = pair.NewCurrencyPairWithDelimiter(symbol.BCH, symbol.USD, "-").Pair().Lower().String()
+ request = append(request, order)
+
+ _, errs := o.PlaceMultipleMarginOrders(request)
+ if errs[0].Error() != "up to 4 trading pairs" {
+ t.Error("Expecting an error when more than 4 trading pairs supplied", errs[0])
+ }
+}
+
+// TestCancelMarginOrder API endpoint test
+func TestCancelMarginOrder(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ request := okgroup.CancelSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ OrderID: 1234,
+ }
+
+ _, err := o.CancelMarginOrder(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestCancelMultipleMarginOrders API endpoint test
+func TestCancelMultipleMarginOrders(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ request := okgroup.CancelMultipleSpotOrdersRequest{
+ InstrumentID: spotCurrency,
+ OrderIDs: []int64{1, 2, 3, 4},
+ }
+
+ _, errs := o.CancelMultipleMarginOrders(request)
+ if len(errs) > 0 {
+ testStandardErrorHandling(t, errs[0])
+ }
+}
+
+// TestCancelMultipleMarginOrdersOverCurrencyLimits API logic test
+func TestCancelMultipleMarginOrdersOverCurrencyLimits(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ request := okgroup.CancelMultipleSpotOrdersRequest{
+ InstrumentID: spotCurrency,
+ OrderIDs: []int64{1, 2, 3, 4, 5},
+ }
+
+ _, errs := o.CancelMultipleMarginOrders(request)
+ if errs[0].Error() != "maximum 4 order cancellations for each pair" {
+ t.Error("Expecting an error when more than 4 orders for a pair supplied", errs[0])
+ }
+}
+
+// TestGetMarginOrders API endpoint test
+func TestGetMarginOrders(t *testing.T) {
+ TestSetDefaults(t)
+ request := okgroup.GetSpotOrdersRequest{
+ InstrumentID: spotCurrency,
+ Status: "all",
+ }
+ _, err := o.GetMarginOrders(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetMarginOpenOrders API endpoint test
+func TestGetMarginOpenOrders(t *testing.T) {
+ TestSetDefaults(t)
+ request := okgroup.GetSpotOpenOrdersRequest{}
+ _, err := o.GetMarginOpenOrders(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetMarginOrder API endpoint test
+func TestGetMarginOrder(t *testing.T) {
+ TestSetDefaults(t)
+ request := okgroup.GetSpotOrderRequest{
+ OrderID: "1234",
+ InstrumentID: pair.NewCurrencyPairWithDelimiter(symbol.BTC, symbol.USD, "-").Pair().Upper().String(),
+ }
+ _, err := o.GetMarginOrder(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetMarginTransactionDetails API endpoint test
+func TestGetMarginTransactionDetails(t *testing.T) {
+ TestSetDefaults(t)
+ request := okgroup.GetSpotTransactionDetailsRequest{
+ OrderID: 1234,
+ InstrumentID: spotCurrency,
+ }
+ _, err := o.GetMarginTransactionDetails(request)
+ testStandardErrorHandling(t, err)
+}
+
+// Websocket tests ----------------------------------------------------------------------------------------------
+
+// TestWsLogin API endpoint test
+func TestWsLogin(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ if o.WebsocketConn == nil {
+ o.Websocket.Shutdown()
+ err := setupWSConnection()
+ if err != nil {
+ t.Error(err)
+ }
+ }
+ if !o.Websocket.IsConnected() {
+ t.Skip()
+ }
+ err := o.WsLogin()
+ if err != nil {
+ t.Error(err)
+ }
+ var errorReceived bool
+ for i := 0; i < 5; i++ {
+ response := <-o.Websocket.DataHandler
+ if err, ok := response.(error); ok && err != nil {
+ errorReceived = true
+ }
+ }
+ if errorReceived {
+ t.Error("Expecting no errors")
+ }
+}
+
+// TestSubscribeToChannel API endpoint test
+func TestSubscribeToChannel(t *testing.T) {
+ defer disconnectFromWS()
+ TestSetWsDefaults(t)
+ if o.WebsocketConn == nil {
+ o.Websocket.Shutdown()
+ err := setupWSConnection()
+ if err != nil {
+ t.Error(err)
+ }
+ }
+ if !o.Websocket.IsConnected() {
+ t.Skip()
+ }
+ channelName := "spot/depth:LTC-BTC"
+ err := o.WsSubscribeToChannel(channelName)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ var errorReceived bool
+ for i := 0; i < 5; i++ {
+ response := <-o.Websocket.DataHandler
+ if err, ok := response.(error); ok && err != nil {
+ t.Log(response)
+ if strings.Contains(response.(error).Error(), channelName) {
+ errorReceived = true
+ }
+ }
+ }
+ if errorReceived {
+ t.Error("Expecting subscription to channel")
+ }
+}
+
+// TestSubscribeToNonExistantChannel Logic test
+// Attempts to subscribe to a channel that doesn't exist
+// Then captures the error response
+func TestSubscribeToNonExistantChannel(t *testing.T) {
+ defer disconnectFromWS()
+ TestSetWsDefaults(t)
+ if o.WebsocketConn == nil {
+ o.Websocket.Shutdown()
+ err := setupWSConnection()
+ if err != nil {
+ t.Error(err)
+ }
+ }
+ if !o.Websocket.IsConnected() {
+ t.Skip("Could not connect to websocket. Skipping")
+ }
+ channelName := "badChannel"
+ err := o.WsSubscribeToChannel(channelName)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ var errorReceived bool
+ for i := 0; i < 5; i++ {
+ response := <-o.Websocket.DataHandler
+ if err, ok := response.(error); ok && err != nil {
+ t.Log(response)
+ if strings.Contains(response.(error).Error(), channelName) {
+ errorReceived = true
+ }
+ }
+ }
+ if !errorReceived {
+ t.Error("Expecting OKEX error - 30040 message: Channel badChannel doesn't exist")
+ }
+}
+
+// TestGetAssetTypeFromTableName logic test
+func TestGetAssetTypeFromTableName(t *testing.T) {
+ str := "spot/candle300s:BTC-USDT"
+ spot := o.GetAssetTypeFromTableName(str)
+ if spot != "SPOT" {
+ t.Errorf("Error, expected 'SPOT', received: '%v'", spot)
+ }
+}
+
+// TestGetWsChannelWithoutOrderType logic test
+func TestGetWsChannelWithoutOrderType(t *testing.T) {
+ TestSetDefaults(t)
+ str := "spot/depth5:BTC-USDT"
+ expected := "depth5"
+ resp := o.GetWsChannelWithoutOrderType(str)
+ if resp != expected {
+ t.Errorf("Logic change error %v should be %v", resp, expected)
+ }
+ str = "spot/depth"
+ resp = o.GetWsChannelWithoutOrderType(str)
+ expected = "depth"
+ if resp != expected {
+ t.Errorf("Logic change error %v should be %v", resp, expected)
+ }
+ str = "testWithBadData"
+ resp = o.GetWsChannelWithoutOrderType(str)
+ if resp != str {
+ t.Errorf("Logic change error %v should be %v", resp, str)
+ }
+}
+
+// TestOrderBookUpdateChecksumCalculator logic test
+func TestOrderBookUpdateChecksumCalculator(t *testing.T) {
+ TestSetDefaults(t)
+ if o.WebsocketConn == nil {
+ o.Websocket.Shutdown()
+ err := setupWSConnection()
+ if err != nil {
+ t.Error(err)
+ }
+ }
+ disconnectFromWS()
+ original := `{"table":"spot/depth","action":"partial","data":[{"instrument_id":"BTC-USDT","asks":[["3864.6786","0.145",1],["3864.7682","0.005",1],["3864.9851","0.57",1],["3864.9852","0.30137754",1],["3864.9986","2.81818419",1],["3864.9995","0.002",1],["3865","0.0597",1],["3865.0309","0.4",1],["3865.1995","0.004",1],["3865.3995","0.004",1],["3865.5995","0.004",1],["3865.7995","0.004",1],["3865.9995","0.004",1],["3866.0961","0.25865886",1],["3866.1995","0.004",1],["3866.3995","0.004",1],["3866.4004","0.3243",2],["3866.5995","0.004",1],["3866.7633","0.44247086",1],["3866.7995","0.004",1],["3866.9197","0.511",1],["3867.256","0.51716256",1],["3867.3951","0.02588112",1],["3867.4014","0.025",1],["3867.4566","0.02499999",1],["3867.4675","4.01155057",5],["3867.5515","1.1",1],["3867.6113","0.009",1],["3867.7349","0.026",1],["3867.7781","0.03738652",1],["3867.9163","0.0521",1],["3868.0381","0.34354941",1],["3868.0436","0.051",1],["3868.0657","0.90552172",3],["3868.1819","0.03863346",1],["3868.2013","0.194",1],["3868.346","0.051",1],["3868.3863","0.01155",1],["3868.7716","0.009",1],["3868.947","0.025",1],["3868.98","0.001",1],["3869.0764","1.03487931",1],["3869.2773","0.07724578",1],["3869.4039","0.025",1],["3869.4068","1.03",1],["3869.7068","2.06976398",1],["3870","0.5",1],["3870.0465","0.01",1],["3870.7042","0.02099651",1],["3870.9451","2.07047375",1],["3871.5254","1.2",1],["3871.5596","0.001",1],["3871.6605","0.01035032",1],["3871.7179","2.07047375",1],["3871.8816","0.51751625",1],["3872.1","0.75",1],["3872.2464","0.0646",1],["3872.3747","0.283",1],["3872.4039","0.2",1],["3872.7655","0.23179307",1],["3872.8005","2.06976398",1],["3873.1509","2",1],["3873.3215","0.26",1],["3874.1392","0.001",1],["3874.1487","3.88224364",4],["3874.1685","1.8",1],["3874.5571","0.08974762",1],["3874.734","2.06976398",1],["3874.99","0.3",1],["3875","1.001",2],["3875.0041","1.03505051",1],["3875.45","0.3",1],["3875.4766","0.15",1],["3875.7057","0.51751625",1],["3876","0.001",1],["3876.68","0.3",1],["3876.7188","0.001",1],["3877","0.75",1],["3877.31","0.035",1],["3877.38","0.3",1],["3877.7","0.3",1],["3877.88","0.3",1],["3878.0364","0.34770122",1],["3878.4525","0.48579748",1],["3878.4955","0.02812511",1],["3878.8855","0.00258579",1],["3878.9605","0.895",1],["3879","0.001",1],["3879.2984","0.002",2],["3879.432","0.001",1],["3879.6313","6",1],["3879.9999","0.002",2],["3880","1.25132834",5],["3880.2526","0.04075162",1],["3880.7145","0.0647",1],["3881.2469","1.883",1],["3881.878","0.002",2],["3884.4576","0.002",2],["3885","0.002",2],["3885.2233","0.28304103",1],["3885.7416","18",1],["3886","0.001",1],["3886.1554","5.4",1],["3887","0.001",1],["3887.0372","0.002",2],["3887.2559","0.05214011",1],["3887.9238","0.0019",1],["3888","0.15810538",4],["3889","0.001",1],["3889.5175","0.50510653",1],["3889.6168","0.002",2],["3889.9999","0.001",1],["3890","2.34968109",4],["3890.5222","0.00257806",1],["3891.2659","5",1],["3891.9999","0.00893897",1],["3892.1964","0.002",2],["3892.4358","0.0176",1],["3893.1388","1.4279",1],["3894","0.0026321",1],["3894.776","0.001",1],["3895","1.501",2],["3895.379","0.25881288",1],["3897","0.05",1],["3897.3556","0.001",1],["3897.8432","0.73708079",1],["3898","3.31353018",7],["3898.4462","4.757",1],["3898.6","0.47159638",1],["3898.8769","0.0129",1],["3899","6",2],["3899.6516","0.025",1],["3899.9352","0.001",1],["3899.9999","0.013",2],["3900","22.37447743",24],["3900.9999","0.07763916",1],["3901","0.10192487",1],["3902.1937","0.00257034",1],["3902.3991","1.5532141",1],["3902.5148","0.001",1],["3904","1.49331984",1],["3904.9999","0.95905447",1],["3905","0.501",2],["3905.0944","0.001",1],["3905.61","0.099",1],["3905.6801","0.54343686",1],["3906.2901","0.0258",1],["3907.674","0.001",1],["3907.85","1.35778084",1],["3908","0.03846153",1],["3908.23","1.95189531",1],["3908.906","0.03148978",1],["3909","0.001",1],["3909.9999","0.01398721",2],["3910","0.016",2],["3910.2536","0.001",1],["3912.5406","0.88270517",1],["3912.8332","0.001",1],["3913","1.2640608",1],["3913.87","1.69114184",1],["3913.9003","0.00256266",1],["3914","1.21766411",1],["3915","0.001",1],["3915.4128","0.001",1],["3915.7425","6.848",1],["3916","0.0050949",1],["3917.36","1.28658296",1],["3917.9924","0.001",1],["3919","0.001",1],["3919.9999","0.001",1],["3920","1.21171832",3],["3920.0002","0.20217038",1],["3920.572","0.001",1],["3921","0.128",1],["3923.0756","0.00148064",1],["3923.1516","0.001",1],["3923.86","1.38831714",1],["3925","0.01867801",2],["3925.642","0.00255499",1],["3925.7312","0.001",1],["3926","0.04290757",1],["3927","0.023",1],["3927.3175","0.01212865",1],["3927.65","1.51375612",1],["3928","0.5",1],["3928.3108","0.001",1],["3929","0.001",1],["3929.9999","0.01519338",2],["3930","0.0174985",3],["3930.21","1.49335799",1],["3930.8904","0.001",1],["3932.2999","0.01953",1],["3932.8962","7.96",1],["3933.0387","11.808",1],["3933.47","0.001",1],["3934","1.40839932",1],["3935","0.001",1],["3936.8","0.62879518",1],["3937.23","1.56977841",1],["3937.4189","0.00254735",1]],"bids":[["3864.5217","0.00540709",1],["3864.5216","0.14068758",2],["3864.2275","0.01033576",1],["3864.0989","0.00825047",1],["3864.0273","0.38",1],["3864.0272","0.4",1],["3863.9957","0.01083539",1],["3863.9184","0.01653723",1],["3863.8282","0.25588165",1],["3863.8153","0.154",1],["3863.7791","1.14122492",1],["3863.6866","0.01733662",1],["3863.6093","0.02645958",1],["3863.3775","0.02773862",1],["3863.0297","0.513",1],["3863.0286","1.1028564",2],["3862.8489","0.01",1],["3862.5972","0.01890179",1],["3862.3431","0.01152944",1],["3862.313","0.009",1],["3862.2445","0.90551002",3],["3862.0734","0.014",1],["3862.0539","0.64976067",1],["3861.8586","0.025",1],["3861.7888","0.025",1],["3861.7673","0.008",1],["3861.5785","0.01",1],["3861.3895","0.005",1],["3861.3338","0.25875855",1],["3861.161","0.01",1],["3861.1111","0.03863352",1],["3861.0732","0.51703882",1],["3860.9116","0.17754895",1],["3860.75","0.19",1],["3860.6554","0.015",1],["3860.6172","0.005",1],["3860.6088","0.008",1],["3860.4724","0.12940042",1],["3860.4424","0.25880084",1],["3860.42","0.01",1],["3860.3725","0.51760102",1],["3859.8449","0.005",1],["3859.8285","0.03738652",1],["3859.7638","0.07726703",1],["3859.4502","0.008",1],["3859.3772","0.05173471",1],["3859.3409","0.194",1],["3859","5",1],["3858.827","0.0521",1],["3858.8208","0.001",1],["3858.679","0.26",1],["3858.4814","0.07477305",1],["3858.1669","1.03503422",1],["3857.6005","0.006",1],["3857.4005","0.004",1],["3857.2005","0.004",1],["3857.1871","1.218",1],["3857.0005","0.004",1],["3856.8135","0.0646",1],["3856.8005","0.004",1],["3856.2412","0.001",1],["3856.2349","1.03503422",1],["3856.0197","0.01037339",1],["3855.8781","0.23178117",1],["3855.8005","0.004",1],["3855.7165","0.00259355",1],["3855.4858","0.25875855",1],["3854.4584","0.01",1],["3853.6616","0.001",1],["3853.1373","0.92",1],["3852.5072","0.48599702",1],["3851.3926","0.13008333",1],["3851.082","0.001",1],["3850.9317","2",1],["3850.6359","0.34770165",1],["3850.2058","0.51751624",1],["3850.0823","0.15",1],["3850.0042","0.5175171",1],["3850","0.001",1],["3849.6325","1.8",1],["3849.41","0.3",1],["3848.9686","1.85",1],["3848.7426","0.18511466",1],["3848.52","0.3",1],["3848.5024","0.001",1],["3848.42","0.3",1],["3848.1618","2.204",1],["3847.77","0.3",1],["3847.48","0.3",1],["3847.3581","2.05",1],["3846.8259","0.0646",1],["3846.59","0.3",1],["3846.49","0.3",1],["3845.9228","0.001",1],["3844.184","0.00260133",1],["3844.0092","6.3",1],["3843.3432","0.001",1],["3841","0.06300963",1],["3840.7636","0.001",1],["3840","0.201",3],["3839.7681","18",1],["3839.5328","0.05214011",1],["3838.184","0.001",1],["3837.2344","0.27589557",1],["3836.6479","5.2",1],["3836","2.37196773",3],["3835.6044","0.001",1],["3833.6053","0.25873556",1],["3833.0248","0.001",1],["3833","0.8726502",1],["3832.6859","0.00260913",1],["3832","0.007",1],["3831.637","6",1],["3831.0602","0.001",1],["3830.4452","0.001",1],["3830","0.20375718",4],["3829.7125","0.07833486",1],["3829.6283","0.3519681",1],["3829","0.0039261",1],["3827.8656","0.001",1],["3826.0001","0.53251232",1],["3826","0.0509",1],["3825.7834","0.00698562",1],["3825.286","0.001",1],["3823.0001","0.03010127",1],["3822.8014","0.00261588",1],["3822.7064","0.001",1],["3822.2","1",1],["3822.1121","0.35994101",1],["3821.2222","0.00261696",1],["3821","0.001",1],["3820.1268","0.001",1],["3820","1.12992803",4],["3819","0.01331195",2],["3817.5472","0.001",1],["3816","1.13807184",2],["3815.8343","0.32463428",1],["3815.7834","0.00525295",1],["3815","28.99386799",4],["3814.9676","0.001",1],["3813","0.91303023",4],["3812.388","0.002",2],["3811.2257","0.07",1],["3810","0.32573997",2],["3809.8084","0.001",1],["3809.7928","0.00262481",1],["3807.2288","0.001",1],["3806.8421","0.07003461",1],["3806","0.19",1],["3805.8041","0.05678805",1],["3805","1.01",2],["3804.6492","0.001",1],["3804.3551","0.1",1],["3803","0.005",1],["3802.22","2.05042631",1],["3802.0696","0.001",1],["3802","1.63290092",1],["3801.2257","0.07",1],["3801","57.4",3],["3800.9853","0.02492278",1],["3800.8421","0.06503533",1],["3800.7844","0.02812628",1],["3800.0001","0.00409473",1],["3800","17.91401074",15],["3799.49","0.001",1],["3799","0.1",1],["3796.9104","0.001",1],["3796","9.00128053",2],["3795.5441","0.0028",1],["3794.3308","0.001",1],["3791","55",1],["3790.7777","0.07",1],["3790","12.03238184",7],["3789","1",1],["3788","0.21110454",2],["3787.2959","9",1],["3786.592","0.001",1],["3786","9.01916822",2],["3785","12.87914268",5],["3784.0124","0.001",1],["3781.4328","0.002",2],["3781","56.3",2],["3780.7777","0.07",1],["3780","23.41537654",10],["3778.8532","0.002",2],["3776","9",1],["3774","0.003",1],["3772.2481","0.06901672",1],["3771","55.1",2],["3770.7777","0.07",1],["3770","7.30268416",5],["3769","0.25",1],["3768","1.3725",3],["3766.66","0.02",1],["3766","7.64837924",2],["3765.58","1.22775492",1],["3762.58","1.22873383",1],["3761","51.68262164",1],["3760.8031","0.0399",1],["3760.7777","0.07",1]],"timestamp":"2019-03-06T23:19:17.705Z","checksum":-1785549915}]}`
+ update := `{"table":"spot/depth","action":"update","data":[{"instrument_id":"BTC-USDT","asks":[["3864.6786","0",0],["3864.9852","0",0],["3865.9994","0.48402971",1],["3866.4004","0.001",1],["3866.7995","0.3273",2],["3867.4566","0",0],["3867.7031","0.025",1],["3868.0436","0",0],["3868.346","0",0],["3868.3695","0.051",1],["3870.9243","0.642",1],["3874.9942","0.51751796",1],["3875.7057","0",0],["3939","0.001",1]],"bids":[["3864.55","0.0565449",1],["3863.8282","0",0],["3863.8153","0",0],["3863.7898","0.01320077",1],["3863.4807","0.02112123",1],["3863.3002","0.04233533",1],["3863.1717","0.03379397",1],["3863.0685","0.04438179",1],["3863.0286","0.7362564",1],["3862.9912","0.06773651",1],["3862.8626","0.05407035",1],["3862.7595","0.07101087",1],["3862.313","0.3756",2],["3862.1848","0.012",1],["3862.0734","0",0],["3861.8391","0.025",1],["3861.7888","0",0],["3856.6716","0.38893641",1],["3768","0",0],["3766.66","0",0],["3766","0",0],["3765.58","0",0],["3762.58","0",0],["3761","0",0],["3760.8031","0",0],["3760.7777","0",0]],"timestamp":"2019-03-06T23:19:18.239Z","checksum":-1587788848}]}`
+ var dataResponse okgroup.WebsocketDataResponse
+ err := common.JSONDecode([]byte(original), &dataResponse)
+ if err != nil {
+ t.Error(err)
+ }
+ err = o.WsProcessOrderBook(&dataResponse)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ var updateResponse okgroup.WebsocketDataResponse
+ err = common.JSONDecode([]byte(update), &updateResponse)
+ if err != nil {
+ t.Error(err)
+ }
+ time.Sleep(2 * time.Second)
+ err = o.WsProcessOrderBook(&updateResponse)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestOrderBookUpdateChecksumCalculatorWithDash logic test
+func TestOrderBookUpdateChecksumCalculatorWith8DecimalPlaces(t *testing.T) {
+ TestSetDefaults(t)
+ if o.WebsocketConn == nil {
+ o.Websocket.Shutdown()
+ err := setupWSConnection()
+ if err != nil {
+ t.Error(err)
+ }
+ }
+ disconnectFromWS()
+ original := `{"table":"spot/depth","action":"partial","data":[{"instrument_id":"WAVES-BTC","asks":[["0.000714","1.15414979",1],["0.000715","3.3",2],["0.000717","426.71348",2],["0.000719","140.84507042",1],["0.00072","590.77",1],["0.000721","991.77",1],["0.000724","0.3532032",1],["0.000725","58.82698567",1],["0.000726","1033.15469748",2],["0.000729","0.35320321",1],["0.00073","352.77",1],["0.000735","0.38469748",1],["0.000736","625.77",1],["0.00075191","152.44796961",1],["0.00075192","114.3359772",1],["0.00075193","85.7519829",1],["0.00075194","64.31398718",1],["0.00075195","48.23549038",1],["0.00075196","36.17661779",1],["0.00075199","61.04804253",1],["0.0007591","70.71318474",1],["0.0007621","53.03488855",1],["0.00076211","39.77616642",1],["0.00076212","29.83212481",1],["0.0007635","22.37409361",1],["0.00076351","29.36599786",2],["0.00076352","9.43907074",1],["0.00076353","7.07930306",1],["0.00076354","14.15860612",1],["0.00076355","3.53965153",1],["0.00076369","3.53965153",1],["0.0008","34.36841101",1],["0.00082858","1.69936503",1],["0.00083232","2.8",1],["0.00084","15.69220129",1],["0.00085","4.42785042",1],["0.00088","0.1",1],["0.000891","0.1",1],["0.0009","12.41486491",2],["0.00093","5",1],["0.0012","12.31486492",1],["0.00531314","6.91803114",1],["0.00799999","0.02",1],["0.0084","0.05989",1],["0.00931314","5.18852336",1],["0.0799999","0.02",1],["0.499","6.00423396",1],["0.5","0.4995",1],["0.799999","0.02",1],["4.99","2",1],["5","3.98583144",1],["7.99999999","0.02",1],["79.99999999","0.02",1],["799.99999999","0.02986704",1]],"bids":[["0.000709","222.91679881",3],["0.000703","0.47161952",1],["0.000701","140.73015789",2],["0.0007","0.3",1],["0.000699","401",1],["0.000698","232.61801667",2],["0.000689","0.71396896",1],["0.000688","0.69910125",1],["0.000613","227.54771052",1],["0.0005","0.01",1],["0.00026789","3.69905341",1],["0.000238","2.4",1],["0.00022","0.53",1],["0.0000055","374.09871696",1],["0.00000056","222",1],["0.00000055","736.84761363",1],["0.0000002","999",1],["0.00000009","1222.22222417",1],["0.00000008","20868.64520447",1],["0.00000002","110000",1],["0.00000001","10000",1]],"timestamp":"2019-03-12T22:22:42.274Z","checksum":1319037905}]}`
+ update := `{"table":"spot/depth","action":"update","data":[{"instrument_id":"WAVES-BTC","asks":[["0.000715","100.48199596",3],["0.000716","62.21679881",1]],"bids":[["0.000713","38.95772168",1]],"timestamp":"2019-03-12T22:22:42.938Z","checksum":-131160897}]}`
+ var dataResponse okgroup.WebsocketDataResponse
+ err := common.JSONDecode([]byte(original), &dataResponse)
+ if err != nil {
+ t.Error(err)
+ }
+ err = o.WsProcessOrderBook(&dataResponse)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ var updateResponse okgroup.WebsocketDataResponse
+ err = common.JSONDecode([]byte(update), &updateResponse)
+ if err != nil {
+ t.Error(err)
+ }
+ time.Sleep(2 * time.Second)
+ err = o.WsProcessOrderBook(&updateResponse)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestOrderBookPartialChecksumCalculator logic test
+func TestOrderBookPartialChecksumCalculator(t *testing.T) {
+ orderbookPartialJSON := `{"table":"spot/depth","action":"partial","data":[{"instrument_id":"EOS-USDT","asks":[["3.5196","0.1077",1],["3.5198","21.71",1],["3.5199","51.1805",1],["3.5208","75.09",1],["3.521","196.3333",1],["3.5213","0.1",1],["3.5218","39.276",2],["3.5219","395.6334",1],["3.522","27.956",1],["3.5222","404.9595",1],["3.5225","300",1],["3.5227","143.5442",2],["3.523","42.4746",1],["3.5231","852.64",2],["3.5235","34.9602",1],["3.5237","442.0918",2],["3.5238","352.8404",2],["3.5239","341.6759",2],["3.524","84.9493",1],["3.5241","148.4882",1],["3.5242","261.64",1],["3.5243","142.045",1],["3.5246","10",1],["3.5247","284.0788",1],["3.5248","720",1],["3.5249","89.2518",2],["3.5251","1201.8965",2],["3.5254","426.2938",1],["3.5255","213.0863",1],["3.5257","568.1576",1],["3.5258","0.3",1],["3.5259","34.4602",1],["3.526","0.1",1],["3.5263","850.771",1],["3.5265","5.9",1],["3.5268","10.5064",2],["3.5272","1136.8965",1],["3.5274","255.1481",1],["3.5276","29.5374",1],["3.5278","50",1],["3.5282","284.1797",1],["3.5283","1136.8965",1],["3.5284","0.4275",1],["3.5285","100",1],["3.5292","90.9",1],["3.5298","0.2",1],["3.5303","568.1576",1],["3.5305","279.9999",1],["3.532","0.409",1],["3.5321","568.1576",1],["3.5326","6016.8756",1],["3.5328","4.9849",1],["3.533","92.88",2],["3.5343","1200.2383",2],["3.5344","100",1],["3.535","359.7047",1],["3.5354","100",1],["3.5355","100",1],["3.5356","10",1],["3.5358","200",2],["3.5362","435.139",1],["3.5365","2152",1],["3.5366","284.1756",1],["3.5367","568.4644",1],["3.5369","33.9878",1],["3.537","337.1191",2],["3.5373","0.4045",1],["3.5383","1136.7188",1],["3.5386","12.1614",1],["3.5387","90.89",1],["3.54","4.54",1],["3.5423","90.8",1],["3.5436","0.1",1],["3.5454","853.4156",1],["3.5468","142.0656",1],["3.5491","0.0008",1],["3.55","14478.8206",6],["3.5537","21521",1],["3.5555","11.53",1],["3.5573","50.6001",1],["3.5599","4591.4221",1],["3.56","1227.0002",4],["3.5603","2670",1],["3.5608","58.6638",1],["3.5613","0.1",1],["3.5621","45.9473",1],["3.57","2141.7274",3],["3.5712","2956.9816",1],["3.5717","27.9978",1],["3.5718","0.9285",1],["3.5739","299.73",1],["3.5761","864",1],["3.579","22.5225",1],["3.5791","38.26",2],["3.58","7618.4634",5],["3.5801","457.2184",1],["3.582","24.5",1],["3.5822","1572.6425",1],["3.5845","14.1438",1],["3.585","527.169",1],["3.5865","20",1],["3.5867","4490",1],["3.5876","39.0493",1],["3.5879","392.9083",1],["3.5888","436.42",2],["3.5896","50",1],["3.59","2608.9128",8],["3.5913","19.5246",1],["3.5938","7082",1],["3.597","0.1",1],["3.5979","399",1],["3.5995","315.1509",1],["3.5999","2566.2648",1],["3.6","18511.2292",35],["3.603","22.3379",2],["3.605","499.5",1],["3.6055","100",1],["3.6058","499.5",1],["3.608","1021.1485",1],["3.61","11755.4596",13],["3.611","42.8571",1],["3.6131","6690",1],["3.6157","19.5247",1],["3.618","2500",1],["3.6197","525.7146",1],["3.6198","0.4455",1],["3.62","6440.6295",8],["3.6219","0.4175",1],["3.6237","168",1],["3.6265","0.1001",1],["3.628","64.9345",1],["3.63","4435.4985",6],["3.6308","1.7815",1],["3.6331","0.1",1],["3.6338","355.527",2],["3.6358","50",1],["3.6363","2074.7096",1],["3.6376","4000",1],["3.6396","11090",1],["3.6399","0.4055",1],["3.64","4161.9805",4],["3.6437","117.6524",1],["3.648","190",1],["3.6488","200",1],["3.65","11740.5045",25],["3.6512","0.1",1],["3.6521","728",1],["3.6555","100",1],["3.6598","36.6914",1],["3.66","4331.2148",6],["3.6638","200",1],["3.6673","100",1],["3.6679","38",1],["3.6688","2",1],["3.6695","0.1",1],["3.67","7984.698",6],["3.672","300",1],["3.6777","257.8247",1],["3.6789","393.4217",2],["3.68","9202.3222",11],["3.6818","500",1],["3.6823","299.7",1],["3.6839","422.3748",1],["3.685","100",1],["3.6878","0.1",1],["3.6888","72.0958",2],["3.6889","2876",1],["3.689","28",1],["3.6891","28",1],["3.6892","28",1],["3.6895","28",1],["3.6898","28",1],["3.69","643.96",7],["3.6908","118",2],["3.691","28",1],["3.6916","28",1],["3.6918","28",1],["3.6926","28",1],["3.6928","28",1],["3.6932","28",1],["3.6933","200",1],["3.6935","28",1],["3.6936","28",1],["3.6938","28",1],["3.694","28",1],["3.698","1498.5",1],["3.6988","2014.2004",2],["3.7","21904.2689",22],["3.7029","71.95",1],["3.704","3690.1362",1],["3.7055","100",1],["3.7063","0.1",1],["3.71","4421.3468",4],["3.719","17.3491",1],["3.72","1304.5995",3],["3.7211","10",1],["3.7248","0.1",1],["3.725","1900",1],["3.73","31.1785",2],["3.7375","38",1]],"bids":[["3.5182","151.5343",6],["3.5181","0.3691",1],["3.518","271.3967",2],["3.5179","257.8352",1],["3.5178","12.3811",1],["3.5173","34.1921",2],["3.5171","1013.8256",2],["3.517","272.1119",2],["3.5168","395.3376",1],["3.5166","317.1756",2],["3.5165","348.302",3],["3.5164","142.0414",1],["3.5163","96.8933",2],["3.516","600.1034",3],["3.5159","27.481",1],["3.5158","27.33",1],["3.5157","583.1898",2],["3.5156","24.6819",2],["3.5154","25",1],["3.5153","0.429",1],["3.5152","453.9204",3],["3.5151","2131.592",4],["3.515","335",3],["3.5149","37.1586",1],["3.5147","41.6759",1],["3.5146","54.569",1],["3.5145","70.3515",1],["3.5143","68.206",3],["3.5142","359.4538",2],["3.5139","45.4123",2],["3.5137","71.673",2],["3.5136","25",1],["3.5135","300",1],["3.5134","442.57",2],["3.5132","83.3518",1],["3.513","1245.2529",3],["3.5127","20",1],["3.512","284.1353",1],["3.5119","1136.8319",1],["3.5113","56.9351",1],["3.5111","588.1898",2],["3.5109","255.0946",1],["3.5105","48.65",1],["3.5103","50.2",1],["3.5098","720",1],["3.5096","148.95",1],["3.5094","570.5758",2],["3.509","2.386",1],["3.5089","0.4065",1],["3.5087","282.3859",2],["3.5086","145.036",2],["3.5084","2.386",1],["3.5082","90.98",1],["3.5081","2.386",1],["3.5079","2.386",1],["3.5078","857.6229",2],["3.5075","2.386",1],["3.5074","284.1877",1],["3.5073","100",1],["3.5071","100",1],["3.507","768.4159",3],["3.5069","313.0863",2],["3.5068","426.2938",1],["3.5066","568.3594",1],["3.5063","1136.6865",1],["3.5059","0.3",1],["3.5054","9.9999",1],["3.5053","0.2",1],["3.5051","392.428",1],["3.505","13.79",1],["3.5048","99.5497",2],["3.5047","78.5331",2],["3.5046","2153",1],["3.5041","5983.999",1],["3.5037","668.5682",1],["3.5036","160.5948",1],["3.5024","534.8075",1],["3.5014","28.5604",1],["3.5011","91",1],["3.5","1058.8771",2],["3.4997","50.2",1],["3.4985","3430.0414",1],["3.4949","232.0591",1],["3.4942","21521",1],["3.493","2",1],["3.4928","2",1],["3.4925","0.44",1],["3.4917","142.0656",1],["3.49","2051.8826",4],["3.488","280.7459",1],["3.4852","643.4038",1],["3.4851","86.0807",1],["3.485","213.2436",1],["3.484","0.1",1],["3.4811","144.3399",1],["3.4808","89",1],["3.4803","12.1999",1],["3.4801","2390",1],["3.48","930.8453",9],["3.4791","310",1],["3.4768","206",1],["3.4767","0.9415",1],["3.4754","1.4387",1],["3.4728","20",1],["3.4701","1219.2873",1],["3.47","1904.3139",7],["3.468","0.4035",1],["3.4667","0.1",1],["3.4666","3020.0101",1],["3.465","10",1],["3.464","0.4485",1],["3.462","2119.6556",1],["3.46","1305.6113",8],["3.4589","8.0228",1],["3.457","100",1],["3.456","70.3859",2],["3.4538","20",1],["3.4536","4323.9486",2],["3.4531","827.0427",1],["3.4528","0.439",1],["3.4522","8.0381",1],["3.4513","441.1873",1],["3.4512","50.707",1],["3.451","87.0902",1],["3.4509","200",1],["3.4506","100",1],["3.4505","86.4045",2],["3.45","12409.4595",28],["3.4494","0.5365",2],["3.449","10761",1],["3.4482","8.0476",1],["3.4469","0.449",1],["3.445","2000",1],["3.4427","14",1],["3.4421","100",1],["3.4416","8.0631",1],["3.4404","1",1],["3.44","4580.733",11],["3.4388","1868.2085",1],["3.438","937.7246",2],["3.4367","1500",1],["3.4366","62",1],["3.436","29.8743",1],["3.4356","25.4801",1],["3.4349","4.3086",1],["3.4343","43.2402",1],["3.433","2.0688",1],["3.4322","2.7335",2],["3.432","93.3233",1],["3.4302","328.8301",2],["3.43","4440.8158",11],["3.4288","754.574",2],["3.4283","125.7043",2],["3.428","744.3154",2],["3.4273","5460",1],["3.4258","50",1],["3.4255","109.005",1],["3.4248","100",1],["3.4241","129.2048",2],["3.4233","5.3598",1],["3.4228","4498.866",1],["3.4222","3.5435",1],["3.4217","404.3252",2],["3.4211","1000",1],["3.4208","31",1],["3.42","1834.024",9],["3.4175","300",1],["3.4162","400",1],["3.4152","0.1",1],["3.4151","4.3336",1],["3.415","1.5974",1],["3.414","1146",1],["3.4134","306.4246",1],["3.4129","7.5556",1],["3.4111","198.5188",1],["3.4109","500",1],["3.4106","4305",1],["3.41","2150.7635",13],["3.4085","4.342",1],["3.4054","5.6985",1],["3.4019","5.438",1],["3.4015","1010.846",1],["3.4009","8610",1],["3.4005","1.9122",1],["3.4004","1",1],["3.4","27081.1806",67],["3.3955","3.2682",1],["3.3953","5.4486",1],["3.3937","1591.3805",1],["3.39","3221.4155",8],["3.3899","3.2736",1],["3.3888","1500",2],["3.3887","5.4592",1],["3.385","117.0969",2],["3.3821","5.4699",1],["3.382","100.0529",1],["3.3818","172.0164",1],["3.3815","165.6288",1],["3.381","887.3115",1],["3.3808","100",1]],"timestamp":"2019-03-04T00:15:04.155Z","checksum":-2036653089}]}`
+ var dataResponse okgroup.WebsocketDataResponse
+ err := common.JSONDecode([]byte(orderbookPartialJSON), &dataResponse)
+ if err != nil {
+ t.Error(err)
+ }
+
+ calculatedChecksum := o.CalculatePartialOrderbookChecksum(&dataResponse.Data[0])
+ if calculatedChecksum != dataResponse.Data[0].Checksum {
+ t.Errorf("Expected %v, Receieved %v", dataResponse.Data[0].Checksum, calculatedChecksum)
+ }
+}
+
+// Function tests ----------------------------------------------------------------------------------------------
func setFeeBuilder() exchange.FeeBuilder {
return exchange.FeeBuilder{
Amount: 1,
@@ -52,23 +1054,14 @@ func setFeeBuilder() exchange.FeeBuilder {
}
}
-func TestGetSpotInstruments(t *testing.T) {
- t.Parallel()
- _, err := o.GetSpotInstruments()
- if err != nil {
- t.Errorf("Test failed - okcoin GetSpotInstruments() failed: %s", err)
- }
-}
-
func TestGetFee(t *testing.T) {
- o.SetDefaults()
+ TestSetDefaults(t)
var feeBuilder = setFeeBuilder()
// CryptocurrencyTradeFee Basic
if resp, err := o.GetFee(feeBuilder); resp != float64(0.0015) || err != nil {
t.Error(err)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0.0015), resp)
}
-
// CryptocurrencyTradeFee High quantity
feeBuilder = setFeeBuilder()
feeBuilder.Amount = 1000
@@ -77,15 +1070,13 @@ func TestGetFee(t *testing.T) {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(1500), resp)
t.Error(err)
}
-
// CryptocurrencyTradeFee IsMaker
feeBuilder = setFeeBuilder()
feeBuilder.IsMaker = true
- if resp, err := o.GetFee(feeBuilder); resp != float64(0.0005) || err != nil {
+ if resp, err := o.GetFee(feeBuilder); resp != float64(0.00100) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0.0005), resp)
t.Error(err)
}
-
// CryptocurrencyTradeFee Negative purchase price
feeBuilder = setFeeBuilder()
feeBuilder.PurchasePrice = -1000
@@ -93,23 +1084,6 @@ func TestGetFee(t *testing.T) {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Error(err)
}
- // CryptocurrencyWithdrawalFee Basic
- feeBuilder = setFeeBuilder()
- feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
- if resp, err := o.GetFee(feeBuilder); resp != float64(0.2) || err != nil {
- t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0.2), resp)
- t.Error(err)
- }
-
- // CryptocurrencyWithdrawalFee Invalid currency
- feeBuilder = setFeeBuilder()
- feeBuilder.FirstCurrency = "hello"
- feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
- if resp, err := o.GetFee(feeBuilder); resp != float64(0) || err != nil {
- t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
- t.Error(err)
- }
-
// CyptocurrencyDepositFee Basic
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.CyptocurrencyDepositFee
@@ -117,7 +1091,6 @@ func TestGetFee(t *testing.T) {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Error(err)
}
-
// InternationalBankDepositFee Basic
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.InternationalBankDepositFee
@@ -125,80 +1098,31 @@ func TestGetFee(t *testing.T) {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Error(err)
}
-
// InternationalBankWithdrawalFee Basic
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee
feeBuilder.CurrencyItem = symbol.USD
- if resp, err := o.GetFee(feeBuilder); resp != float64(15) || err != nil {
- t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(15), resp)
+ if resp, err := o.GetFee(feeBuilder); resp != float64(0) || err != nil {
+ t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Error(err)
}
}
+// TestFormatWithdrawPermissions helper test
func TestFormatWithdrawPermissions(t *testing.T) {
- o.SetDefaults()
- expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText
-
+ TestSetDefaults(t)
+ expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText
withdrawPermissions := o.FormatWithdrawPermissions()
-
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
}
}
-func TestGetActiveOrders(t *testing.T) {
- o.SetDefaults()
- TestSetup(t)
-
- var getOrdersRequest = exchange.GetOrdersRequest{
- OrderType: exchange.AnyOrderType,
- Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.LTC, symbol.BTC)},
- }
-
- _, err := o.GetActiveOrders(getOrdersRequest)
- if areTestAPIKeysSet() && err != nil {
- t.Errorf("Could not get open orders: %s", err)
- } else if !areTestAPIKeysSet() && err == nil {
- t.Error("Expecting an error when no keys are set")
- }
-}
-
-func TestGetOrderHistory(t *testing.T) {
- o.SetDefaults()
- TestSetup(t)
-
- var getOrdersRequest = exchange.GetOrdersRequest{
- OrderType: exchange.AnyOrderType,
- Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.LTC, symbol.BTC)},
- }
-
- _, err := o.GetOrderHistory(getOrdersRequest)
- if areTestAPIKeysSet() && err != nil {
- t.Errorf("Could not get order history: %s", err)
- } else if !areTestAPIKeysSet() && err == nil {
- t.Error("Expecting an error when no keys are set")
- }
-}
-
-// Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them
-// ----------------------------------------------------------------------------------------------------------------------------
-func areTestAPIKeysSet() bool {
- if o.APIKey != "" && o.APIKey != "Key" &&
- o.APISecret != "" && o.APISecret != "Secret" {
- return true
- }
- return false
-}
+// Wrapper tests --------------------------------------------------------------------------------------------------
+// TestSubmitOrder Wrapper test
func TestSubmitOrder(t *testing.T) {
- o.SetDefaults()
- TestSetup(t)
-
- if areTestAPIKeysSet() && !canManipulateRealOrders {
- t.Skip("API keys set, canManipulateRealOrders false, skipping test")
- }
-
+ TestSetRealOrderDefaults(t)
var p = pair.CurrencyPair{
Delimiter: "",
FirstCurrency: symbol.BTC,
@@ -212,15 +1136,10 @@ func TestSubmitOrder(t *testing.T) {
}
}
+// TestCancelExchangeOrder Wrapper test
func TestCancelExchangeOrder(t *testing.T) {
- o.SetDefaults()
- TestSetup(t)
- if areTestAPIKeysSet() && !canManipulateRealOrders {
- t.Skip("API keys set, canManipulateRealOrders false, skipping test")
- }
-
+ TestSetRealOrderDefaults(t)
currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC)
-
var orderCancellation = exchange.OrderCancellation{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -229,24 +1148,14 @@ func TestCancelExchangeOrder(t *testing.T) {
}
err := o.CancelOrder(orderCancellation)
- if !areTestAPIKeysSet() && err == nil {
- t.Error("Expecting an error when no keys are set")
- }
- if areTestAPIKeysSet() && err != nil {
- t.Errorf("Could not cancel orders: %v", err)
- }
+ testStandardErrorHandling(t, err)
+
}
+// TestCancelAllExchangeOrders Wrapper test
func TestCancelAllExchangeOrders(t *testing.T) {
- o.SetDefaults()
- TestSetup(t)
-
- if areTestAPIKeysSet() && !canManipulateRealOrders {
- t.Skip("API keys set, canManipulateRealOrders false, skipping test")
- }
-
+ TestSetRealOrderDefaults(t)
currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC)
-
var orderCancellation = exchange.OrderCancellation{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -255,77 +1164,56 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
resp, err := o.CancelAllOrders(orderCancellation)
-
- if !areTestAPIKeysSet() && err == nil {
- t.Error("Expecting an error when no keys are set")
- }
- if areTestAPIKeysSet() && err != nil {
- t.Errorf("Could not cancel orders: %v", err)
- }
-
+ testStandardErrorHandling(t, err)
if len(resp.OrderStatus) > 0 {
t.Errorf("%v orders failed to cancel", len(resp.OrderStatus))
}
}
+// TestGetAccountInfo Wrapper test
+func TestGetAccountInfo(t *testing.T) {
+ _, err := o.GetAccountInfo()
+ testStandardErrorHandling(t, err)
+}
+
+// TestModifyOrder Wrapper test
func TestModifyOrder(t *testing.T) {
+ TestSetRealOrderDefaults(t)
_, err := o.ModifyOrder(exchange.ModifyOrder{})
- if err == nil {
- t.Error("Test failed - ModifyOrder() error")
+ if err != common.ErrFunctionNotSupported {
+ t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
}
}
+// TestWithdraw Wrapper test
func TestWithdraw(t *testing.T) {
- o.SetDefaults()
- TestSetup(t)
+ TestSetRealOrderDefaults(t)
var withdrawCryptoRequest = exchange.WithdrawRequest{
Amount: 100,
- Currency: "btc_usd",
+ Currency: symbol.BTC,
Address: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
Description: "WITHDRAW IT ALL",
TradePassword: "Password",
FeeAmount: 1,
}
-
- if areTestAPIKeysSet() && !canManipulateRealOrders {
- t.Skip("API keys set, canManipulateRealOrders false, skipping test")
- }
-
_, err := o.WithdrawCryptocurrencyFunds(withdrawCryptoRequest)
- if !areTestAPIKeysSet() && err == nil {
- t.Error("Expecting an error when no keys are set")
- }
- if areTestAPIKeysSet() && err != nil {
- t.Errorf("Withdraw failed to be placed: %v", err)
- }
+ testStandardErrorHandling(t, err)
}
+// TestWithdrawFiat Wrapper test
func TestWithdrawFiat(t *testing.T) {
- o.SetDefaults()
- TestSetup(t)
-
- if areTestAPIKeysSet() && !canManipulateRealOrders {
- t.Skip("API keys set, canManipulateRealOrders false, skipping test")
- }
-
+ TestSetRealOrderDefaults(t)
var withdrawFiatRequest = exchange.WithdrawRequest{}
-
_, err := o.WithdrawFiatFunds(withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
}
}
+// TestSubmitOrder Wrapper test
func TestWithdrawInternationalBank(t *testing.T) {
- o.SetDefaults()
- TestSetup(t)
-
- if areTestAPIKeysSet() && !canManipulateRealOrders {
- t.Skip("API keys set, canManipulateRealOrders false, skipping test")
- }
-
+ TestSetRealOrderDefaults(t)
var withdrawFiatRequest = exchange.WithdrawRequest{}
-
_, err := o.WithdrawFiatFundsToInternationalBank(withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
diff --git a/exchanges/okcoin/okcoin_types.go b/exchanges/okcoin/okcoin_types.go
deleted file mode 100644
index 337390eb..00000000
--- a/exchanges/okcoin/okcoin_types.go
+++ /dev/null
@@ -1,444 +0,0 @@
-package okcoin
-
-import "github.com/thrasher-/gocryptotrader/currency/symbol"
-
-// SpotInstrument stores the spot instrument info
-type SpotInstrument struct {
- BaseCurrency string `json:"base_currency"`
- BaseIncrement float64 `json:"base_increment,string"`
- BaseMinSize float64 `json:"base_min_size,string"`
- InstrumentID string `json:"instrument_id"`
- MinSize float64 `json:"min_size,string"`
- ProductID string `json:"product_id"`
- QuoteCurrency string `json:"quote_currency"`
- QuoteIncrement float64 `json:"quote_increment,string"`
- SizeIncrement float64 `json:"size_increment,string"`
- TickSize float64 `json:"tick_size,string"`
-}
-
-// Ticker holds ticker data
-type Ticker struct {
- Buy float64 `json:",string"`
- High float64 `json:",string"`
- Last float64 `json:",string"`
- Low float64 `json:",string"`
- Sell float64 `json:",string"`
- Vol float64 `json:",string"`
-}
-
-// TickerResponse is the response type for ticker
-type TickerResponse struct {
- Date string
- Ticker Ticker
-}
-
-// FuturesTicker holds futures ticker data
-type FuturesTicker struct {
- Last float64
- Buy float64
- Sell float64
- High float64
- Low float64
- Vol float64
- ContractID int64
- UnitAmount float64
-}
-
-// Orderbook holds orderbook data
-type Orderbook struct {
- Asks [][]float64 `json:"asks"`
- Bids [][]float64 `json:"bids"`
-}
-
-// FuturesTickerResponse is a response type
-type FuturesTickerResponse struct {
- Date string
- Ticker FuturesTicker
-}
-
-// BorrowInfo holds borrowing amount data
-type BorrowInfo struct {
- BorrowBTC float64 `json:"borrow_btc"`
- BorrowLTC float64 `json:"borrow_ltc"`
- BorrowCNY float64 `json:"borrow_cny"`
- CanBorrow float64 `json:"can_borrow"`
- InterestBTC float64 `json:"interest_btc"`
- InterestLTC float64 `json:"interest_ltc"`
- Result bool `json:"result"`
- DailyInterestBTC float64 `json:"today_interest_btc"`
- DailyInterestLTC float64 `json:"today_interest_ltc"`
- DailyInterestCNY float64 `json:"today_interest_cny"`
-}
-
-// BorrowOrder holds order data
-type BorrowOrder struct {
- Amount float64 `json:"amount"`
- BorrowDate int64 `json:"borrow_date"`
- BorrowID int64 `json:"borrow_id"`
- Days int64 `json:"days"`
- TradeAmount float64 `json:"deal_amount"`
- Rate float64 `json:"rate"`
- Status int64 `json:"status"`
- Symbol string `json:"symbol"`
-}
-
-// Record hold record data
-type Record struct {
- Address string `json:"addr"`
- Account int64 `json:"account,string"`
- Amount float64 `json:"amount"`
- Bank string `json:"bank"`
- BenificiaryAddress string `json:"benificiary_addr"`
- TransactionValue float64 `json:"transaction_value"`
- Fee float64 `json:"fee"`
- Date float64 `json:"date"`
-}
-
-// AccountRecords holds account record data
-type AccountRecords struct {
- Records []Record `json:"records"`
- Symbol string `json:"symbol"`
-}
-
-// FuturesOrder holds information about a futures order
-type FuturesOrder struct {
- Amount float64 `json:"amount"`
- ContractName string `json:"contract_name"`
- DateCreated float64 `json:"create_date"`
- TradeAmount float64 `json:"deal_amount"`
- Fee float64 `json:"fee"`
- LeverageRate float64 `json:"lever_rate"`
- OrderID int64 `json:"order_id"`
- Price float64 `json:"price"`
- AvgPrice float64 `json:"avg_price"`
- Status float64 `json:"status"`
- Symbol string `json:"symbol"`
- Type int64 `json:"type"`
- UnitAmount int64 `json:"unit_amount"`
-}
-
-// FuturesHoldAmount contains futures hold amount data
-type FuturesHoldAmount struct {
- Amount float64 `json:"amount"`
- ContractName string `json:"contract_name"`
-}
-
-// FuturesExplosive holds inforamtion about explosive futures
-type FuturesExplosive struct {
- Amount float64 `json:"amount,string"`
- DateCreated string `json:"create_date"`
- Loss float64 `json:"loss,string"`
- Type int64 `json:"type"`
-}
-
-// Trades holds trade data
-type Trades struct {
- Amount float64 `json:"amount,string"`
- Date int64 `json:"date"`
- DateMS int64 `json:"date_ms"`
- Price float64 `json:"price,string"`
- TradeID int64 `json:"tid"`
- Type string `json:"type"`
-}
-
-// FuturesTrades holds trade data for the futures market
-type FuturesTrades struct {
- Amount float64 `json:"amount"`
- Date int64 `json:"date"`
- DateMS int64 `json:"date_ms"`
- Price float64 `json:"price"`
- TradeID int64 `json:"tid"`
- Type string `json:"type"`
-}
-
-// UserInfo holds user account details
-type UserInfo struct {
- Info struct {
- Funds struct {
- Asset struct {
- Net float64 `json:"net,string"`
- Total float64 `json:"total,string"`
- } `json:"asset"`
- Borrow struct {
- BTC float64 `json:"btc,string"`
- LTC float64 `json:"ltc,string"`
- USD float64 `json:"usd,string"`
- CNY float64 `json:"cny,string"`
- } `json:"borrow"`
- Free struct {
- BTC float64 `json:"btc,string"`
- LTC float64 `json:"ltc,string"`
- USD float64 `json:"usd,string"`
- CNY float64 `json:"cny,string"`
- } `json:"free"`
- Freezed struct {
- BTC float64 `json:"btc,string"`
- LTC float64 `json:"ltc,string"`
- USD float64 `json:"usd,string"`
- CNY float64 `json:"cny,string"`
- } `json:"freezed"`
- UnionFund struct {
- BTC float64 `json:"btc,string"`
- LTC float64 `json:"ltc,string"`
- } `json:"union_fund"`
- } `json:"funds"`
- } `json:"info"`
- Result bool `json:"result"`
-}
-
-// BatchTrade holds data on a batch of trades
-type BatchTrade struct {
- OrderInfo []struct {
- OrderID int64 `json:"order_id"`
- ErrorCode int64 `json:"error_code"`
- } `json:"order_info"`
- Result bool `json:"result"`
-}
-
-// CancelOrderResponse is a response type for a cancelled order
-type CancelOrderResponse struct {
- Success string
- ErrorCode string `json:"error_code"`
- Result bool `json:"result"`
-}
-
-// OrderInfo holds data on an order
-type OrderInfo struct {
- Amount float64 `json:"amount"`
- AvgPrice float64 `json:"avg_price"`
- Created int64 `json:"create_date"`
- DealAmount float64 `json:"deal_amount"`
- OrderID int64 `json:"order_id"`
- OrdersID int64 `json:"orders_id"`
- Price float64 `json:"price"`
- Status int `json:"status"`
- Symbol string `json:"symbol"`
- Type string `json:"type"`
-}
-
-// OrderHistory holds information on order history
-type OrderHistory struct {
- CurrentPage int `json:"current_page"`
- Orders []OrderInfo `json:"orders"`
- PageLength int `json:"page_length"`
- Result bool `json:"result"`
- Total int `json:"total"`
-}
-
-// WithdrawalResponse is a response type for withdrawal
-type WithdrawalResponse struct {
- WithdrawID int `json:"withdraw_id"`
- Result bool `json:"result"`
-}
-
-// WithdrawInfo holds data on a withdraw
-type WithdrawInfo struct {
- Address string `json:"address"`
- Amount float64 `json:"amount"`
- Created int64 `json:"created_date"`
- ChargeFee float64 `json:"chargefee"`
- Status int `json:"status"`
- WithdrawID int64 `json:"withdraw_id"`
-}
-
-// OrderFeeInfo holds data on order fees
-type OrderFeeInfo struct {
- Fee float64 `json:"fee,string"`
- OrderID int64 `json:"order_id"`
- Type string `json:"type"`
-}
-
-// LendDepth hold lend depths
-type LendDepth struct {
- Amount float64 `json:"amount"`
- Days string `json:"days"`
- Num int64 `json:"num"`
- Rate float64 `json:"rate,string"`
-}
-
-// BorrowResponse is a response type for borrow
-type BorrowResponse struct {
- Result bool `json:"result"`
- BorrowID int `json:"borrow_id"`
-}
-
-// WebsocketFutureIndex holds future index data for websocket
-type WebsocketFutureIndex struct {
- FutureIndex float64 `json:"futureIndex"`
- Timestamp int64 `json:"timestamp,string"`
-}
-
-// WebsocketFuturesTicker holds futures ticker data for websocket
-type WebsocketFuturesTicker struct {
- Buy float64 `json:"buy"`
- ContractID string `json:"contractId"`
- High float64 `json:"high"`
- HoldAmount float64 `json:"hold_amount"`
- Last float64 `json:"last,string"`
- Low float64 `json:"low"`
- Sell float64 `json:"sell"`
- UnitAmount float64 `json:"unitAmount"`
- Volume float64 `json:"vol,string"`
-}
-
-// WebsocketUserinfo holds user info for websocket
-type WebsocketUserinfo struct {
- Info struct {
- Funds struct {
- Asset struct {
- Net float64 `json:"net,string"`
- Total float64 `json:"total,string"`
- } `json:"asset"`
- Free struct {
- BTC float64 `json:"btc,string"`
- LTC float64 `json:"ltc,string"`
- USD float64 `json:"usd,string"`
- CNY float64 `json:"cny,string"`
- } `json:"free"`
- Frozen struct {
- BTC float64 `json:"btc,string"`
- LTC float64 `json:"ltc,string"`
- USD float64 `json:"usd,string"`
- CNY float64 `json:"cny,string"`
- } `json:"freezed"`
- } `json:"funds"`
- } `json:"info"`
- Result bool `json:"result"`
-}
-
-// WebsocketFuturesContract holds futures contract information for websocket
-type WebsocketFuturesContract struct {
- Available float64 `json:"available"`
- Balance float64 `json:"balance"`
- Bond float64 `json:"bond"`
- ContractID float64 `json:"contract_id"`
- ContractType string `json:"contract_type"`
- Frozen float64 `json:"freeze"`
- Profit float64 `json:"profit"`
- Loss float64 `json:"unprofit"`
-}
-
-// WebsocketFuturesUserInfo holds futures user information for websocket
-type WebsocketFuturesUserInfo struct {
- Info struct {
- BTC struct {
- Balance float64 `json:"balance"`
- Contracts []WebsocketFuturesContract `json:"contracts"`
- Rights float64 `json:"rights"`
- } `json:"btc"`
- LTC struct {
- Balance float64 `json:"balance"`
- Contracts []WebsocketFuturesContract `json:"contracts"`
- Rights float64 `json:"rights"`
- } `json:"ltc"`
- } `json:"info"`
- Result bool `json:"result"`
-}
-
-// WebsocketOrder holds order data for websocket
-type WebsocketOrder struct {
- Amount float64 `json:"amount"`
- AvgPrice float64 `json:"avg_price"`
- DateCreated float64 `json:"create_date"`
- TradeAmount float64 `json:"deal_amount"`
- OrderID float64 `json:"order_id"`
- OrdersID float64 `json:"orders_id"`
- Price float64 `json:"price"`
- Status int64 `json:"status"`
- Symbol string `json:"symbol"`
- OrderType string `json:"type"`
-}
-
-// WebsocketFuturesOrder holds futures order data for websocket
-type WebsocketFuturesOrder struct {
- Amount float64 `json:"amount"`
- ContractName string `json:"contract_name"`
- DateCreated float64 `json:"createdDate"`
- TradeAmount float64 `json:"deal_amount"`
- Fee float64 `json:"fee"`
- LeverageAmount int `json:"lever_rate"`
- OrderID float64 `json:"order_id"`
- Price float64 `json:"price"`
- AvgPrice float64 `json:"avg_price"`
- Status int `json:"status"`
- Symbol string `json:"symbol"`
- TradeType int `json:"type"`
- UnitAmount float64 `json:"unit_amount"`
-}
-
-// WebsocketRealtrades holds real trade data for WebSocket
-type WebsocketRealtrades struct {
- AveragePrice float64 `json:"averagePrice,string"`
- CompletedTradeAmount float64 `json:"completedTradeAmount,string"`
- DateCreated float64 `json:"createdDate"`
- ID float64 `json:"id"`
- OrderID float64 `json:"orderId"`
- SigTradeAmount float64 `json:"sigTradeAmount,string"`
- SigTradePrice float64 `json:"sigTradePrice,string"`
- Status int64 `json:"status"`
- Symbol string `json:"symbol"`
- TradeAmount float64 `json:"tradeAmount,string"`
- TradePrice float64 `json:"buy,string"`
- TradeType string `json:"tradeType"`
- TradeUnitPrice float64 `json:"tradeUnitPrice,string"`
- UnTrade float64 `json:"unTrade,string"`
-}
-
-// WebsocketFuturesRealtrades holds futures real trade data for websocket
-type WebsocketFuturesRealtrades struct {
- Amount float64 `json:"amount,string"`
- ContractID float64 `json:"contract_id,string"`
- ContractName string `json:"contract_name"`
- ContractType string `json:"contract_type"`
- TradeAmount float64 `json:"deal_amount,string"`
- Fee float64 `json:"fee,string"`
- OrderID float64 `json:"orderid"`
- Price float64 `json:"price,string"`
- AvgPrice float64 `json:"price_avg,string"`
- Status int `json:"status,string"`
- TradeType int `json:"type,string"`
- UnitAmount float64 `json:"unit_amount,string"`
- LeverageAmount int `json:"lever_rate,string"`
-}
-
-// WebsocketEvent holds websocket events
-type WebsocketEvent struct {
- Event string `json:"event"`
- Channel string `json:"channel"`
-}
-
-// WebsocketResponse holds websocket responses
-type WebsocketResponse struct {
- Channel string `json:"channel"`
- Data interface{} `json:"data"`
-}
-
-// WebsocketEventAuth holds websocket authenticated events
-type WebsocketEventAuth struct {
- Event string `json:"event"`
- Channel string `json:"channel"`
- Parameters map[string]string `json:"parameters"`
-}
-
-// WebsocketEventAuthRemove holds websocket remove authenticated events
-type WebsocketEventAuthRemove struct {
- Event string `json:"event"`
- Channel string `json:"channel"`
- Parameters map[string]string `json:"parameters"`
-}
-
-// WebsocketTradeOrderResponse holds trade order responses for websocket
-type WebsocketTradeOrderResponse struct {
- OrderID int64 `json:"order_id,string"`
- Result bool `json:"result,string"`
-}
-
-// WithdrawalFees the large list of predefined withdrawal fees
-// Prone to change, using highest value
-var WithdrawalFees = map[string]float64{
- symbol.BTC: 0.005,
- symbol.LTC: 0.2,
- symbol.ETH: 0.01,
- symbol.ETC: 0.2,
- symbol.BCH: 0.002,
-}
diff --git a/exchanges/okcoin/okcoin_websocket.go b/exchanges/okcoin/okcoin_websocket.go
deleted file mode 100644
index efc2c487..00000000
--- a/exchanges/okcoin/okcoin_websocket.go
+++ /dev/null
@@ -1,348 +0,0 @@
-package okcoin
-
-import (
- "encoding/json"
- "errors"
- "fmt"
- "net/http"
- "net/url"
- "strconv"
- "time"
-
- "github.com/gorilla/websocket"
- "github.com/thrasher-/gocryptotrader/common"
- "github.com/thrasher-/gocryptotrader/currency/pair"
- exchange "github.com/thrasher-/gocryptotrader/exchanges"
- log "github.com/thrasher-/gocryptotrader/logger"
-)
-
-const (
- wsSubTicker = "ok_sub_spot_%s_ticker"
- wsSubDepthIncrement = "ok_sub_spot_%s_depth"
- wsSubDepthFull = "ok_sub_spot_%s_depth_%s"
- wsSubTrades = "ok_sub_spot_%s_deals"
- wsSubKline = "ok_sub_spot_%s_kline_%s"
-)
-
-// PingHandler handles the keep alive
-func (o *OKCoin) PingHandler(_ string) error {
- return o.WebsocketConn.WriteControl(websocket.PingMessage,
- []byte("{'event':'ping'}"),
- time.Now().Add(time.Second))
-}
-
-// AddChannel adds a new channel on the websocket client
-func (o *OKCoin) AddChannel(channel string) error {
- event := WebsocketEvent{"addChannel", channel}
- data, err := common.JSONEncode(event)
- if err != nil {
- return err
- }
-
- return o.WebsocketConn.WriteMessage(websocket.TextMessage, data)
-}
-
-// WsConnect initiates a websocket connection
-func (o *OKCoin) WsConnect() error {
- if !o.Websocket.IsEnabled() || !o.IsEnabled() {
- return errors.New(exchange.WebsocketNotEnabled)
- }
-
- klineValues := []string{"1min", "3min", "5min", "15min", "30min", "1hour",
- "2hour", "4hour", "6hour", "12hour", "day", "3day", "week"}
-
- var dialer websocket.Dialer
-
- if o.Websocket.GetProxyAddress() != "" {
- proxy, err := url.Parse(o.Websocket.GetProxyAddress())
- if err != nil {
- return err
- }
-
- dialer.Proxy = http.ProxyURL(proxy)
- }
-
- var err error
- o.WebsocketConn, _, err = dialer.Dial(o.Websocket.GetWebsocketURL(),
- http.Header{})
- if err != nil {
- return err
- }
-
- o.WebsocketConn.SetPingHandler(o.PingHandler)
-
- go o.WsHandleData()
-
- for _, p := range o.GetEnabledCurrencies() {
- fPair := exchange.FormatExchangeCurrency(o.GetName(), p)
-
- o.AddChannel(fmt.Sprintf(wsSubDepthFull, fPair.String(), "20"))
- o.AddChannel(fmt.Sprintf(wsSubKline, fPair.String(), klineValues[0]))
- o.AddChannel(fmt.Sprintf(wsSubTicker, fPair.String()))
- o.AddChannel(fmt.Sprintf(wsSubTrades, fPair.String()))
- }
-
- return nil
-}
-
-// WsReadData reads from the websocket connection
-func (o *OKCoin) WsReadData() (exchange.WebsocketResponse, error) {
- _, resp, err := o.WebsocketConn.ReadMessage()
- if err != nil {
- return exchange.WebsocketResponse{}, err
- }
-
- o.Websocket.TrafficAlert <- struct{}{}
- return exchange.WebsocketResponse{Raw: resp}, nil
-}
-
-// WsHandleData handles stream data from the websocket connection
-func (o *OKCoin) WsHandleData() {
- o.Websocket.Wg.Add(1)
-
- defer func() {
- err := o.WebsocketConn.Close()
- if err != nil {
- o.Websocket.DataHandler <- fmt.Errorf("okcoin_websocket.go - Unable to to close Websocket connection. Error: %s",
- err)
- }
- o.Websocket.Wg.Done()
- }()
-
- for {
- select {
- case <-o.Websocket.ShutdownC:
- return
-
- default:
- resp, err := o.WsReadData()
- if err != nil {
- o.Websocket.DataHandler <- err
- }
-
- var init []WsResponse
- err = common.JSONDecode(resp.Raw, &init)
- if err != nil {
- o.Websocket.DataHandler <- err
- continue
- }
-
- if init[0].ErrorCode != "" {
- log.Error(o.WebsocketErrors[init[0].ErrorCode])
- }
-
- if init[0].Success {
- if init[0].Data == nil {
- continue
- }
- }
-
- if init[0].Channel == "addChannel" {
- continue
- }
-
- var currencyPairSlice []string
- splitChar := common.SplitStrings(init[0].Channel, "_")
- currencyPairSlice = append(currencyPairSlice,
- common.StringToUpper(splitChar[3]),
- common.StringToUpper(splitChar[4]))
- currencyPair := common.JoinStrings(currencyPairSlice, "-")
-
- assetType := common.StringToUpper(splitChar[2])
-
- switch {
- case common.StringContains(init[0].Channel, "ticker") &&
- common.StringContains(init[0].Channel, "spot"):
- var ticker WsTicker
-
- err = common.JSONDecode(init[0].Data, &ticker)
- if err != nil {
- o.Websocket.DataHandler <- err
- continue
- }
-
- o.Websocket.DataHandler <- exchange.TickerData{
- Timestamp: time.Unix(0, ticker.Timestamp),
- Pair: pair.NewCurrencyPairFromString(currencyPair),
- AssetType: assetType,
- Exchange: o.GetName(),
- ClosePrice: ticker.Close,
- OpenPrice: ticker.Open,
- HighPrice: ticker.Last,
- LowPrice: ticker.Low,
- Quantity: ticker.Volume,
- }
-
- case common.StringContains(init[0].Channel, "depth"):
- var orderbook WsOrderbook
-
- err = common.JSONDecode(init[0].Data, &orderbook)
- if err != nil {
- o.Websocket.DataHandler <- err
- continue
- }
-
- o.Websocket.DataHandler <- exchange.WebsocketOrderbookUpdate{
- Pair: pair.NewCurrencyPairFromString(currencyPair),
- Exchange: o.GetName(),
- Asset: assetType,
- }
-
- case common.StringContains(init[0].Channel, "kline"):
- var klineData [][]interface{}
-
- err = common.JSONDecode(init[0].Data, &klineData)
- if err != nil {
- o.Websocket.DataHandler <- err
- continue
- }
-
- var klines []WsKlines
- for _, data := range klineData {
- var newKline WsKlines
-
- newKline.Timestamp, _ = strconv.ParseInt(data[0].(string), 10, 64)
- newKline.Open, _ = strconv.ParseFloat(data[1].(string), 64)
- newKline.High, _ = strconv.ParseFloat(data[1].(string), 64)
- newKline.Low, _ = strconv.ParseFloat(data[1].(string), 64)
- newKline.Close, _ = strconv.ParseFloat(data[1].(string), 64)
- newKline.Volume, _ = strconv.ParseFloat(data[1].(string), 64)
-
- klines = append(klines, newKline)
- }
-
- for _, data := range klines {
- o.Websocket.DataHandler <- exchange.KlineData{
- Timestamp: time.Unix(0, data.Timestamp),
- Pair: pair.NewCurrencyPairFromString(currencyPair),
- AssetType: assetType,
- Exchange: o.GetName(),
- OpenPrice: data.Open,
- ClosePrice: data.Close,
- HighPrice: data.High,
- LowPrice: data.Low,
- Volume: data.Volume,
- }
- }
-
- case common.StringContains(init[0].Channel, "spot") &&
- common.StringContains(init[0].Channel, "deals"):
- var dealsData [][]interface{}
- err = common.JSONDecode(init[0].Data, &dealsData)
- if err != nil {
- o.Websocket.DataHandler <- err
- continue
- }
-
- var deals []WsDeals
- for _, data := range dealsData {
- var newDeal WsDeals
- newDeal.TID, _ = strconv.ParseInt(data[0].(string), 10, 64)
- newDeal.Price, _ = strconv.ParseFloat(data[1].(string), 64)
- newDeal.Amount, _ = strconv.ParseFloat(data[2].(string), 64)
- newDeal.Timestamp, _ = data[3].(string)
- newDeal.Type, _ = data[4].(string)
- deals = append(deals, newDeal) // nolint: staticcheck
- // TODO: will need to link this up
- }
- }
- }
- }
-}
-
-// SetWebsocketErrorDefaults sets default errors for websocket
-func (o *OKCoin) SetWebsocketErrorDefaults() {
- o.WebsocketErrors = map[string]string{
- "10001": "Illegal parameters",
- "10002": "Authentication failure",
- "10003": "This connection has requested other user data",
- "10004": "This connection did not request this user data",
- "10005": "System error",
- "10009": "Order does not exist",
- "10010": "Insufficient funds",
- "10011": "Order quantity too low",
- "10012": "Only support btc_usd/btc_cny ltc_usd/ltc_cny",
- "10014": "Order price must be between 0 - 1,000,000",
- "10015": "Channel subscription temporally not available",
- "10016": "Insufficient coins",
- "10017": "WebSocket authorization error",
- "10100": "User frozen",
- "10216": "Non-public API",
- "20001": "User does not exist",
- "20002": "User frozen",
- "20003": "Frozen due to force liquidation",
- "20004": "Future account frozen",
- "20005": "User future account does not exist",
- "20006": "Required field can not be null",
- "20007": "Illegal parameter",
- "20008": "Future account fund balance is zero",
- "20009": "Future contract status error",
- "20010": "Risk rate information does not exist",
- "20011": `Risk rate bigger than 90% before opening position`,
- "20012": `Risk rate bigger than 90% after opening position`,
- "20013": "Temporally no counter party price",
- "20014": "System error",
- "20015": "Order does not exist",
- "20016": "Liquidation quantity bigger than holding",
- "20017": "Not authorized/illegal order ID",
- "20018": `Order price higher than 105% or lower than 95% of the price of last minute`,
- "20019": "IP restrained to access the resource",
- "20020": "Secret key does not exist",
- "20021": "Index information does not exist",
- "20022": "Wrong API interface",
- "20023": "Fixed margin user",
- "20024": "Signature does not match",
- "20025": "Leverage rate error",
- }
-}
-
-// WsOrderbook defines orderbook data from websocket connection
-type WsOrderbook struct {
- Asks [][]string `json:"asks"`
- Bids [][]string `json:"bids"`
- Timestamp int64 `json:"timestamp"`
-}
-
-// WsResponse defines initial response stream
-type WsResponse struct {
- Channel string `json:"channel"`
- Result bool `json:"result"`
- Success bool `json:"success"`
- ErrorCode string `json:"errorcode"`
- Data json.RawMessage `json:"data"`
-}
-
-// WsKlines defines a Kline response data from the websocket connection
-type WsKlines struct {
- Timestamp int64
- Open float64
- High float64
- Low float64
- Close float64
- Volume float64
-}
-
-// WsTicker holds ticker data for websocket
-type WsTicker struct {
- High float64 `json:"high,string"`
- Volume float64 `json:"vol,string"`
- Last float64 `json:"last,string"`
- Low float64 `json:"low,string"`
- Buy float64 `json:"buy,string"`
- Change float64 `json:"change,string"`
- Sell float64 `json:"sell,string"`
- DayLow float64 `json:"dayLow,string"`
- Close float64 `json:"close,string"`
- DayHigh float64 `json:"dayHigh,string"`
- Open float64 `json:"open,string"`
- Timestamp int64 `json:"timestamp"`
-}
-
-// WsDeals defines a deal response from the websocket connection
-type WsDeals struct {
- TID int64
- Price float64
- Amount float64
- Timestamp string
- Type string
-}
diff --git a/exchanges/okcoin/okcoin_wrapper.go b/exchanges/okcoin/okcoin_wrapper.go
deleted file mode 100644
index b2776610..00000000
--- a/exchanges/okcoin/okcoin_wrapper.go
+++ /dev/null
@@ -1,396 +0,0 @@
-package okcoin
-
-import (
- "errors"
- "fmt"
- "strconv"
- "strings"
- "sync"
- "time"
-
- "github.com/thrasher-/gocryptotrader/common"
- "github.com/thrasher-/gocryptotrader/currency/pair"
- exchange "github.com/thrasher-/gocryptotrader/exchanges"
- "github.com/thrasher-/gocryptotrader/exchanges/orderbook"
- "github.com/thrasher-/gocryptotrader/exchanges/ticker"
- log "github.com/thrasher-/gocryptotrader/logger"
-)
-
-// Start starts the OKCoin go routine
-func (o *OKCoin) Start(wg *sync.WaitGroup) {
- wg.Add(1)
- go func() {
- o.Run()
- wg.Done()
- }()
-}
-
-// Run implements the OKCoin wrapper
-func (o *OKCoin) Run() {
- if o.Verbose {
- log.Debugf("%s Websocket: %s. (url: %s).\n", o.GetName(), common.IsEnabled(o.Websocket.IsEnabled()), o.WebsocketURL)
- log.Debugf("%s polling delay: %ds.\n", o.GetName(), o.RESTPollingDelay)
- log.Debugf("%s %d currencies enabled: %s.\n", o.GetName(), len(o.EnabledPairs), o.EnabledPairs)
- }
-
- if o.APIUrl == okcoinAPIURL {
- // OKCoin International
- forceUpgrade := false
- if !common.StringDataContains(o.EnabledPairs, "_") || !common.StringDataContains(o.AvailablePairs, "_") {
- forceUpgrade = true
- }
-
- prods, err := o.GetSpotInstruments()
- if err != nil {
- log.Errorf("OKEX failed to obtain available spot instruments. Err: %s", err)
- } else {
- var pairs []string
- for x := range prods {
- pairs = append(pairs, prods[x].BaseCurrency+"_"+prods[x].QuoteCurrency)
- }
-
- err = o.UpdateCurrencies(pairs, false, forceUpgrade)
- if err != nil {
- log.Errorf("OKEX failed to update available currencies. Err: %s", err)
- }
- }
-
- if forceUpgrade {
- enabledPairs := []string{"btc_usd"}
- log.Warn("Available pairs for OKCoin International reset due to config upgrade, please enable the pairs you would like again.")
-
- err := o.UpdateCurrencies(enabledPairs, true, true)
- if err != nil {
- log.Errorf("%s failed to update currencies. Err: %s", o.Name, err)
- }
- }
- }
-}
-
-// UpdateTicker updates and returns the ticker for a currency pair
-func (o *OKCoin) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
- currency := exchange.FormatExchangeCurrency(o.Name, p).String()
- var tickerPrice ticker.Price
-
- if assetType != ticker.Spot && o.APIUrl == okcoinAPIURL {
- tick, err := o.GetFuturesTicker(currency, assetType)
- if err != nil {
- return tickerPrice, err
- }
- tickerPrice.Pair = p
- tickerPrice.Ask = tick.Sell
- tickerPrice.Bid = tick.Buy
- tickerPrice.Low = tick.Low
- tickerPrice.Last = tick.Last
- tickerPrice.Volume = tick.Vol
- tickerPrice.High = tick.High
- ticker.ProcessTicker(o.GetName(), p, tickerPrice, assetType)
- } else {
- tick, err := o.GetTicker(currency)
- if err != nil {
- return tickerPrice, err
- }
- tickerPrice.Pair = p
- tickerPrice.Ask = tick.Sell
- tickerPrice.Bid = tick.Buy
- tickerPrice.Low = tick.Low
- tickerPrice.Last = tick.Last
- tickerPrice.Volume = tick.Vol
- tickerPrice.High = tick.High
- ticker.ProcessTicker(o.GetName(), p, tickerPrice, ticker.Spot)
-
- }
- return ticker.GetTicker(o.Name, p, assetType)
-}
-
-// GetTickerPrice returns the ticker for a currency pair
-func (o *OKCoin) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
- tickerNew, err := ticker.GetTicker(o.GetName(), p, assetType)
- if err != nil {
- return o.UpdateTicker(p, assetType)
- }
- return tickerNew, nil
-}
-
-// GetOrderbookEx returns orderbook base on the currency pair
-func (o *OKCoin) GetOrderbookEx(currency pair.CurrencyPair, assetType string) (orderbook.Base, error) {
- ob, err := orderbook.GetOrderbook(o.GetName(), currency, assetType)
- if err != nil {
- return o.UpdateOrderbook(currency, assetType)
- }
- return ob, nil
-}
-
-// UpdateOrderbook updates and returns the orderbook for a currency pair
-func (o *OKCoin) UpdateOrderbook(currency pair.CurrencyPair, assetType string) (orderbook.Base, error) {
- var orderBook orderbook.Base
- orderbookNew, err := o.GetOrderBook(exchange.FormatExchangeCurrency(o.Name, currency).String(), 200, false)
- if err != nil {
- return orderBook, err
- }
-
- for x := range orderbookNew.Bids {
- data := orderbookNew.Bids[x]
- orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data[1], Price: data[0]})
- }
-
- for x := range orderbookNew.Asks {
- data := orderbookNew.Asks[x]
- orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data[1], Price: data[0]})
- }
-
- orderbook.ProcessOrderbook(o.GetName(), currency, orderBook, assetType)
- return orderbook.GetOrderbook(o.Name, currency, assetType)
-}
-
-// GetAccountInfo retrieves balances for all enabled currencies for the
-// OKCoin exchange
-func (o *OKCoin) GetAccountInfo() (exchange.AccountInfo, error) {
- var response exchange.AccountInfo
- response.Exchange = o.GetName()
- assets, err := o.GetUserInfo()
- if err != nil {
- return response, err
- }
-
- var currencies = []exchange.AccountCurrencyInfo{
- {
- CurrencyName: "BTC",
- TotalValue: assets.Info.Funds.Free.BTC,
- Hold: assets.Info.Funds.Freezed.BTC,
- },
- {
- CurrencyName: "LTC",
- TotalValue: assets.Info.Funds.Free.LTC,
- Hold: assets.Info.Funds.Freezed.LTC,
- },
- {
- CurrencyName: "USD",
- TotalValue: assets.Info.Funds.Free.USD,
- Hold: assets.Info.Funds.Freezed.USD,
- },
- {
- CurrencyName: "CNY",
- TotalValue: assets.Info.Funds.Free.CNY,
- Hold: assets.Info.Funds.Freezed.CNY,
- },
- }
- response.Accounts = append(response.Accounts, exchange.Account{
- Currencies: currencies,
- })
-
- return response, nil
-}
-
-// GetFundingHistory returns funding history, deposits and
-// withdrawals
-func (o *OKCoin) GetFundingHistory() ([]exchange.FundHistory, error) {
- var fundHistory []exchange.FundHistory
- return fundHistory, common.ErrFunctionNotSupported
-}
-
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (o *OKCoin) GetExchangeHistory(p pair.CurrencyPair, assetType string) ([]exchange.TradeHistory, error) {
- var resp []exchange.TradeHistory
-
- return resp, common.ErrNotYetImplemented
-}
-
-// SubmitOrder submits a new order
-func (o *OKCoin) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
- var submitOrderResponse exchange.SubmitOrderResponse
- var oT string
-
- switch orderType {
- case exchange.LimitOrderType:
- oT = "sell"
- if side == exchange.BuyOrderSide {
- oT = "buy"
- }
- case exchange.MarketOrderType:
- oT = "sell_market"
- if side == exchange.BuyOrderSide {
- oT = "buy_market"
- }
- default:
- return submitOrderResponse, errors.New("unsupported order type")
- }
-
- response, err := o.Trade(amount, price, p.Pair().String(), oT)
-
- if response > 0 {
- submitOrderResponse.OrderID = fmt.Sprintf("%v", response)
- }
-
- if err == nil {
- submitOrderResponse.IsOrderPlaced = true
- }
-
- return submitOrderResponse, err
-}
-
-// ModifyOrder will allow of changing orderbook placement and limit to
-// market conversion
-func (o *OKCoin) ModifyOrder(action exchange.ModifyOrder) (string, error) {
- return "", common.ErrFunctionNotSupported
-}
-
-// CancelOrder cancels an order by its corresponding ID number
-func (o *OKCoin) CancelOrder(order exchange.OrderCancellation) error {
- orderIDInt, err := strconv.ParseInt(order.OrderID, 10, 64)
- orders := []int64{orderIDInt}
-
- if err != nil {
- return err
- }
-
- resp, err := o.CancelExistingOrder(orders, exchange.FormatExchangeCurrency(o.Name, order.CurrencyPair).String())
- if !resp.Result {
- return errors.New(resp.ErrorCode)
- }
- return err
-}
-
-// CancelAllOrders cancels all orders associated with a currency pair
-func (o *OKCoin) CancelAllOrders(orderCancellation exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) {
- cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{
- OrderStatus: make(map[string]string),
- }
- orderInfo, err := o.GetOrderInformation(-1, exchange.FormatExchangeCurrency(o.Name, orderCancellation.CurrencyPair).String())
- if err != nil {
- return cancelAllOrdersResponse, err
- }
-
- var ordersToCancel []int64
- for _, order := range orderInfo {
- ordersToCancel = append(ordersToCancel, order.OrderID)
- }
-
- if len(ordersToCancel) > 0 {
- resp, err := o.CancelExistingOrder(ordersToCancel, exchange.FormatExchangeCurrency(o.Name, orderCancellation.CurrencyPair).String())
- if err != nil {
- return cancelAllOrdersResponse, err
- }
-
- for _, order := range common.SplitStrings(resp.ErrorCode, ",") {
- if err != nil {
- cancelAllOrdersResponse.OrderStatus[order] = "Order could not be cancelled"
- }
- }
- }
-
- return cancelAllOrdersResponse, nil
-}
-
-// GetOrderInfo returns information on a current open order
-func (o *OKCoin) GetOrderInfo(orderID string) (exchange.OrderDetail, error) {
- var orderDetail exchange.OrderDetail
- return orderDetail, common.ErrNotYetImplemented
-}
-
-// GetDepositAddress returns a deposit address for a specified currency
-func (o *OKCoin) GetDepositAddress(cryptocurrency pair.CurrencyItem, accountID string) (string, error) {
- // NOTE needs API version update to access
- return "", common.ErrNotYetImplemented
-}
-
-// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is
-// submitted
-func (o *OKCoin) WithdrawCryptocurrencyFunds(withdrawRequest exchange.WithdrawRequest) (string, error) {
- resp, err := o.Withdrawal(withdrawRequest.Currency.String(), withdrawRequest.FeeAmount, withdrawRequest.TradePassword, withdrawRequest.Address, withdrawRequest.Amount)
- return fmt.Sprintf("%v", resp), err
-}
-
-// WithdrawFiatFunds returns a withdrawal ID when a
-// withdrawal is submitted
-func (o *OKCoin) WithdrawFiatFunds(withdrawRequest exchange.WithdrawRequest) (string, error) {
- return "", common.ErrFunctionNotSupported
-}
-
-// WithdrawFiatFundsToInternationalBank returns a withdrawal ID when a
-// withdrawal is submitted
-func (o *OKCoin) WithdrawFiatFundsToInternationalBank(withdrawRequest exchange.WithdrawRequest) (string, error) {
- return "", common.ErrFunctionNotSupported
-}
-
-// GetWebsocket returns a pointer to the exchange websocket
-func (o *OKCoin) GetWebsocket() (*exchange.Websocket, error) {
- return o.Websocket, nil
-}
-
-// GetFeeByType returns an estimate of fee based on type of transaction
-func (o *OKCoin) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) {
- return o.GetFee(feeBuilder)
-}
-
-// GetActiveOrders retrieves any orders that are active/open
-func (o *OKCoin) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) {
- var allOrders []OrderInfo
- for _, currency := range getOrdersRequest.Currencies {
- resp, err := o.GetOrderHistoryForCurrency(200, 0, 0, exchange.FormatExchangeCurrency(o.Name, currency).String())
- if err != nil {
- return nil, err
- }
-
- allOrders = append(allOrders, resp.Orders...)
- }
-
- var orders []exchange.OrderDetail
- for _, order := range allOrders {
- // Status 2 == Filled, -1 == Cancelled.
- if order.Status == 2 || order.Status == -1 {
- continue
- }
-
- symbol := pair.NewCurrencyPairDelimiter(order.Symbol, o.ConfigCurrencyPairFormat.Delimiter)
- orderDate := time.Unix(order.Created, 0)
- side := exchange.OrderSide(strings.ToUpper(order.Type))
- orders = append(orders, exchange.OrderDetail{
- ID: fmt.Sprintf("%v", order.OrderID),
- Amount: order.Amount,
- OrderDate: orderDate,
- Price: order.Price,
- OrderSide: side,
- CurrencyPair: symbol,
- Exchange: o.Name,
- })
- }
-
- exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks)
- exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide)
-
- return orders, nil
-}
-
-// GetOrderHistory retrieves account order information
-// Can Limit response to specific order status
-func (o *OKCoin) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) {
- var allOrders []OrderInfo
- for _, currency := range getOrdersRequest.Currencies {
- resp, err := o.GetOrderInformation(-1, exchange.FormatExchangeCurrency(o.Name, currency).String())
- if err != nil {
- return nil, err
- }
- allOrders = append(allOrders, resp...)
- }
- var orders []exchange.OrderDetail
- for _, order := range allOrders {
- symbol := pair.NewCurrencyPairDelimiter(order.Symbol, o.ConfigCurrencyPairFormat.Delimiter)
- orderDate := time.Unix(order.Created, 0)
- side := exchange.OrderSide(strings.ToUpper(order.Type))
- orders = append(orders, exchange.OrderDetail{
- ID: fmt.Sprintf("%v", order.OrderID),
- Amount: order.Amount,
- OrderDate: orderDate,
- Price: order.Price,
- OrderSide: side,
- CurrencyPair: symbol,
- Exchange: o.Name,
- })
- }
-
- exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks)
- exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide)
-
- return orders, nil
-}
diff --git a/exchanges/okex/okex.go b/exchanges/okex/okex.go
index 727dc86c..702670fc 100644
--- a/exchanges/okex/okex.go
+++ b/exchanges/okex/okex.go
@@ -1,110 +1,59 @@
package okex
import (
- "encoding/json"
- "errors"
"fmt"
"net/http"
- "net/url"
- "reflect"
- "strconv"
- "strings"
- "sync"
"time"
- "github.com/gorilla/websocket"
"github.com/thrasher-/gocryptotrader/common"
- "github.com/thrasher-/gocryptotrader/config"
exchange "github.com/thrasher-/gocryptotrader/exchanges"
+ "github.com/thrasher-/gocryptotrader/exchanges/okgroup"
"github.com/thrasher-/gocryptotrader/exchanges/request"
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
- log "github.com/thrasher-/gocryptotrader/logger"
)
const (
- // REST API information
- apiURL = "https://www.okex.com/api/"
- apiVersion = "v1/"
-
- // Contract requests
- // Unauthenticated
- contractPrice = "future_ticker"
- contractFutureDepth = "future_depth"
- contractTradeHistory = "future_trades"
- contractFutureIndex = "future_index"
- contractExchangeRate = "exchange_rate"
- contractFutureEstPrice = "future_estimated_price"
- contractCandleStick = "future_kline"
- contractFutureHoldAmount = "future_hold_amount"
- contractFutureLimits = "future_price_limit"
-
- // Authenticated
- contractFutureUserInfo = "future_userinfo"
- contractFuturePosition = "future_position"
- contractFutureTrade = "future_trade"
- contractFutureTradeHistory = "future_trades_history"
- contractFutureBatchTrade = "future_batch_trade"
- contractFutureCancel = "future_cancel"
- contractFutureOrderInfo = "future_order_info"
- contractFutureMultOrderInfo = "future_orders_info"
- contractFutureUserInfo4fix = "future_userinfo_4fix"
- contractFuturePosition4fix = "future_position_4fix"
- contractFutureExplosive = "future_explosive"
- contractFutureDevolve = "future_devolve"
-
- // Spot requests
- // Unauthenticated
- spotPrice = "ticker"
- spotDepth = "depth"
- spotTrades = "trades"
- spotKline = "kline"
- instruments = "instruments"
-
- // Authenticated
- spotUserInfo = "userinfo"
- spotTrade = "trade"
- spotBatchTrade = "batch_trade"
- spotCancelTrade = "cancel_order"
- spotOrderInfo = "order_info.do"
- spotOrderHistory = "order_history.do"
- spotMultiOrderInfo = "orders_info"
- spotWithdraw = "withdraw.do"
- spotCancelWithdraw = "cancel_withdraw"
- spotWithdrawInfo = "withdraw_info"
- spotAccountRecords = "account_records"
-
- myWalletInfo = "wallet_info.do"
-
- // just your average return type from okex
- returnTypeOne = "map[string]interface {}"
-
- okexAuthRate = 0
- okexUnauthRate = 0
+ okExAuthRate = 600
+ okExUnauthRate = 600
+ okExAPIPath = "api/"
+ okExAPIURL = "https://www.okex.com/" + okExAPIPath
+ okExAPIVersion = "/v3/"
+ okExExchangeName = "OKEX"
+ // OkExWebsocketURL WebsocketURL
+ OkExWebsocketURL = "wss://real.okex.com:10442/ws/v3"
+ // API subsections
+ okGroupFuturesSubsection = "futures"
+ okGroupSwapSubsection = "swap"
+ okGroupETTSubsection = "ett"
+ // Futures based endpoints
+ okGroupFuturePosition = "position"
+ okGroupFutureLeverage = "leverage"
+ okGroupFutureOrder = "order"
+ okGroupFutureHolds = "holds"
+ okGroupIndices = "index"
+ okGroupRate = "rate"
+ okGroupEsimtatedPrice = "estimated_price"
+ okGroupOpenInterest = "open_interest"
+ // Perpetual swap based endpoints
+ okGroupSettings = "settings"
+ okGroupDepth = "depth"
+ okGroupFundingTime = "funding_time"
+ okGroupHistoricalFundingRate = "historical_funding_rate"
+ // ETT endpoints
+ okGroupConstituents = "constituents"
+ okGroupDefinePrice = "define-price"
)
-var errMissValue = errors.New("warning - resp value is missing from exchange")
-
-// OKEX is the overaching type across the OKEX methods
+// OKEX bases all account, spot and margin methods off okgroup implementation
type OKEX struct {
- exchange.Base
- WebsocketConn *websocket.Conn
- mu sync.Mutex
-
- // Spot and contract market error codes as per https://www.okex.com/rest_request.html
- ErrorCodes map[string]error
-
- // Stores for corresponding variable checks
- ContractTypes []string
- CurrencyPairs []string
- ContractPosition []string
- Types []string
+ okgroup.OKGroup
}
-// SetDefaults method assignes the default values for Bittrex
+// SetDefaults method assignes the default values for OKEX
func (o *OKEX) SetDefaults() {
o.SetErrorDefaults()
o.SetCheckVarDefaults()
- o.Name = "OKEX"
+ o.Name = okExExchangeName
o.Enabled = false
o.Verbose = false
o.RESTPollingDelay = 10
@@ -117,1177 +66,425 @@ func (o *OKEX) SetDefaults() {
o.SupportsAutoPairUpdating = true
o.SupportsRESTTickerBatching = false
o.Requester = request.New(o.Name,
- request.NewRateLimit(time.Second, okexAuthRate),
- request.NewRateLimit(time.Second, okexUnauthRate),
+ request.NewRateLimit(time.Second, okExAuthRate),
+ request.NewRateLimit(time.Second, okExUnauthRate),
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
- o.APIUrlDefault = apiURL
- o.APIUrl = o.APIUrlDefault
+ o.APIUrlDefault = okExAPIURL
+ o.APIUrl = okExAPIURL
o.AssetTypes = []string{ticker.Spot}
o.WebsocketInit()
+ o.APIVersion = okExAPIVersion
+ o.WebsocketURL = OkExWebsocketURL
o.Websocket.Functionality = exchange.WebsocketTickerSupported |
exchange.WebsocketTradeDataSupported |
exchange.WebsocketKlineSupported |
exchange.WebsocketOrderbookSupported
}
-// Setup method sets current configuration details if enabled
-func (o *OKEX) Setup(exch config.ExchangeConfig) {
- if !exch.Enabled {
- o.SetEnabled(false)
- } else {
- o.Enabled = true
- o.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
- o.SetAPIKeys(exch.APIKey, exch.APISecret, exch.ClientID, false)
- o.SetHTTPClientTimeout(exch.HTTPTimeout)
- o.SetHTTPClientUserAgent(exch.HTTPUserAgent)
- o.RESTPollingDelay = exch.RESTPollingDelay
- o.Verbose = exch.Verbose
- o.Websocket.SetEnabled(exch.Websocket)
- o.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
- o.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
- o.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
- err := o.SetCurrencyPairFormat()
- if err != nil {
- log.Fatal(err)
- }
- err = o.SetAssetTypes()
- if err != nil {
- log.Fatal(err)
- }
- err = o.SetAutoPairDefaults()
- if err != nil {
- log.Fatal(err)
- }
- err = o.SetAPIURL(exch)
- if err != nil {
- log.Fatal(err)
- }
- err = o.SetClientProxyAddress(exch.ProxyAddress)
- if err != nil {
- log.Fatal(err)
- }
- err = o.WebsocketSetup(o.WsConnect,
- exch.Name,
- exch.Websocket,
- okexDefaultWebsocketURL,
- exch.WebsocketURL)
- if err != nil {
- log.Fatal(err)
- }
- }
+// GetFuturesPostions Get the information of all holding positions in futures trading.Due to high energy consumption, you are advised to capture data with the "Futures Account of a Currency" API instead.
+func (o *OKEX) GetFuturesPostions() (resp okgroup.GetFuturesPositionsResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, okGroupFuturePosition, nil, &resp, true)
}
-// GetSpotInstruments returns a list of tradable spot instruments and their properties
-func (o *OKEX) GetSpotInstruments() ([]SpotInstrument, error) {
- var resp []SpotInstrument
-
- path := fmt.Sprintf("%sspot/v3/%s", o.APIUrl, instruments)
- err := o.SendHTTPRequest(path, &resp)
-
- if err != nil {
- return nil, err
- }
-
- return resp, nil
+// GetFuturesPostionsForCurrency Get the information of holding positions of a contract.
+func (o *OKEX) GetFuturesPostionsForCurrency(instrumentID string) (resp okgroup.GetFuturesPositionsForCurrencyResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v", instrumentID, okGroupFuturePosition)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
-// GetContractPrice returns current contract prices
-//
-// symbol e.g. "btc_usd"
-// contractType e.g. "this_week" "next_week" "quarter"
-func (o *OKEX) GetContractPrice(symbol, contractType string) (ContractPrice, error) {
- resp := ContractPrice{}
-
- if err := o.CheckContractType(contractType); err != nil {
- return resp, err
- }
- if err := o.CheckSymbol(symbol); err != nil {
- return resp, err
- }
-
- values := url.Values{}
- values.Set("symbol", common.StringToLower(symbol))
- values.Set("contract_type", common.StringToLower(contractType))
-
- path := fmt.Sprintf("%s%s%s.do?%s", o.APIUrl, apiVersion, contractPrice, values.Encode())
-
- err := o.SendHTTPRequest(path, &resp)
- if err != nil {
- return resp, err
- }
-
- if !resp.Result {
- if resp.Error != nil {
- return resp, o.GetErrorCode(resp.Error)
- }
- }
- return resp, nil
+// GetFuturesAccountOfAllCurrencies Get the futures account info of all token.
+// Due to high energy consumption, you are advised to capture data with the "Futures Account of a Currency" API instead.
+func (o *OKEX) GetFuturesAccountOfAllCurrencies() (resp okgroup.FuturesAccountForAllCurrenciesResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, okgroup.OKGroupAccounts, nil, &resp, true)
}
-// GetContractMarketDepth returns contract market depth
-//
-// symbol e.g. "btc_usd"
-// contractType e.g. "this_week" "next_week" "quarter"
-func (o *OKEX) GetContractMarketDepth(symbol, contractType string) (ActualContractDepth, error) {
- resp := ContractDepth{}
- fullDepth := ActualContractDepth{}
-
- if err := o.CheckContractType(contractType); err != nil {
- return fullDepth, err
- }
- if err := o.CheckSymbol(symbol); err != nil {
- return fullDepth, err
- }
-
- values := url.Values{}
- values.Set("symbol", common.StringToLower(symbol))
- values.Set("contract_type", common.StringToLower(contractType))
-
- path := fmt.Sprintf("%s%s%s.do?%s", o.APIUrl, apiVersion, contractFutureDepth, values.Encode())
-
- err := o.SendHTTPRequest(path, &resp)
- if err != nil {
- return fullDepth, err
- }
-
- if !resp.Result {
- if resp.Error != nil {
- return fullDepth, o.GetErrorCode(resp.Error)
- }
- }
-
- for _, ask := range resp.Asks {
- var askdepth struct {
- Price float64
- Volume float64
- }
- for i, depth := range ask.([]interface{}) {
- if i == 0 {
- askdepth.Price = depth.(float64)
- }
- if i == 1 {
- askdepth.Volume = depth.(float64)
- }
- }
- fullDepth.Asks = append(fullDepth.Asks, askdepth)
- }
-
- for _, bid := range resp.Bids {
- var bidDepth struct {
- Price float64
- Volume float64
- }
- for i, depth := range bid.([]interface{}) {
- if i == 0 {
- bidDepth.Price = depth.(float64)
- }
- if i == 1 {
- bidDepth.Volume = depth.(float64)
- }
- }
- fullDepth.Bids = append(fullDepth.Bids, bidDepth)
- }
-
- return fullDepth, nil
+// GetFuturesAccountOfACurrency Get the futures account info of a token.
+func (o *OKEX) GetFuturesAccountOfACurrency(instrumentID string) (resp okgroup.FuturesCurrencyData, _ error) {
+ requestURL := fmt.Sprintf("%v/%v", okgroup.OKGroupAccounts, instrumentID)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
-// GetContractTradeHistory returns trade history for the contract market
-func (o *OKEX) GetContractTradeHistory(symbol, contractType string) ([]ActualContractTradeHistory, error) {
- actualTradeHistory := []ActualContractTradeHistory{}
- var resp interface{}
-
- if err := o.CheckContractType(contractType); err != nil {
- return actualTradeHistory, err
- }
- if err := o.CheckSymbol(symbol); err != nil {
- return actualTradeHistory, err
- }
-
- values := url.Values{}
- values.Set("symbol", common.StringToLower(symbol))
- values.Set("contract_type", common.StringToLower(contractType))
-
- path := fmt.Sprintf("%s%s%s.do?%s", o.APIUrl, apiVersion, contractTradeHistory, values.Encode())
-
- err := o.SendHTTPRequest(path, &resp)
- if err != nil {
- return actualTradeHistory, err
- }
-
- if reflect.TypeOf(resp).String() == returnTypeOne {
- errorMap := resp.(map[string]interface{})
- return actualTradeHistory, o.GetErrorCode(errorMap["error_code"].(float64))
- }
-
- for _, tradeHistory := range resp.([]interface{}) {
- quickHistory := ActualContractTradeHistory{}
- tradeHistoryM := tradeHistory.(map[string]interface{})
- quickHistory.Date = tradeHistoryM["date"].(float64)
- quickHistory.DateInMS = tradeHistoryM["date_ms"].(float64)
- quickHistory.Amount = tradeHistoryM["amount"].(float64)
- quickHistory.Price = tradeHistoryM["price"].(float64)
- quickHistory.Type = tradeHistoryM["type"].(string)
- quickHistory.TID = tradeHistoryM["tid"].(float64)
- actualTradeHistory = append(actualTradeHistory, quickHistory)
- }
- return actualTradeHistory, nil
+// GetFuturesLeverage Get the leverage of the futures account
+func (o *OKEX) GetFuturesLeverage(instrumentID string) (resp okgroup.GetFuturesLeverageResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupAccounts, instrumentID, okGroupFutureLeverage)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
-// GetContractIndexPrice returns the current index price
-//
-// symbol e.g. btc_usd
-func (o *OKEX) GetContractIndexPrice(symbol string) (float64, error) {
- if err := o.CheckSymbol(symbol); err != nil {
- return 0, err
- }
-
- values := url.Values{}
- values.Set("symbol", common.StringToLower(symbol))
- path := fmt.Sprintf("%s%s%s.do?%s", o.APIUrl, apiVersion, contractFutureIndex, values.Encode())
- var resp interface{}
-
- err := o.SendHTTPRequest(path, &resp)
- if err != nil {
- return 0, err
- }
-
- futureIndex := resp.(map[string]interface{})
- if i, ok := futureIndex["error_code"].(float64); ok {
- return 0, o.GetErrorCode(i)
- }
-
- if _, ok := futureIndex["future_index"].(float64); ok {
- return futureIndex["future_index"].(float64), nil
- }
- return 0, errMissValue
+// SetFuturesLeverage Adjusting the leverage for futures account。
+// Cross margin request requirements: {"leverage":"10"}
+// Fixed margin request requirements: {"instrument_id":"BTC-USD-180213","direction":"long","leverage":"10"}
+func (o *OKEX) SetFuturesLeverage(request okgroup.SetFuturesLeverageRequest) (resp okgroup.SetFuturesLeverageResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupAccounts, request.Currency, okGroupFutureLeverage)
+ return resp, o.SendHTTPRequest(http.MethodPost, okGroupFuturesSubsection, requestURL, request, &resp, true)
}
-// GetContractExchangeRate returns the current exchange rate for the currency
-// pair
-// USD-CNY exchange rate used by OKEX, updated weekly
-func (o *OKEX) GetContractExchangeRate() (float64, error) {
- path := fmt.Sprintf("%s%s%s.do?", o.APIUrl, apiVersion, contractExchangeRate)
- var resp interface{}
-
- if err := o.SendHTTPRequest(path, &resp); err != nil {
- return 0, err
- }
-
- exchangeRate := resp.(map[string]interface{})
- if i, ok := exchangeRate["error_code"].(float64); ok {
- return 0, o.GetErrorCode(i)
- }
-
- if _, ok := exchangeRate["rate"].(float64); ok {
- return exchangeRate["rate"].(float64), nil
- }
- return 0, errMissValue
+// GetFuturesBillDetails Shows the account’s historical coin in flow and out flow.
+// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKEX) GetFuturesBillDetails(request okgroup.GetSpotBillDetailsForCurrencyRequest) (resp []okgroup.GetSpotBillDetailsForCurrencyResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v%v", okgroup.OKGroupAccounts, request.Currency, okgroup.OKGroupLedger, okgroup.FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
-// GetContractFutureEstimatedPrice returns futures estimated price
-//
-// symbol e.g btc_usd
-func (o *OKEX) GetContractFutureEstimatedPrice(symbol string) (float64, error) {
- if err := o.CheckSymbol(symbol); err != nil {
- return 0, err
- }
-
- values := url.Values{}
- values.Set("symbol", symbol)
- path := fmt.Sprintf("%s%s%s.do?%s", o.APIUrl, apiVersion, contractFutureIndex, values.Encode())
- var resp interface{}
-
- if err := o.SendHTTPRequest(path, &resp); err != nil {
- return 0, err
- }
-
- futuresEstPrice := resp.(map[string]interface{})
- if i, ok := futuresEstPrice["error_code"].(float64); ok {
- return 0, o.GetErrorCode(i)
- }
-
- if _, ok := futuresEstPrice["future_index"].(float64); ok {
- return futuresEstPrice["future_index"].(float64), nil
- }
- return 0, errMissValue
+// PlaceFuturesOrder OKEx futures trading only supports limit orders.
+// You can place an order only if you have enough funds. Once your order is placed, the amount will be put on hold in the order lifecycle.
+// The assets and amount on hold depends on the order's specific type and parameters.
+func (o *OKEX) PlaceFuturesOrder(request okgroup.PlaceFuturesOrderRequest) (resp okgroup.PlaceFuturesOrderResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodPost, okGroupFuturesSubsection, okGroupFutureOrder, request, &resp, true)
}
-// GetContractCandlestickData returns CandleStickData
-//
-// symbol e.g. btc_usd
-// type e.g. 1min or 1 minute candlestick data
-// contract_type e.g. this_week
-// size: specify data size to be acquired
-// since: timestamp(eg:1417536000000). data after the timestamp will be returned
-func (o *OKEX) GetContractCandlestickData(symbol, typeInput, contractType string, size, since int) ([]CandleStickData, error) {
- var candleData []CandleStickData
- if err := o.CheckSymbol(symbol); err != nil {
- return candleData, err
- }
- if err := o.CheckContractType(contractType); err != nil {
- return candleData, err
- }
- if err := o.CheckType(typeInput); err != nil {
- return candleData, err
- }
-
- values := url.Values{}
- values.Set("symbol", symbol)
- values.Set("type", typeInput)
- values.Set("contract_type", contractType)
- values.Set("size", strconv.FormatInt(int64(size), 10))
- values.Set("since", strconv.FormatInt(int64(since), 10))
-
- path := fmt.Sprintf("%s%s%s.do?%s", o.APIUrl, apiVersion, contractCandleStick, values.Encode())
- var resp interface{}
-
- if err := o.SendHTTPRequest(path, &resp); err != nil {
- return candleData, err
- }
-
- if reflect.TypeOf(resp).String() == returnTypeOne {
- errorMap := resp.(map[string]interface{})
- return candleData, o.GetErrorCode(errorMap["error_code"].(float64))
- }
-
- for _, candleStickData := range resp.([]interface{}) {
- var quickCandle CandleStickData
-
- for i, datum := range candleStickData.([]interface{}) {
- switch i {
- case 0:
- quickCandle.Timestamp = datum.(float64)
- case 1:
- quickCandle.Open = datum.(float64)
- case 2:
- quickCandle.High = datum.(float64)
- case 3:
- quickCandle.Low = datum.(float64)
- case 4:
- quickCandle.Close = datum.(float64)
- case 5:
- quickCandle.Volume = datum.(float64)
- case 6:
- quickCandle.Amount = datum.(float64)
- default:
- return candleData, errors.New("incoming data out of range")
- }
- }
- candleData = append(candleData, quickCandle)
- }
-
- return candleData, nil
+// PlaceFuturesOrderBatch Batch contract placing order operation.
+func (o *OKEX) PlaceFuturesOrderBatch(request okgroup.PlaceFuturesOrderBatchRequest) (resp okgroup.PlaceFuturesOrderBatchResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodPost, okGroupFuturesSubsection, okgroup.OKGroupOrders, request, &resp, true)
}
-// GetContractHoldingsNumber returns current number of holdings
-func (o *OKEX) GetContractHoldingsNumber(symbol, contractType string) (number float64, contract string, err error) {
- err = o.CheckSymbol(symbol)
- if err != nil {
- return number, contract, err
- }
-
- err = o.CheckContractType(contractType)
- if err != nil {
- return number, contract, err
- }
-
- values := url.Values{}
- values.Set("symbol", symbol)
- values.Set("contract_type", contractType)
-
- path := fmt.Sprintf("%s%s%s.do?%s", o.APIUrl, apiVersion, contractFutureHoldAmount, values.Encode())
- var resp interface{}
-
- err = o.SendHTTPRequest(path, &resp)
- if err != nil {
- return number, contract, err
- }
-
- if reflect.TypeOf(resp).String() == returnTypeOne {
- errorMap := resp.(map[string]interface{})
- return number, contract, o.GetErrorCode(errorMap["error_code"].(float64))
- }
-
- for _, holdings := range resp.([]interface{}) {
- if reflect.TypeOf(holdings).String() == returnTypeOne {
- holdingMap := holdings.(map[string]interface{})
- number = holdingMap["amount"].(float64)
- contract = holdingMap["contract_name"].(string)
- }
- }
- return number, contract, err
+// CancelFuturesOrder Cancelling an unfilled order.
+func (o *OKEX) CancelFuturesOrder(request okgroup.CancelFuturesOrderRequest) (resp okgroup.CancelFuturesOrderResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupCancelOrder, request.InstrumentID, request.OrderID)
+ return resp, o.SendHTTPRequest(http.MethodPost, okGroupFuturesSubsection, requestURL, request, &resp, true)
}
-// GetContractlimit returns upper and lower price limit
-func (o *OKEX) GetContractlimit(symbol, contractType string) (map[string]float64, error) {
- contractLimits := make(map[string]float64)
- if err := o.CheckSymbol(symbol); err != nil {
- return contractLimits, err
- }
- if err := o.CheckContractType(contractType); err != nil {
- return contractLimits, err
- }
-
- values := url.Values{}
- values.Set("symbol", symbol)
- values.Set("contract_type", contractType)
-
- path := fmt.Sprintf("%s%s%s.do?%s", o.APIUrl, apiVersion, contractFutureLimits, values.Encode())
- var resp interface{}
-
- if err := o.SendHTTPRequest(path, &resp); err != nil {
- return contractLimits, err
- }
-
- contractLimitMap := resp.(map[string]interface{})
- if i, ok := contractLimitMap["error_code"].(float64); ok {
- return contractLimits, o.GetErrorCode(i)
- }
-
- contractLimits["high"] = contractLimitMap["high"].(float64)
- contractLimits["usdCnyRate"] = contractLimitMap["usdCnyRate"].(float64)
- contractLimits["low"] = contractLimitMap["low"].(float64)
- return contractLimits, nil
+// CancelFuturesOrderBatch With best effort, cancel all open orders.
+func (o *OKEX) CancelFuturesOrderBatch(request okgroup.CancelMultipleSpotOrdersRequest) (resp okgroup.CancelMultipleSpotOrdersResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v", okgroup.OKGroupCancelBatchOrders, request.InstrumentID)
+ return resp, o.SendHTTPRequest(http.MethodPost, okGroupFuturesSubsection, requestURL, request, &resp, true)
}
-// GetContractUserInfo returns OKEX Contract Account Info(Cross-Margin Mode)
-func (o *OKEX) GetContractUserInfo() error {
- var resp interface{}
- if err := o.SendAuthenticatedHTTPRequest(contractFutureUserInfo, url.Values{}, &resp); err != nil {
- return err
- }
-
- userInfoMap := resp.(map[string]interface{})
- if code, ok := userInfoMap["error_code"]; ok {
- return o.GetErrorCode(code)
- }
- return nil
+// GetFuturesOrderList List your orders. Cursor pagination is used.
+// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKEX) GetFuturesOrderList(request okgroup.GetFuturesOrdersListRequest) (resp okgroup.GetFuturesOrderListResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v%v", okgroup.OKGroupOrders, request.InstrumentID, okgroup.FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
-// GetContractPosition returns User Contract Positions (Cross-Margin Mode)
-func (o *OKEX) GetContractPosition(symbol, contractType string) error {
- var resp interface{}
-
- if err := o.CheckSymbol(symbol); err != nil {
- return err
- }
- if err := o.CheckContractType(contractType); err != nil {
- return err
- }
-
- values := url.Values{}
- values.Set("symbol", symbol)
- values.Set("contract_type", contractType)
-
- if err := o.SendAuthenticatedHTTPRequest(contractFuturePosition, values, &resp); err != nil {
- return err
- }
-
- userInfoMap := resp.(map[string]interface{})
- if code, ok := userInfoMap["error_code"]; ok {
- return o.GetErrorCode(code)
- }
- return nil
+// GetFuturesOrderDetails Get order details by order ID.
+func (o *OKEX) GetFuturesOrderDetails(request okgroup.GetFuturesOrderDetailsRequest) (resp okgroup.GetFuturesOrderDetailsResponseData, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupOrders, request.InstrumentID, request.OrderID)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
-// PlaceContractOrders places orders
-func (o *OKEX) PlaceContractOrders(symbol, contractType, position string, leverageRate int, price, amount float64, matchPrice bool) (float64, error) {
- var resp interface{}
-
- if err := o.CheckSymbol(symbol); err != nil {
- return 0, err
- }
- if err := o.CheckContractType(contractType); err != nil {
- return 0, err
- }
- if err := o.CheckContractPosition(position); err != nil {
- return 0, err
- }
-
- values := url.Values{}
- values.Set("symbol", symbol)
- values.Set("contract_type", contractType)
- values.Set("price", strconv.FormatFloat(price, 'f', -1, 64))
- values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
- values.Set("type", position)
- if matchPrice {
- values.Set("match_price", "1")
- } else {
- values.Set("match_price", "0")
- }
-
- if leverageRate != 10 && leverageRate != 20 {
- return 0, errors.New("leverage rate can only be 10 or 20")
- }
- values.Set("lever_rate", strconv.FormatInt(int64(leverageRate), 10))
-
- if err := o.SendAuthenticatedHTTPRequest(contractFutureTrade, values, &resp); err != nil {
- return 0, err
- }
-
- contractMap := resp.(map[string]interface{})
- if code, ok := contractMap["error_code"]; ok {
- return 0, o.GetErrorCode(code)
- }
-
- if orderID, ok := contractMap["order_id"]; ok {
- return orderID.(float64), nil
- }
-
- return 0, errors.New("orderID returned nil")
+// GetFuturesTransactionDetails Get details of the recent filled orders. Cursor pagination is used.
+// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKEX) GetFuturesTransactionDetails(request okgroup.GetFuturesTransactionDetailsRequest) (resp []okgroup.GetFuturesTransactionDetailsResponse, _ error) {
+ requestURL := fmt.Sprintf("%v%v", okgroup.OKGroupGetSpotTransactionDetails, okgroup.FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
-// GetContractFuturesTradeHistory returns OKEX Contract Trade History (Not for Personal)
-func (o *OKEX) GetContractFuturesTradeHistory(symbol, date string, since int) error {
- var resp interface{}
-
- if err := o.CheckSymbol(symbol); err != nil {
- return err
- }
-
- values := url.Values{}
- values.Set("symbol", symbol)
- values.Set("date", date)
- values.Set("since", strconv.FormatInt(int64(since), 10))
-
- if err := o.SendAuthenticatedHTTPRequest(contractFutureTradeHistory, values, &resp); err != nil {
- return err
- }
-
- respMap := resp.(map[string]interface{})
- if code, ok := respMap["error_code"]; ok {
- return o.GetErrorCode(code)
- }
- return nil
+// GetFuturesContractInformation Get market data. This endpoint provides the snapshots of market data and can be used without verifications.
+func (o *OKEX) GetFuturesContractInformation() (resp []okgroup.GetFuturesContractInformationResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, okgroup.OKGroupInstruments, nil, &resp, false)
}
-// GetTokenOrders returns details for a single orderID or all open orders when orderID == -1
-func (o *OKEX) GetTokenOrders(symbol string, orderID int64) (TokenOrdersResponse, error) {
- var resp TokenOrdersResponse
- values := url.Values{}
- values.Set("symbol", symbol)
- values.Set("order_id", strconv.FormatInt(orderID, 10))
- return resp, o.SendAuthenticatedHTTPRequest(contractFutureTradeHistory, values, &resp)
+// GetFuturesOrderBook List all contracts. This request does not support pagination. The full list will be returned for a request.
+func (o *OKEX) GetFuturesOrderBook(request okgroup.GetFuturesOrderBookRequest) (resp okgroup.GetFuturesOrderBookResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v%v", okgroup.OKGroupInstruments, request.InstrumentID, okgroup.OKGroupGetSpotOrderBook, okgroup.FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
-// GetUserInfo returns the user info
-func (o *OKEX) GetUserInfo() (SpotUserInfo, error) {
- var resp SpotUserInfo
- return resp, o.SendAuthenticatedHTTPRequest(spotUserInfo, url.Values{}, &resp)
+// GetAllFuturesTokenInfo Get the last traded price, best bid/ask price, 24 hour trading volume and more info of all contracts.
+func (o *OKEX) GetAllFuturesTokenInfo() (resp []okgroup.GetFuturesTokenInfoResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v", okgroup.OKGroupInstruments, okgroup.OKGroupTicker)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
-// SpotNewOrder creates a new spot order
-func (o *OKEX) SpotNewOrder(arg SpotNewOrderRequestParams) (int64, error) {
- type response struct {
- Result bool `json:"result"`
- OrderID int64 `json:"order_id"`
- }
-
- var res response
- params := url.Values{}
- params.Set("symbol", arg.Symbol)
- params.Set("type", string(arg.Type))
- params.Set("price", strconv.FormatFloat(arg.Price, 'f', -1, 64))
- params.Set("amount", strconv.FormatFloat(arg.Amount, 'f', -1, 64))
-
- err := o.SendAuthenticatedHTTPRequest(spotTrade, params, &res)
- if err != nil {
- return res.OrderID, err
- }
-
- return res.OrderID, nil
+// GetFuturesTokenInfoForCurrency Get the last traded price, best bid/ask price, 24 hour trading volume and more info of a contract.
+func (o *OKEX) GetFuturesTokenInfoForCurrency(instrumentID string) (resp okgroup.GetFuturesTokenInfoResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okgroup.OKGroupTicker)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
-// SpotCancelOrder cancels a spot order
-// symbol such as ltc_btc
-// orderID orderID
-// returns orderID or an error
-func (o *OKEX) SpotCancelOrder(symbol string, argOrderID int64) (int64, error) {
- var res = struct {
- Result bool `json:"result"`
- OrderID string `json:"order_id"`
- ErrorCode int `json:"error_code"`
- }{}
-
- params := url.Values{}
- params.Set("symbol", symbol)
- params.Set("order_id", strconv.FormatInt(argOrderID, 10))
- var returnOrderID int64
-
- err := o.SendAuthenticatedHTTPRequest(spotCancelTrade+".do", params, &res)
- if err != nil {
- return returnOrderID, err
- }
-
- if res.ErrorCode != 0 {
- return returnOrderID, fmt.Errorf("failed to cancel order. code: %d err: %s",
- res.ErrorCode,
- o.ErrorCodes[strconv.Itoa(res.ErrorCode)],
- )
- }
-
- returnOrderID, _ = common.Int64FromString(res.OrderID)
- return returnOrderID, nil
+// GetFuturesFilledOrder Get the recent 300 transactions of all contracts. Pagination is not supported here.
+// The whole book will be returned for one request. WebSocket is recommended here.
+func (o *OKEX) GetFuturesFilledOrder(request okgroup.GetFuturesFilledOrderRequest) (resp []okgroup.GetFuturesFilledOrdersResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v%v", okgroup.OKGroupInstruments, request.InstrumentID, okgroup.OKGroupTrades, okgroup.FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
-// GetLatestSpotPrice returns latest spot price of symbol
-//
-// symbol: string of currency pair
-func (o *OKEX) GetLatestSpotPrice(symbol string) (float64, error) {
- spotPrice, err := o.GetSpotTicker(symbol)
-
- if err != nil {
- return 0, err
- }
-
- return spotPrice.Ticker.Last, nil
+// GetFuturesMarketData Get the charts of the trading pairs. Charts are returned in grouped buckets based on requested granularity.
+func (o *OKEX) GetFuturesMarketData(request okgroup.GetFuturesMarketDateRequest) (resp okgroup.GetFuturesMarketDataResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v%v", okgroup.OKGroupInstruments, request.InstrumentID, okgroup.OKGroupGetSpotMarketData, okgroup.FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
-// GetSpotTicker returns Price Ticker
-func (o *OKEX) GetSpotTicker(symbol string) (SpotPrice, error) {
- var resp SpotPrice
-
- values := url.Values{}
- values.Set("symbol", symbol)
- path := fmt.Sprintf("%s%s%s.do?%s", o.APIUrl, apiVersion, "ticker", values.Encode())
-
- err := o.SendHTTPRequest(path, &resp)
- if err != nil {
- return resp, err
- }
-
- if resp.Error != nil {
- return resp, o.GetErrorCode(resp.Error.(float64))
- }
- return resp, nil
+// GetFuturesHoldAmount Get the number of futures with hold.
+func (o *OKEX) GetFuturesHoldAmount(instrumentID string) (resp okgroup.GetFuturesHoldAmountResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupAccounts, instrumentID, okGroupFutureHolds)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
-// GetSpotMarketDepth returns Market Depth
-func (o *OKEX) GetSpotMarketDepth(asd ActualSpotDepthRequestParams) (ActualSpotDepth, error) {
- resp := SpotDepth{}
- fullDepth := ActualSpotDepth{}
-
- values := url.Values{}
- values.Set("symbol", asd.Symbol)
- values.Set("size", fmt.Sprintf("%d", asd.Size))
-
- path := fmt.Sprintf("%s%s%s.do?%s", o.APIUrl, apiVersion, "depth", values.Encode())
-
- err := o.SendHTTPRequest(path, &resp)
- if err != nil {
- return fullDepth, err
- }
-
- if !resp.Result {
- if resp.Error != nil {
- return fullDepth, o.GetErrorCode(resp.Error)
- }
- }
-
- for _, ask := range resp.Asks {
- var askdepth struct {
- Price float64
- Volume float64
- }
- for i, depth := range ask.([]interface{}) {
- if i == 0 {
- askdepth.Price = depth.(float64)
- }
- if i == 1 {
- askdepth.Volume = depth.(float64)
- }
- }
- fullDepth.Asks = append(fullDepth.Asks, askdepth)
- }
-
- for _, bid := range resp.Bids {
- var bidDepth struct {
- Price float64
- Volume float64
- }
- for i, depth := range bid.([]interface{}) {
- if i == 0 {
- bidDepth.Price = depth.(float64)
- }
- if i == 1 {
- bidDepth.Volume = depth.(float64)
- }
- }
- fullDepth.Bids = append(fullDepth.Bids, bidDepth)
- }
-
- return fullDepth, nil
+// GetFuturesIndices Get Indices of tokens. This is a public endpoint, no identity verification is needed.
+func (o *OKEX) GetFuturesIndices(instrumentID string) (resp okgroup.GetFuturesIndicesResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okGroupIndices)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, false)
}
-// GetSpotRecentTrades returns recent trades
-func (o *OKEX) GetSpotRecentTrades(ast ActualSpotTradeHistoryRequestParams) ([]ActualSpotTradeHistory, error) {
- actualTradeHistory := []ActualSpotTradeHistory{}
- var resp interface{}
-
- values := url.Values{}
- values.Set("symbol", ast.Symbol)
- values.Set("since", fmt.Sprintf("%d", ast.Since))
-
- path := fmt.Sprintf("%s%s%s.do?%s", o.APIUrl, apiVersion, "trades", values.Encode())
-
- err := o.SendHTTPRequest(path, &resp)
- if err != nil {
- return actualTradeHistory, err
- }
-
- if reflect.TypeOf(resp).String() == returnTypeOne {
- errorMap := resp.(map[string]interface{})
- return actualTradeHistory, o.GetErrorCode(errorMap["error_code"].(float64))
- }
-
- for _, tradeHistory := range resp.([]interface{}) {
- quickHistory := ActualSpotTradeHistory{}
- tradeHistoryM := tradeHistory.(map[string]interface{})
- quickHistory.Date = tradeHistoryM["date"].(float64)
- quickHistory.DateInMS = tradeHistoryM["date_ms"].(float64)
- quickHistory.Amount = tradeHistoryM["amount"].(float64)
- quickHistory.Price = tradeHistoryM["price"].(float64)
- quickHistory.Type = tradeHistoryM["type"].(string)
- quickHistory.TID = tradeHistoryM["tid"].(float64)
- actualTradeHistory = append(actualTradeHistory, quickHistory)
- }
- return actualTradeHistory, nil
+// GetFuturesExchangeRates Get the fiat exchange rates. This is a public endpoint, no identity verification is needed.
+func (o *OKEX) GetFuturesExchangeRates() (resp okgroup.GetFuturesExchangeRatesResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, okGroupRate, nil, &resp, false)
}
-// GetSpotKline returns candlestick data
-func (o *OKEX) GetSpotKline(arg KlinesRequestParams) ([]CandleStickData, error) {
- var candleData []CandleStickData
-
- values := url.Values{}
- values.Set("symbol", arg.Symbol)
- values.Set("type", string(arg.Type))
- if arg.Size != 0 {
- values.Set("size", strconv.FormatInt(int64(arg.Size), 10))
- }
- if arg.Since != 0 {
- values.Set("since", strconv.FormatInt(arg.Since, 10))
- }
-
- path := fmt.Sprintf("%s%s%s.do?%s", o.APIUrl, apiVersion, spotKline, values.Encode())
- var resp interface{}
-
- if err := o.SendHTTPRequest(path, &resp); err != nil {
- return candleData, err
- }
-
- if reflect.TypeOf(resp).String() == returnTypeOne {
- errorMap := resp.(map[string]interface{})
- return candleData, o.GetErrorCode(errorMap["error_code"].(float64))
- }
-
- for _, candleStickData := range resp.([]interface{}) {
- var quickCandle CandleStickData
-
- for i, datum := range candleStickData.([]interface{}) {
- switch i {
- case 0:
- quickCandle.Timestamp = datum.(float64)
- case 1:
- quickCandle.Open, _ = strconv.ParseFloat(datum.(string), 64)
- case 2:
- quickCandle.High, _ = strconv.ParseFloat(datum.(string), 64)
- case 3:
- quickCandle.Low, _ = strconv.ParseFloat(datum.(string), 64)
- case 4:
- quickCandle.Close, _ = strconv.ParseFloat(datum.(string), 64)
- case 5:
- quickCandle.Volume, _ = strconv.ParseFloat(datum.(string), 64)
- case 6:
- quickCandle.Amount, _ = strconv.ParseFloat(datum.(string), 64)
- default:
- return candleData, errors.New("incoming data out of range")
- }
- }
- candleData = append(candleData, quickCandle)
- }
-
- return candleData, nil
+// GetFuturesEstimatedDeliveryPrice the estimated delivery price. It is available 3 hours before delivery.
+// This is a public endpoint, no identity verification is needed.
+func (o *OKEX) GetFuturesEstimatedDeliveryPrice(instrumentID string) (resp okgroup.GetFuturesEstimatedDeliveryPriceResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okGroupEsimtatedPrice)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, false)
}
-// GetErrorCode finds the associated error code and returns its corresponding
-// string
-func (o *OKEX) GetErrorCode(code interface{}) error {
- var assertedCode string
-
- switch reflect.TypeOf(code).String() {
- case "float64":
- assertedCode = strconv.FormatFloat(code.(float64), 'f', -1, 64)
- case "string":
- assertedCode = code.(string)
- default:
- return errors.New("unusual type returned")
- }
-
- if i, ok := o.ErrorCodes[assertedCode]; ok {
- return i
- }
- return errors.New("unable to find SPOT error code")
+// GetFuturesOpenInterests Get the open interest of a contract. This is a public endpoint, no identity verification is needed.
+func (o *OKEX) GetFuturesOpenInterests(instrumentID string) (resp okgroup.GetFuturesOpenInterestsResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okGroupOpenInterest)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, false)
}
-// SendHTTPRequest sends an unauthenticated HTTP request
-func (o *OKEX) SendHTTPRequest(path string, result interface{}) error {
- return o.SendPayload(http.MethodGet, path, nil, nil, result, false, o.Verbose)
+// GetFuturesCurrentPriceLimit The maximum buying price and the minimum selling price of the contract.
+// This is a public endpoint, no identity verification is needed.
+func (o *OKEX) GetFuturesCurrentPriceLimit(instrumentID string) (resp okgroup.GetFuturesCurrentPriceLimitResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okgroup.OKGroupPriceLimit)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, false)
}
-// SendAuthenticatedHTTPRequest sends an authenticated http request to a desired
-// path
-func (o *OKEX) SendAuthenticatedHTTPRequest(method string, values url.Values, result interface{}) (err error) {
- if !o.AuthenticatedAPISupport {
- return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, o.Name)
- }
-
- values.Set("api_key", o.APIKey)
- hasher := common.GetMD5([]byte(values.Encode() + "&secret_key=" + o.APISecret))
- values.Set("sign", strings.ToUpper(common.HexEncodeToString(hasher)))
-
- encoded := values.Encode()
- path := o.APIUrl + apiVersion + method
-
- if o.Verbose {
- log.Debugf("Sending POST request to %s with params %s\n", path, encoded)
- }
-
- headers := make(map[string]string)
- headers["Content-Type"] = "application/x-www-form-urlencoded"
-
- var intermediary json.RawMessage
-
- errCap := struct {
- Result bool `json:"result"`
- Error int64 `json:"error_code"`
- }{}
-
- err = o.SendPayload(http.MethodPost, path, headers, strings.NewReader(encoded), &intermediary, true, o.Verbose)
- if err != nil {
- return err
- }
-
- err = common.JSONDecode(intermediary, &errCap)
- if err == nil {
- if !errCap.Result {
- return fmt.Errorf("sendAuthenticatedHTTPRequest error - %s",
- o.ErrorCodes[strconv.FormatInt(errCap.Error, 10)])
- }
- }
-
- return common.JSONDecode(intermediary, result)
+// GetFuturesCurrentMarkPrice The maximum buying price and the minimum selling price of the contract.
+// This is a public endpoint, no identity verification is needed.
+func (o *OKEX) GetFuturesCurrentMarkPrice(instrumentID string) (resp okgroup.GetFuturesCurrentMarkPriceResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okgroup.OKGroupMarkPrice)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, false)
}
-// SetErrorDefaults sets the full error default list
-func (o *OKEX) SetErrorDefaults() {
- o.ErrorCodes = map[string]error{
- // Spot Errors
- "10000": errors.New("required field, can not be null"),
- "10001": errors.New("request frequency too high to exceed the limit allowed"),
- "10002": errors.New("system error"),
- "10004": errors.New("request failed - Your API key might need to be recreated"),
- "10005": errors.New("'secretKey' does not exist"),
- "10006": errors.New("'api_key' does not exist"),
- "10007": errors.New("signature does not match"),
- "10008": errors.New("illegal parameter"),
- "10009": errors.New("order does not exist"),
- "10010": errors.New("insufficient funds"),
- "10011": errors.New("amount too low"),
- "10012": errors.New("only btc_usd ltc_usd supported"),
- "10013": errors.New("only support https request"),
- "10014": errors.New("order price must be between 0 and 1,000,000"),
- "10015": errors.New("order price differs from current market price too much"),
- "10016": errors.New("insufficient coins balance"),
- "10017": errors.New("api authorization error"),
- "10018": errors.New("borrow amount less than lower limit [usd:100,btc:0.1,ltc:1]"),
- "10019": errors.New("loan agreement not checked"),
- "10020": errors.New("rate cannot exceed 1%"),
- "10021": errors.New("rate cannot less than 0.01%"),
- "10023": errors.New("fail to get latest ticker"),
- "10024": errors.New("balance not sufficient"),
- "10025": errors.New("quota is full, cannot borrow temporarily"),
- "10026": errors.New("loan (including reserved loan) and margin cannot be withdrawn"),
- "10027": errors.New("cannot withdraw within 24 hrs of authentication information modification"),
- "10028": errors.New("withdrawal amount exceeds daily limit"),
- "10029": errors.New("account has unpaid loan, please cancel/pay off the loan before withdraw"),
- "10031": errors.New("deposits can only be withdrawn after 6 confirmations"),
- "10032": errors.New("please enabled phone/google authenticator"),
- "10033": errors.New("fee higher than maximum network transaction fee"),
- "10034": errors.New("fee lower than minimum network transaction fee"),
- "10035": errors.New("insufficient BTC/LTC"),
- "10036": errors.New("withdrawal amount too low"),
- "10037": errors.New("trade password not set"),
- "10040": errors.New("withdrawal cancellation fails"),
- "10041": errors.New("withdrawal address not exsit or approved"),
- "10042": errors.New("admin password error"),
- "10043": errors.New("account equity error, withdrawal failure"),
- "10044": errors.New("fail to cancel borrowing order"),
- "10047": errors.New("this function is disabled for sub-account"),
- "10048": errors.New("withdrawal information does not exist"),
- "10049": errors.New("user can not have more than 50 unfilled small orders (amount<0.15BTC)"),
- "10050": errors.New("can't cancel more than once"),
- "10051": errors.New("order completed transaction"),
- "10052": errors.New("not allowed to withdraw"),
- "10064": errors.New("after a USD deposit, that portion of assets will not be withdrawable for the next 48 hours"),
- "10100": errors.New("user account frozen"),
- "10101": errors.New("order type is wrong"),
- "10102": errors.New("incorrect ID"),
- "10103": errors.New("the private otc order's key incorrect"),
- "10216": errors.New("non-available API"),
- "1002": errors.New("the transaction amount exceed the balance"),
- "1003": errors.New("the transaction amount is less than the minimum requirement"),
- "1004": errors.New("the transaction amount is less than 0"),
- "1007": errors.New("no trading market information"),
- "1008": errors.New("no latest market information"),
- "1009": errors.New("no order"),
- "1010": errors.New("different user of the cancelled order and the original order"),
- "1011": errors.New("no documented user"),
- "1013": errors.New("no order type"),
- "1014": errors.New("no login"),
- "1015": errors.New("no market depth information"),
- "1017": errors.New("date error"),
- "1018": errors.New("order failed"),
- "1019": errors.New("undo order failed"),
- "1024": errors.New("currency does not exist"),
- "1025": errors.New("no chart type"),
- "1026": errors.New("no base currency quantity"),
- "1027": errors.New("incorrect parameter may exceeded limits"),
- "1028": errors.New("reserved decimal failed"),
- "1029": errors.New("preparing"),
- "1030": errors.New("account has margin and futures, transactions can not be processed"),
- "1031": errors.New("insufficient Transferring Balance"),
- "1032": errors.New("transferring Not Allowed"),
- "1035": errors.New("password incorrect"),
- "1036": errors.New("google Verification code Invalid"),
- "1037": errors.New("google Verification code incorrect"),
- "1038": errors.New("google Verification replicated"),
- "1039": errors.New("message Verification Input exceed the limit"),
- "1040": errors.New("message Verification invalid"),
- "1041": errors.New("message Verification incorrect"),
- "1042": errors.New("wrong Google Verification Input exceed the limit"),
- "1043": errors.New("login password cannot be same as the trading password"),
- "1044": errors.New("old password incorrect"),
- "1045": errors.New("2nd Verification Needed"),
- "1046": errors.New("please input old password"),
- "1048": errors.New("account Blocked"),
- "1201": errors.New("account Deleted at 00: 00"),
- "1202": errors.New("account Not Exist"),
- "1203": errors.New("insufficient Balance"),
- "1204": errors.New("invalid currency"),
- "1205": errors.New("invalid Account"),
- "1206": errors.New("cash Withdrawal Blocked"),
- "1207": errors.New("transfer Not Support"),
- "1208": errors.New("no designated account"),
- "1209": errors.New("invalid api"),
- "1216": errors.New("market order temporarily suspended. Please send limit order"),
- "1217": errors.New("order was sent at ±5% of the current market price. Please resend"),
- "1218": errors.New("place order failed. Please try again later"),
- // Errors for both
- "HTTP ERROR CODE 403": errors.New("too many requests, IP is shielded"),
- "Request Timed Out": errors.New("too many requests, IP is shielded"),
- // contract errors
- "405": errors.New("method not allowed"),
- "20001": errors.New("user does not exist"),
- "20002": errors.New("account frozen"),
- "20003": errors.New("account frozen due to liquidation"),
- "20004": errors.New("contract account frozen"),
- "20005": errors.New("user contract account does not exist"),
- "20006": errors.New("required field missing"),
- "20007": errors.New("illegal parameter"),
- "20008": errors.New("contract account balance is too low"),
- "20009": errors.New("contract status error"),
- "20010": errors.New("risk rate ratio does not exist"),
- "20011": errors.New("risk rate lower than 90%/80% before opening BTC position with 10x/20x leverage. or risk rate lower than 80%/60% before opening LTC position with 10x/20x leverage"),
- "20012": errors.New("risk rate lower than 90%/80% after opening BTC position with 10x/20x leverage. or risk rate lower than 80%/60% after opening LTC position with 10x/20x leverage"),
- "20013": errors.New("temporally no counter party price"),
- "20014": errors.New("system error"),
- "20015": errors.New("order does not exist"),
- "20016": errors.New("close amount bigger than your open positions"),
- "20017": errors.New("not authorized/illegal operation"),
- "20018": errors.New("order price cannot be more than 103% or less than 97% of the previous minute price"),
- "20019": errors.New("ip restricted from accessing the resource"),
- "20020": errors.New("secretKey does not exist"),
- "20021": errors.New("index information does not exist"),
- "20022": errors.New("wrong API interface (Cross margin mode shall call cross margin API, fixed margin mode shall call fixed margin API)"),
- "20023": errors.New("account in fixed-margin mode"),
- "20024": errors.New("signature does not match"),
- "20025": errors.New("leverage rate error"),
- "20026": errors.New("api permission error"),
- "20027": errors.New("no transaction record"),
- "20028": errors.New("no such contract"),
- "20029": errors.New("amount is large than available funds"),
- "20030": errors.New("account still has debts"),
- "20038": errors.New("due to regulation, this function is not available in the country/region your currently reside in"),
- "20049": errors.New("request frequency too high"),
- }
+// GetFuturesForceLiquidatedOrders Get force liquidated orders. This is a public endpoint, no identity verification is needed.
+func (o *OKEX) GetFuturesForceLiquidatedOrders(request okgroup.GetFuturesForceLiquidatedOrdersRequest) (resp []okgroup.GetFuturesForceLiquidatedOrdersResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v%v", okgroup.OKGroupInstruments, request.InstrumentID, okgroup.OKGroupLiquidation, okgroup.FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, false)
}
-// SetCheckVarDefaults sets main variables that will be used in requests because
-// api does not return an error if there are misspellings in strings. So better
-// to check on this, this end.
-func (o *OKEX) SetCheckVarDefaults() {
- o.ContractTypes = []string{"this_week", "next_week", "quarter"}
- o.CurrencyPairs = []string{"btc_usd", "ltc_usd", "eth_usd", "etc_usd", "bch_usd"}
- o.Types = []string{"1min", "3min", "5min", "15min", "30min", "1day", "3day",
- "1week", "1hour", "2hour", "4hour", "6hour", "12hour"}
- o.ContractPosition = []string{"1", "2", "3", "4"}
+// GetFuturesTagPrice Get the tag price. This is a public endpoint, no identity verification is needed.
+func (o *OKEX) GetFuturesTagPrice(instrumentID string) (resp okgroup.GetFuturesTagPriceResponse, _ error) {
+ // OKEX documentation is missing for this endpoint. Guessing "tag_price" for the URL results in 404
+ return okgroup.GetFuturesTagPriceResponse{}, common.ErrNotYetImplemented
}
-// CheckContractPosition checks to see if the string is a valid position for okex
-func (o *OKEX) CheckContractPosition(position string) error {
- if !common.StringDataCompare(o.ContractPosition, position) {
- return errors.New("invalid position string - e.g. 1 = open long position, 2 = open short position, 3 = liquidate long position, 4 = liquidate short position")
- }
- return nil
+// GetSwapPostions Get the information of all holding positions in swap trading.
+// Due to high energy consumption, you are advised to capture data with the "Swap Account of a Currency" API instead.
+func (o *OKEX) GetSwapPostions() (resp []okgroup.GetSwapPostionsResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, okGroupFuturePosition, nil, &resp, true)
}
-// CheckSymbol checks to see if the string is a valid symbol for okex
-func (o *OKEX) CheckSymbol(symbol string) error {
- if !common.StringDataCompare(o.CurrencyPairs, symbol) {
- return errors.New("invalid symbol string")
- }
- return nil
+// GetSwapPostionsForContract Get the information of holding positions of a contract.
+func (o *OKEX) GetSwapPostionsForContract(instrumentID string) (resp okgroup.GetSwapPostionsResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v", instrumentID, okGroupFuturePosition)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
}
-// CheckContractType checks to see if the string is a correct asset
-func (o *OKEX) CheckContractType(contractType string) error {
- if !common.StringDataCompare(o.ContractTypes, contractType) {
- return errors.New("invalid contract type string")
- }
- return nil
+// GetSwapAccountOfAllCurrency Get the perpetual swap account info of a token.
+// Margin ratio set as 10,000 when users have no open position.
+func (o *OKEX) GetSwapAccountOfAllCurrency() (resp okgroup.GetSwapAccountOfAllCurrencyResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, okgroup.OKGroupAccounts, nil, &resp, true)
}
-// CheckType checks to see if the string is a correct type
-func (o *OKEX) CheckType(typeInput string) error {
- if !common.StringDataCompare(o.Types, typeInput) {
- return errors.New("invalid type string")
- }
- return nil
+// GetSwapAccountSettingsOfAContract Get leverage level and margin mode of a contract.
+func (o *OKEX) GetSwapAccountSettingsOfAContract(instrumentID string) (resp okgroup.GetSwapAccountSettingsOfAContractResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupAccounts, instrumentID, okGroupSettings)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
}
-// GetFee returns an estimate of fee based on type of transaction
-func (o *OKEX) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) {
- var fee float64
- switch feeBuilder.FeeType {
- case exchange.CryptocurrencyTradeFee:
- fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.IsMaker)
- case exchange.CryptocurrencyWithdrawalFee:
- fee = getWithdrawalFee(feeBuilder.FirstCurrency)
- }
- if fee < 0 {
- fee = 0
- }
-
- return fee, nil
+// SetSwapLeverageLevelOfAContract Setting the leverage level of a contract
+// TODO this returns invalid parameters, but matches spec. Unsure how to fix
+func (o *OKEX) SetSwapLeverageLevelOfAContract(request okgroup.SetSwapLeverageLevelOfAContractRequest) (resp okgroup.SetSwapLeverageLevelOfAContractResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupAccounts, request.InstrumentID, okGroupFutureLeverage)
+ request.InstrumentID = ""
+ return resp, o.SendHTTPRequest(http.MethodPost, okGroupSwapSubsection, requestURL, request, &resp, true)
}
-func calculateTradingFee(purchasePrice, amount float64, isMaker bool) (fee float64) {
- // TODO volume based fees
- if isMaker {
- fee = 0.001
- } else {
- fee = 0.0015
- }
- return fee * amount * purchasePrice
+// GetSwapBillDetails Shows the account’s historical coin in flow and out flow.
+// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKEX) GetSwapBillDetails(request okgroup.GetSpotBillDetailsForCurrencyRequest) (resp []okgroup.GetSwapBillDetailsResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v%v", okgroup.OKGroupAccounts, request.Currency, okgroup.OKGroupLedger, okgroup.FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
}
-func getWithdrawalFee(currency string) float64 {
- return WithdrawalFees[currency]
+// PlaceSwapOrder OKEx perpetual swap trading only supports limit orders,USD as quote currency for orders.
+// You can place an order only if you have enough funds. Once your order is placed, the amount will be put on hold in the order lifecycle.
+// The assets and amount on hold depends on the order's specific type and parameters.
+func (o *OKEX) PlaceSwapOrder(request okgroup.PlaceSwapOrderRequest) (resp okgroup.PlaceSwapOrderResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodPost, okGroupSwapSubsection, okGroupFutureOrder, request, &resp, true)
}
-// GetBalance returns the full balance across all wallets
-func (o *OKEX) GetBalance() ([]FullBalance, error) {
- var resp Balance
- var balances []FullBalance
-
- err := o.SendAuthenticatedHTTPRequest(myWalletInfo, url.Values{}, &resp)
- if err != nil {
- return balances, err
- }
-
- for key, available := range resp.Info.Funds.Free {
- free, err := strconv.ParseFloat(available, 64)
- if err != nil {
- return balances, err
- }
-
- inUse, ok := resp.Info.Funds.Holds[key]
- if !ok {
- return balances, fmt.Errorf("hold currency %s not found in map", key)
- }
-
- hold, err := strconv.ParseFloat(inUse, 64)
- if err != nil {
- return balances, err
- }
-
- balances = append(balances, FullBalance{
- Currency: key,
- Available: free,
- Hold: hold,
- })
- }
-
- return balances, nil
+// PlaceMultipleSwapOrders Batch contract placing order operation.
+func (o *OKEX) PlaceMultipleSwapOrders(request okgroup.PlaceMultipleSwapOrdersRequest) (resp okgroup.PlaceMultipleSwapOrdersResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodPost, okGroupSwapSubsection, okgroup.OKGroupOrders, request, &resp, true)
}
-// Withdrawal withdraws a cryptocurrency to a supplied address
-func (o *OKEX) Withdrawal(symbol string, fee float64, tradePWD, address string, amount float64) (int, error) {
- v := url.Values{}
- v.Set("symbol", symbol)
-
- if fee != 0 {
- v.Set("chargefee", strconv.FormatFloat(fee, 'f', -1, 64))
- }
- v.Set("trade_pwd", tradePWD)
- v.Set("withdraw_address", address)
- v.Set("withdraw_amount", strconv.FormatFloat(amount, 'f', -1, 64))
- v.Set("target", "address")
- resp := WithdrawalResponse{}
-
- err := o.SendAuthenticatedHTTPRequest(spotWithdraw, v, &resp)
- if err != nil {
- return 0, err
- }
-
- if !resp.Result {
- return 0, errors.New("unable to process withdrawal request")
- }
-
- return resp.WithdrawID, nil
+// CancelSwapOrder Cancelling an unfilled order
+func (o *OKEX) CancelSwapOrder(request okgroup.CancelSwapOrderRequest) (resp okgroup.CancelSwapOrderResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupCancelOrder, request.InstrumentID, request.OrderID)
+ return resp, o.SendHTTPRequest(http.MethodPost, okGroupSwapSubsection, requestURL, nil, &resp, true)
}
-// GetOrderInformation withdraws a cryptocurrency to a supplied address
-func (o *OKEX) GetOrderInformation(orderID int64, symbol string) ([]OrderInfo, error) {
- type Response struct {
- Result bool `json:"result"`
- Orders []OrderInfo `json:"orders"`
- }
-
- v := url.Values{}
- v.Set("symbol", symbol)
- v.Set("order_id", strconv.FormatInt(orderID, 10))
- result := Response{}
-
- err := o.SendAuthenticatedHTTPRequest(spotOrderInfo, v, &result)
- if err != nil {
- return nil, err
- }
-
- if !result.Result {
- return nil, errors.New("unable to retrieve order info")
- }
-
- return result.Orders, nil
+// CancelMultipleSwapOrders With best effort, cancel all open orders.
+func (o *OKEX) CancelMultipleSwapOrders(request okgroup.CancelMultipleSwapOrdersRequest) (resp okgroup.CancelMultipleSwapOrdersResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v", okgroup.OKGroupCancelBatchOrders, request.InstrumentID)
+ request.InstrumentID = ""
+ return resp, o.SendHTTPRequest(http.MethodPost, okGroupSwapSubsection, requestURL, request, &resp, true)
}
-// GetOrderHistoryForCurrency returns a history of orders
-func (o *OKEX) GetOrderHistoryForCurrency(pageLength, currentPage, status int64, symbol string) (OrderHistory, error) {
- v := url.Values{}
- v.Set("symbol", symbol)
- v.Set("status", strconv.FormatInt(status, 10))
- v.Set("current_page", strconv.FormatInt(currentPage, 10))
- v.Set("page_length", strconv.FormatInt(pageLength, 10))
- result := OrderHistory{}
- return result, o.SendAuthenticatedHTTPRequest(spotOrderHistory, v, &result)
+// GetSwapOrderList List your orders. Cursor pagination is used.
+// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKEX) GetSwapOrderList(request okgroup.GetSwapOrderListRequest) (resp okgroup.GetSwapOrderListResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v%v", okgroup.OKGroupOrders, request.InstrumentID, okgroup.FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSwapOrderDetails Get order details by order ID.
+func (o *OKEX) GetSwapOrderDetails(request okgroup.GetSwapOrderDetailsRequest) (resp okgroup.GetSwapOrderListResponseData, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupOrders, request.InstrumentID, request.OrderID)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSwapTransactionDetails Get details of the recent filled orders
+func (o *OKEX) GetSwapTransactionDetails(request okgroup.GetSwapTransactionDetailsRequest) (resp []okgroup.GetSwapTransactionDetailsResponse, _ error) {
+ requestURL := fmt.Sprintf("%v%v", okgroup.OKGroupGetSpotTransactionDetails, okgroup.FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSwapContractInformation Get market data.
+func (o *OKEX) GetSwapContractInformation() (resp []okgroup.GetSwapContractInformationResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, okgroup.OKGroupInstruments, nil, &resp, true)
+}
+
+// GetSwapOrderBook Get the charts of the trading pairs.
+func (o *OKEX) GetSwapOrderBook(request okgroup.GetSwapOrderBookRequest) (resp okgroup.GetSwapOrderBookResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v%v", okgroup.OKGroupInstruments, request.InstrumentID, okGroupDepth, okgroup.FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
+}
+
+// GetAllSwapTokensInformation Get the last traded price, best bid/ask price, 24 hour trading volume and more info of all contracts.
+func (o *OKEX) GetAllSwapTokensInformation() (resp []okgroup.GetAllSwapTokensInformationResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v", okgroup.OKGroupInstruments, okgroup.OKGroupTicker)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSwapTokensInformationForCurrency Get the last traded price, best bid/ask price, 24 hour trading volume and more info of all contracts.
+func (o *OKEX) GetSwapTokensInformationForCurrency(instrumentID string) (resp okgroup.GetAllSwapTokensInformationResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okgroup.OKGroupTicker)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSwapFilledOrdersData Get details of the recent filled orders
+func (o *OKEX) GetSwapFilledOrdersData(request *okgroup.GetSwapFilledOrdersDataRequest) (resp []okgroup.GetSwapFilledOrdersDataResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v%v", okgroup.OKGroupInstruments, request.InstrumentID, okgroup.OKGroupTrades, okgroup.FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSwapMarketData Get the charts of the trading pairs.
+func (o *OKEX) GetSwapMarketData(request okgroup.GetSwapMarketDataRequest) (resp []okgroup.GetSwapMarketDataResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v%v", okgroup.OKGroupInstruments, request.InstrumentID, okgroup.OKGroupGetSpotMarketData, okgroup.FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSwapIndeces Get Indices of tokens.
+func (o *OKEX) GetSwapIndeces(instrumentID string) (resp okgroup.GetSwapIndecesResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okGroupIndices)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSwapExchangeRates Get the fiat exchange rates.
+func (o *OKEX) GetSwapExchangeRates() (resp okgroup.GetSwapExchangeRatesResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, okGroupRate, nil, &resp, true)
+}
+
+// GetSwapOpenInterest Get the open interest of a contract.
+func (o *OKEX) GetSwapOpenInterest(instrumentID string) (resp okgroup.GetSwapExchangeRatesResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okGroupOpenInterest)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSwapCurrentPriceLimits Get the open interest of a contract.
+func (o *OKEX) GetSwapCurrentPriceLimits(instrumentID string) (resp okgroup.GetSwapCurrentPriceLimitsResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okgroup.OKGroupPriceLimit)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSwapForceLiquidatedOrders Get force liquidated orders.
+func (o *OKEX) GetSwapForceLiquidatedOrders(request okgroup.GetSwapForceLiquidatedOrdersRequest) (resp []okgroup.GetSwapForceLiquidatedOrdersResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v%v", okgroup.OKGroupInstruments, request.InstrumentID, okgroup.OKGroupLiquidation, okgroup.FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, false)
+}
+
+// GetSwapOnHoldAmountForOpenOrders Get On Hold Amount for Open Orders.
+func (o *OKEX) GetSwapOnHoldAmountForOpenOrders(instrumentID string) (resp okgroup.GetSwapOnHoldAmountForOpenOrdersResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupAccounts, instrumentID, okGroupFutureHolds)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSwapNextSettlementTime Get the time of next settlement.
+func (o *OKEX) GetSwapNextSettlementTime(instrumentID string) (resp okgroup.GetSwapNextSettlementTimeResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okGroupFundingTime)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSwapMarkPrice Get the time of next settlement.
+func (o *OKEX) GetSwapMarkPrice(instrumentID string) (resp okgroup.GetSwapMarkPriceResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okgroup.OKGroupMarkPrice)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSwapFundingRateHistory Get Funding Rate History.
+func (o *OKEX) GetSwapFundingRateHistory(request okgroup.GetSwapFundingRateHistoryRequest) (resp []okgroup.GetSwapFundingRateHistoryResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v%v", okgroup.OKGroupInstruments, request.InstrumentID, okGroupHistoricalFundingRate, okgroup.FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, false)
+}
+
+// GetETT List the assets in ETT account. Get information such as balance, amount on hold/ available.
+func (o *OKEX) GetETT() (resp []okgroup.GetETTResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupETTSubsection, okgroup.OKGroupAccounts, nil, &resp, true)
+}
+
+// GetETTAccountInformationForCurrency Getting the balance, amount available/on hold of a token in ETT account.
+func (o *OKEX) GetETTAccountInformationForCurrency(currency string) (resp okgroup.GetETTResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v", okgroup.OKGroupAccounts, currency)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupETTSubsection, requestURL, nil, &resp, true)
+}
+
+// GetETTBillsDetails Bills details. All paginated requests return the latest information (newest)
+// as the first page sorted by newest (in chronological time) first
+func (o *OKEX) GetETTBillsDetails(currency string) (resp []okgroup.GetETTBillsDetailsResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupAccounts, currency, okgroup.OKGroupLedger)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupETTSubsection, requestURL, nil, &resp, true)
+}
+
+// PlaceETTOrder You can place subscription or redemption orders under ETT trading.
+// You can place an order only if you have enough funds. Once your order is placed,
+// the amount will be put on hold in the order lifecycle.
+// The assets and amount on hold depends on the order's specific type and parameters.
+func (o *OKEX) PlaceETTOrder(request okgroup.PlaceETTOrderRequest) (resp okgroup.PlaceETTOrderResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodPost, okGroupETTSubsection, okgroup.OKGroupOrders, nil, &resp, true)
+}
+
+// CancelETTOrder Cancel an unfilled order.
+func (o *OKEX) CancelETTOrder(orderID string) (resp okgroup.PlaceETTOrderResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v", okgroup.OKGroupOrders, orderID)
+ return resp, o.SendHTTPRequest(http.MethodDelete, okGroupETTSubsection, requestURL, nil, &resp, true)
+}
+
+// GetETTOrderList List your orders. Cursor pagination is used. All paginated requests return the latest information
+// (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKEX) GetETTOrderList(request okgroup.GetETTOrderListRequest) (resp []okgroup.GetETTOrderListResponse, _ error) {
+ requestURL := fmt.Sprintf("%v%v", okgroup.OKGroupOrders, okgroup.FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupETTSubsection, requestURL, nil, &resp, true)
+}
+
+// GetETTOrderDetails Get order details by order ID.
+func (o *OKEX) GetETTOrderDetails(orderID string) (resp okgroup.GetETTOrderListResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v", okgroup.OKGroupOrders, orderID)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupETTSubsection, requestURL, nil, &resp, true)
+}
+
+// GetETTConstituents Get ETT Constituents.This is a public endpoint, no identity verification is needed.
+func (o *OKEX) GetETTConstituents(ett string) (resp okgroup.GetETTConstituentsResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v", okGroupConstituents, ett)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupETTSubsection, requestURL, nil, &resp, false)
+}
+
+// GetETTSettlementPriceHistory Get ETT settlement price history. This is a public endpoint, no identity verification is needed.
+func (o *OKEX) GetETTSettlementPriceHistory(ett string) (resp []okgroup.GetETTSettlementPriceHistoryResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v", okGroupDefinePrice, ett)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupETTSubsection, requestURL, nil, &resp, false)
}
diff --git a/exchanges/okex/okex_test.go b/exchanges/okex/okex_test.go
index 2eb9efef..8792fc06 100644
--- a/exchanges/okex/okex_test.go
+++ b/exchanges/okex/okex_test.go
@@ -1,300 +1,1803 @@
package okex
import (
+ "fmt"
+ "strings"
"testing"
+ "time"
"github.com/thrasher-/gocryptotrader/common"
"github.com/thrasher-/gocryptotrader/config"
"github.com/thrasher-/gocryptotrader/currency/pair"
"github.com/thrasher-/gocryptotrader/currency/symbol"
exchange "github.com/thrasher-/gocryptotrader/exchanges"
+ "github.com/thrasher-/gocryptotrader/exchanges/okgroup"
)
-var o OKEX
-
// Please supply you own test keys here for due diligence testing.
const (
apiKey = ""
apiSecret = ""
+ passphrase = ""
+ OKGroupExchange = "OKEX"
canManipulateRealOrders = false
)
+var testSetupRan bool
+var o = OKEX{}
+var spotCurrency = pair.NewCurrencyPairWithDelimiter(symbol.BTC, symbol.USDT, "-").Pair().Lower().String()
+
+// TestSetDefaults Sets standard default settings for running a test
func TestSetDefaults(t *testing.T) {
- o.SetDefaults()
- if o.GetName() != "OKEX" {
- t.Error("Test Failed - Bittrex - SetDefaults() error")
+ if o.Name != OKGroupExchange {
+ o.SetDefaults()
+ }
+ if o.GetName() != OKGroupExchange {
+ t.Errorf("Test Failed - %v - SetDefaults() error", OKGroupExchange)
+ }
+ TestSetup(t)
+}
+
+// TestSetRealOrderDefaults Sets test defaults when test can impact real money/orders
+func TestSetRealOrderDefaults(t *testing.T) {
+ TestSetDefaults(t)
+ if areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
}
}
+// TestSetup Sets defaults for test environment
func TestSetup(t *testing.T) {
+ if testSetupRan {
+ return
+ }
+ if o.APIKey == apiKey && o.APISecret == apiSecret &&
+ o.ClientID == passphrase && apiKey != "" && apiSecret != "" && passphrase != "" {
+ return
+ }
+ o.ExchangeName = OKGroupExchange
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
- okexConfig, err := cfg.GetExchangeConfig("OKEX")
+
+ okexConfig, err := cfg.GetExchangeConfig(OKGroupExchange)
if err != nil {
- t.Error("Test Failed - Okex Setup() init error")
+ t.Fatalf("Test Failed - %v Setup() init error", OKGroupExchange)
}
+
okexConfig.AuthenticatedAPISupport = true
okexConfig.APIKey = apiKey
okexConfig.APISecret = apiSecret
-
+ okexConfig.ClientID = passphrase
+ okexConfig.WebsocketURL = o.WebsocketURL
o.Setup(okexConfig)
+ testSetupRan = true
}
-func TestGetSpotInstruments(t *testing.T) {
+func areTestAPIKeysSet() bool {
+ if o.APIKey != "" && o.APIKey != "Key" &&
+ o.APISecret != "" && o.APISecret != "Secret" {
+ return true
+ }
+ return false
+}
+
+func testStandardErrorHandling(t *testing.T, err error) {
+ if !areTestAPIKeysSet() && err == nil {
+ t.Errorf("Expecting an error when no keys are set")
+ }
+ if areTestAPIKeysSet() && err != nil {
+ t.Errorf("Encountered error: %v", err)
+ }
+}
+
+// setupWSConnection Connect to WS, but pass back error so test can handle it if needed
+func setupWSConnection() error {
+ if !o.Websocket.IsEnabled() {
+ err := o.WebsocketSetup(o.WsConnect,
+ o.Name,
+ true,
+ o.WebsocketURL,
+ o.WebsocketURL)
+ o.Websocket.DataHandler = make(chan interface{}, 500)
+ if err != nil {
+ return err
+ }
+ o.Websocket.SetWsStatusAndConnection(true)
+ }
+ return nil
+}
+
+// disconnectFromWS disconnect to WS, but pass back error so test can handle it if needed
+func disconnectFromWS() error {
+ return o.Websocket.Shutdown()
+}
+
+// TestGetAccountCurrencies API endpoint test
+func TestGetAccountCurrencies(t *testing.T) {
+ TestSetDefaults(t)
t.Parallel()
- _, err := o.GetSpotInstruments()
+ _, err := o.GetAccountCurrencies()
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetAccountWalletInformation API endpoint test
+func TestGetAccountWalletInformation(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ resp, err := o.GetAccountWalletInformation("")
+ if areTestAPIKeysSet() {
+ if err != nil {
+ t.Error(err)
+ }
+ if len(resp) == 0 {
+ t.Error("No wallets returned")
+ }
+ } else if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+}
+
+// TestGetAccountWalletInformationForCurrency API endpoint test
+func TestGetAccountWalletInformationForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ resp, err := o.GetAccountWalletInformation(symbol.BTC)
+ if areTestAPIKeysSet() {
+ if err != nil {
+ t.Error(err)
+ }
+ if len(resp) != 1 {
+ t.Errorf("Error receiving wallet information for currency: %v", symbol.BTC)
+ }
+ } else if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+}
+
+// TestTransferAccountFunds API endpoint test
+func TestTransferAccountFunds(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ request := okgroup.TransferAccountFundsRequest{
+ Amount: 10,
+ Currency: symbol.BTC,
+ From: 6,
+ To: 1,
+ }
+
+ _, err := o.TransferAccountFunds(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestBaseWithdraw API endpoint test
+func TestAccountWithdrawRequest(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ request := okgroup.AccountWithdrawRequest{
+ Amount: 10,
+ Currency: symbol.BTC,
+ TradePwd: "1234",
+ Destination: 4,
+ ToAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
+ Fee: 1,
+ }
+
+ _, err := o.AccountWithdraw(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetAccountWithdrawalFee API endpoint test
+func TestGetAccountWithdrawalFee(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ resp, err := o.GetAccountWithdrawalFee("")
+ if areTestAPIKeysSet() {
+ if err != nil {
+ t.Error(err)
+ }
+ if len(resp) == 0 {
+ t.Error("Expected fees")
+ }
+ } else if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+}
+
+// TestGetWithdrawalFeeForCurrency API endpoint test
+func TestGetAccountWithdrawalFeeForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ resp, err := o.GetAccountWithdrawalFee(symbol.BTC)
+ if areTestAPIKeysSet() {
+ if err != nil {
+ t.Error(err)
+ }
+ if len(resp) != 1 {
+ t.Error("Expected fee for one currency")
+ }
+ } else if !areTestAPIKeysSet() && err == nil {
+ t.Error("Expecting an error when no keys are set")
+ }
+}
+
+// TestGetAccountWithdrawalHistory API endpoint test
+func TestGetAccountWithdrawalHistory(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetAccountWithdrawalHistory("")
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetAccountWithdrawalHistoryForCurrency API endpoint test
+func TestGetAccountWithdrawalHistoryForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetAccountWithdrawalHistory(symbol.BTC)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetAccountBillDetails API endpoint test
+func TestGetAccountBillDetails(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetAccountBillDetails(okgroup.GetAccountBillDetailsRequest{})
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetAccountDepositAddressForCurrency API endpoint test
+func TestGetAccountDepositAddressForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetAccountDepositAddressForCurrency(symbol.BTC)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetAccountDepositHistory API endpoint test
+func TestGetAccountDepositHistory(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetAccountDepositHistory("")
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetAccountDepositHistoryForCurrency API endpoint test
+func TestGetAccountDepositHistoryForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetAccountDepositHistory(symbol.BTC)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSpotTradingAccounts API endpoint test
+func TestGetSpotTradingAccounts(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSpotTradingAccounts()
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSpotTradingAccountsForCurrency API endpoint test
+func TestGetSpotTradingAccountsForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSpotTradingAccountForCurrency(symbol.BTC)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSpotBillDetailsForCurrency API endpoint test
+func TestGetSpotBillDetailsForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ request := okgroup.GetSpotBillDetailsForCurrencyRequest{
+ Currency: symbol.BTC,
+ Limit: 100,
+ }
+ _, err := o.GetSpotBillDetailsForCurrency(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSpotBillDetailsForCurrencyBadLimit API logic test
+func TestGetSpotBillDetailsForCurrencyBadLimit(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ request := okgroup.GetSpotBillDetailsForCurrencyRequest{
+ Currency: symbol.BTC,
+ Limit: -1,
+ }
+ _, err := o.GetSpotBillDetailsForCurrency(request)
+ if areTestAPIKeysSet() && err == nil {
+ t.Errorf("Expecting an error when invalid request sent")
+ }
+}
+
+// TestPlaceSpotOrderLimit API endpoint test
+func TestPlaceSpotOrderLimit(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ request := okgroup.PlaceSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ Type: "limit",
+ Side: "buy",
+ MarginTrading: "1",
+ Price: "100",
+ Size: "100",
+ }
+
+ _, err := o.PlaceSpotOrder(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestPlaceSpotOrderMarket API endpoint test
+func TestPlaceSpotOrderMarket(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ request := okgroup.PlaceSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ Type: "market",
+ Side: "buy",
+ MarginTrading: "1",
+ Size: "100",
+ Notional: "100",
+ }
+
+ _, err := o.PlaceSpotOrder(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestPlaceMultipleSpotOrders API endpoint test
+func TestPlaceMultipleSpotOrders(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ order := okgroup.PlaceSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ Type: "market",
+ Side: "buy",
+ MarginTrading: "1",
+ Size: "100",
+ Notional: "100",
+ }
+
+ request := []okgroup.PlaceSpotOrderRequest{
+ order,
+ }
+
+ _, errs := o.PlaceMultipleSpotOrders(request)
+ if len(errs) > 0 {
+ testStandardErrorHandling(t, errs[0])
+ }
+}
+
+// TestPlaceMultipleSpotOrdersOverCurrencyLimits API logic test
+func TestPlaceMultipleSpotOrdersOverCurrencyLimits(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ order := okgroup.PlaceSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ Type: "market",
+ Side: "buy",
+ MarginTrading: "1",
+ Size: "100",
+ Notional: "100",
+ }
+
+ request := []okgroup.PlaceSpotOrderRequest{
+ order,
+ order,
+ order,
+ order,
+ order,
+ }
+
+ _, errs := o.PlaceMultipleSpotOrders(request)
+ if errs[0].Error() != "maximum 4 orders for each pair" {
+ t.Error("Expecting an error when more than 4 orders for a pair supplied", errs[0])
+ }
+}
+
+// TestPlaceMultipleSpotOrdersOverPairLimits API logic test
+func TestPlaceMultipleSpotOrdersOverPairLimits(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ order := okgroup.PlaceSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ Type: "market",
+ Side: "buy",
+ MarginTrading: "1",
+ Size: "100",
+ Notional: "100",
+ }
+
+ request := []okgroup.PlaceSpotOrderRequest{
+ order,
+ }
+
+ order.InstrumentID = pair.NewCurrencyPairWithDelimiter(symbol.LTC, symbol.USDT, "-").Pair().Lower().String()
+ request = append(request, order)
+ order.InstrumentID = pair.NewCurrencyPairWithDelimiter(symbol.DOGE, symbol.USDT, "-").Pair().Lower().String()
+ request = append(request, order)
+ order.InstrumentID = pair.NewCurrencyPairWithDelimiter(symbol.XMR, symbol.USDT, "-").Pair().Lower().String()
+ request = append(request, order)
+ order.InstrumentID = pair.NewCurrencyPairWithDelimiter(symbol.BCH, symbol.USDT, "-").Pair().Lower().String()
+ request = append(request, order)
+
+ _, errs := o.PlaceMultipleSpotOrders(request)
+ if errs[0].Error() != "up to 4 trading pairs" {
+ t.Error("Expecting an error when more than 4 trading pairs supplied", errs[0])
+ }
+}
+
+// TestCancelSpotOrder API endpoint test
+func TestCancelSpotOrder(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ request := okgroup.CancelSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ OrderID: 1234,
+ }
+
+ _, err := o.CancelSpotOrder(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestCancelMultipleSpotOrders API endpoint test
+func TestCancelMultipleSpotOrders(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ request := okgroup.CancelMultipleSpotOrdersRequest{
+ InstrumentID: spotCurrency,
+ OrderIDs: []int64{1, 2, 3, 4},
+ }
+
+ cancellations, err := o.CancelMultipleSpotOrders(request)
+ testStandardErrorHandling(t, err)
+ for _, cancellationsPerCurrency := range cancellations {
+ for _, cancellation := range cancellationsPerCurrency {
+ if !cancellation.Result {
+ t.Error(cancellation.Error)
+ }
+ }
+ }
+}
+
+// TestCancelMultipleSpotOrdersOverCurrencyLimits API logic test
+func TestCancelMultipleSpotOrdersOverCurrencyLimits(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ request := okgroup.CancelMultipleSpotOrdersRequest{
+ InstrumentID: spotCurrency,
+ OrderIDs: []int64{1, 2, 3, 4, 5},
+ }
+
+ _, err := o.CancelMultipleSpotOrders(request)
+ if err.Error() != "maximum 4 order cancellations for each pair" {
+ t.Error("Expecting an error when more than 4 orders for a pair supplied", err)
+ }
+}
+
+// TestGetSpotOrders API endpoint test
+func TestGetSpotOrders(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ request := okgroup.GetSpotOrdersRequest{
+ InstrumentID: spotCurrency,
+ Status: "all",
+ Limit: 1,
+ }
+ _, err := o.GetSpotOrders(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSpotOpenOrders API endpoint test
+func TestGetSpotOpenOrders(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ request := okgroup.GetSpotOpenOrdersRequest{}
+ _, err := o.GetSpotOpenOrders(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSpotOrder API endpoint test
+func TestGetSpotOrder(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ request := okgroup.GetSpotOrderRequest{
+ OrderID: "-1234",
+ InstrumentID: pair.NewCurrencyPairWithDelimiter(symbol.BTC, symbol.USDT, "-").Pair().Upper().String(),
+ }
+ _, err := o.GetSpotOrder(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSpotTransactionDetails API endpoint test
+func TestGetSpotTransactionDetails(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ request := okgroup.GetSpotTransactionDetailsRequest{
+ OrderID: 1234,
+ InstrumentID: spotCurrency,
+ }
+ _, err := o.GetSpotTransactionDetails(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSpotTokenPairDetails API endpoint test
+func TestGetSpotTokenPairDetails(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSpotTokenPairDetails()
if err != nil {
- t.Errorf("Test failed - okex GetSpotInstruments() failed: %s", err)
+ t.Error(err)
}
}
-func TestGetContractPrice(t *testing.T) {
+// TestGetSpotOrderBook API endpoint test
+func TestGetSpotOrderBook(t *testing.T) {
+ TestSetDefaults(t)
t.Parallel()
- _, err := o.GetContractPrice("btc_usd", "this_week")
+ request := okgroup.GetSpotOrderBookRequest{
+ InstrumentID: spotCurrency,
+ }
+ _, err := o.GetSpotOrderBook(request)
if err != nil {
- t.Error("Test failed - okex GetContractPrice() error", err)
- }
- _, err = o.GetContractPrice("btc_bla", "123525")
- if err == nil {
- t.Error("Test failed - okex GetContractPrice() error", err)
- }
- _, err = o.GetContractPrice("btc_bla", "this_week")
- if err == nil {
- t.Error("Test failed - okex GetContractPrice() error", err)
+ t.Error(err)
}
}
-func TestGetContractMarketDepth(t *testing.T) {
+// TestGetSpotAllTokenPairsInformation API endpoint test
+func TestGetSpotAllTokenPairsInformation(t *testing.T) {
+ TestSetDefaults(t)
t.Parallel()
- _, err := o.GetContractMarketDepth("btc_usd", "this_week")
+ _, err := o.GetSpotAllTokenPairsInformation()
if err != nil {
- t.Error("Test failed - okex GetContractMarketDepth() error", err)
- }
- _, err = o.GetContractMarketDepth("btc_bla", "123525")
- if err == nil {
- t.Error("Test failed - okex GetContractMarketDepth() error", err)
- }
- _, err = o.GetContractMarketDepth("btc_bla", "this_week")
- if err == nil {
- t.Error("Test failed - okex GetContractMarketDepth() error", err)
+ t.Error(err)
}
}
-func TestGetContractTradeHistory(t *testing.T) {
+// TestGetSpotAllTokenPairsInformationForCurrency API endpoint test
+func TestGetSpotAllTokenPairsInformationForCurrency(t *testing.T) {
+ TestSetDefaults(t)
t.Parallel()
- _, err := o.GetContractTradeHistory("btc_usd", "this_week")
+ _, err := o.GetSpotAllTokenPairsInformationForCurrency(spotCurrency)
if err != nil {
- t.Error("Test failed - okex GetContractTradeHistory() error", err)
- }
- _, err = o.GetContractTradeHistory("btc_bla", "123525")
- if err == nil {
- t.Error("Test failed - okex GetContractTradeHistory() error", err)
- }
- _, err = o.GetContractTradeHistory("btc_bla", "this_week")
- if err == nil {
- t.Error("Test failed - okex GetContractTradeHistory() error", err)
+ t.Error(err)
}
}
-func TestGetContractIndexPrice(t *testing.T) {
+// TestGetSpotFilledOrdersInformation API endpoint test
+func TestGetSpotFilledOrdersInformation(t *testing.T) {
+ TestSetDefaults(t)
t.Parallel()
- _, err := o.GetContractIndexPrice("btc_usd")
+ request := okgroup.GetSpotFilledOrdersInformationRequest{
+ InstrumentID: spotCurrency,
+ }
+ _, err := o.GetSpotFilledOrdersInformation(request)
if err != nil {
- t.Error("Test failed - okex GetContractIndexPrice() error", err)
- }
- _, err = o.GetContractIndexPrice("lol123")
- if err == nil {
- t.Error("Test failed - okex GetContractTradeHistory() error", err)
+ t.Error(err)
}
}
-func TestGetContractExchangeRate(t *testing.T) {
+// TestGetSpotMarketData API endpoint test
+func TestGetSpotMarketData(t *testing.T) {
+ TestSetDefaults(t)
t.Parallel()
- _, err := o.GetContractExchangeRate()
+ request := okgroup.GetSpotMarketDataRequest{
+ InstrumentID: spotCurrency,
+ Granularity: 604800,
+ }
+ _, err := o.GetSpotMarketData(request)
if err != nil {
- t.Error("Test failed - okex GetContractExchangeRate() error", err)
+ t.Error(err)
}
}
-func TestGetContractCandlestickData(t *testing.T) {
+// TestGetMarginTradingAccounts API endpoint test
+func TestGetMarginTradingAccounts(t *testing.T) {
+ TestSetDefaults(t)
t.Parallel()
- _, err := o.GetContractCandlestickData("btc_usd", "1min", "this_week", 1, 2)
+ _, err := o.GetMarginTradingAccounts()
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetMarginTradingAccountsForCurrency API endpoint test
+func TestGetMarginTradingAccountsForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetMarginTradingAccountsForCurrency(spotCurrency)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetMarginBillDetails API endpoint test
+func TestGetMarginBillDetails(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ request := okgroup.GetMarginBillDetailsRequest{
+ InstrumentID: spotCurrency,
+ Limit: 100,
+ }
+
+ _, err := o.GetMarginBillDetails(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetMarginAccountSettings API endpoint test
+func TestGetMarginAccountSettings(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetMarginAccountSettings("")
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetMarginAccountSettingsForCurrency API endpoint test
+func TestGetMarginAccountSettingsForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetMarginAccountSettings(spotCurrency)
+ testStandardErrorHandling(t, err)
+}
+
+// TestOpenMarginLoan API endpoint test
+func TestOpenMarginLoan(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ request := okgroup.OpenMarginLoanRequest{
+ Amount: 100,
+ InstrumentID: spotCurrency,
+ QuoteCurrency: symbol.USDT,
+ }
+
+ _, err := o.OpenMarginLoan(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestRepayMarginLoan API endpoint test
+func TestRepayMarginLoan(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ request := okgroup.RepayMarginLoanRequest{
+ Amount: 100,
+ InstrumentID: spotCurrency,
+ QuoteCurrency: symbol.USDT,
+ BorrowID: 1,
+ }
+
+ _, err := o.RepayMarginLoan(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestPlaceMarginOrderLimit API endpoint test
+func TestPlaceMarginOrderLimit(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ request := okgroup.PlaceSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ Type: "limit",
+ Side: "buy",
+ MarginTrading: "2",
+ Price: "100",
+ Size: "100",
+ }
+
+ _, err := o.PlaceMarginOrder(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestPlaceMarginOrderMarket API endpoint test
+func TestPlaceMarginOrderMarket(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ request := okgroup.PlaceSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ Type: "market",
+ Side: "buy",
+ MarginTrading: "2",
+ Size: "100",
+ Notional: "100",
+ }
+
+ _, err := o.PlaceMarginOrder(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestPlaceMultipleMarginOrders API endpoint test
+func TestPlaceMultipleMarginOrders(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ order := okgroup.PlaceSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ Type: "market",
+ Side: "buy",
+ MarginTrading: "1",
+ Size: "100",
+ Notional: "100",
+ }
+
+ request := []okgroup.PlaceSpotOrderRequest{
+ order,
+ }
+
+ _, errs := o.PlaceMultipleMarginOrders(request)
+ if len(errs) > 0 {
+ testStandardErrorHandling(t, errs[0])
+ }
+}
+
+// TestPlaceMultipleMarginOrdersOverCurrencyLimits API logic test
+func TestPlaceMultipleMarginOrdersOverCurrencyLimits(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ order := okgroup.PlaceSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ Type: "market",
+ Side: "buy",
+ MarginTrading: "1",
+ Size: "100",
+ Notional: "100",
+ }
+
+ request := []okgroup.PlaceSpotOrderRequest{
+ order,
+ order,
+ order,
+ order,
+ order,
+ }
+
+ _, errs := o.PlaceMultipleMarginOrders(request)
+ if errs[0].Error() != "maximum 4 orders for each pair" {
+ t.Error("Expecting an error when more than 4 orders for a pair supplied", errs[0])
+ }
+}
+
+// TestPlaceMultipleMarginOrdersOverPairLimits API logic test
+func TestPlaceMultipleMarginOrdersOverPairLimits(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ order := okgroup.PlaceSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ Type: "market",
+ Side: "buy",
+ MarginTrading: "1",
+ Size: "100",
+ Notional: "100",
+ }
+
+ request := []okgroup.PlaceSpotOrderRequest{
+ order,
+ }
+
+ order.InstrumentID = pair.NewCurrencyPairWithDelimiter(symbol.LTC, symbol.USDT, "-").Pair().Lower().String()
+ request = append(request, order)
+ order.InstrumentID = pair.NewCurrencyPairWithDelimiter(symbol.DOGE, symbol.USDT, "-").Pair().Lower().String()
+ request = append(request, order)
+ order.InstrumentID = pair.NewCurrencyPairWithDelimiter(symbol.XMR, symbol.USDT, "-").Pair().Lower().String()
+ request = append(request, order)
+ order.InstrumentID = pair.NewCurrencyPairWithDelimiter(symbol.BCH, symbol.USDT, "-").Pair().Lower().String()
+ request = append(request, order)
+
+ _, errs := o.PlaceMultipleMarginOrders(request)
+ if errs[0].Error() != "up to 4 trading pairs" {
+ t.Error("Expecting an error when more than 4 trading pairs supplied", errs[0])
+ }
+}
+
+// TestCancelMarginOrder API endpoint test
+func TestCancelMarginOrder(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ request := okgroup.CancelSpotOrderRequest{
+ InstrumentID: spotCurrency,
+ OrderID: 1234,
+ }
+
+ _, err := o.CancelMarginOrder(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestCancelMultipleMarginOrders API endpoint test
+func TestCancelMultipleMarginOrders(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ request := okgroup.CancelMultipleSpotOrdersRequest{
+ InstrumentID: spotCurrency,
+ OrderIDs: []int64{1, 2, 3, 4},
+ }
+
+ _, errs := o.CancelMultipleMarginOrders(request)
+ if len(errs) > 0 {
+ testStandardErrorHandling(t, errs[0])
+ }
+}
+
+// TestCancelMultipleMarginOrdersOverCurrencyLimits API logic test
+func TestCancelMultipleMarginOrdersOverCurrencyLimits(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ request := okgroup.CancelMultipleSpotOrdersRequest{
+ InstrumentID: spotCurrency,
+ OrderIDs: []int64{1, 2, 3, 4, 5},
+ }
+
+ _, errs := o.CancelMultipleMarginOrders(request)
+ if errs[0].Error() != "maximum 4 order cancellations for each pair" {
+ t.Error("Expecting an error when more than 4 orders for a pair supplied", errs[0])
+ }
+}
+
+// TestGetMarginOrders API endpoint test
+func TestGetMarginOrders(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ request := okgroup.GetSpotOrdersRequest{
+ InstrumentID: spotCurrency,
+ Status: "all",
+ }
+ _, err := o.GetMarginOrders(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetMarginOpenOrders API endpoint test
+func TestGetMarginOpenOrders(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ request := okgroup.GetSpotOpenOrdersRequest{}
+ _, err := o.GetMarginOpenOrders(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetMarginOrder API endpoint test
+func TestGetMarginOrder(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ request := okgroup.GetSpotOrderRequest{
+ OrderID: "1234",
+ InstrumentID: pair.NewCurrencyPairWithDelimiter(symbol.BTC, symbol.USDT, "-").Pair().Upper().String(),
+ }
+ _, err := o.GetMarginOrder(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetMarginTransactionDetails API endpoint test
+func TestGetMarginTransactionDetails(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ request := okgroup.GetSpotTransactionDetailsRequest{
+ OrderID: 1234,
+ InstrumentID: spotCurrency,
+ }
+ _, err := o.GetMarginTransactionDetails(request)
+ testStandardErrorHandling(t, err)
+}
+
+var genericFutureInstrumentID string
+
+// getFutureInstrumentID Future contract ids are date based without an easy way to calculate the closest valid date
+// This retrieves the value and stores it if running all tests so only one call is made
+func getFutureInstrumentID() string {
+ if genericFutureInstrumentID != "" {
+ return genericFutureInstrumentID
+ }
+ resp, err := o.GetFuturesContractInformation()
if err != nil {
- t.Error("Test failed - okex GetContractCandlestickData() error", err)
- }
- _, err = o.GetContractCandlestickData("btc_bla", "1min", "this_week", 1, 2)
- if err == nil {
- t.Error("Test failed - okex GetContractCandlestickData() error", err)
- }
- _, err = o.GetContractCandlestickData("btc_usd", "min", "this_week", 1, 2)
- if err == nil {
- t.Error("Test failed - okex GetContractCandlestickData() error", err)
- }
- _, err = o.GetContractCandlestickData("btc_usd", "1min", "this_wok", 1, 2)
- if err == nil {
- t.Error("Test failed - okex GetContractCandlestickData() error", err)
+ // No error handling here because we're not testing this
+ return err.Error()
}
+ genericFutureInstrumentID = resp[0].InstrumentID
+ return genericFutureInstrumentID
}
-func TestGetContractHoldingsNumber(t *testing.T) {
+// TestGetFuturesPostions API endpoint test
+func TestGetFuturesPostions(t *testing.T) {
+ TestSetDefaults(t)
t.Parallel()
- _, _, err := o.GetContractHoldingsNumber("btc_usd", "this_week")
+ _, err := o.GetFuturesPostions()
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetFuturesPostionsForCurrency API endpoint test
+func TestGetFuturesPostionsForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ currencyContract := getFutureInstrumentID()
+ _, err := o.GetFuturesPostionsForCurrency(currencyContract)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetFuturesAccountOfAllCurrencies API endpoint test
+func TestGetFuturesAccountOfAllCurrencies(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetFuturesAccountOfAllCurrencies()
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetFuturesAccountOfACurrency API endpoint test
+func TestGetFuturesAccountOfACurrency(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetFuturesAccountOfACurrency(symbol.BTC)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetFuturesLeverage API endpoint test
+func TestGetFuturesLeverage(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetFuturesLeverage(symbol.BTC)
+ testStandardErrorHandling(t, err)
+}
+
+// TestSetFuturesLeverage API endpoint test
+func TestSetFuturesLeverage(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ request := okgroup.SetFuturesLeverageRequest{
+ Currency: symbol.BTC,
+ InstrumentID: getFutureInstrumentID(),
+ Leverage: 10,
+ Direction: "Long",
+ }
+ _, err := o.SetFuturesLeverage(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetFuturesBillDetails API endpoint test
+func TestGetFuturesBillDetails(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetFuturesBillDetails(okgroup.GetSpotBillDetailsForCurrencyRequest{
+ Currency: symbol.BTC,
+ })
+ testStandardErrorHandling(t, err)
+}
+
+// TestPlaceFuturesOrder API endpoint test
+func TestPlaceFuturesOrder(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ _, err := o.PlaceFuturesOrder(okgroup.PlaceFuturesOrderRequest{
+ InstrumentID: getFutureInstrumentID(),
+ Leverage: 10,
+ Type: 1,
+ Size: 2,
+ Price: 432.11,
+ ClientOid: "12233456",
+ })
+ testStandardErrorHandling(t, err)
+}
+
+// TestPlaceFuturesOrderBatch API endpoint test
+func TestPlaceFuturesOrderBatch(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ _, err := o.PlaceFuturesOrderBatch(okgroup.PlaceFuturesOrderBatchRequest{
+ InstrumentID: getFutureInstrumentID(),
+ Leverage: 10,
+ OrdersData: []okgroup.PlaceFuturesOrderBatchRequestDetails{
+ {
+ ClientOid: "1",
+ MatchPrice: "0",
+ Price: "100",
+ Size: "100",
+ Type: "1",
+ },
+ },
+ })
+ testStandardErrorHandling(t, err)
+}
+
+// TestCancelFuturesOrder API endpoint test
+func TestCancelFuturesOrder(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ _, err := o.CancelFuturesOrder(okgroup.CancelFuturesOrderRequest{
+ InstrumentID: getFutureInstrumentID(),
+ OrderID: "1",
+ })
+ testStandardErrorHandling(t, err)
+}
+
+// TestCancelMultipleSpotOrders API endpoint test
+func TestCancelMultipleFuturesOrders(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ request := okgroup.CancelMultipleSpotOrdersRequest{
+ InstrumentID: getFutureInstrumentID(),
+ OrderIDs: []int64{1, 2, 3, 4},
+ }
+
+ _, err := o.CancelFuturesOrderBatch(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetFuturesOrderList API endpoint test
+func TestGetFuturesOrderList(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetFuturesOrderList(okgroup.GetFuturesOrdersListRequest{
+ InstrumentID: getFutureInstrumentID(),
+ Status: 6,
+ })
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetFuturesOrderDetails API endpoint test
+func TestGetFuturesOrderDetails(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetFuturesOrderDetails(okgroup.GetFuturesOrderDetailsRequest{
+ InstrumentID: getFutureInstrumentID(),
+ OrderID: 1,
+ })
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetFuturesTransactionDetails API endpoint test
+func TestGetFuturesTransactionDetails(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetFuturesTransactionDetails(okgroup.GetFuturesTransactionDetailsRequest{
+ InstrumentID: getFutureInstrumentID(),
+ OrderID: 1,
+ })
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetFuturesContractInformation API endpoint test
+func TestGetFuturesContractInformation(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetFuturesContractInformation()
if err != nil {
- t.Error("Test failed - okex GetContractHoldingsNumber() error", err)
- }
- _, _, err = o.GetContractHoldingsNumber("btc_bla", "this_week")
- if err == nil {
- t.Error("Test failed - okex GetContractHoldingsNumber() error", err)
- }
- _, _, err = o.GetContractHoldingsNumber("btc_usd", "this_bla")
- if err == nil {
- t.Error("Test failed - okex GetContractHoldingsNumber() error", err)
+ t.Error(err)
}
}
-func TestGetContractlimit(t *testing.T) {
- t.Parallel()
- _, err := o.GetContractlimit("btc_usd", "this_week")
- if err != nil {
- t.Error("Test failed - okex GetContractlimit() error", err)
- }
- _, err = o.GetContractlimit("btc_bla", "this_week")
- if err == nil {
- t.Error("Test failed - okex GetContractlimit() error", err)
- }
- _, err = o.GetContractlimit("btc_usd", "this_bla")
- if err == nil {
- t.Error("Test failed - okex GetContractlimit() error", err)
- }
-}
-
-func TestGetContractUserInfo(t *testing.T) {
- t.Parallel()
- err := o.GetContractUserInfo()
- if err == nil {
- t.Error("Test failed - okex GetContractUserInfo() error", err)
- }
-}
-
-func TestGetContractPosition(t *testing.T) {
- t.Parallel()
- err := o.GetContractPosition("btc_usd", "this_week")
- if err == nil {
- t.Error("Test failed - okex GetContractPosition() error", err)
- }
-}
-
-func TestPlaceContractOrders(t *testing.T) {
- t.Parallel()
- _, err := o.PlaceContractOrders("btc_usd", "this_week", "1", 10, 1, 1, true)
- if err == nil {
- t.Error("Test failed - okex PlaceContractOrders() error", err)
- }
-}
-
-func TestGetContractFuturesTradeHistory(t *testing.T) {
- t.Parallel()
- err := o.GetContractFuturesTradeHistory("btc_usd", "1972-01-01", 0)
- if err == nil {
- t.Error("Test failed - okex GetContractTradeHistory() error", err)
- }
-}
-
-func TestGetLatestSpotPrice(t *testing.T) {
- t.Parallel()
- _, err := o.GetLatestSpotPrice("ltc_btc")
- if err != nil {
- t.Error("Test failed - okex GetLatestSpotPrice() error", err)
- }
-}
-
-func TestGetSpotTicker(t *testing.T) {
- t.Parallel()
- _, err := o.GetSpotTicker("ltc_btc")
- if err != nil {
- t.Error("Test failed - okex GetSpotTicker() error", err)
- }
-}
-
-func TestGetSpotMarketDepth(t *testing.T) {
- t.Parallel()
- _, err := o.GetSpotMarketDepth(ActualSpotDepthRequestParams{
- Symbol: "eth_btc",
- Size: 2,
+// TestGetFuturesContractInformation API endpoint test
+func TestGetFuturesOrderBook(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetFuturesOrderBook(okgroup.GetFuturesOrderBookRequest{
+ InstrumentID: getFutureInstrumentID(),
+ Size: 10,
})
if err != nil {
- t.Error("Test failed - okex GetSpotMarketDepth() error", err)
+ t.Error(err)
}
}
-func TestGetSpotRecentTrades(t *testing.T) {
+// TestGetAllFuturesTokenInfo API endpoint test
+func TestGetAllFuturesTokenInfo(t *testing.T) {
+ TestSetDefaults(t)
t.Parallel()
- _, err := o.GetSpotRecentTrades(ActualSpotTradeHistoryRequestParams{
- Symbol: "ltc_btc",
- Since: 0,
+ _, err := o.GetAllFuturesTokenInfo()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetAllFuturesTokenInfo API endpoint test
+func TestGetFuturesTokenInfoForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetFuturesTokenInfoForCurrency(getFutureInstrumentID())
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetFuturesFilledOrder API endpoint test
+func TestGetFuturesFilledOrder(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetFuturesFilledOrder(okgroup.GetFuturesFilledOrderRequest{
+ InstrumentID: getFutureInstrumentID(),
})
if err != nil {
- t.Error("Test failed - okex GetSpotRecentTrades() error", err)
+ t.Error(err)
}
}
-func TestGetSpotKline(t *testing.T) {
- t.Parallel()
- arg := KlinesRequestParams{
- Symbol: "ltc_btc",
- Type: TimeIntervalFiveMinutes,
- Size: 100,
- }
- _, err := o.GetSpotKline(arg)
+// TestGetFuturesHoldAmount API endpoint test
+func TestGetFuturesHoldAmount(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetFuturesHoldAmount(getFutureInstrumentID())
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetFuturesHoldAmount API endpoint test
+func TestGetFuturesIndices(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetFuturesIndices(getFutureInstrumentID())
if err != nil {
- t.Error("Test failed - okex GetSpotCandleStick() error", err)
+ t.Error(err)
}
}
-func TestSpotNewOrder(t *testing.T) {
+// TestGetFuturesHoldAmount API endpoint test
+func TestGetFuturesExchangeRates(t *testing.T) {
+ TestSetDefaults(t)
t.Parallel()
-
- if o.APIKey == "" || o.APISecret == "" {
- t.Skip()
+ _, err := o.GetFuturesExchangeRates()
+ if err != nil {
+ t.Errorf("Encountered error: %v", err)
}
+}
- _, err := o.SpotNewOrder(SpotNewOrderRequestParams{
- Symbol: "ltc_btc",
- Amount: 1.1,
- Price: 10.1,
- Type: SpotNewOrderRequestTypeBuy,
+// TestGetFuturesHoldAmount API endpoint test
+func TestGetFuturesEstimatedDeliveryPrice(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetFuturesEstimatedDeliveryPrice(getFutureInstrumentID())
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetFuturesOpenInterests API endpoint test
+func TestGetFuturesOpenInterests(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetFuturesOpenInterests(getFutureInstrumentID())
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetFuturesOpenInterests API endpoint test
+func TestGetFuturesCurrentPriceLimit(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetFuturesCurrentPriceLimit(getFutureInstrumentID())
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetFuturesCurrentMarkPrice API endpoint test
+func TestGetFuturesCurrentMarkPrice(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetFuturesCurrentMarkPrice(getFutureInstrumentID())
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetFuturesForceLiquidatedOrders API endpoint test
+func TestGetFuturesForceLiquidatedOrders(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetFuturesForceLiquidatedOrders(okgroup.GetFuturesForceLiquidatedOrdersRequest{
+ InstrumentID: getFutureInstrumentID(),
+ Status: "1",
})
if err != nil {
- t.Error("Test failed - okex SpotNewOrder() error", err)
+ t.Error(err)
}
}
-func TestSpotCancelOrder(t *testing.T) {
+// TestGetFuturesTagPrice API endpoint test
+func TestGetFuturesTagPrice(t *testing.T) {
+ TestSetDefaults(t)
+ _, err := o.GetFuturesTagPrice(getFutureInstrumentID())
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSwapPostions API endpoint test
+func TestGetSwapPostions(t *testing.T) {
+ TestSetDefaults(t)
t.Parallel()
-
- if o.APIKey == "" || o.APISecret == "" {
- t.Skip()
- }
-
- _, err := o.SpotCancelOrder("ltc_btc", 519158961)
- if err != nil {
- t.Error("Test failed - okex SpotCancelOrder() error", err)
- }
+ _, err := o.GetSwapPostions()
+ testStandardErrorHandling(t, err)
}
-func TestGetUserInfo(t *testing.T) {
+// TestGetSwapPostionsForContract API endpoint test
+func TestGetSwapPostionsForContract(t *testing.T) {
+ TestSetDefaults(t)
t.Parallel()
+ _, err := o.GetSwapPostionsForContract(fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD))
+ testStandardErrorHandling(t, err)
+}
- if o.APIKey == "" || o.APISecret == "" {
- t.Skip()
- }
+// TestGetSwapAccountOfAllCurrency API endpoint test
+func TestGetSwapAccountOfAllCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSwapAccountOfAllCurrency()
+ testStandardErrorHandling(t, err)
+}
- _, err := o.GetUserInfo()
+// TestGetSwapAccountSettingsOfAContract API endpoint test
+func TestGetSwapAccountSettingsOfAContract(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSwapAccountSettingsOfAContract(fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD))
+ testStandardErrorHandling(t, err)
+}
+
+// TestSetSwapLeverageLevelOfAContract API endpoint test
+func TestSetSwapLeverageLevelOfAContract(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.SetSwapLeverageLevelOfAContract(okgroup.SetSwapLeverageLevelOfAContractRequest{
+ InstrumentID: fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD),
+ Leverage: 10,
+ Side: 1,
+ })
+
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSwapAccountSettingsOfAContract API endpoint test
+func TestGetSwapBillDetails(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSwapBillDetails(okgroup.GetSpotBillDetailsForCurrencyRequest{
+ Currency: fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD),
+ Limit: 100,
+ })
+ testStandardErrorHandling(t, err)
+}
+
+// TestPlaceSwapOrder API endpoint test
+func TestPlaceSwapOrder(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ _, err := o.PlaceSwapOrder(okgroup.PlaceSwapOrderRequest{
+ InstrumentID: fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD),
+ Size: 1,
+ Type: 1,
+ Price: 1,
+ })
+ testStandardErrorHandling(t, err)
+}
+
+// TestPlaceMultipleSwapOrders API endpoint test
+func TestPlaceMultipleSwapOrders(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ _, err := o.PlaceMultipleSwapOrders(okgroup.PlaceMultipleSwapOrdersRequest{
+ InstrumentID: fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD),
+ Leverage: 10,
+ OrdersData: []okgroup.PlaceMultipleSwapOrderData{
+ {
+ ClientOID: "hello",
+ MatchPrice: "0",
+ Price: "10",
+ Size: "1",
+ Type: "1",
+ }, {
+ ClientOID: "hello2",
+ MatchPrice: "0",
+ Price: "10",
+ Size: "1",
+ Type: "1",
+ }},
+ })
+ testStandardErrorHandling(t, err)
+}
+
+// TestCancelSwapOrder API endpoint test
+func TestCancelSwapOrder(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ _, err := o.CancelSwapOrder(okgroup.CancelSwapOrderRequest{
+ InstrumentID: fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD),
+ OrderID: "64-2a-26132f931-3",
+ })
+ testStandardErrorHandling(t, err)
+}
+
+// TestCancelMultipleSwapOrders API endpoint test
+func TestCancelMultipleSwapOrders(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ _, err := o.CancelMultipleSwapOrders(okgroup.CancelMultipleSwapOrdersRequest{
+ InstrumentID: fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD),
+ OrderIDs: []int64{1, 2, 3, 4},
+ })
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSwapOrderList API endpoint test
+func TestGetSwapOrderList(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSwapOrderList(okgroup.GetSwapOrderListRequest{
+ InstrumentID: fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD),
+ Status: 6,
+ })
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSwapOrderDetails API endpoint test
+func TestGetSwapOrderDetails(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSwapOrderDetails(okgroup.GetSwapOrderDetailsRequest{
+ InstrumentID: fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD),
+ OrderID: "64-2a-26132f931-3",
+ })
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSwapTransactionDetails API endpoint test
+func TestGetSwapTransactionDetails(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSwapTransactionDetails(okgroup.GetSwapTransactionDetailsRequest{
+ InstrumentID: fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD),
+ OrderID: "64-2a-26132f931-3",
+ })
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSwapContractInformation API endpoint test
+func TestGetSwapContractInformation(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSwapContractInformation()
if err != nil {
- t.Error("Test failed - okex GetUserInfo() error", err)
+ t.Error(err)
}
}
+// TestGetSwapOrderBook API endpoint test
+func TestGetSwapOrderBook(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSwapOrderBook(okgroup.GetSwapOrderBookRequest{
+ InstrumentID: fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD),
+ Size: 200,
+ })
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetAllSwapTokensInformation API endpoint test
+func TestGetAllSwapTokensInformation(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetAllSwapTokensInformation()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetSwapTokensInformationForCurrency API endpoint test
+func TestGetSwapTokensInformationForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSwapTokensInformationForCurrency(fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD))
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetSwapFilledOrdersData API endpoint test
+func TestGetSwapFilledOrdersData(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSwapFilledOrdersData(&okgroup.GetSwapFilledOrdersDataRequest{
+ InstrumentID: fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD),
+ Limit: 100,
+ })
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetSwapMarketData API endpoint test
+func TestGetSwapMarketData(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ request := okgroup.GetSwapMarketDataRequest{
+ InstrumentID: fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD),
+ Granularity: 604800,
+ }
+ _, err := o.GetSwapMarketData(request)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetSwapIndeces API endpoint test
+func TestGetSwapIndeces(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSwapIndeces(fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD))
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetSwapExchangeRates API endpoint test
+func TestGetSwapExchangeRates(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSwapExchangeRates()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetSwapOpenInterest API endpoint test
+func TestGetSwapOpenInterest(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSwapOpenInterest(fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD))
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetSwapCurrentPriceLimits API endpoint test
+func TestGetSwapCurrentPriceLimits(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSwapCurrentPriceLimits(fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD))
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetSwapForceLiquidatedOrders API endpoint test
+func TestGetSwapForceLiquidatedOrders(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSwapForceLiquidatedOrders(okgroup.GetSwapForceLiquidatedOrdersRequest{
+ InstrumentID: fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD),
+ Status: "0",
+ })
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetSwapOnHoldAmountForOpenOrders API endpoint test
+func TestGetSwapOnHoldAmountForOpenOrders(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSwapOnHoldAmountForOpenOrders(fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD))
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetSwapNextSettlementTime API endpoint test
+func TestGetSwapNextSettlementTime(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSwapNextSettlementTime(fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD))
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetSwapMarkPrice API endpoint test
+func TestGetSwapMarkPrice(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSwapMarkPrice(fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD))
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetSwapFundingRateHistory API endpoint test
+func TestGetSwapFundingRateHistory(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetSwapFundingRateHistory(okgroup.GetSwapFundingRateHistoryRequest{
+ InstrumentID: fmt.Sprintf("%v-%v-SWAP", symbol.BTC, symbol.USD),
+ Limit: 100,
+ })
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetETT API endpoint test
+func TestGetETT(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetETT()
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetETTAccountInformationForCurrency API endpoint test
+func TestGetETTAccountInformationForCurrency(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetETTBillsDetails(symbol.BTC)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetETTBillsDetails API endpoint test
+func TestGetETTBillsDetails(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetETTBillsDetails(symbol.BTC)
+ testStandardErrorHandling(t, err)
+}
+
+// TestPlaceETTOrder API endpoint test
+func TestPlaceETTOrder(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ request := okgroup.PlaceETTOrderRequest{
+ QuoteCurrency: spotCurrency,
+ Type: 0,
+ Size: "100",
+ Amount: 1,
+ ETT: "OK06",
+ }
+
+ _, err := o.PlaceETTOrder(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestCancelETTOrder API endpoint test
+func TestCancelETTOrder(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ _, err := o.CancelETTOrder("888845120785408")
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetETTOrderList API endpoint test
+// This results in a 500 error when its a request object
+// Or when it is submitted as URL params
+// Unsure how to fix
+func TestGetETTOrderList(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ request := okgroup.GetETTOrderListRequest{
+ Type: 1,
+ ETT: "OK06ETT",
+ Status: 0,
+ }
+
+ _, err := o.GetETTOrderList(request)
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetETTOrderDetails API endpoint test
+func TestGetETTOrderDetails(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetETTOrderDetails("888845020785408")
+ testStandardErrorHandling(t, err)
+}
+
+// TestGetETTConstituents API endpoint test
+func TestGetETTConstituents(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetETTConstituents("OK06ETT")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestGetETTSettlementPriceHistory API endpoint test
+func TestGetETTSettlementPriceHistory(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ _, err := o.GetETTSettlementPriceHistory("OK06ETT")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// Websocket tests ----------------------------------------------------------------------------------------------
+
+// TestWsLogin API endpoint test
+func TestWsLogin(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
+ if o.WebsocketConn == nil {
+ o.Websocket.Shutdown()
+ err := setupWSConnection()
+ if err != nil {
+ t.Error(err)
+ }
+ }
+ if !o.Websocket.IsConnected() {
+ t.Skip("Could not connect to websocket. Skipping")
+ }
+ err := o.WsLogin()
+ if err != nil {
+ t.Error(err)
+ }
+ var errorReceived bool
+ for i := 0; i < 5; i++ {
+ response := <-o.Websocket.DataHandler
+ if err, ok := response.(error); ok && err != nil {
+ t.Log(response)
+ errorReceived = true
+ }
+ }
+ if errorReceived {
+ t.Error("Expecting no errors")
+ }
+}
+
+// TestSubscribeToChannel API endpoint test
+func TestSubscribeToChannel(t *testing.T) {
+ TestSetDefaults(t)
+ if o.WebsocketConn == nil {
+ o.Websocket.Shutdown()
+ err := setupWSConnection()
+ if err != nil {
+ t.Error(err)
+ }
+ }
+ if !o.Websocket.IsConnected() {
+ t.Skip("Could not connect to websocket. Skipping")
+ }
+ channelName := "spot/depth:LTC-BTC"
+ err := o.WsSubscribeToChannel(channelName)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ var errorReceived bool
+ for i := 0; i < 5; i++ {
+ response := <-o.Websocket.DataHandler
+ if err, ok := response.(error); ok && err != nil {
+ t.Log(response)
+ if strings.Contains(response.(error).Error(), channelName) {
+ errorReceived = true
+ }
+ }
+ }
+
+ if errorReceived {
+ t.Error("Expecting subscription to channel")
+ }
+}
+
+// TestSubscribeToNonExistantChannel Logic test
+// Attempts to subscribe to a channel that doesn't exist
+// Then captures the error response
+func TestSubscribeToNonExistantChannel(t *testing.T) {
+ defer disconnectFromWS()
+ TestSetDefaults(t)
+ if o.WebsocketConn == nil {
+ o.Websocket.Shutdown()
+ err := setupWSConnection()
+ if err != nil {
+ t.Error(err)
+ }
+ }
+ if !o.Websocket.IsConnected() {
+ t.Skip("Could not connect to websocket. Skipping")
+ }
+ channelName := "badChannel"
+ err := o.WsSubscribeToChannel(channelName)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ var errorReceived bool
+ for i := 0; i < 5; i++ {
+ response := <-o.Websocket.DataHandler
+ if err, ok := response.(error); ok && err != nil {
+ t.Log(response)
+ if strings.Contains(response.(error).Error(), channelName) {
+ errorReceived = true
+ }
+ }
+ }
+
+ if !errorReceived {
+ t.Error("Expecting OKEX error - 30040 message: Channel badChannel doesn't exist")
+ }
+}
+
+// TestGetAssetTypeFromTableName logic test
+func TestGetAssetTypeFromTableName(t *testing.T) {
+ str := "spot/candle300s:BTC-USDT"
+ spot := o.GetAssetTypeFromTableName(str)
+ if spot != "SPOT" {
+ t.Errorf("Error, expected 'SPOT', received: '%v'", spot)
+ }
+}
+
+// TestGetWsChannelWithoutOrderType logic test
+func TestGetWsChannelWithoutOrderType(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ str := "spot/depth5:BTC-USDT"
+ expected := "depth5"
+ resp := o.GetWsChannelWithoutOrderType(str)
+ if resp != expected {
+ t.Errorf("Logic change error %v should be %v", resp, expected)
+ }
+ str = "spot/depth"
+ resp = o.GetWsChannelWithoutOrderType(str)
+ expected = "depth"
+ if resp != expected {
+ t.Errorf("Logic change error %v should be %v", resp, expected)
+ }
+ str = "testWithBadData"
+ resp = o.GetWsChannelWithoutOrderType(str)
+ if resp != str {
+ t.Errorf("Logic change error %v should be %v", resp, str)
+ }
+}
+
+// TestOrderBookUpdateChecksumCalculator logic test
+func TestOrderBookUpdateChecksumCalculator(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ if o.WebsocketConn == nil {
+ o.Websocket.Shutdown()
+ err := setupWSConnection()
+ if err != nil {
+ t.Error(err)
+ }
+ }
+ disconnectFromWS()
+ original := `{"table":"spot/depth","action":"partial","data":[{"instrument_id":"BTC-USDT","asks":[["3864.6786","0.145",1],["3864.7682","0.005",1],["3864.9851","0.57",1],["3864.9852","0.30137754",1],["3864.9986","2.81818419",1],["3864.9995","0.002",1],["3865","0.0597",1],["3865.0309","0.4",1],["3865.1995","0.004",1],["3865.3995","0.004",1],["3865.5995","0.004",1],["3865.7995","0.004",1],["3865.9995","0.004",1],["3866.0961","0.25865886",1],["3866.1995","0.004",1],["3866.3995","0.004",1],["3866.4004","0.3243",2],["3866.5995","0.004",1],["3866.7633","0.44247086",1],["3866.7995","0.004",1],["3866.9197","0.511",1],["3867.256","0.51716256",1],["3867.3951","0.02588112",1],["3867.4014","0.025",1],["3867.4566","0.02499999",1],["3867.4675","4.01155057",5],["3867.5515","1.1",1],["3867.6113","0.009",1],["3867.7349","0.026",1],["3867.7781","0.03738652",1],["3867.9163","0.0521",1],["3868.0381","0.34354941",1],["3868.0436","0.051",1],["3868.0657","0.90552172",3],["3868.1819","0.03863346",1],["3868.2013","0.194",1],["3868.346","0.051",1],["3868.3863","0.01155",1],["3868.7716","0.009",1],["3868.947","0.025",1],["3868.98","0.001",1],["3869.0764","1.03487931",1],["3869.2773","0.07724578",1],["3869.4039","0.025",1],["3869.4068","1.03",1],["3869.7068","2.06976398",1],["3870","0.5",1],["3870.0465","0.01",1],["3870.7042","0.02099651",1],["3870.9451","2.07047375",1],["3871.5254","1.2",1],["3871.5596","0.001",1],["3871.6605","0.01035032",1],["3871.7179","2.07047375",1],["3871.8816","0.51751625",1],["3872.1","0.75",1],["3872.2464","0.0646",1],["3872.3747","0.283",1],["3872.4039","0.2",1],["3872.7655","0.23179307",1],["3872.8005","2.06976398",1],["3873.1509","2",1],["3873.3215","0.26",1],["3874.1392","0.001",1],["3874.1487","3.88224364",4],["3874.1685","1.8",1],["3874.5571","0.08974762",1],["3874.734","2.06976398",1],["3874.99","0.3",1],["3875","1.001",2],["3875.0041","1.03505051",1],["3875.45","0.3",1],["3875.4766","0.15",1],["3875.7057","0.51751625",1],["3876","0.001",1],["3876.68","0.3",1],["3876.7188","0.001",1],["3877","0.75",1],["3877.31","0.035",1],["3877.38","0.3",1],["3877.7","0.3",1],["3877.88","0.3",1],["3878.0364","0.34770122",1],["3878.4525","0.48579748",1],["3878.4955","0.02812511",1],["3878.8855","0.00258579",1],["3878.9605","0.895",1],["3879","0.001",1],["3879.2984","0.002",2],["3879.432","0.001",1],["3879.6313","6",1],["3879.9999","0.002",2],["3880","1.25132834",5],["3880.2526","0.04075162",1],["3880.7145","0.0647",1],["3881.2469","1.883",1],["3881.878","0.002",2],["3884.4576","0.002",2],["3885","0.002",2],["3885.2233","0.28304103",1],["3885.7416","18",1],["3886","0.001",1],["3886.1554","5.4",1],["3887","0.001",1],["3887.0372","0.002",2],["3887.2559","0.05214011",1],["3887.9238","0.0019",1],["3888","0.15810538",4],["3889","0.001",1],["3889.5175","0.50510653",1],["3889.6168","0.002",2],["3889.9999","0.001",1],["3890","2.34968109",4],["3890.5222","0.00257806",1],["3891.2659","5",1],["3891.9999","0.00893897",1],["3892.1964","0.002",2],["3892.4358","0.0176",1],["3893.1388","1.4279",1],["3894","0.0026321",1],["3894.776","0.001",1],["3895","1.501",2],["3895.379","0.25881288",1],["3897","0.05",1],["3897.3556","0.001",1],["3897.8432","0.73708079",1],["3898","3.31353018",7],["3898.4462","4.757",1],["3898.6","0.47159638",1],["3898.8769","0.0129",1],["3899","6",2],["3899.6516","0.025",1],["3899.9352","0.001",1],["3899.9999","0.013",2],["3900","22.37447743",24],["3900.9999","0.07763916",1],["3901","0.10192487",1],["3902.1937","0.00257034",1],["3902.3991","1.5532141",1],["3902.5148","0.001",1],["3904","1.49331984",1],["3904.9999","0.95905447",1],["3905","0.501",2],["3905.0944","0.001",1],["3905.61","0.099",1],["3905.6801","0.54343686",1],["3906.2901","0.0258",1],["3907.674","0.001",1],["3907.85","1.35778084",1],["3908","0.03846153",1],["3908.23","1.95189531",1],["3908.906","0.03148978",1],["3909","0.001",1],["3909.9999","0.01398721",2],["3910","0.016",2],["3910.2536","0.001",1],["3912.5406","0.88270517",1],["3912.8332","0.001",1],["3913","1.2640608",1],["3913.87","1.69114184",1],["3913.9003","0.00256266",1],["3914","1.21766411",1],["3915","0.001",1],["3915.4128","0.001",1],["3915.7425","6.848",1],["3916","0.0050949",1],["3917.36","1.28658296",1],["3917.9924","0.001",1],["3919","0.001",1],["3919.9999","0.001",1],["3920","1.21171832",3],["3920.0002","0.20217038",1],["3920.572","0.001",1],["3921","0.128",1],["3923.0756","0.00148064",1],["3923.1516","0.001",1],["3923.86","1.38831714",1],["3925","0.01867801",2],["3925.642","0.00255499",1],["3925.7312","0.001",1],["3926","0.04290757",1],["3927","0.023",1],["3927.3175","0.01212865",1],["3927.65","1.51375612",1],["3928","0.5",1],["3928.3108","0.001",1],["3929","0.001",1],["3929.9999","0.01519338",2],["3930","0.0174985",3],["3930.21","1.49335799",1],["3930.8904","0.001",1],["3932.2999","0.01953",1],["3932.8962","7.96",1],["3933.0387","11.808",1],["3933.47","0.001",1],["3934","1.40839932",1],["3935","0.001",1],["3936.8","0.62879518",1],["3937.23","1.56977841",1],["3937.4189","0.00254735",1]],"bids":[["3864.5217","0.00540709",1],["3864.5216","0.14068758",2],["3864.2275","0.01033576",1],["3864.0989","0.00825047",1],["3864.0273","0.38",1],["3864.0272","0.4",1],["3863.9957","0.01083539",1],["3863.9184","0.01653723",1],["3863.8282","0.25588165",1],["3863.8153","0.154",1],["3863.7791","1.14122492",1],["3863.6866","0.01733662",1],["3863.6093","0.02645958",1],["3863.3775","0.02773862",1],["3863.0297","0.513",1],["3863.0286","1.1028564",2],["3862.8489","0.01",1],["3862.5972","0.01890179",1],["3862.3431","0.01152944",1],["3862.313","0.009",1],["3862.2445","0.90551002",3],["3862.0734","0.014",1],["3862.0539","0.64976067",1],["3861.8586","0.025",1],["3861.7888","0.025",1],["3861.7673","0.008",1],["3861.5785","0.01",1],["3861.3895","0.005",1],["3861.3338","0.25875855",1],["3861.161","0.01",1],["3861.1111","0.03863352",1],["3861.0732","0.51703882",1],["3860.9116","0.17754895",1],["3860.75","0.19",1],["3860.6554","0.015",1],["3860.6172","0.005",1],["3860.6088","0.008",1],["3860.4724","0.12940042",1],["3860.4424","0.25880084",1],["3860.42","0.01",1],["3860.3725","0.51760102",1],["3859.8449","0.005",1],["3859.8285","0.03738652",1],["3859.7638","0.07726703",1],["3859.4502","0.008",1],["3859.3772","0.05173471",1],["3859.3409","0.194",1],["3859","5",1],["3858.827","0.0521",1],["3858.8208","0.001",1],["3858.679","0.26",1],["3858.4814","0.07477305",1],["3858.1669","1.03503422",1],["3857.6005","0.006",1],["3857.4005","0.004",1],["3857.2005","0.004",1],["3857.1871","1.218",1],["3857.0005","0.004",1],["3856.8135","0.0646",1],["3856.8005","0.004",1],["3856.2412","0.001",1],["3856.2349","1.03503422",1],["3856.0197","0.01037339",1],["3855.8781","0.23178117",1],["3855.8005","0.004",1],["3855.7165","0.00259355",1],["3855.4858","0.25875855",1],["3854.4584","0.01",1],["3853.6616","0.001",1],["3853.1373","0.92",1],["3852.5072","0.48599702",1],["3851.3926","0.13008333",1],["3851.082","0.001",1],["3850.9317","2",1],["3850.6359","0.34770165",1],["3850.2058","0.51751624",1],["3850.0823","0.15",1],["3850.0042","0.5175171",1],["3850","0.001",1],["3849.6325","1.8",1],["3849.41","0.3",1],["3848.9686","1.85",1],["3848.7426","0.18511466",1],["3848.52","0.3",1],["3848.5024","0.001",1],["3848.42","0.3",1],["3848.1618","2.204",1],["3847.77","0.3",1],["3847.48","0.3",1],["3847.3581","2.05",1],["3846.8259","0.0646",1],["3846.59","0.3",1],["3846.49","0.3",1],["3845.9228","0.001",1],["3844.184","0.00260133",1],["3844.0092","6.3",1],["3843.3432","0.001",1],["3841","0.06300963",1],["3840.7636","0.001",1],["3840","0.201",3],["3839.7681","18",1],["3839.5328","0.05214011",1],["3838.184","0.001",1],["3837.2344","0.27589557",1],["3836.6479","5.2",1],["3836","2.37196773",3],["3835.6044","0.001",1],["3833.6053","0.25873556",1],["3833.0248","0.001",1],["3833","0.8726502",1],["3832.6859","0.00260913",1],["3832","0.007",1],["3831.637","6",1],["3831.0602","0.001",1],["3830.4452","0.001",1],["3830","0.20375718",4],["3829.7125","0.07833486",1],["3829.6283","0.3519681",1],["3829","0.0039261",1],["3827.8656","0.001",1],["3826.0001","0.53251232",1],["3826","0.0509",1],["3825.7834","0.00698562",1],["3825.286","0.001",1],["3823.0001","0.03010127",1],["3822.8014","0.00261588",1],["3822.7064","0.001",1],["3822.2","1",1],["3822.1121","0.35994101",1],["3821.2222","0.00261696",1],["3821","0.001",1],["3820.1268","0.001",1],["3820","1.12992803",4],["3819","0.01331195",2],["3817.5472","0.001",1],["3816","1.13807184",2],["3815.8343","0.32463428",1],["3815.7834","0.00525295",1],["3815","28.99386799",4],["3814.9676","0.001",1],["3813","0.91303023",4],["3812.388","0.002",2],["3811.2257","0.07",1],["3810","0.32573997",2],["3809.8084","0.001",1],["3809.7928","0.00262481",1],["3807.2288","0.001",1],["3806.8421","0.07003461",1],["3806","0.19",1],["3805.8041","0.05678805",1],["3805","1.01",2],["3804.6492","0.001",1],["3804.3551","0.1",1],["3803","0.005",1],["3802.22","2.05042631",1],["3802.0696","0.001",1],["3802","1.63290092",1],["3801.2257","0.07",1],["3801","57.4",3],["3800.9853","0.02492278",1],["3800.8421","0.06503533",1],["3800.7844","0.02812628",1],["3800.0001","0.00409473",1],["3800","17.91401074",15],["3799.49","0.001",1],["3799","0.1",1],["3796.9104","0.001",1],["3796","9.00128053",2],["3795.5441","0.0028",1],["3794.3308","0.001",1],["3791","55",1],["3790.7777","0.07",1],["3790","12.03238184",7],["3789","1",1],["3788","0.21110454",2],["3787.2959","9",1],["3786.592","0.001",1],["3786","9.01916822",2],["3785","12.87914268",5],["3784.0124","0.001",1],["3781.4328","0.002",2],["3781","56.3",2],["3780.7777","0.07",1],["3780","23.41537654",10],["3778.8532","0.002",2],["3776","9",1],["3774","0.003",1],["3772.2481","0.06901672",1],["3771","55.1",2],["3770.7777","0.07",1],["3770","7.30268416",5],["3769","0.25",1],["3768","1.3725",3],["3766.66","0.02",1],["3766","7.64837924",2],["3765.58","1.22775492",1],["3762.58","1.22873383",1],["3761","51.68262164",1],["3760.8031","0.0399",1],["3760.7777","0.07",1]],"timestamp":"2019-03-06T23:19:17.705Z","checksum":-1785549915}]}`
+ update := `{"table":"spot/depth","action":"update","data":[{"instrument_id":"BTC-USDT","asks":[["3864.6786","0",0],["3864.9852","0",0],["3865.9994","0.48402971",1],["3866.4004","0.001",1],["3866.7995","0.3273",2],["3867.4566","0",0],["3867.7031","0.025",1],["3868.0436","0",0],["3868.346","0",0],["3868.3695","0.051",1],["3870.9243","0.642",1],["3874.9942","0.51751796",1],["3875.7057","0",0],["3939","0.001",1]],"bids":[["3864.55","0.0565449",1],["3863.8282","0",0],["3863.8153","0",0],["3863.7898","0.01320077",1],["3863.4807","0.02112123",1],["3863.3002","0.04233533",1],["3863.1717","0.03379397",1],["3863.0685","0.04438179",1],["3863.0286","0.7362564",1],["3862.9912","0.06773651",1],["3862.8626","0.05407035",1],["3862.7595","0.07101087",1],["3862.313","0.3756",2],["3862.1848","0.012",1],["3862.0734","0",0],["3861.8391","0.025",1],["3861.7888","0",0],["3856.6716","0.38893641",1],["3768","0",0],["3766.66","0",0],["3766","0",0],["3765.58","0",0],["3762.58","0",0],["3761","0",0],["3760.8031","0",0],["3760.7777","0",0]],"timestamp":"2019-03-06T23:19:18.239Z","checksum":-1587788848}]}`
+ var dataResponse okgroup.WebsocketDataResponse
+ err := common.JSONDecode([]byte(original), &dataResponse)
+ if err != nil {
+ t.Error(err)
+ }
+ err = o.WsProcessOrderBook(&dataResponse)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ var updateResponse okgroup.WebsocketDataResponse
+ err = common.JSONDecode([]byte(update), &updateResponse)
+ if err != nil {
+ t.Error(err)
+ }
+ time.Sleep(2 * time.Second)
+ err = o.WsProcessOrderBook(&updateResponse)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestOrderBookUpdateChecksumCalculatorWithDash logic test
+func TestOrderBookUpdateChecksumCalculatorWith8DecimalPlaces(t *testing.T) {
+ TestSetDefaults(t)
+ t.Parallel()
+ if o.WebsocketConn == nil {
+ o.Websocket.Shutdown()
+ err := setupWSConnection()
+ if err != nil {
+ t.Error(err)
+ }
+ }
+ disconnectFromWS()
+ original := `{"table":"spot/depth","action":"partial","data":[{"instrument_id":"WAVES-BTC","asks":[["0.000714","1.15414979",1],["0.000715","3.3",2],["0.000717","426.71348",2],["0.000719","140.84507042",1],["0.00072","590.77",1],["0.000721","991.77",1],["0.000724","0.3532032",1],["0.000725","58.82698567",1],["0.000726","1033.15469748",2],["0.000729","0.35320321",1],["0.00073","352.77",1],["0.000735","0.38469748",1],["0.000736","625.77",1],["0.00075191","152.44796961",1],["0.00075192","114.3359772",1],["0.00075193","85.7519829",1],["0.00075194","64.31398718",1],["0.00075195","48.23549038",1],["0.00075196","36.17661779",1],["0.00075199","61.04804253",1],["0.0007591","70.71318474",1],["0.0007621","53.03488855",1],["0.00076211","39.77616642",1],["0.00076212","29.83212481",1],["0.0007635","22.37409361",1],["0.00076351","29.36599786",2],["0.00076352","9.43907074",1],["0.00076353","7.07930306",1],["0.00076354","14.15860612",1],["0.00076355","3.53965153",1],["0.00076369","3.53965153",1],["0.0008","34.36841101",1],["0.00082858","1.69936503",1],["0.00083232","2.8",1],["0.00084","15.69220129",1],["0.00085","4.42785042",1],["0.00088","0.1",1],["0.000891","0.1",1],["0.0009","12.41486491",2],["0.00093","5",1],["0.0012","12.31486492",1],["0.00531314","6.91803114",1],["0.00799999","0.02",1],["0.0084","0.05989",1],["0.00931314","5.18852336",1],["0.0799999","0.02",1],["0.499","6.00423396",1],["0.5","0.4995",1],["0.799999","0.02",1],["4.99","2",1],["5","3.98583144",1],["7.99999999","0.02",1],["79.99999999","0.02",1],["799.99999999","0.02986704",1]],"bids":[["0.000709","222.91679881",3],["0.000703","0.47161952",1],["0.000701","140.73015789",2],["0.0007","0.3",1],["0.000699","401",1],["0.000698","232.61801667",2],["0.000689","0.71396896",1],["0.000688","0.69910125",1],["0.000613","227.54771052",1],["0.0005","0.01",1],["0.00026789","3.69905341",1],["0.000238","2.4",1],["0.00022","0.53",1],["0.0000055","374.09871696",1],["0.00000056","222",1],["0.00000055","736.84761363",1],["0.0000002","999",1],["0.00000009","1222.22222417",1],["0.00000008","20868.64520447",1],["0.00000002","110000",1],["0.00000001","10000",1]],"timestamp":"2019-03-12T22:22:42.274Z","checksum":1319037905}]}`
+ update := `{"table":"spot/depth","action":"update","data":[{"instrument_id":"WAVES-BTC","asks":[["0.000715","100.48199596",3],["0.000716","62.21679881",1]],"bids":[["0.000713","38.95772168",1]],"timestamp":"2019-03-12T22:22:42.938Z","checksum":-131160897}]}`
+ var dataResponse okgroup.WebsocketDataResponse
+ err := common.JSONDecode([]byte(original), &dataResponse)
+ if err != nil {
+ t.Error(err)
+ }
+ err = o.WsProcessOrderBook(&dataResponse)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ var updateResponse okgroup.WebsocketDataResponse
+ err = common.JSONDecode([]byte(update), &updateResponse)
+ if err != nil {
+ t.Error(err)
+ }
+ time.Sleep(2 * time.Second)
+ err = o.WsProcessOrderBook(&updateResponse)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// TestOrderBookPartialChecksumCalculator logic test
+func TestOrderBookPartialChecksumCalculator(t *testing.T) {
+ orderbookPartialJSON := `{"table":"spot/depth","action":"partial","data":[{"instrument_id":"EOS-USDT","asks":[["3.5196","0.1077",1],["3.5198","21.71",1],["3.5199","51.1805",1],["3.5208","75.09",1],["3.521","196.3333",1],["3.5213","0.1",1],["3.5218","39.276",2],["3.5219","395.6334",1],["3.522","27.956",1],["3.5222","404.9595",1],["3.5225","300",1],["3.5227","143.5442",2],["3.523","42.4746",1],["3.5231","852.64",2],["3.5235","34.9602",1],["3.5237","442.0918",2],["3.5238","352.8404",2],["3.5239","341.6759",2],["3.524","84.9493",1],["3.5241","148.4882",1],["3.5242","261.64",1],["3.5243","142.045",1],["3.5246","10",1],["3.5247","284.0788",1],["3.5248","720",1],["3.5249","89.2518",2],["3.5251","1201.8965",2],["3.5254","426.2938",1],["3.5255","213.0863",1],["3.5257","568.1576",1],["3.5258","0.3",1],["3.5259","34.4602",1],["3.526","0.1",1],["3.5263","850.771",1],["3.5265","5.9",1],["3.5268","10.5064",2],["3.5272","1136.8965",1],["3.5274","255.1481",1],["3.5276","29.5374",1],["3.5278","50",1],["3.5282","284.1797",1],["3.5283","1136.8965",1],["3.5284","0.4275",1],["3.5285","100",1],["3.5292","90.9",1],["3.5298","0.2",1],["3.5303","568.1576",1],["3.5305","279.9999",1],["3.532","0.409",1],["3.5321","568.1576",1],["3.5326","6016.8756",1],["3.5328","4.9849",1],["3.533","92.88",2],["3.5343","1200.2383",2],["3.5344","100",1],["3.535","359.7047",1],["3.5354","100",1],["3.5355","100",1],["3.5356","10",1],["3.5358","200",2],["3.5362","435.139",1],["3.5365","2152",1],["3.5366","284.1756",1],["3.5367","568.4644",1],["3.5369","33.9878",1],["3.537","337.1191",2],["3.5373","0.4045",1],["3.5383","1136.7188",1],["3.5386","12.1614",1],["3.5387","90.89",1],["3.54","4.54",1],["3.5423","90.8",1],["3.5436","0.1",1],["3.5454","853.4156",1],["3.5468","142.0656",1],["3.5491","0.0008",1],["3.55","14478.8206",6],["3.5537","21521",1],["3.5555","11.53",1],["3.5573","50.6001",1],["3.5599","4591.4221",1],["3.56","1227.0002",4],["3.5603","2670",1],["3.5608","58.6638",1],["3.5613","0.1",1],["3.5621","45.9473",1],["3.57","2141.7274",3],["3.5712","2956.9816",1],["3.5717","27.9978",1],["3.5718","0.9285",1],["3.5739","299.73",1],["3.5761","864",1],["3.579","22.5225",1],["3.5791","38.26",2],["3.58","7618.4634",5],["3.5801","457.2184",1],["3.582","24.5",1],["3.5822","1572.6425",1],["3.5845","14.1438",1],["3.585","527.169",1],["3.5865","20",1],["3.5867","4490",1],["3.5876","39.0493",1],["3.5879","392.9083",1],["3.5888","436.42",2],["3.5896","50",1],["3.59","2608.9128",8],["3.5913","19.5246",1],["3.5938","7082",1],["3.597","0.1",1],["3.5979","399",1],["3.5995","315.1509",1],["3.5999","2566.2648",1],["3.6","18511.2292",35],["3.603","22.3379",2],["3.605","499.5",1],["3.6055","100",1],["3.6058","499.5",1],["3.608","1021.1485",1],["3.61","11755.4596",13],["3.611","42.8571",1],["3.6131","6690",1],["3.6157","19.5247",1],["3.618","2500",1],["3.6197","525.7146",1],["3.6198","0.4455",1],["3.62","6440.6295",8],["3.6219","0.4175",1],["3.6237","168",1],["3.6265","0.1001",1],["3.628","64.9345",1],["3.63","4435.4985",6],["3.6308","1.7815",1],["3.6331","0.1",1],["3.6338","355.527",2],["3.6358","50",1],["3.6363","2074.7096",1],["3.6376","4000",1],["3.6396","11090",1],["3.6399","0.4055",1],["3.64","4161.9805",4],["3.6437","117.6524",1],["3.648","190",1],["3.6488","200",1],["3.65","11740.5045",25],["3.6512","0.1",1],["3.6521","728",1],["3.6555","100",1],["3.6598","36.6914",1],["3.66","4331.2148",6],["3.6638","200",1],["3.6673","100",1],["3.6679","38",1],["3.6688","2",1],["3.6695","0.1",1],["3.67","7984.698",6],["3.672","300",1],["3.6777","257.8247",1],["3.6789","393.4217",2],["3.68","9202.3222",11],["3.6818","500",1],["3.6823","299.7",1],["3.6839","422.3748",1],["3.685","100",1],["3.6878","0.1",1],["3.6888","72.0958",2],["3.6889","2876",1],["3.689","28",1],["3.6891","28",1],["3.6892","28",1],["3.6895","28",1],["3.6898","28",1],["3.69","643.96",7],["3.6908","118",2],["3.691","28",1],["3.6916","28",1],["3.6918","28",1],["3.6926","28",1],["3.6928","28",1],["3.6932","28",1],["3.6933","200",1],["3.6935","28",1],["3.6936","28",1],["3.6938","28",1],["3.694","28",1],["3.698","1498.5",1],["3.6988","2014.2004",2],["3.7","21904.2689",22],["3.7029","71.95",1],["3.704","3690.1362",1],["3.7055","100",1],["3.7063","0.1",1],["3.71","4421.3468",4],["3.719","17.3491",1],["3.72","1304.5995",3],["3.7211","10",1],["3.7248","0.1",1],["3.725","1900",1],["3.73","31.1785",2],["3.7375","38",1]],"bids":[["3.5182","151.5343",6],["3.5181","0.3691",1],["3.518","271.3967",2],["3.5179","257.8352",1],["3.5178","12.3811",1],["3.5173","34.1921",2],["3.5171","1013.8256",2],["3.517","272.1119",2],["3.5168","395.3376",1],["3.5166","317.1756",2],["3.5165","348.302",3],["3.5164","142.0414",1],["3.5163","96.8933",2],["3.516","600.1034",3],["3.5159","27.481",1],["3.5158","27.33",1],["3.5157","583.1898",2],["3.5156","24.6819",2],["3.5154","25",1],["3.5153","0.429",1],["3.5152","453.9204",3],["3.5151","2131.592",4],["3.515","335",3],["3.5149","37.1586",1],["3.5147","41.6759",1],["3.5146","54.569",1],["3.5145","70.3515",1],["3.5143","68.206",3],["3.5142","359.4538",2],["3.5139","45.4123",2],["3.5137","71.673",2],["3.5136","25",1],["3.5135","300",1],["3.5134","442.57",2],["3.5132","83.3518",1],["3.513","1245.2529",3],["3.5127","20",1],["3.512","284.1353",1],["3.5119","1136.8319",1],["3.5113","56.9351",1],["3.5111","588.1898",2],["3.5109","255.0946",1],["3.5105","48.65",1],["3.5103","50.2",1],["3.5098","720",1],["3.5096","148.95",1],["3.5094","570.5758",2],["3.509","2.386",1],["3.5089","0.4065",1],["3.5087","282.3859",2],["3.5086","145.036",2],["3.5084","2.386",1],["3.5082","90.98",1],["3.5081","2.386",1],["3.5079","2.386",1],["3.5078","857.6229",2],["3.5075","2.386",1],["3.5074","284.1877",1],["3.5073","100",1],["3.5071","100",1],["3.507","768.4159",3],["3.5069","313.0863",2],["3.5068","426.2938",1],["3.5066","568.3594",1],["3.5063","1136.6865",1],["3.5059","0.3",1],["3.5054","9.9999",1],["3.5053","0.2",1],["3.5051","392.428",1],["3.505","13.79",1],["3.5048","99.5497",2],["3.5047","78.5331",2],["3.5046","2153",1],["3.5041","5983.999",1],["3.5037","668.5682",1],["3.5036","160.5948",1],["3.5024","534.8075",1],["3.5014","28.5604",1],["3.5011","91",1],["3.5","1058.8771",2],["3.4997","50.2",1],["3.4985","3430.0414",1],["3.4949","232.0591",1],["3.4942","21521",1],["3.493","2",1],["3.4928","2",1],["3.4925","0.44",1],["3.4917","142.0656",1],["3.49","2051.8826",4],["3.488","280.7459",1],["3.4852","643.4038",1],["3.4851","86.0807",1],["3.485","213.2436",1],["3.484","0.1",1],["3.4811","144.3399",1],["3.4808","89",1],["3.4803","12.1999",1],["3.4801","2390",1],["3.48","930.8453",9],["3.4791","310",1],["3.4768","206",1],["3.4767","0.9415",1],["3.4754","1.4387",1],["3.4728","20",1],["3.4701","1219.2873",1],["3.47","1904.3139",7],["3.468","0.4035",1],["3.4667","0.1",1],["3.4666","3020.0101",1],["3.465","10",1],["3.464","0.4485",1],["3.462","2119.6556",1],["3.46","1305.6113",8],["3.4589","8.0228",1],["3.457","100",1],["3.456","70.3859",2],["3.4538","20",1],["3.4536","4323.9486",2],["3.4531","827.0427",1],["3.4528","0.439",1],["3.4522","8.0381",1],["3.4513","441.1873",1],["3.4512","50.707",1],["3.451","87.0902",1],["3.4509","200",1],["3.4506","100",1],["3.4505","86.4045",2],["3.45","12409.4595",28],["3.4494","0.5365",2],["3.449","10761",1],["3.4482","8.0476",1],["3.4469","0.449",1],["3.445","2000",1],["3.4427","14",1],["3.4421","100",1],["3.4416","8.0631",1],["3.4404","1",1],["3.44","4580.733",11],["3.4388","1868.2085",1],["3.438","937.7246",2],["3.4367","1500",1],["3.4366","62",1],["3.436","29.8743",1],["3.4356","25.4801",1],["3.4349","4.3086",1],["3.4343","43.2402",1],["3.433","2.0688",1],["3.4322","2.7335",2],["3.432","93.3233",1],["3.4302","328.8301",2],["3.43","4440.8158",11],["3.4288","754.574",2],["3.4283","125.7043",2],["3.428","744.3154",2],["3.4273","5460",1],["3.4258","50",1],["3.4255","109.005",1],["3.4248","100",1],["3.4241","129.2048",2],["3.4233","5.3598",1],["3.4228","4498.866",1],["3.4222","3.5435",1],["3.4217","404.3252",2],["3.4211","1000",1],["3.4208","31",1],["3.42","1834.024",9],["3.4175","300",1],["3.4162","400",1],["3.4152","0.1",1],["3.4151","4.3336",1],["3.415","1.5974",1],["3.414","1146",1],["3.4134","306.4246",1],["3.4129","7.5556",1],["3.4111","198.5188",1],["3.4109","500",1],["3.4106","4305",1],["3.41","2150.7635",13],["3.4085","4.342",1],["3.4054","5.6985",1],["3.4019","5.438",1],["3.4015","1010.846",1],["3.4009","8610",1],["3.4005","1.9122",1],["3.4004","1",1],["3.4","27081.1806",67],["3.3955","3.2682",1],["3.3953","5.4486",1],["3.3937","1591.3805",1],["3.39","3221.4155",8],["3.3899","3.2736",1],["3.3888","1500",2],["3.3887","5.4592",1],["3.385","117.0969",2],["3.3821","5.4699",1],["3.382","100.0529",1],["3.3818","172.0164",1],["3.3815","165.6288",1],["3.381","887.3115",1],["3.3808","100",1]],"timestamp":"2019-03-04T00:15:04.155Z","checksum":-2036653089}]}`
+ var dataResponse okgroup.WebsocketDataResponse
+ err := common.JSONDecode([]byte(orderbookPartialJSON), &dataResponse)
+ if err != nil {
+ t.Error(err)
+ }
+
+ calculatedChecksum := o.CalculatePartialOrderbookChecksum(&dataResponse.Data[0])
+ if calculatedChecksum != dataResponse.Data[0].Checksum {
+ t.Errorf("Expected %v, Receieved %v", dataResponse.Data[0].Checksum, calculatedChecksum)
+ }
+}
+
+// Function tests ----------------------------------------------------------------------------------------------
func setFeeBuilder() exchange.FeeBuilder {
return exchange.FeeBuilder{
Amount: 1,
@@ -309,8 +1812,10 @@ func setFeeBuilder() exchange.FeeBuilder {
}
}
+// TestGetFee fee calcuation test
func TestGetFee(t *testing.T) {
- o.SetDefaults()
+ TestSetDefaults(t)
+ t.Parallel()
var feeBuilder = setFeeBuilder()
// CryptocurrencyTradeFee Basic
if resp, err := o.GetFee(feeBuilder); resp != float64(0.0015) || err != nil {
@@ -342,22 +1847,6 @@ func TestGetFee(t *testing.T) {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Error(err)
}
- // CryptocurrencyWithdrawalFee Basic
- feeBuilder = setFeeBuilder()
- feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
- if resp, err := o.GetFee(feeBuilder); resp != float64(0.001) || err != nil {
- t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0.001), resp)
- t.Error(err)
- }
-
- // CryptocurrencyWithdrawalFee Invalid currency
- feeBuilder = setFeeBuilder()
- feeBuilder.FirstCurrency = "hello"
- feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
- if resp, err := o.GetFee(feeBuilder); resp != float64(0) || err != nil {
- t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
- t.Error(err)
- }
// CyptocurrencyDepositFee Basic
feeBuilder = setFeeBuilder()
@@ -385,73 +1874,27 @@ func TestGetFee(t *testing.T) {
}
}
+// TestFormatWithdrawPermissions helper test
func TestFormatWithdrawPermissions(t *testing.T) {
- o.SetDefaults()
+ TestSetDefaults(t)
+ t.Parallel()
expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText
-
withdrawPermissions := o.FormatWithdrawPermissions()
-
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
}
}
-func TestGetActiveOrders(t *testing.T) {
- o.SetDefaults()
- TestSetup(t)
-
- var getOrdersRequest = exchange.GetOrdersRequest{
- OrderType: exchange.AnyOrderType,
- Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.LTC, symbol.BTC)},
- }
-
- _, err := o.GetActiveOrders(getOrdersRequest)
- if areTestAPIKeysSet() && err != nil {
- t.Errorf("Could not get open orders: %s", err)
- } else if !areTestAPIKeysSet() && err == nil {
- t.Error("Expecting an error when no keys are set")
- }
-}
-
-func TestGetOrderHistory(t *testing.T) {
- o.SetDefaults()
- TestSetup(t)
-
- var getOrdersRequest = exchange.GetOrdersRequest{
- OrderType: exchange.AnyOrderType,
- Currencies: []pair.CurrencyPair{pair.NewCurrencyPair(symbol.LTC, symbol.BTC)},
- }
-
- _, err := o.GetOrderHistory(getOrdersRequest)
- if areTestAPIKeysSet() && err != nil {
- t.Errorf("Could not get order history: %s", err)
- } else if !areTestAPIKeysSet() && err == nil {
- t.Error("Expecting an error when no keys are set")
- }
-}
-
-// Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them
-// ----------------------------------------------------------------------------------------------------------------------------
-func areTestAPIKeysSet() bool {
- if o.APIKey != "" && o.APIKey != "Key" &&
- o.APISecret != "" && o.APISecret != "Secret" {
- return true
- }
- return false
-}
+// Wrapper tests --------------------------------------------------------------------------------------------------
+// TestSubmitOrder Wrapper test
func TestSubmitOrder(t *testing.T) {
- o.SetDefaults()
- TestSetup(t)
-
- if areTestAPIKeysSet() && !canManipulateRealOrders {
- t.Skip("API keys set, canManipulateRealOrders false, skipping test")
- }
-
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
var p = pair.CurrencyPair{
Delimiter: "",
FirstCurrency: symbol.BTC,
- SecondCurrency: symbol.EUR,
+ SecondCurrency: symbol.USDT,
}
response, err := o.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 10, "hi")
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
@@ -461,16 +1904,11 @@ func TestSubmitOrder(t *testing.T) {
}
}
+// TestCancelExchangeOrder Wrapper test
func TestCancelExchangeOrder(t *testing.T) {
- o.SetDefaults()
- TestSetup(t)
-
- if areTestAPIKeysSet() && !canManipulateRealOrders {
- t.Skip("API keys set, canManipulateRealOrders false, skipping test")
- }
-
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC)
-
var orderCancellation = exchange.OrderCancellation{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -479,24 +1917,15 @@ func TestCancelExchangeOrder(t *testing.T) {
}
err := o.CancelOrder(orderCancellation)
- if !areTestAPIKeysSet() && err == nil {
- t.Error("Expecting an error when no keys are set")
- }
- if areTestAPIKeysSet() && err != nil {
- t.Errorf("Could not cancel orders: %v", err)
- }
+ testStandardErrorHandling(t, err)
+
}
+// TestCancelAllExchangeOrders Wrapper test
func TestCancelAllExchangeOrders(t *testing.T) {
- o.SetDefaults()
- TestSetup(t)
-
- if areTestAPIKeysSet() && !canManipulateRealOrders {
- t.Skip("API keys set, canManipulateRealOrders false, skipping test")
- }
-
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
currencyPair := pair.NewCurrencyPair(symbol.LTC, symbol.BTC)
-
var orderCancellation = exchange.OrderCancellation{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -505,91 +1934,61 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
resp, err := o.CancelAllOrders(orderCancellation)
-
- if !areTestAPIKeysSet() && err == nil {
- t.Error("Expecting an error when no keys are set")
- }
- if areTestAPIKeysSet() && err != nil {
- t.Errorf("Could not cancel orders: %v", err)
- }
+ testStandardErrorHandling(t, err)
if len(resp.OrderStatus) > 0 {
t.Errorf("%v orders failed to cancel", len(resp.OrderStatus))
}
}
+// TestGetAccountInfo Wrapper test
func TestGetAccountInfo(t *testing.T) {
- if apiKey != "" || apiSecret != "" {
- _, err := o.GetAccountInfo()
- if err != nil {
- t.Error("Test Failed - GetAccountInfo() error", err)
- }
- } else {
- _, err := o.GetAccountInfo()
- if err == nil {
- t.Error("Test Failed - GetAccountInfo() error")
- }
- }
+ _, err := o.GetAccountInfo()
+ testStandardErrorHandling(t, err)
}
+// TestModifyOrder Wrapper test
func TestModifyOrder(t *testing.T) {
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
_, err := o.ModifyOrder(exchange.ModifyOrder{})
- if err == nil {
- t.Error("Test failed - ModifyOrder() error")
+ if err != common.ErrFunctionNotSupported {
+ t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
}
}
+// TestWithdraw Wrapper test
func TestWithdraw(t *testing.T) {
- o.SetDefaults()
- TestSetup(t)
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
var withdrawCryptoRequest = exchange.WithdrawRequest{
Amount: 100,
- Currency: "btc_usd",
+ Currency: symbol.BTC,
Address: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
Description: "WITHDRAW IT ALL",
TradePassword: "Password",
FeeAmount: 1,
}
-
- if areTestAPIKeysSet() && !canManipulateRealOrders {
- t.Skip("API keys set, canManipulateRealOrders false, skipping test")
- }
-
_, err := o.WithdrawCryptocurrencyFunds(withdrawCryptoRequest)
- if !areTestAPIKeysSet() && err == nil {
- t.Error("Expecting an error when no keys are set")
- }
- if areTestAPIKeysSet() && err != nil {
- t.Errorf("Withdraw failed to be placed: %v", err)
- }
+ testStandardErrorHandling(t, err)
}
+// TestWithdrawFiat Wrapper test
func TestWithdrawFiat(t *testing.T) {
- o.SetDefaults()
- TestSetup(t)
-
- if areTestAPIKeysSet() && !canManipulateRealOrders {
- t.Skip("API keys set, canManipulateRealOrders false, skipping test")
- }
-
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
var withdrawFiatRequest = exchange.WithdrawRequest{}
-
_, err := o.WithdrawFiatFunds(withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
}
}
+// TestSubmitOrder Wrapper test
func TestWithdrawInternationalBank(t *testing.T) {
- o.SetDefaults()
- TestSetup(t)
-
- if areTestAPIKeysSet() && !canManipulateRealOrders {
- t.Skip("API keys set, canManipulateRealOrders false, skipping test")
- }
-
+ TestSetRealOrderDefaults(t)
+ t.Parallel()
var withdrawFiatRequest = exchange.WithdrawRequest{}
-
_, err := o.WithdrawFiatFundsToInternationalBank(withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
diff --git a/exchanges/okex/okex_types.go b/exchanges/okex/okex_types.go
deleted file mode 100644
index 9f06b244..00000000
--- a/exchanges/okex/okex_types.go
+++ /dev/null
@@ -1,494 +0,0 @@
-package okex
-
-import "encoding/json"
-import "github.com/thrasher-/gocryptotrader/currency/symbol"
-
-// SpotInstrument stores the spot instrument info
-type SpotInstrument struct {
- BaseCurrency string `json:"base_currency"`
- BaseIncrement float64 `json:"base_increment,string"`
- BaseMinSize float64 `json:"base_min_size,string"`
- InstrumentID string `json:"instrument_id"`
- MinSize float64 `json:"min_size,string"`
- ProductID string `json:"product_id"`
- QuoteCurrency string `json:"quote_currency"`
- QuoteIncrement float64 `json:"quote_increment,string"`
- SizeIncrement float64 `json:"size_increment,string"`
- TickSize float64 `json:"tick_size,string"`
-}
-
-// ContractPrice holds date and ticker price price for contracts.
-type ContractPrice struct {
- Date string `json:"date"`
- Ticker struct {
- Buy float64 `json:"buy"`
- ContractID float64 `json:"contract_id"`
- High float64 `json:"high"`
- Low float64 `json:"low"`
- Last float64 `json:"last"`
- Sell float64 `json:"sell"`
- UnitAmount float64 `json:"unit_amount"`
- Vol float64 `json:"vol"`
- } `json:"ticker"`
- Result bool `json:"result"`
- Error interface{} `json:"error_code"`
-}
-
-// MultiStreamData contains raw data from okex
-type MultiStreamData struct {
- Channel string `json:"channel"`
- Data json.RawMessage `json:"data"`
-}
-
-// TokenOrdersResponse is returned after a request for all Token Orders
-type TokenOrdersResponse struct {
- Result bool `json:"result"`
- Orders []TokenOrder `json:"orders"`
-}
-
-// TokenOrder is the individual order details returned from TokenOrderResponse
-type TokenOrder struct {
- Amount float64 `json:"amount"`
- AvgPrice int64 `json:"avg_price"`
- DealAmount int64 `json:"deal_amount"`
- OrderID int64 `json:"order_id"`
- Price int64 `json:"price"`
- Status int64 `json:"status"`
- Symbol string `json:"symbol"`
- Type string `json:"type"`
-}
-
-// TickerStreamData contains ticker stream data from okex
-type TickerStreamData struct {
- Buy string `json:"buy"`
- Change string `json:"change"`
- High string `json:"high"`
- Low string `json:"low"`
- Last string `json:"last"`
- Sell string `json:"sell"`
- DayLow string `json:"dayLow"`
- DayHigh string `json:"dayHigh"`
- Timestamp float64 `json:"timestamp"`
- Vol string `json:"vol"`
-}
-
-// DealsStreamData defines Deals data
-type DealsStreamData = [][]string
-
-// KlineStreamData defines kline data
-type KlineStreamData = [][]string
-
-// DepthStreamData defines orderbook depth
-type DepthStreamData struct {
- Asks [][]string `json:"asks"`
- Bids [][]string `json:"bids"`
- Timestamp float64 `json:"timestamp"`
-}
-
-// ContractDepth response depth
-type ContractDepth struct {
- Asks []interface{} `json:"asks"`
- Bids []interface{} `json:"bids"`
- Result bool `json:"result"`
- Error interface{} `json:"error_code"`
-}
-
-// ActualContractDepth better manipulated structure to return
-type ActualContractDepth struct {
- Asks []struct {
- Price float64
- Volume float64
- }
- Bids []struct {
- Price float64
- Volume float64
- }
-}
-
-// ActualContractTradeHistory holds contract trade history
-type ActualContractTradeHistory struct {
- Amount float64 `json:"amount"`
- DateInMS float64 `json:"date_ms"`
- Date float64 `json:"date"`
- Price float64 `json:"price"`
- TID float64 `json:"tid"`
- Type string `json:"buy"`
-}
-
-// CandleStickData holds candlestick data
-type CandleStickData struct {
- Timestamp float64 `json:"timestamp"`
- Open float64 `json:"open"`
- High float64 `json:"high"`
- Low float64 `json:"low"`
- Close float64 `json:"close"`
- Volume float64 `json:"volume"`
- Amount float64 `json:"amount"`
-}
-
-// Info holds individual information
-type Info struct {
- AccountRights float64 `json:"account_rights"`
- KeepDeposit float64 `json:"keep_deposit"`
- ProfitReal float64 `json:"profit_real"`
- ProfitUnreal float64 `json:"profit_unreal"`
- RiskRate float64 `json:"risk_rate"`
-}
-
-// UserInfo holds a collection of user data
-type UserInfo struct {
- Info struct {
- BTC Info `json:"btc"`
- LTC Info `json:"ltc"`
- } `json:"info"`
- Result bool `json:"result"`
-}
-
-// HoldData is a sub type for FuturePosition
-type HoldData struct {
- BuyAmount float64 `json:"buy_amount"`
- BuyAvailable float64 `json:"buy_available"`
- BuyPriceAvg float64 `json:"buy_price_avg"`
- BuyPriceCost float64 `json:"buy_price_cost"`
- BuyProfitReal float64 `json:"buy_profit_real"`
- ContractID float64 `json:"contract_id"`
- ContractType string `json:"contract_type"`
- CreateDate int `json:"create_date"`
- LeverRate float64 `json:"lever_rate"`
- SellAmount float64 `json:"sell_amount"`
- SellAvailable float64 `json:"sell_available"`
- SellPriceAvg float64 `json:"sell_price_avg"`
- SellPriceCost float64 `json:"sell_price_cost"`
- SellProfitReal float64 `json:"sell_profit_real"`
- Symbol string `json:"symbol"`
-}
-
-// FuturePosition contains an array of holding types
-type FuturePosition struct {
- ForceLiquidationPrice float64 `json:"force_liqu_price"`
- Holding []HoldData `json:"holding"`
-}
-
-// FutureTradeHistory will contain futures trade data
-type FutureTradeHistory struct {
- Amount float64 `json:"amount"`
- Date int `json:"date"`
- Price float64 `json:"price"`
- TID float64 `json:"tid"`
- Type string `json:"type"`
-}
-
-// SpotPrice holds date and ticker price price for contracts.
-type SpotPrice struct {
- Date string `json:"date"`
- Ticker struct {
- Buy float64 `json:"buy,string"`
- ContractID float64 `json:"contract_id"`
- High float64 `json:"high,string"`
- Low float64 `json:"low,string"`
- Last float64 `json:"last,string"`
- Sell float64 `json:"sell,string"`
- UnitAmount float64 `json:"unit_amount,string"`
- Vol float64 `json:"vol,string"`
- } `json:"ticker"`
- Result bool `json:"result"`
- Error interface{} `json:"error_code"`
-}
-
-// SpotDepth response depth
-type SpotDepth struct {
- Asks []interface{} `json:"asks"`
- Bids []interface{} `json:"bids"`
- Result bool `json:"result"`
- Error interface{} `json:"error_code"`
-}
-
-// ActualSpotDepthRequestParams represents Klines request data.
-type ActualSpotDepthRequestParams struct {
- Symbol string `json:"symbol"` // Symbol; example ltc_btc
- Size int `json:"size"` // value: 1-200
-}
-
-// ActualSpotDepth better manipulated structure to return
-type ActualSpotDepth struct {
- Asks []struct {
- Price float64
- Volume float64
- }
- Bids []struct {
- Price float64
- Volume float64
- }
-}
-
-// ActualSpotTradeHistoryRequestParams represents Klines request data.
-type ActualSpotTradeHistoryRequestParams struct {
- Symbol string `json:"symbol"` // Symbol; example ltc_btc
- Since int `json:"since"` // TID; transaction record ID (return data does not include the current TID value, returning up to 600 items)
-}
-
-// ActualSpotTradeHistory holds contract trade history
-type ActualSpotTradeHistory struct {
- Amount float64 `json:"amount"`
- DateInMS float64 `json:"date_ms"`
- Date float64 `json:"date"`
- Price float64 `json:"price"`
- TID float64 `json:"tid"`
- Type string `json:"buy"`
-}
-
-// SpotUserInfo holds the spot user info
-type SpotUserInfo struct {
- Result bool `json:"result"`
- Info map[string]map[string]map[string]string `json:"info"`
-}
-
-// SpotNewOrderRequestParams holds the params for making a new spot order
-type SpotNewOrderRequestParams struct {
- Amount float64 `json:"amount"` // Order quantity
- Price float64 `json:"price"` // Order price
- Symbol string `json:"symbol"` // Symbol; example btc_usdt, eth_btc......
- Type SpotNewOrderRequestType `json:"type"` // Order type (see below)
-}
-
-// SpotNewOrderRequestType order type
-type SpotNewOrderRequestType string
-
-var (
- // SpotNewOrderRequestTypeBuy buy order
- SpotNewOrderRequestTypeBuy = SpotNewOrderRequestType("buy")
-
- // SpotNewOrderRequestTypeSell sell order
- SpotNewOrderRequestTypeSell = SpotNewOrderRequestType("sell")
-
- // SpotNewOrderRequestTypeBuyMarket buy market order
- SpotNewOrderRequestTypeBuyMarket = SpotNewOrderRequestType("buy_market")
-
- // SpotNewOrderRequestTypeSellMarket sell market order
- SpotNewOrderRequestTypeSellMarket = SpotNewOrderRequestType("sell_market")
-)
-
-// KlinesRequestParams represents Klines request data.
-type KlinesRequestParams struct {
- Symbol string // Symbol; example btcusdt, bccbtc......
- Type TimeInterval // Kline data time interval; 1min, 5min, 15min......
- Size int // Size; [1-2000]
- Since int64 // Since timestamp, return data after the specified timestamp (for example, 1417536000000)
-}
-
-// TimeInterval represents interval enum.
-type TimeInterval string
-
-// vars for time intervals
-var (
- TimeIntervalMinute = TimeInterval("1min")
- TimeIntervalThreeMinutes = TimeInterval("3min")
- TimeIntervalFiveMinutes = TimeInterval("5min")
- TimeIntervalFifteenMinutes = TimeInterval("15min")
- TimeIntervalThirtyMinutes = TimeInterval("30min")
- TimeIntervalHour = TimeInterval("1hour")
- TimeIntervalFourHours = TimeInterval("4hour")
- TimeIntervalSixHours = TimeInterval("6hour")
- TimeIntervalTwelveHours = TimeInterval("12hour")
- TimeIntervalDay = TimeInterval("1day")
- TimeIntervalThreeDays = TimeInterval("3day")
- TimeIntervalWeek = TimeInterval("1week")
-)
-
-// WithdrawalFees the large list of predefined withdrawal fees
-// Prone to change, using highest value
-var WithdrawalFees = map[string]float64{
- symbol.ZRX: 10,
- symbol.ACE: 2.2,
- symbol.ACT: 0.01,
- symbol.AAC: 5,
- symbol.AE: 1,
- symbol.AIDOC: 17,
- symbol.AST: 8,
- symbol.SOC: 20,
- symbol.ABT: 3,
- symbol.ARK: 0.1,
- symbol.ATL: 1.5,
- symbol.AVT: 1,
- symbol.BNT: 1,
- symbol.BKX: 3,
- symbol.BEC: 4,
- symbol.BTC: 0.0005,
- symbol.BCH: 0.0001,
- symbol.BCD: 0.02,
- symbol.BTG: 0.001,
- symbol.VEE: 100,
- symbol.BRD: 1.5,
- symbol.CTR: 7,
- symbol.LINK: 10,
- symbol.CAG: 2,
- symbol.CHAT: 10,
- symbol.CVC: 10,
- symbol.CIC: 150,
- symbol.CBT: 10,
- symbol.CAN: 3,
- symbol.CMT: 10,
- symbol.DADI: 10,
- symbol.DASH: 0.002,
- symbol.DAT: 2,
- symbol.MANA: 20,
- symbol.DCR: 0.03,
- symbol.DPY: 0.8,
- symbol.DENT: 100,
- symbol.DGD: 0.2,
- symbol.DNT: 20,
- symbol.EDO: 2,
- symbol.DNA: 3,
- symbol.ENG: 5,
- symbol.ENJ: 20,
- symbol.ETH: 0.01,
- symbol.ETC: 0.001,
- symbol.LEND: 10,
- symbol.EVX: 1.5,
- symbol.XUC: 5.8,
- symbol.FAIR: 15,
- symbol.FIRST: 6,
- symbol.FUN: 40,
- symbol.GTC: 40,
- symbol.GNX: 8,
- symbol.GTO: 10,
- symbol.GSC: 20,
- symbol.GNT: 5,
- symbol.HMC: 40,
- symbol.HOT: 10,
- symbol.ICN: 2,
- symbol.INS: 2.5,
- symbol.INT: 10,
- symbol.IOST: 100,
- symbol.ITC: 2,
- symbol.IPC: 2.5,
- symbol.KNC: 2,
- symbol.LA: 3,
- symbol.LEV: 20,
- symbol.LIGHT: 100,
- symbol.LSK: 0.4,
- symbol.LTC: 0.001,
- symbol.LRC: 7,
- symbol.MAG: 34,
- symbol.MKR: 0.002,
- symbol.MTL: 0.5,
- symbol.AMM: 5,
- symbol.MITH: 20,
- symbol.MDA: 2,
- symbol.MOF: 5,
- symbol.MCO: 0.2,
- symbol.MTH: 35,
- symbol.NGC: 1.5,
- symbol.NANO: 0.2,
- symbol.NULS: 2,
- symbol.OAX: 6,
- symbol.OF: 600,
- symbol.OKB: 0,
- symbol.MOT: 1.5,
- symbol.OMG: 0.1,
- symbol.RNT: 13,
- symbol.POE: 30,
- symbol.PPT: 0.2,
- symbol.PST: 10,
- symbol.PRA: 4,
- symbol.QTUM: 0.01,
- symbol.QUN: 20,
- symbol.QVT: 10,
- symbol.RDN: 0.3,
- symbol.READ: 20,
- symbol.RCT: 15,
- symbol.RFR: 200,
- symbol.REF: 0.2,
- symbol.REN: 50,
- symbol.REQ: 15,
- symbol.R: 2,
- symbol.RCN: 20,
- symbol.XRP: 0.15,
- symbol.SALT: 0.5,
- symbol.SAN: 1,
- symbol.KEY: 50,
- symbol.SSC: 8,
- symbol.SHOW: 150,
- symbol.SC: 200,
- symbol.OST: 3,
- symbol.SNGLS: 20,
- symbol.SMT: 8,
- symbol.SNM: 20,
- symbol.SPF: 5,
- symbol.SNT: 50,
- symbol.STORJ: 2,
- symbol.SUB: 4,
- symbol.SNC: 10,
- symbol.SWFTC: 350,
- symbol.PAY: 0.5,
- symbol.USDT: 2,
- symbol.TRA: 500,
- symbol.THETA: 20,
- symbol.TNB: 40,
- symbol.TCT: 50,
- symbol.TOPC: 20,
- symbol.TIO: 2.5,
- symbol.TRIO: 200,
- symbol.TRUE: 4,
- symbol.UCT: 10,
- symbol.UGC: 12,
- symbol.UKG: 2.5,
- symbol.UTK: 3,
- symbol.VIB: 6,
- symbol.VIU: 40,
- symbol.WTC: 0.4,
- symbol.WFEE: 500,
- symbol.WRC: 48,
- symbol.YEE: 70,
- symbol.YOYOW: 10,
- symbol.ZEC: 0.001,
- symbol.ZEN: 0.07,
- symbol.ZIL: 20,
- symbol.ZIP: 1000,
-}
-
-// FullBalance defines a structured return type with balance data
-type FullBalance struct {
- Available float64
- Currency string
- Hold float64
-}
-
-// Balance defines returned balance data
-type Balance struct {
- Info struct {
- Funds struct {
- Free map[string]string `json:"free"`
- Holds map[string]string `json:"holds"`
- } `json:"funds"`
- } `json:"info"`
-}
-
-// WithdrawalResponse is a response type for withdrawal
-type WithdrawalResponse struct {
- WithdrawID int `json:"withdraw_id"`
- Result bool `json:"result"`
-}
-
-// OrderInfo holds data on an order
-type OrderInfo struct {
- Amount float64 `json:"amount"`
- AvgPrice float64 `json:"avg_price"`
- Created int64 `json:"create_date"`
- DealAmount float64 `json:"deal_amount"`
- OrderID int64 `json:"order_id"`
- OrdersID int64 `json:"orders_id"`
- Price float64 `json:"price"`
- Status int `json:"status"`
- Symbol string `json:"symbol"`
- Type string `json:"type"`
-}
-
-// OrderHistory holds information on order history
-type OrderHistory struct {
- CurrentPage int `json:"current_page"`
- Orders []OrderInfo `json:"orders"`
- PageLength int `json:"page_length"`
- Result bool `json:"result"`
- Total int `json:"total"`
-}
diff --git a/exchanges/okex/okex_websocket.go b/exchanges/okex/okex_websocket.go
deleted file mode 100644
index 7b22e694..00000000
--- a/exchanges/okex/okex_websocket.go
+++ /dev/null
@@ -1,318 +0,0 @@
-package okex
-
-import (
- "bytes"
- "compress/flate"
- "errors"
- "fmt"
- "io/ioutil"
- "net/http"
- "net/url"
- "strconv"
- "strings"
- "time"
-
- "github.com/gorilla/websocket"
- "github.com/thrasher-/gocryptotrader/common"
- "github.com/thrasher-/gocryptotrader/currency/pair"
- exchange "github.com/thrasher-/gocryptotrader/exchanges"
- log "github.com/thrasher-/gocryptotrader/logger"
-)
-
-const (
- okexDefaultWebsocketURL = "wss://real.okex.com:10440/websocket/okexapi"
-)
-
-func (o *OKEX) writeToWebsocket(message string) error {
- o.mu.Lock()
- defer o.mu.Unlock()
-
- return o.WebsocketConn.WriteMessage(websocket.TextMessage, []byte(message))
-}
-
-// WsConnect initiates a websocket connection
-func (o *OKEX) WsConnect() error {
- if !o.Websocket.IsEnabled() || !o.IsEnabled() {
- return errors.New(exchange.WebsocketNotEnabled)
- }
-
- var dialer websocket.Dialer
-
- if o.Websocket.GetProxyAddress() != "" {
- proxy, err := url.Parse(o.Websocket.GetProxyAddress())
- if err != nil {
- return err
- }
-
- dialer.Proxy = http.ProxyURL(proxy)
- }
-
- var err error
- o.WebsocketConn, _, err = dialer.Dial(o.Websocket.GetWebsocketURL(),
- http.Header{})
- if err != nil {
- return fmt.Errorf("%s Unable to connect to Websocket. Error: %s",
- o.Name,
- err)
- }
-
- go o.WsHandleData()
- go o.wsPingHandler()
-
- err = o.WsSubscribe()
- if err != nil {
- return fmt.Errorf("%s could not subscribe to websocket %s",
- o.Name, err)
- }
-
- return nil
-}
-
-// WsSubscribe subscribes to the websocket channels
-func (o *OKEX) WsSubscribe() error {
- myEnabledSubscriptionChannels := []string{}
-
- for _, pair := range o.EnabledPairs {
-
- // ----------- deprecate when usd pairs are upgraded to usdt ----------
- checkSymbol := common.SplitStrings(pair, "_")
- for i := range checkSymbol {
- if common.StringContains(checkSymbol[i], "usdt") {
- break
- }
- if common.StringContains(checkSymbol[i], "usd") {
- checkSymbol[i] = "usdt"
- }
- }
-
- symbolRedone := common.JoinStrings(checkSymbol, "_")
- // ----------- deprecate when usd pairs are upgraded to usdt ----------
-
- myEnabledSubscriptionChannels = append(myEnabledSubscriptionChannels,
- fmt.Sprintf("{'event':'addChannel','channel':'ok_sub_spot_%s_ticker'}",
- symbolRedone),
- fmt.Sprintf("{'event':'addChannel','channel':'ok_sub_spot_%s_depth'}",
- symbolRedone),
- fmt.Sprintf("{'event':'addChannel','channel':'ok_sub_spot_%s_deals'}",
- symbolRedone),
- fmt.Sprintf("{'event':'addChannel','channel':'ok_sub_spot_%s_kline_1min'}",
- symbolRedone))
- }
-
- for _, outgoing := range myEnabledSubscriptionChannels {
- err := o.writeToWebsocket(outgoing)
- if err != nil {
- return err
- }
- }
-
- return nil
-}
-
-// WsReadData reads data from the websocket connection
-func (o *OKEX) WsReadData() (exchange.WebsocketResponse, error) {
- mType, resp, err := o.WebsocketConn.ReadMessage()
- if err != nil {
- return exchange.WebsocketResponse{}, err
- }
-
- o.Websocket.TrafficAlert <- struct{}{}
-
- var standardMessage []byte
-
- switch mType {
- case websocket.TextMessage:
- standardMessage = resp
-
- case websocket.BinaryMessage:
- reader := flate.NewReader(bytes.NewReader(resp))
- standardMessage, err = ioutil.ReadAll(reader)
- reader.Close()
- if err != nil {
- return exchange.WebsocketResponse{}, err
- }
- }
-
- return exchange.WebsocketResponse{Raw: standardMessage}, nil
-}
-
-func (o *OKEX) wsPingHandler() {
- o.Websocket.Wg.Add(1)
- defer o.Websocket.Wg.Done()
-
- ticker := time.NewTicker(time.Second * 27)
-
- for {
- select {
- case <-o.Websocket.ShutdownC:
- return
-
- case <-ticker.C:
- err := o.writeToWebsocket("{'event':'ping'}")
- if err != nil {
- o.Websocket.DataHandler <- err
- return
- }
- }
- }
-}
-
-// WsHandleData handles the read data from the websocket connection
-func (o *OKEX) WsHandleData() {
- o.Websocket.Wg.Add(1)
-
- defer func() {
- err := o.WebsocketConn.Close()
- if err != nil {
- o.Websocket.DataHandler <- fmt.Errorf("okex_websocket.go - Unable to to close Websocket connection. Error: %s",
- err)
- }
- o.Websocket.Wg.Done()
- }()
-
- for {
- select {
- case <-o.Websocket.ShutdownC:
- return
-
- default:
- resp, err := o.WsReadData()
- if err != nil {
- o.Websocket.DataHandler <- err
- return
- }
-
- multiStreamDataArr := []MultiStreamData{}
-
- err = common.JSONDecode(resp.Raw, &multiStreamDataArr)
- if err != nil {
- if strings.Contains(string(resp.Raw), "pong") {
- continue
- } else {
- o.Websocket.DataHandler <- err
- continue
- }
- }
-
- for _, multiStreamData := range multiStreamDataArr {
- var errResponse ErrorResponse
- if common.StringContains(string(resp.Raw), "error_msg") {
- err = common.JSONDecode(resp.Raw, &errResponse)
- if err != nil {
- log.Error(err)
- }
- o.Websocket.DataHandler <- fmt.Errorf("okex.go error - %s resp: %s ",
- errResponse.ErrorMsg,
- string(resp.Raw))
- continue
- }
-
- var newPair string
- var assetType string
- currencyPairSlice := common.SplitStrings(multiStreamData.Channel, "_")
- if len(currencyPairSlice) > 5 {
- newPair = currencyPairSlice[3] + "_" + currencyPairSlice[4]
- assetType = currencyPairSlice[2]
- }
-
- switch multiStreamData.Channel {
- case "ticker":
- var ticker TickerStreamData
-
- err = common.JSONDecode(multiStreamData.Data, &ticker)
- if err != nil {
- o.Websocket.DataHandler <- err
- continue
- }
-
- o.Websocket.DataHandler <- exchange.TickerData{
- Timestamp: time.Unix(0, int64(ticker.Timestamp)),
- Exchange: o.GetName(),
- AssetType: assetType,
- }
- case "deals":
- var deals DealsStreamData
-
- err = common.JSONDecode(multiStreamData.Data, &deals)
- if err != nil {
- o.Websocket.DataHandler <- err
- continue
- }
-
- for _, trade := range deals {
- price, _ := strconv.ParseFloat(trade[1], 64)
- amount, _ := strconv.ParseFloat(trade[2], 64)
- tradeTime, _ := time.Parse(time.RFC3339, trade[3])
-
- o.Websocket.DataHandler <- exchange.TradeData{
- Timestamp: tradeTime,
- Exchange: o.GetName(),
- AssetType: assetType,
- CurrencyPair: pair.NewCurrencyPairFromString(newPair),
- Price: price,
- Amount: amount,
- EventType: trade[4],
- }
- }
- case "kline":
- var klines KlineStreamData
-
- err := common.JSONDecode(multiStreamData.Data, &klines)
- if err != nil {
- o.Websocket.DataHandler <- err
- continue
- }
-
- for _, kline := range klines {
- ntime, _ := strconv.ParseInt(kline[0], 10, 64)
- open, _ := strconv.ParseFloat(kline[1], 64)
- high, _ := strconv.ParseFloat(kline[2], 64)
- low, _ := strconv.ParseFloat(kline[3], 64)
- klineClose, _ := strconv.ParseFloat(kline[4], 64)
- volume, _ := strconv.ParseFloat(kline[5], 64)
-
- o.Websocket.DataHandler <- exchange.KlineData{
- Timestamp: time.Unix(ntime, 0),
- Pair: pair.NewCurrencyPairFromString(newPair),
- AssetType: assetType,
- Exchange: o.GetName(),
- OpenPrice: open,
- HighPrice: high,
- LowPrice: low,
- ClosePrice: klineClose,
- Volume: volume,
- }
- }
- case "depth":
- var depth DepthStreamData
-
- err := common.JSONDecode(multiStreamData.Data, &depth)
- if err != nil {
- o.Websocket.DataHandler <- err
- continue
- }
-
- o.Websocket.DataHandler <- exchange.WebsocketOrderbookUpdate{
- Exchange: o.GetName(),
- Asset: assetType,
- Pair: pair.NewCurrencyPairFromString(newPair),
- }
- }
- }
- }
- }
-}
-
-// ErrorResponse defines an error response type from the websocket connection
-type ErrorResponse struct {
- Result bool `json:"result"`
- ErrorMsg string `json:"error_msg"`
- ErrorCode int64 `json:"error_code"`
-}
-
-// Request defines the JSON request structure to the websocket server
-type Request struct {
- Event string `json:"event"`
- Channel string `json:"channel"`
- Parameters string `json:"parameters,omitempty"`
-}
diff --git a/exchanges/okex/okex_wrapper.go b/exchanges/okex/okex_wrapper.go
deleted file mode 100644
index adb52fad..00000000
--- a/exchanges/okex/okex_wrapper.go
+++ /dev/null
@@ -1,394 +0,0 @@
-package okex
-
-import (
- "errors"
- "fmt"
- "strconv"
- "strings"
- "sync"
- "time"
-
- "github.com/thrasher-/gocryptotrader/common"
- "github.com/thrasher-/gocryptotrader/currency/pair"
- exchange "github.com/thrasher-/gocryptotrader/exchanges"
- "github.com/thrasher-/gocryptotrader/exchanges/orderbook"
- "github.com/thrasher-/gocryptotrader/exchanges/ticker"
- log "github.com/thrasher-/gocryptotrader/logger"
-)
-
-// Start starts the OKEX go routine
-func (o *OKEX) Start(wg *sync.WaitGroup) {
- wg.Add(1)
- go func() {
- o.Run()
- wg.Done()
- }()
-}
-
-// Run implements the OKEX wrapper
-func (o *OKEX) Run() {
- if o.Verbose {
- log.Debugf("%s Websocket: %s. (url: %s).\n", o.GetName(), common.IsEnabled(o.Websocket.IsEnabled()), o.WebsocketURL)
- log.Debugf("%s polling delay: %ds.\n", o.GetName(), o.RESTPollingDelay)
- log.Debugf("%s %d currencies enabled: %s.\n", o.GetName(), len(o.EnabledPairs), o.EnabledPairs)
- }
-
- prods, err := o.GetSpotInstruments()
- if err != nil {
- log.Errorf("OKEX failed to obtain available spot instruments. Err: %s", err)
- return
- }
-
- var pairs []string
- for x := range prods {
- pairs = append(pairs, prods[x].BaseCurrency+"_"+prods[x].QuoteCurrency)
- }
-
- err = o.UpdateCurrencies(pairs, false, false)
- if err != nil {
- log.Errorf("OKEX failed to update available currencies. Err: %s", err)
- return
- }
-}
-
-// UpdateTicker updates and returns the ticker for a currency pair
-func (o *OKEX) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
- currency := exchange.FormatExchangeCurrency(o.Name, p).String()
- var tickerPrice ticker.Price
-
- if assetType != ticker.Spot {
- tick, err := o.GetContractPrice(currency, assetType)
- if err != nil {
- return tickerPrice, err
- }
-
- tickerPrice.Pair = p
- tickerPrice.Ask = tick.Ticker.Sell
- tickerPrice.Bid = tick.Ticker.Buy
- tickerPrice.Low = tick.Ticker.Low
- tickerPrice.Last = tick.Ticker.Last
- tickerPrice.Volume = tick.Ticker.Vol
- tickerPrice.High = tick.Ticker.High
- ticker.ProcessTicker(o.GetName(), p, tickerPrice, assetType)
- } else {
- tick, err := o.GetSpotTicker(currency)
- if err != nil {
- return tickerPrice, err
- }
- tickerPrice.Pair = p
- tickerPrice.Ask = tick.Ticker.Sell
- tickerPrice.Bid = tick.Ticker.Buy
- tickerPrice.Low = tick.Ticker.Low
- tickerPrice.Last = tick.Ticker.Last
- tickerPrice.Volume = tick.Ticker.Vol
- tickerPrice.High = tick.Ticker.High
- ticker.ProcessTicker(o.GetName(), p, tickerPrice, ticker.Spot)
-
- }
- return ticker.GetTicker(o.Name, p, assetType)
-}
-
-// GetTickerPrice returns the ticker for a currency pair
-func (o *OKEX) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
- tickerNew, err := ticker.GetTicker(o.GetName(), p, assetType)
- if err != nil {
- return o.UpdateTicker(p, assetType)
- }
- return tickerNew, nil
-}
-
-// GetOrderbookEx returns orderbook base on the currency pair
-func (o *OKEX) GetOrderbookEx(currency pair.CurrencyPair, assetType string) (orderbook.Base, error) {
- ob, err := orderbook.GetOrderbook(o.GetName(), currency, assetType)
- if err != nil {
- return o.UpdateOrderbook(currency, assetType)
- }
- return ob, nil
-}
-
-// UpdateOrderbook updates and returns the orderbook for a currency pair
-func (o *OKEX) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
- var orderBook orderbook.Base
- currency := exchange.FormatExchangeCurrency(o.Name, p).String()
-
- if assetType != ticker.Spot {
- orderbookNew, err := o.GetContractMarketDepth(currency, assetType)
- if err != nil {
- return orderBook, err
- }
-
- for x := range orderbookNew.Bids {
- data := orderbookNew.Bids[x]
- orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data.Volume, Price: data.Price})
- }
-
- for x := range orderbookNew.Asks {
- data := orderbookNew.Asks[x]
- orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data.Volume, Price: data.Price})
- }
-
- } else {
- orderbookNew, err := o.GetSpotMarketDepth(ActualSpotDepthRequestParams{
- Symbol: currency,
- Size: 200,
- })
- if err != nil {
- return orderBook, err
- }
-
- for x := range orderbookNew.Bids {
- data := orderbookNew.Bids[x]
- orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data.Volume, Price: data.Price})
- }
-
- for x := range orderbookNew.Asks {
- data := orderbookNew.Asks[x]
- orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data.Volume, Price: data.Price})
- }
- }
-
- orderbook.ProcessOrderbook(o.GetName(), p, orderBook, assetType)
- return orderbook.GetOrderbook(o.Name, p, assetType)
-}
-
-// GetAccountInfo retrieves balances for all enabled currencies for the
-// OKEX exchange
-func (o *OKEX) GetAccountInfo() (exchange.AccountInfo, error) {
- var info exchange.AccountInfo
- bal, err := o.GetBalance()
- if err != nil {
- return info, err
- }
-
- var balances []exchange.AccountCurrencyInfo
- for _, data := range bal {
- balances = append(balances, exchange.AccountCurrencyInfo{
- CurrencyName: data.Currency,
- TotalValue: data.Available + data.Hold,
- Hold: data.Hold,
- })
- }
-
- info.Exchange = o.GetName()
- info.Accounts = append(info.Accounts, exchange.Account{
- Currencies: balances,
- })
-
- return info, nil
-}
-
-// GetFundingHistory returns funding history, deposits and
-// withdrawals
-func (o *OKEX) GetFundingHistory() ([]exchange.FundHistory, error) {
- var fundHistory []exchange.FundHistory
- return fundHistory, common.ErrFunctionNotSupported
-}
-
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (o *OKEX) GetExchangeHistory(p pair.CurrencyPair, assetType string) ([]exchange.TradeHistory, error) {
- var resp []exchange.TradeHistory
-
- return resp, common.ErrNotYetImplemented
-}
-
-// SubmitOrder submits a new order
-func (o *OKEX) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
- var submitOrderResponse exchange.SubmitOrderResponse
- var oT SpotNewOrderRequestType
-
- switch orderType {
- case exchange.LimitOrderType:
- oT = SpotNewOrderRequestTypeSell
- if side == exchange.BuyOrderSide {
- oT = SpotNewOrderRequestTypeBuy
- }
- case exchange.MarketOrderType:
- oT = SpotNewOrderRequestTypeSellMarket
- if side == exchange.BuyOrderSide {
- oT = SpotNewOrderRequestTypeBuyMarket
- }
- default:
- return submitOrderResponse, errors.New("unsupported order type")
- }
-
- var params = SpotNewOrderRequestParams{
- Amount: amount,
- Price: price,
- Symbol: p.Pair().String(),
- Type: oT,
- }
-
- response, err := o.SpotNewOrder(params)
-
- if response > 0 {
- submitOrderResponse.OrderID = fmt.Sprintf("%v", response)
- }
-
- if err == nil {
- submitOrderResponse.IsOrderPlaced = true
- }
-
- return submitOrderResponse, err
-}
-
-// ModifyOrder will allow of changing orderbook placement and limit to
-// market conversion
-func (o *OKEX) ModifyOrder(action exchange.ModifyOrder) (string, error) {
- return "", common.ErrFunctionNotSupported
-}
-
-// CancelOrder cancels an order by its corresponding ID number
-func (o *OKEX) CancelOrder(order exchange.OrderCancellation) error {
- orderIDInt, err := strconv.ParseInt(order.OrderID, 10, 64)
- if err != nil {
- return err
- }
-
- _, err = o.SpotCancelOrder(exchange.FormatExchangeCurrency(o.Name, order.CurrencyPair).String(), orderIDInt)
- return err
-}
-
-// CancelAllOrders cancels all orders for all enabled currencies
-func (o *OKEX) CancelAllOrders(_ exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) {
- cancelAllOrdersResponse := exchange.CancelAllOrdersResponse{
- OrderStatus: make(map[string]string),
- }
- var allOpenOrders []TokenOrder
- for _, currency := range o.GetEnabledCurrencies() {
- formattedCurrency := exchange.FormatExchangeCurrency(o.Name, currency).String()
- openOrders, err := o.GetTokenOrders(formattedCurrency, -1)
- if err != nil {
- return cancelAllOrdersResponse, err
- }
-
- if !openOrders.Result {
- return cancelAllOrdersResponse, fmt.Errorf("something went wrong for currency %s", formattedCurrency)
- }
-
- allOpenOrders = append(allOpenOrders, openOrders.Orders...)
- }
-
- for _, openOrder := range allOpenOrders {
- _, err := o.SpotCancelOrder(openOrder.Symbol, openOrder.OrderID)
- if err != nil {
- cancelAllOrdersResponse.OrderStatus[strconv.FormatInt(openOrder.OrderID, 10)] = err.Error()
- }
- }
-
- return cancelAllOrdersResponse, nil
-}
-
-// GetOrderInfo returns information on a current open order
-func (o *OKEX) GetOrderInfo(orderID string) (exchange.OrderDetail, error) {
- var orderDetail exchange.OrderDetail
- return orderDetail, common.ErrNotYetImplemented
-}
-
-// GetDepositAddress returns a deposit address for a specified currency
-func (o *OKEX) GetDepositAddress(cryptocurrency pair.CurrencyItem, accountID string) (string, error) {
- // NOTE needs API version update to access
- return "", common.ErrNotYetImplemented
-}
-
-// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is
-// submitted
-func (o *OKEX) WithdrawCryptocurrencyFunds(withdrawRequest exchange.WithdrawRequest) (string, error) {
- resp, err := o.Withdrawal(withdrawRequest.Currency.String(), withdrawRequest.FeeAmount, withdrawRequest.TradePassword, withdrawRequest.Address, withdrawRequest.Amount)
- return fmt.Sprintf("%v", resp), err
-}
-
-// WithdrawFiatFunds returns a withdrawal ID when a
-// withdrawal is submitted
-func (o *OKEX) WithdrawFiatFunds(withdrawRequest exchange.WithdrawRequest) (string, error) {
- return "", common.ErrFunctionNotSupported
-}
-
-// WithdrawFiatFundsToInternationalBank returns a withdrawal ID when a
-// withdrawal is submitted
-func (o *OKEX) WithdrawFiatFundsToInternationalBank(withdrawRequest exchange.WithdrawRequest) (string, error) {
- return "", common.ErrFunctionNotSupported
-}
-
-// GetWebsocket returns a pointer to the exchange websocket
-func (o *OKEX) GetWebsocket() (*exchange.Websocket, error) {
- return o.Websocket, nil
-}
-
-// GetFeeByType returns an estimate of fee based on type of transaction
-func (o *OKEX) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) {
- return o.GetFee(feeBuilder)
-}
-
-// GetActiveOrders retrieves any orders that are active/open
-func (o *OKEX) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) {
- var allOrders []OrderInfo
- for _, currency := range getOrdersRequest.Currencies {
- resp, err := o.GetOrderHistoryForCurrency(200, 0, 0, exchange.FormatExchangeCurrency(o.Name, currency).String())
- if err != nil {
- return nil, err
- }
-
- allOrders = append(allOrders, resp.Orders...)
- }
-
- var orders []exchange.OrderDetail
- for _, order := range allOrders {
- // Status 2 == Filled, -1 == Cancelled.
- if order.Status == 2 || order.Status == -1 {
- continue
- }
-
- symbol := pair.NewCurrencyPairDelimiter(order.Symbol, o.ConfigCurrencyPairFormat.Delimiter)
- orderDate := time.Unix(order.Created, 0)
- side := exchange.OrderSide(strings.ToUpper(order.Type))
- orders = append(orders, exchange.OrderDetail{
- ID: fmt.Sprintf("%v", order.OrderID),
- Amount: order.Amount,
- OrderDate: orderDate,
- Price: order.Price,
- OrderSide: side,
- CurrencyPair: symbol,
- Exchange: o.Name,
- })
- }
-
- exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks)
- exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide)
-
- return orders, nil
-}
-
-// GetOrderHistory retrieves account order information
-// Can Limit response to specific order status
-func (o *OKEX) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) {
- var allOrders []OrderInfo
- for _, currency := range getOrdersRequest.Currencies {
- resp, err := o.GetOrderInformation(-1, exchange.FormatExchangeCurrency(o.Name, currency).String())
- if err != nil {
- return nil, err
- }
-
- allOrders = append(allOrders, resp...)
- }
-
- var orders []exchange.OrderDetail
- for _, order := range allOrders {
- symbol := pair.NewCurrencyPairDelimiter(order.Symbol, o.ConfigCurrencyPairFormat.Delimiter)
- orderDate := time.Unix(order.Created, 0)
- side := exchange.OrderSide(strings.ToUpper(order.Type))
- orders = append(orders, exchange.OrderDetail{
- ID: fmt.Sprintf("%v", order.OrderID),
- Amount: order.Amount,
- OrderDate: orderDate,
- Price: order.Price,
- OrderSide: side,
- CurrencyPair: symbol,
- Exchange: o.Name,
- })
- }
-
- exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks)
- exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide)
-
- return orders, nil
-}
diff --git a/exchanges/okgroup/README.md b/exchanges/okgroup/README.md
new file mode 100644
index 00000000..aa5451cb
--- /dev/null
+++ b/exchanges/okgroup/README.md
@@ -0,0 +1,133 @@
+# GoCryptoTrader package Okex
+
+
+
+
+[](https://travis-ci.org/thrasher-/gocryptotrader)
+[](https://github.com/thrasher-/gocryptotrader/blob/master/LICENSE)
+[](https://godoc.org/github.com/thrasher-/gocryptotrader/exchanges/okex)
+[](http://codecov.io/github/thrasher-/gocryptotrader?branch=master)
+[](https://goreportcard.com/report/github.com/thrasher-/gocryptotrader)
+
+
+This okex package is part of the GoCryptoTrader codebase.
+
+## This is still in active development
+
+You can track ideas, planned features and what's in progresss on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader).
+
+Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://gocryptotrader.herokuapp.com/)
+
+## OKex Exchange
+
+### Current Features
+
++ REST Support
+
+### How to enable
+
++ [Enable via configuration](https://github.com/thrasher-/gocryptotrader/tree/master/config#enable-exchange-via-config-example)
+
++ Individual package example below:
+
+```go
+ // Exchanges will be abstracted out in further updates and examples will be
+ // supplied then
+```
+
+### How to do REST public/private calls
+
++ If enabled via "configuration".json file the exchange will be added to the
+IBotExchange array in the ```go var bot Bot``` and you will only be able to use
+the wrapper interface functions for accessing exchange data. View routines.go
+for an example of integration usage with GoCryptoTrader. Rudimentary example
+below:
+
+main.go
+```go
+var o exchange.IBotExchange
+
+for i := range bot.exchanges {
+ if bot.exchanges[i].GetName() == "OKex" {
+ y = bot.exchanges[i]
+ }
+}
+
+// Public calls - wrapper functions
+
+// Fetches current ticker information
+tick, err := o.GetTickerPrice()
+if err != nil {
+ // Handle error
+}
+
+// Fetches current orderbook information
+ob, err := o.GetOrderbookEx()
+if err != nil {
+ // Handle error
+}
+
+// Private calls - wrapper functions - make sure your APIKEY and APISECRET are
+// set and AuthenticatedAPISupport is set to true
+
+// Fetches current account information
+accountInfo, err := o.GetAccountInfo()
+if err != nil {
+ // Handle error
+}
+```
+
++ If enabled via individually importing package, rudimentary example below:
+
+```go
+// Public calls
+
+// Fetches current ticker information
+ticker, err := o.GetSpotTicker()
+if err != nil {
+ // Handle error
+}
+
+// Fetches current orderbook information
+ob, err := o.GetSpotMarketDepth()
+if err != nil {
+ // Handle error
+}
+
+// Private calls - make sure your APIKEY and APISECRET are set and
+// AuthenticatedAPISupport is set to true
+
+// GetContractPosition returns contract positioning
+accountInfo, err := o.GetContractPosition(...)
+if err != nil {
+ // Handle error
+}
+
+// Submits an order and the exchange and returns its tradeID
+tradeID, err := o.PlaceContractOrders(...)
+if err != nil {
+ // Handle error
+}
+```
+
+### Please click GoDocs chevron above to view current GoDoc information for this package
+
+## Contribution
+
+Please feel free to submit any pull requests or suggest any desired features to be added.
+
+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).
++ Pull requests need to be based on and opened against the `master` branch.
+
+## Donations
+
+
+
+If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
+
+***1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB***
+
diff --git a/exchanges/okgroup/okgroup.go b/exchanges/okgroup/okgroup.go
new file mode 100644
index 00000000..83d5dd6b
--- /dev/null
+++ b/exchanges/okgroup/okgroup.go
@@ -0,0 +1,840 @@
+package okgroup
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "net/http"
+ "net/url"
+ "reflect"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/google/go-querystring/query"
+ "github.com/gorilla/websocket"
+ "github.com/thrasher-/gocryptotrader/common"
+ "github.com/thrasher-/gocryptotrader/config"
+ exchange "github.com/thrasher-/gocryptotrader/exchanges"
+ log "github.com/thrasher-/gocryptotrader/logger"
+)
+
+const (
+ okGroupAuthRate = 0
+ okGroupUnauthRate = 0
+ // OKGroupAPIPath const to help with api url formatting
+ OKGroupAPIPath = "api/"
+ // API subsections
+ okGroupAccountSubsection = "account"
+ okGroupTokenSubsection = "spot"
+ okGroupMarginTradingSubsection = "margin"
+ // OKGroupAccounts common api endpoint
+ OKGroupAccounts = "accounts"
+ // OKGroupLedger common api endpoint
+ OKGroupLedger = "ledger"
+ // OKGroupOrders common api endpoint
+ OKGroupOrders = "orders"
+ // OKGroupBatchOrders common api endpoint
+ OKGroupBatchOrders = "batch_orders"
+ // OKGroupCancelOrders common api endpoint
+ OKGroupCancelOrders = "cancel_orders"
+ // OKGroupCancelOrder common api endpoint
+ OKGroupCancelOrder = "cancel_order"
+ // OKGroupCancelBatchOrders common api endpoint
+ OKGroupCancelBatchOrders = "cancel_batch_orders"
+ // OKGroupPendingOrders common api endpoint
+ OKGroupPendingOrders = "orders_pending"
+ // OKGroupTrades common api endpoint
+ OKGroupTrades = "trades"
+ // OKGroupTicker common api endpoint
+ OKGroupTicker = "ticker"
+ // OKGroupInstruments common api endpoint
+ OKGroupInstruments = "instruments"
+ // OKGroupLiquidation common api endpoint
+ OKGroupLiquidation = "liquidation"
+ // OKGroupMarkPrice common api endpoint
+ OKGroupMarkPrice = "mark_price"
+ // OKGroupGetAccountDepositHistory common api endpoint
+ OKGroupGetAccountDepositHistory = "deposit/history"
+ // OKGroupGetSpotTransactionDetails common api endpoint
+ OKGroupGetSpotTransactionDetails = "fills"
+ // OKGroupGetSpotOrderBook common api endpoint
+ OKGroupGetSpotOrderBook = "book"
+ // OKGroupGetSpotMarketData common api endpoint
+ OKGroupGetSpotMarketData = "candles"
+ // OKGroupPriceLimit common api endpoint
+ OKGroupPriceLimit = "price_limit"
+ // Account based endpoints
+ okGroupGetAccountCurrencies = "currencies"
+ okGroupGetAccountWalletInformation = "wallet"
+ okGroupFundsTransfer = "transfer"
+ okGroupWithdraw = "withdrawal"
+ okGroupGetWithdrawalFees = "withdrawal/fee"
+ okGroupGetWithdrawalHistory = "withdrawal/history"
+ okGroupGetDepositAddress = "deposit/address"
+ // Margin based endpoints
+ okGroupGetMarketAvailability = "availability"
+ okGroupGetLoanHistory = "borrowed"
+ okGroupGetLoan = "borrow"
+ okGroupGetRepayment = "repayment"
+)
+
+var errMissValue = errors.New("warning - resp value is missing from exchange")
+
+// OKGroup is the overaching type across the all of OKEx's exchange methods
+type OKGroup struct {
+ exchange.Base
+ ExchangeName string
+ WebsocketConn *websocket.Conn
+ mu sync.Mutex
+ // Spot and contract market error codes as per https://www.okex.com/rest_request.html
+ ErrorCodes map[string]error
+ // Stores for corresponding variable checks
+ ContractTypes []string
+ CurrencyPairs []string
+ ContractPosition []string
+ Types []string
+ // URLs to be overridden by implementations of OKGroup
+ APIURL string
+ APIVersion string
+ WebsocketURL string
+}
+
+// Setup method sets current configuration details if enabled
+func (o *OKGroup) Setup(exch config.ExchangeConfig) {
+ if !exch.Enabled {
+ o.SetEnabled(false)
+ } else {
+ o.Name = exch.Name
+ o.Enabled = true
+ o.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
+ o.SetAPIKeys(exch.APIKey, exch.APISecret, exch.ClientID, false)
+ o.SetHTTPClientTimeout(exch.HTTPTimeout)
+ o.SetHTTPClientUserAgent(exch.HTTPUserAgent)
+ o.RESTPollingDelay = exch.RESTPollingDelay
+ o.Verbose = exch.Verbose
+ o.Websocket.SetWsStatusAndConnection(exch.Websocket)
+ o.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
+ o.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
+ o.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
+ err := o.SetCurrencyPairFormat()
+ if err != nil {
+ log.Fatal(err)
+ }
+ err = o.SetAssetTypes()
+ if err != nil {
+ log.Fatal(err)
+ }
+ err = o.SetAutoPairDefaults()
+ if err != nil {
+ log.Fatal(err)
+ }
+ err = o.SetAPIURL(exch)
+ if err != nil {
+ log.Fatal(err)
+ }
+ err = o.SetClientProxyAddress(exch.ProxyAddress)
+ if err != nil {
+ log.Fatal(err)
+ }
+ err = o.WebsocketSetup(o.WsConnect,
+ exch.Name,
+ exch.Websocket,
+ o.WebsocketURL,
+ exch.WebsocketURL)
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+// GetAccountCurrencies returns a list of tradable spot instruments and their properties
+func (o *OKGroup) GetAccountCurrencies() (resp []GetAccountCurrenciesResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupAccountSubsection, okGroupGetAccountCurrencies, nil, &resp, true)
+}
+
+// GetAccountWalletInformation returns a list of wallets and their properties
+func (o *OKGroup) GetAccountWalletInformation(currency string) (resp []WalletInformationResponse, _ error) {
+ var requestURL string
+ if currency != "" {
+ requestURL = fmt.Sprintf("%v/%v", okGroupGetAccountWalletInformation, currency)
+ } else {
+ requestURL = okGroupGetAccountWalletInformation
+ }
+
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupAccountSubsection, requestURL, nil, &resp, true)
+}
+
+// TransferAccountFunds the transfer of funds between wallet, trading accounts, main account and sub accounts.
+func (o *OKGroup) TransferAccountFunds(request TransferAccountFundsRequest) (resp TransferAccountFundsResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodPost, okGroupAccountSubsection, okGroupFundsTransfer, request, &resp, true)
+}
+
+// AccountWithdraw withdrawal of tokens to OKCoin International, other OKEx accounts or other addresses.
+func (o *OKGroup) AccountWithdraw(request AccountWithdrawRequest) (resp AccountWithdrawResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodPost, okGroupAccountSubsection, okGroupWithdraw, request, &resp, true)
+}
+
+// GetAccountWithdrawalFee retrieves the information about the recommended network transaction fee for withdrawals to digital asset addresses. The higher the fees are, the sooner the confirmations you will get.
+func (o *OKGroup) GetAccountWithdrawalFee(currency string) (resp []GetAccountWithdrawalFeeResponse, _ error) {
+ var requestURL string
+ if currency != "" {
+ requestURL = fmt.Sprintf("%v?currency=%v", okGroupGetWithdrawalFees, currency)
+ } else {
+ requestURL = okGroupGetAccountWalletInformation
+ }
+
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupAccountSubsection, requestURL, nil, &resp, true)
+}
+
+// GetAccountWithdrawalHistory retrieves all recent withdrawal records.
+func (o *OKGroup) GetAccountWithdrawalHistory(currency string) (resp []WithdrawalHistoryResponse, _ error) {
+ var requestURL string
+ if currency != "" {
+ requestURL = fmt.Sprintf("%v/%v", okGroupGetWithdrawalHistory, currency)
+ } else {
+ requestURL = okGroupGetWithdrawalHistory
+ }
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupAccountSubsection, requestURL, nil, &resp, true)
+}
+
+// GetAccountBillDetails retrieves the bill details of the wallet. All the information will be paged and sorted in reverse chronological order,
+// which means the latest will be at the top. Please refer to the pagination section for additional records after the first page.
+// 3 months recent records will be returned at maximum
+func (o *OKGroup) GetAccountBillDetails(request GetAccountBillDetailsRequest) (resp []GetAccountBillDetailsResponse, _ error) {
+ requestURL := fmt.Sprintf("%v%v", OKGroupLedger, FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupAccountSubsection, requestURL, nil, &resp, true)
+}
+
+// GetAccountDepositAddressForCurrency retrieves the deposit addresses of different tokens, including previously used addresses.
+func (o *OKGroup) GetAccountDepositAddressForCurrency(currency string) (resp []GetDepositAddressResponse, _ error) {
+ urlValues := url.Values{}
+ urlValues.Set("currency", currency)
+ requestURL := fmt.Sprintf("%v?%v", okGroupGetDepositAddress, urlValues.Encode())
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupAccountSubsection, requestURL, nil, &resp, true)
+}
+
+// GetAccountDepositHistory retrieves the deposit history of all tokens.100 recent records will be returned at maximum
+func (o *OKGroup) GetAccountDepositHistory(currency string) (resp []GetAccountDepositHistoryResponse, _ error) {
+ var requestURL string
+ if currency != "" {
+ requestURL = fmt.Sprintf("%v/%v", OKGroupGetAccountDepositHistory, currency)
+ } else {
+ requestURL = okGroupGetWithdrawalHistory
+ }
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupAccountSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSpotTradingAccounts retrieves the list of assets(only show pairs with balance larger than 0), the balances, amount available/on hold in spot accounts.
+func (o *OKGroup) GetSpotTradingAccounts() (resp []GetSpotTradingAccountResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupTokenSubsection, OKGroupAccounts, nil, &resp, true)
+}
+
+// GetSpotTradingAccountForCurrency This endpoint supports getting the balance, amount available/on hold of a token in spot account.
+func (o *OKGroup) GetSpotTradingAccountForCurrency(currency string) (resp GetSpotTradingAccountResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v", OKGroupAccounts, currency)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupTokenSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSpotBillDetailsForCurrency This endpoint supports getting the balance, amount available/on hold of a token in spot account.
+func (o *OKGroup) GetSpotBillDetailsForCurrency(request GetSpotBillDetailsForCurrencyRequest) (resp []GetSpotBillDetailsForCurrencyResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v%v", OKGroupAccounts, request.Currency, OKGroupLedger, FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupTokenSubsection, requestURL, nil, &resp, true)
+}
+
+// PlaceSpotOrder token trading only supports limit and market orders (more order types will become available in the future).
+// You can place an order only if you have enough funds.
+// Once your order is placed, the amount will be put on hold.
+func (o *OKGroup) PlaceSpotOrder(request PlaceSpotOrderRequest) (resp PlaceSpotOrderResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodPost, okGroupTokenSubsection, OKGroupOrders, request, &resp, true)
+}
+
+// PlaceMultipleSpotOrders supports placing multiple orders for specific trading pairs
+// up to 4 trading pairs, maximum 4 orders for each pair
+func (o *OKGroup) PlaceMultipleSpotOrders(request []PlaceSpotOrderRequest) (map[string][]PlaceSpotOrderResponse, []error) {
+ currencyPairOrders := make(map[string]int)
+ resp := make(map[string][]PlaceSpotOrderResponse)
+ for _, order := range request {
+ currencyPairOrders[order.InstrumentID]++
+ }
+ if len(currencyPairOrders) > 4 {
+ return resp, []error{errors.New("up to 4 trading pairs")}
+ }
+ for _, orderCount := range currencyPairOrders {
+ if orderCount > 4 {
+ return resp, []error{errors.New("maximum 4 orders for each pair")}
+ }
+ }
+
+ err := o.SendHTTPRequest(http.MethodPost, okGroupTokenSubsection, OKGroupBatchOrders, request, &resp, true)
+ if err != nil {
+ return resp, []error{err}
+ }
+
+ orderErrors := []error{}
+ for currency, orderResponse := range resp {
+ for _, order := range orderResponse {
+ if !order.Result {
+ orderErrors = append(orderErrors, fmt.Errorf("order for currency %v failed to be placed", currency))
+ }
+ }
+ }
+ if len(orderErrors) == 0 {
+ orderErrors = nil
+ }
+
+ return resp, orderErrors
+}
+
+// CancelSpotOrder Cancelling an unfilled order.
+func (o *OKGroup) CancelSpotOrder(request CancelSpotOrderRequest) (resp CancelSpotOrderResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v", OKGroupCancelOrders, request.OrderID)
+ return resp, o.SendHTTPRequest(http.MethodPost, okGroupTokenSubsection, requestURL, request, &resp, true)
+}
+
+// CancelMultipleSpotOrders Cancelling multiple unfilled orders.
+func (o *OKGroup) CancelMultipleSpotOrders(request CancelMultipleSpotOrdersRequest) (resp map[string][]CancelMultipleSpotOrdersResponse, err error) {
+ resp = make(map[string][]CancelMultipleSpotOrdersResponse)
+ if len(request.OrderIDs) > 4 {
+ return resp, errors.New("maximum 4 order cancellations for each pair")
+ }
+
+ err = o.SendHTTPRequest(http.MethodPost, okGroupTokenSubsection, OKGroupCancelBatchOrders, []CancelMultipleSpotOrdersRequest{request}, &resp, true)
+ if err != nil {
+ return
+ }
+
+ for currency, orderResponse := range resp {
+ for _, order := range orderResponse {
+ cancellationResponse := CancelMultipleSpotOrdersResponse{
+ OrderID: order.OrderID,
+ Result: order.Result,
+ ClientOID: order.ClientOID,
+ }
+
+ if !order.Result {
+ cancellationResponse.Error = fmt.Errorf("order %v for currency %v failed to be cancelled", order.OrderID, currency)
+ }
+
+ resp[currency] = append(resp[currency], cancellationResponse)
+ }
+ }
+
+ return
+}
+
+// GetSpotOrders List your orders. Cursor pagination is used.
+// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKGroup) GetSpotOrders(request GetSpotOrdersRequest) (resp []GetSpotOrderResponse, _ error) {
+ requestURL := fmt.Sprintf("%v%v", OKGroupOrders, FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupTokenSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSpotOpenOrders List all your current open orders. Cursor pagination is used.
+// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKGroup) GetSpotOpenOrders(request GetSpotOpenOrdersRequest) (resp []GetSpotOrderResponse, _ error) {
+ requestURL := fmt.Sprintf("%v%v", OKGroupPendingOrders, FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupTokenSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSpotOrder Get order details by order ID.
+func (o *OKGroup) GetSpotOrder(request GetSpotOrderRequest) (resp GetSpotOrderResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v%v", OKGroupOrders, request.OrderID, FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupTokenSubsection, requestURL, request, &resp, true)
+}
+
+// GetSpotTransactionDetails Get details of the recent filled orders. Cursor pagination is used.
+// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKGroup) GetSpotTransactionDetails(request GetSpotTransactionDetailsRequest) (resp []GetSpotTransactionDetailsResponse, _ error) {
+ requestURL := fmt.Sprintf("%v%v", OKGroupGetSpotTransactionDetails, FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupTokenSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSpotTokenPairDetails Get market data. This endpoint provides the snapshots of market data and can be used without verifications.
+// List trading pairs and get the trading limit, price, and more information of different trading pairs.
+func (o *OKGroup) GetSpotTokenPairDetails() (resp []GetSpotTokenPairDetailsResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupTokenSubsection, OKGroupInstruments, nil, &resp, true)
+}
+
+// GetSpotOrderBook Getting the order book of a trading pair. Pagination is not supported here.
+// The whole book will be returned for one request. WebSocket is recommended here.
+func (o *OKGroup) GetSpotOrderBook(request GetSpotOrderBookRequest) (resp GetSpotOrderBookResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v%v", OKGroupInstruments, request.InstrumentID, OKGroupGetSpotOrderBook, FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupTokenSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSpotAllTokenPairsInformation Get the last traded price, best bid/ask price, 24 hour trading volume and more info of all trading pairs.
+func (o *OKGroup) GetSpotAllTokenPairsInformation() (resp []GetSpotTokenPairsInformationResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v", OKGroupInstruments, OKGroupTicker)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupTokenSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSpotAllTokenPairsInformationForCurrency Get the last traded price, best bid/ask price, 24 hour trading volume and more info of a currency
+func (o *OKGroup) GetSpotAllTokenPairsInformationForCurrency(currency string) (resp GetSpotTokenPairsInformationResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v", OKGroupInstruments, currency, OKGroupTicker)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupTokenSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSpotFilledOrdersInformation Get the recent 60 transactions of all trading pairs.
+// Cursor pagination is used. All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKGroup) GetSpotFilledOrdersInformation(request GetSpotFilledOrdersInformationRequest) (resp []GetSpotFilledOrdersInformationResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v%v", OKGroupInstruments, request.InstrumentID, OKGroupTrades, FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupTokenSubsection, requestURL, nil, &resp, true)
+}
+
+// GetSpotMarketData Get the charts of the trading pairs. Charts are returned in grouped buckets based on requested granularity.
+func (o *OKGroup) GetSpotMarketData(request GetSpotMarketDataRequest) (resp GetSpotMarketDataResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v%v", OKGroupInstruments, request.InstrumentID, OKGroupGetSpotMarketData, FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupTokenSubsection, requestURL, nil, &resp, true)
+}
+
+// GetMarginTradingAccounts List all assets under token margin trading account, including information such as balance, amount on hold and more.
+func (o *OKGroup) GetMarginTradingAccounts() (resp []GetMarginAccountsResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupMarginTradingSubsection, OKGroupAccounts, nil, &resp, true)
+}
+
+// GetMarginTradingAccountsForCurrency Get the balance, amount on hold and more useful information.
+func (o *OKGroup) GetMarginTradingAccountsForCurrency(currency string) (resp GetMarginAccountsResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v", OKGroupAccounts, currency)
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupMarginTradingSubsection, requestURL, nil, &resp, true)
+}
+
+// GetMarginBillDetails List all bill details. Pagination is used here.
+// before and after cursor arguments should not be confused with before and after in chronological time.
+// Most paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKGroup) GetMarginBillDetails(request GetMarginBillDetailsRequest) (resp []GetSpotBillDetailsForCurrencyResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v/%v%v", OKGroupAccounts, request.InstrumentID, OKGroupLedger, FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupMarginTradingSubsection, requestURL, nil, &resp, true)
+}
+
+// GetMarginAccountSettings Get all information of the margin trading account,
+// including the maximum loan amount, interest rate, and maximum leverage.
+func (o *OKGroup) GetMarginAccountSettings(currency string) (resp []GetMarginAccountSettingsResponse, _ error) {
+ var requestURL string
+ if currency != "" {
+ requestURL = fmt.Sprintf("%v/%v/%v", OKGroupAccounts, currency, okGroupGetMarketAvailability)
+ } else {
+ requestURL = fmt.Sprintf("%v/%v", OKGroupAccounts, okGroupGetMarketAvailability)
+ }
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupMarginTradingSubsection, requestURL, nil, &resp, true)
+}
+
+// GetMarginLoanHistory Get loan history of the margin trading account.
+// Pagination is used here. before and after cursor arguments should not be confused with before and after in chronological time.
+// Most paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKGroup) GetMarginLoanHistory(request GetMarginLoanHistoryRequest) (resp []GetMarginLoanHistoryResponse, _ error) {
+ var requestURL string
+ if len(request.InstrumentID) > 0 {
+ requestURL = fmt.Sprintf("%v/%v/%v", OKGroupAccounts, request.InstrumentID, okGroupGetLoan)
+ } else {
+ requestURL = fmt.Sprintf("%v/%v", OKGroupAccounts, okGroupGetLoan)
+ }
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupMarginTradingSubsection, requestURL, nil, &resp, true)
+}
+
+// OpenMarginLoan Borrowing tokens in a margin trading account.
+func (o *OKGroup) OpenMarginLoan(request OpenMarginLoanRequest) (resp OpenMarginLoanResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v", OKGroupAccounts, okGroupGetLoan)
+ return resp, o.SendHTTPRequest(http.MethodPost, okGroupMarginTradingSubsection, requestURL, request, &resp, true)
+}
+
+// RepayMarginLoan Repaying tokens in a margin trading account.
+func (o *OKGroup) RepayMarginLoan(request RepayMarginLoanRequest) (resp RepayMarginLoanResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v", OKGroupAccounts, okGroupGetRepayment)
+ return resp, o.SendHTTPRequest(http.MethodPost, okGroupMarginTradingSubsection, requestURL, request, &resp, true)
+}
+
+// PlaceMarginOrder OKEx API only supports limit and market orders (more orders will become available in the future).
+// You can place an order only if you have enough funds. Once your order is placed, the amount will be put on hold.
+func (o *OKGroup) PlaceMarginOrder(request PlaceSpotOrderRequest) (resp PlaceSpotOrderResponse, _ error) {
+ return resp, o.SendHTTPRequest(http.MethodPost, okGroupMarginTradingSubsection, OKGroupOrders, request, &resp, true)
+}
+
+// PlaceMultipleMarginOrders Place multiple orders for specific trading pairs (up to 4 trading pairs, maximum 4 orders each)
+func (o *OKGroup) PlaceMultipleMarginOrders(request []PlaceSpotOrderRequest) (map[string][]PlaceSpotOrderResponse, []error) {
+ currencyPairOrders := make(map[string]int)
+ resp := make(map[string][]PlaceSpotOrderResponse)
+ for _, order := range request {
+ currencyPairOrders[order.InstrumentID]++
+ }
+ if len(currencyPairOrders) > 4 {
+ return resp, []error{errors.New("up to 4 trading pairs")}
+ }
+ for _, orderCount := range currencyPairOrders {
+ if orderCount > 4 {
+ return resp, []error{errors.New("maximum 4 orders for each pair")}
+ }
+ }
+
+ err := o.SendHTTPRequest(http.MethodPost, okGroupMarginTradingSubsection, OKGroupBatchOrders, request, &resp, true)
+ if err != nil {
+ return resp, []error{err}
+ }
+
+ orderErrors := []error{}
+ for currency, orderResponse := range resp {
+ for _, order := range orderResponse {
+ if !order.Result {
+ orderErrors = append(orderErrors, fmt.Errorf("order for currency %v failed to be placed", currency))
+ }
+ }
+ }
+ if len(orderErrors) == 0 {
+ orderErrors = nil
+ }
+
+ return resp, orderErrors
+}
+
+// CancelMarginOrder Cancelling an unfilled order.
+func (o *OKGroup) CancelMarginOrder(request CancelSpotOrderRequest) (resp CancelSpotOrderResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v", OKGroupCancelOrders, request.OrderID)
+ return resp, o.SendHTTPRequest(http.MethodPost, okGroupMarginTradingSubsection, requestURL, request, &resp, true)
+}
+
+// CancelMultipleMarginOrders Cancelling multiple unfilled orders.
+func (o *OKGroup) CancelMultipleMarginOrders(request CancelMultipleSpotOrdersRequest) (map[string][]CancelMultipleSpotOrdersResponse, []error) {
+ resp := make(map[string][]CancelMultipleSpotOrdersResponse)
+ if len(request.OrderIDs) > 4 {
+ return resp, []error{errors.New("maximum 4 order cancellations for each pair")}
+ }
+
+ err := o.SendHTTPRequest(http.MethodPost, okGroupMarginTradingSubsection, OKGroupCancelBatchOrders, []CancelMultipleSpotOrdersRequest{request}, &resp, true)
+ if err != nil {
+ return resp, []error{err}
+ }
+
+ orderErrors := []error{}
+ for currency, orderResponse := range resp {
+ for _, order := range orderResponse {
+ if !order.Result {
+ orderErrors = append(orderErrors, fmt.Errorf("order %v for currency %v failed to be cancelled", order.OrderID, currency))
+ }
+ }
+ }
+ if len(orderErrors) == 0 {
+ orderErrors = nil
+ }
+
+ return resp, orderErrors
+}
+
+// GetMarginOrders List your orders. Cursor pagination is used. All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKGroup) GetMarginOrders(request GetSpotOrdersRequest) (resp []GetSpotOrderResponse, _ error) {
+ requestURL := fmt.Sprintf("%v%v", OKGroupOrders, FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupMarginTradingSubsection, requestURL, nil, &resp, true)
+}
+
+// GetMarginOpenOrders List all your current open orders. Cursor pagination is used. All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKGroup) GetMarginOpenOrders(request GetSpotOpenOrdersRequest) (resp []GetSpotOrderResponse, _ error) {
+ requestURL := fmt.Sprintf("%v%v", OKGroupPendingOrders, FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupMarginTradingSubsection, requestURL, nil, &resp, true)
+}
+
+// GetMarginOrder Get order details by order ID.
+func (o *OKGroup) GetMarginOrder(request GetSpotOrderRequest) (resp GetSpotOrderResponse, _ error) {
+ requestURL := fmt.Sprintf("%v/%v%v", OKGroupOrders, request.OrderID, FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupMarginTradingSubsection, requestURL, request, &resp, true)
+}
+
+// GetMarginTransactionDetails Get details of the recent filled orders. Cursor pagination is used.
+// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
+func (o *OKGroup) GetMarginTransactionDetails(request GetSpotTransactionDetailsRequest) (resp []GetSpotTransactionDetailsResponse, _ error) {
+ requestURL := fmt.Sprintf("%v%v", OKGroupGetSpotTransactionDetails, FormatParameters(request))
+ return resp, o.SendHTTPRequest(http.MethodGet, okGroupMarginTradingSubsection, requestURL, nil, &resp, true)
+}
+
+// FormatParameters Formats URL parameters, useful for optional parameters due to OKEX signature check
+func FormatParameters(request interface{}) (parameters string) {
+ v, err := query.Values(request)
+ if err != nil {
+ log.Errorf("Could not parse %v to URL values. Check that the type has url fields", reflect.TypeOf(request).Name())
+ return
+ }
+ urlEncodedValues := v.Encode()
+ if len(urlEncodedValues) > 0 {
+ parameters = fmt.Sprintf("?%v", urlEncodedValues)
+ }
+ return
+}
+
+// GetErrorCode returns an error code
+func (o *OKGroup) GetErrorCode(code interface{}) error {
+ var assertedCode string
+
+ switch reflect.TypeOf(code).String() {
+ case "float64":
+ assertedCode = strconv.FormatFloat(code.(float64), 'f', -1, 64)
+ case "string":
+ assertedCode = code.(string)
+ default:
+ return errors.New("unusual type returned")
+ }
+
+ if i, ok := o.ErrorCodes[assertedCode]; ok {
+ return i
+ }
+ return errors.New("unable to find SPOT error code")
+}
+
+// SendHTTPRequest sends an authenticated http request to a desired
+// path with a JSON payload (of present)
+// URL arguments must be in the request path and not as url.URL values
+func (o *OKGroup) SendHTTPRequest(httpMethod, requestType, requestPath string, data, result interface{}, authenticated bool) (err error) {
+ if authenticated && !o.AuthenticatedAPISupport {
+ return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, o.Name)
+ }
+
+ utcTime := time.Now().UTC()
+ iso := utcTime.String()
+ isoBytes := []byte(iso)
+ iso = string(isoBytes[:10]) + "T" + string(isoBytes[11:23]) + "Z"
+ payload := []byte("")
+
+ if data != nil {
+ payload, err = common.JSONEncode(data)
+ if err != nil {
+ return errors.New("sendHTTPRequest: Unable to JSON request")
+ }
+
+ if o.Verbose {
+ log.Debugf("Request JSON: %s\n", payload)
+ }
+ }
+
+ path := o.APIUrl + requestType + o.APIVersion + requestPath
+ if o.Verbose {
+ log.Debugf("Sending %v request to %s \n", requestType, path)
+ }
+
+ headers := make(map[string]string)
+ headers["Content-Type"] = "application/json"
+ if authenticated {
+ signPath := fmt.Sprintf("/%v%v%v%v", OKGroupAPIPath, requestType, o.APIVersion, requestPath)
+ hmac := common.GetHMAC(common.HashSHA256, []byte(iso+httpMethod+signPath+string(payload)), []byte(o.APISecret))
+ base64 := common.Base64Encode(hmac)
+ headers["OK-ACCESS-KEY"] = o.APIKey
+ headers["OK-ACCESS-SIGN"] = base64
+ headers["OK-ACCESS-TIMESTAMP"] = iso
+ headers["OK-ACCESS-PASSPHRASE"] = o.ClientID
+ }
+
+ var intermediary json.RawMessage
+ type errCapFormat struct {
+ Error int64 `json:"error_code,omitempty"`
+ ErrorMessage string `json:"error_message,omitempty"`
+ Result bool `json:"result,string,omitempty"`
+ }
+
+ errCap := errCapFormat{}
+ errCap.Result = true
+ err = o.SendPayload(strings.ToUpper(httpMethod), path, headers, bytes.NewBuffer(payload), &intermediary, authenticated, o.Verbose)
+ if err != nil {
+ return err
+ }
+
+ err = common.JSONDecode(intermediary, &errCap)
+ if err == nil {
+ if errCap.ErrorMessage != "" {
+ return fmt.Errorf("error: %v", errCap.ErrorMessage)
+ }
+ if errCap.Error > 0 {
+ return fmt.Errorf("sendHTTPRequest error - %s",
+ o.ErrorCodes[strconv.FormatInt(errCap.Error, 10)])
+ }
+ if !errCap.Result {
+ return errors.New("unspecified error occurred")
+ }
+
+ }
+
+ return common.JSONDecode(intermediary, result)
+}
+
+// SetCheckVarDefaults sets main variables that will be used in requests because
+// api does not return an error if there are misspellings in strings. So better
+// to check on this, this end.
+func (o *OKGroup) SetCheckVarDefaults() {
+ o.ContractTypes = []string{"this_week", "next_week", "quarter"}
+ o.CurrencyPairs = []string{"btc_usd", "ltc_usd", "eth_usd", "etc_usd", "bch_usd"}
+ o.Types = []string{"1min", "3min", "5min", "15min", "30min", "1day", "3day",
+ "1week", "1hour", "2hour", "4hour", "6hour", "12hour"}
+ o.ContractPosition = []string{"1", "2", "3", "4"}
+}
+
+// GetFee returns an estimate of fee based on type of transaction
+func (o *OKGroup) GetFee(feeBuilder exchange.FeeBuilder) (fee float64, _ error) {
+ switch feeBuilder.FeeType {
+ case exchange.CryptocurrencyTradeFee:
+ fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.IsMaker)
+ case exchange.CryptocurrencyWithdrawalFee:
+ withdrawFees, err := o.GetAccountWithdrawalFee(feeBuilder.CurrencyItem)
+ if err != nil {
+ return -1, err
+ }
+ for _, withdrawFee := range withdrawFees {
+ if withdrawFee.Currency == feeBuilder.CurrencyItem {
+ fee = withdrawFee.MinFee
+ break
+ }
+ }
+ }
+ if fee < 0 {
+ fee = 0
+ }
+
+ return fee, nil
+}
+
+func calculateTradingFee(purchasePrice, amount float64, isMaker bool) (fee float64) {
+ // TODO volume based fees
+ if isMaker {
+ fee = 0.001
+ } else {
+ fee = 0.0015
+ }
+ return fee * amount * purchasePrice
+}
+
+// SetErrorDefaults sets the full error default list
+func (o *OKGroup) SetErrorDefaults() {
+ o.ErrorCodes = map[string]error{
+ "0": errors.New("successful"),
+ "1": errors.New("invalid parameter in url normally"),
+ "30001": errors.New("request header \"OK_ACCESS_KEY\" cannot be blank"),
+ "30002": errors.New("request header \"OK_ACCESS_SIGN\" cannot be blank"),
+ "30003": errors.New("request header \"OK_ACCESS_TIMESTAMP\" cannot be blank"),
+ "30004": errors.New("request header \"OK_ACCESS_PASSPHRASE\" cannot be blank"),
+ "30005": errors.New("invalid OK_ACCESS_TIMESTAMP"),
+ "30006": errors.New("invalid OK_ACCESS_KEY"),
+ "30007": errors.New("invalid Content_Type, please use \"application/json\" format"),
+ "30008": errors.New("timestamp request expired"),
+ "30009": errors.New("system error"),
+ "30010": errors.New("api validation failed"),
+ "30011": errors.New("invalid IP"),
+ "30012": errors.New("invalid authorization"),
+ "30013": errors.New("invalid sign"),
+ "30014": errors.New("request too frequent"),
+ "30015": errors.New("request header \"OK_ACCESS_PASSPHRASE\" incorrect"),
+ "30016": errors.New("you are using v1 apiKey, please use v1 endpoint. If you would like to use v3 endpoint, please subscribe to v3 apiKey"),
+ "30017": errors.New("apikey's broker id does not match"),
+ "30018": errors.New("apikey's domain does not match"),
+ "30020": errors.New("body cannot be blank"),
+ "30021": errors.New("json data format error"),
+ "30023": errors.New("required parameter cannot be blank"),
+ "30024": errors.New("parameter value error"),
+ "30025": errors.New("parameter category error"),
+ "30026": errors.New("requested too frequent; endpoint limit exceeded"),
+ "30027": errors.New("login failure"),
+ "30028": errors.New("unauthorized execution"),
+ "30029": errors.New("account suspended"),
+ "30030": errors.New("endpoint request failed. Please try again"),
+ "30031": errors.New("token does not exist"),
+ "30032": errors.New("pair does not exist"),
+ "30033": errors.New("exchange domain does not exist"),
+ "30034": errors.New("exchange ID does not exist"),
+ "30035": errors.New("trading is not supported in this website"),
+ "30036": errors.New("no relevant data"),
+ "30037": errors.New("endpoint is offline or unavailable"),
+ "30038": errors.New("user does not exist"),
+ "32001": errors.New("futures account suspended"),
+ "32002": errors.New("futures account does not exist"),
+ "32003": errors.New("canceling, please wait"),
+ "32004": errors.New("you have no unfilled orders"),
+ "32005": errors.New("max order quantity"),
+ "32006": errors.New("the order price or trigger price exceeds USD 1 million"),
+ "32007": errors.New("leverage level must be the same for orders on the same side of the contract"),
+ "32008": errors.New("max. positions to open (cross margin)"),
+ "32009": errors.New("max. positions to open (fixed margin)"),
+ "32010": errors.New("leverage cannot be changed with open positions"),
+ "32011": errors.New("futures status error"),
+ "32012": errors.New("futures order update error"),
+ "32013": errors.New("token type is blank"),
+ "32014": errors.New("your number of contracts closing is larger than the number of contracts available"),
+ "32015": errors.New("margin ratio is lower than 100% before opening positions"),
+ "32016": errors.New("margin ratio is lower than 100% after opening position"),
+ "32017": errors.New("no BBO"),
+ "32018": errors.New("the order quantity is less than 1, please try again"),
+ "32019": errors.New("the order price deviates from the price of the previous minute by more than 3%"),
+ "32020": errors.New("the price is not in the range of the price limit"),
+ "32021": errors.New("leverage error"),
+ "32022": errors.New("this function is not supported in your country or region according to the regulations"),
+ "32023": errors.New("this account has outstanding loan"),
+ "32024": errors.New("order cannot be placed during delivery"),
+ "32025": errors.New("order cannot be placed during settlement"),
+ "32026": errors.New("your account is restricted from opening positions"),
+ "32027": errors.New("cancelled over 20 orders"),
+ "32028": errors.New("account is suspended and liquidated"),
+ "32029": errors.New("order info does not exist"),
+ "33001": errors.New("margin account for this pair is not enabled yet"),
+ "33002": errors.New("margin account for this pair is suspended"),
+ "33003": errors.New("no loan balance"),
+ "33004": errors.New("loan amount cannot be smaller than the minimum limit"),
+ "33005": errors.New("repayment amount must exceed 0"),
+ "33006": errors.New("loan order not found"),
+ "33007": errors.New("status not found"),
+ "33008": errors.New("loan amount cannot exceed the maximum limit"),
+ "33009": errors.New("user ID is blank"),
+ "33010": errors.New("you cannot cancel an order during session 2 of call auction"),
+ "33011": errors.New("no new market data"),
+ "33012": errors.New("order cancellation failed"),
+ "33013": errors.New("order placement failed"),
+ "33014": errors.New("order does not exist"),
+ "33015": errors.New("exceeded maximum limit"),
+ "33016": errors.New("margin trading is not open for this token"),
+ "33017": errors.New("insufficient balance"),
+ "33018": errors.New("this parameter must be smaller than 1"),
+ "33020": errors.New("request not supported"),
+ "33021": errors.New("token and the pair do not match"),
+ "33022": errors.New("pair and the order do not match"),
+ "33023": errors.New("you can only place market orders during call auction"),
+ "33024": errors.New("trading amount too small"),
+ "33025": errors.New("base token amount is blank"),
+ "33026": errors.New("transaction completed"),
+ "33027": errors.New("cancelled order or order cancelling"),
+ "33028": errors.New("the decimal places of the trading price exceeded the limit"),
+ "33029": errors.New("the decimal places of the trading size exceeded the limit"),
+ "34001": errors.New("withdrawal suspended"),
+ "34002": errors.New("please add a withdrawal address"),
+ "34003": errors.New("sorry, this token cannot be withdrawn to xx at the moment"),
+ "34004": errors.New("withdrawal fee is smaller than minimum limit"),
+ "34005": errors.New("withdrawal fee exceeds the maximum limit"),
+ "34006": errors.New("withdrawal amount is lower than the minimum limit"),
+ "34007": errors.New("withdrawal amount exceeds the maximum limit"),
+ "34008": errors.New("insufficient balance"),
+ "34009": errors.New("your withdrawal amount exceeds the daily limit"),
+ "34010": errors.New("transfer amount must be larger than 0"),
+ "34011": errors.New("conditions not met"),
+ "34012": errors.New("the minimum withdrawal amount for NEO is 1, and the amount must be an integer"),
+ "34013": errors.New("please transfer"),
+ "34014": errors.New("transfer limited"),
+ "34015": errors.New("subaccount does not exist"),
+ "34016": errors.New("transfer suspended"),
+ "34017": errors.New("account suspended"),
+ "34018": errors.New("incorrect trades password"),
+ "34019": errors.New("please bind your email before withdrawal"),
+ "34020": errors.New("please bind your funds password before withdrawal"),
+ "34021": errors.New("not verified address"),
+ "34022": errors.New("withdrawals are not available for sub accounts"),
+ "35001": errors.New("contract subscribing does not exist"),
+ "35002": errors.New("contract is being settled"),
+ "35003": errors.New("contract is being paused"),
+ "35004": errors.New("pending contract settlement"),
+ "35005": errors.New("perpetual swap trading is not enabled"),
+ "35008": errors.New("margin ratio too low when placing order"),
+ "35010": errors.New("closing position size larger than available size"),
+ "35012": errors.New("placing an order with less than 1 contract"),
+ "35014": errors.New("order size is not in acceptable range"),
+ "35015": errors.New("leverage level unavailable"),
+ "35017": errors.New("changing leverage level"),
+ "35019": errors.New("order size exceeds limit"),
+ "35020": errors.New("order price exceeds limit"),
+ "35021": errors.New("order size exceeds limit of the current tier"),
+ "35022": errors.New("contract is paused or closed"),
+ "35030": errors.New("place multiple orders"),
+ "35031": errors.New("cancel multiple orders"),
+ "35061": errors.New("invalid instrument_id"),
+ }
+}
diff --git a/exchanges/okgroup/okgroup_types.go b/exchanges/okgroup/okgroup_types.go
new file mode 100644
index 00000000..cdd6cb8b
--- /dev/null
+++ b/exchanges/okgroup/okgroup_types.go
@@ -0,0 +1,1514 @@
+package okgroup
+
+import (
+ "time"
+)
+
+// GetAccountCurrenciesResponse response data for GetAccountCurrencies
+type GetAccountCurrenciesResponse struct {
+ CanDeposit int64 `json:"can_deposit"`
+ CanWithdraw int64 `json:"can_withdraw"`
+ Currency string `json:"currency"`
+ MinWithdrawal float64 `json:"min_withdrawal"`
+ Name string `json:"name"`
+}
+
+// WalletInformationResponse response data for WalletInformation
+type WalletInformationResponse struct {
+ Available float64 `json:"available"`
+ Balance float64 `json:"balance"`
+ Currency string `json:"currency"`
+ Hold float64 `json:"hold"`
+}
+
+// TransferAccountFundsRequest request data for TransferAccountFunds
+type TransferAccountFundsRequest struct {
+ Currency string `json:"currency"` // [required] token
+ Amount float64 `json:"amount"` // [required] Transfer amount
+ From int64 `json:"from"` // [required] the remitting account (0: sub account 1: spot 3: futures 4:C2C 5: margin 6: wallet 7:ETT 8:PiggyBank 9:swap)
+ To int64 `json:"to"` // [required] the beneficiary account(0: sub account 1:spot 3: futures 4:C2C 5: margin 6: wallet 7:ETT 8:PiggyBank 9 :swap)
+ SubAccountID string `json:"sub_account,omitempty"` // [optional] sub account name
+ InstrumentID int64 `json:"instrument_id,omitempty"` // [optional] margin token pair ID, for supported pairs only
+}
+
+// TransferAccountFundsResponse response data for TransferAccountFunds
+type TransferAccountFundsResponse struct {
+ Amount float64 `json:"amount"`
+ Currency string `json:"currency"`
+ From int64 `json:"from"`
+ Result bool `json:"result"`
+ To int64 `json:"to"`
+ TransferID int64 `json:"transfer_id"`
+}
+
+// AccountWithdrawRequest request data for AccountWithdrawRequest
+type AccountWithdrawRequest struct {
+ Amount float64 `json:"amount"` // [required] withdrawal amount
+ Currency string `json:"currency"` // [required] token
+ Destination int64 `json:"destination"` // [required] withdrawal address(2:OKCoin International 3:OKEx 4:others)
+ Fee float64 `json:"fee"` // [required] Network transaction fee≥0. Withdrawals to OKCoin or OKEx are fee-free, please set as 0. Withdrawal to external digital asset address requires network transaction fee.
+ ToAddress string `json:"to_address"` // [required] verified digital asset address, email or mobile number,some digital asset address format is address+tag , eg: "ARDOR-7JF3-8F2E-QUWZ-CAN7F:123456"
+ TradePwd string `json:"trade_pwd"` // [required] fund password
+}
+
+// AccountWithdrawResponse response data for AccountWithdrawResponse
+type AccountWithdrawResponse struct {
+ Amount float64 `json:"amount"`
+ Currency string `json:"currency"`
+ Result bool `json:"result"`
+ WithdrawalID int64 `json:"withdrawal_id"`
+}
+
+// GetAccountWithdrawalFeeResponse response data for GetAccountWithdrawalFee
+type GetAccountWithdrawalFeeResponse struct {
+ Currency string `json:"currency"`
+ MinFee float64 `json:"min_fee"`
+ MaxFee float64 `json:"max_fee"`
+}
+
+// WithdrawalHistoryResponse response data for WithdrawalHistoryResponse
+type WithdrawalHistoryResponse struct {
+ Amount float64 `json:"amount"`
+ Currency string `json:"currency"`
+ Fee string `json:"fee"`
+ From string `json:"from"`
+ Status int64 `json:"status"`
+ Timestamp time.Time `json:"timestamp"`
+ To string `json:"to"`
+ Txid string `json:"txid"`
+ PaymentID string `json:"payment_id"`
+ Tag string `json:"tag"`
+}
+
+// GetAccountBillDetailsRequest request data for GetAccountBillDetailsRequest
+type GetAccountBillDetailsRequest struct {
+ Currency string `url:"currency,omitempty"` // [optional] token ,information of all tokens will be returned if the field is left blank
+ Type int64 `url:"type,omitempty"` // [optional] 1:deposit 2:withdrawal 13:cancel withdrawal 18: into futures account 19: out of futures account 20:into sub account 21:out of sub account 28: claim 29: into ETT account 30: out of ETT account 31: into C2C account 32:out of C2C account 33: into margin account 34: out of margin account 37: into spot account 38: out of spot account
+ From int64 `url:"from,omitempty"` // [optional] you would request pages after this page.
+ To int64 `url:"to,omitempty"` // [optional] you would request pages before this page
+ Limit int64 `url:"limit,omitempty"` // [optional] Number of results per request. Maximum 100. (default 100)
+}
+
+// GetAccountBillDetailsResponse response data for GetAccountBillDetails
+type GetAccountBillDetailsResponse struct {
+ Amount float64 `json:"amount"`
+ Balance int64 `json:"balance"`
+ Currency string `json:"currency"`
+ Fee int64 `json:"fee"`
+ LedgerID int64 `json:"ledger_id"`
+ Timestamp time.Time `json:"timestamp"`
+ Typename string `json:"typename"`
+}
+
+// GetDepositAddressResponse response data for GetDepositAddress
+type GetDepositAddressResponse struct {
+ Address string `json:"address"`
+ Tag string `json:"tag"`
+ PaymentID string `json:"payment_id,omitempty"`
+ Currency string `json:"currency"`
+}
+
+// GetAccountDepositHistoryResponse response data for GetAccountDepositHistory
+type GetAccountDepositHistoryResponse struct {
+ Amount float64 `json:"amount"`
+ Currency string `json:"currency"`
+ Status int64 `json:"status"`
+ Timestamp time.Time `json:"timestamp"`
+ To string `json:"to"`
+ TransactionID string `json:"txid"`
+}
+
+// GetSpotTradingAccountResponse response data for GetSpotTradingAccount
+type GetSpotTradingAccountResponse struct {
+ Available string `json:"available"`
+ Balance string `json:"balance"`
+ Currency string `json:"currency"`
+ Frozen string `json:"frozen"`
+ Hold string `json:"hold"`
+ Holds string `json:"holds"`
+ ID string `json:"id"`
+}
+
+// GetSpotBillDetailsForCurrencyRequest request data for GetSpotBillDetailsForCurrency
+type GetSpotBillDetailsForCurrencyRequest struct {
+ Currency string `url:"-"` // [required] token
+ From int64 `url:"from,string,omitempty"` // [optional] request page before(newer) this id.
+ To int64 `url:"to,string,omitempty"` // [optional] request page after(older) this id.
+ Limit int64 `url:"limit,string,omitempty"` // [optional] number of results per request. Maximum 100.(default 100)
+}
+
+// GetSpotBillDetailsForCurrencyResponse response data for GetSpotBillDetailsForCurrency
+type GetSpotBillDetailsForCurrencyResponse struct {
+ LedgerID string `json:"ledger_id"`
+ Balance string `json:"balance"`
+ CurrencyResponse string `json:"currency"`
+ Amount string `json:"amount"`
+ Type string `json:"type"`
+ TimeStamp time.Time `json:"timestamp"`
+ Details SpotBillDetails `json:"details"`
+}
+
+// SpotBillDetails response data for GetSpotBillDetailsForCurrency
+type SpotBillDetails struct {
+ OrderID string `json:"order_id"`
+ InstrumentID string `json:"instrument_id"`
+}
+
+// PlaceSpotOrderRequest request data for PlaceSpotOrder
+type PlaceSpotOrderRequest struct {
+ ClientOID string `json:"client_oid,omitempty"` // the order ID customized by yourself
+ Type string `json:"type"` // limit / market(default: limit)
+ Side string `json:"side"` // buy or sell
+ InstrumentID string `json:"instrument_id"` // trading pair
+ MarginTrading string `json:"margin_trading"` // order type (The request value is 1)
+ Size string `json:"size"`
+ Notional string `json:"notional,omitempty"` //
+ Price string `json:"price,omitempty"` // price (Limit order only)
+}
+
+// PlaceSpotOrderResponse response data for PlaceSpotOrder
+type PlaceSpotOrderResponse struct {
+ ClientOid string `json:"client_oid"`
+ OrderID string `json:"order_id"`
+ Result bool `json:"result"`
+}
+
+// CancelSpotOrderRequest request data for CancelSpotOrder
+type CancelSpotOrderRequest struct {
+ ClientOID string `json:"client_oid,omitempty"` // the order ID created by yourself
+ OrderID int64 `json:"order_id,string"` // order ID
+ InstrumentID string `json:"instrument_id"` // By providing this parameter, the corresponding order of a designated trading pair will be cancelled. If not providing this parameter, it will be back to error code.
+}
+
+// CancelSpotOrderResponse response data for CancelSpotOrder
+type CancelSpotOrderResponse struct {
+ ClientOID string `json:"client_oid"`
+ OrderID int64 `json:"order_id"`
+ Result bool `json:"result"`
+}
+
+// CancelMultipleSpotOrdersRequest request data for CancelMultipleSpotOrders
+type CancelMultipleSpotOrdersRequest struct {
+ OrderIDs []int64 `json:"order_ids,omitempty"` // order ID. You may cancel up to 4 orders of a trading pair
+ InstrumentID string `json:"instrument_id"` // by providing this parameter, the corresponding order of a designated trading pair will be cancelled. If not providing this parameter, it will be back to error code.
+}
+
+// CancelMultipleSpotOrdersResponse response data for CancelMultipleSpotOrders
+type CancelMultipleSpotOrdersResponse struct {
+ ClientOID string `json:"client_oid"`
+ OrderID int64 `json:"order_id,string"`
+ Result bool `json:"result"`
+ Error error // Placeholder to store errors
+}
+
+// GetSpotOrdersRequest request data for GetSpotOrders
+type GetSpotOrdersRequest struct {
+ Status string `url:"status"` // list the status of all orders (all, open, part_filled, canceling, filled, cancelled,ordering,failure)
+ // (Multiple status separated by '|',and '|' need encode to ' %7C')
+ InstrumentID string `url:"instrument_id"` // trading pair ,information of all trading pair will be returned if the field is left blank
+ From int64 `url:"from,string,omitempty"` // [optional] request page after this id (latest information) (eg. 1, 2, 3, 4, 5. There is only a 5 "from 4", while there are 1, 2, 3 "to 4")
+ To int64 `url:"to,string,omitempty"` // [optional] request page after (older) this id.
+ Limit int64 `url:"limit,string,omitempty"` // [optional] number of results per request. Maximum 100. (default 100)
+}
+
+// GetSpotOrderResponse response data for GetSpotOrders
+type GetSpotOrderResponse struct {
+ FilledNotional float64 `json:"filled_notional,string"`
+ FilledSize float64 `json:"filled_size,string"`
+ InstrumentID string `json:"instrument_id"`
+ Notional float64 `json:"notional,string"`
+ OrderID string `json:"order_id"`
+ Price float64 `json:"price,string"`
+ Side string `json:"side"`
+ Size float64 `json:"size,string"`
+ Status string `json:"status"`
+ Timestamp time.Time `json:"timestamp"`
+ Type string `json:"type"`
+}
+
+// GetSpotOpenOrdersRequest request data for GetSpotOpenOrders
+type GetSpotOpenOrdersRequest struct {
+ InstrumentID string `json:"instrument_id"` // [optional] trading pair ,information of all trading pair will be returned if the field is left blank
+ From int64 `json:"from,string,omitempty"` // [optional] request page after this id (latest information) (eg. 1, 2, 3, 4, 5. There is only a 5 "from 4", while there are 1, 2, 3 "to 4")
+ To int64 `json:"to,string,omitempty"` // [optional] request page after (older) this id.
+ Limit int64 `json:"limit,string,omitempty"` // [optional] number of results per request. Maximum 100. (default 100)
+}
+
+// GetSpotOrderRequest request data for GetSpotOrder
+type GetSpotOrderRequest struct {
+ OrderID string `url:"-"` // [required] order ID
+ InstrumentID string `url:"instrument_id"` // [required]trading pair
+}
+
+// GetSpotTransactionDetailsRequest request data for GetSpotTransactionDetails
+type GetSpotTransactionDetailsRequest struct {
+ InstrumentID string `url:"instrument_id"` // [required]list all transaction details of this instrument_id.
+ OrderID int64 `url:"order_id,string"` // [required]list all transaction details of this order_id.
+ From int64 `url:"from,string,omitempty"` // [optional] request page after this id (latest information) (eg. 1, 2, 3, 4, 5. There is only a 5 "from 4", while there are 1, 2, 3 "to 4")
+ To int64 `url:"to,string,omitempty"` // [optional] request page after (older) this id.
+ Limit int64 `url:"limit,string,omitempty"` // [optional] number of results per request. Maximum 100. (default 100)
+}
+
+// GetSpotTransactionDetailsResponse response data for GetSpotTransactionDetails
+type GetSpotTransactionDetailsResponse struct {
+ ExecType string `json:"exec_type"`
+ Fee string `json:"fee"`
+ InstrumentID string `json:"instrument_id"`
+ LedgerID string `json:"ledger_id"`
+ OrderID string `json:"order_id"`
+ Price string `json:"price"`
+ Side string `json:"side"`
+ Size string `json:"size"`
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// GetSpotTokenPairDetailsResponse response data for GetSpotTokenPairDetails
+type GetSpotTokenPairDetailsResponse struct {
+ BaseCurrency string `json:"base_currency"`
+ InstrumentID string `json:"instrument_id"`
+ MinSize string `json:"min_size"`
+ QuoteCurrency string `json:"quote_currency"`
+ SizeIncrement string `json:"size_increment"`
+ TickSize string `json:"tick_size"`
+}
+
+// GetSpotOrderBookRequest request data for GetSpotOrderBook
+type GetSpotOrderBookRequest struct {
+ Size int64 `url:"size,string,omitempty"` // [optional] number of results per request. Maximum 200
+ Depth float64 `url:"depth,string,omitempty"` // [optional] the aggregation of the book. e.g . 0.1,0.001
+ InstrumentID string `url:"-"` // [required] trading pairs
+}
+
+// GetSpotOrderBookResponse response data for GetSpotOrderBook
+type GetSpotOrderBookResponse struct {
+ Timestamp time.Time `json:"timestamp"`
+ Asks [][]string `json:"asks"` // [[0]: "Price", [1]: "Size", [2]: "Num_orders"], ...
+ Bids [][]string `json:"bids"` // [[0]: "Price", [1]: "Size", [2]: "Num_orders"], ...
+}
+
+// GetSpotTokenPairsInformationResponse response data for GetSpotTokenPairsInformation
+type GetSpotTokenPairsInformationResponse struct {
+ BaseVolume24h float64 `json:"base_volume_24h,string"` // 24 trading volume of the base currency
+ BestAsk float64 `json:"best_ask,string"` // best ask price
+ BestBid float64 `json:"best_bid,string"` // best bid price
+ High24h float64 `json:"high_24h,string"` // 24 hour high
+ InstrumentID string `json:"instrument_id"` // trading pair
+ Last float64 `json:"last,string"` // last traded price
+ Low24h float64 `json:"low_24h,string"` // 24 hour low
+ Open24h float64 `json:"open_24h,string"` // 24 hour open
+ QuoteVolume24h float64 `json:"quote_volume_24h,string"` // 24 trading volume of the quote currency
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// GetSpotFilledOrdersInformationRequest request data for GetSpotFilledOrdersInformation
+type GetSpotFilledOrdersInformationRequest struct {
+ InstrumentID string `url:"-"` // [required] trading pairs
+ From int64 `url:"from,string,omitempty"` // [optional] number of results per request. Maximum 100. (default 100)
+ To int64 `url:"to,string,omitempty"` // [optional] request page after (older) this id.
+ Limit int64 `url:"limit,string,omitempty"` // [optional] number of results per request. Maximum 100. (default 100)
+}
+
+// GetSpotFilledOrdersInformationResponse response data for GetSpotFilledOrdersInformation
+type GetSpotFilledOrdersInformationResponse struct {
+ Price string `json:"price"`
+ Side string `json:"side"`
+ Size string `json:"size"`
+ Timestamp time.Time `json:"timestamp"`
+ TradeID string `json:"trade_id"`
+}
+
+// GetSpotMarketDataRequest request data for GetSpotMarketData
+type GetSpotMarketDataRequest struct {
+ Start string `url:"start,omitempty"` // [optional] start time in ISO 8601
+ End string `url:"end,omitempty"` // [optional] end time in ISO 8601
+ Granularity int64 `url:"granularity"` // The granularity field must be one of the following values: {60, 180, 300, 900, 1800, 3600, 7200, 14400, 43200, 86400, 604800}.
+ InstrumentID string `url:"-"` // [required] trading pairs
+}
+
+// GetSpotMarketDataResponse response data for GetSpotMarketData
+// Return Parameters
+// time string Start time
+// open string Open price
+// high string Highest price
+// low string Lowest price
+// close string Close price
+// volume string Trading volume
+type GetSpotMarketDataResponse []interface{}
+
+// GetMarginAccountsResponse response data for GetMarginAccounts
+type GetMarginAccountsResponse struct {
+ InstrumentID string `json:"instrument_id,omitempty"`
+ LiquidationPrice string `json:"liquidation_price"`
+ ProductID string `json:"product_id,omitempty"`
+ RiskRate string `json:"risk_rate"`
+ Currencies map[string]MarginAccountInfo
+}
+
+// MarginAccountInfo contains individual currency information
+type MarginAccountInfo struct {
+ Available float64 `json:"available,string"`
+ Balance float64 `json:"balance,string"`
+ Borrowed float64 `json:"borrowed,string"`
+ Frozen float64 `json:"frozen,string"`
+ Hold float64 `json:"hold,string"`
+ Holds float64 `json:"holds,string"`
+ LendingFee float64 `json:"lending_fee,string"`
+}
+
+// GetMarginAccountSettingsResponse response data for GetMarginAccountSettings
+type GetMarginAccountSettingsResponse struct {
+ InstrumentID string `json:"instrument_id"`
+ ProductID string `json:"product_id"`
+ Currencies map[string]MarginAccountSettingsInfo
+}
+
+// GetMarginBillDetailsRequest request data for GetMarginBillDetails
+type GetMarginBillDetailsRequest struct {
+ InstrumentID string `url:"-"` // [required] trading pair
+ Type int64 `url:"type,omitempty"` // [optional] 1:deposit 2:withdrawal 13:cancel withdrawal 18: into futures account 19: out of futures account 20:into sub account 21:out of sub account 28: claim 29: into ETT account 30: out of ETT account 31: into C2C account 32:out of C2C account 33: into margin account 34: out of margin account 37: into spot account 38: out of spot account
+ From int64 `url:"from,omitempty"` // [optional] you would request pages after this page.
+ To int64 `url:"to,omitempty"` // [optional] you would request pages before this page
+ Limit int64 `url:"limit,omitempty"` // [optional] Number of results per request. Maximum 100. (default 100)
+}
+
+// MarginAccountSettingsInfo contains individual currency data
+type MarginAccountSettingsInfo struct {
+ Available float64 `json:"available,string"`
+ Leverage float64 `json:"leverage,string"`
+ LeverageRatio float64 `json:"leverage_ratio,string"`
+ Rate float64 `json:"rate,string"`
+}
+
+// GetMarginLoanHistoryRequest request data for GetMarginLoanHistory
+type GetMarginLoanHistoryRequest struct {
+ InstrumentID string // [optional] Used when a specific currency response is desired
+ Status int64 `json:"status,string,omitempty"` // [optional] status(0: outstanding 1: repaid)
+ From int64 `json:"from,string,omitempty"` // [optional] request page from(newer) this id.
+ To int64 `json:"to,string,omitempty"` // [optional] request page to(older) this id.
+ Limit int64 `json:"limit,string,omitempty"` // [optional] number of results per request. Maximum 100.(default 100)
+}
+
+// GetMarginLoanHistoryResponse response data for GetMarginLoanHistory
+type GetMarginLoanHistoryResponse struct {
+ Amount float64 `json:"amount,string"`
+ BorrowID int64 `json:"borrow_id"`
+ CreatedAt string `json:"created_at"`
+ Currency string `json:"currency"`
+ ForceRepayTime string `json:"force_repay_time"`
+ InstrumentID string `json:"instrument_id"`
+ Interest float64 `json:"interest,string"`
+ LastInterestTime string `json:"last_interest_time"`
+ PaidInterest float64 `json:"paid_interest,string"`
+ ProductID string `json:"product_id"`
+ Rate float64 `json:"rate,string"`
+ RepayAmount string `json:"repay_amount"`
+ RepayInterest string `json:"repay_interest"`
+ ReturnedAmount float64 `json:"returned_amount,string"`
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// OpenMarginLoanRequest request data for OpenMarginLoan
+type OpenMarginLoanRequest struct {
+ QuoteCurrency string `json:"currency"` // [required] Second currency eg BTC-USDT: USDT is quote
+ InstrumentID string `json:"instrument_id"` // [required] Full pair BTC-USDT
+ Amount float64 `json:"amount,string"` // [required] Amount wanting to borrow
+}
+
+// OpenMarginLoanResponse response data for OpenMarginLoan
+type OpenMarginLoanResponse struct {
+ BorrowID int64 `json:"borrow_id"`
+ Result bool `json:"result"`
+}
+
+// RepayMarginLoanRequest request data for RepayMarginLoan
+type RepayMarginLoanRequest struct {
+ Amount float64 `json:"amount,string"` // [required] amount repaid
+ BorrowID float64 `json:"borrow_id"` // [optional] borrow ID . all borrowed token under this trading pair will be repay if the field is left blank
+ QuoteCurrency string `json:"currency"` // [required] Second currency eg BTC-USDT: USDT is quote
+ InstrumentID string `json:"instrument_id"` // [required] Full pair BTC-USDT
+}
+
+// RepayMarginLoanResponse response data for RepayMarginLoan
+type RepayMarginLoanResponse struct {
+ RepaymentID int64 `json:"repayment_id"`
+ Result bool `json:"result"`
+}
+
+// GetFuturesPositionsResponse response data for GetFuturesPositions
+type GetFuturesPositionsResponse struct {
+ Holding [][]GetFuturePostionsDetails `json:"holding"`
+ Result bool `json:"result"`
+}
+
+// GetFuturesPositionsForCurrencyResponse response data for GetFuturesPositionsForCurrency
+type GetFuturesPositionsForCurrencyResponse struct {
+ Holding []GetFuturePostionsDetails `json:"holding"`
+ Result bool `json:"result"`
+}
+
+// GetFuturePostionsDetails Futures details
+type GetFuturePostionsDetails struct {
+ CreatedAt string `json:"created_at"`
+ InstrumentID string `json:"instrument_id"`
+ Leverage string `json:"leverage"`
+ LiquidationPrice string `json:"liquidation_price"`
+ LongAvailQty string `json:"long_avail_qty"`
+ LongAvgCost string `json:"long_avg_cost"`
+ LongLeverage string `json:"long_leverage"`
+ LongLiquiPrice string `json:"long_liqui_price"`
+ LongMargin string `json:"long_margin"`
+ LongPnlRatio string `json:"long_pnl_ratio"`
+ LongQty string `json:"long_qty"`
+ LongSettlementPrice string `json:"long_settlement_price"`
+ MarginMode string `json:"margin_mode"`
+ RealisedPnl string `json:"realised_pnl"`
+ ShortAvailQty string `json:"short_avail_qty"`
+ ShortAvgCost string `json:"short_avg_cost"`
+ ShortLeverage string `json:"short_leverage"`
+ ShortLiquiPrice string `json:"short_liqui_price"`
+ ShortMargin string `json:"short_margin"`
+ ShortPnlRatio string `json:"short_pnl_ratio"`
+ ShortQty string `json:"short_qty"`
+ ShortSettlementPrice string `json:"short_settlement_price"`
+ UpdatedAt string `json:"updated_at"`
+}
+
+// FuturesAccountForAllCurrenciesResponse response data for FuturesAccountForAllCurrencies
+type FuturesAccountForAllCurrenciesResponse struct {
+ Info struct {
+ Currency map[string]FuturesCurrencyData
+ } `json:"info"`
+}
+
+// FuturesCurrencyData Futures details
+type FuturesCurrencyData struct {
+ Contracts []FuturesContractsData `json:"contracts,omitempty"`
+ Equity string `json:"equity,omitempty"`
+ Margin string `json:"margin,omitempty"`
+ MarginMode string `json:"margin_mode,omitempty"`
+ MarginRatio string `json:"margin_ratio,omitempty"`
+ RealizedPnl string `json:"realized_pnl,omitempty"`
+ TotalAvailBalance string `json:"total_avail_balance,omitempty"`
+ UnrealizedPnl string `json:"unrealized_pnl,omitempty"`
+}
+
+// FuturesContractsData Futures details
+type FuturesContractsData struct {
+ AvailableQty string `json:"available_qty"`
+ FixedBalance string `json:"fixed_balance"`
+ InstrumentID string `json:"instrument_id"`
+ MarginForUnfilled string `json:"margin_for_unfilled"`
+ MarginFrozen string `json:"margin_frozen"`
+ RealizedPnl string `json:"realized_pnl"`
+ UnrealizedPnl string `json:"unrealized_pnl"`
+}
+
+// GetFuturesLeverageResponse response data for GetFuturesLeverage
+type GetFuturesLeverageResponse struct {
+ MarginMode string `json:"margin_mode,omitempty"`
+ Currency string `json:"currency,omitempty"`
+ Leverage int64 `json:"leverage,omitempty"`
+ LeveragePerCoin map[string]GetFuturesLeverageData
+}
+
+// GetFuturesLeverageData Futures details
+type GetFuturesLeverageData struct {
+ LongLeverage int64 `json:"long_leverage"`
+ ShortLeverage int64 `json:"short_leverage"`
+}
+
+// SetFuturesLeverageRequest request data for SetFuturesLeverage
+type SetFuturesLeverageRequest struct {
+ Direction string `json:"direction,omitempty"` // opening side (long or short)
+ InstrumentID string `json:"instrument_id,omitempty"` // Contract ID, e.g. "BTC-USD-180213"
+ Leverage int64 `json:"leverage,omitempty"` // 10x or 20x leverage
+ Currency string `json:"currency,omitempty"`
+}
+
+// SetFuturesLeverageResponse returned data for SetFuturesLeverage
+type SetFuturesLeverageResponse struct {
+ Currency string `json:"currency"`
+ Leverage int64 `json:"leverage"`
+ MarginMode string `json:"margin_mode"`
+ Result string `json:"result"`
+ Direction string `json:"direction"`
+ ShortLongDataPerContract map[string]SetFutureLeverageShortLongData
+}
+
+// SetFutureLeverageShortLongData long and short data from SetFuturesLeverage
+type SetFutureLeverageShortLongData struct {
+ Long int `json:"long"`
+ Short int `json:"short"`
+}
+
+// PlaceFuturesOrderRequest request data for PlaceFuturesOrder
+type PlaceFuturesOrderRequest struct {
+ ClientOid string `json:"client_oid,omitempty"` // [optional] the order ID customized by yourself
+ InstrumentID string `json:"instrument_id"` // [required] Contract ID,e.g. "TC-USD-180213"
+ Type int64 `json:"type,string"` // [required] 1:open long 2:open short 3:close long 4:close short
+ Price float64 `json:"price,string"` // [required] Price of each contract
+ Size int64 `json:"size,string"` // [required] The buying or selling quantity
+ MatchPrice int64 `json:"match_price,string,omitempty"` // [optional] Order at best counter party price? (0:no 1:yes) the parameter is defaulted as 0. If it is set as 1, the price parameter will be ignored
+ Leverage int64 `json:"leverage,string"` // [required] 10x or 20x leverage
+}
+
+// PlaceFuturesOrderResponse response data for PlaceFuturesOrder
+type PlaceFuturesOrderResponse struct {
+ ClientOid string `json:"client_oid"`
+ ErrorCode int `json:"error_code"`
+ ErrorMesssage string `json:"error_messsage"`
+ OrderID string `json:"order_id"`
+ Result bool `json:"result"`
+}
+
+// PlaceFuturesOrderBatchRequest request data for PlaceFuturesOrderBatch
+type PlaceFuturesOrderBatchRequest struct {
+ InstrumentID string `json:"instrument_id"` // [required] Contract ID, e.g."BTC-USD-180213"
+ Leverage int `json:"leverage"` // [required] 10x or 20x leverage
+ OrdersData []PlaceFuturesOrderBatchRequestDetails `json:"orders_data"` // [required] the JSON word string for placing multiple orders, include:{client_oid type price size match_price}
+}
+
+// PlaceFuturesOrderBatchRequestDetails individual order details for PlaceFuturesOrderBatchRequest
+type PlaceFuturesOrderBatchRequestDetails struct {
+ ClientOid string `json:"client_oid"` // [required] To identify your order with the order ID set by you
+ MatchPrice string `json:"match_price"` // undocumented
+ Price string `json:"price"` // undocumented
+ Size string `json:"size"` // undocumented
+ Type string `json:"type"` // undocumented
+}
+
+// PlaceFuturesOrderBatchResponse response data from PlaceFuturesOrderBatch
+type PlaceFuturesOrderBatchResponse struct {
+ OrderInfo []PlaceFuturesOrderBatchResponseData `json:"order_info"`
+ Result bool `json:"result"`
+}
+
+// PlaceFuturesOrderBatchResponseData individual order details from PlaceFuturesOrderBatchResponse
+type PlaceFuturesOrderBatchResponseData struct {
+ ClientOid string `json:"client_oid"`
+ ErrorCode int `json:"error_code"`
+ ErrorMessage string `json:"error_message"`
+ OrderID float64 `json:"order_id"`
+}
+
+// CancelFuturesOrderRequest request data for CancelFuturesOrder
+type CancelFuturesOrderRequest struct {
+ OrderID string `json:"order_id"` // [required] Order ID
+ InstrumentID string `json:"instrument_id"` // [required] Contract ID,e.g. "BTC-USD-180213"
+}
+
+// CancelFuturesOrderResponse response data from CancelFuturesOrder
+type CancelFuturesOrderResponse struct {
+ InstrumentID string `json:"instrument_id"`
+ OrderID string `json:"order_id"`
+ Result bool `json:"result"`
+}
+
+// GetFuturesOrdersListRequest request data for GetFutureOrdersList
+type GetFuturesOrdersListRequest struct {
+ InstrumentID string `url:"-"` // [required] Contract ID, e.g. "BTC-USD-180213"
+ Status int64 `url:"status,string"` // [required] Order Status (-1 canceled; 0: pending, 1: partially filled, 2: fully filled, 6: open (pending partially + fully filled), 7: completed (canceled + fully filled))
+ From int64 `url:"from,string,omitempty"` // [optional] Request paging content for this page number.(Example: 1,2,3,4,5. From 4 we only have 4, to 4 we only have 3)
+ To int64 `url:"to,string,omitempty"` // [optional] Request page after (older) this pagination id. (Example: 1,2,3,4,5. From 4 we only have 4, to 4 we only have 3)
+ Limit int64 `url:"limit,string,omitempty"` // [optional] Number of results per request. Maximum 100. (default 100)
+}
+
+// GetFuturesOrderListResponse response data from GetFuturesOrderList
+type GetFuturesOrderListResponse struct {
+ OrderInfo []GetFuturesOrderDetailsResponseData `json:"order_info"`
+ Result bool `json:"result"`
+}
+
+// GetFuturesOrderDetailsResponseData individual order data from GetFuturesOrderList
+type GetFuturesOrderDetailsResponseData struct {
+ ContractVal float64 `json:"contract_val,string"`
+ Fee float64 `json:"fee,string"`
+ FilledQty float64 `json:"filled_qty,string"`
+ InstrumentID string `json:"instrument_id"`
+ Leverage int64 `json:"leverage,string"` // Leverage value:10\20 default:10
+ OrderID int64 `json:"order_id,string"`
+ Price float64 `json:"price,string"`
+ PriceAvg float64 `json:"price_avg,string"`
+ Size float64 `json:"size,string"`
+ Status int64 `json:"status,string"` // Order Status (-1 canceled; 0: pending, 1: partially filled, 2: fully filled)
+ Timestamp time.Time `json:"timestamp"`
+ Type int64 `json:"type,string"` // Type (1: open long 2: open short 3: close long 4: close short)
+}
+
+// GetFuturesOrderDetailsRequest request data for GetFuturesOrderDetails
+type GetFuturesOrderDetailsRequest struct {
+ OrderID int64 `json:"order_id,string"` // [required] Order ID
+ InstrumentID string `json:"instrument_id"` // [required] Contract ID, e.g. "BTC-USD-180213"
+}
+
+// GetFuturesTransactionDetailsRequest request data for GetFuturesTransactionDetails
+type GetFuturesTransactionDetailsRequest struct {
+ OrderID int64 `json:"order_id,string"` // [required] Order ID
+ InstrumentID string `json:"instrument_id"` // [required] Contract ID, e.g. "BTC-USD-180213"
+ Status int64 `json:"status,string"` // [required] Order Status (-1 canceled; 0: pending, 1: partially filled, 2: fully filled, 6: open (pending partially + fully filled), 7: completed (canceled + fully filled))
+ From int64 `json:"from,string,omitempty"` // [optional] Request paging content for this page number.(Example: 1,2,3,4,5. From 4 we only have 4, to 4 we only have 3)
+ To int64 `json:"to,string,omitempty"` // [optional] Request page after (older) this pagination id. (Example: 1,2,3,4,5. From 4 we only have 4, to 4 we only have 3)
+ Limit int64 `json:"limit,string,omitempty"` // [optional] Number of results per request. Maximum 100. (default 100)
+}
+
+// GetFuturesTransactionDetailsResponse response data for GetFuturesTransactionDetails
+type GetFuturesTransactionDetailsResponse struct {
+ CreatedAt string `json:"created_at"`
+ ExecType string `json:"exec_type"`
+ Fee string `json:"fee"`
+ InstrumentID string `json:"instrument_id"`
+ OrderID string `json:"order_id"`
+ OrderQty string `json:"order_qty"`
+ Price string `json:"price"`
+ Side string `json:"side"`
+ TradeID string `json:"trade_id"`
+}
+
+// GetFuturesContractInformationResponse individual contract details from GetFuturesContractInformation
+type GetFuturesContractInformationResponse struct {
+ ContractVal int64 `json:"contract_val,string"`
+ Delivery string `json:"delivery"`
+ InstrumentID string `json:"instrument_id"`
+ Listing string `json:"listing"`
+ QuoteCurrency string `json:"quote_currency"`
+ TickSize float64 `json:"tick_size,string"`
+ TradeIncrement int64 `json:"trade_increment,string"`
+ UnderlyingIndex string `json:"underlying_index"`
+}
+
+// GetFuturesOrderBookRequest request data for GetFuturesOrderBook
+type GetFuturesOrderBookRequest struct {
+ InstrumentID string `url:"-"` // [required] Contract ID, e.g. "BTC-USD-180213"
+ Size int64 `url:"size,omitempty"` // [optional] The size of the price range (max: 200)
+}
+
+// GetFuturesOrderBookResponse response data for GetFuturesOrderBook
+type GetFuturesOrderBookResponse struct {
+ Asks [][]float64 `json:"asks"` // [[0: Price, 1: Size price, 2: number of force liquidated orders, 3: number of orders on the price]]
+ Bids [][]float64 `json:"bids"` // [[0: Price, 1: Size price, 2: number of force liquidated orders, 3: number of orders on the price]]
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// GetFuturesTokenInfoResponse response data for GetFuturesOrderBook
+type GetFuturesTokenInfoResponse struct {
+ BestAsk float64 `json:"best_ask,string"`
+ BestBid float64 `json:"best_bid,string"`
+ High24h float64 `json:"high_24h,string"`
+ InstrumentID string `json:"instrument_id"`
+ Last float64 `json:"last,string"`
+ Low24h float64 `json:"low_24h,string"`
+ Timestamp time.Time `json:"timestamp"`
+ Volume24h int64 `json:"volume_24h,string"`
+}
+
+// GetFuturesFilledOrderRequest request data for GetFuturesFilledOrder
+type GetFuturesFilledOrderRequest struct {
+ InstrumentID string `url:"-"` // [required] Contract ID, e.g. "BTC-USD-180213"
+ From int64 `url:"from,string,omitempty"` // [optional] Request paging content for this page number.(Example: 1,2,3,4,5. From 4 we only have 4, to 4 we only have 3)
+ To int64 `url:"to,string,omitempty"` // [optional] Request page after (older) this pagination id. (Example: 1,2,3,4,5. From 4 we only have 4, to 4 we only have 3)
+ Limit int64 `url:"limit,string,omitempty"` // [optional] Number of results per request. Maximum 100. (default 100)
+}
+
+// GetFuturesFilledOrdersResponse response data for GetFuturesFilledOrders
+type GetFuturesFilledOrdersResponse struct {
+ Price float64 `json:"price,string"`
+ Qty int64 `json:"qty,string"`
+ Side string `json:"side"`
+ Timestamp time.Time `json:"timestamp"`
+ TradeID string `json:"trade_id"`
+}
+
+// GetFuturesMarketDateRequest retrieves candle data information
+type GetFuturesMarketDateRequest struct {
+ Start string `url:"start,omitempty"` // [optional] start time in ISO 8601
+ End string `url:"end,omitempty"` // [optional] end time in ISO 8601
+ Granularity int64 `url:"granularity,omitempty"` // [optional] The granularity field must be one of the following values: {60, 180, 300, 900, 1800, 3600, 7200, 14400, 43200, 86400, 604800}.
+ InstrumentID string `url:"-"` // [required] trading pairs
+}
+
+// GetFuturesMarketDataResponse contains candle data from a GetSpotMarketDataRequest
+// Return Parameters
+// time string Start time
+// open string Open price
+// high string Highest price
+// low string Lowest price
+// close string Close price
+// volume string Trading volume
+// currencyvolume string The trading volume in a specific token
+type GetFuturesMarketDataResponse []interface{}
+
+// GetFuturesHoldAmountResponse response data for GetFuturesHoldAmount
+type GetFuturesHoldAmountResponse struct {
+ Amount float64 `json:"amount,string"`
+ InstrumentID string `json:"instrument_id"`
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// GetFuturesIndicesResponse response data for GetFuturesIndices
+type GetFuturesIndicesResponse struct {
+ Index float64 `json:"index,string"`
+ InstrumentID string `json:"instrument_id"`
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// GetFuturesExchangeRatesResponse response data for GetFuturesExchangeRate
+type GetFuturesExchangeRatesResponse struct {
+ InstrumentID string `json:"instrument_id"`
+ Rate float64 `json:"rate,string"`
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// GetFuturesEstimatedDeliveryPriceResponse response data for GetFuturesEstimatedDeliveryPrice
+type GetFuturesEstimatedDeliveryPriceResponse struct {
+ InstrumentID string `json:"instrument_id"`
+ SettlementPrice float64 `json:"settlement_price,string"`
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// GetFuturesOpenInterestsResponse response data for GetFuturesOpenInterests
+type GetFuturesOpenInterestsResponse struct {
+ Amount float64 `json:"amount,string"`
+ InstrumentID string `json:"instrument_id"`
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// GetFuturesCurrentPriceLimitResponse response data for GetFuturesCurrentPriceLimit
+type GetFuturesCurrentPriceLimitResponse struct {
+ Highest float64 `json:"highest,string"`
+ InstrumentID string `json:"instrument_id"`
+ Lowest float64 `json:"lowest,string"`
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// GetFuturesCurrentMarkPriceResponse response data for GetFuturesCurrentMarkPrice
+type GetFuturesCurrentMarkPriceResponse struct {
+ MarkPrice float64 `json:"mark_price"`
+ InstrumentID string `json:"instrument_id"`
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// GetFuturesForceLiquidatedOrdersRequest request data for GetFuturesForceLiquidatedOrders
+type GetFuturesForceLiquidatedOrdersRequest struct {
+ InstrumentID string `url:"-"` // [required] Contract ID, e.g. "BTC-USD-180213"
+ From int64 `url:"from,string,omitempty"` // [optional] Request paging content for this page number.(Example: 1,2,3,4,5. From 4 we only have 4, to 4 we only have 3)
+ To int64 `url:"to,string,omitempty"` // [optional] Request page after (older) this pagination id. (Example: 1,2,3,4,5. From 4 we only have 4, to 4 we only have 3)
+ Limit int64 `url:"limit,string,omitempty"` // [optional] Number of results per request. Maximum 100. (default 100)
+ Status string `url:"status,omitempty"` // [optional] Status (0:unfilled orders in the recent 7 days 1:filled orders in the recent 7 days)
+}
+
+// GetFuturesForceLiquidatedOrdersResponse response data for GetFuturesForceLiquidatedOrders
+type GetFuturesForceLiquidatedOrdersResponse struct {
+ Loss float64 `json:"loss"`
+ Size int64 `json:"size"`
+ Price float64 `json:"price"`
+ CreatedAt string `json:"created_at"`
+ InstrumentID string `json:"instrument_id"`
+ Type int64 `json:"type"`
+}
+
+// GetFuturesTagPriceResponse response data for GetFuturesTagPrice
+type GetFuturesTagPriceResponse struct {
+ MarkPrice float64 `json:"mark_price"`
+ InstrumentID string `json:"instrument_id"`
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// GetSwapPostionsResponse response data for GetSwapPostions
+type GetSwapPostionsResponse struct {
+ MarginMode string `json:"margin_mode"`
+ Holding []GetSwapPostionsResponseHolding `json:"holding"`
+}
+
+// GetSwapPostionsResponseHolding response data for GetSwapPostions
+type GetSwapPostionsResponseHolding struct {
+ AvailPosition string `json:"avail_position"`
+ AvgCost string `json:"avg_cost"`
+ InstrumentID string `json:"instrument_id"`
+ Leverage string `json:"leverage"`
+ LiquidationPrice string `json:"liquidation_price"`
+ Margin string `json:"margin"`
+ Position string `json:"position"`
+ RealizedPnl string `json:"realized_pnl"`
+ SettlementPrice string `json:"settlement_price"`
+ Side string `json:"side"`
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// GetSwapAccountOfAllCurrencyResponse response data for GetSwapAccountOfAllCurrency
+type GetSwapAccountOfAllCurrencyResponse struct {
+ Info []GetSwapAccountOfAllCurrencyResponseInfo `json:"info"`
+}
+
+// GetSwapAccountOfAllCurrencyResponseInfo response data for GetSwapAccountOfAllCurrency
+type GetSwapAccountOfAllCurrencyResponseInfo struct {
+ Equity string `json:"equity"`
+ FixedBalance string `json:"fixed_balance"`
+ TotalAvailBalance string `json:"total_avail_balance"`
+ Margin string `json:"margin"`
+ RealizedPnl string `json:"realized_pnl"`
+ UnrealizedPnl string `json:"unrealized_pnl"`
+ MarginRatio string `json:"margin_ratio"`
+ InstrumentID string `json:"instrument_id"`
+ MarginFrozen string `json:"margin_frozen"`
+ Timestamp time.Time `json:"timestamp"`
+ MarginMode string `json:"margin_mode"`
+}
+
+// GetSwapAccountSettingsOfAContractResponse response data for GetSwapAccountSettingsOfAContract
+type GetSwapAccountSettingsOfAContractResponse struct {
+ LongLeverage float64 `json:"long_leverage,string"`
+ MarginMode string `json:"margin_mode"`
+ ShortLeverage float64 `json:"short_leverage,string"`
+ InstrumentID string `json:"instrument_id"`
+}
+
+// SetSwapLeverageLevelOfAContractRequest request data for SetSwapLeverageLevelOfAContract
+type SetSwapLeverageLevelOfAContractRequest struct {
+ InstrumentID string `json:"instrument_id,omitempty"` // [required] Contract ID, e.g. BTC-USD-SWAP
+ Leverage int64 `json:"leverage,string"` // [required] New leverage level from 1-100
+ Side int64 `json:"side,string"` // [required] Side: 1.FIXED-LONG 2.FIXED-SHORT 3.CROSSED
+}
+
+// SetSwapLeverageLevelOfAContractResponse response data for SetSwapLeverageLevelOfAContract
+type SetSwapLeverageLevelOfAContractResponse struct {
+ InstrumentID string `json:"instrument_id"`
+ LongLeverage int64 `json:"long_leverage,string"`
+ MarginMode string `json:"margin_mode"`
+ ShortLeverage int64 `json:"short_leverage,string"`
+}
+
+// GetSwapBillDetailsResponse response data for GetSwapBillDetails
+type GetSwapBillDetailsResponse struct {
+ LedgerID string `json:"ledger_id"`
+ Amount string `json:"amount"`
+ Type string `json:"type"`
+ Fee string `json:"fee"`
+ Timestamp time.Time `json:"timestamp"`
+ InstrumentID string `json:"instrument_id"`
+}
+
+// PlaceSwapOrderRequest request data for PlaceSwapOrder
+type PlaceSwapOrderRequest struct {
+ ClientOID string `json:"client_oid,omitempty"` // [optional] the order ID customized by yourself. 1-32 with digits and letter,The type of client_oid should be comprised of alphabets + numbers or only alphabets within 1 – 32 characters,Both uppercase and lowercase letters are supported
+ Size float64 `json:"size,string"` // [required] The buying or selling quantity
+ Type int64 `json:"type,string"` // [required] 1:open long 2:open short 3:close long 4:close short
+ MatchPrice int64 `json:"match_price,string,omitempty"` // [optional] Order at best counter party price? (0:no 1:yes)
+ Price float64 `json:"price,string"` // [required] Price of each contract
+ InstrumentID string `json:"instrument_id"` // [required] Contract ID, e.g. BTC-USD-SWAP
+}
+
+// PlaceSwapOrderResponse response data for PlaceSwapOrder
+type PlaceSwapOrderResponse struct {
+ OrderID string `json:"order_id"`
+ ClientOID int64 `json:"client_oid,string"`
+ ErrorCode int64 `json:"error_code,string"`
+ ErrorMessage string `json:"error_message"`
+ Result bool `json:"result,string"`
+}
+
+// PlaceMultipleSwapOrdersRequest response data for PlaceMultipleSwapOrders
+type PlaceMultipleSwapOrdersRequest struct {
+ InstrumentID string `json:"instrument_id"` // [required] Contract ID, e.g. BTC-USD-SWAP
+ Leverage int64 `json:"leverage"` // [required] 10x or 20x leverage
+ OrdersData []PlaceMultipleSwapOrderData `json:"orders_data"` // [required] the JSON word string for placing multiple orders, include:{client_oid type price size match_price}
+}
+
+// PlaceMultipleSwapOrderData response data for PlaceMultipleSwapOrders
+type PlaceMultipleSwapOrderData struct {
+ ClientOID string `json:"client_oid"` // [required] To identify your order with the order ID set by you
+ Type string `json:"type"` // Undocumented
+ Price string `json:"price"` // Undocumented
+ Size string `json:"size"` // Undocumented
+ MatchPrice string `json:"match_price"` // Undocumented
+}
+
+// PlaceMultipleSwapOrdersResponse response data for PlaceMultipleSwapOrders
+type PlaceMultipleSwapOrdersResponse struct {
+ Result bool `json:"result,string"`
+ OrderInfo []PlaceMultipleSwapOrdersResponseInfo `json:"order_info"`
+}
+
+// PlaceMultipleSwapOrdersResponseInfo response data for PlaceMultipleSwapOrders
+type PlaceMultipleSwapOrdersResponseInfo struct {
+ ErrorMessage string `json:"error_message"`
+ ErrorCode int64 `json:"error_code"`
+ ClientOID string `json:"client_oid"`
+ OrderID string `json:"order_id"`
+}
+
+// CancelSwapOrderRequest request data for CancelSwapOrder
+type CancelSwapOrderRequest struct {
+ OrderID string `json:"order_id"` // [required] Order ID
+ InstrumentID string `json:"instrument_id"` // [required] Contract ID,e.g. BTC-USD-SWAP
+}
+
+// CancelSwapOrderResponse response data for CancelSwapOrder
+type CancelSwapOrderResponse struct {
+ Result bool `json:"result,string"`
+ OrderID string `json:"order_id"`
+ InstrumentID string `json:"instrument_id"`
+}
+
+// CancelMultipleSwapOrdersRequest request data for CancelMultipleSwapOrders
+type CancelMultipleSwapOrdersRequest struct {
+ InstrumentID string `json:"instrument_id,omitempty"` // [required] The contract of the orders to be cancelled
+ OrderIDs []int64 `json:"order_ids"` // [required] ID's of the orders canceled
+}
+
+// CancelMultipleSwapOrdersResponse response data for CancelMultipleSwapOrders
+type CancelMultipleSwapOrdersResponse struct {
+ Result bool `json:"result,string"`
+ OrderIDS []string `json:"order_ids"`
+ InstrumentID string `json:"instrument_id"`
+}
+
+// GetSwapOrderListRequest request data for GetSwapOrderList
+type GetSwapOrderListRequest struct {
+ InstrumentID string `url:"-"` // [required] Contract ID, e.g. "BTC-USD-180213"
+ Status int64 `url:"status,string"` // [required] Order Status (-1 canceled; 0: pending, 1: partially filled, 2: fully filled, 6: open (pending partially + fully filled), 7: completed (canceled + fully filled))
+ From int64 `url:"from,string,omitempty"` // [optional] Request paging content for this page number.(Example: 1,2,3,4,5. From 4 we only have 4, to 4 we only have 3)
+ To int64 `url:"to,string,omitempty"` // [optional] Request page after (older) this pagination id. (Example: 1,2,3,4,5. From 4 we only have 4, to 4 we only have 3)
+ Limit int64 `url:"limit,string,omitempty"` // [optional] Number of results per request. Maximum 100. (default 100)
+}
+
+// GetSwapOrderListResponse response data for GetSwapOrderList
+type GetSwapOrderListResponse struct {
+ Result bool `json:"result,string"`
+ OrderInfo []GetSwapOrderListResponseData `json:"order_info"`
+}
+
+// GetSwapOrderListResponseData individual order data from GetSwapOrderList
+type GetSwapOrderListResponseData struct {
+ ContractVal float64 `json:"contract_val,string"`
+ Fee float64 `json:"fee,string"`
+ FilledQty float64 `json:"filled_qty,string"`
+ InstrumentID string `json:"instrument_id"`
+ Leverage int64 `json:"leverage,string"` // Leverage value:10\20 default:10
+ OrderID int64 `json:"order_id,string"`
+ Price float64 `json:"price,string"`
+ PriceAvg float64 `json:"price_avg,string"`
+ Size float64 `json:"size,string"`
+ Status int64 `json:"status,string"` // Order Status (-1 canceled; 0: pending, 1: partially filled, 2: fully filled)
+ Timestamp time.Time `json:"timestamp"`
+ Type int64 `json:"type,string"` // Type (1: open long 2: open short 3: close long 4: close short)
+}
+
+// GetSwapOrderDetailsRequest request data for GetSwapOrderList
+type GetSwapOrderDetailsRequest struct {
+ InstrumentID string `json:"instrument_id"` // [required] Contract ID,e.g. BTC-USD-SWAP
+ OrderID string `json:"order_id"` // [required] Order ID
+}
+
+// GetSwapTransactionDetailsRequest request data for GetSwapTransactionDetails
+type GetSwapTransactionDetailsRequest struct {
+ InstrumentID string `json:"instrument_id"` // [required] Contract ID, e.g. BTC-USD-SWAP
+ OrderID string `json:"order_id"` // [required] Order ID
+ From int64 `json:"from,string,omitempty"` // [optional] Request paging content for this page number.(Example: 1,2,3,4,5. From 4 we only have 4, to 4 we only have 3)
+ To int64 `json:"to,string,omitempty"` // [optional] Request page after (older) this pagination id. (Example: 1,2,3,4,5. From 4 we only have 4, to 4 we only have 3)
+ Limit int64 `json:"limit,string,omitempty"` // [optional] number of results per request. Maximum 100. (default 100)
+}
+
+// GetSwapTransactionDetailsResponse response data for GetSwapTransactionDetails
+type GetSwapTransactionDetailsResponse struct {
+ TradeID string `json:"trade_id"`
+ InstrumentID string `json:"instrument_id"`
+ OrderID string `json:"order_id"`
+ Price string `json:"price"`
+ OrderQty string `json:"order_qty"`
+ Fee string `json:"fee"`
+ Timestamp time.Time `json:"timestamp"`
+ ExecType string `json:"exec_type"`
+ Side string `json:"side"`
+}
+
+// GetSwapContractInformationResponse response data for GetSwapContractInformation
+type GetSwapContractInformationResponse struct {
+ InstrumentID string `json:"instrument_id"`
+ UnderlyingIndex string `json:"underlying_index"`
+ QuoteCurrency string `json:"quote_currency"`
+ Coin string `json:"coin"`
+ ContractVal float64 `json:"contract_val,string"`
+ Listing string `json:"listing"`
+ Delivery string `json:"delivery"`
+ SizeIncrement float64 `json:"size_increment,string"`
+ TickSize float64 `json:"tick_size,string"`
+}
+
+// GetSwapOrderBookRequest request data for GetSwapOrderBook
+type GetSwapOrderBookRequest struct {
+ InstrumentID string `url:"-"`
+ Size float64 `url:"size,string,omitempty"`
+}
+
+// GetSwapOrderBookResponse response data for GetSwapOrderBook
+type GetSwapOrderBookResponse struct {
+ Asks [][]interface{} `json:"asks"` // eg [["411.3","16",5,4]] [[0: Price, 1: Size price, 2: number of force liquidated orders, 3: number of orders on the price]]
+ Bids [][]interface{} `json:"bids"` // eg [["411.3","16",5,4]] [[0: Price, 1: Size price, 2: number of force liquidated orders, 3: number of orders on the price]]
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// GetAllSwapTokensInformationResponse response data for GetAllSwapTokensInformation
+type GetAllSwapTokensInformationResponse struct {
+ InstrumentID string `json:"instrument_id"`
+ Last float64 `json:"last,string"`
+ High24H float64 `json:"high_24h,string"`
+ Low24H float64 `json:"low_24h,string"`
+ BestBid float64 `json:"best_bid,string"`
+ BestAsk float64 `json:"best_ask,string"`
+ Volume24H float64 `json:"volume_24h,string"`
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// GetSwapFilledOrdersDataRequest request data for GetSwapFilledOrdersData
+type GetSwapFilledOrdersDataRequest struct {
+ InstrumentID string `url:"-"` // [required] Contract ID, e.g. "BTC-USD-SWAP
+ From int64 `url:"from,string,omitempty"` // [optional] Request paging content for this page number.(Example: 1,2,3,4,5. From 4 we only have 4, to 4 we only have 3)
+ To int64 `url:"to,string,omitempty"` // [optional] Request page after (older) this pagination id. (Example: 1,2,3,4,5. From 4 we only have 4, to 4 we only have 3)
+ Limit int64 `url:"limit,string,omitempty"` // [optional] Number of results per request. Maximum 100. (default 100)
+}
+
+// GetSwapFilledOrdersDataResponse response data for GetSwapFilledOrdersData
+type GetSwapFilledOrdersDataResponse struct {
+ TradeID string `json:"trade_id"`
+ Price float64 `json:"price,string"`
+ Size float64 `json:"size,string"`
+ Side string `json:"side"`
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// GetSwapMarketDataRequest retrieves candle data information
+type GetSwapMarketDataRequest struct {
+ Start string `url:"start,omitempty"` // [optional] start time in ISO 8601
+ End string `url:"end,omitempty"` // [optional] end time in ISO 8601
+ Granularity int64 `url:"granularity,omitempty"` // The granularity field must be one of the following values: {60, 180, 300, 900, 1800, 3600, 7200, 14400, 43200, 86400, 604800}.
+ InstrumentID string `url:"-"` // [required] trading pairs
+}
+
+// GetSwapMarketDataResponse response data for GetSwapMarketData
+// Return Parameters
+// time string Start time
+// open string Open price
+// high string Highest price
+// low string Lowest price
+// close string Close price
+// volume string Trading volume
+// currency_volume string Volume in a specific token
+type GetSwapMarketDataResponse []interface{}
+
+// GetSwapIndecesResponse response data for GetSwapIndeces
+type GetSwapIndecesResponse struct {
+ InstrumentID string `json:"instrument_id"`
+ Index float64 `json:"index,string"`
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// GetSwapExchangeRatesResponse response data for GetSwapExchangeRates
+type GetSwapExchangeRatesResponse struct {
+ InstrumentID string `json:"instrument_id"`
+ Rate float64 `json:"rate,string"`
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// GetSwapOpenInterestResponse response data for GetSwapOpenInterest
+type GetSwapOpenInterestResponse struct {
+ InstrumentID string `json:"instrument_id"`
+ Amount float64 `json:"amount,string"`
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// GetSwapCurrentPriceLimitsResponse response data for GetSwapCurrentPriceLimits
+type GetSwapCurrentPriceLimitsResponse struct {
+ InstrumentID string `json:"instrument_id"`
+ Highest float64 `json:"highest,string"`
+ Lowest float64 `json:"lowest,string"`
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// GetSwapForceLiquidatedOrdersRequest request data for GetSwapForceLiquidatedOrders
+type GetSwapForceLiquidatedOrdersRequest struct {
+ InstrumentID string `url:"-"` // [required] Contract ID, e.g. "BTC-USD-180213"
+ From int64 `url:"from,string,omitempty"` // [optional] Request paging content for this page number.(Example: 1,2,3,4,5. From 4 we only have 4, to 4 we only have 3)
+ To int64 `url:"to,string,omitempty"` // [optional] Request page after (older) this pagination id. (Example: 1,2,3,4,5. From 4 we only have 4, to 4 we only have 3)
+ Limit int64 `url:"limit,string,omitempty"` // [optional] Number of results per request. Maximum 100. (default 100)
+ Status string `url:"status,omitempty"` // [optional] Status (0:unfilled orders in the recent 7 days 1:filled orders in the recent 7 days)
+}
+
+// GetSwapForceLiquidatedOrdersResponse response data for GetSwapForceLiquidatedOrders
+type GetSwapForceLiquidatedOrdersResponse struct {
+ Loss float64 `json:"loss"`
+ Size int64 `json:"size"`
+ Price float64 `json:"price"`
+ CreatedAt string `json:"created_at"`
+ InstrumentID string `json:"instrument_id"`
+ Type int64 `json:"type"`
+}
+
+// GetSwapOnHoldAmountForOpenOrdersResponse response data for GetSwapOnHoldAmountForOpenOrders
+type GetSwapOnHoldAmountForOpenOrdersResponse struct {
+ InstrumentID string `json:"instrument_id"`
+ Amount float64 `json:"amount,string"`
+ Timestamp time.Time `json:"timestamp"`
+}
+
+// GetSwapNextSettlementTimeResponse response data for GetSwapNextSettlementTime
+type GetSwapNextSettlementTimeResponse struct {
+ InstrumentID string `json:"instrument_id"`
+ FundingTime string `json:"funding_time"`
+}
+
+// GetSwapMarkPriceResponse response data for GetSwapMarkPrice
+type GetSwapMarkPriceResponse struct {
+ InstrumentID string `json:"instrument_id"`
+ MarkPrice string `json:"mark_price"`
+ Timstamp string `json:"timstamp"`
+}
+
+// GetSwapFundingRateHistoryRequest request data for GetSwapFundingRateHistory
+type GetSwapFundingRateHistoryRequest struct {
+ InstrumentID string `url:"ins-trument_id"` // [required] Contract ID, e.g. "BTC-USD-SWAP
+ From int64 `url:"from,string,omitempty"` // [optional] Request paging content for this page number.(Example: 1,2,3,4,5. From 4 we only have 4, to 4 we only have 3)
+ To int64 `url:"to,string,omitempty"` // [optional] Request page after (older) this pagination id. (Example: 1,2,3,4,5. From 4 we only have 4, to 4 we only have 3)
+ Limit int64 `url:"limit,string,omitempty"` // [optional] Number of results per request. Maximum 100.
+}
+
+// GetSwapFundingRateHistoryResponse response data for GetSwapFundingRateHistory
+type GetSwapFundingRateHistoryResponse struct {
+ InstrumentID string `json:"instrument_id"`
+ FundingRate float64 `json:"funding_rate,string,omitempty"`
+ RealizedRate float64 `json:"realized_rate,string"`
+ InterestRate float64 `json:"interest_rate,string"`
+ FundingTime string `json:"funding_time"`
+ FundingFee float64 `json:"funding_fee,string,omitempty"`
+}
+
+// GetETTResponse response data for GetETT
+type GetETTResponse struct {
+ Currency string `json:"currency"`
+ Balance float64 `json:"balance"`
+ Holds float64 `json:"holds"`
+ Available float64 `json:"available"`
+}
+
+// GetETTBillsDetailsResponse response data for GetETTBillsDetails
+type GetETTBillsDetailsResponse struct {
+ LedgerID int64 `json:"ledger_id"`
+ Currency string `json:"currency"`
+ Balance float64 `json:"balance"`
+ Amount float64 `json:"amount"`
+ Type string `json:"type"`
+ CreatedAt string `json:"created_at"`
+ Details int64 `json:"details"`
+}
+
+// PlaceETTOrderRequest request data for PlaceETTOrder
+type PlaceETTOrderRequest struct {
+ ClientOID string `json:"client_oid"` // [optional]the order ID customized by yourself
+ Type int64 `json:"type"` // Type of order (0:ETT subscription 1:subscribe with USDT 2:Redeem in USDT 3:Redeem in underlying)
+ QuoteCurrency string `json:"quote_currency"` // Subscription/redemption currency
+ Amount float64 `json:"amount"` // Subscription amount. Required for usdt subscription
+ Size string `json:"size"` // Redemption size. Required for ETT subscription and redemption
+ ETT string `json:"ett"` // ETT name
+}
+
+// PlaceETTOrderResponse response data for PlaceETTOrder
+type PlaceETTOrderResponse struct {
+ ClientOID string `json:"client_oid"`
+ OrderID string `json:"type"`
+ Result bool `json:"quote_currency"`
+}
+
+// GetETTOrderListRequest request data for GetETTOrderList
+type GetETTOrderListRequest struct {
+ ETT string `url:"ett"` // [required] list specific ETT order
+ Type int64 `url:"type"` // [required](1: subscription 2: redemption)
+ Status int64 `url:"status,omitempty"` // [optional] List the orders of the status (0:All 1:Unfilled 2:Filled 3:Canceled)
+ From int64 `url:"from,string,omitempty"` // [optional] Request paging content for this page number.(Example: 1,2,3,4,5. From 4 we only have 4, to 4 we only have 3)
+ To int64 `url:"to,string,omitempty"` // [optional] Request page after (older) this pagination id. (Example: 1,2,3,4,5. From 4 we only have 4, to 4 we only have 3)
+ Limit int64 `url:"limit,string,omitempty"` // [optional] Number of results per request. Maximum 100.
+}
+
+// GetETTOrderListResponse response data for GetETTOrderList
+type GetETTOrderListResponse struct {
+ OrderID string `json:"order_id"`
+ Price string `json:"price"`
+ Size string `json:"size"`
+ Amount string `json:"amount"`
+ QuoteCurrency string `json:"quote_currency"`
+ Ett string `json:"ett"`
+ Type int64 `json:"type"`
+ CreatedAt string `json:"created_at"`
+ Status string `json:"status"`
+}
+
+// GetETTConstituentsResponse response data for GetETTConstituents
+type GetETTConstituentsResponse struct {
+ NetValue float64 `json:"net_value"`
+ Ett string `json:"ett"`
+ Constituents []ConstituentData `json:"constituents"`
+}
+
+// ConstituentData response data for GetETTConstituents
+type ConstituentData struct {
+ Amount float64 `json:"amount"`
+ Currency string `json:"currency"`
+}
+
+// GetETTSettlementPriceHistoryResponse response data for GetETTSettlementPriceHistory
+type GetETTSettlementPriceHistoryResponse struct {
+ Date string `json:"date"`
+ Price float64 `json:"price"`
+}
+
+// OrderStatus Holds OKGroup order status values
+var OrderStatus = map[int64]string{
+ -3: "pending cancel",
+ -2: "cancelled",
+ -1: "failed",
+ 0: "pending",
+ 1: "sending",
+ 2: "sent",
+ 3: "email confirmation",
+ 4: "manual confirmation",
+ 5: "awaiting identity confirmation",
+}
+
+// SpotInstrument contains spot data
+type SpotInstrument struct {
+ BaseCurrency string `json:"base_currency"`
+ BaseIncrement float64 `json:"base_increment,string"`
+ BaseMinSize float64 `json:"base_min_size,string"`
+ InstrumentID string `json:"instrument_id"`
+ MinSize float64 `json:"min_size,string"`
+ ProductID string `json:"product_id"`
+ QuoteCurrency string `json:"quote_currency"`
+ QuoteIncrement float64 `json:"quote_increment,string"`
+ SizeIncrement float64 `json:"size_increment,string"`
+ TickSize float64 `json:"tick_size,string"`
+}
+
+// WebsocketEventRequest contains event data for a websocket channel
+type WebsocketEventRequest struct {
+ Operation string `json:"op"` // 1--subscribe 2--unsubscribe 3--login
+ Arguments []string `json:"args"` // args: the value is the channel name, which can be one or more channels
+}
+
+// WebsocketEventResponse contains event data for a websocket channel
+type WebsocketEventResponse struct {
+ Event string `json:"event"`
+ Channel string `json:"channel"`
+}
+
+// WebsocketDataResponse formats all response data for a websocket event
+type WebsocketDataResponse struct {
+ Table string `json:"table"`
+ Action string `json:"action,omitempty"`
+ Data []WebsocketDataWrapper `json:"data"`
+}
+
+// WebsocketDataWrapper holds all data responses for websocket
+// Can review in future if struct becomes too large
+// allows for easy data processing
+type WebsocketDataWrapper struct {
+ InstrumentID string `json:"instrument_id"`
+ Timestamp time.Time `json:"timestamp,omitempty"`
+ WebsocketTickerData
+ WebsocketCandleResponse
+ WebsocketOrderBooksData
+ WebsocketTradeResponse
+ WebsocketFundingFeeResponse
+ WebsocketMarkPriceResponse
+ WebsocketEstimatedPriceResponse
+ WebsocketPriceRangeResponse
+ WebsocketUserSwapPositionResponse
+ WebsocketUserSwapOrdersResponse
+ WebsocketUserSwapFutureAccountResponse
+ WebsocketUserSpotAccountResponse
+ WebsocketSpotMarginOrderResponse
+ WebsocketUserFutureFixedMarginAccountResponse
+ WebsocketUserFuturePositionResponse
+ WebsocketSpotOrderResponse
+}
+
+// WebsocketTickerData contains formatted data for ticker related websocket responses
+type WebsocketTickerData struct {
+ High24H float64 `json:"high_24h,string,omitempty"`
+ Last float64 `json:"last,string,omitempty"`
+ BestBid float64 `json:"best_bid,string,omitempty"`
+ BestAsk float64 `json:"best_ask,string,omitempty"`
+ Low24H float64 `json:"low_24h,string,omitempty"`
+ Volume24H float64 `json:"volume_24h,string,omitempty"`
+}
+
+// WebsocketTradeResponse contains formatted data for trade related websocket responses
+type WebsocketTradeResponse struct {
+ Price float64 `json:"price,string,omitempty"`
+ Side string `json:"side,omitempty"`
+ Qty float64 `json:"qty,string,omitempty"`
+ TradeID string `json:"trade_id,omitempty"`
+}
+
+// WebsocketCandleResponse contains formatted data for candle related websocket responses
+type WebsocketCandleResponse struct {
+ Candle []string `json:"candle,omitempty"` // [0]timestamp, [1]open, [2]high, [3]low, [4]close, [5]volume, [6]currencyVolume
+}
+
+// WebsocketFundingFeeResponse contains formatted data for funding fee related websocket responses
+type WebsocketFundingFeeResponse struct {
+ FundingRate float64 `json:"funding_rate,string,omitempty"`
+ FundingTime time.Time `json:"funding_time,omitempty"`
+ InterestRate float64 `json:"interest_rate,string,omitempty"`
+}
+
+// WebsocketMarkPriceResponse contains formatted data for mark prices
+type WebsocketMarkPriceResponse struct {
+ MarkPrice float64 `json:"mark_price,string,omitempty"`
+}
+
+// WebsocketEstimatedPriceResponse contains formatted data for estimated prices
+type WebsocketEstimatedPriceResponse struct {
+ SettlementPrice float64 `json:"settlement_price,string,omitempty"`
+}
+
+// WebsocketMarkPriceResponse contains formatted data for mark prices
+type WebsocketPriceRangeResponse struct {
+ Highest float64 `json:"highest,omitempty"`
+ Lowest float64 `json:"lowest,omitempty"`
+}
+
+// WebsocketOrderBooksData contains orderbook data from WebsocketOrderBooksResponse
+type WebsocketOrderBooksData struct {
+ Asks [][]interface{} `json:"asks,omitempty"` // [0] Price, [1] Size, [2] Number of orders
+ Bids [][]interface{} `json:"bids,omitempty"` // [0] Price, [1] Size, [2] Number of orders
+ Checksum int32 `json:"checksum,omitempty"`
+}
+
+// WebsocketUserPositionResponse contains formatted data for user position data
+type WebsocketUserSwapPositionResponse struct {
+ Holding []WebsocketUserSwapPositionHoldingData `json:"holding,omitempty"`
+}
+
+// WebsocketUserPositionResponse contains formatted data for user position holding data
+type WebsocketUserSwapPositionHoldingData struct {
+ AvailablePosition float64 `json:"avail_position,string,omitempty"`
+ AverageCost float64 `json:"avg_cost,string,omitempty"`
+ Leverage float64 `json:"leverage,string,omitempty"`
+ LiquidationPrice float64 `json:"liquidation_price,string,omitempty"`
+ Margin float64 `json:"margin,string,omitempty"`
+ Position float64 `json:"position,string,omitempty"`
+ RealizedPnl float64 `json:"realized_pnl,string,omitempty"`
+ SettlementPrice float64 `json:"settlement_price,string,omitempty"`
+ Side string `json:"side,omitempty"`
+ Timestamp time.Time `json:"timestamp,omitempty"`
+}
+
+// WebsocketUserAccountResponse contains formatted data for user account data
+type WebsocketUserSwapFutureAccountResponse struct {
+ Equity float64 `json:"equity,string,omitempty"`
+ FixedBalance float64 `json:"fixed_balance,string,omitempty"`
+ MarginFrozen float64 `json:"margin_frozen,string,omitempty"`
+ MarginRatio float64 `json:"margin_ratio,string,omitempty"`
+ RealizedPnl float64 `json:"realized_pnl,string,omitempty"`
+ UnrealizedPnl float64 `json:"unrealized_pnl,string,omitempty"`
+ // MarginMode A member, but part already exists as part of WebsocketDataResponse
+ // TotalAvailBalance A member, but part already exists as part of WebsocketDataResponse
+ // Margin A member, but part already exists as part of WebsocketDataResponse
+}
+
+// WebsocketUserSpotAccountResponse contains formatted data for user account data
+type WebsocketUserSpotAccountResponse struct {
+ Balance string `json:"balance"`
+ Available string `json:"available"`
+ Currency string `json:"currency"`
+ ID int64 `json:"id"`
+ Hold string `json:"hold"`
+}
+
+// WebsocketSpotMarginOrderResponse contains formatted data for user account data
+type WebsocketSpotMarginOrderResponse struct {
+ MarginMode string `json:"margin_mode"`
+ TotalAvailBalance string `json:"total_avail_balance"`
+ // UnrealizedPnl A member, but part already exists as part of WebsocketDataResponse
+ // Equity A member, but part already exists as part of WebsocketDataResponse
+ // FixedBalance A member, but part already exists as part of WebsocketDataResponse
+ // InstrumentID A member, but part already exists as part of WebsocketDataResponse
+ // Margin A member, but part already exists as part of WebsocketDataResponse
+ // MarginFrozen A member, but part already exists as part of WebsocketDataResponse
+ // MarginRatio A member, but part already exists as part of WebsocketDataResponse
+ // RealizedPnl A member, but part already exists as part of WebsocketDataResponse
+ // Timestamp A member, but part already exists as part of WebsocketDataResponse
+}
+
+// WebsocketUserFutureFixedMarginAccountResponse contains formatted data for user account data
+type WebsocketUserFutureFixedMarginAccountResponse map[string]WebsocketUserFutureFixedMarginAccountData
+
+type WebsocketUserFutureFixedMarginAccountData struct {
+ Contracts []WebsocketUserSwapFutureAccountResponse `json:"contracts"`
+ Equity string `json:"equity"`
+ MarginMode string `json:"margin_mode"`
+ TotalAvailBalance string `json:"total_avail_balance"`
+}
+
+// WebsocketUserOrdersResponse contains formatted data for user order data
+type WebsocketUserSwapOrdersResponse struct {
+ FilledQuantity float64 `json:"filled_qty,string,omitempty"`
+ ClientOID string `json:"client_oid,omitempty"`
+ Fee float64 `json:"fee,string,omitempty"`
+ ContractValue float64 `json:"contract_val,string,omitempty"`
+ PriceAverage float64 `json:"price_avg,string,omitempty"`
+ OrderID string `json:"order_id,omitempty"`
+ // Size A member, but part already exists as part of WebsocketDataResponse
+ // Status A member, but part already exists as part of WebsocketDataResponse
+ // Leverage A member, but part already exists as part of WebsocketDataResponse
+ // Price A member, but part already exists as part of WebsocketDataResponse
+ // Type A member, but part already exists as part of WebsocketDataResponse
+}
+
+// WebsocketUserFuturePositionResponse contains formatted data for futures positions data
+type WebsocketUserFuturePositionResponse struct {
+ LongQty string `json:"long_qty"`
+ LongAvailQty int64 `json:"long_avail_qty"`
+ LongAvgCost string `json:"long_avg_cost"`
+ LongSettlementPrice string `json:"long_settlement_price"`
+ RealisedPnl string `json:"realised_pnl"`
+ ShortQty string `json:"short_qty"`
+ ShortAvailQty string `json:"short_avail_qty"`
+ ShortAvgCost string `json:"short_avg_cost"`
+ ShortSettlementPrice string `json:"short_settlement_price"`
+ LiquidationPrice string `json:"liquidation_price"`
+ Leverage string `json:"leverage"`
+ CreatedAt string `json:"created_at"`
+ UpdatedAt string `json:"updated_at"`
+ LongMargin string `json:"long_margin"`
+ LongLiquiPrice string `json:"long_liqui_price"`
+ LongPnlRatio string `json:"long_pnl_ratio"`
+ ShortMargin string `json:"short_margin"`
+ ShortLiquiPrice string `json:"short_liqui_price"`
+ ShortPnlRatio string `json:"short_pnl_ratio"`
+ LongLeverage string `json:"long_leverage"`
+ ShortLeverage string `json:"short_leverage"`
+ // UpdatedAt A member, but part already exists as part of WebsocketDataResponse
+ // MarginMode A member, but part already exists as part of WebsocketDataResponse
+ // InstrumentID A member, but part already exists as part of WebsocketDataResponse
+}
+
+// WebsocketSpotOrderResponse contains formatted data for spot user orders
+type WebsocketSpotOrderResponse struct {
+ FilledNotional float64 `json:"filled_notional,string"`
+ FilledSize float64 `json:"filled_size,string"`
+ Notional float64 `json:"notional,string"`
+ Size float64 `json:"size,string"`
+ Status string `json:"status"`
+ MarginTrading int64 `json:"margin_trading"`
+ Type string `json:"type"`
+ // Price A member, but part already exists as part of WebsocketDataResponse
+ // InstrumentID A member, but part already exists as part of WebsocketDataResponse
+ // Timestamp A member, but part already exists as part of WebsocketDataResponse
+ // OrderID A member, but part already exists as part of WebsocketDataResponse
+}
+
+// WebsocketErrorResponse yo
+type WebsocketErrorResponse struct {
+ Event string `json:"event"`
+ Message string `json:"message"`
+ ErrorCode int64 `json:"errorCode"`
+}
diff --git a/exchanges/okgroup/okgroup_websocket.go b/exchanges/okgroup/okgroup_websocket.go
new file mode 100644
index 00000000..5ccb557f
--- /dev/null
+++ b/exchanges/okgroup/okgroup_websocket.go
@@ -0,0 +1,759 @@
+package okgroup
+
+import (
+ "bytes"
+ "compress/flate"
+ "errors"
+ "fmt"
+ "hash/crc32"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/gorilla/websocket"
+ "github.com/thrasher-/gocryptotrader/common"
+ "github.com/thrasher-/gocryptotrader/currency/pair"
+ exchange "github.com/thrasher-/gocryptotrader/exchanges"
+ "github.com/thrasher-/gocryptotrader/exchanges/orderbook"
+ log "github.com/thrasher-/gocryptotrader/logger"
+)
+
+// List of all websocket channels to subscribe to
+const (
+ // If a checksum fails, then resubscribing to the channel fails, fatal after these attempts
+ okGroupWsResubscribeFailureLimit = 3
+ okGroupWsResubscribeDelayInSeconds = 3
+ // Orderbook events
+ okGroupWsOrderbookUpdate = "update"
+ okGroupWsOrderbookPartial = "partial"
+ // API subsections
+ okGroupWsSwapSubsection = "swap/"
+ okGroupWsIndexSubsection = "index/"
+ okGroupWsFuturesSubsection = "futures/"
+ okGroupWsSpotSubsection = "spot/"
+ // Shared API endpoints
+ okGroupWsCandle = "candle"
+ okGroupWsCandle60s = okGroupWsCandle + "60s"
+ okGroupWsCandle180s = okGroupWsCandle + "180s"
+ okGroupWsCandle300s = okGroupWsCandle + "300s"
+ okGroupWsCandle900s = okGroupWsCandle + "900s"
+ okGroupWsCandle1800s = okGroupWsCandle + "1800s"
+ okGroupWsCandle3600s = okGroupWsCandle + "3600s"
+ okGroupWsCandle7200s = okGroupWsCandle + "7200s"
+ okGroupWsCandle14400s = okGroupWsCandle + "14400s"
+ okGroupWsCandle21600s = okGroupWsCandle + "21600"
+ okGroupWsCandle43200s = okGroupWsCandle + "43200s"
+ okGroupWsCandle86400s = okGroupWsCandle + "86400s"
+ okGroupWsCandle604900s = okGroupWsCandle + "604800s"
+ okGroupWsTicker = "ticker"
+ okGroupWsTrade = "trade"
+ okGroupWsDepth = "depth"
+ okGroupWsDepth5 = "depth5"
+ okGroupWsAccount = "account"
+ okGroupWsMarginAccount = "margin_account"
+ okGroupWsOrder = "order"
+ okGroupWsFundingRate = "funding_rate"
+ okGroupWsPriceRange = "price_range"
+ okGroupWsMarkPrice = "mark_price"
+ okGroupWsPosition = "position"
+ okGroupWsEstimatedPrice = "estimated_price"
+ // Spot endpoints
+ okGroupWsSpotTicker = okGroupWsSpotSubsection + okGroupWsTicker
+ okGroupWsSpotCandle60s = okGroupWsSpotSubsection + okGroupWsCandle60s
+ okGroupWsSpotCandle180s = okGroupWsSpotSubsection + okGroupWsCandle180s
+ okGroupWsSpotCandle300s = okGroupWsSpotSubsection + okGroupWsCandle300s
+ okGroupWsSpotCandle900s = okGroupWsSpotSubsection + okGroupWsCandle900s
+ okGroupWsSpotCandle1800s = okGroupWsSpotSubsection + okGroupWsCandle1800s
+ okGroupWsSpotCandle3600s = okGroupWsSpotSubsection + okGroupWsCandle3600s
+ okGroupWsSpotCandle7200s = okGroupWsSpotSubsection + okGroupWsCandle7200s
+ okGroupWsSpotCandle14400s = okGroupWsSpotSubsection + okGroupWsCandle14400s
+ okGroupWsSpotCandle21600s = okGroupWsSpotSubsection + okGroupWsCandle21600s
+ okGroupWsSpotCandle43200s = okGroupWsSpotSubsection + okGroupWsCandle43200s
+ okGroupWsSpotCandle86400s = okGroupWsSpotSubsection + okGroupWsCandle86400s
+ okGroupWsSpotCandle604900s = okGroupWsSpotSubsection + okGroupWsCandle604900s
+ okGroupWsSpotTrade = okGroupWsSpotSubsection + okGroupWsTrade
+ okGroupWsSpotDepth = okGroupWsSpotSubsection + okGroupWsDepth
+ okGroupWsSpotDepth5 = okGroupWsSpotSubsection + okGroupWsDepth5
+ okGroupWsSpotAccount = okGroupWsSpotSubsection + okGroupWsAccount
+ okGroupWsSpotMarginAccount = okGroupWsSpotSubsection + okGroupWsMarginAccount
+ okGroupWsSpotOrder = okGroupWsSpotSubsection + okGroupWsOrder
+ // Swap endpoints
+ okGroupWsSwapTicker = okGroupWsSwapSubsection + okGroupWsTicker
+ okGroupWsSwapCandle60s = okGroupWsSwapSubsection + okGroupWsCandle60s
+ okGroupWsSwapCandle180s = okGroupWsSwapSubsection + okGroupWsCandle180s
+ okGroupWsSwapCandle300s = okGroupWsSwapSubsection + okGroupWsCandle300s
+ okGroupWsSwapCandle900s = okGroupWsSwapSubsection + okGroupWsCandle900s
+ okGroupWsSwapCandle1800s = okGroupWsSwapSubsection + okGroupWsCandle1800s
+ okGroupWsSwapCandle3600s = okGroupWsSwapSubsection + okGroupWsCandle3600s
+ okGroupWsSwapCandle7200s = okGroupWsSwapSubsection + okGroupWsCandle7200s
+ okGroupWsSwapCandle14400s = okGroupWsSwapSubsection + okGroupWsCandle14400s
+ okGroupWsSwapCandle21600s = okGroupWsSwapSubsection + okGroupWsCandle21600s
+ okGroupWsSwapCandle43200s = okGroupWsSwapSubsection + okGroupWsCandle43200s
+ okGroupWsSwapCandle86400s = okGroupWsSwapSubsection + okGroupWsCandle86400s
+ okGroupWsSwapCandle604900s = okGroupWsSwapSubsection + okGroupWsCandle604900s
+ okGroupWsSwapTrade = okGroupWsSwapSubsection + okGroupWsTrade
+ okGroupWsSwapDepth = okGroupWsSwapSubsection + okGroupWsDepth
+ okGroupWsSwapDepth5 = okGroupWsSwapSubsection + okGroupWsDepth5
+ okGroupWsSwapFundingRate = okGroupWsSwapSubsection + okGroupWsFundingRate
+ okGroupWsSwapPriceRange = okGroupWsSwapSubsection + okGroupWsPriceRange
+ okGroupWsSwapMarkPrice = okGroupWsSwapSubsection + okGroupWsMarkPrice
+ okGroupWsSwapPosition = okGroupWsSwapSubsection + okGroupWsPosition
+ okGroupWsSwapAccount = okGroupWsSwapSubsection + okGroupWsAccount
+ okGroupWsSwapOrder = okGroupWsSwapSubsection + okGroupWsOrder
+ // Index endpoints
+ okGroupWsIndexTicker = okGroupWsIndexSubsection + okGroupWsTicker
+ okGroupWsIndexCandle60s = okGroupWsIndexSubsection + okGroupWsCandle60s
+ okGroupWsIndexCandle180s = okGroupWsIndexSubsection + okGroupWsCandle180s
+ okGroupWsIndexCandle300s = okGroupWsIndexSubsection + okGroupWsCandle300s
+ okGroupWsIndexCandle900s = okGroupWsIndexSubsection + okGroupWsCandle900s
+ okGroupWsIndexCandle1800s = okGroupWsIndexSubsection + okGroupWsCandle1800s
+ okGroupWsIndexCandle3600s = okGroupWsIndexSubsection + okGroupWsCandle3600s
+ okGroupWsIndexCandle7200s = okGroupWsIndexSubsection + okGroupWsCandle7200s
+ okGroupWsIndexCandle14400s = okGroupWsIndexSubsection + okGroupWsCandle14400s
+ okGroupWsIndexCandle21600s = okGroupWsIndexSubsection + okGroupWsCandle21600s
+ okGroupWsIndexCandle43200s = okGroupWsIndexSubsection + okGroupWsCandle43200s
+ okGroupWsIndexCandle86400s = okGroupWsIndexSubsection + okGroupWsCandle86400s
+ okGroupWsIndexCandle604900s = okGroupWsIndexSubsection + okGroupWsCandle604900s
+ // Futures endpoints
+ okGroupWsFuturesTicker = okGroupWsFuturesSubsection + okGroupWsTicker
+ okGroupWsFuturesCandle60s = okGroupWsFuturesSubsection + okGroupWsCandle60s
+ okGroupWsFuturesCandle180s = okGroupWsFuturesSubsection + okGroupWsCandle180s
+ okGroupWsFuturesCandle300s = okGroupWsFuturesSubsection + okGroupWsCandle300s
+ okGroupWsFuturesCandle900s = okGroupWsFuturesSubsection + okGroupWsCandle900s
+ okGroupWsFuturesCandle1800s = okGroupWsFuturesSubsection + okGroupWsCandle1800s
+ okGroupWsFuturesCandle3600s = okGroupWsFuturesSubsection + okGroupWsCandle3600s
+ okGroupWsFuturesCandle7200s = okGroupWsFuturesSubsection + okGroupWsCandle7200s
+ okGroupWsFuturesCandle14400s = okGroupWsFuturesSubsection + okGroupWsCandle14400s
+ okGroupWsFuturesCandle21600s = okGroupWsFuturesSubsection + okGroupWsCandle21600s
+ okGroupWsFuturesCandle43200s = okGroupWsFuturesSubsection + okGroupWsCandle43200s
+ okGroupWsFuturesCandle86400s = okGroupWsFuturesSubsection + okGroupWsCandle86400s
+ okGroupWsFuturesCandle604900s = okGroupWsFuturesSubsection + okGroupWsCandle604900s
+ okGroupWsFuturesTrade = okGroupWsFuturesSubsection + okGroupWsTrade
+ okGroupWsFuturesEstimatedPrice = okGroupWsFuturesSubsection + okGroupWsTrade
+ okGroupWsFuturesPriceRange = okGroupWsFuturesSubsection + okGroupWsPriceRange
+ okGroupWsFuturesDepth = okGroupWsFuturesSubsection + okGroupWsDepth
+ okGroupWsFuturesDepth5 = okGroupWsFuturesSubsection + okGroupWsDepth5
+ okGroupWsFuturesMarkPrice = okGroupWsFuturesSubsection + okGroupWsMarkPrice
+ okGroupWsFuturesAccount = okGroupWsFuturesSubsection + okGroupWsAccount
+ okGroupWsFuturesPosition = okGroupWsFuturesSubsection + okGroupWsPosition
+ okGroupWsFuturesOrder = okGroupWsFuturesSubsection + okGroupWsOrder
+)
+
+// orderbookMutex Ensures if two entries arrive at once, only one can be processed at a time
+var orderbookMutex sync.Mutex
+
+// writeToWebsocket sends a message to the websocket endpoint
+func (o *OKGroup) writeToWebsocket(message string) error {
+ o.mu.Lock()
+ defer o.mu.Unlock()
+ if o.Verbose {
+ log.Debugf("Sending message to WS: %v", message)
+ }
+ return o.WebsocketConn.WriteMessage(websocket.TextMessage, []byte(message))
+}
+
+// WsConnect initiates a websocket connection
+func (o *OKGroup) WsConnect() error {
+ if !o.Websocket.IsEnabled() || !o.IsEnabled() {
+ return errors.New(exchange.WebsocketNotEnabled)
+ }
+
+ var dialer websocket.Dialer
+
+ if o.Websocket.GetProxyAddress() != "" {
+ proxy, err := url.Parse(o.Websocket.GetProxyAddress())
+ if err != nil {
+ return err
+ }
+
+ dialer.Proxy = http.ProxyURL(proxy)
+ }
+
+ var err error
+ if o.Verbose {
+ log.Debugf("Attempting to connect to %v", o.Websocket.GetWebsocketURL())
+ }
+ o.WebsocketConn, _, err = dialer.Dial(o.Websocket.GetWebsocketURL(),
+ http.Header{})
+ if err != nil {
+ return fmt.Errorf("%s Unable to connect to Websocket. Error: %s",
+ o.Name,
+ err)
+ }
+ if o.Verbose {
+ log.Debugf("Successful connection to %v", o.Websocket.GetWebsocketURL())
+ }
+
+ go o.WsHandleData()
+ go o.wsPingHandler()
+
+ err = o.WsSubscribeToDefaults()
+ if err != nil {
+ return fmt.Errorf("error: Could not subscribe to the OKEX websocket %s",
+ err)
+ }
+ return nil
+}
+
+// WsSubscribeToDefaults subscribes to the websocket channels
+func (o *OKGroup) WsSubscribeToDefaults() (err error) {
+ channelsToSubscribe := []string{okGroupWsSpotDepth, okGroupWsSpotCandle300s, okGroupWsSpotTicker, okGroupWsSpotTrade}
+ for _, pair := range o.EnabledPairs {
+ formattedPair := strings.ToUpper(strings.Replace(pair, "_", "-", 1))
+ for _, channel := range channelsToSubscribe {
+ err = o.WsSubscribeToChannel(fmt.Sprintf("%v:%s", channel, formattedPair))
+ if err != nil {
+ return
+ }
+ }
+ }
+
+ return nil
+}
+
+// WsReadData reads data from the websocket connection
+func (o *OKGroup) WsReadData() (exchange.WebsocketResponse, error) {
+ mType, resp, err := o.WebsocketConn.ReadMessage()
+ if err != nil {
+ return exchange.WebsocketResponse{}, err
+ }
+
+ o.Websocket.TrafficAlert <- struct{}{}
+ var standardMessage []byte
+ switch mType {
+ case websocket.TextMessage:
+ standardMessage = resp
+
+ case websocket.BinaryMessage:
+ reader := flate.NewReader(bytes.NewReader(resp))
+ standardMessage, err = ioutil.ReadAll(reader)
+ reader.Close()
+ if err != nil {
+ return exchange.WebsocketResponse{}, err
+ }
+ }
+ if o.Verbose {
+ log.Debugf("%v Websocket message received: %v", o.Name, string(standardMessage))
+ }
+
+ return exchange.WebsocketResponse{Raw: standardMessage}, nil
+}
+
+// wsPingHandler sends a message "ping" every 27 to maintain the connection to the websocket
+func (o *OKGroup) wsPingHandler() {
+ o.Websocket.Wg.Add(1)
+ defer o.Websocket.Wg.Done()
+
+ ticker := time.NewTicker(time.Second * 27)
+
+ for {
+ select {
+ case <-o.Websocket.ShutdownC:
+ return
+
+ case <-ticker.C:
+ err := o.writeToWebsocket("ping")
+ if o.Verbose {
+ log.Debugf("%v sending ping", o.GetName())
+ }
+ if err != nil {
+ o.Websocket.DataHandler <- err
+ return
+ }
+ }
+ }
+}
+
+// WsHandleData handles the read data from the websocket connection
+func (o *OKGroup) WsHandleData() {
+ o.Websocket.Wg.Add(1)
+ defer func() {
+ err := o.WebsocketConn.Close()
+ if err != nil {
+ o.Websocket.DataHandler <- fmt.Errorf("okex_websocket.go - Unable to to close Websocket connection. Error: %s",
+ err)
+ }
+ o.Websocket.Wg.Done()
+ }()
+
+ for {
+ select {
+ case <-o.Websocket.ShutdownC:
+ return
+ default:
+ resp, err := o.WsReadData()
+ if err != nil {
+ o.Websocket.DataHandler <- err
+ return
+ }
+ var dataResponse WebsocketDataResponse
+ err = common.JSONDecode(resp.Raw, &dataResponse)
+ if err == nil && dataResponse.Table != "" {
+ if len(dataResponse.Data) > 0 {
+ o.WsHandleDataResponse(&dataResponse)
+ }
+ continue
+ }
+ var errorResponse WebsocketErrorResponse
+ err = common.JSONDecode(resp.Raw, &errorResponse)
+ if err == nil && errorResponse.ErrorCode > 0 {
+ if o.Verbose {
+ log.Debugf("WS Error Event: %v Message: %v", errorResponse.Event, errorResponse.Message)
+ }
+ o.WsHandleErrorResponse(errorResponse)
+ continue
+ }
+ var eventResponse WebsocketEventResponse
+ err = common.JSONDecode(resp.Raw, &eventResponse)
+ if err == nil && len(eventResponse.Channel) > 0 {
+ if o.Verbose {
+ log.Debugf("WS Event: %v on Channel: %v", eventResponse.Event, eventResponse.Channel)
+ }
+ continue
+ }
+ o.Websocket.DataHandler <- fmt.Errorf("unrecognised response: %v", resp.Raw)
+ continue
+ }
+ }
+}
+
+// WsSubscribeToChannel sends a request to WS to subscribe to supplied channel
+func (o *OKGroup) WsSubscribeToChannel(topic string) error {
+ resp := WebsocketEventRequest{
+ Operation: "subscribe",
+ Arguments: []string{topic},
+ }
+ json, err := common.JSONEncode(resp)
+ if err != nil {
+ return err
+ }
+ err = o.writeToWebsocket(string(json))
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// WsUnsubscribeToChannel sends a request to WS to unsubscribe to supplied channel
+func (o *OKGroup) WsUnsubscribeToChannel(topic string) error {
+ resp := WebsocketEventRequest{
+ Operation: "unsubscribe",
+ Arguments: []string{topic},
+ }
+ json, err := common.JSONEncode(resp)
+ if err != nil {
+ return err
+ }
+ err = o.writeToWebsocket(string(json))
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// WsLogin sends a login request to websocket to enable access to authenticated endpoints
+func (o *OKGroup) WsLogin() error {
+ utcTime := time.Now().UTC()
+ unixTime := utcTime.Unix()
+ signPath := "/users/self/verify"
+ hmac := common.GetHMAC(common.HashSHA256, []byte(fmt.Sprintf("%v", unixTime)+http.MethodGet+signPath), []byte(o.APISecret))
+ base64 := common.Base64Encode(hmac)
+ resp := WebsocketEventRequest{
+ Operation: "login",
+ Arguments: []string{o.APIKey, o.ClientID, fmt.Sprintf("%v", unixTime), base64},
+ }
+ json, err := common.JSONEncode(resp)
+ if err != nil {
+ return err
+ }
+ err = o.writeToWebsocket(string(json))
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// WsHandleErrorResponse sends an error message to ws handler
+func (o *OKGroup) WsHandleErrorResponse(event WebsocketErrorResponse) {
+ errorMessage := fmt.Sprintf("%v error - %v message: %s ",
+ o.GetName(), event.ErrorCode, event.Message)
+ if o.Verbose {
+ log.Error(errorMessage)
+ }
+ o.Websocket.DataHandler <- fmt.Errorf(errorMessage)
+}
+
+// GetWsChannelWithoutOrderType takes WebsocketDataResponse.Table and returns
+// The base channel name eg receive "spot/depth5:BTC-USDT" return "depth5"
+func (o *OKGroup) GetWsChannelWithoutOrderType(table string) string {
+ index := strings.Index(table, "/")
+ if index == -1 {
+ return table
+ }
+ channel := table[index+1:]
+ index = strings.Index(channel, ":")
+ // Some events do not contain a currency
+ if index == -1 {
+ return channel
+ }
+
+ return channel[:index]
+}
+
+// GetAssetTypeFromTableName gets the asset type from the table name
+// eg "spot/ticker:BTCUSD" results in "SPOT"
+func (o *OKGroup) GetAssetTypeFromTableName(table string) string {
+ assetIndex := strings.Index(table, "/")
+ return strings.ToUpper(table[:assetIndex])
+}
+
+// WsHandleDataResponse classifies the WS response and sends to appropriate handler
+func (o *OKGroup) WsHandleDataResponse(response *WebsocketDataResponse) {
+ switch o.GetWsChannelWithoutOrderType(response.Table) {
+ case okGroupWsCandle60s, okGroupWsCandle180s, okGroupWsCandle300s, okGroupWsCandle900s,
+ okGroupWsCandle1800s, okGroupWsCandle3600s, okGroupWsCandle7200s, okGroupWsCandle14400s,
+ okGroupWsCandle21600s, okGroupWsCandle43200s, okGroupWsCandle86400s, okGroupWsCandle604900s:
+ if o.Verbose {
+ log.Debugf("%v Websocket candle data received", o.GetName())
+ }
+ o.wsProcessCandles(response)
+ case okGroupWsDepth, okGroupWsDepth5:
+ if o.Verbose {
+ log.Debugf("%v Websocket orderbook data received", o.GetName())
+ }
+ // Locking, orderbooks cannot be processed out of order
+ orderbookMutex.Lock()
+ err := o.WsProcessOrderBook(response)
+ if err != nil {
+ log.Error(err)
+ subscriptionChannel := fmt.Sprintf("%v:%v", response.Table, response.Data[0].InstrumentID)
+ o.ResubscribeToChannel(subscriptionChannel)
+ }
+ orderbookMutex.Unlock()
+ case okGroupWsTicker:
+ if o.Verbose {
+ log.Debugf("%v Websocket ticker data received", o.GetName())
+ }
+ o.wsProcessTickers(response)
+ case okGroupWsTrade:
+ if o.Verbose {
+ log.Debugf("%v Websocket trade data received", o.GetName())
+ }
+ o.wsProcessTrades(response)
+ default:
+ logDataResponse(response)
+ }
+}
+
+// resubscribeToChannel will attempt to unsubscribe and resubscribe to a channel
+func (o *OKGroup) ResubscribeToChannel(channel string) {
+ if okGroupWsResubscribeFailureLimit > 0 {
+ var successfulUnsubscribe bool
+ for i := 0; i < okGroupWsResubscribeFailureLimit; i++ {
+ err := o.WsUnsubscribeToChannel(channel)
+ if err != nil {
+ log.Error(err)
+ time.Sleep(okGroupWsResubscribeDelayInSeconds * time.Second)
+ continue
+ }
+ successfulUnsubscribe = true
+ break
+ }
+ if !successfulUnsubscribe {
+ log.Fatalf("%v websocket channel %v failed to unsubscribe after %v attempts", o.GetName(), channel, okGroupWsResubscribeFailureLimit)
+ }
+ successfulSubscribe := true
+ for i := 0; i < okGroupWsResubscribeFailureLimit; i++ {
+ err := o.WsSubscribeToChannel(channel)
+ if err != nil {
+ log.Error(err)
+ time.Sleep(okGroupWsResubscribeDelayInSeconds * time.Second)
+ continue
+ }
+ successfulSubscribe = true
+ break
+ }
+ if !successfulSubscribe {
+ log.Fatalf("%v websocket channel %v failed to resubscribe after %v attempts", o.GetName(), channel, okGroupWsResubscribeFailureLimit)
+ }
+ } else {
+ log.Fatalf("%v websocket channel %v cannot resubscribe. Limit: %v", o.GetName(), channel, okGroupWsResubscribeFailureLimit)
+ }
+}
+
+// logDataResponse will log the details of any websocket data event
+// where there is no websocket datahandler for it
+func logDataResponse(response *WebsocketDataResponse) {
+ for _, data := range response.Data {
+ log.Errorf("Unhandled channel: '%v'. Instrument '%v' Timestamp '%v', Data '%v",
+ response.Table,
+ data.InstrumentID,
+ data.Timestamp,
+ data)
+ }
+}
+
+// wsProcessTickers converts ticker data and sends it to the datahandler
+func (o *OKGroup) wsProcessTickers(response *WebsocketDataResponse) {
+ for _, tickerData := range response.Data {
+ instrument := pair.NewCurrencyPairDelimiter(tickerData.InstrumentID, "-")
+ o.Websocket.DataHandler <- exchange.TickerData{
+ Timestamp: tickerData.Timestamp,
+ Exchange: o.GetName(),
+ AssetType: o.GetAssetTypeFromTableName(response.Table),
+ HighPrice: tickerData.High24H,
+ LowPrice: tickerData.Low24H,
+ ClosePrice: tickerData.Last,
+ Pair: instrument,
+ }
+ }
+}
+
+// wsProcessTrades converts trade data and sends it to the datahandler
+func (o *OKGroup) wsProcessTrades(response *WebsocketDataResponse) {
+ for _, trade := range response.Data {
+ instrument := pair.NewCurrencyPairDelimiter(trade.InstrumentID, "-")
+ o.Websocket.DataHandler <- exchange.TradeData{
+ Amount: trade.Qty,
+ AssetType: o.GetAssetTypeFromTableName(response.Table),
+ CurrencyPair: instrument,
+ EventTime: time.Now().Unix(),
+ Exchange: o.GetName(),
+ Price: trade.WebsocketTradeResponse.Price,
+ Side: trade.Side,
+ Timestamp: trade.Timestamp,
+ }
+ }
+}
+
+// wsProcessCandles converts candle data and sends it to the data handler
+func (o *OKGroup) wsProcessCandles(response *WebsocketDataResponse) {
+ for _, candle := range response.Data {
+ instrument := pair.NewCurrencyPairDelimiter(candle.InstrumentID, "-")
+ timeData, err := time.Parse(time.RFC3339Nano, candle.WebsocketCandleResponse.Candle[0])
+ if err != nil {
+ log.Warnf("%v Time data could not be parsed: %v", o.GetName(), candle.Candle[0])
+ }
+
+ candleIndex := strings.LastIndex(response.Table, okGroupWsCandle)
+ secondIndex := strings.LastIndex(response.Table, "0s")
+ candleInterval := ""
+ if candleIndex > 0 || secondIndex > 0 {
+ candleInterval = response.Table[candleIndex+len(okGroupWsCandle) : secondIndex]
+ }
+
+ klineData := exchange.KlineData{
+ AssetType: o.GetAssetTypeFromTableName(response.Table),
+ Pair: instrument,
+ Exchange: o.GetName(),
+ Timestamp: timeData,
+ Interval: candleInterval,
+ }
+ klineData.OpenPrice, _ = strconv.ParseFloat(candle.Candle[1], 64)
+ klineData.HighPrice, _ = strconv.ParseFloat(candle.Candle[2], 64)
+ klineData.LowPrice, _ = strconv.ParseFloat(candle.Candle[3], 64)
+ klineData.ClosePrice, _ = strconv.ParseFloat(candle.Candle[4], 64)
+ klineData.Volume, _ = strconv.ParseFloat(candle.Candle[5], 64)
+
+ o.Websocket.DataHandler <- klineData
+ }
+}
+
+// WsProcessOrderBook Validates the checksum and updates internal orderbook values
+func (o *OKGroup) WsProcessOrderBook(response *WebsocketDataResponse) (err error) {
+ for i := range response.Data {
+ instrument := pair.NewCurrencyPairDelimiter(response.Data[i].InstrumentID, "-")
+ if response.Action == okGroupWsOrderbookPartial {
+ err = o.WsProcessPartialOrderBook(&response.Data[i], instrument, response.Table)
+ } else if response.Action == okGroupWsOrderbookUpdate {
+ err = o.WsProcessUpdateOrderbook(&response.Data[i], instrument, response.Table)
+ }
+ }
+ return
+}
+
+// AppendWsOrderbookItems adds websocket orderbook data bid/asks into an orderbook item array
+func (o *OKGroup) AppendWsOrderbookItems(entries [][]interface{}) (orderbookItems []orderbook.Item) {
+ for j := range entries {
+ amount, _ := strconv.ParseFloat(entries[j][1].(string), 64)
+ price, _ := strconv.ParseFloat(entries[j][0].(string), 64)
+ orderbookItems = append(orderbookItems, orderbook.Item{
+ Amount: amount,
+ Price: price,
+ })
+ }
+ return
+}
+
+// WsProcessPartialOrderBook takes websocket orderbook data and creates an orderbook
+// Calculates checksum to ensure it is valid
+func (o *OKGroup) WsProcessPartialOrderBook(wsEventData *WebsocketDataWrapper, instrument pair.CurrencyPair, tableName string) error {
+ signedChecksum := o.CalculatePartialOrderbookChecksum(wsEventData)
+ if signedChecksum != wsEventData.Checksum {
+ return fmt.Errorf("channel: %v. Orderbook partial for %v checksum invalid", tableName, instrument)
+ }
+ if o.Verbose {
+ log.Debug("Passed checksum!")
+ }
+ asks := o.AppendWsOrderbookItems(wsEventData.Asks)
+ bids := o.AppendWsOrderbookItems(wsEventData.Bids)
+ newOrderBook := orderbook.Base{
+ Asks: asks,
+ Bids: bids,
+ AssetType: o.GetAssetTypeFromTableName(tableName),
+ CurrencyPair: wsEventData.InstrumentID,
+ LastUpdated: wsEventData.Timestamp,
+ Pair: instrument,
+ }
+
+ err := o.Websocket.Orderbook.LoadSnapshot(newOrderBook, o.GetName(), true)
+ if err != nil {
+ return err
+ }
+ o.Websocket.DataHandler <- exchange.WebsocketOrderbookUpdate{
+ Exchange: o.GetName(),
+ Asset: o.GetAssetTypeFromTableName(tableName),
+ Pair: instrument,
+ }
+ return nil
+}
+
+// WsProcessUpdateOrderbook updates an existing orderbook using websocket data
+// After merging WS data, it will sort, validate and finally update the existing orderbook
+func (o *OKGroup) WsProcessUpdateOrderbook(wsEventData *WebsocketDataWrapper, instrument pair.CurrencyPair, tableName string) error {
+ internalOrderbook, err := o.GetOrderbookEx(instrument, o.GetAssetTypeFromTableName(tableName))
+ if err != nil {
+ return errors.New("orderbook nil, could not load existing orderbook")
+ }
+ if internalOrderbook.LastUpdated.After(wsEventData.Timestamp) {
+ if o.Verbose {
+ log.Errorf("Orderbook update out of order. Existing: %v, Attempted: %v", internalOrderbook.LastUpdated.Unix(), wsEventData.Timestamp.Unix())
+ }
+ return errors.New("updated orderbook is older than existing")
+ }
+ internalOrderbook.Asks = o.WsUpdateOrderbookEntry(wsEventData.Asks, internalOrderbook.Asks)
+ internalOrderbook.Bids = o.WsUpdateOrderbookEntry(wsEventData.Bids, internalOrderbook.Bids)
+ sort.Slice(internalOrderbook.Asks, func(i, j int) bool {
+ return internalOrderbook.Asks[i].Price < internalOrderbook.Asks[j].Price
+ })
+ sort.Slice(internalOrderbook.Bids, func(i, j int) bool {
+ return internalOrderbook.Bids[i].Price > internalOrderbook.Bids[j].Price
+ })
+ checksum := o.CalculateUpdateOrderbookChecksum(internalOrderbook)
+ if checksum == wsEventData.Checksum {
+ if o.Verbose {
+ log.Debug("Orderbook valid")
+ }
+ internalOrderbook.LastUpdated = wsEventData.Timestamp
+ if o.Verbose {
+ log.Debug("Internalising orderbook")
+ }
+
+ err := o.Websocket.Orderbook.LoadSnapshot(internalOrderbook, o.GetName(), true)
+ if err != nil {
+ log.Error(err)
+ }
+ o.Websocket.DataHandler <- exchange.WebsocketOrderbookUpdate{
+ Exchange: o.GetName(),
+ Asset: o.GetAssetTypeFromTableName(tableName),
+ Pair: instrument,
+ }
+ } else {
+ if o.Verbose {
+ log.Debug("Orderbook invalid")
+ }
+ return fmt.Errorf("channel: %v. Orderbook update for %v checksum invalid. Received %v Calculated %v", tableName, instrument, wsEventData.Checksum, checksum)
+ }
+ return nil
+}
+
+// WsUpdateOrderbookEntry takes WS bid or ask data and merges it with existing orderbook bid or ask data
+func (o *OKGroup) WsUpdateOrderbookEntry(wsEntries [][]interface{}, existingOrderbookEntries []orderbook.Item) []orderbook.Item {
+ for j := range wsEntries {
+ wsEntryPrice, _ := strconv.ParseFloat(wsEntries[j][0].(string), 64)
+ wsEntryAmount, _ := strconv.ParseFloat(wsEntries[j][1].(string), 64)
+ matchFound := false
+ for k := 0; k < len(existingOrderbookEntries); k++ {
+ if existingOrderbookEntries[k].Price != wsEntryPrice {
+ continue
+ }
+ matchFound = true
+ if wsEntryAmount == 0 {
+ existingOrderbookEntries = append(existingOrderbookEntries[:k], existingOrderbookEntries[k+1:]...)
+ k--
+ continue
+ }
+ existingOrderbookEntries[k].Amount = wsEntryAmount
+ continue
+ }
+ if !matchFound {
+ existingOrderbookEntries = append(existingOrderbookEntries, orderbook.Item{
+ Amount: wsEntryAmount,
+ Price: wsEntryPrice,
+ })
+ }
+ }
+ return existingOrderbookEntries
+}
+
+// CalculatePartialOrderbookChecksum alternates over the first 25 bid and ask entries from websocket data
+// The checksum is made up of the price and the quantity with a semicolon (:) deliminating them
+// This will also work when there are less than 25 entries (for whatever reason)
+// eg Bid:Ask:Bid:Ask:Ask:Ask
+func (o *OKGroup) CalculatePartialOrderbookChecksum(orderbookData *WebsocketDataWrapper) int32 {
+ var checksum string
+ iterations := 25
+ for i := 0; i < iterations; i++ {
+ bidsMessage := ""
+ askMessage := ""
+ if len(orderbookData.Bids)-1 >= i {
+ bidsMessage = fmt.Sprintf("%v:%v:", orderbookData.Bids[i][0], orderbookData.Bids[i][1])
+ }
+ if len(orderbookData.Asks)-1 >= i {
+ askMessage = fmt.Sprintf("%v:%v:", orderbookData.Asks[i][0], orderbookData.Asks[i][1])
+
+ }
+ if checksum == "" {
+ checksum = fmt.Sprintf("%v%v", bidsMessage, askMessage)
+ } else {
+ checksum = fmt.Sprintf("%v%v%v", checksum, bidsMessage, askMessage)
+ }
+ }
+ checksum = strings.TrimSuffix(checksum, ":")
+ return int32(crc32.ChecksumIEEE([]byte(checksum)))
+}
+
+// CalculateUpdateOrderbookChecksum alternates over the first 25 bid and ask entries of a merged orderbook
+// The checksum is made up of the price and the quantity with a semicolon (:) deliminating them
+// This will also work when there are less than 25 entries (for whatever reason)
+// eg Bid:Ask:Bid:Ask:Ask:Ask
+func (o *OKGroup) CalculateUpdateOrderbookChecksum(orderbookData orderbook.Base) int32 {
+ var checksum string
+ iterations := 25
+ for i := 0; i < iterations; i++ {
+ bidsMessage := ""
+ askMessage := ""
+ if len(orderbookData.Bids)-1 >= i {
+ price := strconv.FormatFloat(orderbookData.Bids[i].Price, 'f', -1, 64)
+ amount := strconv.FormatFloat(orderbookData.Bids[i].Amount, 'f', -1, 64)
+ bidsMessage = fmt.Sprintf("%v:%v:", price, amount)
+ }
+ if len(orderbookData.Asks)-1 >= i {
+ price := strconv.FormatFloat(orderbookData.Asks[i].Price, 'f', -1, 64)
+ amount := strconv.FormatFloat(orderbookData.Asks[i].Amount, 'f', -1, 64)
+ askMessage = fmt.Sprintf("%v:%v:", price, amount)
+ }
+ if checksum == "" {
+ checksum = fmt.Sprintf("%v%v", bidsMessage, askMessage)
+ } else {
+ checksum = fmt.Sprintf("%v%v%v", checksum, bidsMessage, askMessage)
+ }
+ }
+ checksum = strings.TrimSuffix(checksum, ":")
+ return int32(crc32.ChecksumIEEE([]byte(checksum)))
+}
diff --git a/exchanges/okgroup/okgroup_wrapper.go b/exchanges/okgroup/okgroup_wrapper.go
new file mode 100644
index 00000000..0256247e
--- /dev/null
+++ b/exchanges/okgroup/okgroup_wrapper.go
@@ -0,0 +1,411 @@
+package okgroup
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+ "sync"
+
+ "github.com/thrasher-/gocryptotrader/common"
+ "github.com/thrasher-/gocryptotrader/currency/pair"
+ exchange "github.com/thrasher-/gocryptotrader/exchanges"
+ "github.com/thrasher-/gocryptotrader/exchanges/orderbook"
+ "github.com/thrasher-/gocryptotrader/exchanges/ticker"
+ log "github.com/thrasher-/gocryptotrader/logger"
+)
+
+// Note: GoCryptoTrader wrapper funcs currently only support SPOT trades.
+// Therefore this OKGroup_Wrapper can be shared between OKEX and OKCoin.
+// When circumstances change, wrapper funcs can be split appropriately
+
+// Start starts the OKGroup go routine
+func (o *OKGroup) Start(wg *sync.WaitGroup) {
+ wg.Add(1)
+ go func() {
+ o.Run()
+ wg.Done()
+ }()
+}
+
+// Run implements the OKEX wrapper
+func (o *OKGroup) Run() {
+ if o.Verbose {
+ log.Debugf("%s Websocket: %s. (url: %s).\n", o.GetName(), common.IsEnabled(o.Websocket.IsEnabled()), o.WebsocketURL)
+ log.Debugf("%s polling delay: %ds.\n", o.GetName(), o.RESTPollingDelay)
+ log.Debugf("%s %d currencies enabled: %s.\n", o.GetName(), len(o.EnabledPairs), o.EnabledPairs)
+ }
+
+ prods, err := o.GetSpotTokenPairDetails()
+ if err != nil {
+ log.Errorf("%v failed to obtain available spot instruments. Err: %s", o.Name, err)
+ return
+ }
+
+ var pairs []string
+ for x := range prods {
+ pairs = append(pairs, prods[x].BaseCurrency+"_"+prods[x].QuoteCurrency)
+ }
+
+ err = o.UpdateCurrencies(pairs, false, false)
+ if err != nil {
+ log.Errorf("%v failed to update available currencies. Err: %s", o.Name, err)
+ return
+ }
+}
+
+// UpdateTicker updates and returns the ticker for a currency pair
+func (o *OKGroup) UpdateTicker(p pair.CurrencyPair, assetType string) (tickerData ticker.Price, err error) {
+ resp, err := o.GetSpotAllTokenPairsInformationForCurrency(exchange.FormatExchangeCurrency(o.Name, p).String())
+ if err != nil {
+ return
+ }
+ tickerData = ticker.Price{
+ Ask: resp.BestAsk,
+ Bid: resp.BestBid,
+ CurrencyPair: exchange.FormatExchangeCurrency(o.Name, p).String(),
+ High: resp.High24h,
+ Last: resp.Last,
+ LastUpdated: resp.Timestamp,
+ Low: resp.Low24h,
+ Pair: p,
+ Volume: resp.BaseVolume24h,
+ }
+
+ ticker.ProcessTicker(o.Name, p, tickerData, assetType)
+
+ return
+}
+
+// GetTickerPrice returns the ticker for a currency pair
+func (o *OKGroup) GetTickerPrice(p pair.CurrencyPair, assetType string) (tickerData ticker.Price, err error) {
+ tickerData, err = ticker.GetTicker(o.GetName(), p, assetType)
+ if err != nil {
+ return o.UpdateTicker(p, assetType)
+ }
+ return
+}
+
+// GetOrderbookEx returns orderbook base on the currency pair
+func (o *OKGroup) GetOrderbookEx(p pair.CurrencyPair, assetType string) (resp orderbook.Base, err error) {
+ ob, err := orderbook.GetOrderbook(o.GetName(), p, assetType)
+ if err != nil {
+ return o.UpdateOrderbook(p, assetType)
+ }
+ return ob, nil
+}
+
+// UpdateOrderbook updates and returns the orderbook for a currency pair
+func (o *OKGroup) UpdateOrderbook(p pair.CurrencyPair, assetType string) (resp orderbook.Base, err error) {
+ orderbookNew, err := o.GetSpotOrderBook(GetSpotOrderBookRequest{
+ InstrumentID: exchange.FormatExchangeCurrency(o.Name, p).String(),
+ })
+ if err != nil {
+ return
+ }
+
+ for x := range orderbookNew.Bids {
+ amount, err := strconv.ParseFloat(orderbookNew.Bids[x][1], 64)
+ if err != nil {
+ log.Errorf("Could not convert %v to float64", orderbookNew.Bids[x][1])
+ }
+ price, err := strconv.ParseFloat(orderbookNew.Bids[x][0], 64)
+ if err != nil {
+ log.Errorf("Could not convert %v to float64", orderbookNew.Bids[x][0])
+ }
+ resp.Bids = append(resp.Bids, orderbook.Item{
+ Amount: amount,
+ Price: price,
+ })
+ }
+
+ for x := range orderbookNew.Asks {
+ amount, err := strconv.ParseFloat(orderbookNew.Asks[x][1], 64)
+ if err != nil {
+ log.Errorf("Could not convert %v to float64", orderbookNew.Asks[x][1])
+ }
+ price, err := strconv.ParseFloat(orderbookNew.Asks[x][0], 64)
+ if err != nil {
+ log.Errorf("Could not convert %v to float64", orderbookNew.Asks[x][0])
+ }
+ resp.Asks = append(resp.Asks, orderbook.Item{
+ Amount: amount,
+ Price: price,
+ })
+ }
+
+ orderbook.ProcessOrderbook(o.GetName(), p, resp, assetType)
+ return orderbook.GetOrderbook(o.Name, p, assetType)
+}
+
+// GetAccountInfo retrieves balances for all enabled currencies
+func (o *OKGroup) GetAccountInfo() (resp exchange.AccountInfo, err error) {
+ resp.Exchange = o.Name
+ currencies, err := o.GetSpotTradingAccounts()
+ currencyAccount := exchange.Account{}
+
+ for _, curr := range currencies {
+ hold, err := strconv.ParseFloat(curr.Hold, 64)
+ if err != nil {
+ log.Errorf("Could not convert %v to float64", curr.Hold)
+ }
+ totalValue, err := strconv.ParseFloat(curr.Balance, 64)
+ if err != nil {
+ log.Errorf("Could not convert %v to float64", curr.Balance)
+ }
+ currencyAccount.Currencies = append(currencyAccount.Currencies, exchange.AccountCurrencyInfo{
+ CurrencyName: curr.ID,
+ Hold: hold,
+ TotalValue: totalValue,
+ })
+ }
+
+ resp.Accounts = append(resp.Accounts, currencyAccount)
+ return
+}
+
+// GetFundingHistory returns funding history, deposits and
+// withdrawals
+func (o *OKGroup) GetFundingHistory() (resp []exchange.FundHistory, err error) {
+ accountDepositHistory, err := o.GetAccountDepositHistory("")
+ if err != nil {
+ return
+ }
+ for _, deposit := range accountDepositHistory {
+ orderStatus := ""
+ switch deposit.Status {
+ case 0:
+ orderStatus = "waiting"
+ case 1:
+ orderStatus = "confirmation account"
+ case 2:
+ orderStatus = "recharge success"
+ }
+
+ resp = append(resp, exchange.FundHistory{
+ Amount: deposit.Amount,
+ Currency: deposit.Currency,
+ ExchangeName: o.Name,
+ Status: orderStatus,
+ Timestamp: deposit.Timestamp,
+ TransferID: deposit.TransactionID,
+ TransferType: "deposit",
+ })
+ }
+ accountWithdrawlHistory, err := o.GetAccountWithdrawalHistory("")
+ for _, withdrawal := range accountWithdrawlHistory {
+ resp = append(resp, exchange.FundHistory{
+ Amount: withdrawal.Amount,
+ Currency: withdrawal.Currency,
+ ExchangeName: o.Name,
+ Status: OrderStatus[withdrawal.Status],
+ Timestamp: withdrawal.Timestamp,
+ TransferID: withdrawal.Txid,
+ TransferType: "withdrawal",
+ })
+ }
+ return resp, err
+}
+
+// GetExchangeHistory returns historic trade data since exchange opening.
+func (o *OKGroup) GetExchangeHistory(p pair.CurrencyPair, assetType string) ([]exchange.TradeHistory, error) {
+ return nil, common.ErrNotYetImplemented
+}
+
+// SubmitOrder submits a new order
+func (o *OKGroup) SubmitOrder(p pair.CurrencyPair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (resp exchange.SubmitOrderResponse, err error) {
+ request := PlaceSpotOrderRequest{
+ ClientOID: clientID,
+ InstrumentID: exchange.FormatExchangeCurrency(o.Name, p).String(),
+ Side: strings.ToLower(side.ToString()),
+ Type: strings.ToLower(orderType.ToString()),
+ Size: strconv.FormatFloat(amount, 'f', -1, 64),
+ }
+ if orderType == exchange.LimitOrderType {
+ request.Price = strconv.FormatFloat(price, 'f', -1, 64)
+ }
+
+ orderResponse, err := o.PlaceSpotOrder(request)
+ if err != nil {
+ return
+ }
+ resp.IsOrderPlaced = orderResponse.Result
+ resp.OrderID = orderResponse.OrderID
+
+ return
+}
+
+// ModifyOrder will allow of changing orderbook placement and limit to
+// market conversion
+func (o *OKGroup) ModifyOrder(action exchange.ModifyOrder) (string, error) {
+ return "", common.ErrFunctionNotSupported
+}
+
+// CancelOrder cancels an order by its corresponding ID number
+func (o *OKGroup) CancelOrder(orderCancellation exchange.OrderCancellation) (err error) {
+ orderID, err := strconv.ParseInt(orderCancellation.OrderID, 10, 64)
+ if err != nil {
+ return
+ }
+ orderCancellationResponse, err := o.CancelSpotOrder(CancelSpotOrderRequest{
+ InstrumentID: exchange.FormatExchangeCurrency(o.Name, orderCancellation.CurrencyPair).String(),
+ OrderID: orderID,
+ })
+ if !orderCancellationResponse.Result {
+ err = fmt.Errorf("order %v failed to be cancelled", orderCancellationResponse.OrderID)
+ }
+
+ return
+}
+
+// CancelAllOrders cancels all orders associated with a currency pair
+func (o *OKGroup) CancelAllOrders(orderCancellation exchange.OrderCancellation) (resp exchange.CancelAllOrdersResponse, _ error) {
+ orderIDs := strings.Split(orderCancellation.OrderID, ",")
+ var orderIDNumbers []int64
+ for _, i := range orderIDs {
+ orderIDNumber, err := strconv.ParseInt(i, 10, 64)
+ if err != nil {
+ return resp, err
+ }
+ orderIDNumbers = append(orderIDNumbers, orderIDNumber)
+ }
+
+ cancelOrdersResponse, err := o.CancelMultipleSpotOrders(CancelMultipleSpotOrdersRequest{
+ InstrumentID: exchange.FormatExchangeCurrency(o.Name, orderCancellation.CurrencyPair).String(),
+ OrderIDs: orderIDNumbers,
+ })
+ if err != nil {
+ return
+ }
+
+ for _, orderMap := range cancelOrdersResponse {
+ for _, cancelledOrder := range orderMap {
+ resp.OrderStatus[fmt.Sprintf("%v", cancelledOrder.OrderID)] = fmt.Sprintf("%v", cancelledOrder.Result)
+ }
+ }
+
+ return
+}
+
+// GetOrderInfo returns information on a current open order
+func (o *OKGroup) GetOrderInfo(orderID string) (resp exchange.OrderDetail, err error) {
+ order, err := o.GetSpotOrder(GetSpotOrderRequest{OrderID: orderID})
+ if err != nil {
+ return
+ }
+ resp = exchange.OrderDetail{
+ Amount: order.Size,
+ CurrencyPair: pair.NewCurrencyPairDelimiter(order.InstrumentID, o.ConfigCurrencyPairFormat.Delimiter),
+ Exchange: o.Name,
+ OrderDate: order.Timestamp,
+ ExecutedAmount: order.FilledSize,
+ Status: order.Status,
+ OrderSide: exchange.OrderSide(order.Side),
+ }
+ return
+}
+
+// GetDepositAddress returns a deposit address for a specified currency
+func (o *OKGroup) GetDepositAddress(p pair.CurrencyItem, accountID string) (_ string, err error) {
+ wallet, err := o.GetAccountDepositAddressForCurrency(p.Lower().String())
+ if err != nil {
+ return
+ }
+ return wallet[0].Address, nil
+}
+
+// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is
+// submitted
+func (o *OKGroup) WithdrawCryptocurrencyFunds(withdrawRequest exchange.WithdrawRequest) (string, error) {
+ withdrawal, err := o.AccountWithdraw(AccountWithdrawRequest{
+ Amount: withdrawRequest.Amount,
+ Currency: withdrawRequest.Currency.Lower().String(),
+ Destination: 4, // 1, 2, 3 are all internal
+ Fee: withdrawRequest.FeeAmount,
+ ToAddress: withdrawRequest.Address,
+ TradePwd: withdrawRequest.TradePassword,
+ })
+ if err != nil {
+ return "", err
+ }
+ if !withdrawal.Result {
+ return fmt.Sprintf("%v", withdrawal.WithdrawalID), fmt.Errorf("could not withdraw currency %v to %v, no error specified", withdrawRequest.Currency.String(), withdrawRequest.Address)
+ }
+
+ return fmt.Sprintf("%v", withdrawal.WithdrawalID), nil
+}
+
+// WithdrawFiatFunds returns a withdrawal ID when a
+// withdrawal is submitted
+func (o *OKGroup) WithdrawFiatFunds(withdrawRequest exchange.WithdrawRequest) (string, error) {
+ return "", common.ErrFunctionNotSupported
+}
+
+// WithdrawFiatFundsToInternationalBank returns a withdrawal ID when a
+// withdrawal is submitted
+func (o *OKGroup) WithdrawFiatFundsToInternationalBank(withdrawRequest exchange.WithdrawRequest) (string, error) {
+ return "", common.ErrFunctionNotSupported
+}
+
+// GetActiveOrders retrieves any orders that are active/open
+func (o *OKGroup) GetActiveOrders(getOrdersRequest exchange.GetOrdersRequest) (resp []exchange.OrderDetail, err error) {
+ for _, currency := range getOrdersRequest.Currencies {
+ spotOpenOrders, err := o.GetSpotOpenOrders(GetSpotOpenOrdersRequest{
+ InstrumentID: exchange.FormatExchangeCurrency(o.Name, currency).String(),
+ })
+ if err != nil {
+ return resp, err
+ }
+ for _, openOrder := range spotOpenOrders {
+ resp = append(resp, exchange.OrderDetail{
+ Amount: openOrder.Size,
+ CurrencyPair: currency,
+ Exchange: o.Name,
+ ExecutedAmount: openOrder.FilledSize,
+ OrderDate: openOrder.Timestamp,
+ Status: openOrder.Status,
+ })
+ }
+ }
+
+ return
+}
+
+// GetOrderHistory retrieves account order information
+// Can Limit response to specific order status
+func (o *OKGroup) GetOrderHistory(getOrdersRequest exchange.GetOrdersRequest) (resp []exchange.OrderDetail, err error) {
+ for _, currency := range getOrdersRequest.Currencies {
+ spotOpenOrders, err := o.GetSpotOrders(GetSpotOrdersRequest{
+ InstrumentID: exchange.FormatExchangeCurrency(o.Name, currency).String(),
+ })
+ if err != nil {
+ return resp, err
+ }
+ for _, openOrder := range spotOpenOrders {
+ resp = append(resp, exchange.OrderDetail{
+ Amount: openOrder.Size,
+ CurrencyPair: currency,
+ Exchange: o.Name,
+ ExecutedAmount: openOrder.FilledSize,
+ OrderDate: openOrder.Timestamp,
+ Status: openOrder.Status,
+ })
+ }
+ }
+
+ return
+}
+
+// GetWebsocket returns a pointer to the exchange websocket
+func (o *OKGroup) GetWebsocket() (*exchange.Websocket, error) {
+ return o.Websocket, nil
+}
+
+// GetFeeByType returns an estimate of fee based on type of transaction
+func (o *OKGroup) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) {
+ return o.GetFee(feeBuilder)
+}
+
+// GetWithdrawCapabilities returns the types of withdrawal methods permitted by the exchange
+func (o *OKGroup) GetWithdrawCapabilities() uint32 {
+ return o.GetWithdrawPermissions()
+}
diff --git a/exchanges/orderbook/orderbook.go b/exchanges/orderbook/orderbook.go
index 7d3ebf6f..9af1ea94 100644
--- a/exchanges/orderbook/orderbook.go
+++ b/exchanges/orderbook/orderbook.go
@@ -160,7 +160,9 @@ func ProcessOrderbook(exchangeName string, p pair.CurrencyPair, orderbookNew Bas
orderbookNew.Pair = p
}
orderbookNew.CurrencyPair = p.Pair().String()
- orderbookNew.LastUpdated = time.Now()
+ if orderbookNew.LastUpdated.IsZero() {
+ orderbookNew.LastUpdated = time.Now()
+ }
orderbook, err := GetOrderbookByExchange(exchangeName)
if err != nil {
diff --git a/exchanges/poloniex/poloniex.go b/exchanges/poloniex/poloniex.go
index 9faeb280..bfe83ca3 100644
--- a/exchanges/poloniex/poloniex.go
+++ b/exchanges/poloniex/poloniex.go
@@ -100,7 +100,7 @@ func (p *Poloniex) Setup(exch config.ExchangeConfig) {
p.SetHTTPClientUserAgent(exch.HTTPUserAgent)
p.RESTPollingDelay = exch.RESTPollingDelay
p.Verbose = exch.Verbose
- p.Websocket.SetEnabled(exch.Websocket)
+ p.Websocket.SetWsStatusAndConnection(exch.Websocket)
p.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
p.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
p.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
diff --git a/exchanges/yobit/yobit.go b/exchanges/yobit/yobit.go
index c0fbe5eb..66946cdc 100644
--- a/exchanges/yobit/yobit.go
+++ b/exchanges/yobit/yobit.go
@@ -87,7 +87,7 @@ func (y *Yobit) Setup(exch config.ExchangeConfig) {
y.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
y.RESTPollingDelay = exch.RESTPollingDelay
y.Verbose = exch.Verbose
- y.Websocket.SetEnabled(exch.Websocket)
+ y.Websocket.SetWsStatusAndConnection(exch.Websocket)
y.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
y.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
y.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
diff --git a/exchanges/zb/zb.go b/exchanges/zb/zb.go
index ef6d6e1a..73e173d7 100644
--- a/exchanges/zb/zb.go
+++ b/exchanges/zb/zb.go
@@ -93,7 +93,7 @@ func (z *ZB) Setup(exch config.ExchangeConfig) {
z.SetHTTPClientUserAgent(exch.HTTPUserAgent)
z.RESTPollingDelay = exch.RESTPollingDelay
z.Verbose = exch.Verbose
- z.Websocket.SetEnabled(exch.Websocket)
+ z.Websocket.SetWsStatusAndConnection(exch.Websocket)
z.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
z.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
z.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
diff --git a/go.mod b/go.mod
index c3dad935..2bca8062 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,7 @@
module github.com/thrasher-/gocryptotrader
require (
+ github.com/google/go-querystring v1.0.0
github.com/gorilla/mux v1.7.0
github.com/gorilla/websocket v1.4.0
github.com/toorop/go-pusher v0.0.0-20180521062818-4521e2eb39fb
diff --git a/go.sum b/go.sum
index 7dffd19f..e7a91117 100644
--- a/go.sum
+++ b/go.sum
@@ -1,3 +1,5 @@
+github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U=
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
diff --git a/tools/exchange_template/main_file.tmpl b/tools/exchange_template/main_file.tmpl
index ef3edaa9..a65a298d 100644
--- a/tools/exchange_template/main_file.tmpl
+++ b/tools/exchange_template/main_file.tmpl
@@ -61,7 +61,7 @@ func ({{.Variable}} *{{.CapitalName}}) Setup(exch config.ExchangeConfig) {
{{.Variable}}.SetHTTPClientUserAgent(exch.HTTPUserAgent)
{{.Variable}}.RESTPollingDelay = exch.RESTPollingDelay
{{.Variable}}.Verbose = exch.Verbose
- {{.Variable}}.Websocket.SetEnabled(exch.Websocket)
+ {{.Variable}}.Websocket.SetWsStatusAndConnection(exch.Websocket)
{{.Variable}}.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
{{.Variable}}.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
{{.Variable}}.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")