From 02afa1e98bb24183edde196571951766a31f0dfe Mon Sep 17 00:00:00 2001 From: Adrian Gallagher Date: Mon, 29 Nov 2021 11:34:35 +1100 Subject: [PATCH] exchanges: remove Coinbene exchange support (#849) * exchanges: remove Coinbene exchange support * RM Coinbene from apichecker backup --- README.md | 1 - cmd/apichecker/backup.json | 22 - cmd/apichecker/testupdates.json | 22 - cmd/apichecker/updates.json | 22 - .../exchanges_templates/coinbene.tmpl | 106 -- .../exchanges_trade_readme.tmpl | 1 - .../root_templates/root_readme.tmpl | 1 - .../wrapperconfig.json | 5 - config_example.json | 89 -- docs/ADD_NEW_EXCHANGE.md | 2 - docs/MULTICHAIN_TRANSFER_SUPPORT.md | 1 - docs/OHLCV.md | 1 - engine/exchange_manager.go | 3 - engine/exchange_manager_test.go | 2 +- exchanges/coinbene/README.md | 140 -- exchanges/coinbene/coinbene.go | 1284 ----------------- exchanges/coinbene/coinbene_test.go | 892 ------------ exchanges/coinbene/coinbene_types.go | 439 ------ exchanges/coinbene/coinbene_websocket.go | 517 ------- exchanges/coinbene/coinbene_wrapper.go | 1029 ------------- exchanges/coinbene/ratelimit.go | 259 ---- exchanges/support.go | 1 - exchanges/trade/README.md | 1 - testdata/configtest.json | 89 -- testdata/exchangelist.csv | 1 - 25 files changed, 1 insertion(+), 4929 deletions(-) delete mode 100644 cmd/documentation/exchanges_templates/coinbene.tmpl delete mode 100644 exchanges/coinbene/README.md delete mode 100644 exchanges/coinbene/coinbene.go delete mode 100644 exchanges/coinbene/coinbene_test.go delete mode 100644 exchanges/coinbene/coinbene_types.go delete mode 100644 exchanges/coinbene/coinbene_websocket.go delete mode 100644 exchanges/coinbene/coinbene_wrapper.go delete mode 100644 exchanges/coinbene/ratelimit.go diff --git a/README.md b/README.md index 9f17031693a..3db9a3a8457 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,6 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader | BTCMarkets | Yes | Yes | NA | | BTSE | Yes | Yes | NA | | CoinbasePro | Yes | Yes | No| -| Coinbene | Yes | Yes | No | | COINUT | Yes | Yes | NA | | Exmo | Yes | NA | NA | | FTX | Yes | Yes | No | diff --git a/cmd/apichecker/backup.json b/cmd/apichecker/backup.json index 5a918c104df..d1af165f88c 100644 --- a/cmd/apichecker/backup.json +++ b/cmd/apichecker/backup.json @@ -78,28 +78,6 @@ }, "Disabled": false }, - { - "Name": "CoinbeneSpot", - "CheckType": "GitHub Sha Check", - "Data": { - "GitHubData": { - "Repo": "Coinbene/API-SPOT-v2-Documents", - "Sha": "e9135a782ba6016bcf008778be368882ad7c784d" - } - }, - "Disabled": false - }, - { - "Name": "CoinbeneSwap", - "CheckType": "GitHub Sha Check", - "Data": { - "GitHubData": { - "Repo": "Coinbene/API-SWAP-Documents", - "Sha": "6b7871dae4d2af028a33dde956fbce101e2f9acd" - } - }, - "Disabled": false - }, { "Name": "Coinut", "CheckType": "GitHub Sha Check", diff --git a/cmd/apichecker/testupdates.json b/cmd/apichecker/testupdates.json index 9847acc6b12..ce0282c4c6c 100644 --- a/cmd/apichecker/testupdates.json +++ b/cmd/apichecker/testupdates.json @@ -83,28 +83,6 @@ }, "Disabled": false }, - { - "Name": "CoinbeneSpot", - "CheckType": "GitHub Sha Check", - "Data": { - "GitHubData": { - "Repo": "Coinbene/API-SPOT-v2-Documents", - "Sha": "e9135a782ba6016bcf008778be368882ad7c784d" - } - }, - "Disabled": false - }, - { - "Name": "CoinbeneSwap", - "CheckType": "GitHub Sha Check", - "Data": { - "GitHubData": { - "Repo": "Coinbene/API-SWAP-Documents", - "Sha": "6b7871dae4d2af028a33dde956fbce101e2f9acd" - } - }, - "Disabled": false - }, { "Name": "Coinut", "CheckType": "GitHub Sha Check", diff --git a/cmd/apichecker/updates.json b/cmd/apichecker/updates.json index a14c5249cf3..b5a15cfdc75 100644 --- a/cmd/apichecker/updates.json +++ b/cmd/apichecker/updates.json @@ -83,28 +83,6 @@ }, "Disabled": false }, - { - "Name": "CoinbeneSpot", - "CheckType": "GitHub Sha Check", - "Data": { - "GitHubData": { - "Repo": "Coinbene/API-SPOT-v2-Documents", - "Sha": "e9135a782ba6016bcf008778be368882ad7c784d" - } - }, - "Disabled": false - }, - { - "Name": "CoinbeneSwap", - "CheckType": "GitHub Sha Check", - "Data": { - "GitHubData": { - "Repo": "Coinbene/API-SWAP-Documents", - "Sha": "6b7871dae4d2af028a33dde956fbce101e2f9acd" - } - }, - "Disabled": false - }, { "Name": "Coinut", "CheckType": "GitHub Sha Check", diff --git a/cmd/documentation/exchanges_templates/coinbene.tmpl b/cmd/documentation/exchanges_templates/coinbene.tmpl deleted file mode 100644 index 2033bab686c..00000000000 --- a/cmd/documentation/exchanges_templates/coinbene.tmpl +++ /dev/null @@ -1,106 +0,0 @@ -{{define "exchanges coinbene" -}} -{{template "header" .}} -## Coinbene Exchange - -### Current Features - -+ REST Support -+ Websocket Support - -### How to enable - -+ [Enable via configuration](https://github.com/thrasher-corp/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 c exchange.IBotExchange - -for i := range Bot.Exchanges { - if Bot.Exchanges[i].GetName() == "Coinbene" { - c = Bot.Exchanges[i] - } -} - -// Public calls - wrapper functions - -// Fetches current ticker information -tick, err := c.FetchTicker() -if err != nil { - // Handle error -} - -// Fetches current orderbook information -ob, err := c.FetchOrderbook() -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 := c.GetAccountInfo() -if err != nil { - // Handle error -} -``` - -+ If enabled via individually importing package, rudimentary example below: - -```go -// Public calls - -// Fetches current ticker information -ticker, err := c.GetTicker() -if err != nil { - // Handle error -} - -// Fetches current orderbook information -ob, err := c.GetOrderbook() -if err != nil { - // Handle error -} - -// Private calls - make sure your APIKEY and APISECRET are set and -// AuthenticatedAPISupport is set to true - -// GetUserInfo returns account info -accountInfo, err := c.GetUserInfo(...) -if err != nil { - // Handle error -} - -// Submits an order and the exchange and returns its tradeID -resp, err := c.SubmitOrder(...) -if err != nil { - // Handle error -} -``` - -### How to do Websocket public/private calls - -```go - // Exchanges will be abstracted out in further updates and examples will be - // supplied then -``` - -### Please click GoDocs chevron above to view current GoDoc information for this package -{{template "contributions"}} -{{template "donations" .}} -{{end}} diff --git a/cmd/documentation/exchanges_templates/exchanges_trade_readme.tmpl b/cmd/documentation/exchanges_templates/exchanges_trade_readme.tmpl index ffe5168fcd3..629e9a6a505 100644 --- a/cmd/documentation/exchanges_templates/exchanges_trade_readme.tmpl +++ b/cmd/documentation/exchanges_templates/exchanges_trade_readme.tmpl @@ -51,7 +51,6 @@ _b in this context is an `IBotExchange` implemented struct_ | Bittrex | Yes | Yes | No | | BTCMarkets | Yes | Yes | No | | BTSE | Yes | Yes | No | -| Coinbene | Yes | Yes | No | | CoinbasePro | Yes | Yes | No| | COINUT | Yes | Yes | No | | Exmo | Yes | NA | No | diff --git a/cmd/documentation/root_templates/root_readme.tmpl b/cmd/documentation/root_templates/root_readme.tmpl index 434274ec941..6cce76df29d 100644 --- a/cmd/documentation/root_templates/root_readme.tmpl +++ b/cmd/documentation/root_templates/root_readme.tmpl @@ -30,7 +30,6 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader | BTCMarkets | Yes | Yes | NA | | BTSE | Yes | Yes | NA | | CoinbasePro | Yes | Yes | No| -| Coinbene | Yes | Yes | No | | COINUT | Yes | Yes | NA | | Exmo | Yes | NA | NA | | FTX | Yes | Yes | No | diff --git a/cmd/exchange_wrapper_issues/wrapperconfig.json b/cmd/exchange_wrapper_issues/wrapperconfig.json index 4b0a9ac774d..189d6fbc297 100644 --- a/cmd/exchange_wrapper_issues/wrapperconfig.json +++ b/cmd/exchange_wrapper_issues/wrapperconfig.json @@ -86,11 +86,6 @@ "clientID": "ClientID", "otpSecret": "-" }, - "coinbene": { - "key": "Key", - "secret": "Secret", - "otpSecret": "-" - }, "coinut": { "key": "Key", "clientID": "ClientID", diff --git a/config_example.json b/config_example.json index 68d8a3247cb..93b12ed43e5 100644 --- a/config_example.json +++ b/config_example.json @@ -1208,95 +1208,6 @@ } ] }, - { - "name": "Coinbene", - "enabled": true, - "verbose": false, - "httpTimeout": 15000000000, - "websocketResponseCheckTimeout": 30000000, - "websocketResponseMaxLimit": 7000000000, - "websocketTrafficTimeout": 30000000000, - "websocketOrderbookBufferLimit": 5, - "baseCurrencies": "USD", - "currencyPairs": { - "assetTypes": [ - "spot", - "perpetualswap" - ], - "pairs": { - "perpetualswap": { - "enabled": "BTC/USDT", - "available": "LTC/USDT,EOS/USDT,ETH/USDT,BTC/USDT", - "requestFormat": { - "uppercase": true - }, - "configFormat": { - "uppercase": true, - "delimiter": "/" - } - }, - "spot": { - "enabled": "BTC/USDT", - "available": "ABBC/BTC,ABBC/USDT,ABT/ETH,ABT/USDT,ABYSS/ETH,ACDC/USDT,ADA/USDT,ADK/BTC,ADN/BTC,AE/BTC,AE/USDT,AIDOC/BTC,AIDUS/BTC,AION/BTC,AIPE/USDT,ALI/ETH,ALX/ETH,AMDC/USDT,APL/ETH,ATX/BTC,BAAS/BTC,BAT/BTC,BCH/USDT,BCT/USDT,BETHER/ETH,BEZ/BTC,BGC/USDT,BKG/BTC,BNB/USDT,BNT/BTC,BOA/USDT,BR/USDT,BSTN/ETH,BSV/BTC,BSV/USDT,BTC/USDT,BTCV/BTC,BTNT/BTC,BVT/ETH,CCC/ETH,CCE/USDT,CEDEX/ETH,CFT/USDT,CMT/ETH,CMT/USDT,CNN/BTC,CNN/ETH,CNN/USDT,CONI/USDT,COSM/BTC,COSM/ETH,CPM1/USDT,CPS/BTC,CREDO/ETH,CRN/BTC,CSCC/USDT,CTCN/USDT,CTXC/ETH,CUST/USDT,CVC/BTC,DASH/USDT,DDAM/ETH,DDAM/USDT,DENT/BTC,DGD/BTC,DTA/ETH,DVC/ETH,EBC/BTC,EBC/ETH,EBC/USDT,ECA/BTC,ECP/BTC,EDC/BTC,EDR/ETH,EHT/USDT,ELF/BTC,EOS/BTC,EOS/USDT,EQUAD/BTC,ESH/BTC,ETC/BTC,ETC/USDT,ETH/BTC,ETH/USDT,ETK/BTC,ETN/BTC,EXO/USDT,FAB/ETH,FCR/USDT,FND/ETH,FNKOS/ETH,FTN/BTC,FTN/USDT,FTT/BTC,FXT/ETH,GDC/BTC,GDC/ETH,GDC/USDT,GETX/ETH,GOM2/USDT,GRAM/USDT,GRN/BTC,GUSD/USDT,GVT/BTC,HAPPY/BTC,HDAC/BTC,HMB/USDT,HNB/USDT,HTDF/USDT,HT/USDT,HUP/USDT,INCX/ETH,IOST/BTC,IOTE/USDT,ISR/ETH,IVY/ETH,JOB/BTC,KBC/BTC,KBC/USDT,KMD/BTC,KNT/ETH,KST/BTC,KUKY/BTC,LAMB/USDT,LATX/BTC,LBK/BTC,LINK/BTC,LOOM/BTC,LP/USDT,LTC/BTC,LTC/USDT,LUC/ETH,LUCKY/USDT,LUX/BTC,LVTC/ETH,MC/USDT,MIB/BTC,MINX/BTC,MOAC/USDT,MTC/BTC,MTN/ETH,MT/USDT,MVL/ETH,MXM/ETH,MXM/USDT,MZG/USDT,NANO/BTC,NBAI/ETH,NEO/BTC,NEO/USDT,NOBS/BTC,NPXS/USDT,NTY/ETH,ODC/USDT,OKB/USDT,OMG/BTC,OMX/ETH,OVC/ETH,OZX/ETH,PAT/ETH,PAX/USDT,PLF/USDT,PMA/ETH,POLL/BTC,POLY/BTC,PPT/BTC,PSM/BTC,PWT/USDT,QKC/BTC,QTUM/BTC,QTUM/USDT,RBTC/BTC,RCOIN/BTC,RCOIN/USDT,REP/BTC,REV/BTC,RIF/BTC,SBT/USDT,SCC/BTC,SEN/BTC,SHE/BTC,SHVR/BTC,SIM/BTC,SKB/BTC,SKM/ETH,SKYM/USDT,SLT/ETH,SMARTUP/USDT,SMART/USDT,SORO/USDT,SRCOIN/BTC,SRCOIN/ETH,STORJ/BTC,SWET/BTC,SWTC/USDT,SWYFTT/BTC,TCT/BTC,TEN/BTC,TEN/ETH,THC/USDT,TIB/BTC,TMTG/BTC,TOC/USDT,TOSC/BTC,TRUE/ETH,TRX/BTC,TRX/USDT,TWEE/USDT,UTNP/BTC,VEEN/BTC,VME/BTC,VME/ETH,VSC/ETH,W12/BTC,W12/ETH,WBL/BTC,WBX/USDT,WFX/BTC,XEM/BTC,XLM/BTC,XMR/BTC,XNK/ETH,XRP/BTC,XRP/USDT,XSR/USDT,YAP/BTC,YAP/USDT,YTA/USDT,ZAT/ETH,ZEC/BTC,ZEC/USDT,ZRX/BTC", - "requestFormat": { - "uppercase": true, - "delimiter": "/" - }, - "configFormat": { - "uppercase": true, - "delimiter": "/" - } - } - } - }, - "api": { - "authenticatedSupport": false, - "authenticatedWebsocketApiSupport": false, - "endpoints": { - "url": "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API", - "urlSecondary": "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API", - "websocketURL": "NON_DEFAULT_HTTP_LINK_TO_WEBSOCKET_EXCHANGE_API" - }, - "credentials": { - "key": "Key", - "secret": "Secret", - "clientID": "ClientID" - }, - "credentialsValidator": { - "requiresKey": true, - "requiresSecret": true - } - }, - "features": { - "supports": { - "restAPI": true, - "restCapabilities": { - "autoPairUpdates": true - }, - "websocketAPI": true, - "websocketCapabilities": {} - }, - "enabled": { - "autoPairUpdates": true, - "websocketAPI": false - } - }, - "bankAccounts": [ - { - "enabled": false, - "bankName": "", - "bankAddress": "", - "bankPostalCode": "", - "bankPostalCity": "", - "bankCountry": "", - "accountName": "", - "accountNumber": "", - "swiftCode": "", - "iban": "", - "supportedCurrencies": "" - } - ] - }, { "name": "FTX", "enabled": true, diff --git a/docs/ADD_NEW_EXCHANGE.md b/docs/ADD_NEW_EXCHANGE.md index 297877a6fd3..8bfe2df6b46 100644 --- a/docs/ADD_NEW_EXCHANGE.md +++ b/docs/ADD_NEW_EXCHANGE.md @@ -209,7 +209,6 @@ Yes means supported, No means not yet implemented and NA means protocol unsuppor | Exmo | Yes | NA | NA | | FTX | Yes | Yes | No | // <-------- new exchange | CoinbasePro | Yes | Yes | No| -| Coinbene | Yes | No | No | | GateIO | Yes | Yes | NA | | Gemini | Yes | Yes | No | | HitBTC | Yes | Yes | No | @@ -238,7 +237,6 @@ var Exchanges = []string{ "btc markets", "btse", "coinbasepro", - "coinbene", "coinut", "exmo", "ftx", // <-------- new exchange diff --git a/docs/MULTICHAIN_TRANSFER_SUPPORT.md b/docs/MULTICHAIN_TRANSFER_SUPPORT.md index 47ed4576c5d..066bce44eeb 100644 --- a/docs/MULTICHAIN_TRANSFER_SUPPORT.md +++ b/docs/MULTICHAIN_TRANSFER_SUPPORT.md @@ -53,7 +53,6 @@ $ ./gctcli withdrawcryptofunds --exchange=ftx --currency=USDT --address=TJU9piX2 | BTCMarkets | No | No| NA | | BTSE | No | No | Only through website | | CoinbasePro | No | No | No| -| Coinbene | Yes | Yes | Addresses must be created via their website first | | COINUT | No | No | NA | | Exmo | Yes | Yes | Addresses must be created via their website first | | FTX | Yes | Yes | | diff --git a/docs/OHLCV.md b/docs/OHLCV.md index ed4fa2884b0..d0e811288e1 100644 --- a/docs/OHLCV.md +++ b/docs/OHLCV.md @@ -75,7 +75,6 @@ A helper tool [cmd/dbseed](../cmd/dbseed/README.md) has been created for assisti | Bittrex | | | BTSE | Y | | Coinbase Pro | Y | -| Coinbene | Y | | Coinut | | | Exmo | | | GateIO | Y | diff --git a/engine/exchange_manager.go b/engine/exchange_manager.go index fcac392136e..a9dab9ba935 100644 --- a/engine/exchange_manager.go +++ b/engine/exchange_manager.go @@ -17,7 +17,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/btcmarkets" "github.com/thrasher-corp/gocryptotrader/exchanges/btse" "github.com/thrasher-corp/gocryptotrader/exchanges/coinbasepro" - "github.com/thrasher-corp/gocryptotrader/exchanges/coinbene" "github.com/thrasher-corp/gocryptotrader/exchanges/coinut" "github.com/thrasher-corp/gocryptotrader/exchanges/exmo" "github.com/thrasher-corp/gocryptotrader/exchanges/ftx" @@ -160,8 +159,6 @@ func (m *ExchangeManager) NewExchangeByName(name string) (exchange.IBotExchange, exch = new(btcmarkets.BTCMarkets) case "btse": exch = new(btse.BTSE) - case "coinbene": - exch = new(coinbene.Coinbene) case "coinut": exch = new(coinut.COINUT) case "exmo": diff --git a/engine/exchange_manager_test.go b/engine/exchange_manager_test.go index 76fd27f1eeb..6805691b156 100644 --- a/engine/exchange_manager_test.go +++ b/engine/exchange_manager_test.go @@ -82,7 +82,7 @@ func TestExchangeManagerRemoveExchange(t *testing.T) { func TestNewExchangeByName(t *testing.T) { m := SetupExchangeManager() - exchanges := []string{"binance", "bitfinex", "bitflyer", "bithumb", "bitmex", "bitstamp", "bittrex", "btc markets", "btse", "coinbene", "coinut", "exmo", "coinbasepro", "ftx", "gateio", "gemini", "hitbtc", "huobi", "itbit", "kraken", "lbank", "localbitcoins", "okcoin international", "okex", "poloniex", "yobit", "zb", "fake"} + exchanges := []string{"binance", "bitfinex", "bitflyer", "bithumb", "bitmex", "bitstamp", "bittrex", "btc markets", "btse", "coinut", "exmo", "coinbasepro", "ftx", "gateio", "gemini", "hitbtc", "huobi", "itbit", "kraken", "lbank", "localbitcoins", "okcoin international", "okex", "poloniex", "yobit", "zb", "fake"} for i := range exchanges { exch, err := m.NewExchangeByName(exchanges[i]) if err != nil && exchanges[i] != "fake" { diff --git a/exchanges/coinbene/README.md b/exchanges/coinbene/README.md deleted file mode 100644 index a69e3651e9b..00000000000 --- a/exchanges/coinbene/README.md +++ /dev/null @@ -1,140 +0,0 @@ -# GoCryptoTrader package Coinbene - - - - -[![Build Status](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml) -[![Software License](https://img.shields.io/badge/License-MIT-orange.svg?style=flat-square)](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE) -[![GoDoc](https://godoc.org/github.com/thrasher-corp/gocryptotrader?status.svg)](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/coinbene) -[![Coverage Status](http://codecov.io/github/thrasher-corp/gocryptotrader/coverage.svg?branch=master)](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master) -[![Go Report Card](https://goreportcard.com/badge/github.com/thrasher-corp/gocryptotrader)](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader) - - -This coinbene package is part of the GoCryptoTrader codebase. - -## This is still in active development - -You can track ideas, planned features and what's in progress 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://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTc5ZDE1ZTNiOGM3ZGMyMmY1NTAxYWZhODE0MWM5N2JlZDk1NDU0YTViYzk4NTk3OTRiMDQzNGQ1YTc4YmRlMTk) - -## Coinbene Exchange - -### Current Features - -+ REST Support -+ Websocket Support - -### How to enable - -+ [Enable via configuration](https://github.com/thrasher-corp/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 c exchange.IBotExchange - -for i := range Bot.Exchanges { - if Bot.Exchanges[i].GetName() == "Coinbene" { - c = Bot.Exchanges[i] - } -} - -// Public calls - wrapper functions - -// Fetches current ticker information -tick, err := c.FetchTicker() -if err != nil { - // Handle error -} - -// Fetches current orderbook information -ob, err := c.FetchOrderbook() -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 := c.GetAccountInfo() -if err != nil { - // Handle error -} -``` - -+ If enabled via individually importing package, rudimentary example below: - -```go -// Public calls - -// Fetches current ticker information -ticker, err := c.GetTicker() -if err != nil { - // Handle error -} - -// Fetches current orderbook information -ob, err := c.GetOrderbook() -if err != nil { - // Handle error -} - -// Private calls - make sure your APIKEY and APISECRET are set and -// AuthenticatedAPISupport is set to true - -// GetUserInfo returns account info -accountInfo, err := c.GetUserInfo(...) -if err != nil { - // Handle error -} - -// Submits an order and the exchange and returns its tradeID -resp, err := c.SubmitOrder(...) -if err != nil { - // Handle error -} -``` - -### How to do Websocket public/private calls - -```go - // Exchanges will be abstracted out in further updates and examples will be - // supplied then -``` - -### 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-corp/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: - -***bc1qk0jareu4jytc0cfrhr5wgshsq8282awpavfahc*** diff --git a/exchanges/coinbene/coinbene.go b/exchanges/coinbene/coinbene.go deleted file mode 100644 index 8ccd7b73b7c..00000000000 --- a/exchanges/coinbene/coinbene.go +++ /dev/null @@ -1,1284 +0,0 @@ -package coinbene - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "net/url" - "strconv" - "strings" - "time" - - "github.com/thrasher-corp/gocryptotrader/common" - "github.com/thrasher-corp/gocryptotrader/common/crypto" - "github.com/thrasher-corp/gocryptotrader/currency" - exchange "github.com/thrasher-corp/gocryptotrader/exchanges" - "github.com/thrasher-corp/gocryptotrader/exchanges/order" - "github.com/thrasher-corp/gocryptotrader/exchanges/request" -) - -// Coinbene is the overarching type across this package -type Coinbene struct { - exchange.Base -} - -const ( - coinbeneAPIURL = "https://openapi-exchange.coinbene.com" - coinbeneSwapAPIURL = "https://openapi-contract.coinbene.com" - coinbeneSpotPath = "/api/exchange/v2" - coinbeneSwapPath = "/api/usdt/v2" - coinbeneCapitalPath = "/api/capital/v1" - - // Public endpoints - coinbeneGetTicker = "/market/ticker/one" - coinbeneGetTickersSpot = "/market/ticker/list" - coinbeneGetTickers = "/market/tickers" - coinbeneGetOrderBook = "/market/orderBook" - coinbeneGetKlines = "/market/klines" - coinbeneGetInstruments = "/market/instruments" - // TODO: Implement function --- - coinbeneSpotKlines = "/market/instruments/candles" - coinbeneSpotExchangeRate = "/market/rate/list" - // --- - coinbeneGetTrades = "/market/trades" - coinbeneGetAllPairs = "/market/tradePair/list" - coinbenePairInfo = "/market/tradePair/one" - - // Authenticated endpoints - coinbeneAccountInfo = "/account/info" - coinbeneGetUserBalance = "/account/list" - coinbeneAccountBalanceOne = "/account/one" - coinbenePlaceOrder = "/order/place" - coinbeneBatchPlaceOrder = "/order/batchPlaceOrder" - coinbeneTradeFills = "/order/trade/fills" - coinbeneOrderFills = "/order/fills" - coinbeneOrderInfo = "/order/info" - coinbeneCancelOrder = "/order/cancel" - coinbeneBatchCancel = "/order/batchCancel" - coinbeneOpenOrders = "/order/openOrders" - coinbeneOpenOrdersByPage = "/order/openOrdersByPage" - coinbeneClosedOrders = "/order/closedOrders" - coinbeneClosedOrdersByPage = "/order/closedOrdersByPage" - coinbeneListSwapPositions = "/position/list" - coinbenePositionFeeRate = "/position/feeRate" - coinbeneDepositAddress = "/deposit/address/list" - coinbeneWithdraw = "/withdraw/apply" - - limitOrder = "1" - marketOrder = "2" - postOnlyOrder = "8" - fillOrKillOrder = "9" - iosOrder = "10" - buyDirection = "1" - openLong = "openLong" - openShort = "openShort" - sellDirection = "2" -) - -// GetAllPairs gets all pairs on the exchange -func (c *Coinbene) GetAllPairs(ctx context.Context) ([]PairData, error) { - resp := struct { - Data []PairData `json:"data"` - }{} - return resp.Data, c.SendHTTPRequest(ctx, exchange.RestSpot, coinbeneGetAllPairs, spotPairs, &resp) -} - -// GetPairInfo gets info about a single pair -func (c *Coinbene) GetPairInfo(ctx context.Context, symbol string) (PairData, error) { - resp := struct { - Data PairData `json:"data"` - }{} - params := url.Values{} - params.Set("symbol", symbol) - path := common.EncodeURLValues(coinbenePairInfo, params) - return resp.Data, c.SendHTTPRequest(ctx, exchange.RestSpot, path, spotPairInfo, &resp) -} - -// GetOrderbook gets and stores orderbook data for given pair -func (c *Coinbene) GetOrderbook(ctx context.Context, symbol string, size int64) (Orderbook, error) { - resp := struct { - Data struct { - Asks [][]string `json:"asks"` - Bids [][]string `json:"bids"` - Time time.Time `json:"timestamp"` - } `json:"data"` - }{} - - params := url.Values{} - params.Set("symbol", symbol) - params.Set("depth", strconv.FormatInt(size, 10)) - path := common.EncodeURLValues(coinbeneGetOrderBook, params) - err := c.SendHTTPRequest(ctx, exchange.RestSpot, path, spotOrderbook, &resp) - if err != nil { - return Orderbook{}, err - } - - processOB := func(ob [][]string) ([]OrderbookItem, error) { - var o []OrderbookItem - for x := range ob { - var price, amount float64 - amount, err = strconv.ParseFloat(ob[x][1], 64) - if err != nil { - return nil, err - } - price, err = strconv.ParseFloat(ob[x][0], 64) - if err != nil { - return nil, err - } - o = append(o, OrderbookItem{ - Price: price, - Amount: amount, - }) - } - return o, nil - } - - var s Orderbook - s.Bids, err = processOB(resp.Data.Bids) - if err != nil { - return s, err - } - s.Asks, err = processOB(resp.Data.Asks) - if err != nil { - return s, err - } - s.Time = resp.Data.Time - return s, nil -} - -// GetTicker gets and stores ticker data for a currency pair -func (c *Coinbene) GetTicker(ctx context.Context, symbol string) (TickerData, error) { - resp := struct { - TickerData TickerData `json:"data"` - }{} - params := url.Values{} - params.Set("symbol", symbol) - path := common.EncodeURLValues(coinbeneGetTicker, params) - return resp.TickerData, c.SendHTTPRequest(ctx, exchange.RestSpot, path, spotSpecificTicker, &resp) -} - -// GetTickers gets and all spot tickers supported by the exchange -func (c *Coinbene) GetTickers(ctx context.Context) ([]TickerData, error) { - resp := struct { - TickerData []TickerData `json:"data"` - }{} - - return resp.TickerData, c.SendHTTPRequest(ctx, exchange.RestSpot, coinbeneGetTickersSpot, spotTickerList, &resp) -} - -// GetTrades gets recent trades from the exchange -func (c *Coinbene) GetTrades(ctx context.Context, symbol string, limit int64) (Trades, error) { - resp := struct { - Data [][]string `json:"data"` - }{} - - params := url.Values{} - params.Set("symbol", symbol) - params.Set("limit", strconv.FormatInt(limit, 10)) - path := common.EncodeURLValues(coinbeneGetTrades, params) - err := c.SendHTTPRequest(ctx, exchange.RestSpot, path, spotMarketTrades, &resp) - if err != nil { - return nil, err - } - - var trades Trades - for x := range resp.Data { - tm, err := time.Parse(time.RFC3339, resp.Data[x][4]) - if err != nil { - return nil, err - } - price, err := strconv.ParseFloat(resp.Data[x][1], 64) - if err != nil { - return nil, err - } - volume, err := strconv.ParseFloat(resp.Data[x][2], 64) - if err != nil { - return nil, err - } - trades = append(trades, TradeItem{ - CurrencyPair: resp.Data[x][0], - Price: price, - Volume: volume, - Direction: resp.Data[x][3], - TradeTime: tm, - }) - } - return trades, nil -} - -// GetAccountBalances gets user balanace info -func (c *Coinbene) GetAccountBalances(ctx context.Context) ([]UserBalanceData, error) { - resp := struct { - Data []UserBalanceData `json:"data"` - }{} - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSpot, - http.MethodGet, - coinbeneGetUserBalance, - APISpotPath, - nil, - &resp, - spotAccountInfo) - if err != nil { - return nil, err - } - return resp.Data, nil -} - -// GetAccountAssetBalance gets user balanace info -func (c *Coinbene) GetAccountAssetBalance(ctx context.Context, symbol string) (UserBalanceData, error) { - v := url.Values{} - v.Set("asset", symbol) - resp := struct { - Data UserBalanceData `json:"data"` - }{} - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSpot, - http.MethodGet, - coinbeneAccountBalanceOne, - APISpotPath, - v, - &resp, - spotAccountAssetInfo) - if err != nil { - return UserBalanceData{}, err - } - return resp.Data, nil -} - -// PlaceSpotOrder creates an order -func (c *Coinbene) PlaceSpotOrder(ctx context.Context, price, quantity float64, symbol, direction, - orderType, clientID string, notional int) (OrderPlacementResponse, error) { - var resp OrderPlacementResponse - params := url.Values{} - switch direction { - case order.Buy.Lower(): - params.Set("direction", buyDirection) - case order.Sell.Lower(): - params.Set("direction", sellDirection) - default: - return resp, - fmt.Errorf("invalid direction '%v', must be either 'buy' or 'sell'", - direction) - } - - switch orderType { - case order.Limit.Lower(): - params.Set("orderType", limitOrder) - case order.Market.Lower(): - params.Set("orderType", marketOrder) - case order.PostOnly.Lower(): - params.Set("orderType", postOnlyOrder) - case order.FillOrKill.Lower(): - params.Set("orderType", fillOrKillOrder) - case order.IOS.Lower(): - params.Set("orderType", iosOrder) - default: - return resp, - errors.New("invalid order type, must be either 'limit', 'market', 'postOnly', 'fillOrKill', 'ios'") - } - - params.Set("symbol", symbol) - params.Set("price", strconv.FormatFloat(price, 'f', -1, 64)) - params.Set("quantity", strconv.FormatFloat(quantity, 'f', -1, 64)) - if clientID != "" { - params.Set("clientId", clientID) - } - if notional != 0 { - params.Set("notional", strconv.Itoa(notional)) - } - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSpot, - http.MethodPost, - coinbenePlaceOrder, - APISpotPath, - params, - &resp, - spotPlaceOrder) - if err != nil { - return resp, err - } - return resp, nil -} - -// PlaceSpotOrders sets a batchful order request -func (c *Coinbene) PlaceSpotOrders(ctx context.Context, orders []PlaceOrderRequest) ([]OrderPlacementResponse, error) { - if len(orders) == 0 { - return nil, errors.New("orders is nil") - } - - type ord struct { - Symbol string `json:"symbol"` - Direction string `json:"direction"` - Price string `json:"price"` - Quantity string `json:"quantity"` - OrderType string `json:"orderType"` - Notional string `json:"notional,omitempty"` - ClientID string `json:"clientId,omitempty"` - } - - var reqOrders []ord - for x := range orders { - o := ord{ - Symbol: orders[x].Symbol, - Price: strconv.FormatFloat(orders[x].Price, 'f', -1, 64), - Quantity: strconv.FormatFloat(orders[x].Quantity, 'f', -1, 64), - } - switch orders[x].Direction { - case order.Buy.Lower(): - o.Direction = buyDirection - case order.Sell.Lower(): - o.Direction = sellDirection - default: - return nil, - fmt.Errorf("invalid direction '%v', must be either 'buy' or 'sell'", - orders[x].Direction) - } - - switch orders[x].OrderType { - case order.Limit.Lower(): - o.OrderType = limitOrder - case order.Market.Lower(): - o.OrderType = marketOrder - default: - return nil, - errors.New("invalid order type, must be either 'limit' or 'market'") - } - - if orders[x].ClientID != "" { - o.ClientID = orders[x].ClientID - } - if orders[x].Notional != 0 { - o.Notional = strconv.Itoa(orders[x].Notional) - } - reqOrders = append(reqOrders, o) - } - - resp := struct { - Data []OrderPlacementResponse `json:"data"` - }{} - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSpot, - http.MethodPost, - coinbeneBatchPlaceOrder, - APISpotPath, - reqOrders, - &resp, - spotBatchOrder) - if err != nil { - return nil, err - } - return resp.Data, nil -} - -// FetchOpenSpotOrders finds open orders -func (c *Coinbene) FetchOpenSpotOrders(ctx context.Context, symbol string) (OrdersInfo, error) { - params := url.Values{} - params.Set("symbol", symbol) - var orders OrdersInfo - for i := int64(1); ; i++ { - temp := struct { - Data OrdersInfo `json:"data"` - }{} - params.Set("pageNum", strconv.FormatInt(i, 10)) - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSpot, - http.MethodGet, - coinbeneOpenOrders, - APISpotPath, - params, - &temp, - spotQueryOpenOrders) - if err != nil { - return nil, err - } - for j := range temp.Data { - orders = append(orders, temp.Data[j]) - } - - if len(temp.Data) != 20 { - break - } - } - return orders, nil -} - -// FetchClosedOrders finds open orders -func (c *Coinbene) FetchClosedOrders(ctx context.Context, symbol, latestID string) (OrdersInfo, error) { - params := url.Values{} - params.Set("symbol", symbol) - params.Set("latestOrderId", latestID) - var orders OrdersInfo - for i := int64(1); ; i++ { - temp := struct { - Data OrdersInfo `json:"data"` - }{} - params.Set("pageNum", strconv.FormatInt(i, 10)) - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSpot, - http.MethodGet, - coinbeneClosedOrders, - APISpotPath, - params, - &temp, - spotQueryClosedOrders) - if err != nil { - return nil, err - } - for j := range temp.Data { - orders = append(orders, temp.Data[j]) - } - if len(temp.Data) != 20 { - break - } - } - return orders, nil -} - -// FetchSpotOrderInfo gets order info -func (c *Coinbene) FetchSpotOrderInfo(ctx context.Context, orderID string) (OrderInfo, error) { - resp := struct { - Data OrderInfo `json:"data"` - }{} - params := url.Values{} - params.Set("orderId", orderID) - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSpot, - http.MethodGet, - coinbeneOrderInfo, - APISpotPath, - params, - &resp, - spotQuerySpecficOrder) - if err != nil { - return resp.Data, err - } - if resp.Data.OrderID != orderID { - return resp.Data, fmt.Errorf("%s orderID doesn't match the returned orderID %s", - orderID, resp.Data.OrderID) - } - return resp.Data, nil -} - -// GetSpotOrderFills returns a list of fills related to an order ID -func (c *Coinbene) GetSpotOrderFills(ctx context.Context, orderID string) ([]OrderFills, error) { - resp := struct { - Data []OrderFills `json:"data"` - }{} - params := url.Values{} - params.Set("orderId", orderID) - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSpot, - http.MethodGet, - coinbeneTradeFills, - APISpotPath, - params, - &resp, - spotQueryTradeFills) - if err != nil { - return nil, err - } - return resp.Data, nil -} - -// CancelSpotOrder removes a given order -func (c *Coinbene) CancelSpotOrder(ctx context.Context, orderID string) (string, error) { - resp := struct { - Data string `json:"data"` - }{} - req := make(map[string]interface{}) - req["orderId"] = orderID - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSpot, - http.MethodPost, - coinbeneCancelOrder, - APISpotPath, - req, - &resp, - spotCancelOrder) - if err != nil { - return "", err - } - return resp.Data, nil -} - -// CancelSpotOrders cancels a batch of orders -func (c *Coinbene) CancelSpotOrders(ctx context.Context, orderIDs []string) ([]OrderCancellationResponse, error) { - req := make(map[string]interface{}) - req["orderIds"] = orderIDs - type resp struct { - Data []OrderCancellationResponse `json:"data"` - } - - var r resp - err := c.SendAuthHTTPRequest( - ctx, - exchange.RestSpot, - http.MethodPost, - coinbeneBatchCancel, - APISpotPath, - req, - &r, - spotCancelOrdersBatch) - if err != nil { - return nil, err - } - return r.Data, nil -} - -// GetSwapTickers returns a map of swap tickers -func (c *Coinbene) GetSwapTickers(ctx context.Context) (SwapTickers, error) { - type resp struct { - Data SwapTickers `json:"data"` - } - var r resp - err := c.SendHTTPRequest(ctx, exchange.RestSwap, coinbeneGetTickers, contractTickers, &r) - if err != nil { - return nil, err - } - return r.Data, nil -} - -// GetSwapTicker returns a specific swap ticker -func (c *Coinbene) GetSwapTicker(ctx context.Context, symbol string) (SwapTicker, error) { - tickers, err := c.GetSwapTickers(ctx) - if err != nil { - return SwapTicker{}, err - } - t, ok := tickers[strings.ToUpper(symbol)] - if !ok { - return SwapTicker{}, - fmt.Errorf("symbol %s not found in tickers map", symbol) - } - return t, nil -} - -// GetSwapInstruments returns a list of tradable instruments -func (c *Coinbene) GetSwapInstruments(ctx context.Context) ([]Instrument, error) { - resp := struct { - Data []Instrument `json:"data"` - }{} - return resp.Data, c.SendHTTPRequest(ctx, exchange.RestSwap, - coinbeneGetInstruments, contractInstruments, &resp) -} - -// GetSwapOrderbook returns an orderbook for the specified currency -func (c *Coinbene) GetSwapOrderbook(ctx context.Context, symbol string, size int64) (Orderbook, error) { - var s Orderbook - if symbol == "" { - return s, fmt.Errorf("a symbol must be specified") - } - - v := url.Values{} - v.Set("symbol", symbol) - if size != 0 { - v.Set("size", strconv.FormatInt(size, 10)) - } - - type resp struct { - Data struct { - Asks [][]string `json:"asks"` - Bids [][]string `json:"bids"` - Time time.Time `json:"timestamp"` - Symbol string `json:"symbol"` - } `json:"data"` - } - - var r resp - path := common.EncodeURLValues(coinbeneGetOrderBook, v) - err := c.SendHTTPRequest(ctx, exchange.RestSwap, path, contractOrderbook, &r) - if err != nil { - return s, err - } - - processOB := func(ob [][]string) ([]OrderbookItem, error) { - var o []OrderbookItem - for x := range ob { - var price, amount float64 - var count int64 - count, err = strconv.ParseInt(ob[x][2], 10, 64) - if err != nil { - return nil, err - } - price, err = strconv.ParseFloat(ob[x][0], 64) - if err != nil { - return nil, err - } - amount, err = strconv.ParseFloat(ob[x][1], 64) - if err != nil { - return nil, err - } - o = append(o, OrderbookItem{Price: price, - Amount: amount, - Count: count, - }) - } - return o, nil - } - - s.Bids, err = processOB(r.Data.Bids) - if err != nil { - return s, err - } - s.Asks, err = processOB(r.Data.Asks) - if err != nil { - return s, err - } - s.Time = r.Data.Time - s.Symbol = r.Data.Symbol - return s, nil -} - -// GetKlines data returns the kline data for a specific symbol -func (c *Coinbene) GetKlines(ctx context.Context, pair string, start, end time.Time, period string) (resp CandleResponse, err error) { - v := url.Values{} - v.Add("symbol", pair) - if !start.IsZero() { - v.Add("start", strconv.FormatInt(start.Unix(), 10)) - } - if !end.IsZero() { - v.Add("end", strconv.FormatInt(end.Unix(), 10)) - } - v.Add("period", period) - - path := common.EncodeURLValues(coinbeneSpotKlines, v) - if err = c.SendHTTPRequest(ctx, exchange.RestSpot, path, contractKline, &resp); err != nil { - return - } - - if resp.Code != 200 { - return resp, errors.New(resp.Message) - } - - return -} - -// GetSwapKlines data returns the kline data for a specific symbol -func (c *Coinbene) GetSwapKlines(ctx context.Context, symbol string, start, end time.Time, resolution string) (resp CandleResponse, err error) { - v := url.Values{} - v.Set("symbol", symbol) - if !start.IsZero() { - v.Add("startTime", strconv.FormatInt(start.Unix(), 10)) - } - if !end.IsZero() { - v.Add("endTime", strconv.FormatInt(end.Unix(), 10)) - } - v.Set("resolution", resolution) - - path := common.EncodeURLValues(coinbeneGetKlines, v) - if err = c.SendHTTPRequest(ctx, exchange.RestSwap, path, contractKline, &resp); err != nil { - return - } - - return -} - -// GetSwapTrades returns a list of trades for a swap symbol -func (c *Coinbene) GetSwapTrades(ctx context.Context, symbol string, limit int) (SwapTrades, error) { - v := url.Values{} - v.Set("symbol", symbol) - if limit != 0 { - v.Set("limit", strconv.Itoa(limit)) - } - type resp struct { - Data [][]string `json:"data"` - } - var r resp - path := common.EncodeURLValues(coinbeneGetTrades, v) - if err := c.SendHTTPRequest(ctx, exchange.RestSwap, path, contractTrades, &r); err != nil { - return nil, err - } - - var s SwapTrades - for x := range r.Data { - tm, err := time.Parse(time.RFC3339, r.Data[x][3]) - if err != nil { - return nil, err - } - price, err := strconv.ParseFloat(r.Data[x][0], 64) - if err != nil { - return nil, err - } - orderSide := order.Buy - if r.Data[x][1] == "s" { - orderSide = order.Sell - } - volume, err := strconv.ParseFloat(r.Data[x][2], 64) - if err != nil { - return nil, err - } - s = append(s, SwapTrade{ - Price: price, - Side: orderSide, - Volume: volume, - Time: tm, - }) - } - return s, nil -} - -// GetSwapAccountInfo returns a users swap account balance info -func (c *Coinbene) GetSwapAccountInfo(ctx context.Context) (SwapAccountInfo, error) { - type resp struct { - Data SwapAccountInfo `json:"data"` - } - var r resp - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSwap, - http.MethodGet, - coinbeneAccountInfo, - APISwapPath, - nil, - &r, - contractAccountInfo) - if err != nil { - return SwapAccountInfo{}, err - } - return r.Data, nil -} - -// GetSwapPositions returns a list of open swap positions -func (c *Coinbene) GetSwapPositions(ctx context.Context, symbol string) (SwapPositions, error) { - v := url.Values{} - v.Set("symbol", symbol) - type resp struct { - Data SwapPositions `json:"data"` - } - var r resp - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSwap, - http.MethodGet, - coinbeneListSwapPositions, - APISwapPath, - v, - &r, - contractPositionInfo) - if err != nil { - return nil, err - } - return r.Data, nil -} - -// PlaceSwapOrder places a swap order -func (c *Coinbene) PlaceSwapOrder(ctx context.Context, symbol, direction, orderType, marginMode, - clientID string, price, quantity float64, leverage int) (SwapPlaceOrderResponse, error) { - v := url.Values{} - v.Set("symbol", symbol) - - switch direction { - case order.Buy.Lower(): - v.Set("direction", openLong) - case order.Sell.Lower(): - v.Set("direction", openShort) - default: - return SwapPlaceOrderResponse{}, - fmt.Errorf("invalid direction '%v', must be either 'buy' or 'sell'", - direction) - } - - switch orderType { - case order.Limit.Lower(): - v.Set("orderType", limitOrder) - case order.Market.Lower(): - v.Set("orderType", marketOrder) - default: - return SwapPlaceOrderResponse{}, - errors.New("invalid order type, must be either 'limit' or 'market'") - } - - v.Set("leverage", strconv.Itoa(leverage)) - v.Set("orderPrice", strconv.FormatFloat(price, 'f', -1, 64)) - v.Set("quantity", strconv.FormatFloat(quantity, 'f', -1, 64)) - if marginMode != "" { - v.Set("marginMode", marginMode) - } - if clientID != "" { - v.Set("clientId", clientID) - } - - type resp struct { - Data SwapPlaceOrderResponse `json:"data"` - } - var r resp - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSwap, - http.MethodPost, - coinbenePlaceOrder, - APISwapPath, - v, - &r, - contractPlaceOrder) - if err != nil { - return SwapPlaceOrderResponse{}, err - } - return r.Data, nil -} - -// CancelSwapOrder cancels a swap order -func (c *Coinbene) CancelSwapOrder(ctx context.Context, orderID string) (string, error) { - params := make(map[string]interface{}) - params["orderId"] = orderID - type resp struct { - Data string `json:"data"` - } - var r resp - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSwap, - http.MethodPost, - coinbeneCancelOrder, - APISwapPath, - params, - &r, - contractCancelOrder) - if err != nil { - return "", err - } - return r.Data, nil -} - -// GetSwapOpenOrders gets a list of open swap orders -func (c *Coinbene) GetSwapOpenOrders(ctx context.Context, symbol string, pageNum, pageSize int) (SwapOrders, error) { - v := url.Values{} - v.Set("symbol", symbol) - if pageNum != 0 { - v.Set("pageNum", strconv.Itoa(pageNum)) - } - if pageSize != 0 { - v.Set("pageSize", strconv.Itoa(pageSize)) - } - type resp struct { - Data SwapOrders `json:"data"` - } - var r resp - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSwap, - http.MethodGet, - coinbeneOpenOrders, - APISwapPath, - v, - &r, - contractGetOpenOrders) - if err != nil { - return nil, err - } - return r.Data, nil -} - -// GetSwapOpenOrdersByPage gets a list of open orders by page -func (c *Coinbene) GetSwapOpenOrdersByPage(ctx context.Context, symbol string, latestOrderID int64) (SwapOrders, error) { - v := url.Values{} - if symbol != "" { - v.Set("symbol", symbol) - } - if latestOrderID != 0 { - v.Set("latestOrderId", strconv.FormatInt(latestOrderID, 10)) - } - type resp struct { - Data SwapOrders `json:"data"` - } - var r resp - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSwap, - http.MethodGet, - coinbeneOpenOrdersByPage, - APISwapPath, - v, - &r, - contractOpenOrdersByPage) - if err != nil { - return nil, err - } - return r.Data, nil -} - -// GetSwapOrderInfo gets order info for a specific order -func (c *Coinbene) GetSwapOrderInfo(ctx context.Context, orderID string) (SwapOrder, error) { - v := url.Values{} - v.Set("orderId", orderID) - type resp struct { - Data SwapOrder `json:"data"` - } - var r resp - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSwap, - http.MethodGet, - coinbeneOrderInfo, - APISwapPath, - v, - &r, - contractGetOrderInfo) - if err != nil { - return SwapOrder{}, err - } - return r.Data, nil -} - -// GetSwapOrderHistory returns the swap order history for a given symbol -func (c *Coinbene) GetSwapOrderHistory(ctx context.Context, beginTime, endTime, symbol string, pageNum, - pageSize int, direction, orderType string) (SwapOrders, error) { - v := url.Values{} - if beginTime != "" { - v.Set("beginTime", beginTime) - } - if endTime != "" { - v.Set("endTime", endTime) - } - v.Set("symbol", symbol) - if pageNum != 0 { - v.Set("pageNum", strconv.Itoa(pageNum)) - } - if pageSize != 0 { - v.Set("pageSize", strconv.Itoa(pageSize)) - } - if direction != "" { - v.Set("direction", direction) - } - if orderType != "" { - v.Set("orderType", orderType) - } - - type resp struct { - Data SwapOrders `json:"data"` - } - - var r resp - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSwap, - http.MethodGet, - coinbeneClosedOrders, - APISwapPath, - v, - &r, - contractGetClosedOrders) - if err != nil { - return nil, err - } - return r.Data, nil -} - -// GetSwapOrderHistoryByOrderID returns a list of historic orders based on user params -func (c *Coinbene) GetSwapOrderHistoryByOrderID(ctx context.Context, beginTime, endTime, symbol, status string, - latestOrderID int64) (SwapOrders, error) { - v := url.Values{} - if beginTime != "" { - v.Set("beginTime", beginTime) - } - if endTime != "" { - v.Set("endTime", endTime) - } - if symbol != "" { - v.Set("symbol", symbol) - } - if status != "" { - v.Set("status", status) - } - if latestOrderID != 0 { - v.Set("latestOrderId", strconv.FormatInt(latestOrderID, 10)) - } - type resp struct { - Data SwapOrders `json:"data"` - } - - var r resp - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSwap, - http.MethodGet, - coinbeneClosedOrdersByPage, - APISwapPath, - v, - &r, - contractGetClosedOrdersbyPage) - if err != nil { - return nil, err - } - return r.Data, nil -} - -// CancelSwapOrders cancels multiple swap order IDs -func (c *Coinbene) CancelSwapOrders(ctx context.Context, orderIDs []string) ([]OrderCancellationResponse, error) { - if len(orderIDs) > 10 { - return nil, errors.New("only 10 orderIDs are allowed at a time") - } - req := make(map[string]interface{}) - req["orderIds"] = orderIDs - type resp struct { - Data []OrderCancellationResponse `json:"data"` - } - - var r resp - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSwap, - http.MethodPost, - coinbeneBatchCancel, - APISwapPath, - req, - &r, - contractCancelMultipleOrders) - if err != nil { - return nil, err - } - return r.Data, nil -} - -// GetSwapOrderFills returns a list of swap order fills -func (c *Coinbene) GetSwapOrderFills(ctx context.Context, symbol, orderID string, lastTradeID int64) (SwapOrderFills, error) { - v := url.Values{} - if symbol != "" { - v.Set("symbol", symbol) - } - if orderID != "" { - v.Set("orderId", orderID) - } - if lastTradeID != 0 { - v.Set("lastTradedId", strconv.FormatInt(lastTradeID, 10)) - } - type resp struct { - Data SwapOrderFills `json:"data"` - } - - var r resp - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSwap, - http.MethodGet, - coinbeneOrderFills, - APISwapPath, - v, - &r, - contractGetOrderFills) - if err != nil { - return nil, err - } - return r.Data, nil -} - -// GetSwapFundingRates returns a list of funding rates -func (c *Coinbene) GetSwapFundingRates(ctx context.Context, pageNum, pageSize int) ([]SwapFundingRate, error) { - v := url.Values{} - if pageNum != 0 { - v.Set("pageNum", strconv.Itoa(pageNum)) - } - if pageSize != 0 { - v.Set("pageSize", strconv.Itoa(pageSize)) - } - type resp struct { - Data []SwapFundingRate `json:"data"` - } - - var r resp - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSwap, - http.MethodGet, - coinbenePositionFeeRate, - APISwapPath, - v, - &r, - contractGetFundingRates) - if err != nil { - return nil, err - } - return r.Data, nil -} - -// SendHTTPRequest sends an unauthenticated HTTP request -func (c *Coinbene) SendHTTPRequest(ctx context.Context, ep exchange.URL, path string, f request.EndpointLimit, result interface{}) error { - endpoint, err := c.API.Endpoints.GetURL(ep) - if err != nil { - return err - } - - epPath := coinbeneSpotPath - if ep == exchange.RestSwap { - epPath = coinbeneSwapPath - } - - var resp json.RawMessage - item := &request.Item{ - Method: http.MethodGet, - Path: endpoint + epPath + path, - Result: &resp, - Verbose: c.Verbose, - HTTPDebugging: c.HTTPDebugging, - HTTPRecording: c.HTTPRecording, - } - if err := c.SendPayload(ctx, f, func() (*request.Item, error) { - return item, nil - }); err != nil { - return err - } - - errCap := struct { - Code int `json:"code"` - Message string `json:"message"` - }{} - if err := json.Unmarshal(resp, &errCap); err == nil { - if errCap.Code != 200 && errCap.Message != "" { - return errors.New(errCap.Message) - } - } - return json.Unmarshal(resp, result) -} - -// SendAuthHTTPRequest sends an authenticated HTTP request -func (c *Coinbene) SendAuthHTTPRequest(ctx context.Context, ep exchange.URL, method, epPath string, epAuthPath uint8, - params, result interface{}, f request.EndpointLimit) error { - if !c.AllowAuthenticatedRequest() { - return fmt.Errorf("%s %w", c.Name, exchange.ErrAuthenticatedRequestWithoutCredentialsSet) - } - endpoint, err := c.API.Endpoints.GetURL(ep) - if err != nil { - return err - } - - var authPath string - switch epAuthPath { - case APISpotPath: - authPath = coinbeneSpotPath - case APISwapPath: - authPath = coinbeneSwapPath - case APICapitalPath: - authPath = coinbeneCapitalPath - default: - return errors.New("unsupported auth path") - } - - var resp json.RawMessage - newRequest := func() (*request.Item, error) { - timestamp := time.Now().UTC().Format("2006-01-02T15:04:05.999Z") - var finalBody io.Reader - var preSign string - fullPath := authPath + epPath - switch { - case params != nil && method == http.MethodGet: - p, ok := params.(url.Values) - if !ok { - return nil, errors.New("params is not of type url.Values") - } - preSign = common.EncodeURLValues(timestamp+method+authPath+epPath, p) - fullPath = common.EncodeURLValues(authPath+epPath, p) - case params != nil: - var i interface{} - switch p := params.(type) { - case url.Values: - m := make(map[string]string) - for k, v := range p { - m[k] = strings.Join(v, "") - } - i = m - default: - i = p - } - tempBody, err2 := json.Marshal(i) - if err2 != nil { - return nil, err2 - } - finalBody = bytes.NewBufferString(string(tempBody)) - preSign = timestamp + method + authPath + epPath + string(tempBody) - default: - preSign = timestamp + method + authPath + epPath - } - tempSign, err := crypto.GetHMAC(crypto.HashSHA256, - []byte(preSign), - []byte(c.API.Credentials.Secret)) - if err != nil { - return nil, err - } - - headers := make(map[string]string) - headers["Content-Type"] = "application/json" - headers["ACCESS-KEY"] = c.API.Credentials.Key - headers["ACCESS-SIGN"] = crypto.HexEncodeToString(tempSign) - headers["ACCESS-TIMESTAMP"] = timestamp - - return &request.Item{ - Method: method, - Path: endpoint + fullPath, - Headers: headers, - Body: finalBody, - Result: &resp, - AuthRequest: true, - Verbose: c.Verbose, - HTTPDebugging: c.HTTPDebugging, - HTTPRecording: c.HTTPRecording, - }, nil - } - - if err := c.SendPayload(ctx, f, newRequest); err != nil { - return err - } - - errCap := struct { - Code int `json:"code"` - Message string `json:"message"` - }{} - - if err := json.Unmarshal(resp, &errCap); err == nil && - errCap.Code != 200 && - errCap.Message != "" { - return errors.New(errCap.Message) - } - return json.Unmarshal(resp, result) -} - -// ListDepositAddress returns a list of deposit addresses for a given cryptocurrency -func (c *Coinbene) ListDepositAddress(ctx context.Context, crypto currency.Code) ([]DepositAddress, error) { - vals := url.Values{} - if crypto.IsEmpty() { - return nil, errors.New("crypto asset must be specified") - } - vals.Set("asset", crypto.Upper().String()) - resp := struct { - Data []DepositAddress `json:"data"` - }{} - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSpot, - http.MethodGet, - coinbeneDepositAddress, - APICapitalPath, - vals, - &resp, - capitalDeposit) - if err != nil { - return nil, err - } - return resp.Data, nil -} - -// Withdraw issues a withdrawawl request based on the supplied parameters -func (c *Coinbene) Withdraw(ctx context.Context, curr currency.Code, address, tag, chain string, amount float64) (*WithdrawResponse, error) { - if curr.IsEmpty() || address == "" || amount == 0 { - return nil, errors.New("asset, address and amount must be specified") - } - vals := url.Values{} - vals.Set("asset", curr.Upper().String()) - vals.Set("address", address) - vals.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64)) - - if tag != "" { - vals.Set("tag", tag) - } - - if chain != "" { - vals.Set("chain", chain) - } - - resp := struct { - Data WithdrawResponse `json:"data"` - }{} - - err := c.SendAuthHTTPRequest(ctx, - exchange.RestSpot, - http.MethodPost, - coinbeneWithdraw, - APICapitalPath, - vals, - &resp, - capitalWithdraw) - if err != nil { - return nil, err - } - return &resp.Data, nil -} diff --git a/exchanges/coinbene/coinbene_test.go b/exchanges/coinbene/coinbene_test.go deleted file mode 100644 index ff1f587ffc6..00000000000 --- a/exchanges/coinbene/coinbene_test.go +++ /dev/null @@ -1,892 +0,0 @@ -package coinbene - -import ( - "context" - "errors" - "log" - "os" - "sync" - "testing" - "time" - - "github.com/thrasher-corp/gocryptotrader/common" - "github.com/thrasher-corp/gocryptotrader/config" - "github.com/thrasher-corp/gocryptotrader/core" - "github.com/thrasher-corp/gocryptotrader/currency" - "github.com/thrasher-corp/gocryptotrader/exchanges/asset" - "github.com/thrasher-corp/gocryptotrader/exchanges/kline" - "github.com/thrasher-corp/gocryptotrader/exchanges/order" - "github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues" - "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" -) - -// Please supply your own keys here for due diligence testing -const ( - testAPIKey = "" - testAPISecret = "" - canManipulateRealOrders = false - spotTestPair = "BTC/USDT" - swapTestPair = "BTC-SWAP" -) - -var c Coinbene - -func TestMain(m *testing.M) { - c.SetDefaults() - cfg := config.GetConfig() - err := cfg.LoadConfig("../../testdata/configtest.json", true) - if err != nil { - log.Fatal(err) - } - coinbeneConfig, err := cfg.GetExchangeConfig("Coinbene") - if err != nil { - log.Fatal(err) - } - coinbeneConfig.API.AuthenticatedWebsocketSupport = true - coinbeneConfig.API.AuthenticatedSupport = true - coinbeneConfig.API.Credentials.Secret = testAPISecret - coinbeneConfig.API.Credentials.Key = testAPIKey - c.Websocket = sharedtestvalues.NewTestWebsocket() - err = c.Setup(coinbeneConfig) - if err != nil { - log.Fatal(err) - } - os.Exit(m.Run()) -} - -func areTestAPIKeysSet() bool { - return c.AllowAuthenticatedRequest() -} - -func TestStart(t *testing.T) { - t.Parallel() - err := c.Start(nil) - if !errors.Is(err, common.ErrNilPointer) { - t.Fatalf("received: '%v' but expected: '%v'", err, common.ErrNilPointer) - } - var testWg sync.WaitGroup - err = c.Start(&testWg) - if err != nil { - t.Fatal(err) - } - testWg.Wait() -} - -func TestGetAllPairs(t *testing.T) { - t.Parallel() - _, err := c.GetAllPairs(context.Background()) - if err != nil { - t.Error(err) - } -} - -func TestGetPairInfo(t *testing.T) { - t.Parallel() - _, err := c.GetPairInfo(context.Background(), spotTestPair) - if err != nil { - t.Error(err) - } -} - -func TestGetOrderbook(t *testing.T) { - t.Parallel() - _, err := c.GetOrderbook(context.Background(), spotTestPair, 100) - if err != nil { - t.Error(err) - } -} - -func TestGetTicker(t *testing.T) { - t.Parallel() - _, err := c.GetTicker(context.Background(), spotTestPair) - if err != nil { - t.Error(err) - } -} - -func TestGetTrades(t *testing.T) { - t.Parallel() - _, err := c.GetTrades(context.Background(), spotTestPair, 100) - if err != nil { - t.Error(err) - } -} - -func TestGetAcounntBalances(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() { - t.Skip("API keys required but not set, skipping test") - } - _, err := c.GetAccountBalances(context.Background()) - if err != nil { - t.Error(err) - } -} - -func TestGetAccountAssetBalance(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() { - t.Skip("API keys required but not set, skipping test") - } - _, err := c.GetAccountAssetBalance(context.Background(), currency.BTC.String()) - if err != nil { - t.Error(err) - } -} - -func TestPlaceOrder(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() || !canManipulateRealOrders { - t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly") - } - _, err := c.PlaceSpotOrder(context.Background(), - 1, - 1, - spotTestPair, - order.Buy.Lower(), - order.Limit.Lower(), - "Sup3rAw3s0m3Cl13ntiDH", - 0, - ) - if err != nil { - t.Error(err) - } -} - -func TestPlaceOrders(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() || !canManipulateRealOrders { - t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly") - } - - _, err := c.PlaceSpotOrders(context.Background(), - []PlaceOrderRequest{ - { - 1, - 1, - spotTestPair, - order.Buy.Lower(), - order.Limit.Lower(), - "Sup3rAw3s0m3Cl13ntiDH", - 0, - }, - }) - if err != nil { - t.Error(err) - } -} - -func TestFetchOpenOrders(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() { - t.Skip("API keys required but not set, skipping test") - } - _, err := c.FetchOpenSpotOrders(context.Background(), spotTestPair) - if err != nil { - t.Error(err) - } -} - -func TestFetchClosedOrders(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() { - t.Skip("API keys required but not set, skipping test") - } - _, err := c.FetchClosedOrders(context.Background(), spotTestPair, "") - if err != nil { - t.Error(err) - } -} - -func TestFetchOrderInfo(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() { - t.Skip("API keys required but not set, skipping test") - } - _, err := c.FetchSpotOrderInfo(context.Background(), "adfjashjgsag") - if err != nil { - t.Error(err) - } -} - -func TestGetDepositAddress(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() { - t.Skip("API keys required but not set, skipping test") - } - _, err := c.GetDepositAddress(context.Background(), currency.USDT, "", "ETH") - if err != nil { - t.Error(err) - } -} - -func TestWithdraw(t *testing.T) { - t.Parallel() - if areTestAPIKeysSet() && !canManipulateRealOrders { - t.Skip("API keys set, canManipulateRealOrders false, skipping test") - } - - withdrawCryptoRequest := withdraw.Request{ - Exchange: c.Name, - Amount: -1, - Currency: currency.BTC, - Description: "WITHDRAW IT ALL", - Crypto: withdraw.CryptoRequest{ - Address: core.BitcoinDonationAddress, - }, - } - - _, err := c.WithdrawCryptocurrencyFunds(context.Background(), &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) - } -} - -func TestGetSpotOrderFills(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() { - t.Skip("API keys required but not set, skipping test") - } - _, err := c.GetSpotOrderFills(context.Background(), "1912131427156307968") - if err != nil { - t.Error(err) - } -} - -func TestCancelSpotOrder(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() || !canManipulateRealOrders { - t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly") - } - _, err := c.CancelSpotOrder(context.Background(), "adfjashjgsag") - if err != nil { - t.Error(err) - } -} - -func TestCancelSpotOrders(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() || !canManipulateRealOrders { - t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly") - } - - _, err := c.CancelSpotOrders(context.Background(), - []string{"578639816552972288", "578639902896914432"}) - if err != nil { - t.Error(err) - } -} - -func TestUpdateTicker(t *testing.T) { - t.Parallel() - cp, err := currency.NewPairFromString(spotTestPair) - if err != nil { - t.Fatal(err) - } - _, err = c.UpdateTicker(context.Background(), cp, asset.Spot) - if err != nil { - t.Error(err) - } - cp, err = currency.NewPairFromString(swapTestPair) - if err != nil { - t.Fatal(err) - } - _, err = c.UpdateTicker(context.Background(), cp, asset.PerpetualSwap) - if err != nil { - t.Error(err) - } -} - -func TestUpdateTickers(t *testing.T) { - // TODO: fix Coinbene rate limiting that will allow to uncomment the next line - // and enable parallel testing - // t.Parallel() - err := c.UpdateTickers(context.Background(), asset.Spot) - if err != nil { - t.Error(err) - } - - err = c.UpdateTickers(context.Background(), asset.PerpetualSwap) - if err != nil { - t.Error(err) - } -} - -func TestGetAccountInfo(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() { - t.Skip("API keys required but not set, skipping test") - } - _, err := c.UpdateAccountInfo(context.Background(), asset.Spot) - if err != nil { - t.Error(err) - } -} - -func TestUpdateOrderbook(t *testing.T) { - t.Parallel() - cp, err := currency.NewPairFromString(spotTestPair) - if err != nil { - t.Fatal(err) - } - _, err = c.UpdateOrderbook(context.Background(), cp, asset.Spot) - if err != nil { - t.Error(err) - } - cp, err = currency.NewPairFromString(swapTestPair) - if err != nil { - t.Fatal(err) - } - _, err = c.UpdateOrderbook(context.Background(), cp, asset.PerpetualSwap) - if err != nil { - t.Error(err) - } -} - -func TestGetSwapTickers(t *testing.T) { - t.Parallel() - _, err := c.GetSwapTickers(context.Background()) - if err != nil { - t.Error(err) - } -} - -func TestGetSwapTicker(t *testing.T) { - t.Parallel() - _, err := c.GetSwapTicker(context.Background(), swapTestPair) - if err != nil { - t.Error(err) - } -} - -func TestGetSwapOrderbook(t *testing.T) { - t.Parallel() - _, err := c.GetSwapOrderbook(context.Background(), swapTestPair, 100) - if err != nil { - t.Error(err) - } -} - -func TestGetKlines(t *testing.T) { - t.Parallel() - p, err := currency.NewPairFromString(spotTestPair) - if err != nil { - t.Fatal(err) - } - _, err = c.GetKlines(context.Background(), - p.String(), - time.Now().Add(-time.Hour*1), - time.Now(), - "1") - if err != nil { - t.Fatal(err) - } -} - -func TestGetSwapKlines(t *testing.T) { - t.Parallel() - p, err := currency.NewPairFromString(swapTestPair) - if err != nil { - t.Fatal(err) - } - _, err = c.GetSwapKlines(context.Background(), - p.String(), - time.Now().Add(-time.Hour*1), - time.Now(), - "1") - if err != nil { - t.Error(err) - } -} - -func TestGetSwapTrades(t *testing.T) { - t.Parallel() - _, err := c.GetSwapTrades(context.Background(), swapTestPair, 10) - if err != nil { - t.Error(err) - } -} - -func TestGetSwapInstruments(t *testing.T) { - t.Parallel() - _, err := c.GetSwapInstruments(context.Background()) - if err != nil { - t.Error(err) - } -} - -func TestGetSwapAccountInfo(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() { - t.Skip("API keys required but not set, skipping test") - } - _, err := c.GetSwapAccountInfo(context.Background()) - if err != nil { - t.Error(err) - } -} - -func TestGetSwapPositions(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() { - t.Skip("API keys required but not set, skipping test") - } - _, err := c.GetSwapPositions(context.Background(), swapTestPair) - if err != nil { - t.Error(err) - } -} - -func TestPlaceSwapOrder(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() || !canManipulateRealOrders { - t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly") - } - - _, err := c.PlaceSwapOrder(context.Background(), - swapTestPair, - order.Buy.Lower(), - "limit", - "fixed", - "12345", - 1, - 1, - 2) - if err != nil { - t.Error(err) - } -} - -func TestCancelSwapOrder(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() || !canManipulateRealOrders { - t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly") - } - - _, err := c.CancelSwapOrder(context.Background(), "1337") - if err != nil { - t.Error(err) - } -} - -func TestGetOpenSwapOrders(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() || !canManipulateRealOrders { - t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly") - } - - _, err := c.GetSwapOpenOrders(context.Background(), swapTestPair, 0, 0) - if err != nil { - t.Error(err) - } -} - -func TestGetSwapOpenOrdersByPage(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() || !canManipulateRealOrders { - t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly") - } - - _, err := c.GetSwapOpenOrdersByPage(context.Background(), swapTestPair, 0) - if err != nil { - t.Error(err) - } -} - -func TestGetSwapOrderInfo(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() || !canManipulateRealOrders { - t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly") - } - - _, err := c.GetSwapOrderInfo(context.Background(), "1337") - if err != nil { - t.Error(err) - } -} - -func TestGetSwapOrderHistory(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() || !canManipulateRealOrders { - t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly") - } - - _, err := c.GetSwapOrderHistory(context.Background(), - "", "", swapTestPair, 1, 10, "", "") - if err != nil { - t.Error(err) - } -} - -func TestGetSwapOrderHistoryByOrderID(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() || !canManipulateRealOrders { - t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly") - } - - _, err := c.GetSwapOrderHistoryByOrderID(context.Background(), - "", "", swapTestPair, "", 0) - if err != nil { - t.Error(err) - } -} - -func TestCancelSwapOrders(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() || !canManipulateRealOrders { - t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly") - } - - _, err := c.CancelSwapOrders(context.Background(), - []string{"578639816552972288", "578639902896914432"}) - if err != nil { - t.Error(err) - } -} - -func TestGetSwapOrderFills(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() || !canManipulateRealOrders { - t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly") - } - - _, err := c.GetSwapOrderFills(context.Background(), - swapTestPair, "5807143157122003", 580714315825905664) - if err != nil { - t.Error(err) - } -} - -func TestGetSwapFundingRates(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() || !canManipulateRealOrders { - t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly") - } - - _, err := c.GetSwapFundingRates(context.Background(), 1, 2) - if err != nil { - t.Error(err) - } -} - -func TestWsSubscribe(t *testing.T) { - pressXToJSON := []byte(`{"event":"subscribe","topic":"orderBook.BTCUSDT.10"}`) - err := c.wsHandleData(pressXToJSON) - if err != nil { - t.Error(err) - } -} - -func TestWsUnsubscribe(t *testing.T) { - pressXToJSON := []byte(`{"event":"unsubscribe","topic":"tradeList.BTCUSDT"}`) - err := c.wsHandleData(pressXToJSON) - if err != nil { - t.Error(err) - } -} - -func TestWsLogin(t *testing.T) { - pressXToJSON := []byte(`{"event":"login","success":true}`) - err := c.wsHandleData(pressXToJSON) - if err == nil { - t.Error("error cannot be nil as this will initiate an auth subscription") - } - - pressXToJSON = []byte(`{"event":"login","success":false}`) - err = c.wsHandleData(pressXToJSON) - if err == nil { - t.Error("Expected error") - } -} - -func TestWsOrderbook(t *testing.T) { - pressXToJSON := []byte(`{"topic":"spot/orderBook.BTCUSDT","action":"insert","data":[{"bids":[["0.00000015","174215.91"],["0.00000012","600000.00"],["0.00000010","10000.00"],["0.00000006","33333.00"],["0.00000004","50000.00"],["0.00000003","2000000.00"],["0.00000002","100000.00"],["0.00000001","1100000.00"]],"asks":[["0.00000262","5152.79"],["0.00000263","44626.00"],["0.00000340","2649.85"],["0.00000398","20056.93"],["0.00000400","1420385.54"],["0.00000790","8594.85"],["0.00000988","42380.97"],["0.00000997","43850.97"],["0.00001398","10541.59"],["0.00001400","3409.29"],["0.00002636","52.11"],["0.00002810","2543.66"],["0.00003200","1018.36"],["0.00004999","19.81"],["0.00005000","400.00"],["0.00005898","4060.56"],["0.00006498","3302.60"],["0.00006668","4060.56"],["0.00008000","400.00"]],"version":4915,"timestamp":1598529668288}]}`) - err := c.wsHandleData(pressXToJSON) - if err != nil { - t.Error(err) - } - - pressXToJSON = []byte(`{"topic":"spot/orderBook.BTCUSDT","action":"update","data":[{"bids":[["2.983","8696"]],"asks":[["3.113","0"]],"version":34600866,"timestamp":1598587478738}]}`) - err = c.wsHandleData(pressXToJSON) - if err != nil { - t.Error(err) - } -} - -func TestWsTrade(t *testing.T) { - pressXToJSON := []byte(`{"data":[["0.00000050","s","37.00",1598500505000],["0.00000060","s","10.00",1598499782000],["0.00000066","s","1.00",1598499782000],["0.00000067","s","1.00",1598499782000],["0.00000068","s","1.00",1598499745000],["0.00000080","b","1.00",1598262728000],["0.00000089","b","5592.81",1597738441000],["0.00000072","b","1.00",1597693134000],["0.00000069","s","21739.13",1597378140000],["0.00000069","s","1.00",1597378140000],["0.00000074","b","1.00",1597354497000],["0.00000079","b","1.00",1597325675000],["0.00000082","b","1.00",1597162162000],["0.00000089","b","1.00",1597084892000],["0.00000073","b","109404.43",1597015827000],["0.00000070","b","1067.00",1597015827000],["0.00000070","b","1.00",1594732841000],["0.00000070","b","10.00",1592178569000],["0.00000065","b","194.76",1592178545000],["0.00000064","b","2.37",1592105641000],["0.00000064","b","3.00",1592087828000],["0.00000045","b","5.00",1592087828000],["0.00000045","b","5.00",1592004274000],["0.00000030","s","100.00",1591931268000],["0.00000020","b","138.12",1591928623000],["0.00000020","b","55027.66",1591928623000],["0.00000020","b","59880.11",1591572812000],["0.00000021","s","138.12",1590413750000],["0.00000021","s","5.37",1590413750000],["0.00000056","s","1.00",1589567228000],["0.00000065","b","1.00",1589567217000],["0.00000060","b","84890.64",1589407481000],["0.00000060","b","17.13",1589407433000],["0.00000060","b","9148.70",1589389270000],["0.00000059","b","9010.00",1589389159000],["0.00000055","b","3876.00",1589389098000],["0.00000055","b","30000.00",1588899981000],["0.00000055","b","5724.00",1588891192000],["0.00000050","b","400.00",1588891192000],["0.00000048","b","26874.64",1588891129000],["0.00000048","b","2.00",1588891129000],["0.00000049","b","7547.75",1585279296000],["0.00000049","b","12180.30",1584932828000],["0.00000049","b","8256.95",1584932828000],["0.00000053","b","500.42",1583351500000],["0.00000053","b","500.00",1583351484000],["0.00000053","b","400.00",1583351470000],["0.00000053","b","394.62",1583351455000],["0.00000053","b","1.99",1583343633000],["0.00000018","s","250.00",1583338813000]],"topic":"spot/tradeList.BTCUSDT","action":"insert"}`) - err := c.wsHandleData(pressXToJSON) - if err != nil { - t.Error(err) - } -} - -func TestWsTicker(t *testing.T) { - pressXToJSON := []byte(`{"topic": "spot/ticker.BTCUSDT","action":"insert","data": [{"symbol":"BTCUSDT","lastPrice":"23.3746","bestAskPrice":"23.3885","bestBidPrice":"23.3603","high24h":"23.5773","open24h":"22.1961","openPrice":"22.5546","low24h":"21.8077","volume24h":"3784807.9709","timestamp":1598587472634}]}`) - err := c.wsHandleData(pressXToJSON) - if err != nil { - t.Error(err) - } -} - -func TestWsKLine(t *testing.T) { - pressXToJSON := []byte(`{"topic": "spot/kline.BTCUSDT.1h","action":"insert","data": [{"t":1594990800,"o":1.1e-07,"h":1.1e-07,"l":1.1e-07,"c":1.1e-07,"v":0}]}`) - err := c.wsHandleData(pressXToJSON) - if err != nil { - t.Error(err) - } -} - -func TestWsUserAccount(t *testing.T) { - pressXToJSON := []byte(`{ - "topic": "btc/user.account", - "data": [{ - "asset": "BTC", - "availableBalance": "20.3859", - "frozenBalance": "0.7413", - "balance": "21.1272", - "timestamp": "2019-05-22T03:11:22.0Z" - }] -}`) - err := c.wsHandleData(pressXToJSON) - if err != nil { - t.Error(err) - } -} - -func TestWsUserPosition(t *testing.T) { - pressXToJSON := []byte(`{ - "topic": "user.position", - "data": [{ - "availableQuantity": "100", - "avgPrice": "7778.1", - "leverage": "20", - "liquidationPrice": "5441.0", - "markPrice": "8086.5", - "positionMargin": "0.0285", - "quantity": "507", - "realisedPnl": "0.0069", - "side": "long", - "symbol": "BTCUSDT", - "marginMode": "1", - "createTime": "2019-05-22T03:11:22.0Z" - }] -}`) - err := c.wsHandleData(pressXToJSON) - if err != nil { - t.Error(err) - } -} - -func TestWsUserOrder(t *testing.T) { - pressXToJSON := []byte(`{ - "topic": "user.order", - "data": [{ - "orderId": "580721369818955776", - "direction": "openLong", - "leverage": "20", - "symbol": "BTCUSDT", - "orderType": "limit", - "quantity": "7", - "orderPrice": "146.30", - "orderValue": "0.0010", - "fee": "0.0000", - "filledQuantity": "0", - "averagePrice": "0.00", - "orderTime": "2019-05-22T03:39:24.0Z", - "status": "new", - "lastFillQuantity": "0", - "lastFillPrice": "0", - "lastFillTime": "" - }] -}`) - err := c.wsHandleData(pressXToJSON) - if err != nil { - t.Error(err) - } -} - -func TestGetHistoricCandles(t *testing.T) { - currencyPair, err := currency.NewPairFromString(spotTestPair) - if err != nil { - t.Fatal(err) - } - startTime := time.Now().Add(-time.Hour * 24) - _, err = c.GetHistoricCandles(context.Background(), - currencyPair, asset.Spot, startTime, time.Now(), kline.OneHour) - if err != nil { - t.Fatal(err) - } - - currencyPairSwap, err := currency.NewPairFromString(swapTestPair) - if err != nil { - t.Fatal(err) - } - _, err = c.GetHistoricCandles(context.Background(), - currencyPairSwap, asset.PerpetualSwap, startTime, time.Now(), kline.OneHour) - if err != nil { - t.Fatal(err) - } -} - -func TestGetHistoricCandlesExtended(t *testing.T) { - currencyPair, err := currency.NewPairFromString(spotTestPair) - if err != nil { - t.Fatal(err) - } - startTime := time.Now().Add(-time.Hour * 2) - _, err = c.GetHistoricCandlesExtended(context.Background(), - currencyPair, asset.Spot, startTime, time.Now(), kline.OneHour) - if err != nil { - t.Fatal(err) - } -} - -func Test_FormatExchangeKlineInterval(t *testing.T) { - testCases := []struct { - name string - interval kline.Interval - output string - }{ - { - "OneMin", - kline.OneMin, - "1", - }, - { - "OneHour", - kline.OneHour, - "60", - }, - { - "OneDay", - kline.OneDay, - "D", - }, - { - "OneWeek", - kline.OneWeek, - "W", - }, - { - "OneMonth", - kline.OneMonth, - "M", - }, - { - "AllOther", - kline.TwoWeek, - "", - }, - } - - for x := range testCases { - test := testCases[x] - - t.Run(test.name, func(t *testing.T) { - ret := c.FormatExchangeKlineInterval(test.interval) - - if ret != test.output { - t.Fatalf("unexpected result return expected: %v received: %v", test.output, ret) - } - }) - } -} - -func TestInferAssetFromTopic(t *testing.T) { - a := inferAssetFromTopic("spot/orderBook.BSVBTC") - if a != asset.Spot { - t.Error("expected spot") - } - a = inferAssetFromTopic("btc/orderBook.BSVBTC") - if a != asset.PerpetualSwap { - t.Error("expected PerpetualSwap") - } - a = inferAssetFromTopic("usdt/orderBook.BSVBTC") - if a != asset.PerpetualSwap { - t.Error("expected PerpetualSwap") - } - a = inferAssetFromTopic("orderBook.BSVBTC") - if a != asset.PerpetualSwap { - t.Error("expected PerpetualSwap") - } - a = inferAssetFromTopic("") - if a != asset.PerpetualSwap { - t.Error("expected PerpetualSwap") - } -} - -func TestGetCurrencyFromWsTopic(t *testing.T) { - p, err := c.getCurrencyFromWsTopic(asset.Spot, "spot/orderbook.BTCUSDT") - if err != nil { - t.Error(err) - } - if p.Base.String() != "BTC" && p.Quote.String() != "USDT" { - t.Errorf("unexpected currency, wanted BTCUSD, received %v", p.String()) - } - - _, err = c.getCurrencyFromWsTopic(asset.Spot, "fake") - if err != nil && err.Error() != "no currency found in topic fake" { - t.Error(err) - } - - _, err = c.getCurrencyFromWsTopic(asset.Spot, "hello.moto") - if err != nil && err.Error() != "currency moto not found in supplied pairs" { - t.Error(err) - } - - _, err = c.getCurrencyFromWsTopic(asset.Spot, "spot/kline.GOM2USDT.1h") - if err != nil && err.Error() != "currency moto not found in enabled pairs" { - t.Error(err) - } -} - -func TestGetRecentTrades(t *testing.T) { - t.Parallel() - currencyPair, err := currency.NewPairFromString(spotTestPair) - if err != nil { - t.Fatal(err) - } - _, err = c.GetRecentTrades(context.Background(), currencyPair, asset.Spot) - if err != nil { - t.Error(err) - } -} - -func TestGetHistoricTrades(t *testing.T) { - t.Parallel() - currencyPair, err := currency.NewPairFromString(spotTestPair) - if err != nil { - t.Fatal(err) - } - _, err = c.GetHistoricTrades(context.Background(), - currencyPair, asset.Spot, time.Now().Add(-time.Minute*15), time.Now()) - if err != nil && err != common.ErrFunctionNotSupported { - t.Error(err) - } -} - -func TestListDepositAddress(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() { - t.Skip("api keys not set") - } - _, err := c.ListDepositAddress(context.Background(), currency.USDT) - if err != nil { - t.Fatal(err) - } -} - -func TestGetAvailableTransferCurrencies(t *testing.T) { - t.Parallel() - if !areTestAPIKeysSet() { - t.Skip("api keys not set") - } - _, err := c.GetAvailableTransferChains(context.Background(), currency.USDT) - if err != nil { - t.Fatal(err) - } -} diff --git a/exchanges/coinbene/coinbene_types.go b/exchanges/coinbene/coinbene_types.go deleted file mode 100644 index c3924ae5bbc..00000000000 --- a/exchanges/coinbene/coinbene_types.go +++ /dev/null @@ -1,439 +0,0 @@ -package coinbene - -import ( - "time" - - "github.com/thrasher-corp/gocryptotrader/currency" - "github.com/thrasher-corp/gocryptotrader/exchanges/order" -) - -// Coinbene path vals -const ( - APISpotPath uint8 = iota - APISwapPath - APICapitalPath -) - -// TickerData stores ticker data -type TickerData struct { - Symbol string `json:"symbol"` - LatestPrice float64 `json:"latestPrice,string"` - BestBid float64 `json:"bestBid,string"` - BestAsk float64 `json:"bestAsk,string"` - DailyHigh float64 `json:"high24h,string"` - DailyLow float64 `json:"low24h,string"` - DailyVolume float64 `json:"volume24h,string"` -} - -// OrderbookItem stores an individual orderbook item -type OrderbookItem struct { - Price float64 - Amount float64 - Count int64 -} - -// Orderbook stores the orderbook data -type Orderbook struct { - Bids []OrderbookItem - Asks []OrderbookItem - Symbol string - Time time.Time -} - -// TradeItem stores a single trade -type TradeItem struct { - CurrencyPair string - Price float64 - Volume float64 - Direction string - TradeTime time.Time -} - -// Trades stores trade data -type Trades []TradeItem - -// PairData stores pair data -type PairData struct { - Symbol string `json:"symbol"` - BaseAsset string `json:"baseAsset"` - QuoteAsset string `json:"quoteAsset"` - PricePrecision int64 `json:"pricePrecision,string"` - AmountPrecision int64 `json:"amountPrecision,string"` - TakerFeeRate float64 `json:"takerFeeRate,string"` - MakerFeeRate float64 `json:"makerFeeRate,string"` - MinAmount float64 `json:"minAmount,string"` - Site string `json:"site"` - PriceFluctuation float64 `json:"priceFluctuation,string"` -} - -// UserBalanceData stores user balance data -type UserBalanceData struct { - Asset string `json:"asset"` - Available float64 `json:"available,string"` - Reserved float64 `json:"reserved,string"` - Total float64 `json:"total,string"` -} - -// PlaceOrderRequest places an order request -type PlaceOrderRequest struct { - Price float64 - Quantity float64 - Symbol string - Direction string - OrderType string - ClientID string - Notional int -} - -// CancelOrdersResponse stores data for a cancelled order -type CancelOrdersResponse struct { - OrderID string `json:"orderId"` - Message string `json:"message"` -} - -// OrderInfo stores order info -type OrderInfo struct { - OrderID string `json:"orderId"` - BaseAsset string `json:"baseAsset"` - QuoteAsset string `json:"quoteAsset"` - OrderType string `json:"orderDirection"` - Quantity float64 `json:"quntity,string"` - Amount float64 `json:"amout,string"` - FilledAmount float64 `json:"filledAmount"` - TakerRate float64 `json:"takerFeeRate,string"` - MakerRate float64 `json:"makerFeeRate,string"` - AvgPrice float64 `json:"avgPrice,string"` - OrderPrice float64 `json:"orderPrice,string"` - OrderStatus string `json:"orderStatus"` - OrderTime time.Time `json:"orderTime"` - TotalFee float64 `json:"totalFee"` -} - -// OrderFills stores the fill info -type OrderFills struct { - Price float64 `json:"price,string"` - Quantity float64 `json:"quantity,string"` - Amount float64 `json:"amount,string"` - Fee float64 `json:"fee,string"` - Direction string `json:"direction"` - TradeTime time.Time `json:"tradeTime"` - FeeByConi float64 `json:"feeByConi,string"` -} - -// OrdersInfo stores a collection of orders -type OrdersInfo []OrderInfo - -// WsSub stores subscription data -type WsSub struct { - Operation string `json:"op"` - Arguments []string `json:"args"` -} - -// WsTickerData stores websocket ticker data -type WsTickerData struct { - Symbol string `json:"symbol"` - LastPrice float64 `json:"lastPrice,string"` - MarkPrice float64 `json:"markPrice,string"` - BestAskPrice float64 `json:"bestAskPrice,string"` - BestBidPrice float64 `json:"bestBidPrice,string"` - BestAskVolume float64 `json:"bestAskVolume,string"` - BestBidVolume float64 `json:"bestBidVolume,string"` - High24h float64 `json:"high24h,string"` - Low24h float64 `json:"low24h,string"` - Volume24h float64 `json:"volume24h,string"` - Timestamp int64 `json:"timestamp"` -} - -// WsTicker stores websocket ticker -type WsTicker struct { - Topic string `json:"topic"` - Data []WsTickerData `json:"data"` -} - -// WsTradeList stores websocket tradelist data -type WsTradeList struct { - Topic string `json:"topic"` - Data [][4]interface{} `json:"data"` -} - -// WsTradeData stores trade data for websocket -type WsTradeData struct { - BestAskPrice float64 `json:"bestAskPrice,string"` - BestBidPrice float64 `json:"bestBidPrice,string"` - High24h float64 `json:"high24h,string"` - LastPrice float64 `json:"lastPrice,string"` - Low24h float64 `json:"low24h,string"` - Open24h float64 `json:"open24h,string"` - OpenPrice float64 `json:"openPrice,string"` - Symbol string `json:"symbol"` - Timestamp int64 `json:"timestamp"` - Volume24h float64 `json:"volume24h,string"` -} - -// WsKline stores websocket kline data -type WsKline struct { - Topic string `json:"topic"` - Data []WsKLineData `json:"data"` -} - -// WsKLineData holds OHLCV data -type WsKLineData struct { - Open float64 `json:"o"` - High float64 `json:"h"` - Low float64 `json:"l"` - Close float64 `json:"c"` - Volume float64 `json:"v"` - Timestamp int64 `json:"t"` -} - -// WsUserData stores websocket user data -type WsUserData struct { - Asset string `json:"string"` - Available float64 `json:"availableBalance,string"` - Locked float64 `json:"frozenBalance,string"` - Total float64 `json:"balance,string"` - Timestamp time.Time `json:"timestamp"` -} - -// WsUserInfo stores websocket user info -type WsUserInfo struct { - Topic string `json:"topic"` - Data []WsUserData `json:"data"` -} - -// WsPositionData stores websocket info on user's position -type WsPositionData struct { - AvailableQuantity float64 `json:"availableQuantity,string"` - AveragePrice float64 `json:"avgPrice,string"` - Leverage int64 `json:"leverage,string"` - LiquidationPrice float64 `json:"liquidationPrice,string"` - MarkPrice float64 `json:"markPrice,string"` - PositionMargin float64 `json:"positionMargin,string"` - Quantity float64 `json:"quantity,string"` - RealisedPNL float64 `json:"realisedPnl,string"` - Side string `json:"side"` - Symbol string `json:"symbol"` - MarginMode int64 `json:"marginMode,string"` - CreateTime time.Time `json:"createTime"` -} - -// WsPosition stores websocket info on user's positions -type WsPosition struct { - Topic string `json:"topic"` - Data []WsPositionData `json:"data"` -} - -// WsOrderbookData stores ws orderbook data -type WsOrderbookData struct { - Topic string `json:"topic"` - Action string `json:"action"` - Data []struct { - Bids [][2]string `json:"bids"` - Asks [][2]string `json:"asks"` - Version int64 `json:"version"` - Timestamp int64 `json:"timestamp"` - } `json:"data"` -} - -// WsOrderData stores websocket user order data -type WsOrderData struct { - OrderID string `json:"orderId"` - Direction string `json:"direction"` - Leverage int64 `json:"leverage,string"` - Symbol string `json:"symbol"` - OrderType string `json:"orderType"` - Quantity float64 `json:"quantity,string"` - OrderPrice float64 `json:"orderPrice,string"` - OrderValue float64 `json:"orderValue,string"` - Fee float64 `json:"fee,string"` - FilledQuantity float64 `json:"filledQuantity,string"` - AveragePrice float64 `json:"averagePrice,string"` - OrderTime time.Time `json:"orderTime"` - Status string `json:"status"` - LastFillQuantity float64 `json:"lastFillQuantity,string"` - LastFillPrice float64 `json:"lastFillPrice,string"` - LastFillTime string `json:"lastFillTime"` -} - -// WsUserOrders stores websocket user orders' data -type WsUserOrders struct { - Topic string `json:"topic"` - Data []WsOrderData `json:"data"` -} - -// SwapTicker stores the swap ticker info -type SwapTicker struct { - LastPrice float64 `json:"lastPrice,string"` - MarkPrice float64 `json:"markPrice,string"` - BestAskPrice float64 `json:"bestAskPrice,string"` - BestBidPrice float64 `json:"bestBidPrice,string"` - High24Hour float64 `json:"high24h,string"` - Low24Hour float64 `json:"low24h,string"` - Volume24Hour float64 `json:"volume24h,string"` - BestAskVolume float64 `json:"bestAskVolume,string"` - BestBidVolume float64 `json:"bestBidVolume,string"` - Turnover float64 `json:"turnover,string"` - Timestamp time.Time `json:"timeStamp"` - Change24Hour float64 `json:"chg24h,string"` - ChangeZeroHour float64 `json:"chg0h,string"` -} - -// SwapTickers stores a map of swap tickers -type SwapTickers map[string]SwapTicker - -// SwapKlineItem stores an individual kline data item -type SwapKlineItem struct { - Time time.Time - Open float64 - Close float64 - High float64 - Low float64 - Volume float64 - Turnover float64 - BuyVolume float64 - BuyTurnover float64 -} - -// SwapKlines stores an array of kline data -type SwapKlines []SwapKlineItem - -// Instrument stores an individual tradable instrument -type Instrument struct { - InstrumentID currency.Pair `json:"instrumentId"` - Multiplier float64 `json:"multiplier,string"` - MinimumAmount float64 `json:"minAmount,string"` - MaximumAmount float64 `json:"maxAmount,string"` - MinimumPriceChange float64 `json:"minPriceChange,string"` - PricePrecision int64 `json:"pricePrecision,string"` -} - -// SwapTrade stores an individual trade -type SwapTrade struct { - Price float64 - Side order.Side - Volume float64 - Time time.Time -} - -// SwapTrades stores an array of swap trades -type SwapTrades []SwapTrade - -// SwapAccountInfo returns the swap account balance info -type SwapAccountInfo struct { - AvailableBalance float64 `json:"availableBalance,string"` - FrozenBalance float64 `json:"frozenBalance,string"` - MarginBalance float64 `json:"marginBalance,string"` - MarginRate float64 `json:"marginRate,string"` - Balance float64 `json:"balance,string"` - UnrealisedProfitAndLoss float64 `json:"unrealisedPnl,string"` -} - -// SwapPosition stores a single swap position's data -type SwapPosition struct { - AvailableQuantity float64 `json:"availableQuantity,string"` - AveragePrice float64 `json:"averagePrice,string"` - CreateTime time.Time `json:"createTime"` - DeleveragePercentile int64 `json:"deleveragePercentile,string"` - Leverage int64 `json:"leverage,string"` - LiquidationPrice float64 `json:"liquidationPrice,string"` - MarkPrice float64 `json:"markPrice,string"` - PositionMargin float64 `json:"positionMargin,string"` - PositionValue float64 `json:"positionValue,string"` - Quantity float64 `json:"quantity,string"` - RateOfReturn float64 `json:"roe,string"` - Side string `json:"side"` - Symbol string `json:"symbol"` - UnrealisedProfitAndLoss float64 `json:"UnrealisedPnl,string"` -} - -// SwapPositions stores a collection of swap positions -type SwapPositions []SwapPosition - -// SwapPlaceOrderResponse stores the response data for placing a swap order -type SwapPlaceOrderResponse struct { - OrderID string `json:"orderId"` - ClientID string `json:"clientId"` -} - -// SwapOrder stores the swap order data -type SwapOrder struct { - OrderID string `json:"orderId"` - Direction string `json:"direction"` - Leverage int64 `json:"leverage,string"` - OrderType string `json:"orderType"` - Quantity float64 `json:"quantity,string"` - OrderPrice float64 `json:"orderPrice,string"` - OrderValue float64 `json:"orderValue,string"` - Fee float64 `json:"fee"` - FilledQuantity float64 `json:"filledQuantity,string"` - AveragePrice float64 `json:"averagePrice,string"` - OrderTime time.Time `json:"orderTime"` - Status string `json:"status"` -} - -// SwapOrders stores a collection of swap orders -type SwapOrders []SwapOrder - -// OrderCancellationResponse returns a list of cancel order status -type OrderCancellationResponse struct { - OrderID string `json:"orderId"` - Code int `json:"code,string"` - Message string `json:"message"` -} - -// OrderPlacementResponse stores the order placement data -type OrderPlacementResponse OrderCancellationResponse - -// SwapOrderFill stores a swap orders fill info -type SwapOrderFill struct { - Symbol string `json:"symbol"` - TradeTime time.Time `json:"tradeTime"` - TradeID int64 `json:"tradeId,string"` - OrderID int64 `json:"orderId,string"` - Price float64 `json:"price,string"` - Fee float64 `json:"fee,string"` - ExecType string `json:"execType"` - Side string `json:"side"` - Quantity float64 `json:"quantity,string"` -} - -// SwapOrderFills stores a collection of swap order fills -type SwapOrderFills []SwapOrderFill - -// SwapFundingRate stores a collection of funding rates -type SwapFundingRate struct { - Symbol string `json:"symbol"` - Side string `json:"side"` - MarkPrice float64 `json:"markPrice,string"` - PositionValue float64 `json:"positionValue,string"` - Fee float64 `json:"fee,string"` - FeeRate float64 `json:"feeRate,string"` - Leverage int64 `json:"leverage"` -} - -// CandleResponse stores returned kline data -type CandleResponse struct { - Code int64 `json:"code"` - Message string `json:"message"` - Data [][]interface{} `json:"data"` -} - -// DepositAddress stores the deposit address data -type DepositAddress struct { - Asset string `json:"asset"` - Chain string `json:"chain"` - Address string `json:"address"` - AddressTag string `json:"addressTag"` - DepositLimit float64 `json:"depositLimit,string"` - BlockNumber uint8 `json:"blockNumber,string"` -} - -// WithdrawResponse stores the withdrawal request response data -type WithdrawResponse struct { - ID string `json:"id"` - Amount float64 `json:"amount"` - Asset string `json:"asset"` - Address string `json:"address"` - Tag string `json:"tag"` - Chain string `json:"chain"` -} diff --git a/exchanges/coinbene/coinbene_websocket.go b/exchanges/coinbene/coinbene_websocket.go deleted file mode 100644 index ec491511e94..00000000000 --- a/exchanges/coinbene/coinbene_websocket.go +++ /dev/null @@ -1,517 +0,0 @@ -package coinbene - -import ( - "encoding/json" - "errors" - "fmt" - "net/http" - "strconv" - "strings" - "time" - - "github.com/gorilla/websocket" - "github.com/thrasher-corp/gocryptotrader/common/crypto" - "github.com/thrasher-corp/gocryptotrader/currency" - exchange "github.com/thrasher-corp/gocryptotrader/exchanges" - "github.com/thrasher-corp/gocryptotrader/exchanges/asset" - "github.com/thrasher-corp/gocryptotrader/exchanges/order" - "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" - "github.com/thrasher-corp/gocryptotrader/exchanges/stream" - "github.com/thrasher-corp/gocryptotrader/exchanges/stream/buffer" - "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/trade" -) - -const ( - wsContractURL = "wss://ws.coinbene.com/stream/ws" - event = "event" - topic = "topic" - swapChannelPrefix = "btc/" - spotChannelPrefix = "spot/" -) - -// WsConnect connects to websocket -func (c *Coinbene) WsConnect() error { - if !c.Websocket.IsEnabled() || !c.IsEnabled() { - return errors.New(stream.WebsocketNotEnabled) - } - var dialer websocket.Dialer - err := c.Websocket.Conn.Dial(&dialer, http.Header{}) - if err != nil { - return err - } - - c.Websocket.Wg.Add(1) - go c.wsReadData() - - if c.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - err = c.Login() - if err != nil { - c.Websocket.DataHandler <- err - c.Websocket.SetCanUseAuthenticatedEndpoints(false) - } - } - return nil -} - -// GenerateDefaultSubscriptions generates stuff -func (c *Coinbene) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) { - var channels = []string{"orderBook.%s.100", "tradeList.%s", "ticker.%s", "kline.%s.1h"} - var subscriptions []stream.ChannelSubscription - perpetualPairs, err := c.GetEnabledPairs(asset.PerpetualSwap) - if err != nil { - return nil, err - } - var spotPairs currency.Pairs - spotPairs, err = c.GetEnabledPairs(asset.Spot) - if err != nil { - return nil, err - } - for x := range channels { - for y := range perpetualPairs { - perpetualPairs[y].Delimiter = "" - subscriptions = append(subscriptions, stream.ChannelSubscription{ - Channel: swapChannelPrefix + fmt.Sprintf(channels[x], perpetualPairs[y]), - Currency: perpetualPairs[y], - Asset: asset.PerpetualSwap, - }) - } - for z := range spotPairs { - spotPairs[z].Delimiter = "" - subscriptions = append(subscriptions, stream.ChannelSubscription{ - Channel: spotChannelPrefix + fmt.Sprintf(channels[x], spotPairs[z]), - Currency: spotPairs[z], - Asset: asset.Spot, - }) - } - } - - return subscriptions, nil -} - -// GenerateAuthSubs generates auth subs -func (c *Coinbene) GenerateAuthSubs() ([]stream.ChannelSubscription, error) { - var subscriptions []stream.ChannelSubscription - var sub stream.ChannelSubscription - var userChannels = []string{"user.account", "user.position", "user.order"} - for z := range userChannels { - sub.Channel = userChannels[z] - subscriptions = append(subscriptions, sub) - } - return subscriptions, nil -} - -// wsReadData receives and passes on websocket messages for processing -func (c *Coinbene) wsReadData() { - defer c.Websocket.Wg.Done() - for { - resp := c.Websocket.Conn.ReadMessage() - if resp.Raw == nil { - return - } - err := c.wsHandleData(resp.Raw) - if err != nil { - c.Websocket.DataHandler <- err - } - } -} - -func inferAssetFromTopic(topic string) asset.Item { - if strings.Contains(topic, "spot/") { - return asset.Spot - } - return asset.PerpetualSwap -} - -func (c *Coinbene) wsHandleData(respRaw []byte) error { - if string(respRaw) == stream.Ping { - err := c.Websocket.Conn.SendRawMessage(websocket.TextMessage, []byte(stream.Pong)) - if err != nil { - return err - } - return nil - } - var result map[string]interface{} - err := json.Unmarshal(respRaw, &result) - if err != nil { - return err - } - _, ok := result[event] - switch { - case ok && (result[event].(string) == "subscribe" || result[event].(string) == "unsubscribe"): - return nil - case ok && result[event].(string) == "error": - return fmt.Errorf("message: %s. code: %v", result["message"], result["code"]) - } - if ok && strings.Contains(result[event].(string), "login") { - if result["success"].(bool) { - c.Websocket.SetCanUseAuthenticatedEndpoints(true) - var authsubs []stream.ChannelSubscription - authsubs, err = c.GenerateAuthSubs() - if err != nil { - return err - } - return c.Websocket.SubscribeToChannels(authsubs) - } - c.Websocket.SetCanUseAuthenticatedEndpoints(false) - return fmt.Errorf("message: %s. code: %v", result["message"], result["code"]) - } - assetType := inferAssetFromTopic(result[topic].(string)) - var newPair currency.Pair - switch { - case strings.Contains(result[topic].(string), "ticker"): - var wsTicker WsTicker - err = json.Unmarshal(respRaw, &wsTicker) - if err != nil { - return err - } - newPair, err = c.getCurrencyFromWsTopic(assetType, wsTicker.Topic) - if err != nil { - return err - } - - for x := range wsTicker.Data { - c.Websocket.DataHandler <- &ticker.Price{ - Volume: wsTicker.Data[x].Volume24h, - Last: wsTicker.Data[x].LastPrice, - High: wsTicker.Data[x].High24h, - Low: wsTicker.Data[x].Low24h, - Bid: wsTicker.Data[x].BestBidPrice, - Ask: wsTicker.Data[x].BestAskPrice, - Pair: newPair, - ExchangeName: c.Name, - AssetType: assetType, - LastUpdated: time.Unix(wsTicker.Data[x].Timestamp, 0), - } - } - case strings.Contains(result[topic].(string), "tradeList"): - if !c.IsSaveTradeDataEnabled() { - return nil - } - var tradeList WsTradeList - err = json.Unmarshal(respRaw, &tradeList) - if err != nil { - return err - } - var trades []trade.Data - for i := range tradeList.Data { - var price, amount float64 - t := time.Unix(int64(tradeList.Data[i][3].(float64))/1000, 0) - price, err = strconv.ParseFloat(tradeList.Data[i][0].(string), 64) - if err != nil { - return err - } - amount, err = strconv.ParseFloat(tradeList.Data[i][2].(string), 64) - if err != nil { - return err - } - - var tSide = order.Buy - if tradeList.Data[i][1] == "s" { - tSide = order.Sell - } - - newPair, err = c.getCurrencyFromWsTopic(assetType, tradeList.Topic) - if err != nil { - return err - } - - trades = append(trades, trade.Data{ - Timestamp: t, - Exchange: c.Name, - CurrencyPair: newPair, - AssetType: assetType, - Price: price, - Amount: amount, - Side: tSide, - }) - } - return trade.AddTradesToBuffer(c.Name, trades...) - case strings.Contains(result[topic].(string), "orderBook"): - var orderBook WsOrderbookData - err = json.Unmarshal(respRaw, &orderBook) - if err != nil { - return err - } - - if len(orderBook.Data) != 1 { - return errors.New("incomplete orderbook data has been received") - } - - newPair, err = c.getCurrencyFromWsTopic(assetType, orderBook.Topic) - if err != nil { - return err - } - var amount, price float64 - var asks, bids []orderbook.Item - for i := range orderBook.Data[0].Asks { - amount, err = strconv.ParseFloat(orderBook.Data[0].Asks[i][1], 64) - if err != nil { - return err - } - price, err = strconv.ParseFloat(orderBook.Data[0].Asks[i][0], 64) - if err != nil { - return err - } - asks = append(asks, orderbook.Item{ - Amount: amount, - Price: price, - }) - } - for j := range orderBook.Data[0].Bids { - price, err = strconv.ParseFloat(orderBook.Data[0].Bids[j][0], 64) - if err != nil { - return err - } - - if price == 0 { - // Last level is coming back as a float with not enough decimal - // places e.g. ["0.000","1001.95"]], - // This needs to be filtered out as this can skew orderbook - // calculations - continue - } - - amount, err = strconv.ParseFloat(orderBook.Data[0].Bids[j][1], 64) - if err != nil { - return err - } - - bids = append(bids, orderbook.Item{ - Amount: amount, - Price: price, - }) - } - if orderBook.Action == "insert" { - var newOB orderbook.Base - newOB.Asks = asks - newOB.Bids = bids - newOB.Asset = assetType - newOB.Pair = newPair - newOB.Exchange = c.Name - newOB.LastUpdated = time.Unix(orderBook.Data[0].Timestamp, 0) - newOB.VerifyOrderbook = c.CanVerifyOrderbook - err = c.Websocket.Orderbook.LoadSnapshot(&newOB) - if err != nil { - return err - } - } else if orderBook.Action == "update" { - newOB := buffer.Update{ - Asks: asks, - Bids: bids, - Asset: assetType, - Pair: newPair, - UpdateID: orderBook.Data[0].Version, - UpdateTime: time.Unix(orderBook.Data[0].Timestamp, 0), - } - err = c.Websocket.Orderbook.Update(&newOB) - if err != nil { - return err - } - } - case strings.Contains(result[topic].(string), "kline"): - var candleData WsKline - err = json.Unmarshal(respRaw, &candleData) - if err != nil { - return err - } - newPair, err = c.getCurrencyFromWsTopic(assetType, candleData.Topic) - if err != nil { - return err - } - - for i := range candleData.Data { - c.Websocket.DataHandler <- stream.KlineData{ - Pair: newPair, - AssetType: assetType, - Exchange: c.Name, - OpenPrice: candleData.Data[i].Open, - HighPrice: candleData.Data[i].High, - LowPrice: candleData.Data[i].Low, - ClosePrice: candleData.Data[i].Close, - Volume: candleData.Data[i].Volume, - Timestamp: time.Unix(candleData.Data[i].Timestamp, 0), - } - } - case strings.Contains(result[topic].(string), "user.account"): - var userInfo WsUserInfo - err = json.Unmarshal(respRaw, &userInfo) - if err != nil { - return err - } - c.Websocket.DataHandler <- userInfo - case strings.Contains(result[topic].(string), "user.position"): - var position WsPosition - err = json.Unmarshal(respRaw, &position) - if err != nil { - return err - } - c.Websocket.DataHandler <- position - case strings.Contains(result[topic].(string), "user.order"): - var orders WsUserOrders - err = json.Unmarshal(respRaw, &orders) - if err != nil { - return err - } - - format, err := c.GetPairFormat(assetType, true) - if err != nil { - return err - } - - pairs, err := c.GetEnabledPairs(assetType) - if err != nil { - return err - } - - for i := range orders.Data { - oType, err := order.StringToOrderType(orders.Data[i].OrderType) - if err != nil { - c.Websocket.DataHandler <- order.ClassificationError{ - Exchange: c.Name, - OrderID: orders.Data[i].OrderID, - Err: err, - } - } - oStatus, err := order.StringToOrderStatus(orders.Data[i].Status) - if err != nil { - c.Websocket.DataHandler <- order.ClassificationError{ - Exchange: c.Name, - OrderID: orders.Data[i].OrderID, - Err: err, - } - } - - newPair, err = currency.NewPairFromFormattedPairs(orders.Data[i].Symbol, - pairs, - format) - if err != nil { - return err - } - - c.Websocket.DataHandler <- &order.Detail{ - Price: orders.Data[i].OrderPrice, - Amount: orders.Data[i].Quantity, - ExecutedAmount: orders.Data[i].FilledQuantity, - RemainingAmount: orders.Data[i].Quantity - orders.Data[i].FilledQuantity, - Fee: orders.Data[i].Fee, - Exchange: c.Name, - ID: orders.Data[i].OrderID, - Type: oType, - Status: oStatus, - AssetType: assetType, - Date: orders.Data[i].OrderTime, - Leverage: float64(orders.Data[i].Leverage), - Pair: newPair, - } - } - default: - c.Websocket.DataHandler <- stream.UnhandledMessageWarning{ - Message: c.Name + stream.UnhandledMessage + string(respRaw), - } - return nil - } - return nil -} - -func (c *Coinbene) getCurrencyFromWsTopic(assetType asset.Item, channelTopic string) (cp currency.Pair, err error) { - var format currency.PairFormat - format, err = c.GetPairFormat(assetType, true) - if err != nil { - return cp, err - } - - var pairs currency.Pairs - pairs, err = c.GetEnabledPairs(assetType) - if err != nil { - return cp, err - } - // channel topics are formatted as "spot/orderbook.BTCUSDT" - channelSplit := strings.Split(channelTopic, ".") - if len(channelSplit) == 1 { - return currency.Pair{}, errors.New("no currency found in topic " + channelTopic) - } - cp, err = currency.MatchPairsWithNoDelimiter(channelSplit[1], pairs, format) - if err != nil { - return cp, err - } - if !pairs.Contains(cp, true) { - return cp, fmt.Errorf("currency %s not found in enabled pairs", cp.String()) - } - return cp, nil -} - -// Subscribe sends a websocket message to receive data from the channel -func (c *Coinbene) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { - if maxSubsPerHour := 240; len(channelsToSubscribe) > maxSubsPerHour { - return fmt.Errorf("channel subscriptions length %d exceeds coinbene's limit of %d, try reducing enabled pairs", - len(channelsToSubscribe), - maxSubsPerHour) - } - - var sub WsSub - sub.Operation = "subscribe" - // enabling all currencies can lead to a message too large being sent - // and no subscriptions being made - chanLimit := 15 - for i := range channelsToSubscribe { - if len(sub.Arguments) > chanLimit { - err := c.Websocket.Conn.SendJSONMessage(sub) - if err != nil { - return err - } - sub.Arguments = []string{} - } - sub.Arguments = append(sub.Arguments, channelsToSubscribe[i].Channel) - } - err := c.Websocket.Conn.SendJSONMessage(sub) - if err != nil { - return err - } - c.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe...) - return nil -} - -// Unsubscribe sends a websocket message to receive data from the channel -func (c *Coinbene) Unsubscribe(channelToUnsubscribe []stream.ChannelSubscription) error { - var unsub WsSub - unsub.Operation = "unsubscribe" - // enabling all currencies can lead to a message too large being sent - // and no unsubscribes being made - chanLimit := 15 - for i := range channelToUnsubscribe { - if len(unsub.Arguments) > chanLimit { - err := c.Websocket.Conn.SendJSONMessage(unsub) - if err != nil { - return err - } - unsub.Arguments = []string{} - } - unsub.Arguments = append(unsub.Arguments, channelToUnsubscribe[i].Channel) - } - err := c.Websocket.Conn.SendJSONMessage(unsub) - if err != nil { - return err - } - c.Websocket.RemoveSuccessfulUnsubscriptions(channelToUnsubscribe...) - return nil -} - -// Login logs in -func (c *Coinbene) Login() error { - var sub WsSub - expTime := time.Now().Add(time.Minute * 10).Format("2006-01-02T15:04:05Z") - signMsg := expTime + http.MethodGet + "/login" - - tempSign, err := crypto.GetHMAC(crypto.HashSHA256, - []byte(signMsg), - []byte(c.API.Credentials.Secret)) - if err != nil { - return err - } - - sign := crypto.HexEncodeToString(tempSign) - sub.Operation = "login" - sub.Arguments = []string{c.API.Credentials.Key, expTime, sign} - return c.Websocket.Conn.SendJSONMessage(sub) -} diff --git a/exchanges/coinbene/coinbene_wrapper.go b/exchanges/coinbene/coinbene_wrapper.go deleted file mode 100644 index 50b1544f197..00000000000 --- a/exchanges/coinbene/coinbene_wrapper.go +++ /dev/null @@ -1,1029 +0,0 @@ -package coinbene - -import ( - "context" - "errors" - "fmt" - "sort" - "strconv" - "strings" - "sync" - "time" - - "github.com/thrasher-corp/gocryptotrader/common" - "github.com/thrasher-corp/gocryptotrader/config" - "github.com/thrasher-corp/gocryptotrader/currency" - exchange "github.com/thrasher-corp/gocryptotrader/exchanges" - "github.com/thrasher-corp/gocryptotrader/exchanges/account" - "github.com/thrasher-corp/gocryptotrader/exchanges/asset" - "github.com/thrasher-corp/gocryptotrader/exchanges/deposit" - "github.com/thrasher-corp/gocryptotrader/exchanges/kline" - "github.com/thrasher-corp/gocryptotrader/exchanges/order" - "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" - "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" - "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/stream" - "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/trade" - "github.com/thrasher-corp/gocryptotrader/log" - "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" -) - -// GetDefaultConfig returns a default exchange config -func (c *Coinbene) GetDefaultConfig() (*config.Exchange, error) { - c.SetDefaults() - exchCfg := new(config.Exchange) - exchCfg.Name = c.Name - exchCfg.HTTPTimeout = exchange.DefaultHTTPTimeout - exchCfg.BaseCurrencies = c.BaseCurrencies - - err := c.SetupDefaults(exchCfg) - if err != nil { - return nil, err - } - - if c.Features.Supports.RESTCapabilities.AutoPairUpdates { - err = c.UpdateTradablePairs(context.TODO(), true) - if err != nil { - return nil, err - } - } - - return exchCfg, nil -} - -// SetDefaults sets the basic defaults for Coinbene -func (c *Coinbene) SetDefaults() { - c.Name = "Coinbene" - c.Enabled = true - c.Verbose = true - c.API.CredentialsValidator.RequiresKey = true - c.API.CredentialsValidator.RequiresSecret = true - - err := c.StoreAssetPairFormat(asset.Spot, currency.PairStore{ - RequestFormat: ¤cy.PairFormat{ - Uppercase: true, - Delimiter: currency.ForwardSlashDelimiter, - }, - ConfigFormat: ¤cy.PairFormat{ - Uppercase: true, - Delimiter: currency.ForwardSlashDelimiter, - }, - }) - if err != nil { - log.Errorln(log.ExchangeSys, err) - } - - err = c.StoreAssetPairFormat(asset.PerpetualSwap, currency.PairStore{ - RequestFormat: ¤cy.PairFormat{ - Uppercase: true, - Delimiter: currency.DashDelimiter, - }, - ConfigFormat: ¤cy.PairFormat{ - Uppercase: true, - Delimiter: currency.ForwardSlashDelimiter, - }, - }) - if err != nil { - log.Errorln(log.ExchangeSys, err) - } - - c.Features = exchange.Features{ - Supports: exchange.FeaturesSupported{ - REST: true, - Websocket: true, - RESTCapabilities: protocol.Features{ - TickerFetching: true, - TradeFetching: true, - OrderbookFetching: true, - AccountBalance: true, - AutoPairUpdates: true, - GetOrder: true, - GetOrders: true, - CancelOrder: true, - CancelOrders: true, - SubmitOrder: true, - TradeFee: true, - CryptoDeposit: true, - CryptoWithdrawal: true, - MultiChainDeposits: true, - MultiChainWithdrawals: true, - }, - WebsocketCapabilities: protocol.Features{ - TickerFetching: true, - AccountBalance: true, - AccountInfo: true, - OrderbookFetching: true, - TradeFetching: true, - KlineFetching: true, - Subscribe: true, - Unsubscribe: true, - AuthenticatedEndpoints: true, - GetOrders: true, - GetOrder: true, - }, - WithdrawPermissions: exchange.NoFiatWithdrawals | - exchange.WithdrawCryptoWithAPIPermission, - Kline: kline.ExchangeCapabilitiesSupported{ - DateRanges: true, - Intervals: true, - }, - }, - Enabled: exchange.FeaturesEnabled{ - AutoPairUpdates: true, - Kline: kline.ExchangeCapabilitiesEnabled{ - Intervals: map[string]bool{ - kline.OneMin.Word(): true, - kline.ThreeMin.Word(): true, - kline.FiveMin.Word(): true, - kline.FifteenMin.Word(): true, - kline.ThirtyMin.Word(): true, - kline.OneHour.Word(): true, - kline.TwoHour.Word(): true, - kline.FourHour.Word(): true, - kline.SixHour.Word(): true, - kline.TwelveHour.Word(): true, - kline.OneDay.Word(): true, - kline.ThreeDay.Word(): true, - kline.OneWeek.Word(): true, - }, - }, - }, - } - c.Requester = request.New(c.Name, - common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), - request.WithLimiter(SetRateLimit())) - c.API.Endpoints = c.NewEndpoints() - err = c.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ - exchange.RestSpot: coinbeneAPIURL, - exchange.RestSwap: coinbeneSwapAPIURL, - exchange.WebsocketSpot: wsContractURL, - }) - if err != nil { - log.Errorln(log.ExchangeSys, err) - } - c.Websocket = stream.New() - c.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit - c.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout - c.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit -} - -// Setup takes in the supplied exchange configuration details and sets params -func (c *Coinbene) Setup(exch *config.Exchange) error { - err := exch.Validate() - if err != nil { - return err - } - if !exch.Enabled { - c.SetEnabled(false) - return nil - } - err = c.SetupDefaults(exch) - if err != nil { - return err - } - - wsRunningURL, err := c.API.Endpoints.GetURL(exchange.WebsocketSpot) - if err != nil { - return err - } - - err = c.Websocket.Setup(&stream.WebsocketSetup{ - ExchangeConfig: exch, - DefaultURL: wsContractURL, - RunningURL: wsRunningURL, - Connector: c.WsConnect, - Subscriber: c.Subscribe, - Unsubscriber: c.Unsubscribe, - GenerateSubscriptions: c.GenerateDefaultSubscriptions, - Features: &c.Features.Supports.WebsocketCapabilities, - SortBuffer: true, - }) - if err != nil { - return err - } - - return c.Websocket.SetupNewConnection(stream.ConnectionSetup{ - ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, - ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - }) -} - -// Start starts the Coinbene go routine -func (c *Coinbene) Start(wg *sync.WaitGroup) error { - if wg == nil { - return fmt.Errorf("%T %w", wg, common.ErrNilPointer) - } - wg.Add(1) - go func() { - c.Run() - wg.Done() - }() - return nil -} - -// Run implements the Coinbene wrapper -func (c *Coinbene) Run() { - if c.Verbose { - log.Debugf(log.ExchangeSys, - "%s Websocket: %s. (url: %s).\n", - c.Name, - common.IsEnabled(c.Websocket.IsEnabled()), - c.Websocket.GetWebsocketURL(), - ) - c.PrintEnabledPairs() - } - - if !c.GetEnabledFeatures().AutoPairUpdates { - return - } - - err := c.UpdateTradablePairs(context.TODO(), false) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s Failed to update tradable pairs. Error: %s", - c.Name, - err) - } -} - -// FetchTradablePairs returns a list of exchange tradable pairs -func (c *Coinbene) FetchTradablePairs(ctx context.Context, a asset.Item) ([]string, error) { - if !c.SupportsAsset(a) { - return nil, fmt.Errorf("%s does not support asset type %s", c.Name, a) - } - - var currencies []string - switch a { - case asset.Spot: - pairs, err := c.GetAllPairs(ctx) - if err != nil { - return nil, err - } - - for x := range pairs { - currencies = append(currencies, pairs[x].Symbol) - } - case asset.PerpetualSwap: - instruments, err := c.GetSwapInstruments(ctx) - if err != nil { - return nil, err - } - pFmt, err := c.GetPairFormat(asset.PerpetualSwap, false) - if err != nil { - return nil, err - } - for x := range instruments { - currencies = append(currencies, - instruments[x].InstrumentID.Format(pFmt.Delimiter, pFmt.Uppercase).String()) - } - } - return currencies, nil -} - -// UpdateTradablePairs updates the exchanges available pairs and stores -// them -func (c *Coinbene) UpdateTradablePairs(ctx context.Context, forceUpdate bool) error { - assets := c.GetAssetTypes(false) - for x := range assets { - pairs, err := c.FetchTradablePairs(ctx, assets[x]) - if err != nil { - return err - } - - p, err := currency.NewPairsFromStrings(pairs) - if err != nil { - return err - } - - err = c.UpdatePairs(p, assets[x], false, forceUpdate) - if err != nil { - return err - } - } - return nil -} - -// UpdateTickers updates the ticker for all currency pairs of a given asset type -func (c *Coinbene) UpdateTickers(ctx context.Context, a asset.Item) error { - if !c.SupportsAsset(a) { - return fmt.Errorf("%s does not support asset type %s", c.Name, a) - } - - allPairs, err := c.GetEnabledPairs(a) - if err != nil { - return err - } - - switch a { - case asset.Spot: - tickers, err := c.GetTickers(ctx) - if err != nil { - return err - } - - for i := range tickers { - var newP currency.Pair - newP, err = currency.NewPairFromString(tickers[i].Symbol) - if err != nil { - return err - } - - if !allPairs.Contains(newP, true) { - continue - } - - err = ticker.ProcessTicker(&ticker.Price{ - Pair: newP, - Last: tickers[i].LatestPrice, - High: tickers[i].DailyHigh, - Low: tickers[i].DailyLow, - Bid: tickers[i].BestBid, - Ask: tickers[i].BestAsk, - Volume: tickers[i].DailyVolume, - ExchangeName: c.Name, - AssetType: a}) - if err != nil { - return err - } - } - case asset.PerpetualSwap: - tickers, err := c.GetSwapTickers(ctx) - if err != nil { - return err - } - - for x := range allPairs { - fpair, err := c.FormatExchangeCurrency(allPairs[x], a) - if err != nil { - return err - } - - tick, ok := tickers[fpair.String()] - if !ok { - log.Warnf(log.ExchangeSys, - "%s SWAP ticker item was not found", - c.Name) - continue - } - - err = ticker.ProcessTicker(&ticker.Price{ - Pair: allPairs[x], - Last: tick.LastPrice, - High: tick.High24Hour, - Low: tick.Low24Hour, - Bid: tick.BestBidPrice, - Ask: tick.BestAskPrice, - Volume: tick.Volume24Hour, - LastUpdated: tick.Timestamp, - ExchangeName: c.Name, - AssetType: a}) - if err != nil { - return err - } - } - } - return nil -} - -// UpdateTicker updates and returns the ticker for a currency pair -func (c *Coinbene) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) { - if err := c.UpdateTickers(ctx, a); err != nil { - return nil, err - } - return ticker.GetTicker(c.Name, p, a) -} - -// FetchTicker returns the ticker for a currency pair -func (c *Coinbene) FetchTicker(ctx context.Context, p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - if !c.SupportsAsset(assetType) { - return nil, - fmt.Errorf("%s does not support asset type %s", c.Name, assetType) - } - - tickerNew, err := ticker.GetTicker(c.Name, p, assetType) - if err != nil { - return c.UpdateTicker(ctx, p, assetType) - } - return tickerNew, nil -} - -// FetchOrderbook returns orderbook base on the currency pair -func (c *Coinbene) FetchOrderbook(ctx context.Context, p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { - if !c.SupportsAsset(assetType) { - return nil, - fmt.Errorf("%s does not support asset type %s", c.Name, assetType) - } - - ob, err := orderbook.Get(c.Name, p, assetType) - if err != nil { - return c.UpdateOrderbook(ctx, p, assetType) - } - return ob, nil -} - -// UpdateOrderbook updates and returns the orderbook for a currency pair -func (c *Coinbene) UpdateOrderbook(ctx context.Context, p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { - book := &orderbook.Base{ - Exchange: c.Name, - Pair: p, - Asset: assetType, - VerifyOrderbook: c.CanVerifyOrderbook, - } - if !c.SupportsAsset(assetType) { - return book, - fmt.Errorf("%s does not support asset type %s", c.Name, assetType) - } - - fpair, err := c.FormatExchangeCurrency(p, assetType) - if err != nil { - return book, err - } - - var tempResp Orderbook - switch assetType { - case asset.Spot: - tempResp, err = c.GetOrderbook(ctx, - fpair.String(), - 100, // TO-DO: Update this once we support configurable orderbook depth - ) - case asset.PerpetualSwap: - tempResp, err = c.GetSwapOrderbook(ctx, - fpair.String(), - 100, // TO-DO: Update this once we support configurable orderbook depth - ) - } - if err != nil { - return book, err - } - for x := range tempResp.Asks { - item := orderbook.Item{ - Price: tempResp.Asks[x].Price, - Amount: tempResp.Asks[x].Amount, - } - if assetType == asset.PerpetualSwap { - item.OrderCount = tempResp.Asks[x].Count - } - book.Asks = append(book.Asks, item) - } - for x := range tempResp.Bids { - item := orderbook.Item{ - Price: tempResp.Bids[x].Price, - Amount: tempResp.Bids[x].Amount, - } - if assetType == asset.PerpetualSwap { - item.OrderCount = tempResp.Bids[x].Count - } - book.Bids = append(book.Bids, item) - } - err = book.Process() - if err != nil { - return book, err - } - return orderbook.Get(c.Name, p, assetType) -} - -// UpdateAccountInfo retrieves balances for all enabled currencies for the -// Coinbene exchange -func (c *Coinbene) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (account.Holdings, error) { - var info account.Holdings - balance, err := c.GetAccountBalances(ctx) - if err != nil { - return info, err - } - var acc account.SubAccount - for key := range balance { - c := currency.NewCode(balance[key].Asset) - hold := balance[key].Reserved - available := balance[key].Available - acc.Currencies = append(acc.Currencies, - account.Balance{ - CurrencyName: c, - TotalValue: hold + available, - Hold: hold, - }) - } - info.Accounts = append(info.Accounts, acc) - info.Exchange = c.Name - - err = account.Process(&info) - if err != nil { - return account.Holdings{}, err - } - - return info, nil -} - -// FetchAccountInfo retrieves balances for all enabled currencies -func (c *Coinbene) FetchAccountInfo(ctx context.Context, assetType asset.Item) (account.Holdings, error) { - acc, err := account.GetHoldings(c.Name, assetType) - if err != nil { - return c.UpdateAccountInfo(ctx, assetType) - } - - return acc, nil -} - -// GetFundingHistory returns funding history, deposits and -// withdrawals -func (c *Coinbene) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, error) { - return nil, common.ErrFunctionNotSupported -} - -// GetWithdrawalsHistory returns previous withdrawals data -func (c *Coinbene) GetWithdrawalsHistory(_ context.Context, _ currency.Code) (resp []exchange.WithdrawalHistory, err error) { - return nil, common.ErrNotYetImplemented -} - -// GetRecentTrades returns the most recent trades for a currency and asset -func (c *Coinbene) GetRecentTrades(ctx context.Context, p currency.Pair, assetType asset.Item) ([]trade.Data, error) { - var err error - p, err = c.FormatExchangeCurrency(p, assetType) - if err != nil { - return nil, err - } - var tradeData Trades - tradeData, err = c.GetTrades(ctx, p.String(), 100) - if err != nil { - return nil, err - } - var resp []trade.Data - for i := range tradeData { - side := order.Buy - if tradeData[i].Direction == "sell" { - side = order.Sell - } - resp = append(resp, trade.Data{ - Exchange: c.Name, - CurrencyPair: p, - AssetType: assetType, - Side: side, - Price: tradeData[i].Price, - Amount: tradeData[i].Volume, - Timestamp: tradeData[i].TradeTime, - }) - } - - err = c.AddTradesToBuffer(resp...) - if err != nil { - return nil, err - } - - sort.Sort(trade.ByDate(resp)) - return resp, nil -} - -// GetHistoricTrades returns historic trade data within the timeframe provided -func (c *Coinbene) GetHistoricTrades(_ context.Context, _ currency.Pair, _ asset.Item, _, _ time.Time) ([]trade.Data, error) { - return nil, common.ErrFunctionNotSupported -} - -// SubmitOrder submits a new order -func (c *Coinbene) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - var resp order.SubmitResponse - if err := s.Validate(); err != nil { - return resp, err - } - - if s.Side != order.Buy && s.Side != order.Sell { - return resp, - fmt.Errorf("%s orderside is not supported by this exchange", - s.Side) - } - - fpair, err := c.FormatExchangeCurrency(s.Pair, asset.Spot) - if err != nil { - return resp, err - } - - tempResp, err := c.PlaceSpotOrder(ctx, - s.Price, - s.Amount, - fpair.String(), - s.Side.String(), - s.Type.String(), - s.ClientID, - 0) - if err != nil { - return resp, err - } - resp.IsOrderPlaced = true - resp.OrderID = tempResp.OrderID - return resp, nil -} - -// ModifyOrder will allow of changing orderbook placement and limit to -// market conversion -func (c *Coinbene) ModifyOrder(ctx context.Context, action *order.Modify) (order.Modify, error) { - return order.Modify{}, common.ErrFunctionNotSupported -} - -// CancelOrder cancels an order by its corresponding ID number -func (c *Coinbene) CancelOrder(ctx context.Context, o *order.Cancel) error { - if err := o.Validate(o.StandardCancel()); err != nil { - return err - } - _, err := c.CancelSpotOrder(ctx, o.ID) - return err -} - -// CancelBatchOrders cancels an orders by their corresponding ID numbers -func (c *Coinbene) CancelBatchOrders(ctx context.Context, o []order.Cancel) (order.CancelBatchResponse, error) { - return order.CancelBatchResponse{}, common.ErrNotYetImplemented -} - -// CancelAllOrders cancels all orders associated with a currency pair -func (c *Coinbene) CancelAllOrders(ctx context.Context, orderCancellation *order.Cancel) (order.CancelAllResponse, error) { - if err := orderCancellation.Validate(); err != nil { - return order.CancelAllResponse{}, err - } - - var resp order.CancelAllResponse - fpair, err := c.FormatExchangeCurrency(orderCancellation.Pair, - orderCancellation.AssetType) - if err != nil { - return resp, err - } - - orders, err := c.FetchOpenSpotOrders(ctx, fpair.String()) - if err != nil { - return resp, err - } - - tempMap := make(map[string]string) - for x := range orders { - _, err := c.CancelSpotOrder(ctx, orders[x].OrderID) - if err != nil { - tempMap[orders[x].OrderID] = "Failed" - } else { - tempMap[orders[x].OrderID] = "Success" - } - } - resp.Status = tempMap - return resp, nil -} - -// GetOrderInfo returns order information based on order ID -func (c *Coinbene) GetOrderInfo(ctx context.Context, orderID string, pair currency.Pair, assetType asset.Item) (order.Detail, error) { - var resp order.Detail - tempResp, err := c.FetchSpotOrderInfo(ctx, orderID) - if err != nil { - return resp, err - } - resp.Exchange = c.Name - resp.ID = orderID - resp.Pair = currency.NewPairWithDelimiter(tempResp.BaseAsset, - "/", - tempResp.QuoteAsset) - resp.Price = tempResp.OrderPrice - resp.Date = tempResp.OrderTime - resp.ExecutedAmount = tempResp.FilledAmount - resp.Fee = tempResp.TotalFee - return resp, nil -} - -// GetDepositAddress returns a deposit address for a specified currency -func (c *Coinbene) GetDepositAddress(ctx context.Context, curr currency.Code, _, chain string) (*deposit.Address, error) { - d, err := c.ListDepositAddress(ctx, curr) - if err != nil { - return nil, err - } - - if len(d) == 0 { - return nil, errors.New("no address(es) returned, please create one via the Coinbene website") - } - - if chain != "" { - for x := range d { - if strings.EqualFold(d[x].Chain, chain) { - return &deposit.Address{Address: d[x].Address, Tag: d[x].AddressTag}, nil - } - } - return nil, fmt.Errorf("no chain %s found", chain) - } - return &deposit.Address{Address: d[0].Address, Tag: d[0].AddressTag}, nil -} - -// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is -// submitted -func (c *Coinbene) WithdrawCryptocurrencyFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) { - if err := withdrawRequest.Validate(); err != nil { - return nil, err - } - - resp, err := c.Withdraw(ctx, - withdrawRequest.Currency, - withdrawRequest.Crypto.Address, - withdrawRequest.Crypto.AddressTag, - withdrawRequest.Crypto.Chain, - withdrawRequest.Amount) - if err != nil { - return nil, err - } - - return &withdraw.ExchangeResponse{ - ID: resp.ID, - }, err -} - -// WithdrawFiatFunds returns a withdrawal ID when a withdrawal is -// submitted -func (c *Coinbene) WithdrawFiatFunds(_ context.Context, _ *withdraw.Request) (*withdraw.ExchangeResponse, error) { - return nil, common.ErrFunctionNotSupported -} - -// WithdrawFiatFundsToInternationalBank returns a withdrawal ID when a withdrawal is -// submitted -func (c *Coinbene) WithdrawFiatFundsToInternationalBank(_ context.Context, _ *withdraw.Request) (*withdraw.ExchangeResponse, error) { - return nil, common.ErrFunctionNotSupported -} - -// GetActiveOrders retrieves any orders that are active/open -func (c *Coinbene) GetActiveOrders(ctx context.Context, getOrdersRequest *order.GetOrdersRequest) ([]order.Detail, error) { - if err := getOrdersRequest.Validate(); err != nil { - return nil, err - } - - if len(getOrdersRequest.Pairs) == 0 { - allPairs, err := c.GetAllPairs(ctx) - if err != nil { - return nil, err - } - for a := range allPairs { - p, err := currency.NewPairFromString(allPairs[a].Symbol) - if err != nil { - return nil, err - } - getOrdersRequest.Pairs = append(getOrdersRequest.Pairs, p) - } - } - - var resp []order.Detail - for x := range getOrdersRequest.Pairs { - fpair, err := c.FormatExchangeCurrency(getOrdersRequest.Pairs[x], - asset.Spot) - if err != nil { - return nil, err - } - - var tempData OrdersInfo - tempData, err = c.FetchOpenSpotOrders(ctx, fpair.String()) - if err != nil { - return nil, err - } - - for y := range tempData { - var tempResp order.Detail - tempResp.Exchange = c.Name - tempResp.Pair = getOrdersRequest.Pairs[x] - tempResp.Side = order.Buy - if strings.EqualFold(tempData[y].OrderType, order.Sell.String()) { - tempResp.Side = order.Sell - } - tempResp.Date = tempData[y].OrderTime - if tempResp.Status, err = order.StringToOrderStatus(tempData[y].OrderStatus); err != nil { - log.Errorf(log.ExchangeSys, "%s %v", c.Name, err) - } - tempResp.Price = tempData[y].OrderPrice - tempResp.Amount = tempData[y].Amount - tempResp.ExecutedAmount = tempData[y].FilledAmount - tempResp.RemainingAmount = tempData[y].Amount - tempData[y].FilledAmount - tempResp.Fee = tempData[y].TotalFee - resp = append(resp, tempResp) - } - } - return resp, nil -} - -// GetOrderHistory retrieves account order information -// Can Limit response to specific order status -func (c *Coinbene) GetOrderHistory(ctx context.Context, getOrdersRequest *order.GetOrdersRequest) ([]order.Detail, error) { - if err := getOrdersRequest.Validate(); err != nil { - return nil, err - } - - if len(getOrdersRequest.Pairs) == 0 { - allPairs, err := c.GetAllPairs(ctx) - if err != nil { - return nil, err - } - - for a := range allPairs { - p, err := currency.NewPairFromString(allPairs[a].Symbol) - if err != nil { - return nil, err - } - getOrdersRequest.Pairs = append(getOrdersRequest.Pairs, p) - } - } - - var resp []order.Detail - var tempData OrdersInfo - for x := range getOrdersRequest.Pairs { - fpair, err := c.FormatExchangeCurrency(getOrdersRequest.Pairs[x], - asset.Spot) - if err != nil { - return nil, err - } - - tempData, err = c.FetchClosedOrders(ctx, fpair.String(), "") - if err != nil { - return nil, err - } - - for y := range tempData { - var tempResp order.Detail - tempResp.Exchange = c.Name - tempResp.Pair = getOrdersRequest.Pairs[x] - tempResp.Side = order.Buy - if strings.EqualFold(tempData[y].OrderType, order.Sell.String()) { - tempResp.Side = order.Sell - } - tempResp.Date = tempData[y].OrderTime - if tempResp.Status, err = order.StringToOrderStatus(tempData[y].OrderStatus); err != nil { - log.Errorf(log.ExchangeSys, "%s %v", c.Name, err) - } - tempResp.Price = tempData[y].OrderPrice - tempResp.AverageExecutedPrice = tempData[y].AvgPrice - tempResp.Amount = tempData[y].Amount - tempResp.ExecutedAmount = tempData[y].FilledAmount - tempResp.RemainingAmount = tempData[y].Amount - tempData[y].FilledAmount - tempResp.Cost = tempData[y].Quantity - tempResp.CostAsset = tempResp.Pair.Quote - tempResp.Fee = tempData[y].TotalFee - tempResp.InferCostsAndTimes() - resp = append(resp, tempResp) - } - } - return resp, nil -} - -// GetFeeByType returns an estimate of fee based on the type of transaction -func (c *Coinbene) GetFeeByType(ctx context.Context, feeBuilder *exchange.FeeBuilder) (float64, error) { - if feeBuilder == nil { - return 0, fmt.Errorf("%T %w", feeBuilder, common.ErrNilPointer) - } - fpair, err := c.FormatExchangeCurrency(feeBuilder.Pair, asset.Spot) - if err != nil { - return 0, err - } - - tempData, err := c.GetPairInfo(ctx, fpair.String()) - if err != nil { - return 0, err - } - - if feeBuilder.IsMaker { - return feeBuilder.PurchasePrice * feeBuilder.Amount * tempData.MakerFeeRate, nil - } - return feeBuilder.PurchasePrice * feeBuilder.Amount * tempData.TakerFeeRate, nil -} - -// AuthenticateWebsocket sends an authentication message to the websocket -func (c *Coinbene) AuthenticateWebsocket(_ context.Context) error { - return c.Login() -} - -// ValidateCredentials validates current credentials used for wrapper -// functionality -func (c *Coinbene) ValidateCredentials(ctx context.Context, assetType asset.Item) error { - _, err := c.UpdateAccountInfo(ctx, assetType) - return c.CheckTransientError(err) -} - -// FormatExchangeKlineInterval returns Interval to string -func (c *Coinbene) FormatExchangeKlineInterval(in kline.Interval) string { - switch in { - case kline.OneMin, kline.ThreeMin, kline.FiveMin, kline.FifteenMin, - kline.ThirtyMin, kline.OneHour, kline.TwoHour, kline.FourHour, kline.SixHour, kline.TwelveHour: - return strconv.FormatFloat(in.Duration().Minutes(), 'f', 0, 64) - case kline.OneDay: - return "D" - case kline.OneWeek: - return "W" - case kline.OneMonth: - return "M" - } - return "" -} - -// GetHistoricCandles returns candles between a time period for a set time interval -func (c *Coinbene) GetHistoricCandles(ctx context.Context, pair currency.Pair, a asset.Item, start, end time.Time, interval kline.Interval) (kline.Item, error) { - if err := c.ValidateKline(pair, a, interval); err != nil { - return kline.Item{}, err - } - - formattedPair, err := c.FormatExchangeCurrency(pair, asset.PerpetualSwap) - if err != nil { - return kline.Item{}, err - } - - var candles CandleResponse - if a == asset.PerpetualSwap { - candles, err = c.GetSwapKlines(ctx, - formattedPair.String(), - start, end, - c.FormatExchangeKlineInterval(interval)) - } else { - candles, err = c.GetKlines(ctx, - formattedPair.String(), - start, end, - c.FormatExchangeKlineInterval(interval)) - } - if err != nil { - return kline.Item{}, err - } - - ret := kline.Item{ - Exchange: c.Name, - Pair: pair, - Interval: interval, - Asset: a, - } - - for x := range candles.Data { - if len(candles.Data[x]) < 6 { - return kline.Item{}, errors.New("unexpected candle data length") - } - var tempCandle kline.Candle - tempTime, ok := candles.Data[x][0].(string) - if !ok { - return kline.Item{}, errors.New("timestamp conversion failed") - } - timestamp, err := time.Parse(time.RFC3339, tempTime) - if err != nil { - return kline.Item{}, err - } - tempCandle.Time = timestamp - open, ok := candles.Data[x][1].(string) - if !ok { - return kline.Item{}, errors.New("open conversion failed") - } - tempCandle.Open, err = strconv.ParseFloat(open, 64) - if err != nil { - return kline.Item{}, err - } - high, ok := candles.Data[x][2].(string) - if !ok { - return kline.Item{}, errors.New("high conversion failed") - } - tempCandle.High, err = strconv.ParseFloat(high, 64) - if err != nil { - return kline.Item{}, err - } - - low, ok := candles.Data[x][3].(string) - if !ok { - return kline.Item{}, errors.New("low conversion failed") - } - tempCandle.Low, err = strconv.ParseFloat(low, 64) - if err != nil { - return kline.Item{}, err - } - - closeTemp, ok := candles.Data[x][4].(string) - if !ok { - return kline.Item{}, errors.New("close conversion failed") - } - tempCandle.Close, err = strconv.ParseFloat(closeTemp, 64) - if err != nil { - return kline.Item{}, err - } - - vol, ok := candles.Data[x][5].(string) - if !ok { - return kline.Item{}, errors.New("vol conversion failed") - } - tempCandle.Volume, err = strconv.ParseFloat(vol, 64) - if err != nil { - return kline.Item{}, err - } - - ret.Candles = append(ret.Candles, tempCandle) - } - - ret.SortCandlesByTimestamp(false) - return ret, nil -} - -// GetHistoricCandlesExtended returns candles between a time period for a set time interval -func (c *Coinbene) GetHistoricCandlesExtended(ctx context.Context, pair currency.Pair, a asset.Item, start, end time.Time, interval kline.Interval) (kline.Item, error) { - return c.GetHistoricCandles(ctx, pair, a, start, end, interval) -} - -// GetAvailableTransferChains returns the available transfer blockchains for the specific -// cryptocurrency -func (c *Coinbene) GetAvailableTransferChains(ctx context.Context, cryptocurrency currency.Code) ([]string, error) { - r, err := c.ListDepositAddress(ctx, cryptocurrency) - if err != nil { - return nil, err - } - - var availableChains []string - for x := range r { - if r[x].Chain != "" { - availableChains = append(availableChains, r[x].Chain) - } - } - return availableChains, nil -} diff --git a/exchanges/coinbene/ratelimit.go b/exchanges/coinbene/ratelimit.go deleted file mode 100644 index 0d2cf4102b8..00000000000 --- a/exchanges/coinbene/ratelimit.go +++ /dev/null @@ -1,259 +0,0 @@ -package coinbene - -import ( - "context" - "errors" - "time" - - "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "golang.org/x/time/rate" -) - -const ( - // Contract rate limit time interval and request rates - contractRateInterval = time.Second * 2 - orderbookContractReqRate = 20 - tickersContractReqRate = 20 - klineContractReqRate = 20 - tradesContractReqRate = 20 - contractInstrumentsReqRate = 20 - contractAccountInfoContractReqRate = 10 - positionInfoContractReqRate = 10 - placeOrderContractReqRate = 20 - cancelOrderContractReqRate = 20 - getOpenOrdersContractReqRate = 5 - openOrdersByPageContractReqRate = 5 - getOrderInfoContractReqRate = 10 - getClosedOrdersContractReqRate = 5 - getClosedOrdersbyPageContractReqRate = 5 - cancelMultipleOrdersContractReqRate = 5 - getOrderFillsContractReqRate = 10 - getFundingRatesContractReqRate = 10 - - // Spot rate limit time interval and request rates - spotRateInterval = time.Second - getPairsSpotReqRate = 2 - getPairsInfoSpotReqRate = 3 - getOrderbookSpotReqRate = 6 - getTickerListSpotReqRate = 6 - getSpecificTickerSpotReqRate = 6 - getMarketTradesSpotReqRate = 3 - // getKlineSpotReqRate = 1 - // getExchangeRateSpotReqRate = 1 - getAccountInfoSpotReqRate = 3 - queryAccountAssetInfoSpotReqRate = 6 - placeOrderSpotReqRate = 6 - batchOrderSpotReqRate = 3 - queryOpenOrdersSpotReqRate = 3 - queryClosedOrdersSpotReqRate = 3 - querySpecficOrderSpotReqRate = 6 - queryTradeFillsSpotReqRate = 3 - cancelOrderSpotReqRate = 6 - cancelOrdersBatchSpotReqRate = 3 - capitalDepositReqRate = 1 - capitalWithdrawReqRate = 1 - - // Rate limit functionality - contractOrderbook request.EndpointLimit = iota - contractTickers - contractKline - contractTrades - contractInstruments - contractAccountInfo - contractPositionInfo - contractPlaceOrder - contractCancelOrder - contractGetOpenOrders - contractOpenOrdersByPage - contractGetOrderInfo - contractGetClosedOrders - contractGetClosedOrdersbyPage - contractCancelMultipleOrders - contractGetOrderFills - contractGetFundingRates - - spotPairs - spotPairInfo - spotOrderbook - spotTickerList - spotSpecificTicker - spotMarketTrades - spotKline // Not implemented yet - spotExchangeRate // Not implemented yet - spotAccountInfo - spotAccountAssetInfo - spotPlaceOrder - spotBatchOrder - spotQueryOpenOrders - spotQueryClosedOrders - spotQuerySpecficOrder - spotQueryTradeFills - spotCancelOrder - spotCancelOrdersBatch - capitalDeposit - capitalWithdraw -) - -// RateLimit implements the request.Limiter interface -type RateLimit struct { - ContractOrderbook *rate.Limiter - ContractTickers *rate.Limiter - ContractKline *rate.Limiter - ContractTrades *rate.Limiter - ContractInstruments *rate.Limiter - ContractAccountInfo *rate.Limiter - ContractPositionInfo *rate.Limiter - ContractPlaceOrder *rate.Limiter - ContractCancelOrder *rate.Limiter - ContractGetOpenOrders *rate.Limiter - ContractOpenOrdersByPage *rate.Limiter - ContractGetOrderInfo *rate.Limiter - ContractGetClosedOrders *rate.Limiter - ContractGetClosedOrdersbyPage *rate.Limiter - ContractCancelMultipleOrders *rate.Limiter - ContractGetOrderFills *rate.Limiter - ContractGetFundingRates *rate.Limiter - SpotPairs *rate.Limiter - SpotPairInfo *rate.Limiter - SpotOrderbook *rate.Limiter - SpotTickerList *rate.Limiter - SpotSpecificTicker *rate.Limiter - SpotMarketTrades *rate.Limiter - // spotKline // Not implemented yet - // spotExchangeRate // Not implemented yet - SpotAccountInfo *rate.Limiter - SpotAccountAssetInfo *rate.Limiter - SpotPlaceOrder *rate.Limiter - SpotBatchOrder *rate.Limiter - SpotQueryOpenOrders *rate.Limiter - SpotQueryClosedOrders *rate.Limiter - SpotQuerySpecficOrder *rate.Limiter - SpotQueryTradeFills *rate.Limiter - SpotCancelOrder *rate.Limiter - SpotCancelOrdersBatch *rate.Limiter - CapitalDeposit *rate.Limiter - CapitalWithdraw *rate.Limiter -} - -// Limit limits outbound requests -func (r *RateLimit) Limit(ctx context.Context, f request.EndpointLimit) error { - switch f { - case contractOrderbook: - return r.ContractOrderbook.Wait(ctx) - case contractTickers: - return r.ContractTickers.Wait(ctx) - case contractKline: - return r.ContractKline.Wait(ctx) - case contractTrades: - return r.ContractTrades.Wait(ctx) - case contractInstruments: - return r.ContractInstruments.Wait(ctx) - case contractAccountInfo: - return r.ContractAccountInfo.Wait(ctx) - case contractPositionInfo: - return r.ContractPositionInfo.Wait(ctx) - case contractPlaceOrder: - return r.ContractPlaceOrder.Wait(ctx) - case contractCancelOrder: - return r.ContractCancelOrder.Wait(ctx) - case contractGetOpenOrders: - return r.ContractGetOpenOrders.Wait(ctx) - case contractOpenOrdersByPage: - return r.ContractOpenOrdersByPage.Wait(ctx) - case contractGetOrderInfo: - return r.ContractGetOrderInfo.Wait(ctx) - case contractGetClosedOrders: - return r.ContractGetClosedOrders.Wait(ctx) - case contractGetClosedOrdersbyPage: - return r.ContractGetClosedOrdersbyPage.Wait(ctx) - case contractCancelMultipleOrders: - return r.ContractCancelMultipleOrders.Wait(ctx) - case contractGetOrderFills: - return r.ContractGetOrderFills.Wait(ctx) - case contractGetFundingRates: - return r.ContractGetFundingRates.Wait(ctx) - case spotPairs: - return r.SpotPairs.Wait(ctx) - case spotPairInfo: - return r.SpotPairInfo.Wait(ctx) - case spotOrderbook: - return r.SpotOrderbook.Wait(ctx) - case spotTickerList: - return r.SpotTickerList.Wait(ctx) - case spotSpecificTicker: - return r.SpotSpecificTicker.Wait(ctx) - case spotMarketTrades: - return r.SpotMarketTrades.Wait(ctx) - case capitalDeposit: - return r.CapitalDeposit.Wait(ctx) - case capitalWithdraw: - return r.CapitalWithdraw.Wait(ctx) - // case spotKline: // Not implemented yet - // return r.SpotKline.Wait(ctx) - // case spotExchangeRate: - // return r.SpotExchangeRate.Wait(ctx) - case spotAccountInfo: - return r.SpotAccountInfo.Wait(ctx) - case spotAccountAssetInfo: - return r.SpotAccountAssetInfo.Wait(ctx) - case spotPlaceOrder: - return r.SpotPlaceOrder.Wait(ctx) - case spotBatchOrder: - return r.SpotBatchOrder.Wait(ctx) - case spotQueryOpenOrders: - return r.SpotQueryOpenOrders.Wait(ctx) - case spotQueryClosedOrders: - return r.SpotQueryClosedOrders.Wait(ctx) - case spotQuerySpecficOrder: - return r.SpotQuerySpecficOrder.Wait(ctx) - case spotQueryTradeFills: - return r.SpotQueryTradeFills.Wait(ctx) - case spotCancelOrder: - return r.SpotCancelOrder.Wait(ctx) - case spotCancelOrdersBatch: - return r.SpotCancelOrdersBatch.Wait(ctx) - default: - return errors.New("rate limit endpoint functionality not set") - } -} - -// SetRateLimit returns the rate limit for the exchange -func SetRateLimit() *RateLimit { - return &RateLimit{ - ContractOrderbook: request.NewRateLimit(contractRateInterval, orderbookContractReqRate), - ContractTickers: request.NewRateLimit(contractRateInterval, tickersContractReqRate), - ContractKline: request.NewRateLimit(contractRateInterval, klineContractReqRate), - ContractTrades: request.NewRateLimit(contractRateInterval, tradesContractReqRate), - ContractInstruments: request.NewRateLimit(contractRateInterval, contractInstrumentsReqRate), - ContractAccountInfo: request.NewRateLimit(contractRateInterval, contractAccountInfoContractReqRate), - ContractPositionInfo: request.NewRateLimit(contractRateInterval, positionInfoContractReqRate), - ContractPlaceOrder: request.NewRateLimit(contractRateInterval, placeOrderContractReqRate), - ContractCancelOrder: request.NewRateLimit(contractRateInterval, cancelOrderContractReqRate), - ContractGetOpenOrders: request.NewRateLimit(contractRateInterval, getOpenOrdersContractReqRate), - ContractOpenOrdersByPage: request.NewRateLimit(contractRateInterval, openOrdersByPageContractReqRate), - ContractGetOrderInfo: request.NewRateLimit(contractRateInterval, getOrderInfoContractReqRate), - ContractGetClosedOrders: request.NewRateLimit(contractRateInterval, getClosedOrdersContractReqRate), - ContractGetClosedOrdersbyPage: request.NewRateLimit(contractRateInterval, getClosedOrdersbyPageContractReqRate), - ContractCancelMultipleOrders: request.NewRateLimit(contractRateInterval, cancelMultipleOrdersContractReqRate), - ContractGetOrderFills: request.NewRateLimit(contractRateInterval, getOrderFillsContractReqRate), - ContractGetFundingRates: request.NewRateLimit(contractRateInterval, getFundingRatesContractReqRate), - SpotPairs: request.NewRateLimit(spotRateInterval, getPairsSpotReqRate), - SpotPairInfo: request.NewRateLimit(spotRateInterval, getPairsInfoSpotReqRate), - SpotOrderbook: request.NewRateLimit(spotRateInterval, getOrderbookSpotReqRate), - SpotTickerList: request.NewRateLimit(spotRateInterval, getTickerListSpotReqRate), - SpotSpecificTicker: request.NewRateLimit(spotRateInterval, getSpecificTickerSpotReqRate), - SpotMarketTrades: request.NewRateLimit(spotRateInterval, getMarketTradesSpotReqRate), - SpotAccountInfo: request.NewRateLimit(spotRateInterval, getAccountInfoSpotReqRate), - SpotAccountAssetInfo: request.NewRateLimit(spotRateInterval, queryAccountAssetInfoSpotReqRate), - SpotPlaceOrder: request.NewRateLimit(spotRateInterval, placeOrderSpotReqRate), - SpotBatchOrder: request.NewRateLimit(spotRateInterval, batchOrderSpotReqRate), - SpotQueryOpenOrders: request.NewRateLimit(spotRateInterval, queryOpenOrdersSpotReqRate), - SpotQueryClosedOrders: request.NewRateLimit(spotRateInterval, queryClosedOrdersSpotReqRate), - SpotQuerySpecficOrder: request.NewRateLimit(spotRateInterval, querySpecficOrderSpotReqRate), - SpotQueryTradeFills: request.NewRateLimit(spotRateInterval, queryTradeFillsSpotReqRate), - SpotCancelOrder: request.NewRateLimit(spotRateInterval, cancelOrderSpotReqRate), - SpotCancelOrdersBatch: request.NewRateLimit(spotRateInterval, cancelOrdersBatchSpotReqRate), - CapitalDeposit: request.NewRateLimit(spotRateInterval, capitalDepositReqRate), - CapitalWithdraw: request.NewRateLimit(spotRateInterval, capitalWithdrawReqRate), - } -} diff --git a/exchanges/support.go b/exchanges/support.go index ef165c5c294..c5f9abb748c 100644 --- a/exchanges/support.go +++ b/exchanges/support.go @@ -24,7 +24,6 @@ var Exchanges = []string{ "btc markets", "btse", "coinbasepro", - "coinbene", "coinut", "exmo", "ftx", diff --git a/exchanges/trade/README.md b/exchanges/trade/README.md index cf7fb370d75..9f5ccec0f4c 100644 --- a/exchanges/trade/README.md +++ b/exchanges/trade/README.md @@ -69,7 +69,6 @@ _b in this context is an `IBotExchange` implemented struct_ | Bittrex | Yes | Yes | No | | BTCMarkets | Yes | Yes | No | | BTSE | Yes | Yes | No | -| Coinbene | Yes | Yes | No | | CoinbasePro | Yes | Yes | No| | COINUT | Yes | Yes | No | | Exmo | Yes | NA | No | diff --git a/testdata/configtest.json b/testdata/configtest.json index 44383090cc3..42996a2e0c9 100644 --- a/testdata/configtest.json +++ b/testdata/configtest.json @@ -2267,95 +2267,6 @@ } ] }, - { - "name": "Coinbene", - "enabled": true, - "verbose": false, - "httpTimeout": 15000000000, - "websocketResponseCheckTimeout": 30000000, - "websocketResponseMaxLimit": 7000000000, - "websocketTrafficTimeout": 30000000000, - "websocketOrderbookBufferLimit": 5, - "baseCurrencies": "USD", - "currencyPairs": { - "assetTypes": [ - "spot", - "perpetualswap" - ], - "pairs": { - "perpetualswap": { - "enabled": "BTC/SWAP", - "available": "YFI/SWAP,SUSHI/SWAP,TRX/SWAP,ETC/SWAP,XRP/SWAP,LTC/SWAP,EOS/SWAP,FIL/SWAP,UNI/SWAP,DOT/SWAP,LINK/SWAP,BSV/SWAP,BCH/SWAP,ETH/SWAP,BTC/SWAP", - "requestFormat": { - "uppercase": true, - "delimiter": "-" - }, - "configFormat": { - "uppercase": true, - "delimiter": "/" - } - }, - "spot": { - "enabled": "BTC/USDT,GOM2/USDT", - "available": "ABBC/BTC,ABBC/USDT,ABT/ETH,ABT/USDT,ABYSS/ETH,ACDC/BTC,ACDC/USDT,ADI/ETH,ADK/BTC,ADN/BTC,AE/BTC,AE/USDT,AIDOC/BTC,AION/BTC,AIPE/USDT,AIT/USDT,ALI/ETH,ALX/ETH,APL/ETH,ATX/BTC,BAAS/BTC,BABA/USDT,BAT/BTC,BCH/USDT,BETHER/ETH,BEZ/BTC,BGC/USDT,BKG/BTC,BNB/USDT,BNT/BTC,BOA/USDT,BSTN/ETH,BSV/USDT,BTC/USDT,BTNT/BTC,BTSC/BTC,BTT/USDT,BU/ETH,BVT/ETH,CAN/ETH,CCC/ETH,CCE/USDT,CEDEX/ETH,CENT/BTC,CFT/USDT,CMT/ETH,CMT/USDT,CNN/BTC,CNN/ETH,CNN/USDT,CONI/USDT,COSM/BTC,COSM/ETH,CPC/BTC,CREDO/ETH,CRN/BTC,CSCC/USDT,CS/ETH,CS/USDT,CTXC/ETH,CUST/USDT,CVC/BTC,CXP/BTC,DDAM/ETH,DDAM/USDT,DENT/BTC,DGD/BTC,DSCB/USDT,DTA/ETH,DUC/BTC,DVC/ETH,EBC/BTC,EBC/ETH,EBC/USDT,ECP/BTC,EDC/BTC,EDR/ETH,ELF/BTC,EMT/USDT,EOS/BTC,EOS/USDT,EQUAD/BTC,ETC/BTC,ETC/USDT,ETH/BTC,ETH/USDT,ETK/BTC,FAB/ETH,FCC/BTC,FND/ETH,FNKOS/ETH,FTN/BTC,FTN/USDT,FTT/BTC,FXT/ETH,GDC/BTC,GDC/ETH,GDC/USDT,GETX/ETH,GOM2/USDT,GRAM/USDT,GRN/BTC,GUSD/USDT,GVT/BTC,HAPPY/BTC,HDAC/BTC,HMB/USDT,HNB/USDT,HPT/ETH,HT/USDT,HUP/USDT,INCX/ETH,IOST/BTC,IOTE/USDT,ISR/ETH,IVY/ETH,JOB/BTC,KBC/BTC,KBC/USDT,KMD/BTC,KNT/ETH,KST/BTC,LAMB/USDT,LATX/BTC,LBK/BTC,LINK/BTC,LOOM/BTC,LTC/BTC,LTC/USDT,LUC/ETH,LUX/BTC,LVTC/ETH,MC/USDT,MIB/BTC,MINX/BTC,MINX/ETH,MOAC/USDT,MPL/BTC,MTC/BTC,MT/ETH,MTN/ETH,MT/USDT,MVL/ETH,MXM/ETH,MXM/USDT,MZG/USDT,NANO/BTC,NBAI/ETH,NEO/BTC,NEO/USDT,NFT/USDT,NOBS/BTC,NPXS/ETH,NPXS/USDT,NTY/ETH,ODC/USDT,OMG/BTC,OMX/ETH,OVC/ETH,OZX/ETH,PAT/ETH,PAX/USDT,PLAY/BTC,PMA/ETH,POLL/BTC,POLY/BTC,PPT/BTC,PSM/BTC,QKC/BTC,QTUM/BTC,QTUM/USDT,RBTC/BTC,RCOIN/BTC,RCOIN/USDT,REP/BTC,REV/BTC,RIF/BTC,RRW/USDT,SBT/USDT,SCC/BTC,SCO/BTC,SEN/BTC,SENC/ETH,SHE/BTC,SHVR/BTC,SIM/BTC,SKB/BTC,SKM/ETH,SKYM/USDT,SLT/ETH,SMARTUP/ETH,SMARTUP/USDT,SORO/USDT,SRCOIN/BTC,SRCOIN/ETH,STORJ/BTC,SWET/BTC,SWTC/USDT,TCT/BTC,TEMCO/USDT,TEN/BTC,TEN/ETH,TIB/BTC,TMTG/BTC,TOC/ETH,TOOS/USDT,TOSC/BTC,TRUE/ETH,TRX/BTC,TRX/USDT,TSL/BTC,UNI/USDT,UTNP/BTC,VBT/USDT,VEEN/BTC,VME/BTC,VME/ETH,VSC/ETH,VSF/BTC,W12/BTC,W12/ETH,WBL/BTC,WFX/BTC,XEM/BTC,XLM/BTC,XMCT/ETH,XMCT/USDT,XMR/BTC,XNK/ETH,XRP/BTC,XRP/USDT,XSR/USDT,YAP/BTC,YAP/USDT,YTA/USDT,ZAT/ETH,ZDC/BTC,ZEC/BTC,ZGC/BTC,ZRX/BTC", - "requestFormat": { - "uppercase": true, - "delimiter": "/" - }, - "configFormat": { - "uppercase": true, - "delimiter": "/" - } - } - } - }, - "api": { - "authenticatedSupport": false, - "authenticatedWebsocketApiSupport": false, - "endpoints": { - "url": "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API", - "urlSecondary": "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API", - "websocketURL": "NON_DEFAULT_HTTP_LINK_TO_WEBSOCKET_EXCHANGE_API" - }, - "credentials": { - "key": "Key", - "secret": "Secret" - }, - "credentialsValidator": { - "requiresKey": true, - "requiresSecret": true - } - }, - "features": { - "supports": { - "restAPI": true, - "restCapabilities": { - "autoPairUpdates": true - }, - "websocketAPI": true, - "websocketCapabilities": {} - }, - "enabled": { - "autoPairUpdates": true, - "websocketAPI": false - } - }, - "bankAccounts": [ - { - "enabled": false, - "bankName": "", - "bankAddress": "", - "bankPostalCode": "", - "bankPostalCity": "", - "bankCountry": "", - "accountName": "", - "accountNumber": "", - "swiftCode": "", - "iban": "", - "supportedCurrencies": "" - } - ] - }, { "name": "FTX", "enabled": true, diff --git a/testdata/exchangelist.csv b/testdata/exchangelist.csv index 29da937fc59..02077e62787 100644 --- a/testdata/exchangelist.csv +++ b/testdata/exchangelist.csv @@ -8,7 +8,6 @@ bittrex, btc markets, btse, coinbasepro, -coinbene, coinut, exmo, ftx,