From c2c200cd1b4af523c533473ead9478bae7a622b3 Mon Sep 17 00:00:00 2001
From: Adam <31364354+MadCozBadd@users.noreply.github.com>
Date: Thu, 2 Jul 2020 10:38:50 +1000
Subject: [PATCH] (Exchange) Add FTX exchange support with implementation
tutorial (#495)
* initial
* wip
* brokenwip
* broken wipzzzz
* more functions
brokenwip
NO API KEYS
* broken wip
* WIP
* wip
* WIP
work in progress
* WIP
* WIP
* wip
* more wip
* wip
* ws wip
* broken wip
* adding new functions for websocket to work
* trying to fix websocket issues
* websocket bug fix wip
* broken websocket implementation
* WS unauth functions + brokenWS auth func
* authentication problems
* authentication problems fixed
* data handling for websocket
* websocket completed
* remove verbose
* minor error fix changes and testing
* reorganising variable declarations and minor errors fixed
* enabled exchanges updated
* enabled exchanges fixed
* remove keys
* glorious nits
* xdta n shazzy nitzzz
* shazzy n thrasher nitz
* nitz wip
* broken wip
* apichecker donee n make code better
* apichecker donee n make code better
* OB update
* wip
* wip
* all nitz done
* merge conflicts
* go mod tidy
* merge conflicts
* PLEASE merge conflicts
* new funcs added n binanceapi check update
NO APIKEYS
* basic tests
* linter fixs
* linter fixs
* remove verbose
* test errors fixed
* remove comented code
* minor changes
* some tests fixed
no apikeys
* documentation work
* documentation
* wip
* ryan nitz
* nits addressed
* unnecessary conversion
* no fail
* remove verbose
* type field checking
* broken
* websocket nits fixed
* some thangs
* remove verbose
* fix function
* linter issues
* test error fixed
* nits
* bumperino fixed
* very small change
* nits
* errors fixing
* errors fixing retry
* linters
* thrasher glorious nits
* more changes
* changes
* 2 more changes to be addressed
* 2 more changes to be addressed
* issues addressed
* whip
* changes
* missed change
* changes
* currency issues
* changes
* unsaved
* int64
* HUGE
* HUGE
* NO NITS PLS
* no more
* YES
* :
* changes
* PLEASE
* n another one
* thanks guys
* ill believe in god if this ever ends
* :D
---
README.md | 5 +-
cmd/apichecker/apicheck.go | 436 ++++--
cmd/apichecker/apicheck_test.go | 46 +-
cmd/apichecker/backup.json | 430 ++++++
cmd/apichecker/testupdates.json | 27 +-
cmd/apichecker/updates.json | 37 +-
.../exchanges_templates/exchanges_readme.tmpl | 4 +
.../exchanges_templates/ftx.tmpl | 99 ++
.../exchanges_templates/newexchange.tmpl | 0
.../root_templates/root_readme.tmpl | 1 +
cmd/exchange_template/readme_temp.tmpl | 0
cmd/exchange_template/wrapper_file.tmpl | 4 +-
cmd/exchange_wrapper_coverage/main.go | 2 +-
cmd/exchange_wrapper_issues/main.go | 12 +-
config/config_test.go | 2 +-
config_example.json | 164 ++-
currency/code.go | 13 +-
currency/pair.go | 3 +
currency/pair_test.go | 20 +
docs/ADD_NEW_EXCHANGE.md | 1084 +++++++++++++++
engine/exchange.go | 3 +
engine/fake_exchange_test.go | 2 +-
exchanges/README.md | 4 +
exchanges/alphapoint/alphapoint_wrapper.go | 4 +-
exchanges/binance/binance_wrapper.go | 4 +-
exchanges/bitfinex/bitfinex_wrapper.go | 4 +-
exchanges/bitflyer/bitflyer_wrapper.go | 4 +-
exchanges/bithumb/bithumb_wrapper.go | 4 +-
exchanges/bitmex/bitmex_wrapper.go | 4 +-
exchanges/bitstamp/bitstamp_wrapper.go | 4 +-
exchanges/bittrex/bittrex_wrapper.go | 4 +-
exchanges/btcmarkets/btcmarkets_wrapper.go | 4 +-
exchanges/btse/btse_wrapper.go | 4 +-
exchanges/coinbasepro/coinbasepro_wrapper.go | 4 +-
exchanges/coinbene/coinbene_wrapper.go | 4 +-
exchanges/coinut/coinut_wrapper.go | 4 +-
exchanges/exchange.go | 4 +-
exchanges/exchange_test.go | 2 +-
exchanges/exchange_types.go | 1 +
exchanges/exmo/exmo_wrapper.go | 4 +-
exchanges/ftx/README.md | 133 ++
exchanges/ftx/ftx.go | 959 +++++++++++++
exchanges/ftx/ftx_test.go | 1188 +++++++++++++++++
exchanges/ftx/ftx_types.go | 726 ++++++++++
exchanges/ftx/ftx_websocket.go | 511 +++++++
exchanges/ftx/ftx_wrapper.go | 873 ++++++++++++
exchanges/gateio/gateio_wrapper.go | 4 +-
exchanges/gemini/gemini_wrapper.go | 4 +-
exchanges/hitbtc/hitbtc_wrapper.go | 4 +-
exchanges/huobi/huobi_wrapper.go | 4 +-
exchanges/interfaces.go | 2 +-
exchanges/itbit/itbit_wrapper.go | 4 +-
exchanges/kline/types.go | 27 +-
exchanges/kraken/kraken_wrapper.go | 4 +-
exchanges/lakebtc/lakebtc_wrapper.go | 4 +-
exchanges/lbank/lbank_wrapper.go | 4 +-
.../localbitcoins/localbitcoins_wrapper.go | 4 +-
exchanges/okgroup/okgroup_wrapper.go | 5 +-
exchanges/order/order_types.go | 4 +
exchanges/poloniex/poloniex_wrapper.go | 4 +-
exchanges/support.go | 1 +
exchanges/yobit/yobit_wrapper.go | 4 +-
exchanges/zb/zb_wrapper.go | 4 +-
.../wrappers/gct/exchange/exchange_test.go | 4 +-
go.sum | 3 +
testdata/configtest.json | 57 +
66 files changed, 6720 insertions(+), 282 deletions(-)
create mode 100644 cmd/apichecker/backup.json
create mode 100644 cmd/documentation/exchanges_templates/ftx.tmpl
create mode 100644 cmd/documentation/exchanges_templates/newexchange.tmpl
create mode 100644 cmd/exchange_template/readme_temp.tmpl
create mode 100644 docs/ADD_NEW_EXCHANGE.md
create mode 100644 exchanges/ftx/README.md
create mode 100644 exchanges/ftx/ftx.go
create mode 100644 exchanges/ftx/ftx_test.go
create mode 100644 exchanges/ftx/ftx_types.go
create mode 100644 exchanges/ftx/ftx_websocket.go
create mode 100644 exchanges/ftx/ftx_wrapper.go
diff --git a/README.md b/README.md
index 88a68ce7..d558a0d0 100644
--- a/README.md
+++ b/README.md
@@ -30,6 +30,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
| BTSE | Yes | Yes | NA |
| COINUT | Yes | Yes | NA |
| Exmo | Yes | NA | NA |
+| FTX | Yes | Yes | No |
| CoinbasePro | Yes | Yes | No|
| Coinbene | Yes | No | No |
| GateIO | Yes | Yes | NA |
@@ -139,10 +140,10 @@ Binaries will be published once the codebase reaches a stable condition.
|User|Contribution Amount|
|--|--|
-| [thrasher-](https://github.com/thrasher-) | 639 |
+| [thrasher-](https://github.com/thrasher-) | 640 |
| [shazbert](https://github.com/shazbert) | 191 |
| [gloriousCode](https://github.com/gloriousCode) | 169 |
-| [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) | 46 |
+| [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) | 48 |
| [xtda](https://github.com/xtda) | 43 |
| [ermalguni](https://github.com/ermalguni) | 14 |
| [vadimzhukck](https://github.com/vadimzhukck) | 10 |
diff --git a/cmd/apichecker/apicheck.go b/cmd/apichecker/apicheck.go
index 67a89170..2a987d78 100644
--- a/cmd/apichecker/apicheck.go
+++ b/cmd/apichecker/apicheck.go
@@ -31,8 +31,10 @@ const (
backupFile = "backup.json"
github = "GitHub Sha Check"
htmlScrape = "HTML String Check"
+ pathBinance = "https://binance-docs.github.io/apidocs/spot/en/#change-log"
pathOkCoin = "https://www.okcoin.com/docs/en/#change-change"
pathOkex = "https://www.okex.com/docs/en/#change-change"
+ pathFTX = "https://github.com/ftexchange/ftx"
pathBTSE = "https://www.btse.com/apiexplorer/spot/#btse-spot-api"
pathBitfinex = "https://docs.bitfinex.com/docs/changelog"
pathBitmex = "https://www.bitmex.com/static/md/en-US/apiChangelog"
@@ -448,8 +450,12 @@ func checkChangeLog(htmlData *HTMLScrapingData) (string, error) {
var dataStrings []string
var err error
switch htmlData.Path {
+ case pathBinance:
+ dataStrings, err = htmlScrapeBinance(htmlData)
case pathBTSE:
dataStrings, err = htmlScrapeBTSE(htmlData)
+ case pathFTX:
+ dataStrings, err = htmlScrapeFTX(htmlData)
case pathBitfinex:
dataStrings, err = htmlScrapeBitfinex(htmlData)
case pathBitmex:
@@ -637,8 +643,7 @@ loop:
if err != nil {
return resp, err
}
- result := r.MatchString(tempStr)
- if result {
+ if r.MatchString(tempStr) {
appendStr := r.FindString(tempStr)
resp = append(resp, appendStr)
}
@@ -686,39 +691,6 @@ loop:
return resp, nil
}
-// htmlScrapeBitfinex gets the check string for Bitfinex exchange
-func htmlScrapeBitfinex(htmlData *HTMLScrapingData) ([]string, error) {
- temp, err := http.Get(htmlData.Path)
- if err != nil {
- return nil, err
- }
- defer temp.Body.Close()
- a, err := ioutil.ReadAll(temp.Body)
- if err != nil {
- return nil, err
- }
- r, err := regexp.Compile(htmlData.RegExp)
- if err != nil {
- return nil, err
- }
- str := r.FindAllString(string(a), -1)
- var resp []string
- for x := range str {
- tempStr := strings.Replace(str[x], "section-v-", "", 1)
- var repeat bool
- for y := range resp {
- if tempStr == resp[y] {
- repeat = true
- break
- }
- }
- if !repeat {
- resp = append(resp, tempStr)
- }
- }
- return resp, nil
-}
-
// htmlScrapeBitmex gets the check string for Bitmex exchange
func htmlScrapeBitmex(htmlData *HTMLScrapingData) ([]string, error) {
var resp []string
@@ -813,59 +785,6 @@ func htmlScrapeBTCMarkets(htmlData *HTMLScrapingData) ([]string, error) {
return resp, nil
}
-// htmlScrapeBitflyer gets the check string for BTCMarkets exchange
-func htmlScrapeBitflyer(htmlData *HTMLScrapingData) ([]string, error) {
- var resp []string
- var tempArray []string
- temp, err := http.Get(htmlData.Path)
- if err != nil {
- return resp, err
- }
- defer temp.Body.Close()
- tokenizer := html.NewTokenizer(temp.Body)
-loop:
- for {
- next := tokenizer.Next()
- switch next {
- case html.ErrorToken:
- break loop
- case html.StartTagToken:
- token := tokenizer.Token()
- if token.Data == htmlData.TokenData {
- for {
- nextToken := tokenizer.Next()
- switch nextToken {
- case html.EndTagToken:
- t := tokenizer.Token()
- if t.Data == htmlData.TokenDataEnd {
- break loop
- }
- case html.StartTagToken:
- t := tokenizer.Token()
- if t.Data == htmlData.TextTokenData {
- inner := tokenizer.Next()
- if inner == html.TextToken {
- tempStr := string(tokenizer.Text())
- r, err := regexp.Compile(htmlData.RegExp)
- if err != nil {
- return resp, err
- }
- result := r.MatchString(tempStr)
- if result {
- appendStr := r.FindString(tempStr)
- tempArray = append(tempArray, appendStr)
- }
- }
- }
- }
- }
- }
- }
- }
- resp = append(resp, tempArray[1])
- return resp, nil
-}
-
// htmlScrapeOk gets the check string for Okex
func htmlScrapeOk(htmlData *HTMLScrapingData) ([]string, error) {
var resp []string
@@ -1111,62 +1030,6 @@ func htmlScrapeBitstamp(htmlData *HTMLScrapingData) ([]string, error) {
return resp, nil
}
-// htmlScrapeKraken gets the check string for Kraken Exchange
-func htmlScrapeKraken(htmlData *HTMLScrapingData) ([]string, error) {
- var resp []string
- temp, err := http.Get(htmlData.Path)
- if err != nil {
- return resp, err
- }
- defer temp.Body.Close()
- tokenizer := html.NewTokenizer(temp.Body)
-loop:
- for {
- next := tokenizer.Next()
- switch next {
- case html.ErrorToken:
- break loop
- case html.StartTagToken:
- token := tokenizer.Token()
- if token.Data == htmlData.TokenData {
- inner := tokenizer.Next()
- if inner == html.TextToken {
- if string(tokenizer.Text()) == "Get account balance" {
- loop2:
- for {
- next := tokenizer.Next()
- switch next {
- case html.EndTagToken:
- t := tokenizer.Token()
- if t.Data == htmlData.TokenDataEnd {
- break loop2
- }
- case html.StartTagToken:
- t := tokenizer.Token()
- if t.Data == htmlData.TextTokenData {
- inside := tokenizer.Next()
- if inside == html.TextToken {
- tempStr := string(tokenizer.Text())
- r, err := regexp.Compile(htmlData.RegExp)
- if err != nil {
- return resp, err
- }
- result := r.MatchString(tempStr)
- if result {
- resp = append(resp, tempStr)
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- return resp, nil
-}
-
// htmlScrapeAlphaPoint gets the check string for Kraken Exchange
func htmlScrapeAlphaPoint(htmlData *HTMLScrapingData) ([]string, error) {
var resp []string
@@ -1610,3 +1473,288 @@ func trelloCreateNewChecklist() error {
}
return errors.New("checklist id and name not found, checklist creation failed")
}
+
+// htmlScrapeKraken gets the check string for Kraken Exchange
+func htmlScrapeKraken(htmlData *HTMLScrapingData) ([]string, error) {
+ var resp []string
+ temp, err := http.Get(htmlData.Path)
+ if err != nil {
+ return resp, err
+ }
+ defer temp.Body.Close()
+ tokenizer := html.NewTokenizer(temp.Body)
+loop:
+ for {
+ next := tokenizer.Next()
+ switch next {
+ case html.ErrorToken:
+ break loop
+ case html.StartTagToken:
+ token := tokenizer.Token()
+ if token.Data == htmlData.TokenData {
+ inner := tokenizer.Next()
+ if inner == html.TextToken {
+ if string(tokenizer.Text()) == "Get account balance" {
+ loop2:
+ for {
+ next := tokenizer.Next()
+ switch next {
+ case html.EndTagToken:
+ t := tokenizer.Token()
+ if t.Data == htmlData.TokenDataEnd {
+ break loop2
+ }
+ case html.StartTagToken:
+ t := tokenizer.Token()
+ if t.Data == htmlData.TextTokenData {
+ inside := tokenizer.Next()
+ if inside == html.TextToken {
+ tempStr := string(tokenizer.Text())
+ r, err := regexp.Compile(htmlData.RegExp)
+ if err != nil {
+ return resp, err
+ }
+ result := r.MatchString(tempStr)
+ if result {
+ resp = append(resp, tempStr)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return resp, nil
+}
+
+// htmlScrapeBitflyer gets the check string for BTCMarkets exchange
+func htmlScrapeBitflyer(htmlData *HTMLScrapingData) ([]string, error) {
+ var resp []string
+ var tempArray []string
+ temp, err := http.Get(htmlData.Path)
+ if err != nil {
+ return resp, err
+ }
+ defer temp.Body.Close()
+ tokenizer := html.NewTokenizer(temp.Body)
+loop:
+ for {
+ next := tokenizer.Next()
+ switch next {
+ case html.ErrorToken:
+ break loop
+ case html.StartTagToken:
+ token := tokenizer.Token()
+ if token.Data == htmlData.TokenData {
+ for {
+ nextToken := tokenizer.Next()
+ switch nextToken {
+ case html.EndTagToken:
+ t := tokenizer.Token()
+ if t.Data == htmlData.TokenDataEnd {
+ break loop
+ }
+ case html.StartTagToken:
+ t := tokenizer.Token()
+ if t.Data == htmlData.TextTokenData {
+ inner := tokenizer.Next()
+ if inner == html.TextToken {
+ tempStr := string(tokenizer.Text())
+ r, err := regexp.Compile(htmlData.RegExp)
+ if err != nil {
+ return resp, err
+ }
+ result := r.MatchString(tempStr)
+ if result {
+ appendStr := r.FindString(tempStr)
+ tempArray = append(tempArray, appendStr)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ resp = append(resp, tempArray[1])
+ return resp, nil
+}
+
+// htmlScrapeFTX gets the check string for FTX exchange
+func htmlScrapeFTX(htmlData *HTMLScrapingData) ([]string, error) {
+ temp, err := http.Get(htmlData.Path)
+ if err != nil {
+ return nil, err
+ }
+ defer temp.Body.Close()
+ a := temp.Body
+ tokenizer := html.NewTokenizer(a)
+ var respStr string
+loop:
+ for {
+ next := tokenizer.Next()
+ switch next {
+ case html.ErrorToken:
+ break loop
+ case html.StartTagToken:
+ token := tokenizer.Token()
+ if token.Data == htmlData.TokenData {
+ for _, a := range token.Attr {
+ if a.Key == htmlData.Key && a.Val == htmlData.Val {
+ loop2:
+ for {
+ anotherToken := tokenizer.Next()
+ switch anotherToken {
+ case html.StartTagToken:
+ z := tokenizer.Token()
+ if z.Data == "a" {
+ for _, m := range z.Attr {
+ if m.Key == "title" {
+ switch m.Val {
+ case "rest":
+ loop3:
+ for {
+ nextToken := tokenizer.Next()
+ switch nextToken {
+ case html.StartTagToken:
+ f := tokenizer.Token()
+ if f.Data == "time-ago" {
+ for _, b := range f.Attr {
+ if b.Key == "datetime" {
+ respStr += b.Val
+ }
+ }
+ }
+ case html.EndTagToken:
+ tk := tokenizer.Token()
+ if tk.Data == htmlData.TokenDataEnd {
+ break loop3
+ }
+ }
+ }
+ case "websocket":
+ loop4:
+ for {
+ nextToken := tokenizer.Next()
+ switch nextToken {
+ case html.StartTagToken:
+ f := tokenizer.Token()
+ if f.Data == "time-ago" {
+ for _, b := range f.Attr {
+ if b.Key == "datetime" {
+ respStr += b.Val
+ }
+ }
+ }
+ case html.EndTagToken:
+ tk := tokenizer.Token()
+ if tk.Data == htmlData.TokenDataEnd {
+ break loop4
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ case html.ErrorToken:
+ break loop2
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return []string{respStr}, nil
+}
+
+// htmlScrapeBitfinex gets the check string for Bitfinex exchange
+func htmlScrapeBitfinex(htmlData *HTMLScrapingData) ([]string, error) {
+ temp, err := http.Get(htmlData.Path)
+ if err != nil {
+ return nil, err
+ }
+ defer temp.Body.Close()
+ a, err := ioutil.ReadAll(temp.Body)
+ if err != nil {
+ return nil, err
+ }
+ r, err := regexp.Compile(htmlData.RegExp)
+ if err != nil {
+ return nil, err
+ }
+ str := r.FindAllString(string(a), -1)
+ var resp []string
+ for x := range str {
+ tempStr := strings.Replace(str[x], "section-v-", "", 1)
+ var repeat bool
+ for y := range resp {
+ if tempStr == resp[y] {
+ repeat = true
+ break
+ }
+ }
+ if !repeat {
+ resp = append(resp, tempStr)
+ }
+ }
+ return resp, nil
+}
+
+// htmlScrapeBinance gets checkstring for binance exchange
+func htmlScrapeBinance(htmlData *HTMLScrapingData) ([]string, error) {
+ temp, err := http.Get(htmlData.Path)
+ if err != nil {
+ return nil, err
+ }
+ defer temp.Body.Close()
+ tokenizer := html.NewTokenizer(temp.Body)
+ var resp []string
+loop:
+ for {
+ next := tokenizer.Next()
+ switch next {
+ case html.ErrorToken:
+ break loop
+ case html.StartTagToken:
+ token := tokenizer.Token()
+ if token.Data == htmlData.TokenData {
+ for _, a := range token.Attr {
+ if a.Key == htmlData.Key && a.Val == htmlData.Val {
+ loop2:
+ for {
+ nextToken := tokenizer.Next()
+ switch nextToken {
+ case html.EndTagToken:
+ nt := tokenizer.Token()
+ if nt.Data == htmlData.TokenDataEnd {
+ break loop2
+ }
+ case html.StartTagToken:
+ tk := tokenizer.Token()
+ if tk.Data == htmlData.TextTokenData {
+ inner := tokenizer.Next()
+ if inner == html.TextToken {
+ tempStr := string(tokenizer.Text())
+ r, err := regexp.Compile(htmlData.RegExp)
+ if err != nil {
+ return resp, err
+ }
+ if r.MatchString(tempStr) {
+ resp = append(resp, tempStr)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return resp, nil
+}
diff --git a/cmd/apichecker/apicheck_test.go b/cmd/apichecker/apicheck_test.go
index 11437d50..87513bb6 100644
--- a/cmd/apichecker/apicheck_test.go
+++ b/cmd/apichecker/apicheck_test.go
@@ -148,13 +148,15 @@ func TestCheckChangeLog(t *testing.T) {
func TestAdd(t *testing.T) {
t.Parallel()
- data2 := HTMLScrapingData{TokenData: "a",
- Key: "href",
- Val: "./#change-change",
- TokenDataEnd: "./#change-",
- RegExp: `./#change-\d{8}`,
- Path: "wrongpath"}
- err := addExch("WrongExch", htmlScrape, data2, false)
+ data2 := HTMLScrapingData{
+ TokenData: "h1",
+ Key: "id",
+ Val: "change-log",
+ TextTokenData: "strong",
+ TokenDataEnd: "p",
+ Path: "incorrectpath",
+ }
+ err := addExch("FalseName", htmlScrape, data2, false)
if err == nil {
t.Log("expected an error due to invalid path being parsed in")
}
@@ -418,8 +420,6 @@ func TestHTMLYobit(t *testing.T) {
func TestHTMLScrapeLocalBitcoins(t *testing.T) {
t.Parallel()
data := HTMLScrapingData{TokenData: "div",
- Key: "class",
- Val: "col-md-12",
RegExp: `col-md-12([\s\S]*?)clearfix`,
Path: "https://localbitcoins.com/api-docs/"}
_, err := htmlScrapeLocalBitcoins(&data)
@@ -701,3 +701,31 @@ func TestTrelloDeleteCheckItems(t *testing.T) {
t.Error(err)
}
}
+
+func TestHTMLScrapeFTX(t *testing.T) {
+ data := HTMLScrapingData{
+ TokenData: "span",
+ Key: "class",
+ Val: "css-truncate css-truncate-target d-block width-fit",
+ TokenDataEnd: "svg",
+ Path: "https://github.com/ftexchange/ftx"}
+ a, err := htmlScrapeFTX(&data)
+ if err != nil || len(a) != 1 {
+ t.Error(err)
+ }
+}
+
+func TestHTMLScrapeBinance(t *testing.T) {
+ data := HTMLScrapingData{
+ TokenData: "h1",
+ Key: "id",
+ Val: "change-log",
+ TextTokenData: "strong",
+ TokenDataEnd: "p",
+ Path: "https://binance-docs.github.io/apidocs/spot/en/#change-log",
+ }
+ _, err := htmlScrapeBinance(&data)
+ if err != nil {
+ t.Error(err)
+ }
+}
diff --git a/cmd/apichecker/backup.json b/cmd/apichecker/backup.json
new file mode 100644
index 00000000..ae8644af
--- /dev/null
+++ b/cmd/apichecker/backup.json
@@ -0,0 +1,430 @@
+{
+ "CardID": "",
+ "ChecklistID": "",
+ "ListID": "",
+ "BoardID": "",
+ "Key": "",
+ "Token": "",
+ "CreateCardName": "",
+ "CreateListName": "",
+ "CreateChecklistName": "",
+ "Exchanges": [
+ {
+ "Name": "Huobi",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "TokenData": "h1",
+ "Key": "id",
+ "Val": "change-log",
+ "TokenDataEnd": "h2",
+ "TextTokenData": "td",
+ "DateFormat": "2006.01.02 15:04",
+ "RegExp": "^20(\\d){2}.(\\d){2}.(\\d){2} (\\d){2}:(\\d){2}$",
+ "CheckString": "2019.12.27 19:00",
+ "Path": "https://huobiapi.github.io/docs/spot/v1/en/#change-log"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "Coinbasepro",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "TokenData": "h1",
+ "Key": "id",
+ "Val": "changelog",
+ "TokenDataEnd": "ul",
+ "TextTokenData": "strong",
+ "DateFormat": "01/02/06",
+ "RegExp": "^(\\d){1,2}/(\\d){1,2}/(\\d){2}$",
+ "CheckString": "2/20/20",
+ "Path": "https://docs.pro.coinbase.com/#changelog"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "Binance",
+ "CheckType": "GitHub Sha Check",
+ "Data": {
+ "GitHubData": {
+ "Repo": "binance-exchange/binance-official-api-docs",
+ "Sha": "4878d48adc0075669ba85033b0e2d40c2876cf56"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "Bithumb",
+ "CheckType": "GitHub Sha Check",
+ "Data": {
+ "GitHubData": {
+ "Repo": "bithumb-pro/bithumb.pro-official-api-docs",
+ "Sha": "6293502c7736ab4971491978225ef4d104bdff31"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "Bittrex",
+ "CheckType": "GitHub Sha Check",
+ "Data": {
+ "GitHubData": {
+ "Repo": "Bittrex/bittrex.github.io",
+ "Sha": "fc1ea9c10c48aa82c4dc2c6be74887ef61b5b31b"
+ }
+ },
+ "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",
+ "Data": {
+ "GitHubData": {
+ "Repo": "coinut/api",
+ "Sha": "6936dab4d2beba3c8245a603aebf3f545ebcf3f9"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "Gateio",
+ "CheckType": "GitHub Sha Check",
+ "Data": {
+ "GitHubData": {
+ "Repo": "gateio/gateapi-go",
+ "Sha": "81e2f1bb92c5406853139ae054bbda599483e127"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "Lbank",
+ "CheckType": "GitHub Sha Check",
+ "Data": {
+ "GitHubData": {
+ "Repo": "LBank-exchange/lbank-official-api-docs",
+ "Sha": "85e7bb83f03d4239c3aad26cabb997a1a2bbb3d1"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "BTSE",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "TokenData": "h1",
+ "Key": "id",
+ "Val": "btse-spot-api",
+ "TokenDataEnd": "blockquote",
+ "TextTokenData": "h1",
+ "RegExp": "^BTSE Spot API v(\\d){1}.(\\d){1}$",
+ "CheckString": "BTSE Spot API v3.0.2",
+ "Path": "https://www.btse.com/apiexplorer/spot/#btse-spot-api"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "Bitfinex",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "TokenData": "h1",
+ "Key": "class",
+ "Val": "header-scroll",
+ "TokenDataEnd": "p",
+ "DateFormat": "2006-01-02",
+ "RegExp": "section-v-(2\\d{3}-\\d{1,2}-\\d{1,2})",
+ "CheckString": "2019-08-19",
+ "Path": "https://docs.bitfinex.com/docs/changelog"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "ANX",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "RegExp": "ANX Exchange API v\\d{1}",
+ "CheckString": "ANX Exchange API v3",
+ "Path": "https://anxv3.docs.apiary.io/"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "Poloniex",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "TokenData": "h1",
+ "Key": "id",
+ "Val": "changelog",
+ "TokenDataEnd": "div",
+ "TextTokenData": "h2",
+ "DateFormat": "2006-01-02",
+ "RegExp": "(2\\d{3}-\\d{1,2}-\\d{1,2})",
+ "CheckString": "2020-03-24",
+ "Path": "https://docs.poloniex.com/#changelog"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "ItBit",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "TokenData": "a",
+ "Key": "href",
+ "RegExp": "^https://api.itbit.com/v\\d{1}/$",
+ "CheckString": "https://api.itbit.com/v1/",
+ "Path": "https://api.itbit.com/docs"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "Bitmex",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "TokenData": "h4",
+ "Key": "id",
+ "DateFormat": "Jan-2-2006",
+ "RegExp": "([A-Z]{1}[a-z]{2}-\\d{1,2}-2\\d{3})",
+ "CheckString": "Dec-16-2019",
+ "Path": "https://www.bitmex.com/static/md/en-US/apiChangelog"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "HitBTC",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "TokenData": "h1",
+ "Key": "id",
+ "Val": "about-companyname-api",
+ "TokenDataEnd": "h2",
+ "TextTokenData": "p",
+ "RegExp": "newest version \\d{1}.\\d{1}",
+ "CheckString": "newest version 2.0",
+ "Path": "https://api.hitbtc.com/"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "BTC Markets",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "RegExp": "^version: \\d{1}.\\d{1}.\\d{1}",
+ "CheckString": "version: 3.0.0",
+ "Path": "https://api.btcmarkets.net/openapi/info/index.yaml"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "Bitflyer",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "TokenData": "p",
+ "TokenDataEnd": "h3",
+ "TextTokenData": "code",
+ "RegExp": "^https://api.bitflyer.com/v\\d{1}/$",
+ "CheckString": "https://api.bitflyer.com/v1/",
+ "Path": "https://lightning.bitflyer.com/docs?lang=en"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "LakeBTC",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "TokenData": "div",
+ "Key": "class",
+ "Val": "flash-message",
+ "TokenDataEnd": "h2",
+ "TextTokenData": "h1",
+ "RegExp": "APIv\\d{1}",
+ "CheckString": "de2491b95ef1f6ea334247b13f0f14f6816fb5961cc63acc0542b07fc0336dd8",
+ "Path": "https://www.lakebtc.com/s/api_v2"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "Exmo",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "RegExp": "Last updated on [\\s\\S]*, 20\\d{2}",
+ "CheckString": "Last updated on December, 16th, 2019",
+ "Path": "https://exmo.com/en/api/"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "Kraken",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "TokenData": "h3",
+ "TokenDataEnd": "p",
+ "TextTokenData": "p",
+ "RegExp": "URL: https://api.kraken.com/\\d{1}/private/Balance",
+ "CheckString": "URL: https://api.kraken.com/0/private/Balance",
+ "Path": "https://www.kraken.com/features/api"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "Bitstamp",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "TokenData": "h2",
+ "Key": "class",
+ "Val": "text-subtitle mt48",
+ "TokenDataEnd": "h4",
+ "TextTokenData": "p",
+ "RegExp": "refer to the v\\d{1} API for future references.",
+ "CheckString": "refer to the v2 API for future references.",
+ "Path": "https://www.bitstamp.net/api/"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "AlphaPoint",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "TokenData": "h1",
+ "Key": "id",
+ "Val": "introduction",
+ "TokenDataEnd": "blockquote",
+ "TextTokenData": "h3",
+ "RegExp": "revised-calls-\\d{1}-\\d{1}-\\d{1}-gt-\\d{1}-\\d{1}-\\d{1}",
+ "CheckString": "revised-calls-3-3-2-gt-3-3-3",
+ "Path": "https://alphapoint.github.io/slate/#introduction"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "Yobit",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "TokenData": "h2",
+ "Key": "id",
+ "CheckString": "v3",
+ "Path": "https://www.yobit.net/en/api/"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "LocalBitcoins",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "TokenData": "div",
+ "Key": "class",
+ "Val": "col-md-12",
+ "RegExp": "col-md-12([\\s\\S]*?)clearfix",
+ "CheckString": "37a144dc619776b87c098da5a88bef7fed6c8a7cea2d4b9a38c96750726c93ff",
+ "Path": "https://localbitcoins.com/api-docs/"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "OkCoin International",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "TokenData": "a",
+ "Key": "href",
+ "Val": "./#change-change",
+ "TokenDataEnd": "./#change-",
+ "RegExp": "./#change-\\d{8}",
+ "CheckString": "20200229",
+ "Path": "https://www.okcoin.com/docs/en/#change-change"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "Okex",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "TokenData": "a",
+ "Key": "href",
+ "Val": "./#change-change",
+ "TokenDataEnd": "./#change-",
+ "RegExp": "./#change-\\d{8}",
+ "CheckString": "20200331",
+ "Path": "https://www.okex.com/docs/en/#change-change"
+ }
+ },
+ "Disabled": false
+ },
+ {
+ "Name": "Gemini",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "TokenData": "h1",
+ "Key": "id",
+ "Val": "revision-history",
+ "TokenDataEnd": "table",
+ "TextTokenData": "td",
+ "DateFormat": "2006/01/02",
+ "RegExp": "^20(\\d){2}/(\\d){2}/(\\d){2}$",
+ "CheckString": "2020/03/05",
+ "Path": "https://docs.gemini.com/rest-api/#revision-history"
+ }
+ },
+ "Disabled": false
+ }
+ ]
+}
\ No newline at end of file
diff --git a/cmd/apichecker/testupdates.json b/cmd/apichecker/testupdates.json
index ae8644af..5a34be4f 100644
--- a/cmd/apichecker/testupdates.json
+++ b/cmd/apichecker/testupdates.json
@@ -47,11 +47,16 @@
},
{
"Name": "Binance",
- "CheckType": "GitHub Sha Check",
+ "CheckType": "HTML String Check",
"Data": {
- "GitHubData": {
- "Repo": "binance-exchange/binance-official-api-docs",
- "Sha": "4878d48adc0075669ba85033b0e2d40c2876cf56"
+ "HTMLData": {
+ "TokenData": "h1",
+ "Key": "id",
+ "Val": "change-log",
+ "TokenDataEnd": "p",
+ "TextTokenData": "strong",
+ "CheckString": "2020-05-06",
+ "Path": "https://binance-docs.github.io/apidocs/spot/en/#change-log"
}
},
"Disabled": false
@@ -425,6 +430,20 @@
}
},
"Disabled": false
+ },
+ {
+ "Name": "FTX",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "Key": "class",
+ "Val": "css-truncate css-truncate-target",
+ "TokenDataEnd": "td",
+ "CheckString": "65e8800b5c6800aad896f888b2a62afc-d29c4f140f6ca068db9970054076ba63e51a9e96bf56d570e62cdb573c86b18526296117-1c388b9ec24e52ca4537240db3f48025ec9fca7a",
+ "Path": "https://github.com/ftexchange/ftx"
+ }
+ },
+ "Disabled": false
}
]
}
\ No newline at end of file
diff --git a/cmd/apichecker/updates.json b/cmd/apichecker/updates.json
index ae8644af..0140ec98 100644
--- a/cmd/apichecker/updates.json
+++ b/cmd/apichecker/updates.json
@@ -46,15 +46,20 @@
"Disabled": false
},
{
- "Name": "Binance",
- "CheckType": "GitHub Sha Check",
- "Data": {
- "GitHubData": {
- "Repo": "binance-exchange/binance-official-api-docs",
- "Sha": "4878d48adc0075669ba85033b0e2d40c2876cf56"
- }
- },
- "Disabled": false
+ "Name": "Binance",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "TokenData": "h1",
+ "Key": "id",
+ "Val": "change-log",
+ "TokenDataEnd": "p",
+ "TextTokenData": "strong",
+ "CheckString": "2020-05-06",
+ "Path": "https://binance-docs.github.io/apidocs/spot/en/#change-log"
+ }
+ },
+ "Disabled": false
},
{
"Name": "Bithumb",
@@ -425,6 +430,20 @@
}
},
"Disabled": false
+ },
+ {
+ "Name": "FTX",
+ "CheckType": "HTML String Check",
+ "Data": {
+ "HTMLData": {
+ "Key": "class",
+ "Val": "css-truncate css-truncate-target",
+ "TokenDataEnd": "td",
+ "CheckString": "65e8800b5c6800aad896f888b2a62afc-d29c4f140f6ca068db9970054076ba63e51a9e96bf56d570e62cdb573c86b18526296117-1c388b9ec24e52ca4537240db3f48025ec9fca7a",
+ "Path": "https://github.com/ftexchange/ftx"
+ }
+ },
+ "Disabled": false
}
]
}
\ No newline at end of file
diff --git a/cmd/documentation/exchanges_templates/exchanges_readme.tmpl b/cmd/documentation/exchanges_templates/exchanges_readme.tmpl
index 9a65a979..c937bdaa 100644
--- a/cmd/documentation/exchanges_templates/exchanges_readme.tmpl
+++ b/cmd/documentation/exchanges_templates/exchanges_readme.tmpl
@@ -7,6 +7,10 @@
+ Please checkout individual exchange README for more information on
implementation
+## Guide for adding a new exchange
+
++ A guide on implementing API support for a new exchange can be found [here](../docs/ADD_NEW_EXCHANGE.md)
+
### Please click GoDocs chevron above to view current GoDoc information for this package
{{template "contributions"}}
{{template "donations" .}}
diff --git a/cmd/documentation/exchanges_templates/ftx.tmpl b/cmd/documentation/exchanges_templates/ftx.tmpl
new file mode 100644
index 00000000..cc1c547a
--- /dev/null
+++ b/cmd/documentation/exchanges_templates/ftx.tmpl
@@ -0,0 +1,99 @@
+{{define "exchanges ftx" -}}
+{{template "header" .}}
+## FTX 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 f exchange.IBotExchange
+
+for i := range bot.Exchanges {
+ if bot.Exchanges[i].GetName() == "FTX" {
+ f = bot.Exchanges[i]
+ }
+}
+
+// Public calls - wrapper functions
+
+// Fetches current ticker information
+tick, err := f.FetchTicker()
+if err != nil {
+ // Handle error
+}
+
+// Fetches current orderbook information
+ob, err := f.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 := f.GetAccountInfo()
+if err != nil {
+ // Handle error
+}
+```
+
++ If enabled via individually importing package, rudimentary example below:
+
+```go
+// Public calls
+
+// Fetches current ticker information
+ticker, err := f.GetTicker()
+if err != nil {
+ // Handle error
+}
+
+// Fetches current orderbook information
+ob, err := f.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 := f.GetUserInfo(...)
+if err != nil {
+ // Handle error
+}
+
+// Submits an order and the exchange and returns its tradeID
+tradeID, err := f.Trade(...)
+if err != nil {
+ // Handle error
+}
+```
+
+### 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/newexchange.tmpl b/cmd/documentation/exchanges_templates/newexchange.tmpl
new file mode 100644
index 00000000..e69de29b
diff --git a/cmd/documentation/root_templates/root_readme.tmpl b/cmd/documentation/root_templates/root_readme.tmpl
index 5a953d7c..e808237a 100644
--- a/cmd/documentation/root_templates/root_readme.tmpl
+++ b/cmd/documentation/root_templates/root_readme.tmpl
@@ -31,6 +31,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
| BTSE | Yes | Yes | NA |
| COINUT | Yes | Yes | NA |
| Exmo | Yes | NA | NA |
+| FTX | Yes | Yes | No |
| CoinbasePro | Yes | Yes | No|
| Coinbene | Yes | No | No |
| GateIO | Yes | Yes | NA |
diff --git a/cmd/exchange_template/readme_temp.tmpl b/cmd/exchange_template/readme_temp.tmpl
new file mode 100644
index 00000000..e69de29b
diff --git a/cmd/exchange_template/wrapper_file.tmpl b/cmd/exchange_template/wrapper_file.tmpl
index 99d314df..4fecf01f 100644
--- a/cmd/exchange_template/wrapper_file.tmpl
+++ b/cmd/exchange_template/wrapper_file.tmpl
@@ -298,8 +298,8 @@ func ({{.Variable}} *{{.CapitalName}}) GetFundingHistory() ([]exchange.FundHisto
return nil, common.ErrNotYetImplemented
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func ({{.Variable}} *{{.CapitalName}}) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func ({{.Variable}} *{{.CapitalName}}) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/cmd/exchange_wrapper_coverage/main.go b/cmd/exchange_wrapper_coverage/main.go
index 52be1860..de6111f8 100644
--- a/cmd/exchange_wrapper_coverage/main.go
+++ b/cmd/exchange_wrapper_coverage/main.go
@@ -117,7 +117,7 @@ func testWrappers(e exchange.IBotExchange) []string {
funcs = append(funcs, "GetAccountInfo")
}
- _, err = e.GetExchangeHistory(p, assetType)
+ _, err = e.GetExchangeHistory(p, assetType, time.Time{}, time.Time{})
if err == common.ErrNotYetImplemented {
funcs = append(funcs, "GetExchangeHistory")
}
diff --git a/cmd/exchange_wrapper_issues/main.go b/cmd/exchange_wrapper_issues/main.go
index b2a3190d..34ebc111 100644
--- a/cmd/exchange_wrapper_issues/main.go
+++ b/cmd/exchange_wrapper_issues/main.go
@@ -12,6 +12,7 @@ import (
"strings"
"sync"
"text/template"
+ "time"
"github.com/thrasher-corp/gocryptotrader/common/file"
"github.com/thrasher-corp/gocryptotrader/config"
@@ -415,14 +416,14 @@ func testWrappers(e exchange.IBotExchange, base *exchange.Base, config *Config)
})
var r8 []exchange.TradeHistory
- r8, err = e.GetExchangeHistory(p, assetTypes[i])
+ r8, err = e.GetExchangeHistory(p, assetTypes[i], time.Now().Add(-time.Minute), time.Now())
msg = ""
if err != nil {
msg = err.Error()
responseContainer.ErrorCount++
}
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
- SentParams: jsonifyInterface([]interface{}{p, assetTypes[i]}),
+ SentParams: jsonifyInterface([]interface{}{p, assetTypes[i], time.Now().Add(-time.Minute), time.Now()}),
Function: "GetExchangeHistory",
Error: msg,
Response: jsonifyInterface([]interface{}{r8}),
@@ -506,9 +507,10 @@ func testWrappers(e exchange.IBotExchange, base *exchange.Base, config *Config)
})
// r13
cancelRequest := order.Cancel{
- Side: testOrderSide,
- Pair: p,
- ID: config.OrderSubmission.OrderID,
+ Side: testOrderSide,
+ Pair: p,
+ ID: config.OrderSubmission.OrderID,
+ AssetType: assetTypes[i],
}
err = e.CancelOrder(&cancelRequest)
msg = ""
diff --git a/config/config_test.go b/config/config_test.go
index 716513a1..286bba71 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -18,7 +18,7 @@ import (
const (
// Default number of enabled exchanges. Modify this whenever an exchange is
// added or removed
- defaultEnabledExchanges = 27
+ defaultEnabledExchanges = 28
testFakeExchangeName = "Stampbit"
testPair = "BTC-USD"
)
diff --git a/config_example.json b/config_example.json
index 9a6218ef..6128d075 100644
--- a/config_example.json
+++ b/config_example.json
@@ -201,26 +201,38 @@
{
"Address": "1JCe8z4jJVNXSjohjM4i9Hh813dLCNx2Sy",
"CoinType": "BTC",
- "Balance": 53000.01741264,
- "Description": ""
+ "Balance": 0.00108832,
+ "Description": "",
+ "WhiteListed": false,
+ "ColdStorage": false,
+ "SupportedExchanges": ""
},
{
"Address": "3Nxwenay9Z8Lc9JBiywExpnEFiLp6Afp8v",
"CoinType": "BTC",
- "Balance": 107848.28963408,
- "Description": ""
+ "Balance": 0.01798129,
+ "Description": "",
+ "WhiteListed": false,
+ "ColdStorage": false,
+ "SupportedExchanges": ""
},
{
"Address": "LgY8ahfHRhvjVQC1zJnBhFMG5pCTMuKRqh",
"CoinType": "LTC",
"Balance": 0.03665026,
- "Description": ""
+ "Description": "",
+ "WhiteListed": false,
+ "ColdStorage": false,
+ "SupportedExchanges": ""
},
{
"Address": "0xb794f5ea0ba39494ce839613fffba74279579268",
"CoinType": "ETH",
"Balance": 0.25555604051325775,
- "Description": ""
+ "Description": "",
+ "WhiteListed": false,
+ "ColdStorage": false,
+ "SupportedExchanges": ""
}
]
},
@@ -250,7 +262,7 @@
"pairs": {
"spot": {
"enabled": "BTC-USDT,ETH-USDT,LTC-USDT,ADA-USDT,XRP-USDT",
- "available": "ETH-BTC,LTC-BTC,BNB-BTC,NEO-BTC,QTUM-ETH,EOS-ETH,SNT-ETH,BNT-ETH,GAS-BTC,BNB-ETH,BTC-USDT,ETH-USDT,OAX-ETH,DNT-ETH,MCO-ETH,MCO-BTC,WTC-BTC,WTC-ETH,LRC-BTC,LRC-ETH,QTUM-BTC,YOYO-BTC,OMG-BTC,OMG-ETH,ZRX-BTC,ZRX-ETH,STRAT-BTC,STRAT-ETH,SNGLS-BTC,BQX-BTC,BQX-ETH,KNC-BTC,KNC-ETH,FUN-BTC,FUN-ETH,SNM-BTC,SNM-ETH,NEO-ETH,IOTA-BTC,IOTA-ETH,LINK-BTC,LINK-ETH,XVG-BTC,XVG-ETH,MDA-BTC,MDA-ETH,MTL-BTC,MTL-ETH,EOS-BTC,SNT-BTC,ETC-ETH,ETC-BTC,MTH-BTC,MTH-ETH,ENG-BTC,ENG-ETH,DNT-BTC,ZEC-BTC,ZEC-ETH,BNT-BTC,AST-BTC,AST-ETH,DASH-BTC,DASH-ETH,OAX-BTC,BTG-BTC,BTG-ETH,EVX-BTC,EVX-ETH,REQ-BTC,REQ-ETH,VIB-BTC,VIB-ETH,TRX-BTC,TRX-ETH,POWR-BTC,POWR-ETH,ARK-BTC,ARK-ETH,YOYO-ETH,XRP-BTC,XRP-ETH,ENJ-BTC,ENJ-ETH,STORJ-BTC,STORJ-ETH,BNB-USDT,YOYO-BNB,POWR-BNB,KMD-BTC,KMD-ETH,NULS-BNB,RCN-BTC,RCN-ETH,RCN-BNB,NULS-BTC,NULS-ETH,RDN-BTC,RDN-ETH,RDN-BNB,XMR-BTC,XMR-ETH,DLT-BNB,WTC-BNB,DLT-BTC,DLT-ETH,AMB-BTC,AMB-ETH,AMB-BNB,BAT-BTC,BAT-ETH,BAT-BNB,BCPT-BTC,BCPT-ETH,BCPT-BNB,ARN-BTC,ARN-ETH,GVT-BTC,GVT-ETH,CDT-BTC,CDT-ETH,GXS-BTC,GXS-ETH,NEO-USDT,NEO-BNB,POE-BTC,POE-ETH,QSP-BTC,QSP-ETH,QSP-BNB,BTS-BTC,BTS-ETH,XZC-BTC,XZC-ETH,XZC-BNB,LSK-BTC,LSK-ETH,LSK-BNB,TNT-BTC,TNT-ETH,FUEL-BTC,MANA-BTC,MANA-ETH,BCD-BTC,BCD-ETH,DGD-BTC,DGD-ETH,IOTA-BNB,ADX-BTC,ADX-ETH,ADA-BTC,ADA-ETH,PPT-BTC,PPT-ETH,CMT-BTC,CMT-ETH,CMT-BNB,XLM-BTC,XLM-ETH,XLM-BNB,CND-BTC,CND-ETH,CND-BNB,LEND-BTC,LEND-ETH,WABI-BTC,WABI-ETH,WABI-BNB,LTC-ETH,LTC-USDT,LTC-BNB,TNB-BTC,TNB-ETH,WAVES-BTC,WAVES-ETH,WAVES-BNB,GTO-BTC,GTO-ETH,GTO-BNB,ICX-BTC,ICX-ETH,ICX-BNB,OST-BTC,OST-ETH,OST-BNB,ELF-BTC,ELF-ETH,AION-BTC,AION-ETH,AION-BNB,NEBL-BTC,NEBL-ETH,NEBL-BNB,BRD-BTC,BRD-ETH,BRD-BNB,MCO-BNB,EDO-BTC,EDO-ETH,NAV-BTC,LUN-BTC,APPC-BTC,APPC-ETH,APPC-BNB,VIBE-BTC,VIBE-ETH,RLC-BTC,RLC-ETH,RLC-BNB,INS-BTC,INS-ETH,PIVX-BTC,PIVX-ETH,PIVX-BNB,IOST-BTC,IOST-ETH,STEEM-BTC,STEEM-ETH,STEEM-BNB,NANO-BTC,NANO-ETH,NANO-BNB,VIA-BTC,VIA-ETH,VIA-BNB,BLZ-BTC,BLZ-ETH,BLZ-BNB,AE-BTC,AE-ETH,AE-BNB,NCASH-BTC,NCASH-ETH,POA-BTC,POA-ETH,ZIL-BTC,ZIL-ETH,ZIL-BNB,ONT-BTC,ONT-ETH,ONT-BNB,STORM-BTC,STORM-ETH,STORM-BNB,QTUM-BNB,QTUM-USDT,XEM-BTC,XEM-ETH,XEM-BNB,WAN-BTC,WAN-ETH,WAN-BNB,WPR-BTC,WPR-ETH,QLC-BTC,QLC-ETH,SYS-BTC,SYS-ETH,SYS-BNB,QLC-BNB,GRS-BTC,GRS-ETH,ADA-USDT,ADA-BNB,GNT-BTC,GNT-ETH,LOOM-BTC,LOOM-ETH,LOOM-BNB,XRP-USDT,REP-BTC,REP-ETH,BTC-TUSD,ETH-TUSD,ZEN-BTC,ZEN-ETH,ZEN-BNB,SKY-BTC,SKY-ETH,SKY-BNB,EOS-USDT,EOS-BNB,CVC-BTC,CVC-ETH,THETA-BTC,THETA-ETH,THETA-BNB,XRP-BNB,TUSD-USDT,IOTA-USDT,XLM-USDT,IOTX-BTC,IOTX-ETH,QKC-BTC,QKC-ETH,AGI-BTC,AGI-ETH,AGI-BNB,NXS-BTC,NXS-ETH,NXS-BNB,ENJ-BNB,DATA-BTC,DATA-ETH,ONT-USDT,TRX-BNB,TRX-USDT,ETC-USDT,ETC-BNB,ICX-USDT,SC-BTC,SC-ETH,SC-BNB,NPXS-ETH,KEY-BTC,KEY-ETH,NAS-BTC,NAS-ETH,NAS-BNB,MFT-BTC,MFT-ETH,MFT-BNB,DENT-ETH,ARDR-BTC,ARDR-ETH,NULS-USDT,HOT-BTC,HOT-ETH,VET-BTC,VET-ETH,VET-USDT,VET-BNB,DOCK-BTC,DOCK-ETH,POLY-BTC,POLY-BNB,HC-BTC,HC-ETH,GO-BTC,GO-BNB,PAX-USDT,RVN-BTC,RVN-BNB,DCR-BTC,DCR-BNB,MITH-BTC,MITH-BNB,BNB-PAX,BTC-PAX,ETH-PAX,XRP-PAX,EOS-PAX,XLM-PAX,REN-BTC,REN-BNB,BNB-TUSD,XRP-TUSD,EOS-TUSD,XLM-TUSD,BNB-USDC,BTC-USDC,ETH-USDC,XRP-USDC,EOS-USDC,XLM-USDC,USDC-USDT,ADA-TUSD,TRX-TUSD,NEO-TUSD,TRX-XRP,XZC-XRP,PAX-TUSD,USDC-TUSD,USDC-PAX,LINK-USDT,LINK-TUSD,LINK-PAX,LINK-USDC,WAVES-USDT,WAVES-TUSD,WAVES-USDC,LTC-TUSD,LTC-PAX,LTC-USDC,TRX-PAX,TRX-USDC,BTT-BNB,BTT-USDT,BNB-USDS,BTC-USDS,USDS-USDT,USDS-PAX,USDS-TUSD,USDS-USDC,BTT-PAX,BTT-TUSD,BTT-USDC,ONG-BNB,ONG-BTC,ONG-USDT,HOT-BNB,HOT-USDT,ZIL-USDT,ZRX-BNB,ZRX-USDT,FET-BNB,FET-BTC,FET-USDT,BAT-USDT,XMR-BNB,XMR-USDT,ZEC-BNB,ZEC-USDT,ZEC-PAX,ZEC-TUSD,ZEC-USDC,IOST-BNB,IOST-USDT,CELR-BNB,CELR-BTC,CELR-USDT,ADA-PAX,ADA-USDC,NEO-PAX,NEO-USDC,DASH-BNB,DASH-USDT,NANO-USDT,OMG-BNB,OMG-USDT,THETA-USDT,ENJ-USDT,MITH-USDT,MATIC-BNB,MATIC-BTC,MATIC-USDT,ATOM-BNB,ATOM-BTC,ATOM-USDT,ATOM-USDC,ATOM-TUSD,ETC-TUSD,BAT-USDC,BAT-PAX,BAT-TUSD,PHB-BNB,PHB-BTC,PHB-TUSD,TFUEL-BNB,TFUEL-BTC,TFUEL-USDT,ONE-BNB,ONE-BTC,ONE-USDT,ONE-USDC,FTM-BNB,FTM-BTC,FTM-USDT,FTM-USDC,ALGO-BNB,ALGO-BTC,ALGO-USDT,ALGO-TUSD,ALGO-PAX,ALGO-USDC,GTO-USDT,ERD-BNB,ERD-BTC,ERD-USDT,DOGE-BNB,DOGE-BTC,DOGE-USDT,DUSK-BNB,DUSK-BTC,DUSK-USDT,DUSK-USDC,DUSK-PAX,BGBP-USDC,ANKR-BNB,ANKR-BTC,ANKR-USDT,ONT-PAX,ONT-USDC,WIN-BNB,WIN-USDT,WIN-USDC,COS-BNB,COS-BTC,COS-USDT,NPXS-USDT,COCOS-BNB,COCOS-BTC,COCOS-USDT,MTL-USDT,TOMO-BNB,TOMO-BTC,TOMO-USDT,TOMO-USDC,PERL-BNB,PERL-BTC,PERL-USDT,DENT-USDT,MFT-USDT,KEY-USDT,STORM-USDT,DOCK-USDT,WAN-USDT,FUN-USDT,CVC-USDT,BTT-TRX,WIN-TRX,CHZ-BNB,CHZ-BTC,CHZ-USDT,BAND-BNB,BAND-BTC,BAND-USDT,BNB-BUSD,BTC-BUSD,BUSD-USDT,BEAM-BNB,BEAM-BTC,BEAM-USDT,XTZ-BNB,XTZ-BTC,XTZ-USDT,REN-USDT,RVN-USDT,HC-USDT,HBAR-BNB,HBAR-BTC,HBAR-USDT,NKN-BNB,NKN-BTC,NKN-USDT,XRP-BUSD,ETH-BUSD,LTC-BUSD,LINK-BUSD,ETC-BUSD,STX-BNB,STX-BTC,STX-USDT,KAVA-BNB,KAVA-BTC,KAVA-USDT,BUSD-NGN,BNB-NGN,BTC-NGN,ARPA-BNB,ARPA-BTC,ARPA-USDT,TRX-BUSD,EOS-BUSD,IOTX-USDT,RLC-USDT,MCO-USDT,XLM-BUSD,ADA-BUSD,CTXC-BNB,CTXC-BTC,CTXC-USDT,BCH-BNB,BCH-BTC,BCH-USDT,BCH-USDC,BCH-TUSD,BCH-PAX,BCH-BUSD,BTC-RUB,ETH-RUB,XRP-RUB,BNB-RUB,TROY-BNB,TROY-BTC,TROY-USDT,BUSD-RUB,QTUM-BUSD,VET-BUSD"
+ "available": "ETH-BTC,LTC-BTC,BNB-BTC,NEO-BTC,QTUM-ETH,EOS-ETH,SNT-ETH,BNT-ETH,GAS-BTC,BNB-ETH,BTC-USDT,ETH-USDT,MCO-ETH,MCO-BTC,WTC-BTC,WTC-ETH,LRC-BTC,LRC-ETH,QTUM-BTC,YOYO-BTC,OMG-BTC,OMG-ETH,ZRX-BTC,ZRX-ETH,STRAT-BTC,STRAT-ETH,SNGLS-BTC,BQX-BTC,BQX-ETH,KNC-BTC,KNC-ETH,FUN-BTC,FUN-ETH,SNM-BTC,NEO-ETH,IOTA-BTC,IOTA-ETH,LINK-BTC,LINK-ETH,XVG-BTC,XVG-ETH,MDA-BTC,MTL-BTC,MTL-ETH,EOS-BTC,SNT-BTC,ETC-ETH,ETC-BTC,MTH-BTC,ENG-BTC,ENG-ETH,DNT-BTC,ZEC-BTC,ZEC-ETH,BNT-BTC,AST-BTC,DASH-BTC,DASH-ETH,OAX-BTC,BTG-BTC,EVX-BTC,EVX-ETH,REQ-BTC,VIB-BTC,VIB-ETH,TRX-BTC,TRX-ETH,POWR-BTC,POWR-ETH,ARK-BTC,XRP-BTC,XRP-ETH,ENJ-BTC,ENJ-ETH,STORJ-BTC,STORJ-ETH,BNB-USDT,KMD-BTC,KMD-ETH,RCN-BTC,NULS-BTC,NULS-ETH,RDN-BTC,XMR-BTC,XMR-ETH,WTC-BNB,DLT-BTC,AMB-BTC,BAT-BTC,BAT-ETH,BAT-BNB,BCPT-BTC,ARN-BTC,ARN-ETH,GVT-BTC,CDT-BTC,CDT-ETH,GXS-BTC,GXS-ETH,NEO-USDT,NEO-BNB,POE-BTC,QSP-BTC,QSP-ETH,BTS-BTC,XZC-BTC,XZC-ETH,XZC-BNB,LSK-BTC,LSK-ETH,TNT-BTC,TNT-ETH,FUEL-BTC,MANA-BTC,MANA-ETH,BCD-BTC,IOTA-BNB,ADX-BTC,ADX-ETH,ADA-BTC,ADA-ETH,PPT-BTC,CMT-BTC,CMT-ETH,XLM-BTC,XLM-ETH,XLM-BNB,CND-BTC,LEND-BTC,LEND-ETH,WABI-BTC,WABI-BNB,LTC-ETH,LTC-USDT,LTC-BNB,TNB-BTC,WAVES-BTC,WAVES-ETH,WAVES-BNB,GTO-BTC,GTO-ETH,ICX-BTC,ICX-ETH,ICX-BNB,OST-BTC,OST-ETH,ELF-BTC,ELF-ETH,AION-BTC,AION-ETH,AION-BNB,NEBL-BTC,NEBL-ETH,NEBL-BNB,BRD-BTC,BRD-ETH,BRD-BNB,NAV-BTC,LUN-BTC,APPC-BTC,VIBE-BTC,RLC-BTC,RLC-ETH,RLC-BNB,INS-BTC,PIVX-BTC,PIVX-ETH,IOST-BTC,IOST-ETH,STEEM-BTC,STEEM-ETH,STEEM-BNB,NANO-BTC,NANO-ETH,VIA-BTC,BLZ-BTC,BLZ-ETH,BLZ-BNB,AE-BTC,AE-ETH,NCASH-ETH,POA-BTC,ZIL-BTC,ZIL-ETH,ZIL-BNB,ONT-BTC,ONT-ETH,ONT-BNB,QTUM-USDT,XEM-BTC,XEM-ETH,WAN-BTC,WAN-ETH,WAN-BNB,WPR-BTC,QLC-BTC,QLC-ETH,SYS-BTC,GRS-BTC,ADA-USDT,ADA-BNB,GNT-BTC,GNT-ETH,LOOM-BTC,LOOM-ETH,XRP-USDT,REP-BTC,REP-ETH,BTC-TUSD,ETH-TUSD,ZEN-BTC,ZEN-ETH,ZEN-BNB,SKY-BTC,EOS-USDT,EOS-BNB,CVC-BTC,CVC-ETH,THETA-BTC,THETA-ETH,THETA-BNB,XRP-BNB,TUSD-USDT,IOTA-USDT,XLM-USDT,IOTX-BTC,IOTX-ETH,QKC-BTC,QKC-ETH,AGI-BTC,NXS-BTC,ENJ-BNB,DATA-BTC,DATA-ETH,ONT-USDT,TRX-BNB,TRX-USDT,ETC-USDT,ETC-BNB,ICX-USDT,SC-BTC,SC-ETH,SC-BNB,NPXS-ETH,KEY-ETH,NAS-BTC,NAS-ETH,MFT-ETH,MFT-BNB,DENT-ETH,ARDR-BTC,NULS-USDT,HOT-BTC,HOT-ETH,VET-BTC,VET-ETH,VET-USDT,VET-BNB,DOCK-BTC,DOCK-ETH,POLY-BTC,HC-BTC,GO-BTC,PAX-USDT,RVN-BTC,RVN-BNB,DCR-BTC,MITH-BTC,MITH-BNB,BNB-PAX,BTC-PAX,ETH-PAX,XRP-PAX,REN-BTC,BNB-TUSD,XRP-TUSD,EOS-TUSD,BNB-USDC,BTC-USDC,ETH-USDC,XRP-USDC,EOS-USDC,USDC-USDT,ADA-TUSD,TRX-TUSD,NEO-TUSD,TRX-XRP,USDC-TUSD,LINK-USDT,LINK-TUSD,LINK-USDC,WAVES-USDT,LTC-TUSD,LTC-PAX,LTC-USDC,TRX-PAX,TRX-USDC,BTT-BNB,BTT-USDT,BTT-PAX,BTT-TUSD,BTT-USDC,ONG-BTC,ONG-USDT,HOT-BNB,HOT-USDT,ZIL-USDT,ZRX-BNB,ZRX-USDT,FET-BNB,FET-BTC,FET-USDT,BAT-USDT,XMR-BNB,XMR-USDT,ZEC-BNB,ZEC-USDT,ZEC-USDC,IOST-BNB,IOST-USDT,CELR-BNB,CELR-BTC,CELR-USDT,ADA-USDC,NEO-USDC,DASH-BNB,DASH-USDT,NANO-USDT,OMG-USDT,THETA-USDT,ENJ-USDT,MITH-USDT,MATIC-BNB,MATIC-BTC,MATIC-USDT,ATOM-BNB,ATOM-BTC,ATOM-USDT,ATOM-USDC,BAT-USDC,PHB-BTC,PHB-TUSD,TFUEL-BTC,TFUEL-USDT,ONE-BNB,ONE-BTC,ONE-USDT,ONE-USDC,FTM-BNB,FTM-BTC,FTM-USDT,ALGO-BNB,ALGO-BTC,ALGO-USDT,ALGO-TUSD,ALGO-PAX,GTO-USDT,ERD-BNB,ERD-BTC,ERD-USDT,DOGE-BTC,DOGE-USDT,DUSK-BTC,DUSK-USDT,BGBP-USDC,ANKR-BNB,ANKR-BTC,ANKR-USDT,WIN-BNB,WIN-USDT,WIN-USDC,COS-BNB,COS-BTC,COS-USDT,NPXS-USDT,COCOS-BNB,COCOS-USDT,MTL-USDT,TOMO-BNB,TOMO-BTC,TOMO-USDT,PERL-BNB,PERL-BTC,PERL-USDT,DENT-USDT,MFT-USDT,KEY-USDT,DOCK-USDT,WAN-USDT,FUN-USDT,CVC-USDT,BTT-TRX,WIN-TRX,CHZ-BNB,CHZ-BTC,CHZ-USDT,BAND-BNB,BAND-BTC,BAND-USDT,BNB-BUSD,BTC-BUSD,BUSD-USDT,BEAM-BTC,BEAM-USDT,XTZ-BNB,XTZ-BTC,XTZ-USDT,REN-USDT,RVN-USDT,HC-USDT,HBAR-BNB,HBAR-BTC,HBAR-USDT,NKN-BNB,NKN-BTC,NKN-USDT,XRP-BUSD,ETH-BUSD,LTC-BUSD,LINK-BUSD,ETC-BUSD,STX-BNB,STX-BTC,STX-USDT,KAVA-BNB,KAVA-BTC,KAVA-USDT,BUSD-NGN,BNB-NGN,BTC-NGN,ARPA-BNB,ARPA-BTC,ARPA-USDT,TRX-BUSD,EOS-BUSD,IOTX-USDT,RLC-USDT,MCO-USDT,XLM-BUSD,ADA-BUSD,CTXC-BTC,CTXC-USDT,BCH-BNB,BCH-BTC,BCH-USDT,BCH-USDC,BCH-TUSD,BCH-PAX,BCH-BUSD,BTC-RUB,ETH-RUB,XRP-RUB,BNB-RUB,TROY-BNB,TROY-BTC,TROY-USDT,BUSD-RUB,QTUM-BUSD,VET-BUSD,VITE-BTC,VITE-USDT,FTT-BNB,FTT-BTC,FTT-USDT,BTC-TRY,BNB-TRY,BUSD-TRY,ETH-TRY,XRP-TRY,USDT-TRY,USDT-RUB,BTC-EUR,ETH-EUR,BNB-EUR,XRP-EUR,EUR-BUSD,EUR-USDT,OGN-BNB,OGN-BTC,OGN-USDT,DREP-BTC,DREP-USDT,TCT-BTC,TCT-USDT,WRX-BNB,WRX-BTC,WRX-USDT,ICX-BUSD,BTS-USDT,BTS-BUSD,LSK-USDT,BNT-USDT,BNT-BUSD,LTO-BTC,LTO-USDT,ATOM-BUSD,DASH-BUSD,NEO-BUSD,WAVES-BUSD,XTZ-BUSD,BAT-BUSD,ENJ-BUSD,NANO-BUSD,ONT-BUSD,RVN-BUSD,STRAT-BUSD,STRAT-USDT,AION-BUSD,AION-USDT,MBL-BNB,MBL-BTC,MBL-USDT,COTI-BNB,COTI-BTC,COTI-USDT,ALGO-BUSD,BTT-BUSD,TOMO-BUSD,XMR-BUSD,ZEC-BUSD,STPT-BTC,STPT-USDT,BTC-ZAR,ETH-ZAR,BNB-ZAR,USDT-ZAR,BUSD-ZAR,BTC-BKRW,ETH-BKRW,BNB-BKRW,WTC-USDT,DATA-BUSD,DATA-USDT,XZC-USDT,SOL-BNB,SOL-BTC,SOL-BUSD,BTC-IDRT,BNB-IDRT,USDT-IDRT,BUSD-IDRT,CTSI-BTC,CTSI-USDT,CTSI-BNB,CTSI-BUSD,HIVE-BNB,HIVE-BTC,HIVE-USDT,CHR-BNB,CHR-BTC,CHR-USDT,BTCUP-USDT,BTCDOWN-USDT,GXS-USDT,ARDR-USDT,ERD-BUSD,LEND-USDT,HBAR-BUSD,MATIC-BUSD,WRX-BUSD,ZIL-BUSD,MDT-BNB,MDT-BTC,MDT-USDT,STMX-BNB,STMX-BTC,STMX-ETH,STMX-USDT,KNC-BUSD,KNC-USDT,REP-BUSD,REP-USDT,LRC-BUSD,LRC-USDT,IQ-BNB,IQ-BUSD,PNT-BTC,PNT-USDT,BTC-GBP,ETH-GBP,XRP-GBP,BNB-GBP,GBP-BUSD,DGB-BNB,DGB-BTC,DGB-BUSD,BTC-UAH,USDT-UAH,COMP-BTC,COMP-BNB,COMP-BUSD,COMP-USDT"
}
}
},
@@ -326,7 +338,7 @@
"pairs": {
"spot": {
"enabled": "BTCUSD,LTCUSD,LTCBTC,ETHUSD,ETHBTC",
- "available": "BTCUSD,LTCUSD,LTCBTC,ETHUSD,ETHBTC,ETCBTC,ETCUSD,RRTUSD,RRTBTC,ZECUSD,ZECBTC,XMRUSD,XMRBTC,DSHUSD,DSHBTC,BTCEUR,BTCJPY,XRPUSD,XRPBTC,IOTUSD,IOTBTC,IOTETH,EOSUSD,EOSBTC,EOSETH,SANUSD,SANBTC,SANETH,OMGUSD,OMGBTC,OMGETH,NEOUSD,NEOBTC,NEOETH,ETPUSD,ETPBTC,ETPETH,QTMUSD,QTMBTC,QTMETH,AVTUSD,AVTBTC,AVTETH,EDOUSD,EDOBTC,EDOETH,BTGUSD,BTGBTC,DATUSD,DATBTC,DATETH,QSHUSD,QSHBTC,QSHETH,YYWUSD,YYWBTC,YYWETH,GNTUSD,GNTBTC,GNTETH,SNTUSD,SNTBTC,SNTETH,IOTEUR,BATUSD,BATBTC,BATETH,MNAUSD,MNABTC,MNAETH,FUNUSD,FUNBTC,FUNETH,ZRXUSD,ZRXBTC,ZRXETH,TNBUSD,TNBBTC,TNBETH,SPKUSD,SPKBTC,SPKETH,TRXUSD,TRXBTC,TRXETH,RCNUSD,RCNBTC,RCNETH,RLCUSD,RLCBTC,RLCETH,AIDUSD,AIDBTC,AIDETH,SNGUSD,SNGBTC,SNGETH,REPUSD,REPBTC,REPETH,ELFUSD,ELFBTC,ELFETH,NECUSD,NECBTC,NECETH,BTCGBP,ETHEUR,ETHJPY,ETHGBP,NEOEUR,NEOJPY,NEOGBP,EOSEUR,EOSJPY,EOSGBP,IOTJPY,IOTGBP,IOSUSD,IOSBTC,IOSETH,AIOUSD,AIOBTC,AIOETH,REQUSD,REQBTC,REQETH,RDNUSD,RDNBTC,RDNETH,LRCUSD,LRCBTC,LRCETH,WAXUSD,WAXBTC,WAXETH,DAIUSD,DAIBTC,DAIETH,AGIUSD,AGIBTC,AGIETH,BFTUSD,BFTBTC,BFTETH,MTNUSD,MTNBTC,MTNETH,ODEUSD,ODEBTC,ODEETH,ANTUSD,ANTBTC,ANTETH,DTHUSD,DTHBTC,DTHETH,MITUSD,MITBTC,MITETH,STJUSD,STJBTC,STJETH,XLMUSD,XLMEUR,XLMJPY,XLMGBP,XLMBTC,XLMETH,XVGUSD,XVGEUR,XVGJPY,XVGGBP,XVGBTC,XVGETH,BCIUSD,BCIBTC,MKRUSD,MKRBTC,MKRETH,KNCUSD,KNCBTC,KNCETH,POAUSD,POABTC,POAETH,EVTUSD,LYMUSD,LYMBTC,LYMETH,UTKUSD,UTKBTC,UTKETH,VEEUSD,VEEBTC,VEEETH,DADUSD,DADBTC,DADETH,ORSUSD,ORSBTC,ORSETH,AUCUSD,AUCBTC,AUCETH,POYUSD,POYBTC,POYETH,FSNUSD,FSNBTC,FSNETH,CBTUSD,CBTBTC,CBTETH,ZCNUSD,ZCNBTC,ZCNETH,SENUSD,SENBTC,SENETH,NCAUSD,NCABTC,NCAETH,CNDUSD,CNDBTC,CNDETH,CTXUSD,CTXBTC,CTXETH,PAIUSD,PAIBTC,SEEUSD,SEEBTC,SEEETH,ESSUSD,ESSBTC,ESSETH,ATMUSD,ATMBTC,ATMETH,HOTUSD,HOTBTC,HOTETH,DTAUSD,DTABTC,DTAETH,IQXUSD,IQXBTC,IQXEOS,WPRUSD,WPRBTC,WPRETH,ZILUSD,ZILBTC,ZILETH,BNTUSD,BNTBTC,BNTETH,ABSUSD,ABSETH,XRAUSD,XRAETH,MANUSD,MANETH,BBNUSD,BBNETH,NIOUSD,NIOETH,DGXUSD,DGXETH,VETUSD,VETBTC,VETETH,UTNUSD,UTNETH,TKNUSD,TKNETH,GOTUSD,GOTEUR,GOTETH,XTZUSD,XTZBTC,CNNUSD,CNNETH,BOXUSD,BOXETH,TRXEUR,TRXGBP,TRXJPY,MGOUSD,MGOETH,RTEUSD,RTEETH,YGGUSD,YGGETH,MLNUSD,MLNETH,WTCUSD,WTCETH,CSXUSD,CSXETH,OMNUSD,OMNBTC,INTUSD,INTETH,DRNUSD,DRNETH,PNKUSD,PNKETH,DGBUSD,DGBBTC,BSVUSD,BSVBTC,BABUSD,BABBTC,WLOUSD,WLOXLM,VLDUSD,VLDETH,ENJUSD,ENJETH,ONLUSD,ONLETH,RBTUSD,RBTBTC,USTUSD,EUTEUR,EUTUSD,GSDUSD,UDCUSD,TSDUSD,PAXUSD,RIFUSD,RIFBTC,PASUSD,PASETH,VSYUSD,VSYBTC,ZRXDAI,MKRDAI,OMGDAI,BTTUSD,BTTBTC,BTCUST,ETHUST,CLOUSD,CLOBTC,IMPUSD,IMPETH,LTCUST,EOSUST,BABUST,SCRUSD,SCRETH,GNOUSD,GNOETH,GENUSD,GENETH,ATOUSD,ATOBTC,ATOETH,WBTUSD,XCHUSD,EUSUSD,WBTETH,XCHETH,EUSETH,LEOUSD,LEOBTC,LEOUST,LEOEOS,LEOETH,ASTUSD,ASTETH,FOAUSD,FOAETH,UFRUSD,UFRETH,ZBTUSD,ZBTUST,OKBUSD,USKUSD,GTXUSD,KANUSD,OKBUST,OKBETH,OKBBTC,USKUST,USKETH,USKBTC,USKEOS,GTXUST,KANUST,AMPUSD,ALGUSD,ALGBTC,ALGUST,BTCXCH,SWMUSD,SWMETH,TRIUSD,TRIETH,LOOUSD,LOOETH,AMPUST,DUSK:USD,DUSK:BTC,UOSUSD,UOSBTC,RRBUSD,RRBUST,DTXUSD,DTXUST,AMPBTC,FTTUSD,FTTUST,PAXUST,UDCUST,TSDUST,BTC:CNHT,UST:CNHT,CNH:CNHT,CHZUSD,CHZUST,BTCF0:USTF0,ETHF0:USTF0"
+ "available": "XMRUSD,IOTUSD,MLNUSD,OMGBTC,SNGUSD,ODEUSD,CSXUSD,MITUSD,XLMUSD,RRTUSD,ZECBTC,BATBTC,TRXETH,ETHGBP,IOSETH,MKRDAI,CHZUST,BTCGBP,WAXUSD,USTUSD,GENUSD,LEOETH,NEOBTC,RCNUSD,REPUSD,ATMUSD,RBTUSD,EUTUSD,NEOUSD,KNCBTC,UTKUSD,RIFBTC,TESTBTC:TESTUSDT,IOTETH,ELFUSD,WTCUSD,BTTUSD,CLOUSD,GTXUST,REPBTC,ANTUSD,XTZUSD,EUTEUR,UST:CNHT,DSHBTC,SPKUSD,CNDETH,FOAUSD,KANUSD,BTC:CNHT,AAABBB,NEOETH,ETHEUR,BFTUSD,XCHUSD,SWMUSD,AMPBTC,UFRUSD,SANETH,ETHJPY,IOSUSD,MKRUSD,BSVUSD,EOSUST,TRXBTC,IOTJPY,REQUSD,DAIBTC,XVGBTC,VETUSD,AIOUSD,ZILUSD,RBTBTC,AMPUSD,SANUSD,XLMEUR,SCRUSD,ETHBTC,LRCUSD,MKRETH,LOOUSD,XAUT:BTC,GOTETH,TRIUSD,XAUT:USD,QSHUSD,TRXUSD,LRCBTC,DUSK:BTC,XAUTF0:USTF0,XRAUSD,DGXETH,ATOBTC,RRBUST,ETPBTC,NECBTC,CNDUSD,YGGUSD,LEOBTC,ALGUSD,ASTUSD,OKBUSD,AMPUST,TSDUST,ETHUSD,MNAUSD,IQXEOS,BTTBTC,CLOBTC,FTTUST,LYMUSD,BTGUSD,DATUSD,NCAUSD,DGXUSD,VETBTC,LEOUSD,AIOBTC,DAIETH,XMRBTC,GNTBTC,MNABTC,FUNUSD,TNBETH,EOSGBP,TRXJPY,WBTUSD,LEOEOS,ZBTUSD,ETPUSD,GNTETH,NECETH,IOTGBP,ENJUSD,ETHUST,BTCJPY,POYUSD,MANUSD,GOTEUR,MGOUSD,BTCXCH,XTZBTC,BOXUSD,DGBBTC,GSDUSD,USKUSD,BTGBTC,TESTBTC:TESTUSD,OKBBTC,BTSE:USD,ETCUSD,NEOEUR,ORSUSD,WPRUSD,TSDUSD,EUSUSD,RRBUSD,AUCUSD,HOTUSD,RIFUSD,XLMETH,TRXGBP,USKBTC,EOSEUR,IOSBTC,STJUSD,LEOUST,ALGUST,QTMUSD,OKBUST,DRNUSD,IMPUSD,IOTEUR,ZRXUSD,ZRXETH,ODEETH,CBTUSD,TRXEUR,ALGBTC,LTCUST,KANUST,BATETH,FUNETH,ABSUSD,GOTUSD,PNKETH,ONLUSD,BABUST,ETCBTC,SNTETH,FSNUSD,PNKUSD,BTCF0:USTF0,DUSK:USD,BTCUSD,GNTUSD,TNBUSD,USKETH,DSHUSD,BTCEUR,EOSETH,ANTBTC,BTCDOMF0:USTF0,EDOUSD,CNNUSD,ETPETH,BABUSD,ETHF0:USTF0,OMGETH,WLOUSD,GNOUSD,IOTBTC,ATOETH,UTNUSD,RTEUSD,SANBTC,RLCUSD,RLCBTC,RDNUSD,ODEBTC,XVGUSD,BABBTC,LTCUSD,ZRXBTC,SEEUSD,CNH:CNHT,SNTBTC,PAXUSD,VSYUSD,DTXUSD,XLMBTC,DTAUSD,ATOUSD,XRPBTC,NEOGBP,IQXUSD,PASUSD,BSVBTC,UDCUSD,QTMBTC,NEOJPY,DAIUSD,DTHUSD,XLMGBP,POAUSD,XCHETH,FTTUSD,YYWUSD,CHZUSD,XAUT:UST,DATETH,REPETH,CTXUSD,BNTUSD,USKEOS,UOSBTC,EOSBTC,OMGUSD,WAXBTC,AGIUSD,EOSUSD,EDOETH,INTUSD,UOSUSD,NECUSD,ANTETH,USKUST,VEEUSD,OMNBTC,DGBUSD,BATUSD,PAIUSD,VLDUSD,PAXUST,EDOBTC,SNTUSD,BFTBTC,BTCUST,GTXUSD,RINGX:USD,EOSJPY,MKRBTC,KNCUSD,OMNUSD,WBTETH,AVTUSD,DATBTC,ZCNUSD,TKNUSD,LTCBTC,ZECUSD,MTNUSD,ESSUSD,VSYBTC,UDCUST,XRPUSD,TNBBTC,AIDUSD"
}
}
},
@@ -482,7 +494,7 @@
"pairs": {
"spot": {
"enabled": "BTCKRW,ETHKRW,DASHKRW,LTCKRW,ETCKRW,XRPKRW,BCHKRW,XMRKRW,ZECKRW,QTUMKRW,BTGKRW,EOSKRW",
- "available": "STRATKRW,ETHOSKRW,ETHKRW,AMOKRW,ZRXKRW,THETAKRW,TMTGKRW,VETKRW,LOOMKRW,BHPKRW,REPKRW,AUTOKRW,WICCKRW,QTUMKRW,ETCKRW,WETKRW,HCKRW,OGOKRW,BCHKRW,POLYKRW,PIVXKRW,CTXCKRW,ETZKRW,BTGKRW,AOAKRW,MXCKRW,EOSKRW,ADAKRW,PPTKRW,BCDKRW,ICXKRW,OMGKRW,IOSTKRW,VALORKRW,WOMKRW,LAMBKRW,DASHKRW,XSRKRW,WTCKRW,CROKRW,KNCKRW,FZZKRW,FABKRW,FCTKRW,PAYKRW,MIXKRW,LRCKRW,HDACKRW,WAVESKRW,LTCKRW,FXKRW,DVPKRW,DADKRW,RDNKRW,ZECKRW,XEMKRW,MBLKRW,CMTKRW,APISKRW,NPXSKRW,TRUEKRW,XRPKRW,STEEMKRW,MITHKRW,PCMKRW,QKCKRW,LBAKRW,ZILKRW,ORBSKRW,XLMKRW,CONKRW,MTLKRW,OCNKRW,HYCKRW,GTOKRW,ELFKRW,AEKRW,TRXKRW,ITCKRW,ENJKRW,XMRKRW,MCOKRW,DACKRW,PLYKRW,FNBKRW,LINKKRW,POWRKRW,XVGKRW,BTTKRW,BZNTKRW,GXCKRW,CHRKRW,BSVKRW,ARNKRW,SALTKRW,GNTKRW,SNTKRW,INSKRW,TRVKRW,WAXPKRW,BTCKRW,BATKRW,ANKRKRW,RNTKRW,ABTKRW"
+ "available": "FNBKRW,ETHKRW,MTLKRW,SNTKRW,BATKRW,OGOKRW,BTCKRW,DADKRW,BCHKRW,FABKRW,MBLKRW,ADAKRW,MXCKRW,SXPKRW,STRATKRW,LAMBKRW,FLETAKRW,EMKRW,ZILKRW,CTXCKRW,OMGKRW,CROKRW,LRCKRW,LUNAKRW,DASHKRW,QBZKRW,COSKRW,TRVKRW,LINKKRW,MCOKRW,VALORKRW,EGGKRW,LBAKRW,PCMKRW,FCTKRW,ITCKRW,ENJKRW,SOCKRW,METAKRW,VSYSKRW,DACKRW,EOSKRW,REPKRW,XLMKRW,ICXKRW,CONKRW,BTTKRW,XRPKRW,ZECKRW,QTUMKRW,BTGKRW,XSRKRW,XEMKRW,FXKRW,THETAKRW,RNTKRW,WTCKRW,LTCKRW,TRUEKRW,GXCKRW,NPXSKRW,BASICKRW,AIONKRW,PIVXKRW,AMOKRW,TRXKRW,QKCKRW,ETCKRW,AEKRW,IOSTKRW,ORBSKRW,IPXKRW,ZRXKRW,XPRKRW,APIXKRW,MIXKRW,WAXPKRW,BSVKRW,GNTKRW,BOAKRW,DVPKRW,WICCKRW,ARPAKRW,LOOMKRW,FZZKRW,COSMKRW,ELFKRW,HDACKRW,VETKRW,HYCKRW,POWRKRW,WETKRW,BCDKRW,ELKRW,AOAKRW,WOMKRW,WAVESKRW,ANKRKRW,TMTGKRW,CHRKRW,PLXKRW,BZNTKRW,INSKRW,KNCKRW,STEEMKRW"
}
}
},
@@ -671,7 +683,7 @@
"pairs": {
"spot": {
"enabled": "BTCUSD,BTCEUR,EURUSD,XRPUSD,XRPEUR",
- "available": "LTCUSD,ETHUSD,XRPEUR,BCHUSD,BCHEUR,BTCEUR,XRPBTC,EURUSD,BCHBTC,LTCEUR,BTCUSD,LTCBTC,XRPUSD,ETHBTC,ETHEUR"
+ "available": "BTCUSD,BTCEUR,BTCGBP,BTCPAX,GBPUSD,GBPEUR,EURUSD,XRPUSD,XRPEUR,XRPBTC,XRPGBP,XRPPAX,LTCUSD,LTCEUR,LTCBTC,LTCGBP,ETHUSD,ETHEUR,ETHBTC,ETHGBP,ETHPAX,BCHUSD,BCHEUR,BCHBTC,BCHGBP,PAXUSD,PAXEUR,PAXGBP,XLMBTC,XLMUSD,XLMEUR,XLMGBP"
}
}
},
@@ -750,7 +762,7 @@
"pairs": {
"spot": {
"enabled": "USDT-BTC",
- "available": "BTC-LTC,BTC-DOGE,BTC-VTC,BTC-PPC,BTC-FTC,BTC-RDD,BTC-NXT,BTC-DASH,BTC-POT,BTC-BLK,BTC-EMC2,BTC-XMY,BTC-GRS,BTC-NLG,BTC-MONA,BTC-VRC,BTC-CURE,BTC-XMR,BTC-XDN,BTC-NAV,BTC-XST,BTC-VIA,BTC-PINK,BTC-IOC,BTC-SYS,BTC-DGB,BTC-BURST,BTC-EXCL,BTC-BLOCK,BTC-BTS,BTC-XRP,BTC-GAME,BTC-NXS,BTC-GEO,BTC-FLO,BTC-MUE,BTC-XEM,BTC-SPHR,BTC-OK,BTC-AEON,BTC-ETH,BTC-EXP,BTC-XLM,USDT-BTC,BTC-FCT,BTC-MAID,BTC-SLS,BTC-RADS,BTC-DCR,BTC-XVG,BTC-PIVX,BTC-MEME,BTC-STEEM,BTC-LSK,BTC-WAVES,BTC-LBC,BTC-SBD,BTC-ETC,ETH-ETC,BTC-STRAT,BTC-REP,BTC-ARDR,BTC-XZC,BTC-NEO,BTC-ZEC,BTC-UBQ,BTC-KMD,BTC-SIB,BTC-ION,BTC-CRW,BTC-ARK,BTC-INCNT,BTC-GBYTE,BTC-GNT,BTC-EDG,BTC-MORE,ETH-GNT,ETH-REP,USDT-ETH,BTC-RLC,BTC-GNO,BTC-GUP,ETH-GNO,BTC-HMQ,BTC-ANT,ETH-ANT,BTC-SC,ETH-BAT,BTC-BAT,BTC-ZEN,BTC-1ST,BTC-QRL,BTC-PTOY,BTC-BNT,ETH-BNT,BTC-NMR,ETH-LTC,ETH-XRP,BTC-SNT,ETH-SNT,BTC-DCT,BTC-XEL,BTC-MCO,ETH-MCO,BTC-ADT,BTC-PAY,ETH-PAY,BTC-MTL,BTC-STORJ,BTC-ADX,ETH-ADX,ETH-DASH,ETH-SC,ETH-ZEC,USDT-ZEC,USDT-LTC,USDT-ETC,USDT-XRP,BTC-OMG,ETH-OMG,BTC-CVC,ETH-CVC,BTC-PART,BTC-QTUM,ETH-QTUM,ETH-XMR,ETH-XEM,ETH-XLM,ETH-NEO,USDT-XMR,USDT-DASH,ETH-BCH,USDT-BCH,BTC-BCH,BTC-DNT,USDT-NEO,ETH-WAVES,ETH-STRAT,ETH-DGB,USDT-OMG,BTC-ADA,BTC-MANA,ETH-MANA,BTC-RCN,BTC-VIB,ETH-VIB,BTC-MER,ETH-ADA,BTC-ENG,ETH-ENG,USDT-ADA,USDT-XVG,BTC-UKG,ETH-UKG,BTC-IGNIS,BTC-SRN,ETH-SRN,BTC-WAXP,ETH-WAXP,BTC-ZRX,ETH-ZRX,BTC-VEE,BTC-TRX,ETH-TRX,BTC-TUSD,BTC-LRC,ETH-TUSD,BTC-DMT,ETH-DMT,USDT-TUSD,USDT-SC,USDT-TRX,BTC-STORM,ETH-STORM,BTC-AID,BTC-GTO,USDT-DCR,USD-BTC,USD-USDT,USD-TUSD,BTC-TUBE,BTC-CMCT,USD-ETH,BTC-NLC2,BTC-BKX,BTC-MFT,BTC-LOOM,BTC-RFR,USDT-DGB,BTC-RVN,USD-XRP,USD-ETC,BTC-BFT,BTC-GO,BTC-HYDRO,BTC-UPP,USD-ADA,USD-ZEC,USDT-DOGE,BTC-ENJ,BTC-MET,USD-LTC,USD-TRX,BTC-DTA,BTC-EDR,BTC-IHT,USD-BCH,BTC-XHV,USDT-ZRX,BTC-NPXS,BTC-PMA,USDT-BAT,USDT-RVN,BTC-PAL,USD-SC,BTC-PAX,BTC-ZIL,BTC-MOC,BTC-OST,BTC-SPC,BTC-MED,BTC-BSV,BTC-IOST,USDT-BSV,ETH-BSV,BTC-SOLVE,BTC-USDS,USDT-PMA,ETH-NPXS,USDT-NPXS,USD-ZRX,BTC-JNT,BTC-LBA,USD-BAT,USD-BSV,BTC-DENT,USD-USDS,BTC-DRGN,USD-PAX,BTC-VITE,BTC-IOTX,USD-DGB,BTC-BTM,BTC-ELF,BTC-QNT,BTC-BTU,USD-ZEN,BTC-SPND,BTC-BTT,BTC-NKN,USD-KMD,USDT-BTT,BTC-GRIN,BTC-CTXC,BTC-HXRO,BTC-META,USDT-GRIN,BTC-FSN,BTC-ANKR,USDT-XLM,BTC-TRAC,BTC-CRO,BTC-ONT,ETH-SOLVE,BTC-ONG,BTC-TTC,BTC-PTON,BTC-PI,ETH-ANKR,BTC-PLA,BTC-ART,BTC-ORBS,USDT-ENJ,BTC-VBK,BTC-BORA,BTC-CND,USDT-ONT,BTC-FX,ETH-FX,BTC-ATOM,USDT-ATOM,ETH-ATOM,BTC-OCEAN,USDT-OCEAN,BTC-BWX,BTC-VDX,USDT-VDX,ETH-VDX,BTC-COSM,BTC-LAMB,BTC-STPT,BTC-DAI,ETH-DAI,USDT-DAI,BTC-CPT,BTC-FNB,BTC-PROM,BTC-ABYSS,BTC-EOS,ETH-EOS,USDT-EOS,BTC-FXC,BTC-DUSK,BTC-URAC,BTC-BLOC,BTC-TEMCO,BTC-SPIN,BTC-LUNA,BTC-CHR,BTC-TUDA,BTC-UTK,BTC-PXL,BTC-AKRO,BTC-TSHP,BTC-HEDG,BTC-MRPH,BTC-HBAR,ETH-HBAR,USD-HBAR,USDT-HBAR,BTC-PLG,BTC-VET,USDT-VET,BTC-SIX,BTC-WGP,BTC-APM,BTC-FLETA,USD-DCR,BTC-BLTV,BTC-HDAC,BTC-LINK,USD-EOS,BTC-APIX"
+ "available": "BTC-LTC,BTC-DOGE,BTC-VTC,BTC-PPC,BTC-FTC,BTC-RDD,BTC-NXT,BTC-DASH,BTC-POT,BTC-BLK,BTC-EMC2,BTC-XMY,BTC-GRS,BTC-NLG,BTC-MONA,BTC-VRC,BTC-CURE,BTC-XMR,BTC-XDN,BTC-NAV,BTC-XST,BTC-AR,BTC-VIA,BTC-PINK,BTC-IOC,BTC-SYS,BTC-DGB,BTC-BURST,BTC-EXCL,BTC-BLOCK,BTC-BTS,BTC-XRP,BTC-GAME,BTC-NXS,BTC-GEO,BTC-FLO,BTC-MUE,BTC-XEM,BTC-SPHR,BTC-OK,BTC-AEON,BTC-ETH,BTC-EXP,BTC-XLM,USDT-BTC,BTC-FCT,BTC-MAID,BTC-SLS,BTC-RADS,BTC-DCR,BTC-XVG,BTC-PIVX,BTC-MEME,BTC-STEEM,BTC-LSK,BTC-WAVES,BTC-LBC,BTC-SBD,BTC-ETC,ETH-ETC,BTC-STRAT,BTC-REP,BTC-ARDR,BTC-XZC,BTC-NEO,BTC-ZEC,BTC-UBQ,BTC-KMD,BTC-SIB,BTC-ION,BTC-CRW,BTC-ARK,BTC-INCNT,BTC-GBYTE,BTC-GNT,BTC-EDG,BTC-MORE,ETH-GNT,ETH-REP,USDT-ETH,BTC-WINGS,BTC-RLC,BTC-GNO,BTC-GUP,ETH-GNO,BTC-HMQ,BTC-ANT,ETH-ANT,BTC-SC,ETH-BAT,BTC-BAT,BTC-ZEN,BTC-QRL,BTC-PTOY,BTC-BNT,ETH-BNT,BTC-NMR,ETH-NMR,ETH-LTC,ETH-XRP,BTC-SNT,ETH-SNT,BTC-DCT,BTC-XEL,BTC-MCO,ETH-MCO,BTC-ADT,BTC-PAY,ETH-PAY,BTC-MTL,BTC-STORJ,BTC-ADX,ETH-ADX,ETH-DASH,ETH-SC,ETH-ZEC,USDT-ZEC,USDT-LTC,USDT-ETC,USDT-XRP,BTC-OMG,ETH-OMG,BTC-CVC,ETH-CVC,BTC-PART,BTC-QTUM,ETH-QTUM,ETH-XMR,ETH-XEM,ETH-XLM,ETH-NEO,USDT-XMR,USDT-DASH,ETH-BCH,USDT-BCH,BTC-BCH,BTC-DNT,USDT-NEO,ETH-WAVES,ETH-STRAT,ETH-DGB,USDT-OMG,BTC-ADA,BTC-MANA,ETH-MANA,BTC-RCN,BTC-VIB,ETH-VIB,BTC-MER,BTC-POWR,ETH-POWR,ETH-ADA,BTC-ENG,ETH-ENG,USDT-ADA,USDT-XVG,BTC-UKG,ETH-UKG,BTC-IGNIS,BTC-SRN,ETH-SRN,BTC-WAXP,ETH-WAXP,BTC-ZRX,ETH-ZRX,BTC-VEE,BTC-TRX,ETH-TRX,BTC-TUSD,BTC-LRC,ETH-TUSD,BTC-DMT,ETH-DMT,USDT-TUSD,USDT-SC,USDT-TRX,BTC-STMX,ETH-STMX,BTC-AID,BTC-NGC,BTC-GTO,USDT-DCR,USD-BTC,USD-USDT,USD-TUSD,BTC-TUBE,BTC-CMCT,USD-ETH,BTC-NLC2,BTC-MFT,BTC-LOOM,BTC-RFR,USDT-DGB,BTC-RVN,USD-XRP,USD-ETC,BTC-BFT,BTC-GO,BTC-HYDRO,BTC-UPP,USD-ADA,USD-ZEC,USDT-DOGE,BTC-ENJ,BTC-MET,USD-LTC,USD-TRX,BTC-DTA,BTC-EDR,BTC-IHT,USD-BCH,BTC-XHV,USDT-ZRX,BTC-NPXS,BTC-PMA,USDT-BAT,USDT-RVN,BTC-PAL,USD-SC,BTC-PAX,BTC-ZIL,BTC-MOC,BTC-OST,BTC-SPC,BTC-MED,BTC-BSV,BTC-IOST,USDT-BSV,ETH-BSV,BTC-SOLVE,BTC-USDS,USDT-PMA,ETH-NPXS,USDT-NPXS,USD-ZRX,BTC-JNT,BTC-LBA,USD-BAT,USD-BSV,BTC-DENT,USD-USDS,BTC-DRGN,USD-PAX,BTC-VITE,BTC-IOTX,USD-DGB,BTC-BTM,BTC-ELF,BTC-QNT,BTC-BTU,USD-ZEN,BTC-SPND,BTC-BTT,BTC-NKN,USD-KMD,USDT-BTT,BTC-GRIN,BTC-CTXC,BTC-HXRO,BTC-META,USDT-GRIN,BTC-FSN,BTC-ANKR,USDT-XLM,BTC-TRAC,BTC-CRO,BTC-ONT,ETH-SOLVE,BTC-ONG,BTC-TTC,BTC-PTON,BTC-PI,ETH-ANKR,BTC-PLA,BTC-ART,BTC-ORBS,USDT-ENJ,BTC-VBK,BTC-BORA,BTC-CND,USDT-ONT,BTC-FX,ETH-FX,BTC-ATOM,USDT-ATOM,ETH-ATOM,BTC-OCEAN,USDT-OCEAN,BTC-BWX,BTC-VDX,USDT-VDX,ETH-VDX,BTC-COSM,BTC-LAMB,BTC-STPT,BTC-DAI,ETH-DAI,USDT-DAI,BTC-FNB,BTC-PROM,BTC-ABYSS,BTC-EOS,ETH-EOS,USDT-EOS,BTC-FXC,BTC-DUSK,BTC-URAC,BTC-BLOC,BTC-BRZ,BTC-TEMCO,BTC-SPIN,BTC-LUNA,BTC-CHR,BTC-TUDA,BTC-UTK,BTC-PXL,BTC-AKRO,BTC-TSHP,BTC-HEDG,BTC-MRPH,BTC-HBAR,ETH-HBAR,USD-HBAR,USDT-HBAR,BTC-PLG,BTC-VET,USDT-VET,BTC-SIX,BTC-WGP,BTC-APM,BTC-FLETA,USD-DCR,BTC-BLTV,BTC-HDAC,BTC-HYC,BTC-LINK,USD-EOS,BTC-APIX,BTC-XTZ,ETH-XTZ,USD-XTZ,USDT-XTZ,BTC-XTP,BTC-XSR,BTC-CTC,USD-ATOM,BTC-IOTA,ETH-LINK,USD-LINK,USDT-LINK,BTC-VRA,BTC-ABBC,BTC-FRSP,BTC-WICC,USDT-WICC,USDT-NMR,USD-DASH,USD-RVN,USD-DAI,BTC-VANY,BTC-BOA,BTC-CPC,BTC-CKB,USDT-CKB,BTC-MOF,USDT-MOF,USD-WAXP,USDT-WAXP,BTC-UPT,BTC-UPUSD,BTC-UPEUR,BTC-CVT,BTC-HBD,BTC-HIVE,USDT-CRO,BTC-SXP,BTC-ELAMA,BTC-STC,BTC-IRIS,USDT-IRIS,USDT-BOA,EUR-BTC,EUR-ETH,EUR-USDT,EUR-BSV,EUR-BCH,EUR-TRX,USDT-APM,USDT-HXRO,BTC-OGN,ETH-OGN,BTC-ALGO,BTC-OXT,USDT-OXT,BTC-ICX,BTC-USDC,ETH-USDC,USD-USDC,USDT-USDC,USDT-UPUSD,USDT-BRZ,BTC-XUC,BTC-MDT,USDT-MDT,BTC-REV,USDT-XUC,USDT-REV,BTC-UCT,USDT-UCT,BTC-YOU,USD-HIVE,USDT-HIVE,USD-ENJ,ETH-ENJ,BTC-HDAO,USDT-HDAO,BTC-DNA,USDT-DNA,USDT-SOLVE,BTC-CNTM,USDT-LBC,BTC-LOON,BTC-TNC,USDT-LOON,USD-ALGO,USDT-ALGO,BTC-UBT,ETH-UBT,BTC-DEP,USDT-DEP,EUR-USD,BTC-CELO,ETH-CELO,USD-CELO,USDT-CELO,USDT-CNTM,BTC-VID,BTC-HNS,ETH-HNS,USDT-HNS,BTC-PHNX,BTC-UTI,USD-SOLVE,BTC-4ART,USDT-4ART,BTC-VLX,USDT-VLX,ETH-MET,ETH-TRAC,USDT-TRAC,BTC-ME,BTC-DAWN,BTC-KDA,USDT-KDA"
}
}
},
@@ -828,7 +840,7 @@
"pairs": {
"spot": {
"enabled": "BTC-USD",
- "available": "BTC-CNY,BTC-EUR,BTC-GBP,BTC-HKD,BTC-JPY,BTC-SGD,BTC-USD,ETH-CNY,ETH-EUR,ETH-GBP,ETH-HKD,ETH-JPY,ETH-SGD,ETH-USD,LTC-CNY,LTC-EUR,LTC-GBP,LTC-HKD,LTC-JPY,LTC-SGD,LTC-USD,USDT-CNY,USDT-EUR,USDT-GBP,USDT-HKD,USDT-JPY,USDT-SGD,USDT-USD,XMR-CNY,XMR-EUR,XMR-GBP,XMR-HKD,XMR-JPY,XMR-SGD,XMR-USD"
+ "available": "BCB-EUR,BCB-GBP,BCB-HKD,BCB-JPY,BCB-SGD,BCB-USD,BTC-EUR,BTC-GBP,BTC-HKD,BTC-JPY,BTC-SGD,BTC-USD,BTSE-EUR,BTSE-GBP,BTSE-HKD,BTSE-JPY,BTSE-SGD,BTSE-USD,ETH-EUR,ETH-GBP,ETH-HKD,ETH-JPY,ETH-SGD,ETH-USD,LEO-EUR,LEO-GBP,LEO-HKD,LEO-JPY,LEO-SGD,LEO-USD,LTC-EUR,LTC-GBP,LTC-HKD,LTC-JPY,LTC-SGD,LTC-USD,USDT-EUR,USDT-GBP,USDT-HKD,USDT-JPY,USDT-SGD,USDT-USD,XAUT-EUR,XAUT-GBP,XAUT-HKD,XAUT-JPY,XAUT-SGD,XAUT-USD,XMR-EUR,XMR-GBP,XMR-HKD,XMR-JPY,XMR-SGD,XMR-USD,XRP-EUR,XRP-GBP,XRP-HKD,XRP-JPY,XRP-SGD,XRP-USD"
}
}
},
@@ -905,7 +917,7 @@
"pairs": {
"spot": {
"enabled": "BTC-AUD",
- "available": "BTC-AUD,LTC-AUD,LTC-BTC,ETH-BTC,ETH-AUD,ETC-AUD,ETC-BTC,XRP-AUD,XRP-BTC,POWR-AUD,POWR-BTC,OMG-AUD,OMG-BTC,BCHABC-AUD,BCHABC-BTC,BCHSV-AUD,BCHSV-BTC,GNT-AUD,GNT-BTC,BAT-AUD,BAT-BTC,XLM-AUD,XLM-BTC"
+ "available": "BTC-AUD,LTC-AUD,LTC-BTC,ETH-BTC,ETH-AUD,ETC-AUD,ETC-BTC,XRP-AUD,XRP-BTC,POWR-AUD,POWR-BTC,OMG-AUD,OMG-BTC,BCH-AUD,BCH-BTC,BSV-AUD,BSV-BTC,GNT-AUD,GNT-BTC,BAT-AUD,BAT-BTC,XLM-AUD,XLM-BTC,ENJ-AUD,ENJ-BTC,LINK-AUD,LINK-BTC"
}
}
},
@@ -931,6 +943,7 @@
"supports": {
"restAPI": true,
"restCapabilities": {
+ "tickerBatching": true,
"autoPairUpdates": true
},
"websocketAPI": true,
@@ -982,7 +995,7 @@
"pairs": {
"spot": {
"enabled": "LTC-USDT",
- "available": "ETH-BTC,LTC-SGD,BTC-CAD,DAI-SGD,ETH-CAD,ETH-SGD,ETH-USD,ZEC-USDT,BTC-USD,ETC-SGD,ETH-LTC,ZEC-SGD,BTC-SGD,ETC-USDT,XMR-BTC,XMR-USDT,ZEC-BTC,ZEC-CAD,ZEC-LTC,LTC-CAD,LTC-USDT,LTC-USD,XMR-LTC,ETC-LTC,LTC-BTC,ETH-USDT,ZEC-USD,USDT-SGD,USDT-USD,BTC-USDT,ETC-BTC"
+ "available": "BTC-USDT,LTC-USDT,ZEC-BTC,ETC-SGD,ETC-USDT,ETH-SGD,LTC-USD,ETH-BTC,LTC-BTC,LTC-SGD,XMR-BTC,ETH-CAD,ETH-USD,USDT-USD,BTC-USD,USDT-SGD,ETC-LTC,ETH-LTC,ZEC-USDT,ZEC-LTC,BTC-SGD,DAI-SGD,ETH-USDT,ZEC-CAD,XMR-USDT,ZEC-USD,BTC-CAD,ETC-BTC,LTC-CAD,XMR-LTC"
}
}
},
@@ -1061,7 +1074,7 @@
"pairs": {
"spot": {
"enabled": "BTC_USD,LTC_USD",
- "available": "LSK_RUB,QTUM_USD,LSK_USD,XLM_RUB,HP_BTC,BTC_RUB,XRP_EUR,BCH_USDT,BTT_RUB,GUSD_USD,KICK_BTC,ETH_USD,BCH_ETH,ETZ_ETH,XLM_USD,XLM_BTC,BTG_USD,DAI_USD,MNC_USD,SMART_RUB,XMR_USD,ZEC_RUB,BTT_BTC,ETH_UAH,TRX_BTC,USDC_USDT,XTZ_RUB,DCR_UAH,LSK_BTC,DCR_RUB,ETH_EUR,BTC_UAH,XRP_UAH,XTZ_USD,ZEC_EUR,BTC_USD,WAVES_ETH,TRX_UAH,USDT_UAH,ZRX_USD,EXM_BTC,INK_ETH,MKR_BTC,ETH_LTC,INK_BTC,KICK_RUB,LTC_RUB,MKR_DAI,OMG_ETH,EOS_USD,ETC_BTC,WAVES_USD,WAVES_BTC,XMR_EUR,ATMCASH_BTC,PTI_USDT,USDC_ETH,QTUM_ETH,ZEC_BTC,GAS_USD,ETH_BTC,DXT_BTC,USDC_USD,DOGE_USD,KICK_ETH,OMG_BTC,PTI_RUB,DASH_UAH,GNT_ETH,NEO_RUB,ROOBEE_BTC,ETC_USD,MNX_ETH,DASH_RUB,EOS_BTC,HB_BTC,INK_USD,MNX_USD,MNX_BTC,XRP_BTC,BCH_UAH,USDC_BTC,ZEC_USD,BTT_UAH,DASH_USD,DASH_BTC,DCR_BTC,DOGE_BTC,DXT_USD,GAS_BTC,LTC_BTC,BCH_EUR,BTG_BTC,SMART_BTC,XEM_USD,NEO_USD,OMG_USD,XRP_ETH,ZRX_BTC,HP_EXM,BTC_TRY,SMART_USD,ZRX_ETH,XEM_BTC,XTZ_BTC,USD_RUB,ETZ_BTC,XEM_UAH,ETH_USDT,LTC_EUR,XLM_TRY,ZAG_BTC,XRP_USD,DAI_ETH,ADA_ETH,BTG_ETH,LTC_UAH,PTI_EOS,USDT_EUR,VLX_BTC,BTC_USDT,XRP_RUB,XRP_USDT,XEM_EUR,XMR_RUB,EOS_EUR,WAVES_RUB,XMR_UAH,ADA_BTC,DAI_BTC,DASH_USDT,GNT_BTC,LTC_USD,BTC_EUR,ETH_RUB,ETH_PLN,QTUM_BTC,MNC_ETH,PTI_BTC,TRX_USD,XTZ_ETH,GUSD_RUB,GUSD_BTC,BCH_RUB,BTCZ_BTC,ETH_TRY,ETZ_USDT,NEO_BTC,XMR_ETH,ADA_USD,BCH_USD,XRP_TRY,KICK_USDT,MNC_BTC,USDT_USD,XMR_BTC,BTC_PLN,BCH_BTC,SMART_EUR,TRX_RUB,USDT_RUB,DAI_RUB,ETC_RUB"
+ "available": "DAI_USD,GNT_ETH,LSK_BTC,MKR_BTC,XTZ_RUB,ATOM_EXM,BTT_BTC,ZAG_BTC,USDT_USD,XTZ_ETH,USDT_KZT,LSK_USD,TRX_UAH,ONG_EXM,SMART_USD,XEM_UAH,EOS_USD,ETH_GBP,ETH_EUR,ETH_USDT,HP_BTC,XRP_EUR,ATOM_EUR,DASH_RUB,TRX_EUR,XRP_USDT,ZRX_USD,CRON_ETH,ETZ_ETH,ONT_RUB,USDC_USDT,LSK_RUB,SMART_BTC,TRX_RUB,ZEC_EUR,ETH_RUB,EXM_ETH,GNT_BTC,MNC_BTC,ONG_BTC,XMR_EUR,ZEC_BTC,BTC_PLN,ETH_KZT,ALGO_EXM,DASH_BTC,PTI_EOS,XLM_USD,XRP_GBP,ZEC_RUB,BCH_USDT,DAI_RUB,DCR_BTC,ETH_UAH,ETZ_BTC,LTC_BTC,ZRX_ETH,ALGO_USDT,DCR_RUB,HB_BTC,WAVES_BTC,XLM_BTC,XRP_BTC,ETC_RUB,EOS_BTC,OMG_ETH,ONG_UAH,XMR_UAH,ATOM_BTC,BTG_ETH,GAS_BTC,GUSD_BTC,LTC_UAH,NEO_RUB,ROOBEE_BTC,WAVES_USD,ETC_USD,ETC_BTC,WAVES_ETH,BTT_UAH,DAI_BTC,DAI_ETH,XTZ_USD,ALGO_RUB,BTT_RUB,ETH_LTC,DOGE_BTC,NEO_BTC,XEM_EUR,XLM_TRY,BCH_ETH,CRON_EXM,ETH_PLN,SMART_EUR,USDC_ETH,BTC_USDT,ETH_BTC,MNC_ETH,TRX_USD,XRP_UAH,EXM_BTC,BTC_EUR,BTG_BTC,OMG_BTC,ONT_UAH,GAS_USD,LTC_RUB,OMG_USD,DOGE_USD,GUSD_RUB,ONG_RUB,ONT_BTC,USDC_BTC,USDT_UAH,XMR_ETH,ADA_BTC,DASH_USD,BCH_RUB,DCR_UAH,MKR_DAI,PTI_RUB,VLX_BTC,XEM_USD,ATOM_USD,BCH_USD,XMR_USD,ZRX_BTC,ETZ_USDT,QTUM_BTC,XEM_BTC,XMR_RUB,XMR_BTC,XRP_TRY,BTC_GBP,XRP_RUB,BCH_EUR,CRON_USDT,PTI_USDT,QTUM_USD,QTUM_ETH,USDT_EUR,BTC_USD,BTC_RUB,CRON_BTC,LTC_EUR,ONT_EXM,BTC_TRY,XRP_USD,SMART_RUB,TRX_BTC,USDT_RUB,XLM_RUB,XRP_ETH,ZEC_USD,ALGO_EUR,EOS_EUR,BTG_USD,GUSD_USD,PTI_BTC,USDT_GBP,WAVES_RUB,XTZ_BTC,ETH_USD,BCH_BTC,BCH_UAH,DASH_UAH,DASH_USDT,ETH_TRY,BTC_UAH,ALGO_BTC,MNC_USD,USDC_USD,ADA_USD,EXM_USDT,LTC_USD,NEO_USD,BTC_KZT,ADA_ETH"
}
}
},
@@ -1139,7 +1152,7 @@
"pairs": {
"spot": {
"enabled": "BTC-USD",
- "available": "BTC-GBP,BTC-USD,REP-BTC,LTC-EUR,BCH-BTC,XRP-USD,BTC-USDC,DNT-USDC,ETH-DAI,GNT-USDC,EOS-BTC,XTZ-BTC,EOS-USD,ZEC-USDC,BTC-EUR,BAT-USDC,DASH-USD,ETC-USD,XLM-BTC,XRP-EUR,ETH-BTC,BCH-GBP,XRP-BTC,LTC-BTC,MANA-USDC,LOOM-USDC,BAT-ETH,ZRX-BTC,REP-USD,LTC-USD,EOS-EUR,BCH-USD,XLM-EUR,XTZ-USD,ETC-BTC,ZEC-BTC,ETC-EUR,ZRX-EUR,ETH-EUR,LTC-GBP,DAI-USDC,ZRX-USD,ETH-USDC,BCH-EUR,LINK-ETH,ETC-GBP,DASH-BTC,XLM-USD,CVC-USDC,ETH-USD,ETH-GBP,ALGO-USD,LINK-USD"
+ "available": "ETC-GBP,CVC-USDC,LINK-ETH,KNC-BTC,GNT-USDC,EOS-BTC,ETC-BTC,LTC-BTC,ZRX-USD,XRP-EUR,ZRX-EUR,ATOM-USD,BTC-USD,LTC-EUR,XRP-USD,MANA-USDC,XRP-BTC,LTC-GBP,DAI-USD,COMP-BTC,ETH-DAI,XTZ-USD,DASH-BTC,OMG-BTC,BTC-USDC,BCH-USD,DNT-USDC,COMP-USD,LOOM-USDC,OMG-GBP,BCH-GBP,ZRX-BTC,ATOM-BTC,EOS-EUR,ETH-USD,XLM-EUR,KNC-USD,OXT-USD,ETC-EUR,OMG-USD,BTC-GBP,OMG-EUR,DASH-USD,MKR-BTC,XTZ-BTC,BAT-ETH,REP-USD,XLM-BTC,ETH-USDC,REP-BTC,LTC-USD,ZEC-BTC,ZEC-USDC,EOS-USD,MKR-USD,ALGO-USD,LINK-USD,BCH-EUR,XLM-USD,ETH-GBP,ETC-USD,ETH-EUR,BCH-BTC,BTC-EUR,ETH-BTC,DAI-USDC,BAT-USDC"
}
}
},
@@ -1222,7 +1235,7 @@
},
"spot": {
"enabled": "BTC/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,STQ/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",
+ "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": "/"
@@ -1282,6 +1295,95 @@
}
]
},
+ {
+ "name": "FTX",
+ "enabled": true,
+ "verbose": false,
+ "httpTimeout": 15000000000,
+ "websocketResponseCheckTimeout": 30000000,
+ "websocketResponseMaxLimit": 7000000000,
+ "websocketTrafficTimeout": 30000000000,
+ "websocketOrderbookBufferLimit": 5,
+ "baseCurrencies": "USD",
+ "currencyPairs": {
+ "assetTypes": [
+ "spot",
+ "futures"
+ ],
+ "pairs": {
+ "futures": {
+ "enabled": "BTC-PERP",
+ "available": "ADA-PERP,ADA-0925,ALGO-PERP,ALGO-0925,ALT-PERP,ALT-0925,ATOM-PERP,ATOM-0925,BAL-PERP,BAL-0925,BCH-PERP,BCH-0925,BNB-PERP,BNB-0925,BRZ-PERP,BRZ-0925,BSV-PERP,BSV-0925,BTC-PERP,BTC-MOVE-0630,BTC-MOVE-0701,BTC-MOVE-WK-0703,BTC-MOVE-WK-0710,BTC-MOVE-WK-0717,BTC-MOVE-WK-0724,BTC-0925,BTC-MOVE-2020Q3,BTC-1225,BTC-MOVE-2020Q4,BTC-MOVE-2021Q1,BTC-HASH-2020Q3,BTC-HASH-2020Q4,BTC-HASH-2021Q1,BTMX-PERP,BTMX-0925,COMP-PERP,COMP-0925,CUSDT-PERP,CUSDT-0925,DEFI-PERP,DEFI-0925,DOGE-PERP,DOGE-0925,DRGN-PERP,DRGN-0925,EOS-PERP,EOS-0925,ETC-PERP,ETC-0925,ETH-PERP,ETH-0925,EXCH-PERP,EXCH-0925,HT-PERP,HT-0925,KNC-PERP,KNC-0925,LEO-PERP,LEO-0925,LINK-PERP,LINK-0925,LTC-PERP,LTC-0925,MATIC-PERP,MATIC-0925,MID-PERP,MID-0925,OIL100-0629,OKB-PERP,OKB-0925,PAXG-PERP,PAXG-0925,BERNIE,BIDEN,BLOOMBERG,PETE,TRUMP,WARREN,PRIV-PERP,PRIV-0925,SHIT-PERP,SHIT-0925,THETA-PERP,THETA-0925,TOMO-PERP,TOMO-0925,TRX-PERP,TRX-0925,TRYB-PERP,TRYB-0925,USDT-PERP,USDT-0925,XAUT-PERP,XAUT-0925,XRP-PERP,XRP-0925,XTZ-PERP,XTZ-0925",
+ "requestFormat": {
+ "uppercase": true,
+ "delimiter": "-"
+ },
+ "configFormat": {
+ "uppercase": true,
+ "delimiter": "-"
+ }
+ },
+ "spot": {
+ "enabled": "BTC/USD",
+ "available": "BAL/USD,BAL/USDT,BCH/USD,BCH/USDT,BNB/USD,BNB/USDT,BRL/USD,BRL/USDT,BTC/BRL,BTC/USD,BTC/USDT,BTMX/USD,COMP/USD,COMP/USDT,CUSDT/USD,CUSDT/USDT,ETH/BTC,ETH/USD,ETH/USDT,FTT/BTC,FTT/USD,FTT/USDT,KNC/USD,KNC/USDT,LINK/USD,LINK/USDT,LTC/USD,LTC/USDT,PAXG/USD,PAXG/USDT,TRX/USD,TRX/USDT,TRYB/USD,USDT/USD,WRX/USD,WRX/USDT,XAUT/USD,XAUT/USDT,ADABEAR/USD,ADABULL/USD,ADAHALF/USD,ADAHALF/USDT,ADAHEDGE/USD,ALGOBEAR/USD,ALGOBULL/USD,ALGOHALF/USD,ALGOHALF/USDT,ALGOHEDGE/USD,ALTBEAR/USD,ALTBULL/USD,ALTHALF/USD,ALTHALF/USDT,ALTHEDGE/USD,ATOMBEAR/USD,ATOMBULL/USD,ATOMHALF/USD,ATOMHALF/USDT,ATOMHEDGE/USD,BALBEAR/USD,BALBEAR/USDT,BALBULL/USD,BALBULL/USDT,BALHALF/USD,BALHEDGE/USD,BCHBEAR/USD,BCHBEAR/USDT,BCHBULL/USD,BCHBULL/USDT,BCHHALF/USD,BCHHALF/USDT,BCHHEDGE/USD,BEAR/USD,BEAR/USDT,BEARSHIT/USD,BNBBEAR/USD,BNBBEAR/USDT,BNBBULL/USD,BNBBULL/USDT,BNBHALF/USD,BNBHALF/USDT,BNBHEDGE/USD,BSVBEAR/USD,BSVBEAR/USDT,BSVBULL/USD,BSVBULL/USDT,BSVHALF/USD,BSVHALF/USDT,BSVHEDGE/USD,BTMXBEAR/USD,BTMXBEAR/USDT,BTMXBULL/USD,BTMXBULL/USDT,BTMXHALF/USD,BTMXHALF/USDT,BTMXHEDGE/USD,BULL/USD,BULL/USDT,BULLSHIT/USD,BVOL/USD,BVOL/USDT,COMPBEAR/USD,COMPBEAR/USDT,COMPBULL/USD,COMPBULL/USDT,COMPHALF/USD,COMPHEDGE/USD,CUSDTBEAR/USD,CUSDTBEAR/USDT,CUSDTBULL/USD,CUSDTBULL/USDT,CUSDTHALF/USD,CUSDTHEDGE/USD,DEFIBEAR/USD,DEFIBEAR/USDT,DEFIBULL/USD,DEFIBULL/USDT,DEFIHALF/USD,DEFIHALF/USDT,DEFIHEDGE/USD,DOGEBEAR/USD,DOGEBULL/USD,DOGEHALF/USD,DOGEHALF/USDT,DOGEHEDGE/USD,DRGNBEAR/USD,DRGNBULL/USD,DRGNHALF/USD,DRGNHALF/USDT,DRGNHEDGE/USD,EOSBEAR/USD,EOSBEAR/USDT,EOSBULL/USD,EOSBULL/USDT,EOSHALF/USD,EOSHALF/USDT,EOSHEDGE/USD,ETCBEAR/USD,ETCBULL/USD,ETCHALF/USD,ETCHALF/USDT,ETCHEDGE/USD,ETHBEAR/USD,ETHBEAR/USDT,ETHBULL/USD,ETHBULL/USDT,ETHHALF/USD,ETHHALF/USDT,ETHHEDGE/USD,EXCHBEAR/USD,EXCHBULL/USD,EXCHHALF/USD,EXCHHALF/USDT,EXCHHEDGE/USD,HALF/USD,HALF/USDT,HALFSHIT/USD,HALFSHIT/USDT,HEDGE/USD,HEDGESHIT/USD,HTBEAR/USD,HTBULL/USD,HTHALF/USD,HTHALF/USDT,HTHEDGE/USD,IBVOL/USD,IBVOL/USDT,KNCBEAR/USD,KNCBEAR/USDT,KNCBULL/USD,KNCBULL/USDT,KNCHALF/USD,KNCHEDGE/USD,LEOBEAR/USD,LEOBULL/USD,LEOHALF/USD,LEOHALF/USDT,LEOHEDGE/USD,LINKBEAR/USD,LINKBEAR/USDT,LINKBULL/USD,LINKBULL/USDT,LINKHALF/USD,LINKHALF/USDT,LINKHEDGE/USD,LTCBEAR/USD,LTCBEAR/USDT,LTCBULL/USD,LTCBULL/USDT,LTCHALF/USD,LTCHALF/USDT,LTCHEDGE/USD,MATICBEAR/USD,MATICBULL/USD,MATICHALF/USD,MATICHALF/USDT,MATICHEDGE/USD,MIDBEAR/USD,MIDBULL/USD,MIDHALF/USD,MIDHALF/USDT,MIDHEDGE/USD,OKBBEAR/USD,OKBBULL/USD,OKBHALF/USD,OKBHALF/USDT,OKBHEDGE/USD,PAXGBEAR/USD,PAXGBULL/USD,PAXGHALF/USD,PAXGHALF/USDT,PAXGHEDGE/USD,PRIVBEAR/USD,PRIVBULL/USD,PRIVHALF/USD,PRIVHALF/USDT,PRIVHEDGE/USD,THETABEAR/USD,THETABULL/USD,THETAHALF/USD,THETAHALF/USDT,THETAHEDGE/USD,TOMOBEAR/USD,TOMOBULL/USD,TOMOHALF/USD,TOMOHALF/USDT,TOMOHEDGE/USD,TRXBEAR/USD,TRXBULL/USD,TRXHALF/USD,TRXHALF/USDT,TRXHEDGE/USD,TRYBBEAR/USD,TRYBBULL/USD,TRYBHALF/USD,TRYBHALF/USDT,TRYBHEDGE/USD,USDTBEAR/USD,USDTBULL/USD,USDTHALF/USD,USDTHALF/USDT,USDTHEDGE/USD,XAUTBEAR/USD,XAUTBULL/USD,XAUTHALF/USD,XAUTHALF/USDT,XAUTHEDGE/USD,XRPBEAR/USD,XRPBEAR/USDT,XRPBULL/USD,XRPBULL/USDT,XRPHALF/USD,XRPHALF/USDT,XRPHEDGE/USD,XTZBEAR/USD,XTZBEAR/USDT,XTZBULL/USD,XTZBULL/USDT,XTZHALF/USD,XTZHALF/USDT,XTZHEDGE/USD",
+ "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": true
+ }
+ },
+ "bankAccounts": [
+ {
+ "enabled": false,
+ "bankName": "",
+ "bankAddress": "",
+ "bankPostalCode": "",
+ "bankPostalCity": "",
+ "bankCountry": "",
+ "accountName": "",
+ "accountNumber": "",
+ "swiftCode": "",
+ "iban": "",
+ "supportedCurrencies": ""
+ }
+ ]
+ },
{
"name": "GateIO",
"enabled": true,
@@ -1308,7 +1410,7 @@
"pairs": {
"spot": {
"enabled": "BTC_USDT",
- "available": "USDT_CNYX,BTC_CNYX,ETH_CNYX,EOS_CNYX,BCH_CNYX,XRP_CNYX,DOGE_CNYX,TIPS_CNYX,BTC_USDC,BTC_PAX,BTC_USDT,BCH_USDT,ETH_USDT,ETC_USDT,QTUM_USDT,LTC_USDT,DASH_USDT,ZEC_USDT,BTM_USDT,EOS_USDT,REQ_USDT,SNT_USDT,OMG_USDT,PAY_USDT,CVC_USDT,ZRX_USDT,TNT_USDT,XMR_USDT,XRP_USDT,DOGE_USDT,BAT_USDT,PST_USDT,BTG_USDT,DPY_USDT,LRC_USDT,STORJ_USDT,RDN_USDT,STX_USDT,KNC_USDT,LINK_USDT,CDT_USDT,AE_USDT,AE_ETH,AE_BTC,CDT_ETH,RDN_ETH,STX_ETH,KNC_ETH,LINK_ETH,REQ_ETH,RCN_ETH,TRX_ETH,ARN_ETH,BNT_ETH,VET_ETH,MCO_ETH,FUN_ETH,DATA_ETH,RLC_ETH,RLC_USDT,ZSC_ETH,WINGS_ETH,MDA_ETH,RCN_USDT,TRX_USDT,VET_USDT,MCO_USDT,FUN_USDT,DATA_USDT,ZSC_USDT,MDA_USDT,XTZ_USDT,XTZ_BTC,XTZ_ETH,GNT_USDT,GNT_ETH,GEM_USDT,GEM_ETH,RFR_USDT,RFR_ETH,DADI_USDT,DADI_ETH,ABT_USDT,ABT_ETH,LEDU_BTC,LEDU_ETH,OST_USDT,OST_ETH,XLM_USDT,XLM_ETH,XLM_BTC,MOBI_USDT,MOBI_ETH,MOBI_BTC,OCN_USDT,OCN_ETH,OCN_BTC,ZPT_USDT,ZPT_ETH,ZPT_BTC,COFI_USDT,COFI_ETH,JNT_USDT,JNT_ETH,JNT_BTC,BLZ_USDT,BLZ_ETH,GXS_USDT,GXS_BTC,MTN_USDT,MTN_ETH,RUFF_USDT,RUFF_ETH,RUFF_BTC,TNC_USDT,TNC_ETH,TNC_BTC,ZIL_USDT,ZIL_ETH,BTO_USDT,BTO_ETH,THETA_USDT,THETA_ETH,DDD_USDT,DDD_ETH,DDD_BTC,MKR_USDT,MKR_ETH,DAI_USDT,SMT_USDT,SMT_ETH,MDT_USDT,MDT_ETH,MDT_BTC,MANA_USDT,MANA_ETH,LUN_USDT,LUN_ETH,SALT_USDT,SALT_ETH,FUEL_USDT,FUEL_ETH,ELF_USDT,ELF_ETH,DRGN_USDT,DRGN_ETH,GTC_USDT,GTC_ETH,GTC_BTC,QLC_USDT,QLC_BTC,QLC_ETH,DBC_USDT,DBC_BTC,DBC_ETH,BNTY_USDT,BNTY_ETH,LEND_USDT,LEND_ETH,ICX_USDT,ICX_ETH,BTF_USDT,BTF_BTC,ADA_USDT,ADA_BTC,LSK_USDT,LSK_BTC,WAVES_USDT,WAVES_BTC,BIFI_USDT,BIFI_BTC,MDS_ETH,MDS_USDT,DGD_USDT,DGD_ETH,QASH_USDT,QASH_ETH,QASH_BTC,POWR_USDT,POWR_ETH,POWR_BTC,FIL_USDT,BCD_USDT,BCD_BTC,SBTC_USDT,SBTC_BTC,GOD_USDT,GOD_BTC,BCX_USDT,BCX_BTC,QSP_USDT,QSP_ETH,INK_BTC,INK_USDT,INK_ETH,INK_QTUM,QBT_QTUM,QBT_ETH,QBT_USDT,TSL_QTUM,TSL_USDT,GNX_USDT,GNX_ETH,NEO_USDT,GAS_USDT,NEO_BTC,GAS_BTC,IOTA_USDT,IOTA_BTC,NAS_USDT,NAS_ETH,NAS_BTC,ETH_BTC,ETC_BTC,ETC_ETH,ZEC_BTC,DASH_BTC,LTC_BTC,BCH_BTC,BTG_BTC,QTUM_BTC,QTUM_ETH,XRP_BTC,DOGE_BTC,XMR_BTC,ZRX_BTC,ZRX_ETH,DNT_ETH,DPY_ETH,OAX_BTC,OAX_USDT,OAX_ETH,REP_ETH,LRC_ETH,LRC_BTC,PST_ETH,BCDN_ETH,BCDN_USDT,TNT_ETH,SNT_ETH,SNT_BTC,BTM_ETH,BTM_BTC,SNET_ETH,SNET_USDT,LLT_SNET,OMG_ETH,OMG_BTC,PAY_ETH,PAY_BTC,BAT_ETH,BAT_BTC,CVC_ETH,STORJ_ETH,STORJ_BTC,EOS_ETH,EOS_BTC,BTS_USDT,BTS_BTC,TIPS_ETH,GT_BTC,GT_USDT,ATOM_BTC,ATOM_USDT,XEM_ETH,XEM_USDT,XEM_BTC,BU_USDT,BU_ETH,BU_BTC,BCHSV_USDT,BCHSV_CNYX,BCHSV_BTC,DCR_USDT,DCR_BTC,BCN_USDT,BCN_BTC,XMC_USDT,XMC_BTC,ATP_USDT,ATP_ETH,NAX_ETH,NBOT_ETH,NBOT_USDT,MED_USDT,MED_ETH,GRIN_USDT,GRIN_ETH,GRIN_BTC,BEAM_USDT,BEAM_ETH,BEAM_BTC,VTHO_ETH,BTT_USDT,BTT_ETH,BTT_TRX,TFUEL_ETH,TFUEL_USDT,CELR_ETH,CELR_USDT,CS_ETH,CS_USDT,MAN_ETH,MAN_USDT,REM_ETH,REM_USDT,LYM_ETH,LYM_BTC,LYM_USDT,ONG_ETH,ONG_USDT,ONT_ETH,ONT_USDT,BFT_ETH,BFT_USDT,IHT_ETH,IHT_USDT,SENC_ETH,SENC_USDT,TOMO_ETH,TOMO_USDT,ELEC_ETH,ELEC_USDT,HAV_ETH,HAV_USDT,SWTH_ETH,SWTH_USDT,NKN_ETH,NKN_USDT,SOUL_ETH,SOUL_USDT,LRN_ETH,LRN_USDT,EOSDAC_ETH,EOSDAC_USDT,DOCK_USDT,DOCK_ETH,GSE_USDT,GSE_ETH,RATING_USDT,RATING_ETH,HSC_USDT,HSC_ETH,HIT_USDT,HIT_ETH,DX_USDT,DX_ETH,CNNS_ETH,CNNS_USDT,DREP_ETH,DREP_USDT,MBL_USDT,MBL_ETH,GMAT_USDT,GMAT_ETH,MIX_USDT,MIX_ETH,LAMB_USDT,LAMB_ETH,LEO_USDT,LEO_BTC,WICC_USDT,WICC_ETH,SERO_USDT,SERO_ETH,VIDY_USDT,VIDY_ETH,KGC_USDT,FTM_USDT,FTM_ETH,COS_USDT,CRO_USDT,ALY_USDT,WIN_USDT,MTV_USDT,ONE_USDT,ARPA_USDT,ARPA_ETH,DILI_USDT,ALGO_USDT,PI_USDT,CKB_USDT,CKB_BTC,CKB_ETH,BKC_USDT,BXC_USDT,BXC_ETH,PAX_USDT,PAX_CNYX,USDC_CNYX,USDC_USDT,TUSD_CNYX,TUSD_USDT,HC_USDT,HC_BTC,HC_ETH,GARD_USDT,GARD_ETH,FTI_USDT,FTI_ETH,SOP_ETH,SOP_USDT,LEMO_USDT,LEMO_ETH,QKC_USDT,QKC_ETH,QKC_BTC,IOTX_USDT,IOTX_ETH,RED_USDT,RED_ETH,LBA_USDT,LBA_ETH,OPEN_USDT,OPEN_ETH,MITH_USDT,MITH_ETH,SKM_USDT,SKM_ETH,XVG_USDT,XVG_BTC,NANO_USDT,NANO_BTC,HT_USDT,BNB_USDT,MET_ETH,MET_USDT,TCT_ETH,TCT_USDT,MXC_USDT,MXC_BTC,MXC_ETH"
+ "available": "USDT_CNYX,BTC_CNYX,ETH_CNYX,EOS_CNYX,BCH_CNYX,XRP_CNYX,DOGE_CNYX,TIPS_CNYX,BTC_USDC,BTC_PAX,BTC_USDT,BCH_USDT,ETH_USDT,ETC_USDT,QTUM_USDT,LTC_USDT,DASH_USDT,ZEC_USDT,BTM_USDT,EOS_USDT,REQ_USDT,SNT_USDT,OMG_USDT,PAY_USDT,CVC_USDT,ZRX_USDT,TNT_USDT,XMR_USDT,XRP_USDT,DOGE_USDT,BAT_USDT,PST_USDT,BTG_USDT,DPY_USDT,LRC_USDT,STORJ_USDT,RDN_USDT,STX_USDT,KNC_USDT,LINK_USDT,CDT_USDT,AE_USDT,AE_ETH,AE_BTC,CDT_ETH,RDN_ETH,STX_ETH,KNC_ETH,LINK_ETH,REQ_ETH,RCN_ETH,TRX_ETH,ARN_ETH,BNT_ETH,VET_ETH,MCO_ETH,FUN_ETH,DATA_ETH,RLC_ETH,RLC_USDT,ZSC_ETH,WINGS_ETH,MDA_ETH,RCN_USDT,TRX_USDT,VET_USDT,MCO_USDT,FUN_USDT,DATA_USDT,ZSC_USDT,MDA_USDT,XTZ_USDT,XTZ_BTC,XTZ_ETH,GNT_USDT,GNT_ETH,GEM_USDT,GEM_ETH,RFR_USDT,RFR_ETH,DADI_USDT,DADI_ETH,ABT_USDT,ABT_ETH,LEDU_BTC,LEDU_ETH,OST_USDT,OST_ETH,XLM_USDT,XLM_ETH,XLM_BTC,MOBI_USDT,MOBI_ETH,MOBI_BTC,OCN_USDT,OCN_ETH,OCN_BTC,ZPT_USDT,ZPT_ETH,ZPT_BTC,COFI_USDT,COFI_ETH,JNT_USDT,JNT_ETH,JNT_BTC,BLZ_USDT,BLZ_ETH,GXS_USDT,GXS_BTC,MTN_USDT,MTN_ETH,RUFF_USDT,RUFF_ETH,RUFF_BTC,TNC_USDT,TNC_ETH,TNC_BTC,ZIL_USDT,ZIL_ETH,BTO_USDT,BTO_ETH,THETA_USDT,THETA_ETH,DDD_USDT,DDD_ETH,DDD_BTC,MKR_USDT,MKR_ETH,DAI_USDT,SMT_USDT,SMT_ETH,MDT_USDT,MDT_ETH,MDT_BTC,MANA_USDT,MANA_ETH,LUN_USDT,LUN_ETH,SALT_USDT,SALT_ETH,FUEL_USDT,FUEL_ETH,ELF_USDT,ELF_ETH,DRGN_USDT,DRGN_ETH,GTC_USDT,GTC_ETH,GTC_BTC,QLC_USDT,QLC_BTC,QLC_ETH,DBC_USDT,DBC_BTC,DBC_ETH,BNTY_USDT,BNTY_ETH,LEND_USDT,LEND_ETH,ICX_USDT,ICX_ETH,BTF_USDT,BTF_BTC,ADA_USDT,ADA_BTC,LSK_USDT,LSK_BTC,WAVES_USDT,WAVES_BTC,BIFI_USDT,BIFI_BTC,MDS_ETH,MDS_USDT,DGD_USDT,DGD_ETH,QASH_USDT,QASH_ETH,QASH_BTC,POWR_USDT,POWR_ETH,POWR_BTC,FIL_USDT,BCD_USDT,BCD_BTC,SBTC_USDT,SBTC_BTC,GOD_USDT,GOD_BTC,BCX_USDT,BCX_BTC,QSP_USDT,QSP_ETH,INK_BTC,INK_USDT,INK_ETH,INK_QTUM,QBT_QTUM,QBT_ETH,QBT_USDT,TSL_QTUM,TSL_USDT,GNX_USDT,GNX_ETH,NEO_USDT,GAS_USDT,NEO_BTC,GAS_BTC,IOTA_USDT,IOTA_BTC,NAS_USDT,NAS_ETH,NAS_BTC,ETH_BTC,ETC_BTC,ETC_ETH,ZEC_BTC,DASH_BTC,LTC_BTC,BCH_BTC,BTG_BTC,QTUM_BTC,QTUM_ETH,XRP_BTC,DOGE_BTC,XMR_BTC,ZRX_BTC,ZRX_ETH,DNT_ETH,DPY_ETH,OAX_BTC,OAX_USDT,OAX_ETH,REP_ETH,LRC_ETH,LRC_BTC,PST_ETH,BCDN_ETH,BCDN_USDT,TNT_ETH,SNT_ETH,SNT_BTC,BTM_ETH,BTM_BTC,SNET_ETH,SNET_USDT,LLT_SNET,OMG_ETH,OMG_BTC,PAY_ETH,PAY_BTC,BAT_ETH,BAT_BTC,CVC_ETH,STORJ_ETH,STORJ_BTC,EOS_ETH,EOS_BTC,BTS_USDT,BTS_BTC,TIPS_ETH,GT_BTC,GT_USDT,ATOM_BTC,ATOM_USDT,KAVA_USDT,ANKR_USDT,RSR_USDT,RSV_USDT,KAI_USDT,COMP_USDT,SOL_USDT,COTI_USDT,LBK_USDT,BTMX_USDT,XEM_ETH,XEM_USDT,XEM_BTC,BU_USDT,BU_ETH,BU_BTC,HNS_BTC,HNS_USDT,BTC3L_USDT,BTC3S_USDT,ETH3L_USDT,ETH3S_USDT,EOS3L_USDT,EOS3S_USDT,BSV3L_USDT,BSV3S_USDT,BCH3L_USDT,BCH3S_USDT,LTC3L_USDT,LTC3S_USDT,XTZ3L_USDT,XTZ3S_USDT,BCHSV_USDT,BCHSV_CNYX,BCHSV_BTC,RVN_USDT,RVC_USDT,AR_USDT,DCR_USDT,DCR_BTC,BCN_USDT,BCN_BTC,XMC_USDT,XMC_BTC,STEEM_USDT,HIVE_USDT,ATP_USDT,ATP_ETH,NAX_USDT,NAX_ETH,KLAY_USDT,NBOT_ETH,NBOT_USDT,MED_USDT,MED_ETH,GRIN_USDT,GRIN_ETH,GRIN_BTC,BEAM_USDT,BEAM_ETH,BEAM_BTC,HBAR_USDT,OKB_USDT,VTHO_ETH,BTT_USDT,BTT_ETH,BTT_TRX,TFUEL_ETH,TFUEL_USDT,CELR_ETH,CELR_USDT,CS_ETH,CS_USDT,MAN_ETH,MAN_USDT,REM_ETH,REM_USDT,LYM_ETH,LYM_BTC,LYM_USDT,ONG_ETH,ONG_USDT,ONT_ETH,ONT_USDT,BFT_ETH,BFT_USDT,IHT_ETH,IHT_USDT,SENC_ETH,SENC_USDT,TOMO_ETH,TOMO_USDT,ELEC_ETH,ELEC_USDT,HAV_ETH,HAV_USDT,SWTH_ETH,SWTH_USDT,NKN_ETH,NKN_USDT,SOUL_ETH,SOUL_USDT,LRN_ETH,LRN_USDT,EOSDAC_ETH,EOSDAC_USDT,DOCK_USDT,DOCK_ETH,GSE_USDT,GSE_ETH,RATING_USDT,RATING_ETH,HSC_USDT,HSC_ETH,HIT_USDT,HIT_ETH,DX_USDT,DX_ETH,CNNS_ETH,CNNS_USDT,DREP_ETH,DREP_USDT,MBL_USDT,MBL_ETH,GMAT_USDT,GMAT_ETH,MIX_USDT,MIX_ETH,LAMB_USDT,LAMB_ETH,LEO_USDT,LEO_BTC,BTCBULL_USDT,BTCBEAR_USDT,ETHBEAR_USDT,ETHBULL_USDT,EOSBULL_USDT,EOSBEAR_USDT,XRPBEAR_USDT,XRPBULL_USDT,WICC_USDT,WICC_ETH,SERO_USDT,SERO_ETH,VIDY_USDT,VIDY_ETH,KGC_USDT,FTM_USDT,FTM_ETH,COS_USDT,CRO_USDT,ALY_USDT,WIN_USDT,MTV_USDT,ONE_USDT,ARPA_USDT,ARPA_ETH,DILI_USDT,ALGO_USDT,PI_USDT,CKB_USDT,CKB_BTC,CKB_ETH,BKC_USDT,BXC_USDT,BXC_ETH,PAX_USDT,PAX_CNYX,USDC_CNYX,USDC_USDT,TUSD_CNYX,TUSD_USDT,HC_USDT,HC_BTC,HC_ETH,GARD_USDT,GARD_ETH,FTI_USDT,FTI_ETH,SOP_ETH,SOP_USDT,LEMO_USDT,LEMO_ETH,QKC_USDT,QKC_ETH,QKC_BTC,IOTX_USDT,IOTX_ETH,RED_USDT,RED_ETH,LBA_USDT,LBA_ETH,OPEN_USDT,OPEN_ETH,MITH_USDT,MITH_ETH,SKM_USDT,SKM_ETH,XVG_USDT,XVG_BTC,NANO_USDT,NANO_BTC,HT_USDT,BNB_USDT,MET_ETH,MET_USDT,TCT_ETH,TCT_USDT,MXC_USDT,MXC_BTC,MXC_ETH"
}
}
},
@@ -1384,7 +1486,7 @@
"pairs": {
"spot": {
"enabled": "BTCUSD",
- "available": "BTCUSD,ETHBTC,ETHUSD,BCHUSD,BCHBTC,BCHETH,LTCUSD,LTCBTC,LTCETH,LTCBCH,ZECUSD,ZECBTC,ZECETH,ZECBCH,ZECLTC"
+ "available": "BTCUSD,ETHBTC,ETHUSD,BCHUSD,BCHBTC,BCHETH,LTCUSD,LTCBTC,LTCETH,LTCBCH,ZECUSD,ZECBTC,ZECETH,ZECBCH,ZECLTC,BATUSD,BATBTC,BATETH,DAIUSD,DAIBTC,DAIETH,LINKUSD,LINKBTC,LINKETH,OXTUSD,OXTBTC,OXTETH"
}
}
},
@@ -1460,7 +1562,7 @@
"pairs": {
"spot": {
"enabled": "BTC-USD",
- "available": "BCN-BTC,BTC-USD,DASH-BTC,DOGE-BTC,DOGE-USD,EMC-BTC,ETH-BTC,LSK-BTC,LTC-BTC,LTC-USD,NXT-BTC,SBD-BTC,SC-BTC,STEEM-BTC,XDN-BTC,XEM-BTC,XMR-BTC,ARDR-BTC,ZEC-BTC,WAVES-BTC,MAID-BTC,DGD-BTC,SNGLS-BTC,1ST-BTC,TRST-BTC,TIME-BTC,GNO-BTC,REP-BTC,XMR-USD,DASH-USD,ETH-USD,NXT-USD,ZRC-BTC,BOS-BTC,DCT-BTC,ANT-BTC,AEON-BTC,GUP-BTC,PLU-BTC,LUN-BTC,EDG-BTC,RLC-BTC,SWT-BTC,TKN-BTC,WINGS-BTC,XAUR-BTC,AE-BTC,PTOY-BTC,ZEC-USD,XEM-USD,BCN-USD,XDN-USD,MAID-USD,ETC-BTC,ETC-USD,PLBT-BTC,BNT-BTC,SNT-ETH,CVC-USD,PAY-ETH,OAX-ETH,OMG-ETH,BQX-ETH,XTZ-BTC,DICE-BTC,PTOY-ETH,1ST-ETH,XAUR-ETH,TIME-ETH,DICE-ETH,SWT-ETH,XMR-ETH,ETC-ETH,DASH-ETH,ZEC-ETH,PLU-ETH,GNO-ETH,XRP-BTC,STRAT-USD,STRAT-BTC,SNC-ETH,ADX-ETH,BET-ETH,EOS-ETH,DENT-ETH,SAN-ETH,EOS-BTC,EOS-USD,XTZ-ETH,XTZ-USD,MYB-ETH,SUR-ETH,IXT-ETH,PLR-ETH,TIX-ETH,PRO-ETH,AVT-ETH,EVX-USD,DLT-BTC,BNT-ETH,BNT-USD,MANA-USD,DNT-BTC,FYP-BTC,OPT-BTC,TNT-ETH,STX-BTC,STX-ETH,STX-USD,TNT-USD,TNT-BTC,ENG-ETH,XUC-USD,SNC-BTC,SNC-USD,OAX-USD,OAX-BTC,ZRX-BTC,ZRX-ETH,ZRX-USD,RVT-BTC,PPC-BTC,PPC-USD,QTUM-ETH,IGNIS-ETH,BMC-BTC,BMC-ETH,BMC-USD,CND-BTC,CND-ETH,CND-USD,CDT-ETH,CDT-USD,FUN-BTC,FUN-ETH,FUN-USD,HVN-BTC,HVN-ETH,POE-BTC,POE-ETH,AMB-USD,AMB-ETH,AMB-BTC,HPC-BTC,PPT-ETH,MTH-BTC,MTH-ETH,LRC-BTC,LRC-ETH,ICX-BTC,ICX-ETH,NEO-BTC,NEO-ETH,NEO-USD,CSNO-BTC,ICX-USD,IND-ETH,KICK-BTC,YOYOW-BTC,CDT-BTC,XVG-BTC,XVG-ETH,XVG-USD,DGB-BTC,DGB-ETH,DGB-USD,DCN-ETH,DCN-USD,VIBE-BTC,ENJ-BTC,ENJ-ETH,ENJ-USD,ZSC-BTC,ZSC-ETH,ZSC-USD,TRX-BTC,TRX-ETH,TRX-USD,ART-BTC,EVX-BTC,EVX-ETH,SUB-BTC,SUB-ETH,SUB-USD,WTC-BTC,BTM-BTC,BTM-ETH,BTM-USD,LIFE-BTC,VIB-BTC,VIB-ETH,VIB-USD,DRT-ETH,STU-USD,OMG-BTC,PAY-BTC,PPT-BTC,SNT-BTC,BTG-BTC,BTG-ETH,BTG-USD,SMART-BTC,SMART-ETH,SMART-USD,XUC-ETH,XUC-BTC,LA-ETH,EDO-BTC,EDO-ETH,EDO-USD,HGT-ETH,IXT-BTC,SCL-BTC,ETP-BTC,ETP-ETH,ETP-USD,NEBL-BTC,NEBL-ETH,ARN-BTC,ARN-ETH,STU-BTC,STU-ETH,GVT-ETH,BTX-BTC,LTC-ETH,BCN-ETH,MAID-ETH,NXT-ETH,STRAT-ETH,XDN-ETH,XEM-ETH,PLR-BTC,SUR-BTC,BQX-BTC,DOGE-ETH,AMM-BTC,AMM-ETH,AMM-USD,DBIX-BTC,PRE-BTC,ZAP-BTC,DOV-BTC,DOV-ETH,XRP-ETH,XRP-USD,HSR-BTC,LEND-BTC,LEND-ETH,SPF-ETH,SBTC-BTC,SBTC-ETH,LOC-BTC,LOC-ETH,LOC-USD,SWFTC-BTC,SWFTC-ETH,SWFTC-USD,STAR-ETH,SBTC-USD,STORM-BTC,DIM-ETH,DIM-USD,DIM-BTC,NGC-BTC,NGC-ETH,NGC-USD,EMC-ETH,EMC-USD,MCO-BTC,MCO-ETH,MCO-USD,MANA-ETH,MANA-BTC,CPAY-ETH,DATA-BTC,DATA-ETH,DATA-USD,UTT-BTC,UTT-ETH,UTT-USD,KMD-BTC,KMD-ETH,KMD-USD,QTUM-USD,QTUM-BTC,SNT-USD,OMG-USD,EKO-BTC,EKO-ETH,ADX-BTC,ADX-USD,LSK-ETH,LSK-USD,PLR-USD,SUR-USD,BQX-USD,DRT-USD,REP-ETH,REP-USD,WAXP-BTC,WAXP-ETH,WAXP-USD,C20-BTC,C20-ETH,IDH-BTC,IDH-ETH,IPL-BTC,COV-BTC,COV-ETH,SENT-BTC,SENT-ETH,SENT-USD,SMT-BTC,SMT-ETH,SMT-USD,CHAT-BTC,CHAT-ETH,CHAT-USD,TRAC-ETH,JNT-ETH,UTK-BTC,UTK-ETH,UTK-USD,GNX-ETH,CHSB-BTC,CHSB-ETH,DAY-BTC,DAY-ETH,DAY-USD,NEU-BTC,NEU-ETH,NEU-USD,TAU-BTC,FLP-BTC,FLP-ETH,FLP-USD,R-BTC,R-ETH,EKO-USD,BCPT-ETH,BCPT-USD,PKT-BTC,PKT-ETH,BETR-BTC,BETR-ETH,HAND-ETH,HAND-USD,CHP-ETH,BCPT-BTC,ACT-BTC,ACT-ETH,ACT-USD,ADA-BTC,ADA-ETH,ADA-USD,SIG-BTC,MTX-BTC,MTX-ETH,MTX-USD,WIZ-BTC,WIZ-ETH,WIZ-USD,DADI-BTC,DADI-ETH,BDG-ETH,DATX-BTC,DATX-ETH,TRUE-BTC,DRG-BTC,DRG-ETH,BANCA-BTC,BANCA-ETH,ZAP-ETH,ZAP-USD,AUTO-BTC,SOC-BTC,OCN-BTC,OCN-ETH,STQ-BTC,STQ-ETH,XLM-BTC,XLM-ETH,XLM-USD,IOTA-BTC,IOTA-ETH,IOTA-USD,DRT-BTC,BETR-USD,ERT-BTC,CRPT-BTC,CRPT-USD,MESH-BTC,MESH-ETH,MESH-USD,IHT-BTC,IHT-ETH,IHT-USD,SCC-BTC,YCC-BTC,DAN-BTC,TEL-BTC,TEL-ETH,NCT-BTC,NCT-ETH,NCT-USD,BMH-BTC,BANCA-USD,BERRY-BTC,BERRY-ETH,BERRY-USD,GBX-BTC,GBX-ETH,GBX-USD,SHIP-BTC,SHIP-ETH,NANO-BTC,NANO-ETH,NANO-USD,LNC-BTC,KIN-ETH,ARDR-USD,FOTA-ETH,FOTA-BTC,CVT-BTC,CVT-ETH,CVT-USD,STQ-USD,GNT-BTC,GNT-ETH,GNT-USD,GET-BTC,MITH-BTC,MITH-ETH,MITH-USD,DADI-USD,TKY-BTC,ACAT-BTC,ACAT-ETH,ACAT-USD,BTX-USD,WIKI-BTC,WIKI-ETH,WIKI-USD,ONT-BTC,ONT-ETH,ONT-USD,FTX-BTC,FTX-ETH,NAVI-BTC,VME-ETH,NAVI-ETH,LND-ETH,CSM-BTC,NANJ-BTC,NTK-BTC,NTK-ETH,NTK-USD,AUC-BTC,AUC-ETH,CMCT-BTC,CMCT-ETH,CMCT-USD,MAN-BTC,MAN-ETH,MAN-USD,PNT-BTC,PNT-ETH,FXT-BTC,NEXO-BTC,PAT-BTC,PAT-ETH,XMC-BTC,FXT-ETH,XMC-ETH,XMC-USD,FDZ-BTC,FDZ-ETH,FDZ-USD,SPD-BTC,SPD-ETH,MITX-BTC,TIV-BTC,B2G-BTC,B2G-USD,HBZ-BTC,FACE-BTC,FACE-ETH,HBZ-ETH,HBZ-USD,CPT-BTC,PAT-USD,HTML-BTC,HTML-ETH,MITX-ETH,BTS-BTC,BNK-BTC,BNK-ETH,BNK-USD,TIV-ETH,TIV-USD,CSM-ETH,CSM-USD,INK-BTC,IOST-BTC,INK-ETH,INK-USD,CBC-BTC,IOST-USD,ZIL-BTC,ABYSS-BTC,ABYSS-ETH,ZIL-USD,BCI-BTC,CBC-ETH,CBC-USD,PITCH-BTC,PITCH-ETH,HTML-USD,TDS-BTC,TDS-ETH,TDS-USD,SBD-ETH,SBD-USD,DPN-BTC,UUU-BTC,UUU-ETH,XBP-BTC,ELEC-BTC,ELEC-ETH,ELEC-USD,QNTU-BTC,QNTU-ETH,QNTU-USD,IPL-ETH,IPL-USD,CENNZ-BTC,CENNZ-ETH,SWM-BTC,SPF-USD,SPF-BTC,LCC-BTC,HGT-BTC,ETH-TUSD,BTC-TUSD,LTC-TUSD,XMR-TUSD,ZRX-TUSD,NEO-TUSD,USD-TUSD,BTC-DAI,ETH-DAI,MKR-DAI,EOS-DAI,USD-DAI,MKR-BTC,MKR-ETH,MKR-USD,TUSD-DAI,NEO-DAI,LTC-DAI,XMR-DAI,XRP-DAI,NEXO-ETH,NEXO-USD,DWS-BTC,DWS-ETH,DWS-USD,APPC-BTC,APPC-ETH,APPC-USD,BIT-ETH,SPC-BTC,SPC-ETH,SPC-USD,REX-BTC,REX-ETH,REX-USD,ELF-BTC,ELF-USD,BCD-BTC,BCD-USD,CVCOIN-BTC,CVCOIN-ETH,CVCOIN-USD,EDG-ETH,EDG-USD,NLC2-BTC,DASH-EURS,ZEC-EURS,BTC-EURS,EOS-EURS,ETH-EURS,LTC-EURS,NEO-EURS,XMR-EURS,XRP-EURS,EURS-USD,EURS-TUSD,EURS-DAI,MNX-USD,ROX-ETH,ZPR-ETH,MNX-BTC,MNX-ETH,KIND-BTC,KIND-ETH,ENGT-BTC,ENGT-ETH,PMA-BTC,PMA-ETH,TV-BTC,TV-ETH,TV-USD,BAT-BTC,BAT-ETH,BAT-USD,SRN-BTC,SRN-ETH,SRN-USD,SVD-BTC,SVD-ETH,SVD-USD,GST-BTC,GST-ETH,GST-USD,BNB-BTC,BNB-ETH,BNB-USD,DIT-BTC,DIT-ETH,POA20-BTC,PROC-BTC,POA20-ETH,POA20-USD,POA20-DAI,NIM-BTC,USE-BTC,USE-ETH,DAV-BTC,DAV-ETH,ABTC-BTC,NIM-ETH,ABA-BTC,ABA-ETH,ABA-USD,BCN-EOS,LTC-EOS,XMR-EOS,DASH-EOS,TRX-EOS,NEO-EOS,ZEC-EOS,LSK-EOS,XEM-EOS,XRP-EOS,RCN-BTC,RCN-ETH,RCN-USD,HMQ-BTC,HMQ-ETH,MYST-BTC,MYST-ETH,USD-GUSD,BTC-GUSD,ETH-GUSD,EOS-GUSD,AXPR-BTC,AXPR-ETH,DAG-BTC,DAG-ETH,BITS-BTC,BITS-ETH,BITS-USD,CDCC-BTC,CDCC-ETH,CDCC-USD,VET-BTC,VET-ETH,VET-USD,SILK-ETH,BOX-BTC,BOX-ETH,BOX-EURS,BOX-EOS,VOCO-BTC,VOCO-ETH,VOCO-USD,PASS-BTC,PASS-ETH,SLX-BTC,SLX-USD,PBTT-BTC,PMA-USD,TRAD-BTC,DGTX-BTC,DGTX-ETH,DGTX-USD,MRK-BTC,MRK-ETH,DGB-TUSD,SNBL-BTC,BCH-BTC,BCH-USD,BSV-BTC,BSV-USD,BKX-BTC,NPLC-BTC,NPLC-ETH,ETN-BTC,ETN-ETH,ETN-USD,DTR-BTC,DTR-ETH,TDP-BTC,HBT-ETH,PXG-BTC,PXG-USD,BTC-PAX,ETH-PAX,USD-PAX,BTC-USDC,ETH-USDC,USD-USDC,TUSD-USDC,DAI-USDC,EOS-PAX,CLO-BTC,CLO-ETH,CLO-USD,PETH-BTC,PETH-ETH,PETH-USD,BRD-BTC,BRD-ETH,NMR-BTC,SALT-BTC,SALT-ETH,POLY-BTC,POLY-ETH,POWR-BTC,POWR-ETH,STORJ-BTC,STORJ-ETH,STORJ-USD,MLN-BTC,MLN-ETH,BDG-BTC,POA-ETH,POA-BTC,POA-USD,POA-DAI,KIN-BTC,VEO-BTC,PLA-BTC,PLA-ETH,PLA-USD,BTT-BTC,BTT-USD,BTT-ETH,ZEN-BTC,ZEN-ETH,ZEN-USD,GRIN-BTC,GRIN-ETH,GRIN-USD,FET-BTC,HT-BTC,HT-USD,XZC-BTC,XZC-ETH,XZC-USD,VRA-BTC,VRA-ETH,BTC-KRWB,USD-KRWB,WBTC-ETH,CRO-BTC,CRO-ETH,CRO-USD,GAS-BTC,GAS-ETH,GAS-USD,ORMEUS-BTC,ORMEUS-ETH,SWM-ETH,SWM-USD,PRE-ETH,PHX-BTC,PHX-ETH,PHX-USD,BET-BTC,USD-EOSDT,BTC-EOSDT,ETH-EOSDT,EOS-EOSDT,DAI-EOSDT,NUT-BTC,NUT-EOS,NUT-USD,CUTE-BTC,CUTE-ETH,CUTE-USD,CUTE-EOS,XCON-BTC,DCR-BTC,DCR-ETH,DCR-USD,MG-BTC,MG-ETH,MG-EOS,MG-USD,GNX-BTC,PRO-BTC,EURS-EOSDT,TUSD-EOSDT,ECOIN-BTC,ECOIN-ETH,ECOIN-USD,AGI-BTC,LOOM-BTC,LOOM-ETH,BLZ-BTC,QKC-BTC,QKC-ETH,KNC-BTC,KNC-ETH,KNC-USD,KEY-BTC,KEY-ETH,ATOM-BTC,ATOM-USD,ATOM-ETH,BRDG-BTC,BRDG-ETH,BRDG-USD,MTL-BTC,MTL-ETH,EXP-BTC,BTCB-BTC,PBT-BTC,PBT-ETH,LINK-BTC,LINK-ETH,LINK-USD,USD-USDT20,PHB-BTC,BCH-ETH,BCH-DAI,BCH-TUSD,BCH-EURS,DAPP-BTC,DAPP-EOS,BTC-USDT20,DENT-BTC,DENT-USD,NJBC-BTC,NJBC-ETH,XRC-BTC,EOS-BCH,LTC-BCH,XRP-BCH,TRX-BCH,XLM-BCH,ETC-BCH,DASH-BCH,ZEC-BCH,BKX-USD,LAMB-BTC,NPXS-BTC,HBAR-BTC,HBAR-USD,ONE-BTC,RFR-BTC,RFR-USD,BUSD-USD,PAXG-BTC,PAXG-USD,REN-BTC,IGNIS-BTC,CEL-BTC,CEL-ETH,WIN-USD,ADK-BTC,PART-BTC,SOZ-BTC,SOZ-ETH,SOZ-USD,WAVES-USD,ADA-BCH,ONT-BCH,XMR-BCH,ATOM-BCH,LINK-BCH,OMG-BCH,WAVES-BCH,IOTX-BTC,HOT-BTC,SLV-BTC,HEDG-BTC,CHZ-BTC,CHZ-USD,COCOS-BTC,COCOS-USD,SEELE-BTC,SEELE-USD,MDA-BTC,LEO-USD,REM-BTC,REM-ETH,REM-USD,SCD-DAI,BTC-BUSD,RVN-BTC,BST-BTC,ERD-BTC,KRL-BTC,FTT-BTC,FTT-USD,RAISE-BTC,RAISE-ETH"
+ "available": "XTZ-USD,MITH-USD,ZRC-BTC,ZEN-ETH,EDG-BTC,IHT-BTC,AEON-BTC,DGB-ETH,EOS-USD,ZEC-EOS,NEO-TUSD,XZC-USD,SOC-BTC,PPC-USD,BCH-USD,HBAR-BTC,EOS-GUSD,ZRX-BTC,OPT-BTC,NGC-USD,APPC-BTC,PHX-ETH,DRGN-BTC,POA20-USD,BOX-EURS,PTOY-BTC,XDN-BTC,OKB-BTC,CHSB-BTC,BNB-USD,NCT-BTC,BTC-GUSD,COCOS-USD,CVCOIN-ETH,SILK-ETH,GET-BTC,FUN-BTC,EXP-BTC,ETC-USD,EMRX-BTC,REP-ETH,NEO-USD,REV-BTC,LSK-ETH,TRAC-ETH,GHOST-BTC,CND-ETH,BMH-BTC,SNC-BTC,DTR-BTC,ERD-BTC,HTML-ETH,BQX-USD,SCL-BTC,KNC-ETH,SMT-USD,HMQ-BTC,ACT-BTC,BOX-ETH,ZEC-BCH,ETC-BTC,IQ-USD,QTUM-BTC,ETH-GUSD,LEO-USD,EDG-USD,MTX-BTC,SBTC-BTC,BNT-ETH,CUR-ETH,KIND-BTC,CRO-ETH,SMT-BTC,BTB-BTC,ACAT-ETH,AMM-ETH,SWFTC-BTC,STX-USD,1ST-BTC,GBX-USD,UTT-BTC,AXPR-BTC,NMR-BTC,XMR-EOS,ACT-USD,NTK-ETH,EVX-BTC,EURS-USD,BTT-ETH,IOTA-BTC,NUT-EOS,EURS-DAI,STMX-BTC,SALT-BTC,MBL-USD,DGB-BTC,WAVES-BCH,NTK-BTC,AMM-BTC,GNT-ETH,NIM-ETH,SMT-ETH,ALGO-BTC,BTG-USD,USE-ETH,ACAT-USD,TNT-USD,LINK-USD,CENNZ-ETH,VET-USD,ORMEUS-BTC,KIND-ETH,DCR-ETH,DGB-USD,RCN-USD,BDG-BTC,BQX-BTC,EKO-BTC,CLO-ETH,FYP-BTC,IPX-BTC,HOT-BTC,AMM-USD,MG-BTC,FLP-ETH,BTS-BTC,ADX-BTC,XRP-DAI,XLM-ETH,CRPT-BTC,WAXP-BTC,FDZ-USD,NXT-ETH,POA-BTC,NEO-EOS,PXG-USD,PLBT-BTC,SHIP-BTC,HTML-BTC,GAS-USD,QTUM-ETH,CDT-USD,CVC-USD,DICE-ETH,BOX-BTC,TRX-ETH,ETH-EURS,GNO-BTC,GRIN-ETH,CUTE-ETH,PAXG-USD,UBT-BTC,BTT-BTC,IOTA-ETH,BETR-USD,ZEN-BTC,XVG-ETH,REM-USD,CHAT-ETH,ACT-ETH,ARPA-USD,WAVES-USD,VEO-BTC,POA20-BTC,MTX-ETH,TIME-ETH,BCPT-BTC,EMC-USD,XAUR-ETH,SRN-BTC,NCT-ETH,XUC-USD,XPR-BTC,SNC-ETH,DTR-ETH,LEND-ETH,ZAP-ETH,POA20-DAI,ETHBNT-BTC,MANA-BTC,QKC-BTC,MLN-BTC,FLP-BTC,SOLO-BTC,SNT-USD,USD-TUSD,QKC-ETH,FXT-ETH,TRUE-BTC,VSYS-BTC,DCN-ETH,USG-ETH,GNO-ETH,GNT-BTC,MESH-USD,BOS-BTC,PHB-BTC,REX-USD,EURS-TUSD,XRP-EURS,ZEC-BTC,ESH-BTC,SWM-BTC,DASH-EURS,ADX-ETH,MLN-ETH,BCPT-ETH,NANO-BTC,VIBE-BTC,LA-ETH,LSK-EOS,HVN-BTC,NPLC-ETH,LTC-USD,SOLVE-BTC,HBT-ETH,ELEC-BTC,PLA-ETH,PTOY-ETH,PAD-USD,ETC-ETH,COV-ETH,NUT-USD,LRC-BTC,STRAT-ETH,VET-ETH,AGI-BTC,XPR-ETH,LNC-BTC,WAVES-BTC,WTC-BTC,ABTC-BTC,SWFTC-ETH,NANO-ETH,ONT-BTC,STRAT-BTC,GNX-BTC,TUSD-EOSDT,NEU-BTC,BCN-BTC,PAY-ETH,XPNT-BTC,TNT-ETH,XRP-ETH,INK-ETH,ECA-BTC,ARDR-BTC,XPNT-ETH,KIN-BTC,LSK-BTC,VIB-ETH,FACE-ETH,USE-BTC,AMB-ETH,SALT-ETH,NEXO-USD,IOTX-BTC,ATOM-BCH,LCX-ETH,CRO-BTC,XMR-USD,IDH-BTC,LINK-BTC,BCH-TUSD,AXPR-ETH,PASS-ETH,CDT-ETH,OAX-BTC,CPT-BTC,PLU-ETH,NGC-BTC,GBX-ETH,DATA-USD,XNS-BTC,KEY-BTC,TKY-BTC,HBAR-USD,XUC-ETH,HSR-BTC,TNT-BTC,SMART-BTC,TRST-BTC,LND-ETH,UTT-ETH,AUC-ETH,DCR-BTC,DRT-USD,WINGS-BTC,GT-BTC,BMC-USD,EKO-USD,ADX-USD,MKR-BTC,LTC-DAI,TNC-USD,BDP-ETH,ONE-BTC,DOGE-BTC,ARN-BTC,RCN-ETH,NAV-USD,SRN-USD,ACAT-BTC,ZRX-ETH,BMC-BTC,GVT-ETH,RAISE-BTC,VIB-USD,XDN-USD,BCN-ETH,MITX-ETH,PITCH-ETH,EXM-BTC,OMG-USD,IPL-ETH,TIME-BTC,MCO-ETH,REX-BTC,FDZ-BTC,HT-BTC,MTH-BTC,HEDG-USD,ICX-USD,SCC-BTC,BET-BTC,MKR-ETH,XNS-USD,DENT-BTC,IDRT-BTC,IPL-BTC,SWT-ETH,NEXO-ETH,ZAP-BTC,CMCT-BTC,COMP-USD,CBC-ETH,SENT-ETH,ZIL-USD,TDP-BTC,XAUR-BTC,MTL-BTC,LINK-ETH,POLY-ETH,BTM-USD,REP-USD,NEBL-BTC,SBTC-USD,DATA-ETH,BAT-BTC,STEEM-BTC,LTC-EOS,CUR-BTC,BYTZ-BTC,PMA-ETH,AYA-USD,APPC-ETH,PRO-BTC,ETH-DAI,EVX-ETH,LOOM-BTC,BTC-USD,BAT-USD,HMQ-ETH,CUR-USD,DRG-BTC,DICE-BTC,ADK-BTC,COMP-BTC,BDG-ETH,DRT-BTC,XTZ-BTC,BAT-ETH,ETC-BCH,TRX-EOS,WBTC-ETH,C20-ETH,BTC-EURS,DENT-USD,CHZ-BTC,BDP-USD,CHSB-ETH,DAY-USD,NEO-BTC,NPLC-BTC,STAR-ETH,USD-GUSD,XCON-BTC,LEVL-BTC,BTC-PAX,PLR-ETH,PART-BTC,ELEC-ETH,ADA-ETH,PRE-BTC,MAN-USD,BCH-EURS,NEO-DAI,ERK-BTC,NANO-USD,HEDG-BTC,MG-ETH,CVCOIN-USD,BIT-ETH,IPL-USD,XRP-EOS,FET-BTC,XVG-USD,PAXG-BTC,DCR-USD,USD-DAI,XRP-USD,PBT-ETH,DAG-BTC,AVA-BTC,CUTE-BTC,NEXO-BTC,DAY-BTC,MG-EOS,SUB-ETH,PITCH-BTC,MITX-BTC,DAY-ETH,FLP-USD,MCO-USD,NXT-BTC,POWR-BTC,APPC-USD,PLR-BTC,CVCOIN-BTC,XTZ-ETH,NCT-USD,BTC-TUSD,CHP-ETH,SENSO-USD,MYST-BTC,DLT-BTC,HTML-USD,DIT-ETH,REM-BTC,EOS-ETH,DAG-ETH,RLC-BTC,DNA-BTC,FOTA-BTC,STORJ-ETH,MKR-USD,NEO-EURS,SBD-BTC,SPC-ETH,ETH-PAX,SBD-ETH,ELF-BTC,FTX-ETH,DENT-ETH,ELEC-USD,STORJ-USD,DGTX-ETH,TEL-BTC,C20-BTC,PMA-USD,PNT-BTC,CND-BTC,ENJ-USD,BETR-BTC,ZEN-USD,UTK-BTC,SWM-ETH,CVC-BTC,JNT-ETH,ETP-BTC,ETH-BTC,ZPR-ETH,FOTA-ETH,ZIL-BTC,APL-USD,ARPA-BTC,USD-EOSDT,ICX-ETH,INK-BTC,POA20-ETH,XEM-USD,DGB-TUSD,NPXS-BTC,XLM-BCH,HVN-ETH,LTC-EURS,MESH-BTC,NIM-BTC,DATX-BTC,HT-USD,FXT-BTC,WIN-USD,IHT-USD,NTK-USD,ADA-BCH,PBT-BTC,ZEC-USD,GST-BTC,BSV-BTC,GAS-BTC,CBC-BTC,MCO-BTC,ATOM-ETH,MTL-ETH,SENT-BTC,GBX-BTC,XRC-BTC,CPAY-ETH,GST-USD,POE-BTC,SUR-BTC,SOLO-USD,LOC-BTC,BNT-USD,WIKI-BTC,PPT-BTC,ZRX-TUSD,VSYS-USD,WAXP-USD,TV-ETH,DCN-USD,BMC-ETH,CVT-BTC,APM-BTC,BCH-DAI,ZSC-ETH,BCN-EOS,ZRX-USD,LEND-BTC,CBC-USD,NUT-BTC,DOV-BTC,KMD-BTC,XDN-ETH,TUSD-USDC,ETP-USD,AYA-BTC,EURS-EOSDT,ECA-USD,MITH-ETH,XZC-ETH,ARN-ETH,IOTA-USD,LUN-BTC,XEM-BTC,RVN-BTC,EOS-DAI,BET-ETH,DASH-EOS,EVX-USD,FUN-USD,ONT-BCH,ZEC-EURS,BCD-BTC,SPC-USD,XMR-BTC,USG-USD,CVT-USD,LTC-TUSD,DRG-ETH,BCH-ETH,ABYSS-ETH,MTX-USD,CUTE-EOS,CHZ-USD,DGTX-USD,ETH-EOSDT,ENJ-ETH,EOS-PAX,NWC-BTC,USG-BTC,USD-USDC,CLO-BTC,BANCA-ETH,NLC2-BTC,IOST-USD,BTCB-BTC,BERRY-BTC,ART-BTC,GRIN-BTC,VITAE-BTC,XBP-BTC,OMG-BTC,BTX-USD,BTG-ETH,BTM-ETH,MDA-BTC,KRL-BTC,IGNIS-ETH,BCH-BTC,UUU-ETH,SUR-ETH,POLY-BTC,NEBL-ETH,PLA-BTC,SUB-USD,AVT-ETH,BERRY-USD,BANCA-BTC,NXT-USD,ENJ-BTC,LRC-ETH,TRIGX-BTC,UUU-BTC,PASS-BTC,POA-USD,CRPT-USD,DATX-ETH,ANT-BTC,LAMB-BTC,CMCT-USD,SRN-ETH,UTT-USD,ALGO-USD,STX-ETH,DAPP-EOS,RFR-BTC,CVT-ETH,AMB-BTC,ROOBEE-BTC,SNT-ETH,BST-BTC,LCC-BTC,DASH-BCH,BCN-USD,RCN-BTC,MIN-BTC,ATOM-USD,RFR-USD,KMD-USD,BTC-BUSD,KIN-ETH,POST-USD,OAX-ETH,DIT-BTC,ETN-USD,PPC-BTC,XMR-ETH,AE-BTC,FDZ-ETH,COMP-ETH,IQ-BTC,BNK-BTC,CENNZ-BTC,PPT-ETH,SUB-BTC,USD-IDRT,BCPT-USD,OCN-BTC,DGD-BTC,HGT-ETH,VRA-BTC,STX-BTC,AERGO-BTC,HGT-BTC,TRAD-BTC,TRX-BCH,PLBT-ETH,IGNIS-BTC,NGC-ETH,FTT-USD,POA-DAI,REP-BTC,DAPP-BTC,DNT-BTC,CDT-BTC,YCC-BTC,BCD-USD,NWC-USD,SNGLS-BTC,ICX-BTC,JST-USD,BERRY-ETH,OAX-USD,PKT-BTC,TRX-USD,DRT-ETH,COV-BTC,SUR-USD,PRE-ETH,ZEC-ETH,PAY-BTC,MYST-ETH,ABYSS-BTC,DAI-USDC,FUN-ETH,BLZ-BTC,DAV-BTC,GNT-USD,BSV-BCH,TKN-BTC,PKT-ETH,VRA-ETH,IDH-ETH,SWFTC-USD,ERT-BTC,NEU-USD,XMC-ETH,SPC-BTC,CEL-ETH,SEELE-BTC,MESH-ETH,XPNT-USD,XZC-BTC,MAID-BTC,NEU-ETH,PLR-USD,AUTO-BTC,REN-BTC,MAID-USD,DATA-BTC,LOC-USD,AUC-BTC,TV-BTC,DASH-ETH,KEY-ETH,DAV-ETH,LAVA-BTC,KAVA-BTC,AVA-ETH,LINK-BCH,GNX-ETH,ETP-ETH,LSK-USD,POE-ETH,EKO-ETH,NEO-ETH,LOOM-ETH,BTC-DAI,UTK-ETH,XMR-TUSD,ADA-USD,UTK-USD,EMC-ETH,TV-USD,EOS-EURS,OCN-ETH,DAPS-BTC,SHIP-ETH,KMD-ETH,BRDG-USD,IND-ETH,ADA-BTC,MKR-DAI,COCOS-BTC,MITH-BTC,BSV-USD,SWM-USD,SBTC-ETH,ELF-USD,NRG-BTC,CHAT-USD,PHX-BTC,EOS-EOSDT,DASH-BTC,MTH-ETH,ZSC-USD,REV-USD,CHAT-BTC,LTC-ETH,CLO-USD,SENT-USD,VET-BTC,MANA-ETH,BTC-EOSDT,LTC-BCH,GHOST-USD,ZSC-BTC,TUSD-DAI,OMG-BCH,ETH-TUSD,CUTE-USD,KNC-BTC,PNT-ETH,DGTX-BTC,CEL-BTC,ETH-USD,WIKI-USD,GST-ETH,XLM-USD,ETN-ETH,MG-USD,XMR-EURS,RAISE-ETH,BYTZ-USD,EOS-BCH,BTCSHORT-USD,IOST-BTC,BNB-BTC,PBTT-BTC,SBD-USD,XMC-BTC,ALGO-ETH,EMC-BTC,VIB-BTC,PLA-USD,STRAT-USD,XPR-USD,BNT-BTC,STORJ-BTC,ERK-USD,BRDG-ETH,AMB-USD,ORMEUS-ETH,ECA-ETH,ONT-USD,ATOM-BTC,SNC-USD,LCX-BTC,BETR-ETH,QTUM-USD,SC-BTC,REX-ETH,FTT-BTC,BTM-BTC,XMR-DAI,DAI-EOSDT,INK-USD,WAXP-ETH,XLM-BTC,SAN-ETH,IPX-USD,TRX-BTC,TEL-ETH,CELR-BTC,BRD-BTC,DASH-USD,WIKI-ETH,DBIX-BTC,ETN-BTC,SNT-BTC,LOC-ETH,XRP-BCH,ZAP-USD,MAID-ETH,ONT-ETH,BNK-USD,DOV-ETH,XMR-BCH,BOX-EOS,MOF-BTC,HEX-BTC,XUC-BTC,PLU-BTC,FACE-BTC,GRIN-USD,EDG-ETH,USD-PAX,SEELE-USD,TNC-BTC,REM-ETH,SIG-BTC,PXG-BTC,SMART-USD,BDP-BTC,BTX-BTC,TAU-BTC,XEM-ETH,ARDR-USD,CRO-USD,KNC-USD,BRD-ETH,AVA-USD,DCT-BTC,SMART-ETH,GAS-ETH,POA-ETH,YOYOW-BTC,XEM-EOS,POWR-ETH,BNB-ETH,BTT-USD,SWT-BTC,MAN-BTC,BQX-ETH,GLEEC-BTC,EOS-BTC,ETH-USDC,1ST-ETH,OMG-ETH,ROX-ETH,PHX-USD,IHT-ETH,BNK-ETH,BUSD-USD,XVG-BTC,NAV-BTC,CURE-BTC,CND-USD,ERK-ETH,DOGE-ETH,CMCT-ETH,PRO-ETH,XRP-BTC,BANCA-USD,KICK-BTC,MAN-ETH,BRDG-BTC,LTC-BTC,DOGE-USD,MANA-USD,XMC-USD,BTC-USDC,MATIC-BTC,FTX-BTC,BTG-BTC,PMA-BTC"
}
}
},
@@ -1537,7 +1639,7 @@
"pairs": {
"spot": {
"enabled": "BTC-USDT",
- "available": "WTC-BTC,LTC-HT,BCH-USDT,PVT-BTC,OCN-BTC,DTA-ETH,SKM-USDT,TNT-ETH,HC-ETH,ADA-USDT,SNT-USDT,ZJLT-BTC,LOL-USDT,LAMB-USDT,SOC-BTC,NKN-USDT,CNNS-USDT,CTXC-BTC,ELA-USDT,PAI-BTC,GNT-BTC,RSR-BTC,FOR-USDT,THETA-ETH,REN-USDT,DOCK-ETH,CVCOIN-BTC,CRO-BTC,ETN-ETH,AE-ETH,TOS-ETH,FTI-ETH,AST-BTC,SC-ETH,IOST-ETH,WXT-HT,RUFF-BTC,ARPA-USDT,OST-BTC,CMT-USDT,COVA-BTC,ZRX-BTC,FSN-USDT,LINK-ETH,NEW-HT,DAT-ETH,RTE-ETH,LXT-BTC,ACT-BTC,PNT-BTC,MX-HT,TOP-BTC,VSYS-USDT,IOST-USDT,IOTA-BTC,BIX-BTC,OGO-USDT,HPT-HT,YEE-BTC,HIT-BTC,QTUM-ETH,BSV-BTC,ITC-ETH,EGCC-BTC,XRP-BTC,DGD-BTC,DBC-ETH,UIP-BTC,SWFTC-ETH,CNN-BTC,SNC-BTC,PAY-ETH,QASH-BTC,CKB-USDT,BCH-HUSD,WAXP-BTC,LINK-USDT,ZEC-USDT,TOPC-ETH,MUSK-ETH,LET-BTC,EVX-ETH,BUT-BTC,DAC-BTC,ITC-USDT,XRP-HT,BTT-TRX,ONT-BTC,CVNT-ETH,DGB-ETH,PROPY-ETH,STEEM-ETH,QSP-ETH,BTT-BTC,CRE-BTC,MEX-ETH,LSK-ETH,LUN-ETH,EDU-ETH,XMX-BTC,UTK-BTC,MCO-BTC,EKO-BTC,ZLA-ETH,BTM-USDT,SMT-BTC,NPXS-BTC,SSP-ETH,BTM-ETH,LBA-USDT,SHE-BTC,TT-HT,SEELE-USDT,HC-USDT,ETC-HT,EKT-BTC,DCR-ETH,LAMB-ETH,ALGO-ETH,UUU-ETH,UUU-BTC,REN-BTC,ALGO-BTC,RBTC-BTC,SALT-BTC,GVE-ETH,GVE-BTC,DASH-HT,TUSD-HUSD,RSR-USDT,SEELE-ETH,PORTAL-ETH,SEELE-BTC,PORTAL-BTC,GNT-USDT,XTZ-BTC,XTZ-ETH,REN-ETH,MAN-BTC,TT-BTC,18C-ETH,ARDR-ETH,MAN-ETH,GTC-BTC,BFT-ETH,PAX-HUSD,AIDOC-BTC,RDN-BTC,NCASH-BTC,MX-USDT,IDT-BTC,ETH-HUSD,LOL-BTC,SSP-BTC,ATP-BTC,POWR-BTC,ENG-ETH,QTUM-USDT,APPC-ETH,CVNT-BTC,BHD-HT,APPC-BTC,BAT-ETH,WAN-BTC,NANO-ETH,LXT-USDT,OGO-HT,NULS-USDT,EDU-BTC,IRIS-USDT,SRN-BTC,DOGE-BTC,GNX-BTC,SRN-ETH,CKB-BTC,GAS-BTC,GSC-ETH,ZLA-BTC,IDT-ETH,ELF-BTC,UTK-ETH,LOOM-ETH,LSK-BTC,ELF-ETH,GTC-ETH,SOC-USDT,18C-BTC,ATOM-ETH,GRS-BTC,MT-BTC,HT-ETH,BSV-HUSD,BHT-USDT,KAN-USDT,MXC-BTC,BOX-BTC,CMT-BTC,POWR-ETH,WICC-ETH,BIX-ETH,REQ-ETH,ADX-BTC,ITC-BTC,VET-ETH,BLZ-BTC,MT-HT,BTT-USDT,ETH-USDT,YEE-ETH,FOR-BTC,BHD-USDT,QTUM-BTC,NODE-HT,WAVES-BTC,UIP-USDT,AAC-ETH,LOOM-BTC,SMT-ETH,DASH-USDT,ATOM-BTC,EKT-ETH,OGO-BTC,BTM-BTC,HIT-USDT,STORJ-USDT,WXT-BTC,BLZ-ETH,KCASH-ETH,FOR-HT,BSV-USDT,MANA-BTC,QUN-BTC,DATX-ETH,FTI-BTC,CVC-BTC,POLY-BTC,BTS-BTC,NEXO-ETH,DTA-USDT,BCH-BTC,DOGE-ETH,TOPC-BTC,NEO-BTC,GNX-ETH,GSC-BTC,FTT-HT,LET-USDT,SBTC-BTC,VIDY-HT,PAI-USDT,BKBT-ETH,GET-ETH,BFT-BTC,NODE-BTC,TRX-USDT,ARDR-BTC,GXC-USDT,FTT-BTC,ATP-HT,IIC-ETH,BCH-HT,QASH-ETH,AIDOC-ETH,RDN-ETH,ADA-ETH,BUT-ETH,CRE-USDT,ENG-BTC,WAXP-ETH,GT-USDT,VIDY-BTC,LTC-BTC,ZIL-USDT,XEM-USDT,EGT-BTC,OCN-ETH,XZC-USDT,DTA-BTC,EKT-USDT,SOC-ETH,BHT-BTC,ZJLT-ETH,HC-BTC,TNT-BTC,WTC-ETH,SMT-USDT,FTT-USDT,CKB-HT,NPXS-ETH,DAT-BTC,XMR-USDT,NCC-ETH,AE-BTC,BKBT-BTC,ONT-ETH,GET-BTC,BTT-ETH,ETN-BTC,UUU-USDT,QSP-BTC,IOST-BTC,LOL-HT,VSYS-BTC,AST-ETH,BTS-ETH,DATX-BTC,NEW-USDT,BTG-BTC,MDS-USDT,LUN-BTC,IOTA-USDT,PHX-BTC,OST-ETH,ABT-ETH,POLY-ETH,NAS-USDT,NKN-HT,TOS-BTC,CNN-ETH,CVCOIN-ETH,ICX-ETH,ATOM-USDT,SKM-HT,MUSK-BTC,MDS-ETH,WPR-BTC,GNT-ETH,IRIS-ETH,THETA-BTC,BCX-BTC,EM-USDT,ELF-USDT,MANA-ETH,DOCK-BTC,SNC-ETH,LXT-ETH,WICC-BTC,ACT-ETH,BAT-USDT,REQ-BTC,HPT-BTC,VET-BTC,MEET-BTC,ATP-USDT,XMR-ETH,TOP-HT,FSN-HT,DGD-ETH,WAVES-ETH,MANA-USDT,WXT-USDT,YCC-ETH,HB10-USDT,MTX-ETH,ZRX-USDT,KCASH-HT,RCCC-BTC,COVA-ETH,GRS-ETH,MT-ETH,XLM-ETH,AKRO-USDT,ADA-BTC,UC-BTC,IOTA-ETH,HT-BTC,RUFF-USDT,EOS-ETH,BHD-BTC,IIC-BTC,ADX-ETH,ZRX-ETH,ZEN-ETH,FSN-BTC,SC-BTC,QUN-ETH,LTC-HUSD,ALGO-USDT,RSR-HT,NEXO-BTC,PVT-USDT,RUFF-ETH,RTE-BTC,GT-BTC,EOS-USDT,HOT-BTC,CNNS-HT,ZIL-BTC,ETC-BTC,GAS-ETH,LTC-USDT,STORJ-BTC,CVC-ETH,DASH-BTC,EOS-HUSD,NODE-USDT,MX-BTC,CTXC-ETH,CTXC-USDT,LBA-ETH,UGAS-BTC,GXC-BTC,LYM-ETH,SNT-BTC,CNNS-BTC,STEEM-USDT,XLM-USDT,NANO-BTC,AAC-BTC,KCASH-BTC,GT-HT,OMG-USDT,CRE-HT,CVC-USDT,TNB-ETH,CMT-ETH,AE-USDT,MTN-BTC,NEW-BTC,BHT-HT,BTS-USDT,KMD-ETH,OMG-ETH,OMG-BTC,EGT-HT,VIDY-USDT,KNC-ETH,KNC-BTC,CRO-HT,MTN-ETH,DCR-USDT,GXC-ETH,NKN-BTC,NAS-ETH,RCN-BTC,STK-BTC,TRX-BTC,ELA-ETH,SHE-ETH,BCD-BTC,KMD-BTC,NEO-USDT,USDT-HUSD,PVT-HT,SALT-ETH,BCV-BTC,WAVES-USDT,XZC-ETH,LAMB-BTC,BOX-ETH,WTC-USDT,TOP-USDT,ONE-USDT,CRO-USDT,EGT-USDT,SKM-BTC,OCN-USDT,WAN-ETH,ACT-USDT,CHAT-ETH,ETH-BTC,BAT-BTC,IOST-HT,XMX-ETH,XVG-BTC,USDC-HUSD,THETA-USDT,DOCK-USDT,XEM-BTC,FAIR-ETH,HT-USDT,STK-ETH,UIP-ETH,RCN-ETH,NAS-BTC,PC-BTC,HOT-ETH,TRIO-BTC,ZEN-BTC,ELA-BTC,NCASH-ETH,ARPA-BTC,ONT-USDT,PNT-ETH,ONE-BTC,MEET-ETH,EM-HT,NANO-USDT,NULS-ETH,NULS-BTC,HIT-ETH,XMR-BTC,YCC-BTC,KAN-ETH,IRIS-BTC,EM-BTC,KAN-BTC,DBC-BTC,MTX-BTC,EGCC-ETH,ARPA-HT,TNB-BTC,EOS-HT,LYM-BTC,UGAS-ETH,BTC-HUSD,LINK-BTC,AKRO-BTC,ICX-BTC,LBA-BTC,AKRO-HT,BIFI-BTC,TRIO-ETH,DOGE-USDT,ONE-HT,WPR-ETH,XRP-USDT,DCR-BTC,PC-ETH,MDS-BTC,CHAT-BTC,MCO-ETH,XZC-BTC,ABT-BTC,BIX-USDT,PROPY-BTC,VET-USDT,ZEC-BTC,MEX-BTC,EKO-ETH,XVG-ETH,BTC-USDT,STEEM-BTC,MTL-BTC,VSYS-HT,XLM-BTC,FAIR-BTC,HPT-USDT,LAMB-HT,TRX-ETH,ZIL-ETH,DGB-BTC,SWFTC-BTC,RCCC-ETH,TT-USDT,HT-HUSD,EVX-BTC,UC-ETH,LET-ETH,ETC-USDT,PAY-BTC,BCV-ETH,XTZ-USDT,NCC-BTC,EOS-BTC,WICC-USDT,PAI-ETH,DAC-ETH,XRP-HUSD"
+ "available": "EKT-USDT,IIC-BTC,LINK-ETH,LYM-BTC,PVT-HT,BHT-USDT,TRX-BTC,BSV-USDT,NODE-BTC,NULS-BTC,XZC-ETH,BTC-HUSD,RCCC-BTC,ATP-HT,SEELE-ETH,AE-BTC,NEXO-BTC,PNT-ETH,IOTA-BTC,ATP-USDT,BHT-HT,KMD-ETH,REN-USDT,AST-BTC,UTK-BTC,LAMB-BTC,HIVE-HT,CHAT-BTC,YEE-ETH,SMT-BTC,GVE-BTC,NAS-BTC,BCD-BTC,GTC-ETH,BCH-HUSD,USDC-HUSD,ZEC-BTC,CKB-USDT,CTXC-ETH,EGCC-BTC,NKN-BTC,TNB-BTC,TOS-BTC,ELA-ETH,CNNS-USDT,LET-USDT,MAN-BTC,EOS-ETH,STK-ETH,IRIS-BTC,MT-ETH,DCR-ETH,RTE-BTC,WXT-HT,VIDY-USDT,ICX-BTC,WTC-BTC,BCV-ETH,ONE-HT,EKT-ETH,DATX-BTC,NANO-BTC,ITC-BTC,ETC-USDT,ETN-ETH,OCN-USDT,POWR-ETH,PAX-HUSD,ACT-ETH,AKRO-USDT,HOT-BTC,BOX-BTC,GNT-ETH,TT-USDT,DTA-USDT,HIT-BTC,UC-BTC,NEW-HT,MEET-BTC,DOGE-ETH,YCC-ETH,NEW-USDT,LBA-BTC,ETH-BTC,EDU-BTC,SHE-BTC,EVX-BTC,XZC-USDT,CTXC-USDT,IDT-ETH,LOOM-ETH,KAN-USDT,BAT-USDT,ADX-BTC,KNC-ETH,APPC-BTC,WXT-USDT,VIDY-HT,PC-BTC,CRE-BTC,ATOM-BTC,WAN-BTC,SNC-ETH,XMX-BTC,AKRO-HT,DAC-ETH,SNT-USDT,SWFTC-ETH,RCN-BTC,MXC-BTC,OGO-HT,AAC-BTC,THETA-ETH,IOST-USDT,CNNS-HT,CKB-HT,SEELE-USDT,ZEC-HUSD,MTX-ETH,GXC-ETH,LXT-USDT,BHD-HT,PVT-USDT,STEEM-BTC,REN-ETH,ALGO-ETH,QASH-ETH,MCO-BTC,HPT-BTC,DOCK-USDT,CVNT-BTC,NCASH-BTC,ETH-HUSD,ZIL-BTC,GRS-ETH,XEM-USDT,OST-BTC,OCN-ETH,FOR-BTC,XRP-USDT,QTUM-BTC,SKM-HT,WAVES-ETH,MUSK-BTC,BCX-BTC,OGN-BTC,ITC-ETH,KNC-BTC,VSYS-HT,OMG-USDT,FSN-USDT,RSR-BTC,LOL-HT,TOPC-ETH,ARPA-USDT,SC-ETH,FTT-BTC,TRIO-BTC,ZEN-ETH,FSN-BTC,WAXP-BTC,BTT-ETH,ARPA-HT,RDN-BTC,WICC-ETH,EM-USDT,FTT-USDT,FOR-USDT,XRP-BTC,BAT-BTC,LXT-ETH,ONT-BTC,XVG-ETH,BTS-USDT,LTC-USDT,DBC-BTC,XTZ-USDT,KCASH-HT,EGT-USDT,LXT-BTC,COVA-ETH,SRN-ETH,REQ-ETH,BAT-ETH,OGN-USDT,ADA-HUSD,ZIL-USDT,CMT-BTC,IOST-HT,DCR-USDT,MDS-ETH,CNN-ETH,HT-USDT,ETC-BTC,MTX-BTC,PROPY-ETH,SEELE-BTC,ZJLT-ETH,STEEM-USDT,NANO-USDT,BHD-USDT,PAI-ETH,GT-USDT,ZRX-ETH,GT-BTC,TT-BTC,DASH-HUSD,SALT-BTC,QTUM-ETH,LINK-USDT,WAVES-USDT,LINK-BTC,MX-HT,XLM-USDT,DAT-BTC,BTT-BTC,MTL-BTC,OCN-BTC,ONT-ETH,EOS-HT,DASH-HT,ZRX-BTC,AIDOC-BTC,FTI-BTC,MDS-USDT,WICC-BTC,LYM-ETH,GXC-USDT,CVCOIN-BTC,XTZ-BTC,SC-BTC,EGT-BTC,ARDR-BTC,HIVE-USDT,BCH-BTC,MDS-BTC,CVC-ETH,MTN-BTC,UIP-ETH,ZEN-BTC,BTS-BTC,BIFI-BTC,CRO-BTC,ZJLT-BTC,LSK-ETH,EM-BTC,CMT-ETH,AIDOC-ETH,PAI-BTC,BTS-ETH,PNT-BTC,LSK-BTC,RSR-USDT,CNN-BTC,EOS-HUSD,MEX-BTC,WPR-BTC,BTT-USDT,UIP-BTC,CVC-BTC,AE-ETH,SSP-BTC,TOP-BTC,REQ-BTC,TUSD-HUSD,FTI-ETH,BHT-BTC,HOT-ETH,DGB-BTC,BSV-BTC,SSP-ETH,MT-BTC,ATP-BTC,UIP-USDT,SOC-ETH,ZLA-BTC,UC-ETH,NKN-USDT,WTC-ETH,AE-USDT,EKT-BTC,NULS-USDT,IDT-BTC,NODE-HT,EDU-ETH,BLZ-BTC,NODE-USDT,SNC-BTC,TRX-USDT,EGCC-ETH,PAI-USDT,ETN-BTC,HC-ETH,NAS-USDT,BCH-HT,TOS-ETH,STK-BTC,POWR-BTC,SMT-USDT,DATX-ETH,CRO-USDT,IOTA-USDT,LAMB-USDT,LAMB-HT,BTC-USDT,ARDR-ETH,QASH-BTC,MX-USDT,ATOM-USDT,MCO-ETH,XTZ-ETH,CVC-USDT,NKN-HT,REN-BTC,RCCC-ETH,YEE-BTC,AAC-USDT,CVNT-ETH,ZRX-USDT,BFT-BTC,HB10-USDT,ELF-USDT,DAT-ETH,ELF-ETH,HC-USDT,HPT-USDT,PVT-BTC,LBA-ETH,UTK-ETH,NANO-ETH,KMD-BTC,WXT-BTC,NEO-BTC,QSP-BTC,SBTC-BTC,DOCK-BTC,NULS-ETH,TOP-USDT,DGD-BTC,LAMB-ETH,LUN-BTC,WAVES-BTC,MUSK-ETH,SOC-USDT,VET-ETH,NEW-BTC,PROPY-BTC,IIC-ETH,GSC-BTC,TRIO-ETH,XLM-BTC,KCASH-BTC,XVG-BTC,IOTA-ETH,CRE-HT,STEEM-ETH,QUN-ETH,BCH-USDT,GXC-BTC,LBA-USDT,ETH-USDT,WAXP-ETH,WICC-USDT,AKRO-BTC,BKBT-ETH,ETC-HUSD,CTXC-BTC,TOPC-BTC,ENG-BTC,SWFTC-BTC,SRN-BTC,SKM-USDT,BHD-BTC,HIVE-BTC,COVA-BTC,CNNS-BTC,CMT-USDT,SMT-ETH,TNB-ETH,APPC-ETH,OGO-BTC,BIX-USDT,PC-ETH,DBC-ETH,ZEC-USDT,NCC-BTC,BKBT-BTC,ICX-ETH,DCR-BTC,RTE-ETH,IRIS-ETH,SHE-ETH,UUU-USDT,BSV-HUSD,GNX-BTC,HPT-HT,WTC-USDT,HIT-ETH,HT-BTC,ONE-BTC,LTC-BTC,CKB-BTC,NEXO-ETH,VIDY-BTC,XLM-ETH,ACT-BTC,EVX-ETH,LTC-HT,ABT-ETH,VET-USDT,QTUM-USDT,FAIR-BTC,UUU-BTC,LTC-HUSD,BTM-USDT,GET-BTC,KCASH-ETH,DGD-ETH,ARPA-BTC,GAS-ETH,GSC-ETH,RSR-HT,LOOM-BTC,VET-BTC,GNX-ETH,YCC-BTC,OMG-BTC,EKO-BTC,VSYS-BTC,MEET-ETH,ZIL-ETH,HT-ETH,BTM-BTC,XMR-USDT,BOX-ETH,SKM-BTC,ADA-USDT,ACT-USDT,XEM-BTC,AAC-ETH,QUN-BTC,DTA-ETH,KAN-BTC,MANA-ETH,FOR-HT,THETA-BTC,BFT-ETH,THETA-USDT,GNT-USDT,GNT-BTC,ONT-USDT,ETC-HT,18C-BTC,POLY-BTC,BIX-BTC,ALGO-BTC,FTT-HT,BTG-BTC,WAN-ETH,ATOM-ETH,XRP-HT,EOS-USDT,CRE-USDT,GRS-BTC,HT-HUSD,XRP-HUSD,STORJ-BTC,MANA-USDT,BTT-TRX,FSN-HT,OGO-USDT,DASH-BTC,LOL-BTC,GAS-BTC,RUFF-USDT,NPXS-ETH,ABT-BTC,BUT-BTC,DOGE-BTC,XMX-ETH,IOST-BTC,XLM-HUSD,EKO-ETH,DAC-BTC,ONE-USDT,EGT-HT,NPXS-BTC,XZC-BTC,SOC-BTC,BTM-ETH,ELA-USDT,BLZ-ETH,IOST-ETH,BUT-ETH,PORTAL-BTC,UGAS-BTC,TRX-ETH,EM-HT,UUU-ETH,DOCK-ETH,SNT-BTC,RUFF-ETH,ELA-BTC,LET-ETH,FAIR-ETH,DGB-ETH,BIX-ETH,ITC-USDT,PAY-ETH,XMR-ETH,OMG-ETH,MX-BTC,CRO-HT,EOS-BTC,ADA-BTC,PAY-BTC,MAN-ETH,HIT-USDT,DASH-USDT,TT-HT,LOL-USDT,ADA-ETH,ALGO-USDT,MANA-BTC,CHAT-ETH,STORJ-USDT,XMR-BTC,DTA-BTC,BCV-BTC,KAN-ETH,TNT-BTC,MT-HT,VSYS-USDT,TOP-HT,PHX-BTC,GTC-BTC,IRIS-USDT,GET-ETH,LET-BTC,NAS-ETH,HC-BTC,RUFF-BTC,OGN-HT,18C-ETH,POLY-ETH,PORTAL-ETH,ELF-BTC,DOGE-USDT,NEO-USDT,UGAS-ETH,GT-HT,USDT-HUSD"
}
}
},
@@ -1691,7 +1793,7 @@
"pairs": {
"spot": {
"enabled": "XBT-USD",
- "available": "LINK-EUR,PAXG-ETH,SC-ETH,XRP-XBT,DAI-EUR,LINK-USD,QTUM-CAD,MLN-ETH,BCH-USD,GNO-USD,LTC-USD,XLM-EUR,XMR-XBT,XMR-EUR,ADA-USD,ATOM-XBT,NANO-XBT,OMG-EUR,ETH-EUR,ADA-XBT,OMG-ETH,REP-XBT,ETH-JPY,REP-USD,XTZ-XBT,BAT-ETH,DAI-USDT,GNO-EUR,NANO-EUR,XTZ-ETH,XDG-XBT,BAT-EUR,OMG-USD,XRP-JPY,XRP-USD,DASH-EUR,PAXG-EUR,QTUM-USD,WAVES-XBT,REP-ETH,XLM-USD,ADA-EUR,ATOM-CAD,XBT-USD,EOS-XBT,QTUM-EUR,ETC-ETH,LTC-XBT,XTZ-EUR,ICX-USD,ICX-XBT,PAXG-XBT,ETC-EUR,XLM-XBT,BAT-USD,BCH-EUR,EOS-EUR,NANO-ETH,GNO-ETH,SC-USD,ADA-ETH,ATOM-ETH,GNO-XBT,REP-EUR,ETH-CHF,LINK-XBT,ETH-CAD,DASH-XBT,XBT-EUR,ATOM-USD,ICX-ETH,XBT-CHF,XTZ-USD,XRP-CAD,XRP-EUR,ETC-USD,BAT-XBT,USDT-USD,XBT-CAD,ZEC-EUR,DASH-USD,LSK-XBT,ETH-XBT,XTZ-CAD,XMR-USD,EOS-USD,WAVES-USD,SC-EUR,ETH-GBP,ETH-USD,EOS-ETH,WAVES-EUR,ATOM-EUR,LSK-ETH,ICX-EUR,LINK-ETH,LSK-USD,NANO-USD,XBT-GBP,ZEC-XBT,ADA-CAD,ETH-DAI,DAI-USD,PAXG-USD,LTC-EUR,ZEC-JPY,ZEC-USD,LSK-EUR,QTUM-ETH,ETC-XBT,XBT-JPY,BCH-XBT,OMG-XBT,QTUM-XBT,SC-XBT,WAVES-ETH,MLN-XBT"
+ "available": "BAT-XBT,EOS-EUR,ETH-DAI,ETH-USDC,NANO-EUR,ETH-JPY,REP-EUR,QTUM-EUR,AUD-USD,BCH-ETH,BCH-EUR,ICX-ETH,LINK-ETH,LTC-GBP,PAXG-ETH,QTUM-USD,QTUM-XBT,USDT-CAD,MLN-XBT,XRP-USD,ATOM-USD,PAXG-XBT,ETC-XBT,ZEC-XBT,LSK-USD,USDT-AUD,XBT-CAD,ZEC-EUR,ZEC-USD,ADA-EUR,PAXG-USD,XBT-USD,ALGO-USD,BCH-AUD,DAI-USDT,QTUM-ETH,DASH-XBT,ICX-EUR,WAVES-EUR,WAVES-XBT,XRP-XBT,ADA-XBT,DAI-EUR,LINK-EUR,XDG-USD,MLN-ETH,EUR-GBP,GNO-XBT,OMG-USD,OMG-XBT,OXT-EUR,XBT-JPY,USD-CAD,DASH-USD,EOS-ETH,EUR-AUD,TRX-ETH,TRX-USD,XTZ-ETH,EUR-USD,XTZ-XBT,ATOM-EUR,PAXG-EUR,USDC-USD,USDT-USD,XDG-EUR,MLN-USD,XMR-EUR,ALGO-EUR,ICX-XBT,OMG-EUR,SC-USD,USDT-CHF,XTZ-EUR,XTZ-USD,ICX-USD,OXT-USD,USDT-GBP,ETH-CAD,LTC-XBT,REP-USD,XLM-USD,USD-CHF,REP-ETH,XRP-EUR,GNO-EUR,GNO-USD,LSK-XBT,OMG-ETH,XRP-ETH,BCH-USD,DASH-EUR,LTC-AUD,XRP-USDT,BCH-USDT,TRX-EUR,USDT-EUR,ETC-EUR,BAT-USD,EOS-USD,NANO-USD,SC-ETH,ETC-USD,DAI-USD,USD-JPY,LTC-USDT,ETC-ETH,LTC-USD,XRP-GBP,XBT-EUR,MLN-EUR,ETH-USDT,LINK-USD,LINK-XBT,SC-XBT,USDC-EUR,USDC-USDT,ETH-XBT,REP-XBT,XBT-GBP,XLM-EUR,EOS-XBT,ETH-AUD,GNO-ETH,NANO-XBT,WAVES-ETH,WAVES-USD,ATOM-ETH,AUD-JPY,XBT-CHF,ETH-GBP,ATOM-XBT,BAT-EUR,OXT-ETH,SC-EUR,XRP-AUD,XDG-XBT,XLM-XBT,EUR-CAD,OXT-XBT,ADA-ETH,ALGO-XBT,NANO-ETH,TRX-XBT,ETH-EUR,GBP-USD,EUR-CHF,LSK-EUR,LTC-ETH,USDT-JPY,XBT-DAI,XBT-USDT,XMR-USD,ADA-USD,BAT-ETH,BCH-GBP,XBT-USDC,ETH-USD,BCH-XBT,ETH-CHF,EUR-JPY,LSK-ETH,XMR-XBT,XRP-CAD,ALGO-ETH,XBT-AUD,LTC-EUR,XRP-JPY"
}
}
},
@@ -1768,7 +1870,7 @@
"pairs": {
"spot": {
"enabled": "BTCUSD,BTCAUD",
- "available": "BTCNZD,BTCUSD,BTCAUD,USDNGN,BCHBTC,BTCJPY,BTCCHF,BTCSGD,BTCCAD,GBPUSD,XRPBTC,USDCAD,AUDUSD,USDCHF,BACETH,USDJPY,NZDUSD,BTCEUR,USDSGD,USDHKD,ETHBTC,LTCBTC,EURUSD,BTCNGN,BTCGBP"
+ "available": "USDSGD,ETHBTC,LTCBTC,BTCNZD,BTCSGD,AUDUSD,BTCJPY,BTCEUR,BTCCAD,BTCNGN,USDCAD,USDCHF,BTCAUD,BTCUSD,USDHKD,USDNGN,EURUSD,BACETH,BTCHKD,GBPUSD,BTCCHF,USDJPY,BTCGBP,XRPBTC,NZDUSD,BCHBTC"
}
}
},
@@ -1846,7 +1948,7 @@
"pairs": {
"spot": {
"enabled": "btc_usdt",
- "available": "FBC_USDT,GALT_USDT,IOEX_USDT,OATH_USDT,BLOC_USDT,BTC_USDT,ETH_USDT,ETH_BTC,ABBC_BTC,KISC_ETH,BXA_USDT,ATP_USDT,MAT_USDT,SKY_BTC,RNT_USDT,VENA_USDT,GRIN_USDT,IDA_USDT,PNT_USDT,OPX_USDT,VTHO_BTC,AMO_ETH,UBEX_BTC,EOS_BTC,UBEX_USDT,TNS_BTC,SAIT_ETH,DAX_BTC,DAX_ETH,DALI_USDT,VET_USDT,BCH_BTC,BCH_USDT,NEO_USDT,QTUM_USDT,ZEC_USDT,VET_BTC,PAI_BTC,PNT_BTC,NEO_BTC,DASH_BTC,LTC_BTC,ETC_BTC,QTUM_BTC,ZEC_BTC,SC_BTC,BTS_BTC,CPX_BTC,XWC_BTC,FIL6_BTC,FIL12_BTC,FIL36_BTC,EOS_USDT,UT_ETH,ELA_ETH,VET_ETH,VTHO_ETH,PAI_ETH,HER_ETH,PTT_ETH,TAC_ETH,IDHUB_ETH,SSC_ETH,SKM_ETH,PLY_ETH,EXT_ETH,EOS_ETH,YOYOW_ETH,TRX_ETH,QTUM_ETH,ZEC_ETH,BTS_ETH,BTM_ETH,MITH_ETH,NAS_ETH,MAN_ETH,DBC_ETH,BTO_ETH,DDD_ETH,CPX_ETH,CS_ETH,IHT_ETH,OCN_ETH,EKO_ETH,XWC_ETH,PUT_ETH,PNT_ETH,AAC_ETH,FIL6_ETH,FIL12_ETH,FIL36_ETH,SEER_ETH,BSB_ETH,CDC_ETH,GRAMS_ETH,DDMX_ETH,EAI_ETH,BNB_USDT,HT_USDT,KBC_BTC,KBC_USDT,MAI_USDT,PHV_USDT,GT_USDT,VOKEN_USDT,CYE_USDT,BRC_USDT,BTC_AUSD,DDMX_USDT,SEAL_USDT,SEOS_BTC,BTY_USDT,FO_USDT,DLX_USDT,BFC_USDT,LBK_USDT,SERO_USDT,MTV_USDT,CKB_USDT,ARPA_USDT,ZIP_USDT,AT_USDT,DOT_USDT,DILI_USDT,DUO_USDT,TEP_USDT,BIKI_USDT,MX_USDT,DNS_USDT,OKB_USDT,FLDT_USDT,CCTC_USDT,WIN_USDT,BTT_USDT,TRX_USDT,GRS_BTC,GST_USDT,GST_ETH,ABBC_USDT,UTK_USDT,GKI_USDT,BPX_USDT,SUTER_USDT,LT_USDT,LM_USDT,HTDF_USDT"
+ "available": "BTC_USDT,ETH_USDT,QTUM_USDT,NEO_USDT,ZEC_USDT,BCH_USDT,BCH_BTC,ETH_BTC,LTC_BTC,DASH_BTC,QTUM_BTC,NEO_BTC,ETC_BTC,ZEC_BTC,BTS_BTC,SC_BTC,FIL6_BTC,FIL12_BTC,FIL36_BTC,EOS_ETH,QTUM_ETH,ZEC_ETH,FIL6_ETH,FIL12_ETH,FIL36_ETH,NAS_ETH,DBC_ETH,SEER_ETH,AAC_ETH,DDD_ETH,TRX_ETH,GRAMS_ETH,MITH_ETH,EAI_ETH,PNT_ETH,DAX_ETH,DAX_BTC,MAN_ETH,EXT_ETH,SKM_ETH,SSC_ETH,IDHUB_ETH,PTT_ETH,BNB_USDT,HT_USDT,PAI_ETH,PAI_BTC,VET_ETH,VET_BTC,VET_USDT,VTHO_ETH,ELA_ETH,DALI_USDT,UT_ETH,SAIT_ETH,TNS_BTC,AMO_ETH,VTHO_BTC,IDA_USDT,VENA_USDT,GRIN_USDT,RNT_USDT,SKY_BTC,ATP_USDT,KISC_ETH,BXA_USDT,ABBC_BTC,BLOC_USDT,OATH_USDT,IOEX_USDT,KBC_USDT,KBC_BTC,MAI_USDT,EOS_BTC,EOS_USDT,GALT_USDT,PHV_USDT,FBC_USDT,GT_USDT,CYE_USDT,BRC_USDT,BTC_AUSD,DDMX_USDT,SEAL_USDT,SEOS_BTC,BTY_USDT,FO_USDT,DLX_USDT,BFC_USDT,LBK_USDT,SERO_USDT,MTV_USDT,CKB_USDT,ARPA_USDT,ZIP_USDT,AT_USDT,DOT_USDT,DILI_USDT,DUO_USDT,TEP_USDT,BIKI_USDT,MX_USDT,DNS_USDT,OKB_USDT,FLDT_USDT,CCTC_USDT,WIN_USDT,BTT_USDT,TRX_USDT,GRS_BTC,GST_USDT,ABBC_USDT,UTK_USDT,KMT_USDT,SUTER_USDT,LT_USDT,LM_USDT,HTDF_USDT,FOG_USDT,SLA_USDT,ATOM_USDT,DCN_ETH,DOGE_USDT,XRP_USDT,CNEX_BTC,ETC_USDT,ETC_ETH,ATOM_BTC,XRP_BTC,XRP_ETH,BTC3L_USDT,BTC3S_USDT,ETH3L_USDT,ETH3S_USDT,GL_USDT,GE_USDT,QNUT_USDT,BKS_USDT,HEX_USDT,JUS_USDT,GLEEC_BTC,FOIN_BTC,BBC_USDT,Z1OSS_VUSD,Z1OSL_VUSD,XWC2_USDT,ELAMA_USDT,PLF_BTC,PLF_ETH,LBKL_USDT,TFD_USDT,ELAMA_BTC,BV_USDT,AIN_USDT,ABTC_BTC,ABTC_USDT"
}
}
},
@@ -1922,7 +2024,7 @@
"pairs": {
"spot": {
"enabled": "BTCAUD,BTCUSD",
- "available": "BTCPKR,BTCUGX,BTCXOF,BTCAWG,BTCUYU,BTCPYG,BTCVND,BTCEUR,BTCNOK,BTCRUB,BTCILS,BTCCOP,BTCCRC,BTCMYR,BTCSEK,BTCBRL,BTCKWD,BTCTRY,BTCQAR,BTCLTC,BTCTTD,BTCCLP,BTCKZT,BTCGHS,BTCSGD,BTCKRW,BTCNZD,BTCPLN,BTCTWD,BTCAED,BTCGBP,BTCLKR,BTCJPY,BTCZMW,BTCPHP,BTCPAB,BTCXRP,BTCBYN,BTCARS,BTCRWF,BTCZAR,BTCCZK,BTCJOD,BTCRON,BTCCAD,BTCIRR,BTCIDR,BTCMWK,BTCAUD,BTCTHB,BTCCNY,BTCUAH,BTCDKK,BTCEGP,BTCBAM,BTCSAR,BTCGEL,BTCMDL,BTCMAD,BTCKES,BTCBOB,BTCDOP,BTCRSD,BTCCHF,BTCBDT,BTCHRK,BTCCDF,BTCUSD,BTCETB,BTCTZS,BTCGTQ,BTCAOA,BTCETH,BTCVES,BTCINR,BTCNGN,BTCHKD,BTCXAF,BTCPEN,BTCMXN"
+ "available": "BTCBOB,BTCCZK,BTCUAH,BTCKZT,BTCPAB,BTCPLN,BTCJMD,BTCSZL,BTCILS,BTCMAD,BTCCOP,BTCHRK,BTCEUR,BTCUYU,BTCRSD,BTCPEN,BTCJOD,BTCCHF,BTCKWD,BTCAOA,BTCRUB,BTCVND,BTCRON,BTCHNL,BTCLTC,BTCCLP,BTCNOK,BTCINR,BTCQAR,BTCPKR,BTCTZS,BTCUGX,BTCZMW,BTCXAF,BTCBDT,BTCUSD,BTCDKK,BTCPYG,BTCTHB,BTCSEK,BTCSGD,BTCGEL,BTCBYN,BTCTWD,BTCAED,BTCCAD,BTCAUD,BTCKRW,BTCBRL,BTCCRC,BTCETH,BTCHKD,BTCBWP,BTCXRP,BTCZAR,BTCVES,BTCAMD,BTCSAR,BTCPHP,BTCMVR,BTCGTQ,BTCLKR,BTCCDF,BTCARS,BTCEGP,BTCOMR,BTCNZD,BTCNGN,BTCGBP,BTCMWK,BTCXOF,BTCGHS,BTCDOP,BTCKES,BTCIDR,BTCJPY,BTCNAD,BTCMYR,BTCMUR,BTCCNY,BTCRWF,BTCTRY,BTCMXN"
}
}
},
@@ -2001,7 +2103,7 @@
"pairs": {
"spot": {
"enabled": "BTC-USD",
- "available": "BTC-USD,LTC-USD,ETH-USD,ETC-USD,TUSD-USD,BCH-USD,EOS-USD,XRP-USD,TRX-USD,BSV-USD,USDT-USD,USDK-USD,XLM-USD,ADA-USD,BAT-USD,DCR-USD,EURS-USD,HBAR-USD,PAX-USD,USDC-USD,ZEC-USD,BTC-USDT,BTC-SGD,ETH-SGD,BTC-EUR,BTC-EURS,ETH-EUR,BCH-EUR,EURS-EUR"
+ "available": "BTC-USD,LTC-USD,ETH-USD,ETC-USD,TUSD-USD,CELO-USD,BCH-USD,EOS-USD,XRP-USD,TRX-USD,BSV-USD,USDT-USD,USDK-USD,USDC-USD,DAI-USD,EURS-USD,HBAR-USD,PAX-USD,BTC-USDT,BTC-SGD,ETH-SGD,BTC-EUR,BTC-EURS,ETH-EUR,BCH-EUR,EURS-EUR"
}
}
},
@@ -2074,7 +2176,7 @@
"pairs": {
"futures": {
"enabled": "LTC-USDT_191213",
- "available": "XRP-USD_191213,XRP-USD_191220,XRP-USD_191227,BTC-USD_191213,BTC-USD_191220,BTC-USD_191227,BTC-USDT_191213,BTC-USDT_191220,BTC-USDT_191227,LTC-USD_191213,LTC-USD_191220,LTC-USD_191227,LTC-USDT_191213,LTC-USDT_191220,LTC-USDT_191227,ETH-USD_191213,ETH-USD_191220,ETH-USD_191227,ETH-USDT_191213,ETH-USDT_191220,ETH-USDT_191227,TRX-USDT_191213,TRX-USDT_191220,TRX-USDT_191227,ETC-USD_191213,ETC-USD_191220,ETC-USD_191227,BCH-USD_191213,BCH-USD_191220,BCH-USD_191227,BCH-USDT_191213,BCH-USDT_191220,BCH-USDT_191227,BSV-USD_191213,BSV-USD_191220,BSV-USD_191227,BSV-USDT_191213,BSV-USDT_191220,BSV-USDT_191227,EOS-USDT_191213,EOS-USDT_191220,EOS-USDT_191227,XRP-USDT_191213,XRP-USDT_191220,XRP-USDT_191227,ETC-USDT_191213,ETC-USDT_191220,ETC-USDT_191227,EOS-USD_191213,EOS-USD_191220,EOS-USD_191227,TRX-USD_191213,TRX-USD_191220,TRX-USD_191227",
+ "available": "XRP-USD_200703,XRP-USD_200710,XRP-USD_200925,XRP-USD_201225,BTC-USD_200703,BTC-USD_200710,BTC-USD_200925,BTC-USD_201225,BTC-USDT_200703,BTC-USDT_200710,BTC-USDT_200925,BTC-USDT_201225,LTC-USD_200703,LTC-USD_200710,LTC-USD_200925,LTC-USD_201225,LTC-USDT_200703,LTC-USDT_200710,LTC-USDT_200925,LTC-USDT_201225,ETH-USD_200703,ETH-USD_200710,ETH-USD_200925,ETH-USD_201225,ETH-USDT_200703,ETH-USDT_200710,ETH-USDT_200925,ETH-USDT_201225,TRX-USDT_200703,TRX-USDT_200710,TRX-USDT_200925,TRX-USDT_201225,ETC-USD_200703,ETC-USD_200710,ETC-USD_200925,ETC-USD_201225,BCH-USD_200703,BCH-USD_200710,BCH-USD_200925,BCH-USD_201225,BCH-USDT_200703,BCH-USDT_200710,BCH-USDT_200925,BCH-USDT_201225,BSV-USD_200703,BSV-USD_200710,BSV-USD_200925,BSV-USD_201225,BSV-USDT_200703,BSV-USDT_200710,BSV-USDT_200925,BSV-USDT_201225,EOS-USDT_200703,EOS-USDT_200710,EOS-USDT_200925,EOS-USDT_201225,XRP-USDT_200703,XRP-USDT_200710,XRP-USDT_200925,XRP-USDT_201225,ETC-USDT_200703,ETC-USDT_200710,ETC-USDT_200925,ETC-USDT_201225,EOS-USD_200703,EOS-USD_200710,EOS-USD_200925,EOS-USD_201225,TRX-USD_200703,TRX-USD_200710,TRX-USD_200925,TRX-USD_201225",
"requestFormat": {
"uppercase": true,
"delimiter": "-"
@@ -2097,7 +2199,7 @@
},
"perpetualswap": {
"enabled": "EOS-USD_SWAP",
- "available": "BTC-USD_SWAP,LTC-USD_SWAP,ETH-USD_SWAP,TRX-USD_SWAP,BCH-USD_SWAP,BSV-USD_SWAP,EOS-USD_SWAP,XRP-USD_SWAP,ETC-USD_SWAP",
+ "available": "NEO-USDT_SWAP,LINK-USDT_SWAP,DASH-USDT_SWAP,ADA-USDT_SWAP,ZEC-USDT_SWAP,XTZ-USDT_SWAP,ONT-USDT_SWAP,ATOM-USDT_SWAP,QTUM-USDT_SWAP,XLM-USDT_SWAP,XMR-USDT_SWAP,IOTA-USDT_SWAP,ALGO-USDT_SWAP,IOST-USDT_SWAP,THETA-USDT_SWAP,KNC-USDT_SWAP,NEO-USD_SWAP,LINK-USD_SWAP,DASH-USD_SWAP,ADA-USD_SWAP,ZEC-USD_SWAP,XTZ-USD_SWAP,ONT-USD_SWAP,ATOM-USD_SWAP,QTUM-USD_SWAP,XLM-USD_SWAP,XMR-USD_SWAP,IOTA-USD_SWAP,ALGO-USD_SWAP,IOST-USD_SWAP,THETA-USD_SWAP,KNC-USD_SWAP,BTC-USDT_SWAP,LTC-USDT_SWAP,ETH-USDT_SWAP,TRX-USDT_SWAP,BCH-USDT_SWAP,BSV-USDT_SWAP,EOS-USDT_SWAP,XRP-USDT_SWAP,ETC-USDT_SWAP,BTC-USD_SWAP,LTC-USD_SWAP,ETH-USD_SWAP,TRX-USD_SWAP,BCH-USD_SWAP,BSV-USD_SWAP,EOS-USD_SWAP,XRP-USD_SWAP,ETC-USD_SWAP",
"requestFormat": {
"uppercase": true,
"delimiter": "-"
@@ -2109,7 +2211,7 @@
},
"spot": {
"enabled": "EOS-USDT",
- "available": "XPO-USDT,SPND-USDK,SPND-BTC,ROAD-USDK,BCH-BTC,BSV-BTC,DASH-BTC,ADA-BTC,ABL-BTC,AE-BTC,ALGO-BTC,ARDR-BTC,ATOM-BTC,BLOC-BTC,BTT-BTC,CAI-BTC,CRO-BTC,CTXC-BTC,CVT-BTC,DCR-BTC,EGT-BTC,GUSD-BTC,HBAR-BTC,HPB-BTC,HYC-BTC,KAN-BTC,LBA-BTC,LEO-BTC,LET-BTC,LSK-BTC,NXT-BTC,ORS-BTC,PAX-BTC,PMA-BTC,SC-BTC,TUSD-BTC,USDC-BTC,VITE-BTC,VSYS-BTC,WAVES-BTC,WIN-BTC,WXT-BTC,XAS-BTC,XTZ-BTC,YOU-BTC,ZIL-BTC,XRP-BTC,ELF-BTC,LRC-BTC,MCO-BTC,NULS-BTC,BCX-BTC,CMT-BTC,EDO-BTC,ITC-BTC,SBTC-BTC,ZEC-BTC,NEO-BTC,GAS-BTC,HC-BTC,QTUM-BTC,IOTA-BTC,XUC-BTC,EOS-BTC,SNT-BTC,OMG-BTC,LTC-BTC,ETH-BTC,ETC-BTC,BCD-BTC,BTG-BTC,ACT-BTC,PAY-BTC,BTM-BTC,DGD-BTC,GNT-BTC,LINK-BTC,WTC-BTC,ZRX-BTC,BNT-BTC,CVC-BTC,MANA-BTC,KNC-BTC,GNX-BTC,ICX-BTC,XEM-BTC,ARK-BTC,YOYO-BTC,FUN-BTC,ACE-BTC,TRX-BTC,DGB-BTC,SWFTC-BTC,XMR-BTC,XLM-BTC,KCASH-BTC,MDT-BTC,NAS-BTC,UGC-BTC,DPY-BTC,SSC-BTC,AAC-BTC,VIB-BTC,QUN-BTC,INT-BTC,IOST-BTC,INS-BTC,MOF-BTC,TCT-BTC,STC-BTC,THETA-BTC,PST-BTC,SNC-BTC,MKR-BTC,LIGHT-BTC,OF-BTC,TRUE-BTC,SOC-BTC,ZEN-BTC,HMC-BTC,ZIP-BTC,NANO-BTC,CIC-BTC,GTO-BTC,CHAT-BTC,INSUR-BTC,R-BTC,BEC-BTC,MITH-BTC,ABT-BTC,BKX-BTC,RFR-BTC,TRIO-BTC,EDGE-BTC,ONT-BTC,OKB-BTC,ADA-ETH,ABL-ETH,AE-ETH,ALGO-ETH,ATOM-ETH,BTT-ETH,CAI-ETH,CTXC-ETH,DCR-ETH,EGT-ETH,HPB-ETH,HYC-ETH,KAN-ETH,LEO-ETH,MVP-ETH,ORS-ETH,SC-ETH,SDA-ETH,WAVES-ETH,WIN-ETH,YOU-ETH,ZIL-ETH,ELF-ETH,LTC-ETH,CMT-ETH,PRA-ETH,LRC-ETH,MCO-ETH,NULS-ETH,DGD-ETH,STORJ-ETH,BTM-ETH,EOS-ETH,OMG-ETH,DASH-ETH,XRP-ETH,ZEC-ETH,NEO-ETH,GAS-ETH,HC-ETH,QTUM-ETH,IOTA-ETH,ETC-ETH,LINK-ETH,WTC-ETH,ZRX-ETH,CVC-ETH,MANA-ETH,GNX-ETH,XEM-ETH,TRX-ETH,SWFTC-ETH,XMR-ETH,XLM-ETH,KCASH-ETH,MDT-ETH,NAS-ETH,SSC-ETH,AAC-ETH,FAIR-ETH,RCT-ETH,TOPC-ETH,INT-ETH,IOST-ETH,INS-ETH,MOF-ETH,REF-ETH,MKR-ETH,LIGHT-ETH,OF-ETH,TRUE-ETH,ZEN-ETH,NANO-ETH,CIC-ETH,GTO-ETH,UCT-ETH,MITH-ETH,ABT-ETH,AUTO-ETH,TRIO-ETH,ONT-ETH,OKB-ETH,BTC-USDK,LTC-USDK,ETH-USDK,OKB-USDK,ETC-USDK,BCH-USDT,BCH-USDK,EOS-USDK,XRP-USDK,TRX-USDK,BSV-USDT,BSV-USDK,USDT-USDK,ADA-USDT,AE-USDT,ALGO-USDT,ALGO-USDK,ALV-USDT,ATOM-USDT,BLOC-USDT,BTT-USDT,CAI-USDT,CRO-USDT,CRO-USDK,CTXC-USDT,CVT-USDT,DCR-USDT,DOGE-USDT,DOGE-USDK,EC-USDT,EC-USDK,EGT-USDT,EM-USDT,EM-USDK,ETM-USDT,ETM-USDK,FSN-USDT,FSN-USDK,FTM-USDT,FTM-USDK,GUSD-USDT,HBAR-USDT,HBAR-USDK,HPB-USDT,HYC-USDT,KAN-USDT,LAMB-USDT,LAMB-USDK,LBA-USDT,LEO-USDT,LEO-USDK,LET-USDT,LSK-USDT,MVP-USDT,ORBS-USDT,ORBS-USDK,ORS-USDT,PAX-USDT,PLG-USDT,PLG-USDK,PMA-USDK,ROAD-USDT,SC-USDT,TUSD-USDT,USDC-USDT,VNT-USDT,VNT-USDK,VSYS-USDT,VSYS-USDK,WAVES-USDT,WIN-USDT,WXT-USDT,WXT-USDK,XAS-USDT,XPO-USDK,XTZ-USDT,YOU-USDT,ZIL-USDT,TRX-OKB,AE-OKB,BLOC-OKB,EGT-OKB,SC-OKB,WXT-OKB,ELF-USDT,DASH-USDT,BTG-USDT,LRC-USDT,MCO-USDT,NULS-USDT,DASH-OKB,XRP-USDT,ZEC-USDT,NEO-USDT,GAS-USDT,HC-USDT,QTUM-USDT,IOTA-USDT,BTC-USDT,BCD-USDT,XUC-USDT,CMT-USDT,EDO-USDT,ITC-USDT,PRA-USDT,ETH-USDT,LTC-USDT,ETC-USDT,EOS-USDT,OMG-USDT,ACT-USDT,BTM-USDT,DGD-USDT,GNT-USDT,PAY-USDT,STORJ-USDT,SNT-USDT,LINK-USDT,WTC-USDT,ZRX-USDT,BNT-USDT,CVC-USDT,MANA-USDT,KNC-USDT,ICX-USDT,XEM-USDT,ARK-USDT,YOYO-USDT,AST-USDT,TRX-USDT,MDA-USDT,DGB-USDT,PPT-USDT,SWFTC-USDT,XMR-USDT,XLM-USDT,KCASH-USDT,MDT-USDT,NAS-USDT,RNT-USDT,UGC-USDT,DPY-USDT,SSC-USDT,AAC-USDT,FAIR-USDT,UBTC-USDT,SHOW-USDT,VIB-USDT,MOT-USDT,UTK-USDT,TOPC-USDT,QUN-USDT,INT-USDT,IPC-USDT,IOST-USDT,INS-USDT,YEE-USDT,MOF-USDT,TCT-USDT,STC-USDT,THETA-USDT,PST-USDT,MKR-USDT,LIGHT-USDT,OF-USDT,TRUE-USDT,SOC-USDT,ZEN-USDT,HMC-USDT,ZIP-USDT,NANO-USDT,CIC-USDT,GTO-USDT,CHAT-USDT,INSUR-USDT,R-USDT,BEC-USDT,MITH-USDT,ABT-USDT,BKX-USDT,RFR-USDT,TRIO-USDT,EDGE-USDT,ONT-USDT,OKB-USDT,NEO-OKB,LTC-OKB,ETC-OKB,XRP-OKB,ZEC-OKB,IOTA-OKB,EOS-OKB",
+ "available": "XPO-USDT,SPND-BTC,ROAD-USDK,RVN-USDT,HDAO-USDK,BAT-USDT,OXT-USDT,OXT-BTC,BCH-BTC,BSV-BTC,USDC-BTC,DASH-BTC,ADA-BTC,AE-BTC,ALGO-BTC,APM-BTC,ARDR-BTC,ATOM-BTC,BAT-BTC,BHP-BTC,BTT-BTC,COMP-BTC,CRO-BTC,CTC-BTC,CTXC-BTC,CVT-BTC,DCR-BTC,DNA-BTC,EGT-BTC,GUSD-BTC,HBAR-BTC,HYC-BTC,IQ-BTC,KAN-BTC,LBA-BTC,LEO-BTC,LET-BTC,LSK-BTC,ORS-BTC,PAX-BTC,PMA-BTC,RVN-BTC,SC-BTC,TMTG-BTC,TUSD-BTC,VITE-BTC,VSYS-BTC,WAVES-BTC,WXT-BTC,XTZ-BTC,YOU-BTC,ZIL-BTC,XRP-BTC,ELF-BTC,LRC-BTC,MCO-BTC,NULS-BTC,BCX-BTC,CMT-BTC,ITC-BTC,SBTC-BTC,ZEC-BTC,NEO-BTC,GAS-BTC,HC-BTC,QTUM-BTC,IOTA-BTC,EOS-BTC,SNT-BTC,OMG-BTC,LTC-BTC,ETH-BTC,ETC-BTC,BCD-BTC,BTG-BTC,ACT-BTC,PAY-BTC,BTM-BTC,GNT-BTC,LINK-BTC,WTC-BTC,ZRX-BTC,BNT-BTC,CVC-BTC,MANA-BTC,KNC-BTC,GNX-BTC,ICX-BTC,XEM-BTC,ARK-BTC,YOYO-BTC,FUN-BTC,TRX-BTC,DGB-BTC,SWFTC-BTC,XMR-BTC,XLM-BTC,KCASH-BTC,MDT-BTC,NAS-BTC,AAC-BTC,VIB-BTC,QUN-BTC,INT-BTC,IOST-BTC,MOF-BTC,TCT-BTC,THETA-BTC,PST-BTC,SNC-BTC,MKR-BTC,TRUE-BTC,SOC-BTC,ZEN-BTC,NANO-BTC,GTO-BTC,CHAT-BTC,MITH-BTC,ABT-BTC,TRIO-BTC,ONT-BTC,OKB-BTC,ADA-ETH,AE-ETH,ALGO-ETH,ATOM-ETH,BTT-ETH,CTXC-ETH,EGT-ETH,HYC-ETH,KAN-ETH,LEO-ETH,ORS-ETH,SC-ETH,WAVES-ETH,YOU-ETH,ZIL-ETH,ELF-ETH,LTC-ETH,CMT-ETH,LRC-ETH,MCO-ETH,NULS-ETH,STORJ-ETH,BTM-ETH,EOS-ETH,OMG-ETH,DASH-ETH,XRP-ETH,ZEC-ETH,NEO-ETH,GAS-ETH,HC-ETH,QTUM-ETH,IOTA-ETH,ETC-ETH,LINK-ETH,WTC-ETH,ZRX-ETH,CVC-ETH,MANA-ETH,GNX-ETH,XEM-ETH,TRX-ETH,SWFTC-ETH,XMR-ETH,XLM-ETH,KCASH-ETH,MDT-ETH,NAS-ETH,AAC-ETH,FAIR-ETH,TOPC-ETH,INT-ETH,IOST-ETH,MOF-ETH,MKR-ETH,TRUE-ETH,ZEN-ETH,NANO-ETH,GTO-ETH,MITH-ETH,ABT-ETH,TRIO-ETH,ONT-ETH,OKB-ETH,BTC-USDK,LTC-USDK,ETH-USDK,OKB-USDK,ETC-USDK,BCH-USDK,EOS-USDK,XRP-USDK,TRX-USDK,BSV-USDK,USDT-USDK,ALGO-USDK,CRO-USDK,DEP-USDK,DOGE-USDK,EC-USDK,EM-USDK,FSN-USDK,FTM-USDK,HBAR-USDK,LAMB-USDK,LEO-USDK,NDN-USDK,ORBS-USDK,PLG-USDK,PMA-USDK,VSYS-USDK,WGRT-USDK,WXT-USDK,BCH-USDT,BSV-USDT,USDC-USDT,ADA-USDT,AE-USDT,ALGO-USDT,ALV-USDT,APM-USDT,ATOM-USDT,BHP-USDT,BLOC-USDT,BTT-USDT,COMP-USDT,CRO-USDT,CTC-USDT,CTXC-USDT,CVT-USDT,DAI-USDT,DCR-USDT,DEP-USDT,DNA-USDT,DOGE-USDT,EC-USDT,EGT-USDT,EM-USDT,ETM-USDT,FSN-USDT,FTM-USDT,GUSD-USDT,HBAR-USDT,HDAO-USDT,HYC-USDT,IQ-USDT,KAN-USDT,LAMB-USDT,LBA-USDT,LEO-USDT,LET-USDT,LSK-USDT,NDN-USDT,ORBS-USDT,ORS-USDT,PAX-USDT,PLG-USDT,ROAD-USDT,SC-USDT,TMTG-USDT,TUSD-USDT,VSYS-USDT,WAVES-USDT,WGRT-USDT,WXT-USDT,XTZ-USDT,YOU-USDT,ZIL-USDT,TRX-OKB,EGT-OKB,SC-OKB,WXT-OKB,BTC-DAI,ETH-DAI,ELF-USDT,DASH-USDT,BTG-USDT,LRC-USDT,MCO-USDT,NULS-USDT,DASH-OKB,XRP-USDT,ZEC-USDT,NEO-USDT,GAS-USDT,HC-USDT,QTUM-USDT,IOTA-USDT,BTC-USDT,BCD-USDT,XUC-USDT,CMT-USDT,ITC-USDT,ETH-USDT,LTC-USDT,ETC-USDT,EOS-USDT,OMG-USDT,ACT-USDT,BTM-USDT,GNT-USDT,PAY-USDT,STORJ-USDT,SNT-USDT,LINK-USDT,WTC-USDT,ZRX-USDT,BNT-USDT,CVC-USDT,MANA-USDT,KNC-USDT,ICX-USDT,XEM-USDT,ARK-USDT,YOYO-USDT,AST-USDT,TRX-USDT,MDA-USDT,DGB-USDT,PPT-USDT,SWFTC-USDT,XMR-USDT,XLM-USDT,KCASH-USDT,MDT-USDT,NAS-USDT,RNT-USDT,AAC-USDT,FAIR-USDT,UBTC-USDT,VIB-USDT,UTK-USDT,TOPC-USDT,QUN-USDT,INT-USDT,IOST-USDT,YEE-USDT,MOF-USDT,TCT-USDT,THETA-USDT,PST-USDT,MKR-USDT,TRUE-USDT,SOC-USDT,ZEN-USDT,ZIP-USDT,NANO-USDT,GTO-USDT,CHAT-USDT,BEC-USDT,MITH-USDT,ABT-USDT,TRIO-USDT,ONT-USDT,OKB-USDT,NEO-OKB,LTC-OKB,ETC-OKB,XRP-OKB,ZEC-OKB,IOTA-OKB,EOS-OKB,BTC-USDC,LTC-USDC,ETH-USDC,OKB-USDC,ETC-USDC,BCH-USDC,EOS-USDC,XRP-USDC,TRX-USDC,BSV-USDC",
"requestFormat": {
"uppercase": true,
"delimiter": "-"
@@ -2196,7 +2298,7 @@
"pairs": {
"spot": {
"enabled": "BTC_LTC,BTC_ETH,BTC_DOGE,BTC_DASH,BTC_XRP",
- "available": "BTC_ATOM,USDT_BCHABC,BTC_STR,USDT_DASH,ETH_BAT,BTC_MANA,USDT_MANA,USDT_ETC,USDT_REP,BTC_STORJ,USDT_BCHSV,USDC_GRIN,BTC_DOGE,BTC_XRP,USDT_ZEC,USDT_DOGE,USDC_ETH,BTC_LSK,ETH_ETC,BTC_ZRX,USDC_ZEC,USDT_XMR,USDT_EOS,BTC_QTUM,USDC_STR,USDC_USDT,BTC_BNT,BTC_BCHSV,BTC_LTC,BTC_ETC,BTC_REP,BTC_ARDR,BTC_KNC,BTC_LPT,BTC_TRX,USDT_ETH,USDT_ATOM,USDC_ETC,USDC_BCHABC,USDC_ATOM,USDT_GRIN,BTC_XPM,USDT_BAT,BTC_LOOM,USDT_QTUM,BTC_BCHABC,BTC_GAS,USDT_GNT,USDC_LTC,BTC_BTS,BTC_DASH,USDT_STR,BTC_ETH,ETH_ZEC,USDC_DASH,BTC_XMR,USDT_BTC,BTC_SC,BTC_ZEC,BTC_MAID,BTC_NXT,ETH_REP,BTC_STRAT,BTC_NMR,USDC_XMR,USDC_EOS,BTC_OMNI,BTC_OMG,ETH_EOS,USDT_ZRX,USDC_BCHSV,BTC_BCN,BTC_EOS,BTC_BAT,USDT_DGB,USDC_BTC,USDC_DOGE,USDT_TRX,BTC_DGB,BTC_FCT,ETH_ZRX,USDT_LSK,USDT_SC,BTC_VIA,USDT_LTC,BTC_GNT,USDC_XRP,BTC_CVC,BTC_SNT,BTC_FOAM,BTC_VTC,BTC_XEM,USDT_NXT,USDT_XRP,BTC_DCR,BTC_POLY,BTC_GRIN,USDC_TRX"
+ "available": "BTC_SC,BTC_GNT,USDC_LTC,PAX_ETH,USDT_BSVBEAR,BUSD_BNB,USDT_WRX,USDT_IBVOL,USDT_ETH,BTC_ETC,USDC_ETH,BTC_POLY,BTC_ATOM,USDT_ATOM,TRX_STEEM,USDT_NEO,USDT_XRPBULL,USDT_AVA,USDT_DASH,USDT_ZRX,USDT_TRXBEAR,USDT_PAX,DAI_ETH,BTC_NEO,TRX_JST,BTC_FXC,USDT_ETC,USDT_ZEC,BTC_STORJ,USDT_GNT,USDT_QTUM,BTC_FOAM,TRX_BTT,USDT_BCHC,BTC_XFIL,USDC_XMR,TRX_WIN,USDT_SNX,BTC_LEND,USDT_BSVBULL,BTC_DOGE,BTC_REP,USDT_REP,BTC_STRAT,USDT_EOS,BTC_BAT,BTC_BCHABC,USDT_BUSD,BTC_XRP,BTC_ARDR,USDT_LINKBULL,BTC_CHR,USDT_CUSDT,TRX_WRX,USDT_XRP,ETH_ZEC,BTC_KNC,USDT_DOGE,BTC_LPT,USDT_BTT,ETH_COMP,USDC_GRIN,USDC_DASH,USDT_GRIN,USDT_XTZ,BTC_GAS,USDT_DAI,TRX_NEO,USDT_LINKBEAR,TRX_CHR,BTC_MDT,USDT_XMR,BTC_DCR,BTC_CVC,USDT_BNB,BTC_NXT,BTC_LSK,BTC_OMG,BTC_QTUM,BTC_BCHSV,BTC_SNX,BTC_MANA,USDC_EOS,DAI_BTC,BTC_SWFTC,TRX_BNB,BTC_STEEM,USDJ_BTC,USDJ_BTT,USDT_LRC,USDT_BAL,BNB_BTC,USDC_DOGE,BTC_TRX,BTC_XTZ,TRX_XTZ,USDT_MATIC,USDT_BCHBEAR,USDT_SWFTC,BTC_LTC,USDC_XRP,BTC_NMR,TRX_MATIC,USDT_XRPBEAR,BTC_ZEC,BTC_SNT,USDT_SC,USDT_BCHSV,TRX_ETH,BUSD_BTC,BTC_DASH,ETH_BAT,BTC_LOOM,TRX_SNX,ETH_ZRX,USDC_BCHABC,USDC_ETC,USDT_BULL,PAX_BTC,USDJ_TRX,BTC_WRX,BTC_BTS,USDT_LTC,BTC_LINK,USDT_USDJ,TRX_SWFTC,USDT_LINK,TRX_AVA,USDC_USDT,USDT_JST,TRX_FXC,USDT_CHR,USDT_XFIL,BTC_ETHBNT,BTC_LRC,USDT_BAT,USDC_ATOM,USDT_WIN,ETH_EOS,USDT_TRX,TRX_MDT,BTC_AVA,BTC_XEM,USDT_BTC,BTC_EOS,USDT_LSK,BTC_MATIC,USDT_FXC,USDT_STEEM,USDC_BCHSV,USDT_TRXBULL,USDT_EOSBULL,USDT_LEND,ETH_ETC,USDT_MANA,USDT_ETHBULL,USDT_REN,USDT_COMP,BTC_XMR,BTC_ETH,BTC_ZRX,USDC_BTC,TRX_XRP,USDT_ETHBEAR,USDT_BVOL,ETH_BAL,USDT_MDT,BTC_BNT,USDC_ZEC,USDT_BCHABC,TRX_LINK,BTC_MKR,USDT_BCN,USDT_EOSBEAR,BTC_REN,USDT_STR,USDC_STR,USDT_BEAR,USDT_BCHBULL,BTC_STR,USDC_TRX,USDT_MKR"
}
}
},
@@ -2354,7 +2456,7 @@
"pairs": {
"spot": {
"enabled": "BTC_USDT,ETH_USDT",
- "available": "QUN_USDT,UBTC_USDT,DDM_USDT,PAX_QC,OMG_QC,DASH_QC,LTC_USDT,NXWC_USDT,HLC_QC,OMG_USDT,1ST_QC,EOSDAC_QC,DASH_BTC,USDT_QC,HLC_USDT,CRO_USDT,BTM_QC,ZB_QC,BTC_QC,TOPC_QC,KAN_QC,SNT_USDT,QTUM_BTC,BRC_BTC,TV_USDT,ETC_BTC,BCHABC_QC,XEM_BTC,AE_BTC,TV_QC,BCHSV_USDT,BTP_USDT,GRAM_BTC,BSV_USDT,SAFE_QC,GRIN_QC,ACC_USDT,EDO_USDT,ADA_USDT,BCHSV_QC,EOS_QC,HSR_BTC,BTN_QC,HOTC_USDT,BTH_QC,KNC_USDT,B91_USDT,DASH_USDT,DOGE_BTC,UBTC_QC,MANA_USDT,XWC_USDT,BSV_QC,XEM_USDT,HOTC_QC,HC_BTC,NEO_QC,XWC_QC,ETH_BTC,SNT_BTC,ENTC_USDT,GRAM_USDT,TV_BTC,BTM_BTC,BCW_QC,TRUE_QC,BTH_USDT,XLM_USDT,ICX_QC,QTUM_USDT,LTC_BTC,HX_QC,LBTC_USDT,HX_USDT,EPC_QC,PDX_USDT,XRP_QC,EOSDAC_USDT,BTN_USDT,SLT_QC,DDM_QC,BITCNY_QC,SLT_BTC,SLT_USDT,ICX_BTC,HC_QC,BCW_USDT,EPC_BTC,SBTC_USDT,MTL_USDT,VSYS_QC,HSR_USDT,TOPC_USDT,TSR_USDT,ZB_USDT,BCX_BTC,MCO_QC,LEO_USDT,RCN_USDT,INK_QC,ETC_QC,HSR_QC,BAT_QC,BRC_QC,BTM_USDT,XMR_USDT,BDS_QC,TRX_BTC,NEO_BTC,SNT_QC,TRUE_USDT,XRP_USDT,GRIN_USDT,MITH_QC,CDC_USDT,HPY_QC,DOGE_QC,XLM_QC,LBTC_QC,KAN_BTC,ZRX_USDT,BTS_USDT,OMG_BTC,BTC_USDT,ZB_BTC,TRX_USDT,LVN_QC,FN_QC,MANA_BTC,QUN_QC,BCX_USDT,SUB_QC,BITE_BTC,BAR_USDT,BAT_BTC,LBTC_BTC,B91_QC,ETH_QC,XTZ_USDT,GNT_BTC,EOS_USDT,BCHABC_USDT,ZRX_BTC,VSYS_BTC,DOGE_USDT,LTC_QC,NEO_USDT,BCD_QC,INK_USDT,BTS_BTC,MCO_USDT,YTNB_USDT,BCX_QC,ADA_QC,VSYS_ZB,CDC_QC,ZRX_QC,HC_USDT,ADA_BTC,BTS_QC,BTP_QC,GRAM_QC,KNC_QC,XRP_BTC,TUSD_USDT,XMR_QC,GNT_QC,NWT_USDT,ETH_USDT,MANA_QC,BAT_USDT,SAFE_USDT,XLM_BTC,1ST_USDT,ICX_USDT,ETZ_QC,CRO_QC,GNT_USDT,TRX_QC,ETC_USDT,XUC_QC,BRC_USDT,PDX_BTC,TRUE_BTC,PDX_QC,HPY_USDT,XEM_QC,CHAT_USDT,BCD_USDT,ETZ_USDT,AAA_QC,LVN_USDT,EOS_BTC,PAX_USDT,AE_QC,QTUM_QC"
+ "available": "NXWC_QC,LBTC_USDT,CDC_QC,EPC_QC,BSV_USDT,AE_QC,EDO_USDT,HLC_QC,KPG_QC,NEO_QC,PAX_QC,HC_BTC,BTN_QC,UFO_USDT,TRUE_BTC,DNA_QC,GUSD_QC,BTP_QC,VBT_QC,OMG_QC,HSR_QC,ACC_USDT,KAN_BTC,LBTC_QC,BITE_BTC,GUCS_USDT,BCHABC_QC,QTUM_USDT,BTS_USDT,ETH_QC,XLM_USDT,QTUM_QC,SNT_USDT,UBTC_QC,ZB_QC,ZB_BTC,XRP_USDT,USDC_USDT,LUCKY_USDT,BTC_USDT,NEO_BTC,PAX_USDT,QUN_QC,LVN_USDT,EOS_BTC,ADA_QC,BAT_QC,ETH_USDT,DASH_BTC,VSYS_BTC,MTL_USDT,TSR_USDT,BCHABC_USDT,EOS_USDT,XMR_QC,XTZ_USDT,BCH_QC,DOGE_USDT,BTS_BTC,GUSD_USDT,LTC_QC,XWC_USDT,NXWC_USDT,TRUE_QC,ZB_USDT,BCHSV_USDT,ADA_BTC,BDS_QC,ETC_USDT,DASH_USDT,KAN_QC,HNS_USDT,CDC_USDT,DDM_QC,LTC_BTC,HC_USDT,TRUE_USDT,GRIN_QC,HOTC_QC,ETZ_USDT,SLT_QC,XWC_QC,BITCNY_QC,BTC_QC,CRO_USDT,CRO_QC,BSV_QC,ADA_USDT,DASH_QC,TRX_BTC,TOPC_USDT,USDT_QC,BCH_USDT,TRX_QC,LTG_USDT,PDX_QC,GST_USDT,GRAM_QC,BTH_QC,AAA_QC,LVN_QC,DOGE_QC,HSR_USDT,DNA_USDT,ETZ_QC,CHAT_USDT,B91_QC,DDM_USDT,DOGE_BTC,BTS_QC,SAFE_QC,TUSD_USDT,UFO_QC,TOPC_QC,XRP_BTC,HC_QC,MCO_QC,BTM_QC,HPY_QC,FN_QC,OMG_USDT,ICX_QC,LEO_USDT,BRC_QC,BCHSV_QC,LTC_USDT,NEO_USDT,ZRX_QC,VSYS_QC,XRP_QC,EOS_QC,XLM_QC,HX_QC,TV_QC,TRX_USDT,QTUM_BTC,MANA_QC,ETH_BTC,XEM_QC,GNT_USDT,USDC_QC,SAFE_USDT,GRAM_USDT,LUCKY_QC,OMG_BTC,HSR_BTC,KNC_QC,ETC_BTC,BTH_USDT,EPC_BTC,XLM_BTC,XMR_USDT,ETC_QC"
}
}
},
diff --git a/currency/code.go b/currency/code.go
index eadae293..634f2100 100644
--- a/currency/code.go
+++ b/currency/code.go
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"strings"
+ "unicode"
"github.com/thrasher-corp/gocryptotrader/common"
)
@@ -268,7 +269,17 @@ func (b *BaseCodes) UpdateContract(fullName, symbol, assocExchange string) error
// Register registers a currency from a string and returns a currency code
func (b *BaseCodes) Register(c string) Code {
- NewUpperCode := strings.ToUpper(c)
+ NewUpperCode := c
+ lower := true
+ for _, r := range c {
+ if !unicode.IsLower(r) {
+ lower = false
+ break
+ }
+ }
+ if lower {
+ NewUpperCode = strings.ToUpper(c)
+ }
format := strings.Contains(c, NewUpperCode)
b.mtx.Lock()
diff --git a/currency/pair.go b/currency/pair.go
index cd699ebb..bf4ff95b 100644
--- a/currency/pair.go
+++ b/currency/pair.go
@@ -10,6 +10,9 @@ import (
// a Pair struct
func NewPairDelimiter(currencyPair, delimiter string) Pair {
result := strings.Split(currencyPair, delimiter)
+ if len(result) > 2 {
+ result[1] = strings.Join(result[1:], delimiter)
+ }
return Pair{
Delimiter: delimiter,
Base: NewCode(result[0]),
diff --git a/currency/pair_test.go b/currency/pair_test.go
index b3856528..f429f27a 100644
--- a/currency/pair_test.go
+++ b/currency/pair_test.go
@@ -337,6 +337,26 @@ func TestNewPairDelimiter(t *testing.T) {
actual, expected,
)
}
+
+ pair = NewPairDelimiter("BTC-MOVE-0626", "-")
+ actual = pair.String()
+ expected = "BTC-MOVE-0626"
+ if actual != expected {
+ t.Errorf(
+ "Pair(): %s was not equal to expected value: %s",
+ actual, expected,
+ )
+ }
+
+ pair = NewPairDelimiter("fBTC-USDT", "-")
+ actual = pair.String()
+ expected = "fBTC-USDT"
+ if actual != expected {
+ t.Errorf(
+ "Pair(): %s was not equal to expected value: %s",
+ actual, expected,
+ )
+ }
}
// TestNewPairFromIndex returns a CurrencyPair via a currency string and
diff --git a/docs/ADD_NEW_EXCHANGE.md b/docs/ADD_NEW_EXCHANGE.md
new file mode 100644
index 00000000..169c44c8
--- /dev/null
+++ b/docs/ADD_NEW_EXCHANGE.md
@@ -0,0 +1,1084 @@
+# GoCryptoTrader ADD NEW EXCHANGE
+
+
+
+[](https://travis-ci.org/thrasher-corp/gocryptotrader)
+[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
+[](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges)
+[](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
+[](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
+
+This exchanges 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)
+
+## How to add a new exchange
+
+This document is from a perspective of adding a new exchange called FTX to the codebase:
+
+### Run the [exchange templating tool](../cmd/exchange_template/) which will create a base exchange package based on the features the exchange supports
+
+#### Linux/OSX
+GoCryptoTrader is built using [Go Modules](https://github.com/golang/go/wiki/Modules) and requires Go 1.11 or above
+Using Go Modules you now clone this repository **outside** your GOPATH
+
+```bash
+git clone https://github.com/thrasher-corp/gocryptotrader.git
+cd gocryptotrader/cmd/documentation/exchange_template
+go run exchange_template.go -name FTX -ws -rest
+```
+
+#### Windows
+
+```bash
+git clone https://github.com/thrasher-corp/gocryptotrader.git
+cd gocryptotrader\cmd\exchange_template
+go run exchange_template.go -name FTX -ws -rest
+```
+
+### Add exchange struct to [config_example.json](../config_example.json), [configtest.json](../testdata/configtest.json):
+
+Find out which asset types are supported by the exchange and add them to the pairs struct (spot is enabled by default)
+
+#### If main config path is unknown the following function can be used:
+```go
+config.GetDefaultFilePath()
+```
+
+```go
+ {
+ "name": "FTX",
+ "enabled": true,
+ "verbose": false,
+ "httpTimeout": 15000000000,
+ "websocketResponseCheckTimeout": 30000000,
+ "websocketResponseMaxLimit": 7000000000,
+ "websocketTrafficTimeout": 30000000000,
+ "websocketOrderbookBufferLimit": 5,
+ "baseCurrencies": "USD",
+ "currencyPairs": {
+ "assetTypes": [
+ "spot",
+ "futures"
+ ],
+ "pairs": {
+ "futures": {
+ "enabled": "BTC-PERP",
+ "available": "BTC-PERP",
+ "requestFormat": {
+ "uppercase": true,
+ "delimiter": "-"
+ },
+ "configFormat": {
+ "uppercase": true,
+ "delimiter": "-"
+ }
+ },
+ "spot": {
+ "enabled": "BTC/USD",
+ "available": "BTC/USD",
+ "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": {
+ "tickerBatching": true,
+ "autoPairUpdates": true
+ },
+ "websocketAPI": true,
+ "websocketCapabilities": {}
+ },
+ "enabled": {
+ "autoPairUpdates": true,
+ "websocketAPI": false
+ }
+ },
+ "bankAccounts": [
+ {
+ "enabled": false,
+ "bankName": "",
+ "bankAddress": "",
+ "bankPostalCode": "",
+ "bankPostalCity": "",
+ "bankCountry": "",
+ "accountName": "",
+ "accountNumber": "",
+ "swiftCode": "",
+ "iban": "",
+ "supportedCurrencies": ""
+ }
+ ]
+ },
+```
+
+#### Configs can be updated automatically by running the following command:
+
+Check to make sure that the command does not override the NTP client and encrypt config default settings:
+
+```bash
+go build && gocryptotrader.exe --config=config_example.json
+```
+
+### Add the currency pair format structs in ftx_wrapper.go:
+
+#### Futures currency support:
+
+Similar to the configs, spot support is inbuilt but other asset types will need to be manually supported
+
+```go
+ spot := currency.PairStore{
+ RequestFormat: ¤cy.PairFormat{
+ Uppercase: true,
+ Delimiter: "/",
+ },
+ ConfigFormat: ¤cy.PairFormat{
+ Uppercase: true,
+ Delimiter: "/",
+ },
+ }
+ futures := currency.PairStore{
+ RequestFormat: ¤cy.PairFormat{
+ Uppercase: true,
+ Delimiter: "-",
+ },
+ ConfigFormat: ¤cy.PairFormat{
+ Uppercase: true,
+ Delimiter: "-",
+ },
+ }
+```
+
+### Document the addition of the new exchange (FTX exchange is used as an example below):
+
+Yes means supported, No means not yet implemented and NA means protocol unsupported
+
+#### Add exchange to the root [readme](../README.md) file:
+```go
+| Exchange | REST API | Streaming API | FIX API |
+|----------|------|-----------|-----|
+| Alphapoint | Yes | Yes | NA |
+| Binance| Yes | Yes | NA |
+| Bitfinex | Yes | Yes | NA |
+| Bitflyer | Yes | No | NA |
+| Bithumb | Yes | NA | NA |
+| BitMEX | Yes | Yes | NA |
+| Bitstamp | Yes | Yes | No |
+| Bittrex | Yes | No | NA |
+| BTCMarkets | Yes | No | NA |
+| BTSE | Yes | Yes | NA |
+| COINUT | Yes | Yes | NA |
+| 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 |
+| Huobi.Pro | Yes | Yes | NA |
+| ItBit | Yes | NA | No |
+| Kraken | Yes | Yes | NA |
+| Lbank | Yes | No | NA |
+| LakeBTC | Yes | No | NA |
+| LocalBitcoins | Yes | NA | NA |
+| OKCoin International | Yes | Yes | No |
+| OKEX | Yes | Yes | No |
+| Poloniex | Yes | Yes | NA |
+| Yobit | Yes | NA | NA |
+| ZB.COM | Yes | Yes | NA |
+```
+
+#### Add exchange to the list of [supported exchanges](../exchanges/support.go):
+```go
+var Exchanges = []string{
+ "binance",
+ "bitfinex",
+ "bitflyer",
+ "bithumb",
+ "bitmex",
+ "bitstamp",
+ "bittrex",
+ "btc markets",
+ "btse",
+ "coinbasepro",
+ "coinbene",
+ "coinut",
+ "exmo",
+ "ftx", // <-------- new exchange
+ "gateio",
+ "gemini",
+ "hitbtc",
+ "huobi",
+ "itbit",
+ "kraken",
+ "lakebtc",
+ "lbank",
+ "localbitcoins",
+ "okcoin international",
+ "okex",
+ "poloniex",
+ "yobit",
+ "zb",
+```
+
+#### Increment the default number of supported exchanges in [config/config_test.go](../config/config_test.go):
+```go
+func TestGetEnabledExchanges(t *testing.T) {
+ cfg := GetConfig()
+ err := cfg.LoadConfig(TestFile, true)
+ if err != nil {
+ t.Errorf(
+ "TestGetEnabledExchanges. LoadConfig Error: %s", err.Error(),
+ )
+ }
+
+ exchanges := cfg.GetEnabledExchanges()
+ if len(exchanges) != defaultEnabledExchanges { // modify the value of defaultEnabledExchanges at the top of the config_test.go file to match the total count of exchanges
+ t.Error(
+ "TestGetEnabledExchanges. Enabled exchanges value mismatch",
+ )
+ }
+
+ if !common.StringDataCompare(exchanges, "Bitfinex") {
+ t.Error(
+ "TestGetEnabledExchanges. Expected exchange Bitfinex not found",
+ )
+ }
+}
+```
+
+#### Increment the number of supported exchanges in [the gctscript exchange wrapper test file](../gctscript/wrappers/gct/exchange/exchange_test.go):
+```go
+func TestExchange_Exchanges(t *testing.T) {
+ t.Parallel()
+ x := exchangeTest.Exchanges(false)
+ y := len(x)
+ expected := 28 // modify this value to match the total count of exchanges
+ if y != expected {
+ t.Fatalf("expected %v received %v", expected , y)
+ }
+}
+```
+
+#### Setup and run the [documentation tool](../cmd/documentation):
+
+- Create a new file named *exchangename*.tmpl
+- Copy contents of template from another exchange example here being Exmo
+- Replace names and variables as shown:
+
+```go
+{{define "exchanges exmo" -}} // exmo -> ftx
+{{template "header" .}}
+## Exmo Exchange
+
+#### Current Features
+
++ REST Support // if websocket or fix are supported, add that in too
+```
+
+```go
+var e exchange.IBotExchange // We name the exchange.IBotExchange variable after the first character of the exchange, eg f for FTX. e -> f
+
+for i := range bot.Exchanges {
+ if bot.Exchanges[i].GetName() == "Exmo" { // Exmo -> FTX
+ e = bot.Exchanges[i] // e -> f
+ }
+}
+
+// Public calls - wrapper functions
+
+// Fetches current ticker information
+tick, err := e.FetchTicker() // e -> f
+if err != nil {
+ // Handle error
+}
+
+// Fetches current orderbook information
+ob, err := e.FetchOrderbook() // e -> f (do so for the rest of the functions too)
+if err != nil {
+ // Handle error
+}
+```
+
+- Run documentation.go to generate readme file for the exchange:
+```bash
+cd gocryptotrader\cmd\documentation
+go run documentation.go
+```
+
+This will generate a readme file for the exchange which can be found in the new exchange's folder
+
+### Create functions supported by the exchange:
+
+#### Requester functions:
+
+```go
+// SendHTTPRequest sends an unauthenticated HTTP request
+func (f *FTX) SendHTTPRequest(path string, result interface{}) error {
+ return f.SendPayload(context.Background(), &request.Item{
+ Method: http.MethodGet,
+ Path: path,
+ Result: result,
+ Verbose: f.Verbose,
+ HTTPDebugging: f.HTTPDebugging,
+ HTTPRecording: f.HTTPRecording,
+ })
+}
+```
+
+#### Unauthenticated Functions:
+
+https://docs.ftx.com/#get-markets
+
+Create a type struct in types.go for the response type shown on the documentation website:
+
+For efficiency, a JSON to Golang converter can be used: https://mholt.github.io/json-to-go/.
+However, great care must be taken as to the values which are autogenerated. The JSON converter tool will default to whatever type it detects, but ultimately conversions to a more useful variable type would be better. For example, price and quantity on some exchange API's provide these as strings. Internally, it would be better if they're converted to the more useful float64 var type.
+
+```go
+// MarketData stores market data
+type MarketData struct {
+ Name string `json:"name"`
+ BaseCurrency string `json:"baseCurrency"`
+ QuoteCurrency string `json:"quoteCurrency"`
+ MarketType string `json:"type"`
+ Underlying string `json:"underlying"`
+ Enabled bool `json:"enabled"`
+ Ask float64 `json:"ask"`
+ Bid float64 `json:"bid"`
+ Last float64 `json:"last"`
+ PriceIncrement float64 `json:"priceIncrement"`
+ SizeIncrement float64 `json:"sizeIncrement"`
+}
+```
+
+Create new consts to define endpoint strings, they are created at the top of ftx.go file:
+```go
+const (
+ ftxAPIURL = "https://ftx.com/api"
+
+ // Public endpoints
+ getMarkets = "/markets"
+ getMarket = "/markets/"
+ getOrderbook = "/markets/%s/orderbook?depth=%s"
+ getTrades = "/markets/%s/trades?"
+ getHistoricalData = "/markets/%s/candles?"
+ getFutures = "/futures"
+ getFuture = "/futures/"
+ getFutureStats = "/futures/%s/stats"
+ getFundingRates = "/funding_rates"
+ getAllWallegetAllWalletBalances = "/wallet/all_balances"
+ ```
+
+Create a get function in ftx.go file and unmarshall the data in the created type:
+```go
+// GetMarkets gets market data
+func (f *FTX) GetMarkets() (Markets, error) {
+ var resp Markets
+ return resp, f.SendHTTPRequest(ftxAPIURL+getMarkets, &resp)
+}
+```
+
+Create a test function in ftx_test.go to see if the data is received and unmarshalled correctly
+```go
+const(
+ spotPair = "FTT/BTC"
+)
+
+func TestGetMarket(t *testing.T) {
+ t.Parallel() // adding t.Parralel() is preferred as it allows tests to run simultaneously, speeding up package test time
+ f.Verbose = true // used for more detailed output
+ a, err := f.GetMarket(spotPair) // spotPair is just a const so it can be reused in other tests too
+ t.Log(a)
+ if err != nil {
+ t.Error(err)
+ }
+}
+```
+Verbose can be set to true to see the data received if there are errors unmarshalling
+Once testing is done remove verbose, variable a and t.Log(a) since they produce unnecessary output when GCT is run
+```go
+_, err := f.GetMarket(spotPair)
+```
+
+Ensure each endpoint is implemented and has an associated test to improve test coverage and increase confidence
+
+#### Authenticated functions:
+
+Authenticated request function is created based on the way the exchange documentation specifies: https://docs.ftx.com/#authentication
+```go
+// SendAuthHTTPRequest sends an authenticated request
+func (f *FTX) SendAuthHTTPRequest(method, path string, data, result interface{}) error {
+ ts := strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
+ var body io.Reader
+ var hmac, payload []byte
+ var err error
+ if data != nil {
+ payload, err = json.Marshal(data)
+ if err != nil {
+ return err
+ }
+ body = bytes.NewBuffer(payload)
+ sigPayload := ts + method + "/api" + path + string(payload)
+ hmac = crypto.GetHMAC(crypto.HashSHA256, []byte(sigPayload), []byte(f.API.Credentials.Secret))
+ } else {
+ sigPayload := ts + method + "/api" + path
+ hmac = crypto.GetHMAC(crypto.HashSHA256, []byte(sigPayload), []byte(f.API.Credentials.Secret))
+ }
+ headers := make(map[string]string)
+ headers["FTX-KEY"] = f.API.Credentials.Key
+ headers["FTX-SIGN"] = crypto.HexEncodeToString(hmac)
+ headers["FTX-TS"] = ts
+ headers["Content-Type"] = "application/json"
+ return f.SendPayload(context.Background(), &request.Item{
+ Method: method,
+ Path: ftxAPIURL + path,
+ Headers: headers,
+ Body: body,
+ Result: result,
+ AuthRequest: true,
+ Verbose: f.Verbose,
+ HTTPDebugging: f.HTTPDebugging,
+ HTTPRecording: f.HTTPRecording,
+ })
+}
+```
+
+To test authenticated functions, you must have an account with API keys and SendAuthHTTPRequest must be implemented.
+
+HTTP Mocking framework can also be added for the exchange. For reference, please see the [HTTP mock](../testdata/http_mock) package.
+
+Create authenticated functions and test along the way similar to the functions above:
+
+https://docs.ftx.com/#get-account-information:
+
+```go
+// GetAccountInfo gets account info
+func (f *FTX) GetAccountInfo() (AccountData, error) {
+ var resp AccountData
+ return resp, f.SendAuthHTTPRequest(http.MethodGet, getAccountInfo, nil, &resp)
+}
+```
+
+Get Request params for authenticated requests are sent through url.Values{}:
+
+https://docs.ftx.com/#get-withdrawal-history:
+
+```go
+// GetTriggerOrderHistory gets trigger orders that are currently open
+func (f *FTX) GetTriggerOrderHistory(marketName string, startTime, endTime time.Time, side, orderType, limit string) (TriggerOrderHistory, error) {
+ var resp TriggerOrderHistory
+ params := url.Values{}
+ if marketName != "" {
+ params.Set("market", marketName)
+ }
+ if !startTime.IsZero() && !endTime.IsZero() {
+ params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10))
+ params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10))
+ if startTime.After(endTime) {
+ return resp, errors.New("startTime cannot be after endTime")
+ }
+ }
+ if side != "" {
+ params.Set("side", side)
+ }
+ if orderType != "" {
+ params.Set("type", orderType)
+ }
+ if limit != "" {
+ params.Set("limit", limit)
+ }
+ return resp, f.SendAuthHTTPRequest(http.MethodGet, getTriggerOrderHistory+params.Encode(), nil, &resp)
+}
+```
+
+https://docs.ftx.com/#place-order
+
+
+Structs for unmarshalling the data are made exactly the same way as the previous functions.
+
+```go
+type OrderData struct {
+ CreatedAt time.Time `json:"createdAt"`
+ FilledSize float64 `json:"filledSize"`
+ Future string `json:"future"`
+ ID int64 `json:"id"`
+ Market string `json:"market"`
+ Price float64 `json:"price"`
+ AvgFillPrice float64 `json:"avgFillPrice"`
+ RemainingSize float64 `json:"remainingSize"`
+ Side string `json:"side"`
+ Size float64 `json:"size"`
+ Status string `json:"status"`
+ OrderType string `json:"type"`
+ ReduceOnly bool `json:"reduceOnly"`
+ IOC bool `json:"ioc"`
+ PostOnly bool `json:"postOnly"`
+ ClientID string `json:"clientId"`
+}
+
+// PlaceOrder stores data of placed orders
+type PlaceOrder struct {
+ Success bool `json:"success"`
+ Result OrderData `json:"result"`
+}
+```
+
+For `POST` or `DELETE` requests, params are sent through a map[string]interface{}:
+
+```go
+// Order places an order
+func (f *FTX) Order(marketName, side, orderType, reduceOnly, ioc, postOnly, clientID string, price, size float64) (PlaceOrder, error) {
+ req := make(map[string]interface{})
+ req["market"] = marketName
+ req["side"] = side
+ req["price"] = price
+ req["type"] = orderType
+ req["size"] = size
+ if reduceOnly != "" {
+ req["reduceOnly"] = reduceOnly
+ }
+ if ioc != "" {
+ req["ioc"] = ioc
+ }
+ if postOnly != "" {
+ req["postOnly"] = postOnly
+ }
+ if clientID != "" {
+ req["clientID"] = clientID
+ }
+ var resp PlaceOrder
+ return resp, f.SendAuthHTTPRequest(http.MethodPost, placeOrder, req, &resp)
+}
+```
+
+### Implementing wrapper functions:
+
+Wrapper functions are the interface in which the GoCryptoTrader engine communicates with an exchange for receiving data and sending requests. A breakdown of all API functions can be found [here](../exchanges/interfaces.go).
+The exchanges may not support all the functionality in the wrapper, so fill out the ones that are supported as shown in the examples below:
+
+Unsupported Example:
+
+```go
+// WithdrawFiatFunds returns a withdrawal ID when a withdrawal is
+// submitted
+func (f *FTX) WithdrawFiatFunds(withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) {
+ var resp *withdraw.ExchangeResponse
+ return resp, common.ErrFunctionNotSupported
+}
+```
+
+Supported Examples:
+
+```go
+// FetchTradablePairs returns a list of the exchanges tradable pairs
+func (f *FTX) FetchTradablePairs(a asset.Item) ([]string, error) {
+ if !f.SupportsAsset(a) {
+ return nil, fmt.Errorf("asset type of %s is not supported by %s", a, f.Name)
+ }
+ markets, err := f.GetMarkets()
+ if err != nil {
+ return nil, err
+ }
+ var pairs []string
+ switch a {
+ case asset.Spot:
+ for x := range markets.Result {
+ if markets.Result[x].MarketType == spotString {
+ pairs = append(pairs, markets.Result[x].Name)
+ }
+ }
+ case asset.Futures:
+ for x := range markets.Result {
+ if markets.Result[x].MarketType == futuresString {
+ pairs = append(pairs, markets.Result[x].Name)
+ }
+ }
+ }
+ return pairs, nil
+}
+```
+
+Wrapper functions on most exchanges are written in similar ways so other exchanges can be used as a reference.
+
+Many helper functions defined in [exchange.go](../exchanges/exchange.go) can be useful when implementing wrapper functions. See examples below:
+
+```go
+f.FormatExchangeCurrency(p, a) // Formats the currency pair to the style accepted by the exchange. p is the currency pair & a is the asset type
+
+f.SupportsAsset(a) // Checks if an asset type is supported by the bot
+
+f.GetPairAssetType(p) // Returns the asset type of currency pair p
+```
+
+The currency package contains many helper functions to format and process currency pairs. See [currency](../currency/README.md).
+
+### Websocket addition if exchange supports it:
+
+#### Add websocket to exchange struct in ftx.go
+
+```go
+// FTX is the overarching type across this package
+type FTX struct {
+ exchange.Base
+ WebsocketConn *wshandler.WebsocketConnection // Add this line
+}
+```
+
+#### Websocket Setup:
+
+- Set the websocket url in ftx_websocket.go that is provided in the documentation:
+
+```go
+ ftxWSURL = "wss://ftx.com/ws/"
+```
+
+#### Complete WsConnect function:
+
+```go
+// WsConnect connects to a websocket feed
+func (f *FTX) WsConnect() error {
+ if !f.Websocket.IsEnabled() || !f.IsEnabled() {
+ return errors.New(wshandler.WebsocketNotEnabled)
+ }
+ var dialer websocket.Dialer
+ err := f.WebsocketConn.Dial(&dialer, http.Header{})
+ if err != nil {
+ return err
+ }
+ f.WebsocketConn.SetupPingHandler(wshandler.WebsocketPingHandler{
+ MessageType: websocket.PingMessage,
+ Delay: ftxWebsocketTimer,
+ })
+ if f.Verbose {
+ log.Debugf(log.ExchangeSys, "%s Connected to Websocket.\n", f.Name)
+ }
+ go f.wsReadData()
+ if f.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) {
+ err := f.WsAuth()
+ if err != nil {
+ f.Websocket.DataHandler <- err
+ f.Websocket.SetCanUseAuthenticatedEndpoints(false)
+ }
+ }
+ f.GenerateDefaultSubscriptions()
+ return nil
+}
+```
+
+- Create function to generate default subscriptions:
+
+```go
+// GenerateDefaultSubscriptions generates default subscription
+func (f *FTX) GenerateDefaultSubscriptions() {
+ var channels = []string{wsTicker, wsTrades, wsOrderbook, wsMarkets, wsFills, wsOrders}
+ var subscriptions []wshandler.WebsocketChannelSubscription
+ for a := range f.CurrencyPairs.AssetTypes {
+ pairs := f.GetEnabledPairs(f.CurrencyPairs.AssetTypes[a])
+ for z := range pairs {
+ newPair := currency.NewPairWithDelimiter(pairs[z].Base.String(), pairs[z].Quote.String(), "-")
+ for x := range channels {
+ subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{
+ Channel: channels[x],
+ Currency: newPair,
+ })
+ }
+ }
+ }
+ f.Websocket.SubscribeToChannels(subscriptions)
+}
+```
+
+- To receive data from websocket, a subscription needs to be made with one or more of the available channels:
+
+- Set channel names as consts for ease of use:
+
+```go
+ wsTicker = "ticker"
+ wsTrades = "trades"
+ wsOrderbook = "orderbook"
+ wsMarkets = "markets"
+ wsFills = "fills"
+ wsOrders = "orders"
+ wsUpdate = "update"
+ wsPartial = "partial"
+```
+
+- Create subscribe function with the data provided by the exchange documentation:
+
+https://docs.ftx.com/#request-process
+
+- Create a struct required to subscribe to channels:
+
+```go
+// WsSub has the data used to subscribe to a channel
+type WsSub struct {
+ Channel string `json:"channel,omitempty"`
+ Market string `json:"market,omitempty"`
+ Operation string `json:"op,omitempty"`
+}
+```
+
+- Create the subscription function:
+
+```go
+// Subscribe sends a websocket message to receive data from the channel
+func (f *FTX) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error {
+ var sub WsSub
+ a, err := f.GetPairAssetType(channelToSubscribe.Currency)
+ if err != nil {
+ return err
+ }
+ switch channelToSubscribe.Channel {
+ case wsFills, wsOrders:
+ sub.Operation = "subscribe"
+ sub.Channel = channelToSubscribe.Channel
+ default:
+ sub.Operation = "subscribe"
+ sub.Channel = channelToSubscribe.Channel
+ sub.Market = f.FormatExchangeCurrency(channelToSubscribe.Currency, a).String()
+ }
+ return f.WebsocketConn.SendJSONMessage(sub)
+}
+```
+
+- Test subscriptions and check to see if data is received from websocket:
+
+Run gocryptotrader with the following settings enabled in config
+
+```go
+ "websocketAPI": true,
+ "websocketCapabilities": {}
+ },
+ "enabled": {
+ "autoPairUpdates": true,
+ "websocketAPI": true
+```
+
+#### Handle websocket data:
+
+- Function to read data received from websocket:
+
+```go
+// wsReadData gets and passes on websocket messages for processing
+func (f *FTX) wsReadData() {
+ f.Websocket.Wg.Add(1)
+ defer f.Websocket.Wg.Done()
+
+ for {
+ select {
+ case <-f.Websocket.ShutdownC:
+ return
+ default:
+ resp, err := f.WebsocketConn.ReadMessage()
+ if err != nil {
+ f.Websocket.ReadMessageErrors <- err
+ return
+ }
+ f.Websocket.TrafficAlert <- struct{}{}
+ err = f.wsHandleData(resp.Raw)
+ if err != nil {
+ f.Websocket.DataHandler <- err
+ }
+ }
+ }
+}
+```
+
+- Simple Examples of data handling:
+
+1. Create the main struct used for unmarshalling data
+
+2. Unmarshall the data into the overarching result type
+
+```go
+// WsResponseData stores basic ws response data on being subscribed to a channel successfully
+type WsResponseData struct {
+ ResponseType string `json:"type"`
+ Channel string `json:"channel"`
+ Market string `json:"market"`
+ Data interface{} `json:"data"`
+}
+```
+
+- Unmarshall the raw data into the main type:
+
+```go
+ var result map[string]interface{}
+ err := json.Unmarshal(respRaw, &result)
+ if err != nil {
+ return err
+ }
+```
+
+Using switch cases and types created earlier, unmarshall the data into the more specific structs.
+There are some built in structs in wshandler which are used to store the websocket data such as wshandler.TradeData or wshandler.KlineData.
+If a suitable struct does not exist in wshandler, wrapper types are the next preference to store the data such as in the market channel example given below:
+
+```go
+ switch result["channel"] {
+ case wsTicker:
+ var resultData WsTickerDataStore
+ err = json.Unmarshal(respRaw, &resultData)
+ if err != nil {
+ return err
+ }
+ f.Websocket.DataHandler <- &ticker.Price{
+ ExchangeName: f.Name,
+ Bid: resultData.Ticker.Bid,
+ Ask: resultData.Ticker.Ask,
+ Last: resultData.Ticker.Last,
+ LastUpdated: timestampFromFloat64(resultData.Ticker.Time),
+ Pair: p,
+ AssetType: a,
+ }
+```
+
+If neither of those provide a suitable struct to store the data in, the data can just be passed onto wshandler without any further changes:
+
+```go
+ case wsFills:
+ var resultData WsFillsDataStore
+ err = json.Unmarshal(respRaw, &resultData)
+ if err != nil {
+ return err
+ }
+ f.Websocket.DataHandler <- resultData.FillsData
+```
+
+- Data Handling can be tested offline similar to the following example:
+
+```go
+func TestParsingWSOrdersData(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip("API keys required but not set, skipping test")
+ }
+ data := []byte(`{
+ "channel": "orders",
+ "data": {
+ "id": 24852229,
+ "clientId": null,
+ "market": "BTC-PERP",
+ "type": "limit",
+ "side": "buy",
+ "size": 42353.0,
+ "price": 0.2977,
+ "reduceOnly": false,
+ "ioc": false,
+ "postOnly": false,
+ "status": "closed",
+ "filledSize": 0.0,
+ "remainingSize": 0.0,
+ "avgFillPrice": 0.2978
+ },
+ "type": "update"
+ }`)
+ err := f.wsHandleData(data)
+ if err != nil {
+ t.Error(err)
+ }
+}
+```
+
+- Create types given in the documentation to unmarshall the streamed data:
+
+https://docs.ftx.com/#fills-2
+
+```go
+// WsFills stores websocket fills' data
+type WsFills struct {
+ Fee float64 `json:"fee"`
+ FeeRate float64 `json:"feeRate"`
+ Future string `json:"future"`
+ ID int64 `json:"id"`
+ Liquidity string `json:"liquidity"`
+ Market string `json:"market"`
+ OrderID int64 `json:"int64"`
+ TradeID int64 `json:"tradeID"`
+ Price float64 `json:"price"`
+ Side string `json:"side"`
+ Size float64 `json:"size"`
+ Time time.Time `json:"time"`
+ OrderType string `json:"orderType"`
+}
+
+// WsFillsDataStore stores ws fills' data
+type WsFillsDataStore struct {
+ Channel string `json:"channel"`
+ MessageType string `json:"type"`
+ FillsData WsFills `json:"fills"`
+}
+```
+
+- Create the authentication function based on specifications provided in the documentation:
+
+https://docs.ftx.com/#private-channels
+
+```go
+// WsAuth sends an authentication message to receive auth data
+func (f *FTX) WsAuth() error {
+ intNonce := time.Now().UnixNano() / 1000000
+ strNonce := strconv.FormatInt(intNonce, 10)
+ hmac := crypto.GetHMAC(
+ crypto.HashSHA256,
+ []byte(strNonce+"websocket_login"),
+ []byte(f.API.Credentials.Secret),
+ )
+ sign := crypto.HexEncodeToString(hmac)
+ req := Authenticate{Operation: "login",
+ Args: AuthenticationData{
+ Key: f.API.Credentials.Key,
+ Sign: sign,
+ Time: intNonce,
+ },
+ }
+ return f.WebsocketConn.SendJSONMessage(req)
+}
+```
+
+- Create an unsubscribe function if the exchange has the functionality:
+
+```go
+// Unsubscribe sends a websocket message to stop receiving data from the channel
+func (f *FTX) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error {
+ var unSub WsSub
+ a, err := f.GetPairAssetType(channelToSubscribe.Currency)
+ if err != nil {
+ return err
+ }
+ unSub.Operation = "unsubscribe"
+ unSub.Channel = channelToSubscribe.Channel
+ unSub.Market = f.FormatExchangeCurrency(channelToSubscribe.Currency, a).String()
+ return f.WebsocketConn.SendJSONMessage(unSub)
+}
+```
+
+- Complete websocket setup in wrapper:
+
+Add websocket functionality if supported to Setup:
+
+```go
+ err = f.Websocket.Setup(
+ &wshandler.WebsocketSetup{
+ Enabled: exch.Features.Enabled.Websocket,
+ Verbose: exch.Verbose,
+ AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport,
+ WebsocketTimeout: exch.WebsocketTrafficTimeout,
+ DefaultURL: ftxWSURL,
+ ExchangeName: exch.Name,
+ RunningURL: exch.API.Endpoints.WebsocketURL,
+ Connector: f.WsConnect,
+ Subscriber: f.Subscribe,
+ UnSubscriber: f.Unsubscribe,
+ Features: &f.Features.Supports.WebsocketCapabilities,
+ })
+ if err != nil {
+ return err
+ }
+ ```
+
+Below are the features supported by FTX API protocol:
+
+ ```go
+ f.Features = exchange.Features{
+ Supports: exchange.FeaturesSupported{
+ REST: true,
+ Websocket: true,
+ RESTCapabilities: protocol.Features{
+ TickerFetching: true,
+ KlineFetching: true,
+ TradeFetching: true,
+ OrderbookFetching: true,
+ AutoPairUpdates: true,
+ AccountInfo: true,
+ GetOrder: true,
+ GetOrders: true,
+ CancelOrders: true,
+ CancelOrder: true,
+ SubmitOrder: true,
+ TradeFee: true,
+ FiatDepositFee: true,
+ FiatWithdrawalFee: true,
+ CryptoWithdrawalFee: true,
+ },
+ WebsocketCapabilities: protocol.Features{
+ OrderbookFetching: true,
+ TradeFetching: true,
+ Subscribe: true,
+ Unsubscribe: true,
+ GetOrders: true,
+ GetOrder: true,
+ },
+ WithdrawPermissions: exchange.NoAPIWithdrawalMethods,
+ },
+ Enabled: exchange.FeaturesEnabled{
+ AutoPairUpdates: true,
+ },
+ }
+```
+
+- Link websocket to wrapper functions:
+
+Initially the functions return nil or common.ErrNotYetImplemented
+
+```go
+// GetWebsocket returns a pointer to the exchange websocket
+func (f *FTX) GetWebsocket() (*wshandler.Websocket, error) {
+ return f.Websocket, nil
+}
+
+// SubscribeToWebsocketChannels appends to ChannelsToSubscribe
+// which lets websocket.manageSubscriptions handle subscribing
+func (f *FTX) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error {
+ f.Websocket.SubscribeToChannels(channels)
+ return nil
+}
+
+// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe
+// which lets websocket.manageSubscriptions handle unsubscribing
+func (f *FTX) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error {
+ f.Websocket.RemoveSubscribedChannels(channels)
+ return nil
+}
+
+// GetSubscriptions returns a copied list of subscriptions
+func (f *FTX) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) {
+ return f.Websocket.GetSubscriptions(), nil
+}
+
+// AuthenticateWebsocket sends an authentication message to the websocket
+func (f *FTX) AuthenticateWebsocket() error {
+ return f.WsAuth()
+}
+```
\ No newline at end of file
diff --git a/engine/exchange.go b/engine/exchange.go
index 021e5f17..e42ca7c3 100644
--- a/engine/exchange.go
+++ b/engine/exchange.go
@@ -20,6 +20,7 @@ import (
"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"
"github.com/thrasher-corp/gocryptotrader/exchanges/gateio"
"github.com/thrasher-corp/gocryptotrader/exchanges/gemini"
"github.com/thrasher-corp/gocryptotrader/exchanges/hitbtc"
@@ -187,6 +188,8 @@ func LoadExchange(name string, useWG bool, wg *sync.WaitGroup) error {
exch = new(exmo.EXMO)
case "coinbasepro":
exch = new(coinbasepro.CoinbasePro)
+ case "ftx":
+ exch = new(ftx.FTX)
case "gateio":
exch = new(gateio.Gateio)
case "gemini":
diff --git a/engine/fake_exchange_test.go b/engine/fake_exchange_test.go
index 0d0de220..343278b0 100644
--- a/engine/fake_exchange_test.go
+++ b/engine/fake_exchange_test.go
@@ -107,7 +107,7 @@ func (h *FakePassingExchange) SetPairs(_ currency.Pairs, _ asset.Item, _ bool) e
return nil
}
func (h *FakePassingExchange) GetAssetTypes() asset.Items { return asset.Items{asset.Spot} }
-func (h *FakePassingExchange) GetExchangeHistory(_ currency.Pair, _ asset.Item) ([]exchange.TradeHistory, error) {
+func (h *FakePassingExchange) GetExchangeHistory(_ currency.Pair, _ asset.Item, _, _ time.Time) ([]exchange.TradeHistory, error) {
return nil, nil
}
func (h *FakePassingExchange) SupportsAutoPairUpdates() bool { return true }
diff --git a/exchanges/README.md b/exchanges/README.md
index 02689727..5cee45c7 100644
--- a/exchanges/README.md
+++ b/exchanges/README.md
@@ -25,6 +25,10 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
+ Please checkout individual exchange README for more information on
implementation
+## Guide for adding a new exchange
+
++ A guide on implementing API support for a new exchange can be found [here](../docs/ADD_NEW_EXCHANGE.md)
+
### Please click GoDocs chevron above to view current GoDoc information for this package
## Contribution
diff --git a/exchanges/alphapoint/alphapoint_wrapper.go b/exchanges/alphapoint/alphapoint_wrapper.go
index b4aff762..0eb6fe15 100644
--- a/exchanges/alphapoint/alphapoint_wrapper.go
+++ b/exchanges/alphapoint/alphapoint_wrapper.go
@@ -210,8 +210,8 @@ func (a *Alphapoint) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrNotYetImplemented
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (a *Alphapoint) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (a *Alphapoint) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/binance/binance_wrapper.go b/exchanges/binance/binance_wrapper.go
index b7ac74db..1a507a5b 100644
--- a/exchanges/binance/binance_wrapper.go
+++ b/exchanges/binance/binance_wrapper.go
@@ -409,8 +409,8 @@ func (b *Binance) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (b *Binance) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (b *Binance) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/bitfinex/bitfinex_wrapper.go b/exchanges/bitfinex/bitfinex_wrapper.go
index 7afabd83..7b6951f8 100644
--- a/exchanges/bitfinex/bitfinex_wrapper.go
+++ b/exchanges/bitfinex/bitfinex_wrapper.go
@@ -424,8 +424,8 @@ func (b *Bitfinex) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (b *Bitfinex) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (b *Bitfinex) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/bitflyer/bitflyer_wrapper.go b/exchanges/bitflyer/bitflyer_wrapper.go
index 7c065e28..f954fc6c 100644
--- a/exchanges/bitflyer/bitflyer_wrapper.go
+++ b/exchanges/bitflyer/bitflyer_wrapper.go
@@ -280,8 +280,8 @@ func (b *Bitflyer) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (b *Bitflyer) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (b *Bitflyer) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/bithumb/bithumb_wrapper.go b/exchanges/bithumb/bithumb_wrapper.go
index 37f2a7bd..bc059c62 100644
--- a/exchanges/bithumb/bithumb_wrapper.go
+++ b/exchanges/bithumb/bithumb_wrapper.go
@@ -311,8 +311,8 @@ func (b *Bithumb) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (b *Bithumb) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (b *Bithumb) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/bitmex/bitmex_wrapper.go b/exchanges/bitmex/bitmex_wrapper.go
index adebbea5..a23bcfe6 100644
--- a/exchanges/bitmex/bitmex_wrapper.go
+++ b/exchanges/bitmex/bitmex_wrapper.go
@@ -421,8 +421,8 @@ func (b *Bitmex) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrNotYetImplemented
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (b *Bitmex) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (b *Bitmex) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/bitstamp/bitstamp_wrapper.go b/exchanges/bitstamp/bitstamp_wrapper.go
index 738176a4..5235168c 100644
--- a/exchanges/bitstamp/bitstamp_wrapper.go
+++ b/exchanges/bitstamp/bitstamp_wrapper.go
@@ -362,8 +362,8 @@ func (b *Bitstamp) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (b *Bitstamp) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (b *Bitstamp) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/bittrex/bittrex_wrapper.go b/exchanges/bittrex/bittrex_wrapper.go
index fd0d6602..83263bea 100644
--- a/exchanges/bittrex/bittrex_wrapper.go
+++ b/exchanges/bittrex/bittrex_wrapper.go
@@ -339,8 +339,8 @@ func (b *Bittrex) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (b *Bittrex) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (b *Bittrex) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/btcmarkets/btcmarkets_wrapper.go b/exchanges/btcmarkets/btcmarkets_wrapper.go
index 8f83c3eb..f356a9ff 100644
--- a/exchanges/btcmarkets/btcmarkets_wrapper.go
+++ b/exchanges/btcmarkets/btcmarkets_wrapper.go
@@ -369,8 +369,8 @@ func (b *BTCMarkets) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (b *BTCMarkets) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (b *BTCMarkets) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/btse/btse_wrapper.go b/exchanges/btse/btse_wrapper.go
index 5b12a87d..2b246fa8 100644
--- a/exchanges/btse/btse_wrapper.go
+++ b/exchanges/btse/btse_wrapper.go
@@ -348,8 +348,8 @@ func (b *BTSE) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (b *BTSE) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (b *BTSE) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/coinbasepro/coinbasepro_wrapper.go b/exchanges/coinbasepro/coinbasepro_wrapper.go
index 0477bf86..8a186625 100644
--- a/exchanges/coinbasepro/coinbasepro_wrapper.go
+++ b/exchanges/coinbasepro/coinbasepro_wrapper.go
@@ -385,8 +385,8 @@ func (c *CoinbasePro) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (c *CoinbasePro) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (c *CoinbasePro) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/coinbene/coinbene_wrapper.go b/exchanges/coinbene/coinbene_wrapper.go
index 0289c9fe..d9cf506c 100644
--- a/exchanges/coinbene/coinbene_wrapper.go
+++ b/exchanges/coinbene/coinbene_wrapper.go
@@ -461,8 +461,8 @@ func (c *Coinbene) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (c *Coinbene) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (c *Coinbene) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrFunctionNotSupported
}
diff --git a/exchanges/coinut/coinut_wrapper.go b/exchanges/coinut/coinut_wrapper.go
index e3d24e22..bf84e38d 100644
--- a/exchanges/coinut/coinut_wrapper.go
+++ b/exchanges/coinut/coinut_wrapper.go
@@ -462,8 +462,8 @@ func (c *COINUT) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (c *COINUT) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (c *COINUT) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/exchange.go b/exchanges/exchange.go
index 003dd6d3..2cddbe29 100644
--- a/exchanges/exchange.go
+++ b/exchanges/exchange.go
@@ -211,10 +211,12 @@ func (e *Base) GetAssetTypes() asset.Items {
}
// GetPairAssetType returns the associated asset type for the currency pair
+// This method is only useful for exchanges that have pair names with multiple delimiters (BTC-USD-0626)
+// Helpful if the exchange has only a single asset type but in that case the asset type can be hard coded
func (e *Base) GetPairAssetType(c currency.Pair) (asset.Item, error) {
assetTypes := e.GetAssetTypes()
for i := range assetTypes {
- if e.GetEnabledPairs(assetTypes[i]).Contains(c, true) {
+ if e.GetAvailablePairs(assetTypes[i]).Contains(c, true) {
return assetTypes[i], nil
}
}
diff --git a/exchanges/exchange_test.go b/exchanges/exchange_test.go
index a9d3680f..93b9501a 100644
--- a/exchanges/exchange_test.go
+++ b/exchanges/exchange_test.go
@@ -1428,7 +1428,7 @@ func TestGetAssetType(t *testing.T) {
b.CurrencyPairs.AssetTypes = asset.Items{asset.Spot}
b.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore)
b.CurrencyPairs.Pairs[asset.Spot] = ¤cy.PairStore{
- Enabled: currency.Pairs{
+ Available: currency.Pairs{
currency.NewPair(currency.BTC, currency.USD),
},
ConfigFormat: ¤cy.PairFormat{Delimiter: "-"},
diff --git a/exchanges/exchange_types.go b/exchanges/exchange_types.go
index 18d42711..3e53fe6d 100644
--- a/exchanges/exchange_types.go
+++ b/exchanges/exchange_types.go
@@ -115,6 +115,7 @@ type TradeHistory struct {
Amount float64
Exchange string
Type string
+ Side string
Fee float64
Description string
}
diff --git a/exchanges/exmo/exmo_wrapper.go b/exchanges/exmo/exmo_wrapper.go
index dd194a17..85c6830a 100644
--- a/exchanges/exmo/exmo_wrapper.go
+++ b/exchanges/exmo/exmo_wrapper.go
@@ -348,8 +348,8 @@ func (e *EXMO) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (e *EXMO) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (e *EXMO) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/ftx/README.md b/exchanges/ftx/README.md
new file mode 100644
index 00000000..6b212b94
--- /dev/null
+++ b/exchanges/ftx/README.md
@@ -0,0 +1,133 @@
+# GoCryptoTrader package Ftx
+
+
+
+
+[](https://travis-ci.org/thrasher-corp/gocryptotrader)
+[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
+[](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/ftx)
+[](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
+[](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
+
+
+This ftx 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)
+
+## FTX 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 f exchange.IBotExchange
+
+for i := range bot.Exchanges {
+ if bot.Exchanges[i].GetName() == "FTX" {
+ f = bot.Exchanges[i]
+ }
+}
+
+// Public calls - wrapper functions
+
+// Fetches current ticker information
+tick, err := f.FetchTicker()
+if err != nil {
+ // Handle error
+}
+
+// Fetches current orderbook information
+ob, err := f.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 := f.GetAccountInfo()
+if err != nil {
+ // Handle error
+}
+```
+
++ If enabled via individually importing package, rudimentary example below:
+
+```go
+// Public calls
+
+// Fetches current ticker information
+ticker, err := f.GetTicker()
+if err != nil {
+ // Handle error
+}
+
+// Fetches current orderbook information
+ob, err := f.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 := f.GetUserInfo(...)
+if err != nil {
+ // Handle error
+}
+
+// Submits an order and the exchange and returns its tradeID
+tradeID, err := f.Trade(...)
+if err != nil {
+ // Handle error
+}
+```
+
+### Please click GoDocs chevron above to view current GoDoc information for this package
+
+## Contribution
+
+Please feel free to submit any pull requests or suggest any desired features to be added.
+
+When submitting a PR, please abide by our coding guidelines:
+
++ Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
++ Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
++ Code must adhere to our [coding style](https://github.com/thrasher-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/ftx/ftx.go b/exchanges/ftx/ftx.go
new file mode 100644
index 00000000..55395096
--- /dev/null
+++ b/exchanges/ftx/ftx.go
@@ -0,0 +1,959 @@
+package ftx
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/thrasher-corp/gocryptotrader/common/crypto"
+ exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
+ "github.com/thrasher-corp/gocryptotrader/exchanges/kline"
+ "github.com/thrasher-corp/gocryptotrader/exchanges/order"
+ "github.com/thrasher-corp/gocryptotrader/exchanges/request"
+ "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
+)
+
+// FTX is the overarching type across this package
+type FTX struct {
+ exchange.Base
+ WebsocketConn *wshandler.WebsocketConnection
+}
+
+const (
+ ftxAPIURL = "https://ftx.com/api"
+
+ // Public endpoints
+ getMarkets = "/markets"
+ getMarket = "/markets/"
+ getOrderbook = "/markets/%s/orderbook?depth=%s"
+ getTrades = "/markets/%s/trades?"
+ getHistoricalData = "/markets/%s/candles?"
+ getFutures = "/futures"
+ getFuture = "/futures/"
+ getFutureStats = "/futures/%s/stats"
+ getFundingRates = "/funding_rates"
+ getIndexWeights = "/indexes/%s/weights"
+ getAllWalletBalances = "/wallet/all_balances"
+
+ // Authenticated endpoints
+ getAccountInfo = "/account"
+ getPositions = "/positions"
+ setLeverage = "/account/leverage"
+ getCoins = "/wallet/coins"
+ getBalances = "/wallet/balances"
+ getDepositAddress = "/wallet/deposit_address/"
+ getDepositHistory = "/wallet/deposits"
+ getWithdrawalHistory = "/wallet/withdrawals"
+ withdrawRequest = "/wallet/withdrawals"
+ getOpenOrders = "/orders?"
+ getOrderHistory = "/orders/history?"
+ getOpenTriggerOrders = "/conditional_orders?"
+ getTriggerOrderTriggers = "/conditional_orders/%s/triggers"
+ getTriggerOrderHistory = "/conditional_orders/history?"
+ placeOrder = "/orders"
+ placeTriggerOrder = "/conditional_orders"
+ modifyOrder = "/orders/%s/modify"
+ modifyOrderByClientID = "/orders/by_client_id/%s/modify"
+ modifyTriggerOrder = "/conditional_orders/%s/modify"
+ getOrderStatus = "/orders/"
+ getOrderStatusByClientID = "/orders/by_client_id/"
+ deleteOrder = "/orders/"
+ deleteOrderByClientID = "/orders/by_client_id/"
+ cancelTriggerOrder = "/conditional_orders/"
+ getFills = "/fills?"
+ getFundingPayments = "/funding_payments?"
+ getLeveragedTokens = "/lt/tokens"
+ getTokenInfo = "/lt/"
+ getLTBalances = "/lt/balances"
+ getLTCreations = "/lt/creations"
+ requestLTCreation = "/lt/%s/create"
+ getLTRedemptions = "/lt/redemptions"
+ requestLTRedemption = "/lt/%s/redeem"
+ getListQuotes = "/options/requests"
+ getMyQuotesRequests = "/options/my_requests"
+ createQuoteRequest = "/options/requests"
+ deleteQuote = "/options/requests/"
+ endpointQuote = "/options/requests/%s/quotes"
+ getMyQuotes = "/options/my_quotes"
+ deleteMyQuote = "/options/quotes/"
+ acceptQuote = "/options/quotes/%s/accept"
+ getOptionsInfo = "/options/account_info"
+ getOptionsPositions = "/options/positions"
+ getPublicOptionsTrades = "/options/trades"
+ getOptionsFills = "/options/fills"
+ requestOTCQuote = "/otc/quotes"
+ getOTCQuoteStatus = "/otc/quotes/"
+ acceptOTCQuote = "/otc/quotes/%s/accept"
+
+ // Other Consts
+ trailingStopOrderType = "trailingStop"
+ takeProfitOrderType = "takeProfit"
+ closedStatus = "closed"
+ spotString = "spot"
+ futuresString = "future"
+
+ ratePeriod = time.Second
+ rateLimit = 30
+)
+
+// GetMarkets gets market data
+func (f *FTX) GetMarkets() ([]MarketData, error) {
+ resp := struct {
+ Data []MarketData `json:"result"`
+ }{}
+ return resp.Data, f.SendHTTPRequest(ftxAPIURL+getMarkets, &resp)
+}
+
+// GetMarket gets market data for a provided asset type
+func (f *FTX) GetMarket(marketName string) (MarketData, error) {
+ resp := struct {
+ Data MarketData `json:"result"`
+ }{}
+ return resp.Data, f.SendHTTPRequest(ftxAPIURL+getMarket+marketName,
+ &resp)
+}
+
+// GetOrderbook gets orderbook for a given market with a given depth (default depth 20)
+func (f *FTX) GetOrderbook(marketName string, depth int64) (OrderbookData, error) {
+ result := struct {
+ Data TempOBData `json:"result"`
+ }{}
+ strDepth := strconv.FormatInt(depth, 10)
+ var resp OrderbookData
+ err := f.SendHTTPRequest(fmt.Sprintf(ftxAPIURL+getOrderbook, marketName, strDepth), &result)
+ if err != nil {
+ return resp, err
+ }
+ resp.MarketName = marketName
+ for x := range result.Data.Asks {
+ resp.Asks = append(resp.Asks, OData{Price: result.Data.Asks[x][0],
+ Size: result.Data.Asks[x][1],
+ })
+ }
+ for y := range result.Data.Bids {
+ resp.Bids = append(resp.Bids, OData{Price: result.Data.Bids[y][0],
+ Size: result.Data.Bids[y][1],
+ })
+ }
+ return resp, nil
+}
+
+// GetTrades gets trades based on the conditions specified
+func (f *FTX) GetTrades(marketName string, startTime, endTime time.Time, limit int64) ([]TradeData, error) {
+ strLimit := strconv.FormatInt(limit, 10)
+ params := url.Values{}
+ params.Set("limit", strLimit)
+ resp := struct {
+ Data []TradeData `json:"result"`
+ }{}
+ if !startTime.IsZero() && !endTime.IsZero() {
+ if startTime.After(endTime) {
+ return resp.Data, errors.New("startTime cannot be after endTime")
+ }
+ params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10))
+ params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10))
+ }
+ return resp.Data, f.SendHTTPRequest(fmt.Sprintf(ftxAPIURL+getTrades, marketName)+params.Encode(),
+ &resp)
+}
+
+// GetHistoricalData gets historical OHLCV data for a given market pair
+func (f *FTX) GetHistoricalData(marketName, timeInterval, limit string, startTime, endTime time.Time) ([]OHLCVData, error) {
+ resp := struct {
+ Data []OHLCVData `json:"result"`
+ }{}
+ params := url.Values{}
+ params.Set("resolution", timeInterval)
+ if limit != "" {
+ params.Set("limit", limit)
+ }
+ if !startTime.IsZero() && !endTime.IsZero() {
+ if startTime.After(endTime) {
+ return resp.Data, errors.New("startTime cannot be after endTime")
+ }
+ params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10))
+ params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10))
+ }
+ return resp.Data, f.SendHTTPRequest(fmt.Sprintf(ftxAPIURL+getHistoricalData, marketName)+params.Encode(), &resp)
+}
+
+// GetFutures gets data on futures
+func (f *FTX) GetFutures() ([]FuturesData, error) {
+ resp := struct {
+ Data []FuturesData `json:"result"`
+ }{}
+ return resp.Data, f.SendHTTPRequest(ftxAPIURL+getFutures, &resp)
+}
+
+// GetFuture gets data on a given future
+func (f *FTX) GetFuture(futureName string) (FuturesData, error) {
+ resp := struct {
+ Data FuturesData `json:"result"`
+ }{}
+ return resp.Data, f.SendHTTPRequest(ftxAPIURL+getFuture+futureName, &resp)
+}
+
+// GetFutureStats gets data on a given future's stats
+func (f *FTX) GetFutureStats(futureName string) (FutureStatsData, error) {
+ resp := struct {
+ Data FutureStatsData `json:"result"`
+ }{}
+ return resp.Data, f.SendHTTPRequest(fmt.Sprintf(ftxAPIURL+getFutureStats, futureName), &resp)
+}
+
+// GetFundingRates gets data on funding rates
+func (f *FTX) GetFundingRates() ([]FundingRatesData, error) {
+ resp := struct {
+ Data []FundingRatesData `json:"result"`
+ }{}
+ return resp.Data, f.SendHTTPRequest(ftxAPIURL+getFundingRates, &resp)
+}
+
+// GetIndexWeights gets index weights
+func (f *FTX) GetIndexWeights(index string) (IndexWeights, error) {
+ var resp IndexWeights
+ return resp, f.SendHTTPRequest(ftxAPIURL+fmt.Sprintf(getIndexWeights, index), &resp)
+}
+
+// SendHTTPRequest sends an unauthenticated HTTP request
+func (f *FTX) SendHTTPRequest(path string, result interface{}) error {
+ return f.SendPayload(context.Background(), &request.Item{
+ Method: http.MethodGet,
+ Path: path,
+ Result: result,
+ Verbose: f.Verbose,
+ HTTPDebugging: f.HTTPDebugging,
+ HTTPRecording: f.HTTPRecording,
+ })
+}
+
+// GetAccountInfo gets account info
+func (f *FTX) GetAccountInfo() (AccountInfoData, error) {
+ resp := struct {
+ Data AccountInfoData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getAccountInfo, nil, &resp)
+}
+
+// GetPositions gets the users positions
+func (f *FTX) GetPositions() ([]PositionData, error) {
+ resp := struct {
+ Data []PositionData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getPositions, nil, &resp)
+}
+
+// ChangeAccountLeverage changes default leverage used by account
+func (f *FTX) ChangeAccountLeverage(leverage float64) error {
+ req := make(map[string]interface{})
+ req["leverage"] = leverage
+ return f.SendAuthHTTPRequest(http.MethodPost, setLeverage, req, nil)
+}
+
+// GetCoins gets coins' data in the account wallet
+func (f *FTX) GetCoins() ([]WalletCoinsData, error) {
+ resp := struct {
+ Data []WalletCoinsData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getCoins, nil, &resp)
+}
+
+// GetBalances gets balances of the account
+func (f *FTX) GetBalances() ([]BalancesData, error) {
+ resp := struct {
+ Data []BalancesData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getBalances, nil, &resp)
+}
+
+// GetAllWalletBalances gets all wallets' balances
+func (f *FTX) GetAllWalletBalances() (AllWalletAccountData, error) {
+ resp := struct {
+ Data AllWalletAccountData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getAllWalletBalances, nil, &resp)
+}
+
+// FetchDepositAddress gets deposit address for a given coin
+func (f *FTX) FetchDepositAddress(coin string) (DepositData, error) {
+ resp := struct {
+ Data DepositData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getDepositAddress+coin, nil, &resp)
+}
+
+// FetchDepositHistory gets deposit history
+func (f *FTX) FetchDepositHistory() ([]TransactionData, error) {
+ resp := struct {
+ Data []TransactionData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getDepositHistory, nil, &resp)
+}
+
+// FetchWithdrawalHistory gets withdrawal history
+func (f *FTX) FetchWithdrawalHistory() ([]TransactionData, error) {
+ resp := struct {
+ Data []TransactionData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getWithdrawalHistory, nil, &resp)
+}
+
+// Withdraw sends a withdrawal request
+func (f *FTX) Withdraw(coin, address, tag, password, code string, size float64) (TransactionData, error) {
+ req := make(map[string]interface{})
+ req["coin"] = coin
+ req["address"] = address
+ req["size"] = size
+ if code != "" {
+ req["code"] = code
+ }
+ if tag != "" {
+ req["tag"] = tag
+ }
+ if password != "" {
+ req["password"] = password
+ }
+ resp := struct {
+ Data TransactionData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, withdrawRequest, req, &resp)
+}
+
+// GetOpenOrders gets open orders
+func (f *FTX) GetOpenOrders(marketName string) ([]OrderData, error) {
+ params := url.Values{}
+ if marketName != "" {
+ params.Set("market", marketName)
+ }
+ resp := struct {
+ Data []OrderData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOpenOrders+params.Encode(), nil, &resp)
+}
+
+// FetchOrderHistory gets order history
+func (f *FTX) FetchOrderHistory(marketName string, startTime, endTime time.Time, limit string) ([]OrderData, error) {
+ resp := struct {
+ Data []OrderData `json:"result"`
+ }{}
+ params := url.Values{}
+ if marketName != "" {
+ params.Set("market", marketName)
+ }
+ if !startTime.IsZero() && !endTime.IsZero() {
+ if startTime.After(endTime) {
+ return resp.Data, errors.New("startTime cannot be after endTime")
+ }
+ params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10))
+ params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10))
+ }
+ if limit != "" {
+ params.Set("limit", limit)
+ }
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOrderHistory+params.Encode(), nil, &resp)
+}
+
+// GetOpenTriggerOrders gets trigger orders that are currently open
+func (f *FTX) GetOpenTriggerOrders(marketName, orderType string) ([]TriggerOrderData, error) {
+ params := url.Values{}
+ if marketName != "" {
+ params.Set("market", marketName)
+ }
+ if orderType != "" {
+ params.Set("type", orderType)
+ }
+ resp := struct {
+ Data []TriggerOrderData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOpenTriggerOrders+params.Encode(), nil, &resp)
+}
+
+// GetTriggerOrderTriggers gets trigger orders that are currently open
+func (f *FTX) GetTriggerOrderTriggers(orderID string) ([]TriggerData, error) {
+ resp := struct {
+ Data []TriggerData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, fmt.Sprintf(getTriggerOrderTriggers, orderID), nil, &resp)
+}
+
+// GetTriggerOrderHistory gets trigger orders that are currently open
+func (f *FTX) GetTriggerOrderHistory(marketName string, startTime, endTime time.Time, side, orderType, limit string) ([]TriggerOrderData, error) {
+ resp := struct {
+ Data []TriggerOrderData `json:"result"`
+ }{}
+ params := url.Values{}
+ if marketName != "" {
+ params.Set("market", marketName)
+ }
+ if !startTime.IsZero() && !endTime.IsZero() {
+ if startTime.After(endTime) {
+ return resp.Data, errors.New("startTime cannot be after endTime")
+ }
+ params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10))
+ params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10))
+ }
+ if side != "" {
+ params.Set("side", side)
+ }
+ if orderType != "" {
+ params.Set("type", orderType)
+ }
+ if limit != "" {
+ params.Set("limit", limit)
+ }
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getTriggerOrderHistory+params.Encode(), nil, &resp)
+}
+
+// Order places an order
+func (f *FTX) Order(marketName, side, orderType, reduceOnly, ioc, postOnly, clientID string, price, size float64) (OrderData, error) {
+ req := make(map[string]interface{})
+ req["market"] = marketName
+ req["side"] = side
+ req["price"] = price
+ req["type"] = orderType
+ req["size"] = size
+ if reduceOnly != "" {
+ req["reduceOnly"] = reduceOnly
+ }
+ if ioc != "" {
+ req["ioc"] = ioc
+ }
+ if postOnly != "" {
+ req["postOnly"] = postOnly
+ }
+ if clientID != "" {
+ req["clientID"] = clientID
+ }
+ resp := struct {
+ Data OrderData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, placeOrder, req, &resp)
+}
+
+// TriggerOrder places an order
+func (f *FTX) TriggerOrder(marketName, side, orderType, reduceOnly, retryUntilFilled string, size, triggerPrice, orderPrice, trailValue float64) (TriggerOrderData, error) {
+ req := make(map[string]interface{})
+ req["market"] = marketName
+ req["side"] = side
+ req["type"] = orderType
+ req["size"] = size
+ if reduceOnly != "" {
+ req["reduceOnly"] = reduceOnly
+ }
+ if retryUntilFilled != "" {
+ req["retryUntilFilled"] = retryUntilFilled
+ }
+ if orderType == order.Stop.Lower() || orderType == "" {
+ req["triggerPrice"] = triggerPrice
+ req["orderPrice"] = orderPrice
+ }
+ if orderType == trailingStopOrderType {
+ req["trailValue"] = trailValue
+ }
+ if orderType == takeProfitOrderType {
+ req["triggerPrice"] = triggerPrice
+ req["orderPrice"] = orderPrice
+ }
+ resp := struct {
+ Data TriggerOrderData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, placeTriggerOrder, req, &resp)
+}
+
+// ModifyPlacedOrder modifies a placed order
+func (f *FTX) ModifyPlacedOrder(orderID, clientID string, price, size float64) (OrderData, error) {
+ req := make(map[string]interface{})
+ req["price"] = price
+ req["size"] = size
+ if clientID != "" {
+ req["clientID"] = clientID
+ }
+ resp := struct {
+ Data OrderData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(modifyOrder, orderID), req, &resp)
+}
+
+// ModifyOrderByClientID modifies a placed order via clientOrderID
+func (f *FTX) ModifyOrderByClientID(clientOrderID, clientID string, price, size float64) (OrderData, error) {
+ req := make(map[string]interface{})
+ req["price"] = price
+ req["size"] = size
+ if clientID != "" {
+ req["clientID"] = clientID
+ }
+ resp := struct {
+ Data OrderData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(modifyOrderByClientID, clientOrderID), req, &resp)
+}
+
+// ModifyTriggerOrder modifies an existing trigger order
+// Choices for ordertype include stop, trailingStop, takeProfit
+func (f *FTX) ModifyTriggerOrder(orderID, orderType string, size, triggerPrice, orderPrice, trailValue float64) (TriggerOrderData, error) {
+ req := make(map[string]interface{})
+ req["size"] = size
+ if orderType == order.Stop.Lower() || orderType == "" {
+ req["triggerPrice"] = triggerPrice
+ req["orderPrice"] = orderPrice
+ }
+ if orderType == trailingStopOrderType {
+ req["trailValue"] = trailValue
+ }
+ if orderType == takeProfitOrderType {
+ req["triggerPrice"] = triggerPrice
+ req["orderPrice"] = orderPrice
+ }
+ resp := struct {
+ Data TriggerOrderData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(modifyTriggerOrder, orderID), req, &resp)
+}
+
+// GetOrderStatus gets the order status of a given orderID
+func (f *FTX) GetOrderStatus(orderID string) (OrderData, error) {
+ resp := struct {
+ Data OrderData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOrderStatus+orderID, nil, &resp)
+}
+
+// GetOrderStatusByClientID gets the order status of a given clientOrderID
+func (f *FTX) GetOrderStatusByClientID(clientOrderID string) (OrderData, error) {
+ resp := struct {
+ Data OrderData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOrderStatusByClientID+clientOrderID, nil, &resp)
+}
+
+// DeleteOrder deletes an order
+func (f *FTX) DeleteOrder(orderID string) (string, error) {
+ resp := struct {
+ Data string `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, deleteOrder+orderID, nil, &resp)
+}
+
+// DeleteOrderByClientID deletes an order
+func (f *FTX) DeleteOrderByClientID(clientID string) (string, error) {
+ resp := struct {
+ Data string `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, deleteOrderByClientID+clientID, nil, &resp)
+}
+
+// DeleteTriggerOrder deletes an order
+func (f *FTX) DeleteTriggerOrder(orderID string) (string, error) {
+ resp := struct {
+ Data string `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodDelete, cancelTriggerOrder+orderID, nil, &resp)
+}
+
+// GetFills gets fills' data
+func (f *FTX) GetFills(market, limit string, startTime, endTime time.Time) ([]FillsData, error) {
+ resp := struct {
+ Data []FillsData `json:"result"`
+ }{}
+ params := url.Values{}
+ if market != "" {
+ params.Set("market", market)
+ }
+ if limit != "" {
+ params.Set("limit", limit)
+ }
+ if !startTime.IsZero() && !endTime.IsZero() {
+ if startTime.After(endTime) {
+ return resp.Data, errors.New("startTime cannot be after endTime")
+ }
+ params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10))
+ params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10))
+ }
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getFills+params.Encode(), nil, &resp)
+}
+
+// GetFundingPayments gets funding payments
+func (f *FTX) GetFundingPayments(startTime, endTime time.Time, future string) ([]FundingPaymentsData, error) {
+ resp := struct {
+ Data []FundingPaymentsData `json:"result"`
+ }{}
+ params := url.Values{}
+ if !startTime.IsZero() && !endTime.IsZero() {
+ if startTime.After(endTime) {
+ return resp.Data, errors.New("startTime cannot be after endTime")
+ }
+ params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10))
+ params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10))
+ }
+ if future != "" {
+ params.Set("future", future)
+ }
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getFundingPayments+params.Encode(), nil, &resp)
+}
+
+// ListLeveragedTokens lists leveraged tokens
+func (f *FTX) ListLeveragedTokens() ([]LeveragedTokensData, error) {
+ resp := struct {
+ Data []LeveragedTokensData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getLeveragedTokens, nil, &resp)
+}
+
+// GetTokenInfo gets token info
+func (f *FTX) GetTokenInfo(tokenName string) ([]LeveragedTokensData, error) {
+ resp := struct {
+ Data []LeveragedTokensData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getTokenInfo+tokenName, nil, &resp)
+}
+
+// ListLTBalances gets leveraged tokens' balances
+func (f *FTX) ListLTBalances() ([]LTBalanceData, error) {
+ resp := struct {
+ Data []LTBalanceData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getLTBalances, nil, &resp)
+}
+
+// ListLTCreations lists the leveraged tokens' creation requests
+func (f *FTX) ListLTCreations() ([]LTCreationData, error) {
+ resp := struct {
+ Data []LTCreationData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getLTCreations, nil, &resp)
+}
+
+// RequestLTCreation sends a request to create a leveraged token
+func (f *FTX) RequestLTCreation(tokenName string, size float64) (RequestTokenCreationData, error) {
+ req := make(map[string]interface{})
+ req["size"] = size
+ resp := struct {
+ Data RequestTokenCreationData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(requestLTCreation, tokenName), req, &resp)
+}
+
+// ListLTRedemptions lists the leveraged tokens' redemption requests
+func (f *FTX) ListLTRedemptions() ([]LTRedemptionData, error) {
+ resp := struct {
+ Data []LTRedemptionData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getLTRedemptions, nil, &resp)
+}
+
+// RequestLTRedemption sends a request to redeem a leveraged token
+func (f *FTX) RequestLTRedemption(tokenName string, size float64) (LTRedemptionRequestData, error) {
+ req := make(map[string]interface{})
+ req["size"] = size
+ resp := struct {
+ Data LTRedemptionRequestData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(requestLTRedemption, tokenName), req, &resp)
+}
+
+// GetQuoteRequests gets a list of quote requests
+func (f *FTX) GetQuoteRequests() ([]QuoteRequestData, error) {
+ resp := struct {
+ Data []QuoteRequestData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getListQuotes, nil, &resp)
+}
+
+// GetYourQuoteRequests gets a list of your quote requests
+func (f *FTX) GetYourQuoteRequests() ([]PersonalQuotesData, error) {
+ resp := struct {
+ Data []PersonalQuotesData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getMyQuotesRequests, nil, &resp)
+}
+
+// CreateQuoteRequest sends a request to create a quote
+func (f *FTX) CreateQuoteRequest(underlying, optionType, side string, expiry int64, requestExpiry string, strike, size, limitPrice, counterParyID float64, hideLimitPrice bool) (CreateQuoteRequestData, error) {
+ req := make(map[string]interface{})
+ req["underlying"] = underlying
+ req["type"] = optionType
+ req["side"] = side
+ req["strike"] = strike
+ req["expiry"] = expiry
+ req["size"] = size
+ if limitPrice != 0 {
+ req["limitPrice"] = limitPrice
+ }
+ if requestExpiry != "" {
+ req["requestExpiry"] = requestExpiry
+ }
+ if counterParyID != 0 {
+ req["counterParyID"] = counterParyID
+ }
+ req["hideLimitPrice"] = hideLimitPrice
+ resp := struct {
+ Data CreateQuoteRequestData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, createQuoteRequest, req, &resp)
+}
+
+// DeleteQuote sends request to cancel a quote
+func (f *FTX) DeleteQuote(requestID string) (CancelQuoteRequestData, error) {
+ resp := struct {
+ Data CancelQuoteRequestData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodDelete, deleteQuote+requestID, nil, &resp)
+}
+
+// GetQuotesForYourQuote gets a list of quotes for your quote
+func (f *FTX) GetQuotesForYourQuote(requestID string) (QuoteForQuoteData, error) {
+ var resp QuoteForQuoteData
+ return resp, f.SendAuthHTTPRequest(http.MethodGet, fmt.Sprintf(endpointQuote, requestID), nil, &resp)
+}
+
+// MakeQuote makes a quote for a quote
+func (f *FTX) MakeQuote(requestID, price string) ([]QuoteForQuoteData, error) {
+ params := url.Values{}
+ params.Set("price", price)
+ resp := struct {
+ Data []QuoteForQuoteData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(endpointQuote, requestID), nil, &resp)
+}
+
+// MyQuotes gets a list of my quotes for quotes
+func (f *FTX) MyQuotes() ([]QuoteForQuoteData, error) {
+ resp := struct {
+ Data []QuoteForQuoteData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getMyQuotes, nil, &resp)
+}
+
+// DeleteMyQuote deletes my quote for quotes
+func (f *FTX) DeleteMyQuote(quoteID string) ([]QuoteForQuoteData, error) {
+ resp := struct {
+ Data []QuoteForQuoteData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodDelete, deleteMyQuote+quoteID, nil, &resp)
+}
+
+// AcceptQuote accepts the quote for quote
+func (f *FTX) AcceptQuote(quoteID string) ([]QuoteForQuoteData, error) {
+ resp := struct {
+ Data []QuoteForQuoteData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(acceptQuote, quoteID), nil, &resp)
+}
+
+// GetAccountOptionsInfo gets account's options' info
+func (f *FTX) GetAccountOptionsInfo() (AccountOptionsInfoData, error) {
+ resp := struct {
+ Data AccountOptionsInfoData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOptionsInfo, nil, &resp)
+}
+
+// GetOptionsPositions gets options' positions
+func (f *FTX) GetOptionsPositions() ([]OptionsPositionsData, error) {
+ resp := struct {
+ Data []OptionsPositionsData `json:"result"`
+ }{}
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOptionsPositions, nil, &resp)
+}
+
+// GetPublicOptionsTrades gets options' trades from public
+func (f *FTX) GetPublicOptionsTrades(startTime, endTime time.Time, limit string) ([]OptionsTradesData, error) {
+ resp := struct {
+ Data []OptionsTradesData `json:"result"`
+ }{}
+ req := make(map[string]interface{})
+ if !startTime.IsZero() && !endTime.IsZero() {
+ req["start_time"] = strconv.FormatInt(startTime.Unix(), 10)
+ req["end_time"] = strconv.FormatInt(endTime.Unix(), 10)
+ if startTime.After(endTime) {
+ return resp.Data, errors.New("startTime cannot be after endTime")
+ }
+ }
+ if limit != "" {
+ req["limit"] = limit
+ }
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getPublicOptionsTrades, req, &resp)
+}
+
+// GetOptionsFills gets fills data for options
+func (f *FTX) GetOptionsFills(startTime, endTime time.Time, limit string) ([]OptionFillsData, error) {
+ resp := struct {
+ Data []OptionFillsData `json:"result"`
+ }{}
+ req := make(map[string]interface{})
+ if !startTime.IsZero() && !endTime.IsZero() {
+ req["start_time"] = strconv.FormatInt(startTime.Unix(), 10)
+ req["end_time"] = strconv.FormatInt(endTime.Unix(), 10)
+ if startTime.After(endTime) {
+ return resp.Data, errors.New("startTime cannot be after endTime")
+ }
+ }
+ if limit != "" {
+ req["limit"] = limit
+ }
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOptionsFills, req, &resp)
+}
+
+// SendAuthHTTPRequest sends an authenticated request
+func (f *FTX) SendAuthHTTPRequest(method, path string, data, result interface{}) error {
+ ts := strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
+ var body io.Reader
+ var hmac, payload []byte
+ var err error
+ if data != nil {
+ payload, err = json.Marshal(data)
+ if err != nil {
+ return err
+ }
+ body = bytes.NewBuffer(payload)
+ sigPayload := ts + method + "/api" + path + string(payload)
+ hmac = crypto.GetHMAC(crypto.HashSHA256, []byte(sigPayload), []byte(f.API.Credentials.Secret))
+ } else {
+ sigPayload := ts + method + "/api" + path
+ hmac = crypto.GetHMAC(crypto.HashSHA256, []byte(sigPayload), []byte(f.API.Credentials.Secret))
+ }
+ headers := make(map[string]string)
+ headers["FTX-KEY"] = f.API.Credentials.Key
+ headers["FTX-SIGN"] = crypto.HexEncodeToString(hmac)
+ headers["FTX-TS"] = ts
+ headers["Content-Type"] = "application/json"
+ return f.SendPayload(context.Background(), &request.Item{
+ Method: method,
+ Path: ftxAPIURL + path,
+ Headers: headers,
+ Body: body,
+ Result: result,
+ AuthRequest: true,
+ Verbose: f.Verbose,
+ HTTPDebugging: f.HTTPDebugging,
+ HTTPRecording: f.HTTPRecording,
+ })
+}
+
+// GetFee returns an estimate of fee based on type of transaction
+func (f *FTX) GetFee(feeBuilder *exchange.FeeBuilder) (float64, error) {
+ var fee float64
+ switch feeBuilder.FeeType {
+ case exchange.OfflineTradeFee:
+ fee = getOfflineTradeFee(feeBuilder)
+ default:
+ feeData, err := f.GetAccountInfo()
+ if err != nil {
+ return 0, err
+ }
+ switch feeBuilder.IsMaker {
+ case true:
+ fee = feeData.MakerFee * feeBuilder.Amount * feeBuilder.PurchasePrice
+ case false:
+ fee = feeData.TakerFee * feeBuilder.Amount * feeBuilder.PurchasePrice
+ }
+ if fee < 0 {
+ fee = 0
+ }
+ }
+ return fee, nil
+}
+
+// getOfflineTradeFee calculates the worst case-scenario trading fee
+func getOfflineTradeFee(feeBuilder *exchange.FeeBuilder) float64 {
+ if feeBuilder.IsMaker {
+ return 0.0002 * feeBuilder.PurchasePrice * feeBuilder.Amount
+ }
+ return 0.0007 * feeBuilder.PurchasePrice * feeBuilder.Amount
+}
+
+func parseInterval(in time.Duration) (TimeInterval, error) {
+ switch in {
+ case kline.FifteenSecond:
+ return TimeIntervalFifteenSeconds, nil
+ case kline.OneMin:
+ return TimeIntervalMinute, nil
+ case kline.FiveMin:
+ return TimeIntervalFiveMinutes, nil
+ case kline.FifteenMin:
+ return TimeIntervalFifteenMinutes, nil
+ case kline.OneHour:
+ return TimeIntervalHour, nil
+ case kline.FourHour:
+ return TimeIntervalFourHours, nil
+ case kline.OneDay:
+ return TimeIntervalDay, nil
+ default:
+ return TimeIntervalMinute, errInvalidInterval
+ }
+}
+
+func (f *FTX) compatibleOrderVars(orderSide, orderStatus, orderType string, amount, filledAmount, avgFillPrice float64) (OrderVars, error) {
+ var resp OrderVars
+ switch orderSide {
+ case order.Buy.Lower():
+ resp.Side = order.Buy
+ case order.Sell.Lower():
+ resp.Side = order.Sell
+ }
+ switch orderStatus {
+ case strings.ToLower(order.New.String()):
+ resp.Status = order.New
+ case strings.ToLower(order.Open.String()):
+ resp.Status = order.Open
+ case closedStatus:
+ if filledAmount != 0 && filledAmount != amount {
+ resp.Status = order.PartiallyCancelled
+ }
+ if filledAmount == 0 {
+ resp.Status = order.Cancelled
+ }
+ if filledAmount == amount {
+ resp.Status = order.Filled
+ }
+ }
+ var feeBuilder exchange.FeeBuilder
+ feeBuilder.PurchasePrice = avgFillPrice
+ feeBuilder.Amount = amount
+ resp.OrderType = order.Market
+ if strings.EqualFold(orderType, order.Limit.String()) {
+ resp.OrderType = order.Limit
+ feeBuilder.IsMaker = true
+ }
+ fee, err := f.GetFee(&feeBuilder)
+ if err != nil {
+ return resp, err
+ }
+ resp.Fee = fee
+ return resp, nil
+}
+
+// RequestForQuotes requests for otc quotes
+func (f *FTX) RequestForQuotes(base, quote string, amount float64) (RequestQuoteData, error) {
+ resp := struct {
+ Data RequestQuoteData `json:"result"`
+ }{}
+ req := make(map[string]interface{})
+ req["fromCoin"] = base
+ req["toCoin"] = quote
+ req["size"] = amount
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, requestOTCQuote, req, &resp)
+}
+
+// GetOTCQuoteStatus gets quote status of a quote
+func (f *FTX) GetOTCQuoteStatus(marketName, quoteID string) ([]QuoteStatusData, error) {
+ resp := struct {
+ Data []QuoteStatusData `json:"result"`
+ }{}
+ params := url.Values{}
+ params.Set("market", marketName)
+ return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOTCQuoteStatus+quoteID, params, &resp)
+}
+
+// AcceptOTCQuote requests for otc quotes
+func (f *FTX) AcceptOTCQuote(quoteID string) error {
+ return f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(acceptOTCQuote, quoteID), nil, nil)
+}
diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go
new file mode 100644
index 00000000..1eabcf8f
--- /dev/null
+++ b/exchanges/ftx/ftx_test.go
@@ -0,0 +1,1188 @@
+package ftx
+
+import (
+ "log"
+ "os"
+ "reflect"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/thrasher-corp/gocryptotrader/config"
+ "github.com/thrasher-corp/gocryptotrader/core"
+ "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/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 to do authenticated endpoint testing
+const (
+ apiKey = ""
+ apiSecret = ""
+ canManipulateRealOrders = false
+ spotPair = "FTT/BTC"
+ futuresPair = "LEO-0327"
+ testToken = "ADAMOON"
+ btcusd = "BTC/USD"
+)
+
+var f FTX
+
+func TestMain(m *testing.M) {
+ f.SetDefaults()
+ cfg := config.GetConfig()
+ err := cfg.LoadConfig("../../testdata/configtest.json", true)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ exchCfg, err := cfg.GetExchangeConfig("FTX")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ exchCfg.API.AuthenticatedSupport = true
+ exchCfg.API.AuthenticatedWebsocketSupport = true
+ exchCfg.API.Credentials.Key = apiKey
+ exchCfg.API.Credentials.Secret = apiSecret
+
+ err = f.Setup(exchCfg)
+ if err != nil {
+ log.Fatal(err)
+ }
+ f.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
+ f.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride()
+ os.Exit(m.Run())
+}
+
+func areTestAPIKeysSet() bool {
+ return f.ValidateAPICredentials()
+}
+
+// Implement tests for API endpoints below
+
+func TestGetMarkets(t *testing.T) {
+ t.Parallel()
+ _, err := f.GetMarkets()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetMarket(t *testing.T) {
+ t.Parallel()
+ _, err := f.GetMarket(spotPair)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetOrderbook(t *testing.T) {
+ t.Parallel()
+ _, err := f.GetOrderbook(spotPair, 5)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetTrades(t *testing.T) {
+ t.Parallel()
+ _, err := f.GetTrades(spotPair, time.Time{}, time.Time{}, 200)
+ if err != nil {
+ t.Error(err)
+ }
+ _, err = f.GetTrades(spotPair, time.Time{}, time.Time{}, 5)
+ if err != nil {
+ t.Error(err)
+ }
+ _, err = f.GetTrades(spotPair, time.Unix(1559901511, 0), time.Unix(1559881511, 0), 5)
+ if err == nil {
+ t.Error(err)
+ }
+}
+
+func TestGetHistoricalData(t *testing.T) {
+ t.Parallel()
+ _, err := f.GetHistoricalData(spotPair, "86400", "5", time.Time{}, time.Time{})
+ if err != nil {
+ t.Error(err)
+ }
+ _, err = f.GetHistoricalData(spotPair, "86400", "5", time.Unix(1559881511, 0), time.Unix(1559901511, 0))
+ if err != nil {
+ t.Error(err)
+ }
+ _, err = f.GetHistoricalData(spotPair, "86400", "5", time.Unix(1559901511, 0), time.Unix(1559881511, 0))
+ if err == nil {
+ t.Error(err)
+ }
+}
+
+func TestGetFutures(t *testing.T) {
+ t.Parallel()
+ _, err := f.GetFutures()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetFuture(t *testing.T) {
+ t.Parallel()
+ _, err := f.GetFuture(futuresPair)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetFutureStats(t *testing.T) {
+ t.Parallel()
+ _, err := f.GetFutureStats("BTC-PERP")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetFundingRates(t *testing.T) {
+ t.Parallel()
+ _, err := f.GetFundingRates()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetAccountInfo(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.GetAccountInfo()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetPositions(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.GetPositions()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestChangeAccountLeverage(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly")
+ }
+ err := f.ChangeAccountLeverage(50)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetCoins(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.GetCoins()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetBalances(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.GetBalances()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetAllWalletBalances(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.GetAllWalletBalances()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestFetchDepositAddress(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.FetchDepositAddress("TUSD")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestFetchDepositHistory(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.FetchDepositHistory()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestFetchWithdrawalHistory(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.FetchWithdrawalHistory()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestWithdraw(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly")
+ }
+ _, err := f.Withdraw("BTC", core.BitcoinDonationAddress, "", "", "957378", 0.0009)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetOpenOrders(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.GetOpenOrders(spotPair)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestFetchOrderHistory(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.FetchOrderHistory(spotPair, time.Time{}, time.Time{}, "2")
+ if err != nil {
+ t.Error(err)
+ }
+ _, err = f.FetchOrderHistory(spotPair, time.Unix(1559881511, 0), time.Unix(1559901511, 0), "2")
+ if err != nil {
+ t.Error(err)
+ }
+ _, err = f.FetchOrderHistory(spotPair, time.Unix(1559901511, 0), time.Unix(1559881511, 0), "2")
+ if err == nil {
+ t.Error(err)
+ }
+}
+
+func TestGetOpenTriggerOrders(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.GetOpenTriggerOrders(spotPair, "")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetTriggerOrderTriggers(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.GetTriggerOrderTriggers("1031")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetTriggerOrderHistory(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.GetTriggerOrderHistory(spotPair, time.Time{}, time.Time{}, order.Buy.Lower(), "stop", "1")
+ if err != nil {
+ t.Error(err)
+ }
+ _, err = f.GetTriggerOrderHistory(spotPair, time.Unix(1559881511, 0), time.Unix(1559901511, 0), order.Buy.Lower(), "stop", "1")
+ if err != nil {
+ t.Error(err)
+ }
+ _, err = f.GetTriggerOrderHistory(spotPair, time.Unix(1559901511, 0), time.Unix(1559881511, 0), order.Buy.Lower(), "stop", "1")
+ if err == nil {
+ t.Error(err)
+ }
+}
+
+func TestOrder(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly")
+ }
+ _, err := f.Order(spotPair, order.Buy.Lower(), "limit", "", "", "", "", 0.0001, 500)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestTriggerOrder(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly")
+ }
+ _, err := f.TriggerOrder(spotPair, order.Buy.Lower(), order.Stop.Lower(), "", "", 500, 0.0004, 0.0001, 0)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestDeleteOrder(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly")
+ }
+ _, err := f.DeleteOrder("1031")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestDeleteOrderByClientID(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly")
+ }
+ _, err := f.DeleteOrderByClientID("clientID123")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestDeleteTriggerOrder(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly")
+ }
+ _, err := f.DeleteTriggerOrder("1031")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetFills(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.GetFills(spotPair, "", time.Time{}, time.Time{})
+ if err != nil {
+ t.Error(err)
+ }
+ _, err = f.GetFills(spotPair, "", time.Unix(1559881511, 0), time.Unix(1559901511, 0))
+ if err != nil {
+ t.Error(err)
+ }
+ _, err = f.GetFills(spotPair, "", time.Unix(1559901511, 0), time.Unix(1559881511, 0))
+ if err == nil {
+ t.Error(err)
+ }
+}
+
+func TestGetFundingPayments(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.GetFundingPayments(time.Unix(1559881511, 0), time.Unix(1559901511, 0), "")
+ if err != nil {
+ t.Error(err)
+ }
+ _, err = f.GetFundingPayments(time.Unix(1559901511, 0), time.Unix(1559881511, 0), "")
+ if err == nil {
+ t.Error(err)
+ }
+}
+
+func TestListLeveragedTokens(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.ListLeveragedTokens()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetTokenInfo(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.GetTokenInfo("")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestListLTBalances(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.ListLTBalances()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestListLTCreations(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.ListLTCreations()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestRequestLTCreation(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.RequestLTCreation(testToken, 1)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestListLTRedemptions(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.ListLTRedemptions()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetQuoteRequests(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.GetQuoteRequests()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetYourQuoteRequests(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.GetYourQuoteRequests()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestCreateQuoteRequest(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly")
+ }
+ _, err := f.CreateQuoteRequest(strings.ToUpper(currency.BTC.String()), "call", order.Buy.Lower(), 1593140400, "", 10, 10, 5, 0, false)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestDeleteQuote(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly")
+ }
+ _, err := f.DeleteQuote("1031")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetQuotesForYourQuote(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.GetQuotesForYourQuote("1031")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestMakeQuote(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly")
+ }
+ _, err := f.MakeQuote("1031", "5")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestMyQuotes(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.MyQuotes()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestDeleteMyQuote(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly")
+ }
+ _, err := f.DeleteMyQuote("1031")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestAcceptQuote(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly")
+ }
+ _, err := f.AcceptQuote("1031")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetAccountOptionsInfo(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.GetAccountOptionsInfo()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetOptionsPositions(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.GetOptionsPositions()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetPublicOptionsTrades(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.GetPublicOptionsTrades(time.Time{}, time.Time{}, "5")
+ if err != nil {
+ t.Error(err)
+ }
+ _, err = f.GetPublicOptionsTrades(time.Unix(1559881511, 0), time.Unix(1559901511, 0), "5")
+ if err != nil {
+ t.Error(err)
+ }
+ _, err = f.GetPublicOptionsTrades(time.Unix(1559901511, 0), time.Unix(1559881511, 0), "5")
+ if err == nil {
+ t.Error(err)
+ }
+}
+
+func TestGetOptionsFills(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip()
+ }
+ _, err := f.GetOptionsFills(time.Time{}, time.Time{}, "5")
+ if err != nil {
+ t.Error(err)
+ }
+ _, err = f.GetOptionsFills(time.Unix(1559881511, 0), time.Unix(1559901511, 0), "5")
+ if err != nil {
+ t.Error(err)
+ }
+ _, err = f.GetOptionsFills(time.Unix(1559901511, 0), time.Unix(1559881511, 0), "5")
+ if err == nil {
+ t.Error(err)
+ }
+}
+
+func TestUpdateOrderbook(t *testing.T) {
+ t.Parallel()
+ cp := currency.NewPairWithDelimiter(currency.BTC.String(), currency.USDT.String(), "/")
+ _, err := f.UpdateOrderbook(cp, asset.Spot)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestUpdateTicker(t *testing.T) {
+ t.Parallel()
+ cp := currency.NewPairWithDelimiter(currency.BTC.String(), currency.USDT.String(), "/")
+ _, err := f.UpdateTicker(cp, asset.Spot)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetActiveOrders(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip("API keys required but not set, skipping test")
+ }
+ var orderReq order.GetOrdersRequest
+ cp := currency.NewPairWithDelimiter(currency.BTC.String(), currency.USDT.String(), "/")
+ orderReq.Pairs = append(orderReq.Pairs, cp)
+ _, err := f.GetActiveOrders(&orderReq)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestGetOrderHistory(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip("API keys required but not set, skipping test")
+ }
+ var orderReq order.GetOrdersRequest
+ cp := currency.NewPairWithDelimiter(currency.BTC.String(), currency.USDT.String(), "/")
+ orderReq.Pairs = append(orderReq.Pairs, cp)
+ _, err := f.GetOrderHistory(&orderReq)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestUpdateAccountHoldings(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip("API keys required but not set, skipping test")
+ }
+ _, err := f.UpdateAccountInfo()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestFetchAccountInfo(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip("API keys required but not set, skipping test")
+ }
+ _, err := f.FetchAccountInfo()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetFee(t *testing.T) {
+ t.Parallel()
+ var x exchange.FeeBuilder
+ x.PurchasePrice = 10
+ x.Amount = 1
+ x.IsMaker = true
+ var a float64
+ var err error
+ if areTestAPIKeysSet() {
+ a, err = f.GetFee(&x)
+ if err != nil {
+ t.Error(err)
+ }
+ if a != 0.0039 {
+ t.Errorf("incorrect maker fee value")
+ }
+ }
+ x.IsMaker = false
+ if areTestAPIKeysSet() {
+ a, err = f.GetFee(&x)
+ if err != nil {
+ t.Error(err)
+ }
+ if a != 0.00865 {
+ t.Errorf("incorrect taker fee value")
+ }
+ }
+ x.FeeType = exchange.OfflineTradeFee
+ a, err = f.GetFee(&x)
+ if err != nil {
+ t.Error(err)
+ }
+ if a != 0.007 {
+ t.Errorf("incorrect offline taker fee value")
+ }
+ x.IsMaker = true
+ a, err = f.GetFee(&x)
+ if err != nil {
+ t.Error(err)
+ }
+ if a != 0.002 {
+ t.Errorf("incorrect offline maker fee value")
+ }
+}
+
+func TestGetOfflineTradingFee(t *testing.T) {
+ t.Parallel()
+ var f exchange.FeeBuilder
+ f.PurchasePrice = 10
+ f.Amount = 1
+ f.IsMaker = true
+ fee := getOfflineTradeFee(&f)
+ if fee != 0.002 {
+ t.Errorf("incorrect offline maker fee")
+ }
+ f.IsMaker = false
+ fee = getOfflineTradeFee(&f)
+ if fee != 0.007 {
+ t.Errorf("incorrect offline taker fee")
+ }
+}
+
+func TestGetOrderStatus(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip("API keys required but not set, skipping test")
+ }
+ _, err := f.GetOrderStatus("1031")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetOrderStatusByClientID(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip("API keys required but not set, skipping test")
+ }
+ _, err := f.GetOrderStatusByClientID("testID")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestRequestLTRedemption(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip("API keys required but not set, skipping test")
+ }
+ _, err := f.RequestLTRedemption("ETHBULL", 5)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestWithdrawCryptocurrencyFunds(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly")
+ }
+ var request = new(withdraw.Request)
+ request.Amount = 5
+ request.Currency = currency.NewCode("FTT")
+ var cryptoData withdraw.CryptoRequest
+ cryptoData.Address = "testaddress123"
+ cryptoData.AddressTag = "testtag123"
+ request.Crypto = &cryptoData
+ request.OneTimePassword = 123456
+ request.TradePassword = "incorrectTradePassword"
+ _, err := f.WithdrawCryptocurrencyFunds(request)
+ 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 := f.GetDepositAddress(currency.NewCode("FTT"), "")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetFundingHistory(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip("API keys required but not set, skipping test")
+ }
+ _, err := f.GetFundingHistory()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetHistoricCandles(t *testing.T) {
+ t.Parallel()
+ currencyPair := currency.NewPairFromString(spotPair)
+ start := time.Date(2019, 11, 12, 0, 0, 0, 0, time.UTC)
+ end := start.AddDate(0, 0, 5)
+ _, err := f.GetHistoricCandles(currencyPair, asset.Spot, start, end, kline.OneDay)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestParsingWSFillData(t *testing.T) {
+ t.Parallel()
+ data := []byte(`{
+ "channel": "fills",
+ "data": {
+ "fee": 78.05799225,
+ "feeRate": 0.0014,
+ "future": "BTC-PERP",
+ "id": 7828307,
+ "liquidity": "taker",
+ "market": "BTC-PERP",
+ "orderId": 38065410,
+ "tradeId": 19129310,
+ "price": 3723.75,
+ "side": "buy",
+ "size": 14.973,
+ "time": "2019-05-07T16:40:58.358438+00:00",
+ "type": "order"
+ },
+ "type": "update"
+ }`)
+ err := f.wsHandleData(data)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestParsingOrders(t *testing.T) {
+ t.Parallel()
+ data := []byte(`{
+ "channel": "fills",
+ "data": {
+ "id": 24852229,
+ "clientId": null,
+ "market": "XRP-PERP",
+ "type": "limit",
+ "side": "buy",
+ "size": 42353.0,
+ "price": 0.2977,
+ "reduceOnly": false,
+ "ioc": false,
+ "postOnly": false,
+ "status": "closed",
+ "filledSize": 0.0,
+ "remainingSize": 0.0,
+ "avgFillPrice": 0.2978
+ },
+ "type": "update"
+ }`)
+ err := f.wsHandleData(data)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestParsingWSTradesData(t *testing.T) {
+ t.Parallel()
+ data := []byte(`{
+ "channel": "trades",
+ "market": "BTC-PERP",
+ "type": "update",
+ "data": [
+ {
+ "id": 44200173,
+ "price": 9761.0,
+ "size": 0.0008,
+ "side": "buy",
+ "liquidation": false,
+ "time": "2020-05-15T01:10:04.369194+00:00"
+ }
+ ]
+ }`)
+ err := f.wsHandleData(data)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestParsingWSTickerData(t *testing.T) {
+ t.Parallel()
+ data := []byte(`{
+ "channel": "ticker",
+ "market": "BTC-PERP",
+ "type": "update",
+ "data": {
+ "bid": 9760.5,
+ "ask": 9761.0,
+ "bidSize": 3.36,
+ "askSize": 71.8484,
+ "last": 9761.0,
+ "time": 1589505004.4237103
+ }
+ }`)
+ err := f.wsHandleData(data)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestParsingWSOrdersData(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip("API keys required but not set, skipping test")
+ }
+ data := []byte(`{
+ "channel": "orders",
+ "data": {
+ "id": 24852229,
+ "clientId": null,
+ "market": "BTC-PERP",
+ "type": "limit",
+ "side": "buy",
+ "size": 42353.0,
+ "price": 0.2977,
+ "reduceOnly": false,
+ "ioc": false,
+ "postOnly": false,
+ "status": "closed",
+ "filledSize": 0.0,
+ "remainingSize": 0.0,
+ "avgFillPrice": 0.2978
+ },
+ "type": "update"
+ }`)
+ err := f.wsHandleData(data)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestParsingMarketsData(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip("API keys required but not set, skipping test")
+ }
+ data := []byte(`{"channel": "markets",
+ "type": "partial",
+ "data": {
+ "ADA-0626": {
+ "name": "ADA-0626",
+ "enabled": true,
+ "priceIncrement": 5e-06,
+ "sizeIncrement": 1.0,
+ "type": "future",
+ "baseCurrency": null,
+ "quoteCurrency": null,
+ "restricted": false,
+ "underlying": "ADA",
+ "future": {
+ "name": "ADA-0626",
+ "underlying": "ADA",
+ "description": "Cardano June 2020 Futures",
+ "type": "future", "expiry": "2020-06-26T003:00:00+00:00",
+ "perpetual": false,
+ "expired": false,
+ "enabled": true,
+ "postOnly": false,
+ "imfFactor": 4e-05,
+ "underlyingDescription": "Cardano",
+ "expiryDescription": "June 2020",
+ "moveStart": null, "positionLimitWeight": 10.0,
+ "group": "quarterly"}}},
+ "action": "partial"
+ }`)
+ err := f.wsHandleData(data)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestParsingWSOBData(t *testing.T) {
+ data := []byte(`{"channel": "orderbook", "market": "BTC-PERP", "type": "partial", "data": {"time": 1589855831.4606245, "checksum": 225973019, "bids": [[9602.0, 3.2903], [9601.5, 3.11], [9601.0, 2.1356], [9600.5, 3.0991], [9600.0, 8.014], [9599.5, 4.1571], [9599.0, 79.1846], [9598.5, 3.099], [9598.0, 3.985], [9597.5, 3.999], [9597.0, 16.4335], [9596.5, 4.006], [9596.0, 3.2596], [9595.0, 6.334], [9594.0, 3.5685], [9593.0, 14.2717], [9592.5, 0.5], [9591.0, 2.181], [9590.5, 40.4246], [9590.0, 1.0], [9589.0, 1.357], [9588.5, 0.4738], [9587.5, 0.15], [9587.0, 16.811], [9586.5, 1.2], [9586.0, 0.2], [9585.5, 1.0], [9584.5, 0.002], [9584.0, 1.51], [9583.5, 0.01], [9583.0, 1.4], [9582.5, 0.1], [9582.0, 24.7921], [9581.0, 2.087], [9580.5, 2.0], [9580.0, 0.1], [9579.0, 1.1588], [9578.0, 0.9477], [9577.5, 22.216], [9576.0, 0.2], [9574.0, 22.0], [9573.5, 1.0], [9572.0, 0.203], [9570.0, 0.1026], [9565.5, 5.5332], [9565.0, 27.5243], [9563.5, 2.6], [9562.0, 0.0175], [9561.0, 2.0085], [9552.0, 1.6], [9550.5, 27.3399], [9550.0, 0.1046], [9548.0, 0.0175], [9544.0, 4.8197], [9542.5, 26.5754], [9542.0, 0.003], [9541.0, 0.0549], [9540.0, 0.1984], [9537.5, 0.0008], [9535.5, 0.0105], [9535.0, 1.514], [9534.5, 36.5858], [9532.5, 4.7798], [9531.0, 40.6564], [9525.0, 0.001], [9523.5, 1.6], [9522.0, 0.0894], [9521.0, 0.315], [9520.5, 5.4525], [9520.0, 0.07], [9518.0, 0.034], [9517.5, 4.0], [9513.0, 0.0175], [9512.5, 15.6016], [9512.0, 32.7882], [9511.5, 0.0482], [9510.5, 0.0482], [9510.0, 0.2999], [9509.0, 2.0], [9508.5, 0.0482], [9506.0, 0.0416], [9505.5, 0.0492], [9505.0, 0.2], [9502.5, 0.01], [9502.0, 0.01], [9501.5, 0.0592], [9501.0, 0.001], [9500.0, 3.4913], [9499.5, 39.8683], [9498.0, 4.6108], [9497.0, 0.0481], [9492.0, 41.3559], [9490.0, 1.1104], [9488.0, 0.0105], [9486.0, 5.4443], [9485.5, 0.0482], [9484.0, 4.0], [9482.0, 0.25], [9481.5, 2.0], [9481.0, 8.1572]], "asks": [[9602.5, 3.0], [9603.0, 2.8979], [9603.5, 54.49], [9604.0, 5.9982], [9604.5, 3.028], [9605.0, 4.657], [9606.5, 5.2512], [9607.0, 4.003], [9607.5, 4.011], [9608.0, 13.7505], [9608.5, 3.994], [9609.0, 2.974], [9609.5, 3.002], [9612.0, 10.298], [9612.5, 13.455], [9613.5, 3.013], [9614.0, 2.02], [9614.5, 3.359], [9615.0, 21.2429], [9616.0, 0.5], [9616.5, 0.01], [9617.0, 2.182], [9617.5, 23.0223], [9618.0, 0.0623], [9618.5, 1.5795], [9619.0, 0.3065], [9620.0, 3.9], [9621.0, 1.5], [9622.0, 1.5], [9622.5, 1.216], [9625.0, 1.0], [9625.5, 0.9477], [9626.0, 0.05], [9628.5, 1.1588], [9629.0, 1.4], [9630.0, 4.2332], [9630.5, 1.228], [9631.0, 1.5], [9631.5, 0.0104], [9632.5, 26.7529], [9633.0, 0.25], [9638.0, 1.0], [9640.0, 0.2], [9641.0, 1.001], [9642.0, 0.0175], [9643.0, 0.25], [9643.5, 1.6], [9644.0, 31.4166], [9646.5, 41.6609], [9649.5, 0.2], [9653.5, 1.5], [9656.5, 1.6], [9657.0, 0.2], [9658.0, 1.5], [9659.5, 4.7804], [9660.5, 43.3405], [9665.5, 40.6564], [9670.0, 0.1034], [9671.5, 4.9098], [9674.0, 0.25], [9678.0, 15.6016], [9678.5, 1.5], [9681.0, 34.9683], [9683.0, 0.2], [9683.5, 5.3845], [9684.5, 5.087], [9685.0, 0.1032], [9686.5, 0.0075], [9689.0, 1.6], [9691.0, 34.7472], [9692.0, 0.001], [9694.0, 0.5], [9695.0, 0.0109], [9696.5, 4.825], [9700.0, 1.0595], [9701.5, 2.0], [9702.0, 0.011], [9702.5, 0.01], [9706.0, 1.2], [9708.0, 0.0175], [9710.0, 39.153], [9712.0, 48.6163], [9712.5, 1.5], [9713.0, 8.1572], [9715.5, 0.5021], [9716.5, 2.0], [9719.0, 0.0245], [9721.0, 0.5], [9724.0, 0.251], [9726.0, 0.12], [9727.5, 0.5075], [9730.0, 0.015], [9732.0, 58.5394], [9733.0, 0.001], [9734.0, 20.0], [9743.0, 0.06], [9750.0, 9.5], [9755.0, 52.4404], [9757.0, 48.6121], [9764.0, 0.015]], "action": "partial"}}`)
+ err := f.wsHandleData(data)
+ if err != nil {
+ t.Error(err)
+ }
+ data = []byte(`{"channel": "orderbook", "market": "BTC-PERP", "type": "update", "data": {"time": 1589855831.5128105, "checksum": 365946911, "bids": [[9596.0, 4.2656], [9512.0, 32.7912]], "asks": [[9613.5, 4.012], [9702.0, 0.021]], "action": "update"}}`)
+ err = f.wsHandleData(data)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetOTCQuoteStatus(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip("API keys required but not set, skipping test")
+ }
+ _, err := f.GetOTCQuoteStatus(btcusd, "1")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestRequestForQuotes(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly")
+ }
+ _, err := f.RequestForQuotes("BTC", "USD", 0.5)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestAcceptOTCQuote(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly")
+ }
+ err := f.AcceptOTCQuote("1031")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestGetExchangeHistory(t *testing.T) {
+ t.Parallel()
+ p := currency.NewPairFromString("ADA-PERP")
+ a, err := f.GetPairAssetType(p)
+ if err != nil {
+ t.Error(err)
+ }
+ _, err = f.GetExchangeHistory(p, a, time.Now().Add(-time.Minute*500), time.Now())
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestTimestampFromFloat64(t *testing.T) {
+ t.Parallel()
+ constTime := 1592697600.0
+ checkTime := time.Date(2020, time.June, 21, 0, 0, 0, 0, time.UTC)
+ timeConst := timestampFromFloat64(constTime)
+ if timeConst != checkTime {
+ t.Error("invalid time conversion")
+ }
+}
+
+func TestCompatibleOrderVars(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() {
+ t.Skip("API keys required but not set, skipping test")
+ }
+ a, err := f.compatibleOrderVars("buy", "closed", "limit", 0.5, 0.5, 9500)
+ if err != nil {
+ t.Error(err)
+ }
+ var b OrderVars
+ b.Side = order.Buy
+ b.OrderType = order.Limit
+ b.Status = order.Filled
+ b.Fee = a.Fee // having a preset value will fail because fees are calculated live with getaccount
+ if !reflect.DeepEqual(a, b) {
+ t.Errorf("incorrect compatible vars")
+ }
+}
+
+func TestGetIndexWeights(t *testing.T) {
+ t.Parallel()
+ _, err := f.GetIndexWeights("SHIT")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestModifyPlacedOrder(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly")
+ }
+ _, err := f.ModifyPlacedOrder("1234", "", -0.1, 0.1)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestModifyOrderByClientID(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly")
+ }
+ _, err := f.ModifyOrderByClientID("1234", "", -0.1, 0.1)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestModifyTriggerOrder(t *testing.T) {
+ t.Parallel()
+ if !areTestAPIKeysSet() || !canManipulateRealOrders {
+ t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly")
+ }
+ _, err := f.ModifyTriggerOrder("1234", "stop", -0.1, 0.1, 0.02, 0)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestParsingWSOBData2(t *testing.T) {
+ t.Parallel()
+ data := []byte(`{"channel": "orderbook", "market": "PRIVBEAR/USD", "type": "partial", "data": {"time": 1593498757.0915809, "checksum": 87356415, "bids": [[1389.5, 5.1019], [1384.5, 16.6318], [1371.5, 23.5531], [1365.5, 23.3001], [1354.0, 26.758], [1352.5, 24.6891], [1337.5, 30.3091], [1333.5, 24.9583], [1323.0, 30.9597], [1302.0, 40.9241], [1282.5, 38.0319], [1272.5, 39.1436], [1084.5, 1.8934], [1080.0, 2.0595], [1075.0, 2.0527], [1069.0, 1.8077], [1053.5, 1.855], [1.0, 2.0]], "asks": [[1403.5, 6.8077], [1407.5, 17.6482], [1417.0, 14.6401], [1418.5, 22.6664], [1426.0, 20.3936], [1430.5, 34.2797], [1435.0, 30.6073], [1443.0, 20.2036], [1471.5, 35.5789], [1494.5, 29.2815], [1505.0, 30.9842], [1511.5, 39.4325], [1799.5, 1.7529], [1810.5, 2.0379], [1813.5, 2.0423], [1817.5, 2.0393], [1821.0, 1.7148], [86347.5, 9e-05], [94982.5, 0.0001], [104480.0, 0.0001], [114930.0, 0.00011], [126420.0, 0.00011], [139065.0, 0.00011], [152970.0, 0.00012], [168267.5, 0.00012], [185092.5, 0.00012], [223962.5, 0.00013], [246360.0, 0.00014], [270995.0, 0.00017], [1203602.5, 0.00013]], "action": "partial"}}`)
+ err := f.wsHandleData(data)
+ if err != nil {
+ t.Fatal(err)
+ }
+ data = []byte(`{"channel": "orderbook", "market": "DOGE-PERP", "type": "partial", "data": {"time": 1593395710.072698, "checksum": 2591057682, "bids": [[0.0023085, 507742.0], [0.002308, 7000.0], [0.0023075, 100000.0], [0.0023065, 324770.0], [0.002305, 46000.0], [0.0023035, 879600.0], [0.002303, 49000.0], [0.0023025, 1076421.0], [0.002296, 30511800.0], [0.002293, 3006300.0], [0.0022925, 1256349.0], [0.0022895, 11855700.0], [0.0022855, 1008960.0], [0.0022775, 1047578.0], [0.0022745, 3070200.0], [0.00227, 2939100.0], [0.002269, 1599711.0], [0.00226, 1671504.0], [0.00225, 1957119.0], [0.00224, 5225404.0], [0.0022395, 250.0], [0.002233, 2994000.0], [0.002229, 2336857.0], [0.002218, 2144227.0], [0.002205, 2101662.0], [0.0021985, 7406099.0], [0.0021915, 2470187.0], [0.0021775, 2690545.0], [0.0021755, 250.0], [0.002162, 2997201.0], [0.00215, 11464856.0], [0.002148, 16178857.0], [0.0021255, 11063510.0], [0.002119, 164239.0], [0.0020435, 19124572.0], [0.0020395, 18376430.0], [0.0020125, 1250.0], [0.0019655, 50.0], [0.001958, 97012.0], [0.001942, 50000.0], [0.001899, 50000.0], [0.001895, 1250.0], [0.001712, 2500.0], [0.0012075, 70190.0], [0.00112, 22321.0], [1.65e-05, 31889.0]], "asks": [[0.0023145, 359557.0], [0.0023155, 222497.0], [0.0023175, 40000.0], [0.002319, 879600.0], [0.0023195, 50000.0], [0.0023205, 1067334.0], [0.0023215, 45000.0], [0.002326, 33518100.0], [0.0023265, 1113997.0], [0.0023285, 1170756.0], [0.002331, 11855700.0], [0.002336, 1105442.0], [0.002344, 1244804.0], [0.002348, 3070200.0], [0.0023525, 1546561.0], [0.0023555, 2939100.0], [0.0023575, 2928000.0], [0.002362, 1509707.0], [0.0023725, 1786697.0], [0.002374, 5710.0], [0.0023795, 151098.0], [0.0023835, 1747428.0], [0.002385, 2994000.0], [0.002395, 1721532.0], [0.0024015, 5710.0], [0.002408, 2552142.0], [0.002422, 2188855.0], [0.002429, 5710.0], [0.0024295, 8441953.0], [0.002437, 2196750.0], [0.002445, 122574.0], [0.002454, 1974273.0], [0.0024565, 5710.0], [0.0024715, 2864643.0], [0.00248, 15238408.0], [0.002484, 5710.0], [0.002497, 16343646.0], [0.0025025, 12177084.0], [0.0025115, 5710.0], [0.002539, 5710.0], [0.002566, 16643688.0], [0.0025665, 5710.0], [0.002594, 5710.0], [0.002617, 50.0], [0.002623, 10.0], [0.0027685, 20825893.0], [0.003178, 50000.0], [0.003811, 68952.0], [0.0074, 41460.0]], "action": "partial"}}`)
+ err = f.wsHandleData(data)
+ if err != nil {
+ t.Error(err)
+ }
+ data = []byte(`{"channel": "orderbook", "market": "BTC-PERP", "type": "partial", "data": {"time": 1589855831.4606245, "checksum": 225973019, "bids": [[9602.0, 3.2903], [9601.5, 3.11], [9601.0, 2.1356], [9600.5, 3.0991], [9600.0, 8.014], [9599.5, 4.1571], [9599.0, 79.1846], [9598.5, 3.099], [9598.0, 3.985], [9597.5, 3.999], [9597.0, 16.4335], [9596.5, 4.006], [9596.0, 3.2596], [9595.0, 6.334], [9594.0, 3.5685], [9593.0, 14.2717], [9592.5, 0.5], [9591.0, 2.181], [9590.5, 40.4246], [9590.0, 1.0], [9589.0, 1.357], [9588.5, 0.4738], [9587.5, 0.15], [9587.0, 16.811], [9586.5, 1.2], [9586.0, 0.2], [9585.5, 1.0], [9584.5, 0.002], [9584.0, 1.51], [9583.5, 0.01], [9583.0, 1.4], [9582.5, 0.1], [9582.0, 24.7921], [9581.0, 2.087], [9580.5, 2.0], [9580.0, 0.1], [9579.0, 1.1588], [9578.0, 0.9477], [9577.5, 22.216], [9576.0, 0.2], [9574.0, 22.0], [9573.5, 1.0], [9572.0, 0.203], [9570.0, 0.1026], [9565.5, 5.5332], [9565.0, 27.5243], [9563.5, 2.6], [9562.0, 0.0175], [9561.0, 2.0085], [9552.0, 1.6], [9550.5, 27.3399], [9550.0, 0.1046], [9548.0, 0.0175], [9544.0, 4.8197], [9542.5, 26.5754], [9542.0, 0.003], [9541.0, 0.0549], [9540.0, 0.1984], [9537.5, 0.0008], [9535.5, 0.0105], [9535.0, 1.514], [9534.5, 36.5858], [9532.5, 4.7798], [9531.0, 40.6564], [9525.0, 0.001], [9523.5, 1.6], [9522.0, 0.0894], [9521.0, 0.315], [9520.5, 5.4525], [9520.0, 0.07], [9518.0, 0.034], [9517.5, 4.0], [9513.0, 0.0175], [9512.5, 15.6016], [9512.0, 32.7882], [9511.5, 0.0482], [9510.5, 0.0482], [9510.0, 0.2999], [9509.0, 2.0], [9508.5, 0.0482], [9506.0, 0.0416], [9505.5, 0.0492], [9505.0, 0.2], [9502.5, 0.01], [9502.0, 0.01], [9501.5, 0.0592], [9501.0, 0.001], [9500.0, 3.4913], [9499.5, 39.8683], [9498.0, 4.6108], [9497.0, 0.0481], [9492.0, 41.3559], [9490.0, 1.1104], [9488.0, 0.0105], [9486.0, 5.4443], [9485.5, 0.0482], [9484.0, 4.0], [9482.0, 0.25], [9481.5, 2.0], [9481.0, 8.1572]], "asks": [[9602.5, 3.0], [9603.0, 2.8979], [9603.5, 54.49], [9604.0, 5.9982], [9604.5, 3.028], [9605.0, 4.657], [9606.5, 5.2512], [9607.0, 4.003], [9607.5, 4.011], [9608.0, 13.7505], [9608.5, 3.994], [9609.0, 2.974], [9609.5, 3.002], [9612.0, 10.298], [9612.5, 13.455], [9613.5, 3.013], [9614.0, 2.02], [9614.5, 3.359], [9615.0, 21.2429], [9616.0, 0.5], [9616.5, 0.01], [9617.0, 2.182], [9617.5, 23.0223], [9618.0, 0.0623], [9618.5, 1.5795], [9619.0, 0.3065], [9620.0, 3.9], [9621.0, 1.5], [9622.0, 1.5], [9622.5, 1.216], [9625.0, 1.0], [9625.5, 0.9477], [9626.0, 0.05], [9628.5, 1.1588], [9629.0, 1.4], [9630.0, 4.2332], [9630.5, 1.228], [9631.0, 1.5], [9631.5, 0.0104], [9632.5, 26.7529], [9633.0, 0.25], [9638.0, 1.0], [9640.0, 0.2], [9641.0, 1.001], [9642.0, 0.0175], [9643.0, 0.25], [9643.5, 1.6], [9644.0, 31.4166], [9646.5, 41.6609], [9649.5, 0.2], [9653.5, 1.5], [9656.5, 1.6], [9657.0, 0.2], [9658.0, 1.5], [9659.5, 4.7804], [9660.5, 43.3405], [9665.5, 40.6564], [9670.0, 0.1034], [9671.5, 4.9098], [9674.0, 0.25], [9678.0, 15.6016], [9678.5, 1.5], [9681.0, 34.9683], [9683.0, 0.2], [9683.5, 5.3845], [9684.5, 5.087], [9685.0, 0.1032], [9686.5, 0.0075], [9689.0, 1.6], [9691.0, 34.7472], [9692.0, 0.001], [9694.0, 0.5], [9695.0, 0.0109], [9696.5, 4.825], [9700.0, 1.0595], [9701.5, 2.0], [9702.0, 0.011], [9702.5, 0.01], [9706.0, 1.2], [9708.0, 0.0175], [9710.0, 39.153], [9712.0, 48.6163], [9712.5, 1.5], [9713.0, 8.1572], [9715.5, 0.5021], [9716.5, 2.0], [9719.0, 0.0245], [9721.0, 0.5], [9724.0, 0.251], [9726.0, 0.12], [9727.5, 0.5075], [9730.0, 0.015], [9732.0, 58.5394], [9733.0, 0.001], [9734.0, 20.0], [9743.0, 0.06], [9750.0, 9.5], [9755.0, 52.4404], [9757.0, 48.6121], [9764.0, 0.015]], "action": "partial"}}`)
+ err = f.wsHandleData(data)
+ if err != nil {
+ t.Error(err)
+ }
+ data = []byte(`{"channel": "orderbook", "market": "BTC-PERP", "type": "update", "data": {"time": 1589855831.5128105, "checksum": 365946911, "bids": [[9596.0, 4.2656], [9512.0, 32.7912]], "asks": [[9613.5, 4.012], [9702.0, 0.021]], "action": "update"}}`)
+ err = f.wsHandleData(data)
+ if err != nil {
+ t.Error(err)
+ }
+}
diff --git a/exchanges/ftx/ftx_types.go b/exchanges/ftx/ftx_types.go
new file mode 100644
index 00000000..0206fb53
--- /dev/null
+++ b/exchanges/ftx/ftx_types.go
@@ -0,0 +1,726 @@
+package ftx
+
+import (
+ "errors"
+ "time"
+
+ "github.com/thrasher-corp/gocryptotrader/exchanges/order"
+)
+
+// MarketData stores market data
+type MarketData struct {
+ Name string `json:"name"`
+ BaseCurrency string `json:"baseCurrency"`
+ QuoteCurrency string `json:"quoteCurrency"`
+ MarketType string `json:"type"`
+ Underlying string `json:"underlying"`
+ Change1h float64 `json:"change1h"`
+ Change24h float64 `json:"change24h"`
+ ChangeBod float64 `json:"changeBod"`
+ QuoteVolume24h float64 `json:"quoteVolume24h"`
+ Enabled bool `json:"enabled"`
+ Ask float64 `json:"ask"`
+ Bid float64 `json:"bid"`
+ Last float64 `json:"last"`
+ USDVolume24h float64 `json:"volumeUSD24h"`
+ MinProvideSize float64 `json:"minProvideSize"`
+ PriceIncrement float64 `json:"priceIncrement"`
+ SizeIncrement float64 `json:"sizeIncrement"`
+ Restricted bool `json:"restricted"`
+}
+
+// OData stores orderdata in orderbook
+type OData struct {
+ Price float64
+ Size float64
+}
+
+// OrderbookData stores orderbook data
+type OrderbookData struct {
+ MarketName string
+ Asks []OData
+ Bids []OData
+}
+
+// TempOBData stores orderbook data temporarily
+type TempOBData struct {
+ Asks [][2]float64 `json:"asks"`
+ Bids [][2]float64 `json:"bids"`
+}
+
+// TradeData stores data from trades
+type TradeData struct {
+ ID int64 `json:"id"`
+ Liquidation bool `json:"liquidation"`
+ Price float64 `json:"price"`
+ Side string `json:"side"`
+ Size float64 `json:"size"`
+ Time time.Time `json:"time"`
+}
+
+// OHLCVData stores historical OHLCV data
+type OHLCVData struct {
+ Close float64 `json:"close"`
+ High float64 `json:"high"`
+ Low float64 `json:"low"`
+ Open float64 `json:"open"`
+ StartTime time.Time `json:"startTime"`
+ Time float64 `json:"time"`
+ Volume float64 `json:"volume"`
+}
+
+// FuturesData stores data for futures
+type FuturesData struct {
+ Ask float64 `json:"ask"`
+ Bid float64 `json:"bid"`
+ Change1h float64 `json:"change1h"`
+ Change24h float64 `json:"change24h"`
+ ChangeBod float64 `json:"changeBod"`
+ VolumeUSD24h float64 `json:"volumeUsd24h"`
+ Volume float64 `json:"volume"`
+ Description string `json:"description"`
+ Enabled bool `json:"enabled"`
+ Expired bool `json:"expired"`
+ Expiry time.Time `json:"expiry"`
+ ExpiryDescription string `json:"expiryDescription"`
+ Group string `json:"group"`
+ Index float64 `json:"index"`
+ IMFFactor float64 `json:"imfFactor"`
+ Last float64 `json:"last"`
+ LowerBound float64 `json:"lowerBound"`
+ MarginPrice float64 `json:"marginPrice"`
+ Mark float64 `json:"mark"`
+ MoveStart interface{} `json:"moveStart"`
+ Name string `json:"name"`
+ Perpetual bool `json:"perpetual"`
+ PositionLimitWeight float64 `json:"positionLimitWeight"`
+ PostOnly bool `json:"postOnly"`
+ PriceIncrement float64 `json:"priceIncrement"`
+ SizeIncrement float64 `json:"sizeIncrement"`
+ Underlying string `json:"underlying"`
+ UpperBound float64 `json:"upperBound"`
+ FutureType string `json:"type"`
+}
+
+// FutureStatsData stores data on futures stats
+type FutureStatsData struct {
+ Volume float64 `json:"volume"`
+ NextFundingRate float64 `json:"nextFundingRate"`
+ NextFundingTime time.Time `json:"nextFundingTime"`
+ ExpirationPrice float64 `json:"expirationPrice"`
+ PredictedExpirationPrice float64 `json:"predictedExpirationPrice"`
+ OpenInterest float64 `json:"openInterest"`
+ StrikePrice float64 `json:"strikePrice"`
+}
+
+// FundingRatesData stores data on funding rates
+type FundingRatesData struct {
+ Future string `json:"future"`
+ Rate float64 `json:"rate"`
+ Time time.Time `json:"time"`
+}
+
+// IndexWeights stores index weights' data
+type IndexWeights struct {
+ Result map[string]float64 `json:"result"`
+}
+
+// PositionData stores data of an open position
+type PositionData struct {
+ Cost float64 `json:"cost"`
+ EntryPrice float64 `json:"entryPrice"`
+ Future string `json:"future"`
+ InitialMarginRequirement float64 `json:"initialMarginRequirement"`
+ LongOrderSize float64 `json:"longOrderSize"`
+ MaintenanceMarginRequirement float64 `json:"maintenanceMarginRequirement"`
+ NetSize float64 `json:"netSize"`
+ OpenSize float64 `json:"openSize"`
+ RealisedPnL float64 `json:"realisedPnL"`
+ ShortOrderSide float64 `json:"shortOrderSide"`
+ Side string `json:"side"`
+ Size float64 `json:"size"`
+ UnrealisedPnL float64 `json:"unrealisedPnL"`
+}
+
+// AccountInfoData stores account data
+type AccountInfoData struct {
+ BackstopProvider bool `json:"backstopProvider"`
+ ChargeInterestOnNegativeUSD bool `json:"chargeInterestOnNegativeUsd"`
+ Collateral float64 `json:"collateral"`
+ FreeCollateral float64 `json:"freeCollateral"`
+ InitialMarginRequirement float64 `json:"initialMarginRequirement"`
+ Leverage float64 `json:"float64"`
+ Liquidating bool `json:"liquidating"`
+ MaintenanceMarginRequirement float64 `json:"maintenanceMarginRequirement"`
+ MakerFee float64 `json:"makerFee"`
+ MarginFraction float64 `json:"marginFraction"`
+ OpenMarginFraction float64 `json:"openMarginFraction"`
+ PositionLimit float64 `json:"positionLimit"`
+ PositionLimitUsed float64 `json:"positionLimitUsed"`
+ SpotLendingEnabled bool `json:"spotLendingEnabled"`
+ SpotMarginEnabled bool `json:"spotMarginEnabled"`
+ TakerFee float64 `json:"takerFee"`
+ TotalAccountValue float64 `json:"totalAccountValue"`
+ TotalPositionSize float64 `json:"totalPositionSize"`
+ UseFTTCollateral bool `json:"useFttCollateral"`
+ Username string `json:"username"`
+ Positions []PositionData `json:"positions"`
+}
+
+// WalletCoinsData stores data about wallet coins
+type WalletCoinsData struct {
+ Bep2Asset interface{} `json:"bep2Asset"`
+ CanConvert bool `json:"canConvert"`
+ CanDeposit bool `json:"canDeposit"`
+ CanWithdraw bool `json:"canWithdraw"`
+ Collateral bool `json:"collateral"`
+ CollateralWeight float64 `json:"collateralWeight"`
+ CreditTo interface{} `json:"creditTo"`
+ ERC20Contract interface{} `json:"erc20Contract"`
+ Fiat bool `json:"fiat"`
+ HasTag bool `json:"hasTag"`
+ Hidden bool `json:"hidden"`
+ IsETF bool `json:"isEtf"`
+ IsToken bool `json:"isToken"`
+ Methods []interface{}
+ ID string `json:"id"`
+ Name string `json:"name"`
+}
+
+// BalancesData stores balances data
+type BalancesData struct {
+ Coin string `json:"coin"`
+ Free float64 `json:"free"`
+ Total float64 `json:"total"`
+}
+
+// AllWalletAccountData stores account data on all WalletCoins
+type AllWalletAccountData struct {
+ Main []BalancesData `json:"main"`
+ BattleRoyale []BalancesData `json:"Battle Royale"`
+}
+
+// DepositData stores deposit address data
+type DepositData struct {
+ Address string `json:"address"`
+ Tag string `json:"tag"`
+}
+
+// TransactionData stores data about deposit history
+type TransactionData struct {
+ Coin string `json:"coin"`
+ Confirmations int64 `json:"conformations"`
+ ConfirmedTime time.Time `json:"confirmedTime"`
+ Fee float64 `json:"fee"`
+ ID int64 `json:"id"`
+ SentTime time.Time `json:"sentTime"`
+ Size float64 `json:"size"`
+ Status string `json:"status"`
+ Time time.Time `json:"time"`
+ TxID string `json:"txid"`
+}
+
+// OrderData stores open order data
+type OrderData struct {
+ CreatedAt time.Time `json:"createdAt"`
+ FilledSize float64 `json:"filledSize"`
+ Future string `json:"future"`
+ ID int64 `json:"id"`
+ Market string `json:"market"`
+ Price float64 `json:"price"`
+ AvgFillPrice float64 `json:"avgFillPrice"`
+ RemainingSize float64 `json:"remainingSize"`
+ Side string `json:"side"`
+ Size float64 `json:"size"`
+ Status string `json:"status"`
+ OrderType string `json:"type"`
+ ReduceOnly bool `json:"reduceOnly"`
+ IOC bool `json:"ioc"`
+ PostOnly bool `json:"postOnly"`
+ ClientID string `json:"clientId"`
+}
+
+// TriggerOrderData stores trigger order data
+type TriggerOrderData struct {
+ CreatedAt time.Time `json:"createdAt"`
+ Error string `json:"error"`
+ Future string `json:"future"`
+ ID int64 `json:"id"`
+ Market string `json:"market"`
+ OrderID int64 `json:"orderId"`
+ OrderPrice float64 `json:"orderPrice"`
+ ReduceOnly bool `json:"reduceOnly"`
+ Side string `json:"side"`
+ Size float64 `json:"size"`
+ Status string `json:"status"`
+ TrailStart float64 `json:"trailStart"`
+ TrailValue float64 `json:"trailvalue"`
+ TriggerPrice float64 `json:"triggerPrice"`
+ TriggeredAt string `json:"triggeredAt"`
+ OrderType string `json:"type"`
+ MarketOrLimit string `json:"orderType"`
+ FilledSize float64 `json:"filledSize"`
+ AvgFillPrice float64 `json:"avgFillPrice"`
+ RetryUntilFilled bool `json:"retryUntilFilled"`
+}
+
+// TriggerData stores trigger orders' trigger data
+type TriggerData struct {
+ Error string `json:"error"`
+ FilledSize float64 `json:"filledSize"`
+ OrderSize float64 `json:"orderSize"`
+ OrderID int64 `json:"orderId"`
+ Time time.Time `json:"time"`
+}
+
+// FillsData stores fills' data
+type FillsData struct {
+ Fee float64 `json:"fee"`
+ FeeRate float64 `json:"feeRate"`
+ Future string `json:"future"`
+ ID string `json:"id"`
+ Liquidity string `json:"liquidity"`
+ Market string `json:"market"`
+ BaseCurrency string `json:"baseCurrency"`
+ QuoteCurrency string `json:"quoteCurrency"`
+ OrderID string `json:"orderID"`
+ TradeID string `json:"tradeID"`
+ Price float64 `json:"price"`
+ Side string `json:"side"`
+ Size float64 `json:"size"`
+ Time time.Time `json:"time"`
+ OrderType string `json:"type"`
+}
+
+// FundingPaymentsData stores funding payments' data
+type FundingPaymentsData struct {
+ Future string `json:"future"`
+ ID string `json:"id"`
+ Payment float64 `json:"payment"`
+ Time time.Time `json:"time"`
+ Rate float64 `json:"rate"`
+}
+
+// LeveragedTokensData stores data of leveraged tokens
+type LeveragedTokensData struct {
+ Basket map[string]interface{} `json:"basket"`
+ Bep2AssetName string `json:"bep2AssetName"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Underlying string `json:"underlying"`
+ Leverage float64 `json:"leverage"`
+ Outstanding float64 `json:"outstanding"`
+ PricePerShare float64 `json:"pricePerShare"`
+ PositionPerShare float64 `json:"positionPerShare"`
+ PositionsPerShare interface{} `json:"positionsPerShare"`
+ TargetComponents []string `json:"targetComponents"`
+ TotalCollateral float64 `json:"totalCollateral"`
+ TotalNav float64 `json:"totalNav"`
+ UnderlyingMark float64 `json:"underlyingMark"`
+ ContactAddress string `json:"contactAddress"`
+ Change1h float64 `json:"change1h"`
+ Change24h float64 `json:"change24h"`
+ ChangeBod float64 `json:"changeBod"`
+}
+
+// LTBalanceData stores balances of leveraged tokens
+type LTBalanceData struct {
+ Token string `json:"token"`
+ Balance float64 `json:"balance"`
+}
+
+// LTCreationData stores token creation requests' data
+type LTCreationData struct {
+ ID string `json:"id"`
+ Token string `json:"token"`
+ RequestedSize float64 `json:"requestedSize"`
+ Pending bool `json:"pending"`
+ CreatedSize float64 `json:"createdize"`
+ Price float64 `json:"price"`
+ Cost float64 `json:"cost"`
+ Fee float64 `json:"fee"`
+ RequestedAt time.Time `json:"requestedAt"`
+ FulfilledAt time.Time `json:"fulfilledAt"`
+}
+
+// RequestTokenCreationData stores data of the token creation requested
+type RequestTokenCreationData struct {
+ ID string `json:"id"`
+ Token string `json:"token"`
+ RequestedSize float64 `json:"requestedSize"`
+ Cost float64 `json:"cost"`
+ Pending bool `json:"pending"`
+ RequestedAt time.Time `json:"requestedAt"`
+}
+
+// LTRedemptionData stores data of the token redemption request
+type LTRedemptionData struct {
+ ID int64 `json:"id"`
+ Token string `json:"token"`
+ Size float64 `json:"size"`
+ Pending bool `json:"pending"`
+ Price float64 `json:"price"`
+ Proceeds float64 `json:"proceeds"`
+ Fee float64 `json:"fee"`
+ RequestedAt time.Time `json:"requestedAt"`
+ FulfilledAt time.Time `json:"fulfilledAt"`
+}
+
+// LTRedemptionRequestData stores redemption request data for a leveraged token
+type LTRedemptionRequestData struct {
+ ID string `json:"id"`
+ Token string `json:"token"`
+ Size float64 `json:"size"`
+ ProjectedProceeds float64 `json:"projectedProceeds"`
+ Pending bool `json:"pending"`
+ RequestedAt time.Time `json:"requestedAt"`
+}
+
+// OptionData stores options' data
+type OptionData struct {
+ Underlying string `json:"underlying"`
+ OptionType string `json:"type"`
+ Strike float64 `json:"strike"`
+ Expiry time.Time `json:"expiry"`
+}
+
+// QuoteRequestData stores option's quote request data
+type QuoteRequestData struct {
+ ID int64 `json:"id"`
+ Option OptionData `json:"option"`
+ Side string `json:"side"`
+ Size float64 `json:"size"`
+ Time time.Time `json:"time"`
+ RequestExpiry string `json:"requestExpiry"`
+ Status string `json:"status"`
+}
+
+// QuoteData stores quote's data
+type QuoteData struct {
+ Collateral float64 `json:"collateral"`
+ ID int64 `json:"id"`
+ Price float64 `json:"price"`
+ QuoteExpiry string `json:"quoteExpiry"`
+ Status string `json:"status"`
+ Time time.Time `json:"time"`
+}
+
+// PersonalQuotesData stores data of your quotes
+type PersonalQuotesData struct {
+ ID int64 `json:"id"`
+ Option OptionData `json:"option"`
+ Side string `json:"side"`
+ Size float64 `json:"size"`
+ Time time.Time `json:"time"`
+ RequestExpiry string `json:"requestExpiry"`
+ Status string `json:"status"`
+ HideLimitPrice bool `json:"hideLimitPrice"`
+ LimitPrice float64 `json:"limitPrice"`
+ Quotes []QuoteData `json:"quotes"`
+}
+
+// CreateQuoteRequestData stores quote data of the request sent
+type CreateQuoteRequestData struct {
+ ID int64 `json:"id"`
+ Expiry time.Time `json:"expiry"`
+ Strike float64 `json:"strike"`
+ OptionType string `json:"type"`
+ Underlying string `json:"underlying"`
+ RequestExpiry string `json:"requestExpiry"`
+ Side string `json:"side"`
+ Size float64 `json:"size"`
+ Status string `json:"status"`
+ Time time.Time `json:"time"`
+}
+
+// CancelQuoteRequestData stores cancel quote request data
+type CancelQuoteRequestData struct {
+ ID int64 `json:"id"`
+ Option OptionData `json:"option"`
+ RequestExpiry string `json:"requestExpiry"`
+ Side string `json:"side"`
+ Size float64 `json:"size"`
+ Status string `json:"status"`
+ Time time.Time `json:"time"`
+}
+
+// QuoteForQuoteData gets quote data for your quote
+type QuoteForQuoteData struct {
+ Collateral float64 `json:"collateral"`
+ ID int64 `json:"id"`
+ Option OptionData `json:"option"`
+ Price float64 `json:"price"`
+ QuoteExpiry string `json:"quoteExpiry"`
+ QuoterSide string `json:"quoterSide"`
+ RequestID int64 `json:"requestID"`
+ RequestSide string `json:"requestSide"`
+ Size float64 `json:"size"`
+ Status string `json:"status"`
+ Time time.Time `json:"time"`
+}
+
+// AccountOptionsInfoData stores account's options' info data
+type AccountOptionsInfoData struct {
+ USDBalance float64 `json:"usdBalance"`
+ LiquidationPrice float64 `json:"liquidationPrice"`
+ Liquidating bool `json:"liquidating"`
+}
+
+// OptionsPositionsData stores options positions' data
+type OptionsPositionsData struct {
+ EntryPrice float64 `json:"entryPrice"`
+ NetSize float64 `json:"netSize"`
+ Option OptionData `json:"option"`
+ Side string `json:"side"`
+ Size float64 `json:"size"`
+ PessimisticValuation float64 `json:"pessimisticValuation,omitempty"`
+ PessimisticIndexPrice float64 `json:"pessimisticIndexPrice,omitempty"`
+}
+
+// OptionsTradesData stores options' trades' data
+type OptionsTradesData struct {
+ ID int64 `json:"id"`
+ Option OptionData `json:"option"`
+ Price float64 `json:"price"`
+ Size float64 `json:"size"`
+ Time time.Time `json:"time"`
+}
+
+// OptionFillsData stores option's fills data
+type OptionFillsData struct {
+ Fee float64 `json:"fee"`
+ FeeRate float64 `json:"feeRate"`
+ ID int64 `json:"id"`
+ Liquidity string `json:"liquidity"`
+ Option OptionData `json:"option"`
+ Price float64 `json:"price"`
+ QuoteID int64 `json:"quoteId"`
+ Side string `json:"side"`
+ Size float64 `json:"size"`
+ Time string `json:"time"`
+}
+
+// AuthenticationData stores authentication variables required
+type AuthenticationData struct {
+ Key string `json:"key"`
+ Sign string `json:"sign"`
+ Time int64 `json:"time"`
+}
+
+// Authenticate stores authentication variables required
+type Authenticate struct {
+ Args AuthenticationData `json:"args"`
+ Operation string `json:"op"`
+}
+
+// WsResponseData stores basic ws response data on being subscribed to a channel successfully
+type WsResponseData struct {
+ ResponseType string `json:"type"`
+ Channel string `json:"channel"`
+ Market string `json:"market"`
+ Data interface{} `json:"data"`
+}
+
+// WsTickerData stores ws ticker data
+type WsTickerData struct {
+ Bid float64 `json:"bid"`
+ Ask float64 `json:"ask"`
+ BidSize float64 `json:"bidSize"`
+ AskSize float64 `json:"askSize"`
+ Last float64 `json:"last"`
+ Time float64 `json:"time"`
+}
+
+// WsTradeData stores ws trade data
+type WsTradeData struct {
+ ID int64 `json:"id"`
+ Price float64 `json:"price"`
+ Size float64 `json:"size"`
+ Side string `json:"side"`
+ Liquidation bool `json:"liquidation"`
+ Time time.Time `json:"time"`
+}
+
+// WsOrderbookData stores ws orderbook data
+type WsOrderbookData struct {
+ Action string `json:"action"`
+ Bids [][2]float64 `json:"bids"`
+ Asks [][2]float64 `json:"asks"`
+ Time float64 `json:"time"`
+ Checksum int64 `json:"checksum"`
+}
+
+// WsOrders stores ws orders' data
+type WsOrders struct {
+ ID int64 `json:"id"`
+ ClientID string `json:"clientId"`
+ Market string `json:"market"`
+ OrderType string `json:"type"`
+ Side string `json:"side"`
+ Size float64 `json:"size"`
+ Price float64 `json:"price"`
+ ReduceOnly bool `json:"reduceOnly"`
+ IOC bool `json:"ioc"`
+ PostOnly bool `json:"postOnly"`
+ Status string `json:"status"`
+ FilledSize float64 `json:"filedSize"`
+ RemainingSize float64 `json:"remainingSize"`
+ AvgFillPrice float64 `json:"avgFillPrice"`
+}
+
+// WsFills stores websocket fills' data
+type WsFills struct {
+ Fee float64 `json:"fee"`
+ FeeRate float64 `json:"feeRate"`
+ Future string `json:"future"`
+ ID int64 `json:"id"`
+ Liquidity string `json:"liquidity"`
+ Market string `json:"market"`
+ OrderID int64 `json:"int64"`
+ TradeID int64 `json:"tradeID"`
+ Price float64 `json:"price"`
+ Side string `json:"side"`
+ Size float64 `json:"size"`
+ Time time.Time `json:"time"`
+ OrderType string `json:"orderType"`
+}
+
+// WsSub has the data used to subscribe to a channel
+type WsSub struct {
+ Channel string `json:"channel,omitempty"`
+ Market string `json:"market,omitempty"`
+ Operation string `json:"op,omitempty"`
+}
+
+// WsTickerDataStore stores ws ticker data
+type WsTickerDataStore struct {
+ Channel string `json:"channel"`
+ Market string `json:"market"`
+ MessageType string `json:"type"`
+ Ticker WsTickerData `json:"data"`
+}
+
+// WsOrderbookDataStore stores ws orderbook data
+type WsOrderbookDataStore struct {
+ Channel string `json:"channel"`
+ Market string `json:"market"`
+ MessageType string `json:"type"`
+ OBData WsOrderbookData `json:"data"`
+}
+
+// WsTradeDataStore stores ws trades' data
+type WsTradeDataStore struct {
+ Channel string `json:"channel"`
+ Market string `json:"market"`
+ MessageType string `json:"type"`
+ TradeData []WsTradeData `json:"data"`
+}
+
+// WsOrderDataStore stores ws orders' data
+type WsOrderDataStore struct {
+ Channel string `json:"channel"`
+ MessageType string `json:"type"`
+ OrderData WsOrders `json:"data"`
+}
+
+// WsFillsDataStore stores ws fills' data
+type WsFillsDataStore struct {
+ Channel string `json:"channel"`
+ MessageType string `json:"type"`
+ FillsData WsFills `json:"fills"`
+}
+
+// TimeInterval represents interval enum.
+type TimeInterval string
+
+// Vars related to time intervals
+var (
+ TimeIntervalFifteenSeconds = TimeInterval("15")
+ TimeIntervalMinute = TimeInterval("60")
+ TimeIntervalFiveMinutes = TimeInterval("300")
+ TimeIntervalFifteenMinutes = TimeInterval("900")
+ TimeIntervalHour = TimeInterval("3600")
+ TimeIntervalFourHours = TimeInterval("14400")
+ TimeIntervalDay = TimeInterval("86400")
+)
+
+var errInvalidInterval = errors.New("invalid interval")
+
+// OrderVars stores side, status and type for any order/trade
+type OrderVars struct {
+ Side order.Side
+ Status order.Status
+ OrderType order.Type
+ Fee float64
+}
+
+// WsMarketsData stores websocket markets data
+type WsMarketsData struct {
+ Data map[string]WsMarketsDataStorage `json:"data"`
+}
+
+// WsMarketsDataStorage stores websocket markets data
+type WsMarketsDataStorage struct {
+ Name string `json:"name,omitempty"`
+ Enabled bool `json:"enabled,omitempty"`
+ PriceIncrement float64 `json:"priceIncrement,omitempty"`
+ SizeIncrement float64 `json:"sizeIncrement,omitempty"`
+ MarketType string `json:"marketType,omitempty"`
+ BaseCurrency string `json:"baseCurrency,omitempty"`
+ QuoteCurrency string `json:"quoteCurrency,omitempty"`
+ Underlying string `json:"underlying,omitempty"`
+ Restricted bool `json:"restricted,omitempty"`
+ Future WsMarketsFutureData `json:"future,omitempty"`
+}
+
+// WsMarketsFutureData stores websocket markets' future data
+type WsMarketsFutureData struct {
+ Name string `json:"name,omitempty"`
+ Underlying string `json:"underlying,omitempty"`
+ Description string `json:"description,omitempty"`
+ MarketType string `json:"type,omitempty"`
+ Expiry time.Time `json:"expiry,omitempty"`
+ Perpetual bool `json:"perpetual,omitempty"`
+ Expired bool `json:"expired,omitempty"`
+ Enabled bool `json:"enabled,omitempty"`
+ PostOnly bool `json:"postOnly,omitempty"`
+ IMFFactor float64 `json:"imfFactor,omitempty"`
+ UnderlyingDescription string `json:"underlyingDescription,omitempty"`
+ ExpiryDescription string `json:"expiryDescription,omitempty"`
+ MoveStart string `json:"moveStart,omitempty"`
+ PositionLimitWeight float64 `json:"positionLimitWeight,omitempty"`
+ Group string `json:"group,omitempty"`
+}
+
+// WSMarkets stores websocket markets data
+type WSMarkets struct {
+ Channel string `json:"channel"`
+ MessageType string `json:"type"`
+ Data WsMarketsData `json:"data"`
+ Action string `json:"action"`
+}
+
+// RequestQuoteData stores data on the requested quote
+type RequestQuoteData struct {
+ QuoteID int64 `json:"quoteId"`
+}
+
+// QuoteStatusData stores data of quotes' status
+type QuoteStatusData struct {
+ BaseCoin string `json:"baseCoin"`
+ Cost float64 `json:"cost"`
+ Expired bool `json:"expired"`
+ Filled bool `json:"filled"`
+ FromCoin string `json:"fromCoin"`
+ ID int64 `json:"id"`
+ Price float64 `json:"price"`
+ Proceeds float64 `json:"proceeds"`
+ QuoteCoin string `json:"quoteCoin"`
+ Side string `json:"side"`
+ ToCoin string `json:"toCoin"`
+}
+
+// AcceptQuote stores data of accepted quote
+type AcceptQuote struct {
+ Success bool `json:"success"`
+}
diff --git a/exchanges/ftx/ftx_websocket.go b/exchanges/ftx/ftx_websocket.go
new file mode 100644
index 00000000..34703676
--- /dev/null
+++ b/exchanges/ftx/ftx_websocket.go
@@ -0,0 +1,511 @@
+package ftx
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "hash/crc32"
+ "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/ticker"
+ "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
+ "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook"
+ "github.com/thrasher-corp/gocryptotrader/log"
+)
+
+const (
+ ftxWSURL = "wss://ftx.com/ws/"
+ ftxWebsocketTimer = 13 * time.Second
+ wsTicker = "ticker"
+ wsTrades = "trades"
+ wsOrderbook = "orderbook"
+ wsMarkets = "markets"
+ wsFills = "fills"
+ wsOrders = "orders"
+ wsUpdate = "update"
+ wsPartial = "partial"
+ subscribe = "subscribe"
+ unsubscribe = "unsubscribe"
+)
+
+var obSuccess = make(map[currency.Pair]bool)
+
+// WsConnect connects to a websocket feed
+func (f *FTX) WsConnect() error {
+ if !f.Websocket.IsEnabled() || !f.IsEnabled() {
+ return errors.New(wshandler.WebsocketNotEnabled)
+ }
+ var dialer websocket.Dialer
+ err := f.WebsocketConn.Dial(&dialer, http.Header{})
+ if err != nil {
+ return err
+ }
+ f.WebsocketConn.SetupPingHandler(wshandler.WebsocketPingHandler{
+ MessageType: websocket.PingMessage,
+ Delay: ftxWebsocketTimer,
+ })
+ if f.Verbose {
+ log.Debugf(log.ExchangeSys, "%s Connected to Websocket.\n", f.Name)
+ }
+ f.GenerateDefaultSubscriptions()
+ go f.wsReadData()
+ if f.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) {
+ err := f.WsAuth()
+ if err != nil {
+ f.Websocket.DataHandler <- err
+ f.Websocket.SetCanUseAuthenticatedEndpoints(false)
+ }
+ f.GenerateAuthSubscriptions()
+ }
+ return nil
+}
+
+// WsAuth sends an authentication message to receive auth data
+func (f *FTX) WsAuth() error {
+ intNonce := time.Now().UnixNano() / 1000000
+ strNonce := strconv.FormatInt(intNonce, 10)
+ hmac := crypto.GetHMAC(
+ crypto.HashSHA256,
+ []byte(strNonce+"websocket_login"),
+ []byte(f.API.Credentials.Secret),
+ )
+ sign := crypto.HexEncodeToString(hmac)
+ req := Authenticate{Operation: "login",
+ Args: AuthenticationData{
+ Key: f.API.Credentials.Key,
+ Sign: sign,
+ Time: intNonce,
+ },
+ }
+ return f.WebsocketConn.SendJSONMessage(req)
+}
+
+// Subscribe sends a websocket message to receive data from the channel
+func (f *FTX) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error {
+ var sub WsSub
+ switch channelToSubscribe.Channel {
+ case wsFills, wsOrders, wsMarkets:
+ sub.Operation = subscribe
+ sub.Channel = channelToSubscribe.Channel
+ default:
+ a, err := f.GetPairAssetType(channelToSubscribe.Currency)
+ if err != nil {
+ return err
+ }
+ sub.Operation = subscribe
+ sub.Channel = channelToSubscribe.Channel
+ sub.Market = f.FormatExchangeCurrency(channelToSubscribe.Currency, a).String()
+ }
+ return f.WebsocketConn.SendJSONMessage(sub)
+}
+
+// GenerateDefaultSubscriptions generates default subscription
+func (f *FTX) GenerateDefaultSubscriptions() {
+ var subscriptions []wshandler.WebsocketChannelSubscription
+ subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{
+ Channel: wsMarkets,
+ })
+ var channels = []string{wsTicker, wsTrades, wsOrderbook}
+ for a := range f.CurrencyPairs.AssetTypes {
+ pairs := f.GetEnabledPairs(f.CurrencyPairs.AssetTypes[a])
+ for z := range pairs {
+ newPair := currency.NewPairWithDelimiter(pairs[z].Base.String(), pairs[z].Quote.String(), "-")
+ for x := range channels {
+ subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{
+ Channel: channels[x],
+ Currency: newPair,
+ })
+ }
+ }
+ }
+ f.Websocket.SubscribeToChannels(subscriptions)
+}
+
+// GenerateAuthSubscriptions generates default subscription
+func (f *FTX) GenerateAuthSubscriptions() {
+ var subscriptions []wshandler.WebsocketChannelSubscription
+ var channels = []string{wsOrders, wsFills}
+ for x := range channels {
+ subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{
+ Channel: channels[x],
+ })
+ }
+ f.Websocket.SubscribeToChannels(subscriptions)
+}
+
+// wsReadData gets and passes on websocket messages for processing
+func (f *FTX) wsReadData() {
+ f.Websocket.Wg.Add(1)
+
+ defer f.Websocket.Wg.Done()
+
+ for {
+ select {
+ case <-f.Websocket.ShutdownC:
+ return
+
+ default:
+ resp, err := f.WebsocketConn.ReadMessage()
+ if err != nil {
+ f.Websocket.ReadMessageErrors <- err
+ return
+ }
+ f.Websocket.TrafficAlert <- struct{}{}
+ err = f.wsHandleData(resp.Raw)
+ if err != nil {
+ f.Websocket.DataHandler <- err
+ }
+ }
+ }
+}
+
+func timestampFromFloat64(ts float64) time.Time {
+ secs := int64(ts)
+ nsecs := int64((ts - float64(secs)) * 1e9)
+ return time.Unix(secs, nsecs).UTC()
+}
+
+func (f *FTX) wsHandleData(respRaw []byte) error {
+ var result map[string]interface{}
+ err := json.Unmarshal(respRaw, &result)
+ if err != nil {
+ return err
+ }
+ switch result["type"] {
+ case wsUpdate:
+ var p currency.Pair
+ var a asset.Item
+ market, ok := result["market"]
+ if ok {
+ p = currency.NewPairFromString(market.(string))
+ a, err = f.GetPairAssetType(p)
+ if err != nil {
+ return err
+ }
+ }
+ switch result["channel"] {
+ case wsTicker:
+ var resultData WsTickerDataStore
+ err = json.Unmarshal(respRaw, &resultData)
+ if err != nil {
+ return err
+ }
+ f.Websocket.DataHandler <- &ticker.Price{
+ ExchangeName: f.Name,
+ Bid: resultData.Ticker.Bid,
+ Ask: resultData.Ticker.Ask,
+ Last: resultData.Ticker.Last,
+ LastUpdated: timestampFromFloat64(resultData.Ticker.Time),
+ Pair: p,
+ AssetType: a,
+ }
+ case wsOrderbook:
+ var resultData WsOrderbookDataStore
+ err = json.Unmarshal(respRaw, &resultData)
+ if err != nil {
+ return err
+ }
+ if len(resultData.OBData.Asks) == 0 && len(resultData.OBData.Bids) == 0 {
+ return nil
+ }
+ err = f.WsProcessUpdateOB(&resultData.OBData, p, a)
+ if err != nil {
+ f.wsResubToOB(p)
+ return err
+ }
+ case wsTrades:
+ var resultData WsTradeDataStore
+ err = json.Unmarshal(respRaw, &resultData)
+ if err != nil {
+ return err
+ }
+ for z := range resultData.TradeData {
+ var oSide order.Side
+ oSide, err = order.StringToOrderSide(resultData.TradeData[z].Side)
+ if err != nil {
+ f.Websocket.DataHandler <- order.ClassificationError{
+ Exchange: f.Name,
+ Err: err,
+ }
+ }
+ f.Websocket.DataHandler <- wshandler.TradeData{
+ Timestamp: resultData.TradeData[z].Time,
+ CurrencyPair: p,
+ AssetType: a,
+ Exchange: f.Name,
+ Price: resultData.TradeData[z].Price,
+ Amount: resultData.TradeData[z].Size,
+ Side: oSide,
+ }
+ }
+ case wsOrders:
+ var resultData WsOrderDataStore
+ err = json.Unmarshal(respRaw, &resultData)
+ if err != nil {
+ return err
+ }
+ pair := currency.NewPairFromString(resultData.OrderData.Market)
+ var assetType asset.Item
+ assetType, err = f.GetPairAssetType(pair)
+ if err != nil {
+ return err
+ }
+ var oSide order.Side
+ oSide, err = order.StringToOrderSide(resultData.OrderData.Side)
+ if err != nil {
+ f.Websocket.DataHandler <- order.ClassificationError{
+ Exchange: f.Name,
+ Err: err,
+ }
+ }
+ var resp order.Detail
+ resp.Side = oSide
+ resp.Amount = resultData.OrderData.Size
+ resp.AssetType = assetType
+ resp.ClientOrderID = resultData.OrderData.ClientID
+ resp.Exchange = f.Name
+ resp.ExecutedAmount = resultData.OrderData.FilledSize
+ resp.ID = strconv.FormatInt(resultData.OrderData.ID, 10)
+ resp.Pair = pair
+ resp.RemainingAmount = resultData.OrderData.Size - resultData.OrderData.FilledSize
+ var orderVars OrderVars
+ orderVars, err = f.compatibleOrderVars(resultData.OrderData.Side,
+ resultData.OrderData.Status,
+ resultData.OrderData.OrderType,
+ resultData.OrderData.FilledSize,
+ resultData.OrderData.Size,
+ resultData.OrderData.AvgFillPrice)
+ if err != nil {
+ return err
+ }
+ resp.Status = orderVars.Status
+ resp.Side = orderVars.Side
+ resp.Type = orderVars.OrderType
+ resp.Fee = orderVars.Fee
+ f.Websocket.DataHandler <- &resp
+ case wsFills:
+ var resultData WsFillsDataStore
+ err = json.Unmarshal(respRaw, &resultData)
+ if err != nil {
+ return err
+ }
+ f.Websocket.DataHandler <- resultData.FillsData
+ default:
+ f.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: f.Name + wshandler.UnhandledMessage + string(respRaw)}
+ }
+ case wsPartial:
+ switch result["channel"] {
+ case "orderbook":
+ var p currency.Pair
+ var a asset.Item
+ market, ok := result["market"]
+ if ok {
+ p = currency.NewPairFromString(market.(string))
+ a, err = f.GetPairAssetType(p)
+ if err != nil {
+ return err
+ }
+ }
+ var resultData WsOrderbookDataStore
+ err = json.Unmarshal(respRaw, &resultData)
+ if err != nil {
+ return err
+ }
+ err = f.WsProcessPartialOB(&resultData.OBData, p, a)
+ if err != nil {
+ f.wsResubToOB(p)
+ return err
+ }
+ // reset obchecksum failure blockage for pair
+ delete(obSuccess, p)
+ case wsMarkets:
+ var resultData WSMarkets
+ err = json.Unmarshal(respRaw, &resultData)
+ if err != nil {
+ return err
+ }
+ f.Websocket.DataHandler <- resultData.Data
+ }
+ case "error":
+ f.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: f.Name + wshandler.UnhandledMessage + string(respRaw)}
+ }
+ return nil
+}
+
+// Unsubscribe sends a websocket message to stop receiving data from the channel
+func (f *FTX) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error {
+ var unSub WsSub
+ a, err := f.GetPairAssetType(channelToSubscribe.Currency)
+ if err != nil {
+ return err
+ }
+ unSub.Operation = unsubscribe
+ unSub.Channel = channelToSubscribe.Channel
+ unSub.Market = f.FormatExchangeCurrency(channelToSubscribe.Currency, a).String()
+ return f.WebsocketConn.SendJSONMessage(unSub)
+}
+
+// WsProcessUpdateOB processes an update on the orderbook
+func (f *FTX) WsProcessUpdateOB(data *WsOrderbookData, p currency.Pair, a asset.Item) error {
+ update := wsorderbook.WebsocketOrderbookUpdate{
+ Asset: a,
+ Pair: p,
+ UpdateTime: timestampFromFloat64(data.Time),
+ }
+
+ var err error
+ for x := range data.Bids {
+ update.Bids = append(update.Bids, orderbook.Item{
+ Price: data.Bids[x][0],
+ Amount: data.Bids[x][1],
+ })
+ }
+ for x := range data.Asks {
+ update.Asks = append(update.Asks, orderbook.Item{
+ Price: data.Asks[x][0],
+ Amount: data.Asks[x][1],
+ })
+ }
+
+ err = f.Websocket.Orderbook.Update(&update)
+ if err != nil {
+ return err
+ }
+
+ updatedOb := f.Websocket.Orderbook.GetOrderbook(p, a)
+ checksum := f.CalcUpdateOBChecksum(updatedOb)
+
+ if checksum != data.Checksum {
+ log.Warnf(log.ExchangeSys, "%s checksum failure for item %s",
+ f.Name,
+ p)
+ return errors.New("checksum failed")
+ }
+ f.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{
+ Exchange: f.Name,
+ Asset: a,
+ Pair: p,
+ }
+
+ return nil
+}
+
+func (f *FTX) wsResubToOB(p currency.Pair) {
+ if ok := obSuccess[p]; ok {
+ return
+ }
+
+ obSuccess[p] = true
+
+ channelToResubscribe := wshandler.WebsocketChannelSubscription{
+ Channel: wsOrderbook,
+ Currency: p,
+ }
+ f.Websocket.ResubscribeToChannel(channelToResubscribe)
+}
+
+// WsProcessPartialOB creates an OB from websocket data
+func (f *FTX) WsProcessPartialOB(data *WsOrderbookData, p currency.Pair, a asset.Item) error {
+ signedChecksum := f.CalcPartialOBChecksum(data)
+ if signedChecksum != data.Checksum {
+ return fmt.Errorf("%s channel: %s. Orderbook partial for %v checksum invalid",
+ f.Name,
+ a,
+ p)
+ }
+ var bids, asks []orderbook.Item
+ for x := range data.Bids {
+ bids = append(bids, orderbook.Item{
+ Price: data.Bids[x][0],
+ Amount: data.Bids[x][1],
+ })
+ }
+ for x := range data.Asks {
+ asks = append(asks, orderbook.Item{
+ Price: data.Asks[x][0],
+ Amount: data.Asks[x][1],
+ })
+ }
+
+ newOrderBook := orderbook.Base{
+ Asks: asks,
+ Bids: bids,
+ AssetType: a,
+ LastUpdated: timestampFromFloat64(data.Time),
+ Pair: p,
+ ExchangeName: f.Name,
+ }
+
+ if err := f.Websocket.Orderbook.LoadSnapshot(&newOrderBook); err != nil {
+ return err
+ }
+
+ f.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{
+ Exchange: f.Name,
+ Asset: a,
+ Pair: p,
+ }
+ return nil
+}
+
+// CalcPartialOBChecksum calculates checksum of partial OB data received from WS
+func (f *FTX) CalcPartialOBChecksum(data *WsOrderbookData) int64 {
+ var checksum strings.Builder
+ var price, amount string
+ for i := 0; i < 100; i++ {
+ if len(data.Bids)-1 >= i {
+ price = checksumParseNumber(data.Bids[i][0])
+ amount = checksumParseNumber(data.Bids[i][1])
+ checksum.WriteString(price + ":" + amount + ":")
+ }
+ if len(data.Asks)-1 >= i {
+ price = checksumParseNumber(data.Asks[i][0])
+ amount = checksumParseNumber(data.Asks[i][1])
+ checksum.WriteString(price + ":" + amount + ":")
+ }
+ }
+ checksumStr := strings.TrimSuffix(checksum.String(), ":")
+ return int64(crc32.ChecksumIEEE([]byte(checksumStr)))
+}
+
+// CalcUpdateOBChecksum calculates checksum of update OB data received from WS
+func (f *FTX) CalcUpdateOBChecksum(data *orderbook.Base) int64 {
+ var checksum strings.Builder
+ var price, amount string
+ for i := 0; i < 100; i++ {
+ if len(data.Bids)-1 >= i {
+ price = checksumParseNumber(data.Bids[i].Price)
+ amount = checksumParseNumber(data.Bids[i].Amount)
+ checksum.WriteString(price + ":" + amount + ":")
+ }
+ if len(data.Asks)-1 >= i {
+ price = checksumParseNumber(data.Asks[i].Price)
+ amount = checksumParseNumber(data.Asks[i].Amount)
+ checksum.WriteString(price + ":" + amount + ":")
+ }
+ }
+ checksumStr := strings.TrimSuffix(checksum.String(), ":")
+ return int64(crc32.ChecksumIEEE([]byte(checksumStr)))
+}
+
+func checksumParseNumber(num float64) string {
+ modifier := byte('f')
+ if num < 0.0001 {
+ modifier = 'e'
+ }
+ r := strconv.FormatFloat(num, modifier, -1, 64)
+ if strings.IndexByte(r, '.') == -1 && modifier != 'e' {
+ r += ".0"
+ }
+ return r
+}
diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go
new file mode 100644
index 00000000..3243ce81
--- /dev/null
+++ b/exchanges/ftx/ftx_wrapper.go
@@ -0,0 +1,873 @@
+package ftx
+
+import (
+ "fmt"
+ "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/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/ticker"
+ "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
+ "github.com/thrasher-corp/gocryptotrader/log"
+ "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
+)
+
+// GetDefaultConfig returns a default exchange config
+func (f *FTX) GetDefaultConfig() (*config.ExchangeConfig, error) {
+ f.SetDefaults()
+ exchCfg := new(config.ExchangeConfig)
+ exchCfg.Name = f.Name
+ exchCfg.HTTPTimeout = exchange.DefaultHTTPTimeout
+ exchCfg.BaseCurrencies = f.BaseCurrencies
+
+ err := f.SetupDefaults(exchCfg)
+ if err != nil {
+ return nil, err
+ }
+
+ if f.Features.Supports.RESTCapabilities.AutoPairUpdates {
+ err = f.UpdateTradablePairs(true)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return exchCfg, nil
+}
+
+// SetDefaults sets the basic defaults for FTX
+func (f *FTX) SetDefaults() {
+ f.Name = "FTX"
+ f.Enabled = true
+ f.Verbose = true
+ f.API.CredentialsValidator.RequiresKey = true
+ f.API.CredentialsValidator.RequiresSecret = true
+ f.CurrencyPairs = currency.PairsManager{
+ AssetTypes: asset.Items{
+ asset.Spot,
+ asset.Futures,
+ },
+ }
+ spot := currency.PairStore{
+ RequestFormat: ¤cy.PairFormat{
+ Uppercase: true,
+ Delimiter: "/",
+ },
+ ConfigFormat: ¤cy.PairFormat{
+ Uppercase: true,
+ Delimiter: "/",
+ },
+ }
+ futures := currency.PairStore{
+ RequestFormat: ¤cy.PairFormat{
+ Uppercase: true,
+ Delimiter: "-",
+ },
+ ConfigFormat: ¤cy.PairFormat{
+ Uppercase: true,
+ Delimiter: "-",
+ },
+ }
+ f.CurrencyPairs.Store(asset.Spot, spot)
+ f.CurrencyPairs.Store(asset.Futures, futures)
+ f.Features = exchange.Features{
+ Supports: exchange.FeaturesSupported{
+ REST: true,
+ Websocket: true,
+ RESTCapabilities: protocol.Features{
+ TickerFetching: true,
+ KlineFetching: true,
+ TradeFetching: true,
+ OrderbookFetching: true,
+ AutoPairUpdates: true,
+ AccountInfo: true,
+ GetOrder: true,
+ GetOrders: true,
+ CancelOrders: true,
+ CancelOrder: true,
+ SubmitOrder: true,
+ TradeFee: true,
+ FiatDepositFee: true,
+ FiatWithdrawalFee: true,
+ CryptoWithdrawalFee: true,
+ },
+ WebsocketCapabilities: protocol.Features{
+ OrderbookFetching: true,
+ TradeFetching: true,
+ Subscribe: true,
+ Unsubscribe: true,
+ GetOrders: true,
+ GetOrder: true,
+ },
+ WithdrawPermissions: exchange.NoAPIWithdrawalMethods,
+ },
+ Enabled: exchange.FeaturesEnabled{
+ AutoPairUpdates: true,
+ },
+ }
+
+ f.Requester = request.New(f.Name,
+ common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
+ request.WithLimiter(request.NewBasicRateLimit(ratePeriod, rateLimit)))
+
+ f.API.Endpoints.URLDefault = ftxAPIURL
+ f.API.Endpoints.URL = f.API.Endpoints.URLDefault
+ f.Websocket = wshandler.New()
+ f.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
+ f.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
+ f.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit
+}
+
+// Setup takes in the supplied exchange configuration details and sets params
+func (f *FTX) Setup(exch *config.ExchangeConfig) error {
+ if !exch.Enabled {
+ f.SetEnabled(false)
+ return nil
+ }
+
+ err := f.SetupDefaults(exch)
+ if err != nil {
+ return err
+ }
+
+ err = f.Websocket.Setup(
+ &wshandler.WebsocketSetup{
+ Enabled: exch.Features.Enabled.Websocket,
+ Verbose: exch.Verbose,
+ AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport,
+ WebsocketTimeout: exch.WebsocketTrafficTimeout,
+ DefaultURL: ftxWSURL,
+ ExchangeName: exch.Name,
+ RunningURL: exch.API.Endpoints.WebsocketURL,
+ Connector: f.WsConnect,
+ Subscriber: f.Subscribe,
+ UnSubscriber: f.Unsubscribe,
+ Features: &f.Features.Supports.WebsocketCapabilities,
+ })
+ if err != nil {
+ return err
+ }
+
+ f.WebsocketConn = &wshandler.WebsocketConnection{
+ ExchangeName: f.Name,
+ URL: f.Websocket.GetWebsocketURL(),
+ ProxyURL: f.Websocket.GetProxyAddress(),
+ Verbose: f.Verbose,
+ ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
+ ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
+ }
+
+ f.Websocket.Orderbook.Setup(
+ exch.WebsocketOrderbookBufferLimit,
+ false,
+ false,
+ false,
+ false,
+ exch.Name)
+ return nil
+}
+
+// Start starts the FTX go routine
+func (f *FTX) Start(wg *sync.WaitGroup) {
+ wg.Add(1)
+ go func() {
+ f.Run()
+ wg.Done()
+ }()
+}
+
+// Run implements the FTX wrapper
+func (f *FTX) Run() {
+ if f.Verbose {
+ log.Debugf(log.ExchangeSys,
+ "%s Websocket: %s.",
+ f.Name,
+ common.IsEnabled(f.Websocket.IsEnabled()))
+ f.PrintEnabledPairs()
+ }
+
+ if !f.GetEnabledFeatures().AutoPairUpdates {
+ return
+ }
+
+ err := f.UpdateTradablePairs(false)
+ if err != nil {
+ log.Errorf(log.ExchangeSys,
+ "%s failed to update tradable pairs. Err: %s",
+ f.Name,
+ err)
+ }
+}
+
+// FetchTradablePairs returns a list of the exchanges tradable pairs
+func (f *FTX) FetchTradablePairs(a asset.Item) ([]string, error) {
+ if !f.SupportsAsset(a) {
+ return nil, fmt.Errorf("asset type of %s is not supported by %s", a, f.Name)
+ }
+ markets, err := f.GetMarkets()
+ if err != nil {
+ return nil, err
+ }
+ var pairs []string
+ switch a {
+ case asset.Spot:
+ for x := range markets {
+ if markets[x].MarketType == spotString {
+ pairs = append(pairs, markets[x].Name)
+ }
+ }
+ case asset.Futures:
+ for x := range markets {
+ if markets[x].MarketType == futuresString {
+ pairs = append(pairs, markets[x].Name)
+ }
+ }
+ }
+ return pairs, nil
+}
+
+// UpdateTradablePairs updates the exchanges available pairs and stores
+// them in the exchanges config
+func (f *FTX) UpdateTradablePairs(forceUpdate bool) error {
+ for x := range f.CurrencyPairs.AssetTypes {
+ pairs, err := f.FetchTradablePairs(f.CurrencyPairs.AssetTypes[x])
+ if err != nil {
+ return err
+ }
+ err = f.UpdatePairs(currency.NewPairsFromStrings(pairs),
+ f.CurrencyPairs.AssetTypes[x], false, forceUpdate)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// UpdateTicker updates and returns the ticker for a currency pair
+func (f *FTX) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) {
+ allPairs := f.GetEnabledPairs(assetType)
+ if !allPairs.Contains(p, true) {
+ allPairs = append(allPairs, p)
+ }
+ markets, err := f.GetMarkets()
+ if err != nil {
+ return nil, err
+ }
+ for a := range allPairs {
+ for x := range markets {
+ if markets[x].Name != f.FormatExchangeCurrency(allPairs[a], assetType).String() {
+ continue
+ }
+ var resp ticker.Price
+ resp.Pair = currency.NewPairFromString(markets[x].Name)
+ resp.Last = markets[x].Last
+ resp.Bid = markets[x].Bid
+ resp.Ask = markets[x].Ask
+ resp.LastUpdated = time.Now()
+ err = ticker.ProcessTicker(f.Name, &resp, assetType)
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+ return ticker.GetTicker(f.Name, p, assetType)
+}
+
+// FetchTicker returns the ticker for a currency pair
+func (f *FTX) FetchTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) {
+ tickerNew, err := ticker.GetTicker(f.Name, p, assetType)
+ if err != nil {
+ return f.UpdateTicker(p, assetType)
+ }
+ return tickerNew, nil
+}
+
+// FetchOrderbook returns orderbook base on the currency pair
+func (f *FTX) FetchOrderbook(currency currency.Pair, assetType asset.Item) (*orderbook.Base, error) {
+ ob, err := orderbook.Get(f.Name, currency, assetType)
+ if err != nil {
+ return f.UpdateOrderbook(currency, assetType)
+ }
+ return ob, nil
+}
+
+// UpdateOrderbook updates and returns the orderbook for a currency pair
+func (f *FTX) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) {
+ orderBook := new(orderbook.Base)
+ tempResp, err := f.GetOrderbook(f.FormatExchangeCurrency(p, assetType).String(), 0)
+ if err != nil {
+ return orderBook, err
+ }
+ for x := range tempResp.Bids {
+ orderBook.Bids = append(orderBook.Bids, orderbook.Item{
+ Amount: tempResp.Bids[x].Size,
+ Price: tempResp.Bids[x].Price})
+ }
+ for y := range tempResp.Asks {
+ orderBook.Asks = append(orderBook.Asks, orderbook.Item{
+ Amount: tempResp.Asks[y].Size,
+ Price: tempResp.Asks[y].Price})
+ }
+ orderBook.Pair = p
+ orderBook.ExchangeName = f.Name
+ orderBook.AssetType = assetType
+ err = orderBook.Process()
+ if err != nil {
+ return orderBook, err
+ }
+ return orderbook.Get(f.Name, p, assetType)
+}
+
+// UpdateAccountInfo retrieves balances for all enabled currencies
+func (f *FTX) UpdateAccountInfo() (account.Holdings, error) {
+ var resp account.Holdings
+ data, err := f.GetBalances()
+ if err != nil {
+ return resp, err
+ }
+ var acc account.SubAccount
+ for i := range data {
+ c := currency.NewCode(data[i].Coin)
+ hold := data[i].Total - data[i].Free
+ total := data[i].Total
+ acc.Currencies = append(acc.Currencies,
+ account.Balance{CurrencyName: c,
+ TotalValue: total,
+ Hold: hold})
+ }
+ resp.Accounts = append(resp.Accounts, acc)
+ resp.Exchange = f.Name
+
+ err = account.Process(&resp)
+ if err != nil {
+ return account.Holdings{}, err
+ }
+
+ return resp, nil
+}
+
+// FetchAccountInfo retrieves balances for all enabled currencies
+func (f *FTX) FetchAccountInfo() (account.Holdings, error) {
+ acc, err := account.GetHoldings(f.Name)
+ if err != nil {
+ return f.UpdateAccountInfo()
+ }
+
+ return acc, nil
+}
+
+// GetFundingHistory returns funding history, deposits and
+// withdrawals
+func (f *FTX) GetFundingHistory() ([]exchange.FundHistory, error) {
+ var resp []exchange.FundHistory
+ depositData, err := f.FetchDepositHistory()
+ if err != nil {
+ return resp, err
+ }
+ for x := range depositData {
+ var tempData exchange.FundHistory
+ tempData.Fee = depositData[x].Fee
+ tempData.Timestamp = depositData[x].Time
+ tempData.ExchangeName = f.Name
+ tempData.CryptoTxID = depositData[x].TxID
+ tempData.Status = depositData[x].Status
+ tempData.Amount = depositData[x].Size
+ tempData.Currency = depositData[x].Coin
+ tempData.TransferID = strconv.FormatInt(depositData[x].ID, 10)
+ resp = append(resp, tempData)
+ }
+ withdrawalData, err := f.FetchWithdrawalHistory()
+ if err != nil {
+ return resp, err
+ }
+ for y := range withdrawalData {
+ var tempData exchange.FundHistory
+ tempData.Fee = depositData[y].Fee
+ tempData.Timestamp = depositData[y].Time
+ tempData.ExchangeName = f.Name
+ tempData.CryptoTxID = depositData[y].TxID
+ tempData.Status = depositData[y].Status
+ tempData.Amount = depositData[y].Size
+ tempData.Currency = depositData[y].Coin
+ tempData.TransferID = strconv.FormatInt(depositData[y].ID, 10)
+ resp = append(resp, tempData)
+ }
+ return resp, nil
+}
+
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (f *FTX) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
+ marketName := f.FormatExchangeCurrency(p, assetType).String()
+ var resp []exchange.TradeHistory
+ trades, err := f.GetTrades(marketName, time.Unix(timestampStart.Unix(), 0), time.Unix(timestampEnd.Unix(), 0), 100)
+ if err != nil {
+ return nil, err
+ }
+ for {
+ var tempResp exchange.TradeHistory
+ if len(trades) > 0 {
+ tempResp.Amount = trades[0].Size
+ tempResp.Price = trades[0].Price
+ tempResp.Exchange = f.Name
+ tempResp.Timestamp = trades[0].Time
+ tempResp.TID = strconv.FormatInt(trades[0].ID, 10)
+ tempResp.Side = trades[0].Side
+ resp = append(resp, tempResp)
+ }
+ for y := 1; y < len(trades); y++ {
+ tempResp.Amount = trades[y].Size
+ tempResp.Price = trades[y].Price
+ tempResp.Exchange = f.Name
+ tempResp.Timestamp = trades[y].Time
+ tempResp.TID = strconv.FormatInt(trades[y].ID, 10)
+ tempResp.Side = trades[y].Side
+ resp = append(resp, tempResp)
+ }
+ if len(trades) != 100 {
+ break
+ }
+ trades, err = f.GetTrades(marketName, time.Unix(timestampStart.Unix(), 0), time.Unix(trades[len(trades)-1].Time.Unix(), 0), 100)
+ if err != nil {
+ return resp, err
+ }
+ }
+ return resp, nil
+}
+
+// SubmitOrder submits a new order
+func (f *FTX) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
+ var resp order.SubmitResponse
+ if err := s.Validate(); err != nil {
+ return resp, err
+ }
+
+ if s.Side == order.Sell {
+ s.Side = order.Ask
+ }
+ if s.Side == order.Buy {
+ s.Side = order.Bid
+ }
+
+ tempResp, err := f.Order(f.FormatExchangeCurrency(s.Pair, s.AssetType).String(),
+ s.Side.String(),
+ s.Type.String(),
+ "",
+ "",
+ "",
+ s.ClientOrderID,
+ s.Price,
+ s.Amount)
+ if err != nil {
+ return resp, err
+ }
+ resp.IsOrderPlaced = true
+ resp.OrderID = strconv.FormatInt(tempResp.ID, 10)
+ return resp, nil
+}
+
+// ModifyOrder will allow of changing orderbook placement and limit to
+// market conversion
+func (f *FTX) ModifyOrder(action *order.Modify) (string, error) {
+ if action.TriggerPrice != 0 {
+ a, err := f.ModifyTriggerOrder(action.ID,
+ action.Type.String(),
+ action.Amount,
+ action.TriggerPrice,
+ action.Price,
+ 0)
+ if err != nil {
+ return "", err
+ }
+ return strconv.FormatInt(a.ID, 10), err
+ }
+ var o OrderData
+ var err error
+ switch action.ID {
+ case "":
+ o, err = f.ModifyOrderByClientID(action.ClientOrderID, action.ClientOrderID, action.Price, action.Amount)
+ if err != nil {
+ return "", err
+ }
+ default:
+ o, err = f.ModifyPlacedOrder(action.ID, action.ClientOrderID, action.Price, action.Amount)
+ if err != nil {
+ return "", err
+ }
+ }
+ return strconv.FormatInt(o.ID, 10), err
+}
+
+// CancelOrder cancels an order by its corresponding ID number
+func (f *FTX) CancelOrder(order *order.Cancel) error {
+ _, err := f.DeleteOrder(order.ID)
+ return err
+}
+
+// CancelAllOrders cancels all orders associated with a currency pair
+func (f *FTX) CancelAllOrders(orderCancellation *order.Cancel) (order.CancelAllResponse, error) {
+ var resp order.CancelAllResponse
+ tempMap := make(map[string]string)
+ orders, err := f.GetOpenOrders(f.FormatExchangeCurrency(orderCancellation.Pair, orderCancellation.AssetType).String())
+ if err != nil {
+ return resp, err
+ }
+ for x := range orders {
+ _, err := f.DeleteOrder(strconv.FormatInt(orders[x].ID, 10))
+ if err != nil {
+ tempMap[strconv.FormatInt(orders[x].ID, 10)] = "Cancellation Failed"
+ continue
+ }
+ tempMap[strconv.FormatInt(orders[x].ID, 10)] = "Success"
+ }
+ resp.Status = tempMap
+ return resp, nil
+}
+
+// GetCompatible gets compatible variables for order vars
+func (s *OrderData) GetCompatible(f *FTX) (OrderVars, error) {
+ var resp OrderVars
+ switch s.Side {
+ case order.Buy.Lower():
+ resp.Side = order.Buy
+ case order.Sell.Lower():
+ resp.Side = order.Sell
+ }
+ switch s.Status {
+ case strings.ToLower(order.New.String()):
+ resp.Status = order.New
+ case strings.ToLower(order.Open.String()):
+ resp.Status = order.Open
+ case closedStatus:
+ if s.FilledSize != 0 && s.FilledSize != s.Size {
+ resp.Status = order.PartiallyCancelled
+ }
+ if s.FilledSize == 0 {
+ resp.Status = order.Cancelled
+ }
+ if s.FilledSize == s.Size {
+ resp.Status = order.Filled
+ }
+ }
+ var feeBuilder exchange.FeeBuilder
+ feeBuilder.PurchasePrice = s.AvgFillPrice
+ feeBuilder.Amount = s.Size
+ resp.OrderType = order.Market
+ if strings.EqualFold(s.OrderType, order.Limit.String()) {
+ resp.OrderType = order.Limit
+ feeBuilder.IsMaker = true
+ }
+ fee, err := f.GetFee(&feeBuilder)
+ if err != nil {
+ return resp, err
+ }
+ resp.Fee = fee
+ return resp, nil
+}
+
+// GetOrderInfo returns information on a current open order
+func (f *FTX) GetOrderInfo(orderID string) (order.Detail, error) {
+ var resp order.Detail
+ orderData, err := f.GetOrderStatus(orderID)
+ if err != nil {
+ return resp, err
+ }
+ p := currency.NewPairFromString(orderData.Market)
+ assetType, err := f.GetPairAssetType(p)
+ if err != nil {
+ return resp, err
+ }
+ resp.ID = strconv.FormatInt(orderData.ID, 10)
+ resp.Amount = orderData.Size
+ resp.ClientOrderID = orderData.ClientID
+ resp.Date = orderData.CreatedAt
+ resp.Exchange = f.Name
+ resp.ExecutedAmount = orderData.Size - orderData.RemainingSize
+ resp.Pair = p
+ resp.AssetType = assetType
+ resp.Price = orderData.Price
+ resp.RemainingAmount = orderData.RemainingSize
+ orderVars, err := orderData.GetCompatible(f)
+ if err != nil {
+ return resp, err
+ }
+ resp.Status = orderVars.Status
+ resp.Side = orderVars.Side
+ resp.Type = orderVars.OrderType
+ resp.Fee = orderVars.Fee
+ return resp, nil
+}
+
+// GetDepositAddress returns a deposit address for a specified currency
+func (f *FTX) GetDepositAddress(cryptocurrency currency.Code, _ string) (string, error) {
+ a, err := f.FetchDepositAddress(cryptocurrency.String())
+ if err != nil {
+ return "", err
+ }
+ return a.Address, nil
+}
+
+// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is
+// submitted
+func (f *FTX) WithdrawCryptocurrencyFunds(withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) {
+ var address, addressTag string
+ if withdrawRequest.Crypto != nil {
+ address = withdrawRequest.Crypto.Address
+ addressTag = withdrawRequest.Crypto.AddressTag
+ }
+ resp := withdraw.ExchangeResponse{}
+ a, err := f.Withdraw(withdrawRequest.Currency.String(),
+ address,
+ addressTag,
+ withdrawRequest.TradePassword,
+ strconv.FormatInt(withdrawRequest.OneTimePassword, 10),
+ withdrawRequest.Amount)
+ if err != nil {
+ return &resp, err
+ }
+ resp.ID = strconv.FormatInt(a.ID, 10)
+ resp.Status = a.Status
+ return &resp, nil
+}
+
+// WithdrawFiatFunds returns a withdrawal ID when a withdrawal is
+// submitted
+func (f *FTX) WithdrawFiatFunds(_ *withdraw.Request) (*withdraw.ExchangeResponse, error) {
+ return nil, common.ErrFunctionNotSupported
+}
+
+// WithdrawFiatFundsToInternationalBank returns a withdrawal ID when a
+// withdrawal is submitted
+func (f *FTX) WithdrawFiatFundsToInternationalBank(_ *withdraw.Request) (*withdraw.ExchangeResponse, error) {
+ return nil, common.ErrFunctionNotSupported
+}
+
+// GetWebsocket returns a pointer to the exchange websocket
+func (f *FTX) GetWebsocket() (*wshandler.Websocket, error) {
+ return f.Websocket, nil
+}
+
+// GetActiveOrders retrieves any orders that are active/open
+func (f *FTX) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest) ([]order.Detail, error) {
+ var resp []order.Detail
+ for x := range getOrdersRequest.Pairs {
+ assetType, err := f.GetPairAssetType(getOrdersRequest.Pairs[x])
+ if err != nil {
+ return resp, err
+ }
+ var tempResp order.Detail
+ orderData, err := f.GetOpenOrders(f.FormatExchangeCurrency(getOrdersRequest.Pairs[x], assetType).String())
+ if err != nil {
+ return resp, err
+ }
+ for y := range orderData {
+ tempResp.ID = strconv.FormatInt(orderData[y].ID, 10)
+ tempResp.Amount = orderData[y].Size
+ tempResp.AssetType = assetType
+ tempResp.ClientOrderID = orderData[y].ClientID
+ tempResp.Date = orderData[y].CreatedAt
+ tempResp.Exchange = f.Name
+ tempResp.ExecutedAmount = orderData[y].Size - orderData[y].RemainingSize
+ tempResp.Pair = currency.NewPairFromString(orderData[y].Market)
+ tempResp.Price = orderData[y].Price
+ tempResp.RemainingAmount = orderData[y].RemainingSize
+ var orderVars OrderVars
+ orderVars, err = f.compatibleOrderVars(orderData[y].Side,
+ orderData[y].Status,
+ orderData[y].OrderType,
+ orderData[y].FilledSize,
+ orderData[y].Size,
+ orderData[y].AvgFillPrice)
+ if err != nil {
+ return resp, err
+ }
+ tempResp.Status = orderVars.Status
+ tempResp.Side = orderVars.Side
+ tempResp.Type = orderVars.OrderType
+ tempResp.Fee = orderVars.Fee
+ resp = append(resp, tempResp)
+ }
+ triggerOrderData, err := f.GetOpenTriggerOrders(f.FormatExchangeCurrency(getOrdersRequest.Pairs[x], assetType).String(), getOrdersRequest.Type.String())
+ if err != nil {
+ return resp, err
+ }
+ for z := range triggerOrderData {
+ tempResp.ID = strconv.FormatInt(triggerOrderData[z].ID, 10)
+ tempResp.Amount = triggerOrderData[z].Size
+ tempResp.AssetType = assetType
+ tempResp.Date = triggerOrderData[z].CreatedAt
+ tempResp.Exchange = f.Name
+ tempResp.ExecutedAmount = triggerOrderData[z].FilledSize
+ tempResp.Pair = currency.NewPairFromString(triggerOrderData[z].Market)
+ tempResp.Price = triggerOrderData[z].AvgFillPrice
+ tempResp.RemainingAmount = triggerOrderData[z].Size - triggerOrderData[z].FilledSize
+ tempResp.TriggerPrice = triggerOrderData[z].TriggerPrice
+ orderVars, err := f.compatibleOrderVars(triggerOrderData[z].Side,
+ triggerOrderData[z].Status,
+ triggerOrderData[z].OrderType,
+ triggerOrderData[z].FilledSize,
+ triggerOrderData[z].Size,
+ triggerOrderData[z].AvgFillPrice)
+ if err != nil {
+ return resp, err
+ }
+ tempResp.Status = orderVars.Status
+ tempResp.Side = orderVars.Side
+ tempResp.Type = orderVars.OrderType
+ tempResp.Fee = orderVars.Fee
+ resp = append(resp, tempResp)
+ }
+ }
+ return resp, nil
+}
+
+// GetOrderHistory retrieves account order information
+// Can Limit response to specific order status
+func (f *FTX) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([]order.Detail, error) {
+ var resp []order.Detail
+ for x := range getOrdersRequest.Pairs {
+ var tempResp order.Detail
+ assetType, err := f.GetPairAssetType(getOrdersRequest.Pairs[x])
+ if err != nil {
+ return resp, err
+ }
+ orderData, err := f.FetchOrderHistory(f.FormatExchangeCurrency(getOrdersRequest.Pairs[x], assetType).String(),
+ getOrdersRequest.StartTicks, getOrdersRequest.EndTicks, "")
+ if err != nil {
+ return resp, err
+ }
+ for y := range orderData {
+ tempResp.ID = strconv.FormatInt(orderData[y].ID, 10)
+ tempResp.Amount = orderData[y].Size
+ tempResp.AssetType = assetType
+ tempResp.ClientOrderID = orderData[y].ClientID
+ tempResp.Date = orderData[y].CreatedAt
+ tempResp.Exchange = f.Name
+ tempResp.ExecutedAmount = orderData[y].Size - orderData[y].RemainingSize
+ tempResp.Pair = currency.NewPairFromString(orderData[y].Market)
+ tempResp.Price = orderData[y].Price
+ tempResp.RemainingAmount = orderData[y].RemainingSize
+ var orderVars OrderVars
+ orderVars, err = f.compatibleOrderVars(orderData[y].Side,
+ orderData[y].Status,
+ orderData[y].OrderType,
+ orderData[y].FilledSize,
+ orderData[y].Size,
+ orderData[y].AvgFillPrice)
+ if err != nil {
+ return resp, err
+ }
+ tempResp.Status = orderVars.Status
+ tempResp.Side = orderVars.Side
+ tempResp.Type = orderVars.OrderType
+ tempResp.Fee = orderVars.Fee
+ resp = append(resp, tempResp)
+ }
+ triggerOrderData, err := f.GetTriggerOrderHistory(f.FormatExchangeCurrency(getOrdersRequest.Pairs[x], assetType).String(),
+ getOrdersRequest.StartTicks, getOrdersRequest.EndTicks, strings.ToLower(getOrdersRequest.Side.String()), strings.ToLower(getOrdersRequest.Type.String()), "")
+ if err != nil {
+ return resp, err
+ }
+ for z := range triggerOrderData {
+ tempResp.ID = strconv.FormatInt(triggerOrderData[z].ID, 10)
+ tempResp.Amount = triggerOrderData[z].Size
+ tempResp.AssetType = assetType
+ tempResp.Date = triggerOrderData[z].CreatedAt
+ tempResp.Exchange = f.Name
+ tempResp.ExecutedAmount = triggerOrderData[z].FilledSize
+ tempResp.Pair = currency.NewPairFromString(triggerOrderData[z].Market)
+ tempResp.Price = triggerOrderData[z].AvgFillPrice
+ tempResp.RemainingAmount = triggerOrderData[z].Size - triggerOrderData[z].FilledSize
+ tempResp.TriggerPrice = triggerOrderData[z].TriggerPrice
+ orderVars, err := f.compatibleOrderVars(triggerOrderData[z].Side,
+ triggerOrderData[z].Status,
+ triggerOrderData[z].OrderType,
+ triggerOrderData[z].FilledSize,
+ triggerOrderData[z].Size,
+ triggerOrderData[z].AvgFillPrice)
+ if err != nil {
+ return resp, err
+ }
+ tempResp.Status = orderVars.Status
+ tempResp.Side = orderVars.Side
+ tempResp.Type = orderVars.OrderType
+ tempResp.Fee = orderVars.Fee
+ resp = append(resp, tempResp)
+ }
+ }
+ return resp, nil
+}
+
+// GetFeeByType returns an estimate of fee based on the type of transaction
+func (f *FTX) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) {
+ return f.GetFee(feeBuilder)
+}
+
+// SubscribeToWebsocketChannels appends to ChannelsToSubscribe
+// which lets websocket.manageSubscriptions handle subscribing
+func (f *FTX) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error {
+ f.Websocket.SubscribeToChannels(channels)
+ return nil
+}
+
+// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe
+// which lets websocket.manageSubscriptions handle unsubscribing
+func (f *FTX) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error {
+ f.Websocket.RemoveSubscribedChannels(channels)
+ return nil
+}
+
+// GetSubscriptions returns a copied list of subscriptions
+func (f *FTX) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) {
+ return f.Websocket.GetSubscriptions(), nil
+}
+
+// AuthenticateWebsocket sends an authentication message to the websocket
+func (f *FTX) AuthenticateWebsocket() error {
+ return f.WsAuth()
+}
+
+// ValidateCredentials validates current credentials used for wrapper
+// functionality
+func (f *FTX) ValidateCredentials() error {
+ _, err := f.UpdateAccountInfo()
+ return f.CheckTransientError(err)
+}
+
+// GetHistoricCandles returns candles between a time period for a set time interval
+func (f *FTX) GetHistoricCandles(pair currency.Pair, a asset.Item, start, end time.Time, interval time.Duration) (kline.Item, error) {
+ intervalToString, err := parseInterval(interval)
+ if err != nil {
+ return kline.Item{}, err
+ }
+ var resp kline.Item
+ ohlcData, err := f.GetHistoricalData(f.FormatExchangeCurrency(pair, a).String(),
+ string(intervalToString), "", start, end)
+ if err != nil {
+ return resp, err
+ }
+ resp.Exchange = f.Name
+ resp.Asset = a
+ resp.Pair = pair
+ for x := range ohlcData {
+ var tempData kline.Candle
+ tempData.Open = ohlcData[x].Open
+ tempData.High = ohlcData[x].High
+ tempData.Low = ohlcData[x].Low
+ tempData.Close = ohlcData[x].Close
+ tempData.Volume = ohlcData[x].Volume
+ tempData.Time = ohlcData[x].StartTime
+ resp.Candles = append(resp.Candles, tempData)
+ }
+ return resp, nil
+}
diff --git a/exchanges/gateio/gateio_wrapper.go b/exchanges/gateio/gateio_wrapper.go
index eb6a342f..ea91b94c 100644
--- a/exchanges/gateio/gateio_wrapper.go
+++ b/exchanges/gateio/gateio_wrapper.go
@@ -408,8 +408,8 @@ func (g *Gateio) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (g *Gateio) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (g *Gateio) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/gemini/gemini_wrapper.go b/exchanges/gemini/gemini_wrapper.go
index 3ed2bf34..0a236954 100644
--- a/exchanges/gemini/gemini_wrapper.go
+++ b/exchanges/gemini/gemini_wrapper.go
@@ -330,8 +330,8 @@ func (g *Gemini) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (g *Gemini) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (g *Gemini) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/hitbtc/hitbtc_wrapper.go b/exchanges/hitbtc/hitbtc_wrapper.go
index 0557c928..ee5bf8c3 100644
--- a/exchanges/hitbtc/hitbtc_wrapper.go
+++ b/exchanges/hitbtc/hitbtc_wrapper.go
@@ -386,8 +386,8 @@ func (h *HitBTC) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (h *HitBTC) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (h *HitBTC) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/huobi/huobi_wrapper.go b/exchanges/huobi/huobi_wrapper.go
index a0c927a1..9078799e 100644
--- a/exchanges/huobi/huobi_wrapper.go
+++ b/exchanges/huobi/huobi_wrapper.go
@@ -498,8 +498,8 @@ func (h *HUOBI) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (h *HUOBI) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (h *HUOBI) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/interfaces.go b/exchanges/interfaces.go
index 2616c5d9..ae0ae9ba 100644
--- a/exchanges/interfaces.go
+++ b/exchanges/interfaces.go
@@ -39,7 +39,7 @@ type IBotExchange interface {
GetAuthenticatedAPISupport(endpoint uint8) bool
SetPairs(pairs currency.Pairs, a asset.Item, enabled bool) error
GetAssetTypes() asset.Items
- GetExchangeHistory(p currency.Pair, a asset.Item) ([]TradeHistory, error)
+ GetExchangeHistory(p currency.Pair, a asset.Item, startTime, endTime time.Time) ([]TradeHistory, error)
SupportsAutoPairUpdates() bool
SupportsRESTTickerBatchUpdates() bool
GetFeeByType(f *FeeBuilder) (float64, error)
diff --git a/exchanges/itbit/itbit_wrapper.go b/exchanges/itbit/itbit_wrapper.go
index 57070886..d5f54e03 100644
--- a/exchanges/itbit/itbit_wrapper.go
+++ b/exchanges/itbit/itbit_wrapper.go
@@ -304,8 +304,8 @@ func (i *ItBit) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (i *ItBit) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (i *ItBit) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/kline/types.go b/exchanges/kline/types.go
index 4d38477b..b3957c89 100644
--- a/exchanges/kline/types.go
+++ b/exchanges/kline/types.go
@@ -9,19 +9,20 @@ import (
// Consts here define basic time intervals
const (
- OneMin = time.Minute
- ThreeMin = 3 * time.Minute
- FiveMin = 5 * time.Minute
- FifteenMin = 15 * time.Minute
- ThirtyMin = 30 * time.Minute
- OneHour = 1 * time.Hour
- TwoHour = 2 * time.Hour
- FourHour = 4 * time.Hour
- SixHour = 6 * time.Hour
- TwelveHour = 12 * time.Hour
- OneDay = 24 * time.Hour
- ThreeDay = 72 * time.Hour
- OneWeek = 168 * time.Hour
+ FifteenSecond = 15 * time.Second
+ OneMin = time.Minute
+ ThreeMin = 3 * time.Minute
+ FiveMin = 5 * time.Minute
+ FifteenMin = 15 * time.Minute
+ ThirtyMin = 30 * time.Minute
+ OneHour = 1 * time.Hour
+ TwoHour = 2 * time.Hour
+ FourHour = 4 * time.Hour
+ SixHour = 6 * time.Hour
+ TwelveHour = 12 * time.Hour
+ OneDay = 24 * time.Hour
+ ThreeDay = 72 * time.Hour
+ OneWeek = 168 * time.Hour
)
// Item holds all the relevant information for internal kline elements
diff --git a/exchanges/kraken/kraken_wrapper.go b/exchanges/kraken/kraken_wrapper.go
index 8a2451d6..65e575e5 100644
--- a/exchanges/kraken/kraken_wrapper.go
+++ b/exchanges/kraken/kraken_wrapper.go
@@ -450,8 +450,8 @@ func (k *Kraken) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (k *Kraken) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (k *Kraken) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/lakebtc/lakebtc_wrapper.go b/exchanges/lakebtc/lakebtc_wrapper.go
index ceb42a7d..def38cc6 100644
--- a/exchanges/lakebtc/lakebtc_wrapper.go
+++ b/exchanges/lakebtc/lakebtc_wrapper.go
@@ -335,8 +335,8 @@ func (l *LakeBTC) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (l *LakeBTC) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (l *LakeBTC) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/lbank/lbank_wrapper.go b/exchanges/lbank/lbank_wrapper.go
index 7eea8a92..014a3f85 100644
--- a/exchanges/lbank/lbank_wrapper.go
+++ b/exchanges/lbank/lbank_wrapper.go
@@ -304,8 +304,8 @@ func (l *Lbank) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (l *Lbank) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (l *Lbank) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrFunctionNotSupported
}
diff --git a/exchanges/localbitcoins/localbitcoins_wrapper.go b/exchanges/localbitcoins/localbitcoins_wrapper.go
index 98b1834d..3ea0cfa6 100644
--- a/exchanges/localbitcoins/localbitcoins_wrapper.go
+++ b/exchanges/localbitcoins/localbitcoins_wrapper.go
@@ -285,8 +285,8 @@ func (l *LocalBitcoins) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (l *LocalBitcoins) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (l *LocalBitcoins) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/okgroup/okgroup_wrapper.go b/exchanges/okgroup/okgroup_wrapper.go
index fe9ba7ab..d26ee67a 100644
--- a/exchanges/okgroup/okgroup_wrapper.go
+++ b/exchanges/okgroup/okgroup_wrapper.go
@@ -5,6 +5,7 @@ import (
"fmt"
"strconv"
"strings"
+ "time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
@@ -261,8 +262,8 @@ func (o *OKGroup) GetFundingHistory() (resp []exchange.FundHistory, err error) {
return resp, err
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (o *OKGroup) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (o *OKGroup) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/order/order_types.go b/exchanges/order/order_types.go
index 2116e4b4..3c443205 100644
--- a/exchanges/order/order_types.go
+++ b/exchanges/order/order_types.go
@@ -43,6 +43,7 @@ type Submit struct {
ID string
AccountID string
ClientID string
+ ClientOrderID string
WalletAddress string
Type Type
Side Side
@@ -83,6 +84,7 @@ type Modify struct {
Exchange string
InternalOrderID string
ID string
+ ClientOrderID string
AccountID string
ClientID string
WalletAddress string
@@ -122,6 +124,7 @@ type Detail struct {
Exchange string
InternalOrderID string
ID string
+ ClientOrderID string
AccountID string
ClientID string
WalletAddress string
@@ -145,6 +148,7 @@ type Cancel struct {
Amount float64
Exchange string
ID string
+ ClientOrderID string
AccountID string
ClientID string
WalletAddress string
diff --git a/exchanges/poloniex/poloniex_wrapper.go b/exchanges/poloniex/poloniex_wrapper.go
index 17413be1..0fcfc956 100644
--- a/exchanges/poloniex/poloniex_wrapper.go
+++ b/exchanges/poloniex/poloniex_wrapper.go
@@ -370,8 +370,8 @@ func (p *Poloniex) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (p *Poloniex) GetExchangeHistory(currencyPair currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (p *Poloniex) GetExchangeHistory(currencyPair currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/support.go b/exchanges/support.go
index bd3ea4a3..4cd8a546 100644
--- a/exchanges/support.go
+++ b/exchanges/support.go
@@ -27,6 +27,7 @@ var Exchanges = []string{
"coinbene",
"coinut",
"exmo",
+ "ftx",
"gateio",
"gemini",
"hitbtc",
diff --git a/exchanges/yobit/yobit_wrapper.go b/exchanges/yobit/yobit_wrapper.go
index 4b43cf2b..06133d63 100644
--- a/exchanges/yobit/yobit_wrapper.go
+++ b/exchanges/yobit/yobit_wrapper.go
@@ -321,8 +321,8 @@ func (y *Yobit) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (y *Yobit) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (y *Yobit) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/exchanges/zb/zb_wrapper.go b/exchanges/zb/zb_wrapper.go
index a8f8522e..fe62dadc 100644
--- a/exchanges/zb/zb_wrapper.go
+++ b/exchanges/zb/zb_wrapper.go
@@ -374,8 +374,8 @@ func (z *ZB) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
-// GetExchangeHistory returns historic trade data since exchange opening.
-func (z *ZB) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
+// GetExchangeHistory returns historic trade data within the timeframe provided.
+func (z *ZB) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
return nil, common.ErrNotYetImplemented
}
diff --git a/gctscript/wrappers/gct/exchange/exchange_test.go b/gctscript/wrappers/gct/exchange/exchange_test.go
index 3971e79b..c846b33c 100644
--- a/gctscript/wrappers/gct/exchange/exchange_test.go
+++ b/gctscript/wrappers/gct/exchange/exchange_test.go
@@ -58,8 +58,8 @@ func TestExchange_Exchanges(t *testing.T) {
t.Parallel()
x := exchangeTest.Exchanges(false)
y := len(x)
- if y != 27 {
- t.Fatalf("expected 27 received %v", y)
+ if y != 28 {
+ t.Fatalf("expected 28 received %v", y)
}
}
diff --git a/go.sum b/go.sum
index 42202390..105d6367 100644
--- a/go.sum
+++ b/go.sum
@@ -160,7 +160,9 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/kat-co/vala v0.0.0-20170210184112-42e1d8b61f12 h1:DQVOxR9qdYEybJUr/c7ku34r3PfajaMYXZwgDM7KuSk=
github.com/kat-co/vala v0.0.0-20170210184112-42e1d8b61f12/go.mod h1:u9MdXq/QageOOSGp7qG4XAQsYUMP+V5zEel/Vrl6OOc=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
@@ -231,6 +233,7 @@ github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9Nz
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
diff --git a/testdata/configtest.json b/testdata/configtest.json
index 8dd9c220..b452a883 100644
--- a/testdata/configtest.json
+++ b/testdata/configtest.json
@@ -2430,6 +2430,63 @@
"supportedCurrencies": ""
}
]
+ },
+ {
+ "name": "FTX",
+ "enabled": true,
+ "verbose": false,
+ "httpTimeout": 0,
+ "websocketResponseCheckTimeout": 0,
+ "websocketResponseMaxLimit": 0,
+ "websocketTrafficTimeout": 0,
+ "websocketOrderbookBufferLimit": 0,
+ "baseCurrencies": "USD",
+ "currencyPairs": {
+ "assetTypes": [
+ "spot",
+ "futures"
+ ],
+ "pairs": {
+ "futures": {
+ "enabled": "DOGE-PERP",
+ "available": "ADA-PERP,ADA-0626,ALGO-PERP,ALGO-0626,ALT-PERP,ALT-0626,ATOM-PERP,ATOM-0626,BCH-PERP,BCH-0626,BNB-PERP,BNB-0626,BSV-PERP,BSV-0626,BTC-PERP,BTC-MOVE,BTC-MOVE,BTC-MOVE,BTC-MOVE,BTC-MOVE,BTC-MOVE,BTC-0626,BTC-MOVE,BTC-0925,BTC-MOVE,BTC-MOVE,BTMX-PERP,BTMX-0626,DOGE-PERP,DOGE-0626,DRGN-PERP,DRGN-0626,EOS-PERP,EOS-0626,ETC-PERP,ETC-0626,ETH-PERP,ETH-0626,EXCH-PERP,EXCH-0626,HT-PERP,HT-0626,LEO-PERP,LEO-0626,LINK-PERP,LINK-0626,LTC-PERP,LTC-0626,MATIC-PERP,MATIC-0626,MID-PERP,MID-0626,OIL100-0525,OKB-PERP,OKB-0626,PAXG-PERP,PAXG-0626,BERNIE,BIDEN,BLOOMBERG,PETE,TRUMP,WARREN,PRIV-PERP,PRIV-0626,SHIT-PERP,SHIT-0626,TOMO-PERP,TOMO-0626,TRX-PERP,TRX-0626,TRYB-PERP,TRYB-0626,USDT-PERP,USDT-0626,XAUT-PERP,XAUT-0626,XRP-PERP,XRP-0626,XTZ-PERP,XTZ-0626",
+ "requestFormat": {
+ "uppercase": true,
+ "delimiter": "-"
+ },
+ "configFormat": {
+ "uppercase": true,
+ "delimiter": "-"
+ }
+ },
+ "spot": {
+ "enabled": "BTC/USD",
+ "available": "BCH/USD,BCH/USDT,BNB/USD,BNB/USDT,BTC/USD,BTC/USDT,BTMX/USD,ETH/USD,ETH/USDT,FTT/BTC,FTT/USD,FTT/USDT,LINK/USD,LINK/USDT,LTC/USD,LTC/USDT,PAXG/USD,PAXG/USDT,TRX/USD,TRX/USDT,TRYB/USD,USDT/USD,XAUT/USD,XAUT/USDT,ADABEAR/USD,ADABULL/USD,ADAHALF/USD,ADAHALF/USDT,ADAHEDGE/USD,ALGOBEAR/USD,ALGOBULL/USD,ALGOHALF/USD,ALGOHALF/USDT,ALGOHEDGE/USD,ALTBEAR/USD,ALTBULL/USD,ALTHALF/USD,ALTHALF/USDT,ALTHEDGE/USD,ATOMBEAR/USD,ATOMBULL/USD,ATOMHALF/USD,ATOMHALF/USDT,ATOMHEDGE/USD,BCHBEAR/USD,BCHBEAR/USDT,BCHBULL/USD,BCHBULL/USDT,BCHHALF/USD,BCHHALF/USDT,BCHHEDGE/USD,BEAR/USD,BEAR/USDT,BEARSHIT/USD,BNBBEAR/USD,BNBBEAR/USDT,BNBBULL/USD,BNBBULL/USDT,BNBHALF/USD,BNBHALF/USDT,BNBHEDGE/USD,BSVBEAR/USD,BSVBEAR/USDT,BSVBULL/USD,BSVBULL/USDT,BSVHALF/USD,BSVHALF/USDT,BSVHEDGE/USD,BTMXBEAR/USD,BTMXBEAR/USDT,BTMXBULL/USD,BTMXBULL/USDT,BTMXHALF/USD,BTMXHALF/USDT,BTMXHEDGE/USD,BULL/USD,BULL/USDT,BULLSHIT/USD,BVOL/USD,BVOL/USDT,DOGEBEAR/USD,DOGEBULL/USD,DOGEHALF/USD,DOGEHALF/USDT,DOGEHEDGE/USD,DRGNBEAR/USD,DRGNBULL/USD,DRGNHALF/USD,DRGNHALF/USDT,DRGNHEDGE/USD,EOSBEAR/USD,EOSBEAR/USDT,EOSBULL/USD,EOSBULL/USDT,EOSHALF/USD,EOSHALF/USDT,EOSHEDGE/USD,ETCBEAR/USD,ETCBULL/USD,ETCHALF/USD,ETCHALF/USDT,ETCHEDGE/USD,ETHBEAR/USD,ETHBEAR/USDT,ETHBULL/USD,ETHBULL/USDT,ETHHALF/USD,ETHHALF/USDT,ETHHEDGE/USD,EXCHBEAR/USD,EXCHBULL/USD,EXCHHALF/USD,EXCHHALF/USDT,EXCHHEDGE/USD,HALF/USD,HALF/USDT,HALFSHIT/USD,HALFSHIT/USDT,HEDGE/USD,HEDGESHIT/USD,HTBEAR/USD,HTBULL/USD,HTHALF/USD,HTHALF/USDT,HTHEDGE/USD,IBVOL/USD,IBVOL/USDT,LEOBEAR/USD,LEOBULL/USD,LEOHALF/USD,LEOHALF/USDT,LEOHEDGE/USD,LINKBEAR/USD,LINKBEAR/USDT,LINKBULL/USD,LINKBULL/USDT,LINKHALF/USD,LINKHALF/USDT,LINKHEDGE/USD,LTCBEAR/USD,LTCBEAR/USDT,LTCBULL/USD,LTCBULL/USDT,LTCHALF/USD,LTCHALF/USDT,LTCHEDGE/USD,MATICBEAR/USD,MATICBULL/USD,MATICHALF/USD,MATICHALF/USDT,MATICHEDGE/USD,MIDBEAR/USD,MIDBULL/USD,MIDHALF/USD,MIDHALF/USDT,MIDHEDGE/USD,OKBBEAR/USD,OKBBULL/USD,OKBHALF/USD,OKBHALF/USDT,OKBHEDGE/USD,PAXGBEAR/USD,PAXGBULL/USD,PAXGHALF/USD,PAXGHALF/USDT,PAXGHEDGE/USD,PRIVBEAR/USD,PRIVBULL/USD,PRIVHALF/USD,PRIVHALF/USDT,PRIVHEDGE/USD,TOMOBEAR/USD,TOMOBULL/USD,TOMOHALF/USD,TOMOHALF/USDT,TOMOHEDGE/USD,TRXBEAR/USD,TRXBULL/USD,TRXHALF/USD,TRXHALF/USDT,TRXHEDGE/USD,TRYBBEAR/USD,TRYBBULL/USD,TRYBHALF/USD,TRYBHALF/USDT,TRYBHEDGE/USD,USDTBEAR/USD,USDTBULL/USD,USDTHALF/USD,USDTHALF/USDT,USDTHEDGE/USD,XAUTBEAR/USD,XAUTBULL/USD,XAUTHALF/USD,XAUTHALF/USDT,XAUTHEDGE/USD,XRPBEAR/USD,XRPBEAR/USDT,XRPBULL/USD,XRPBULL/USDT,XRPHALF/USD,XRPHALF/USDT,XRPHEDGE/USD,XTZBEAR/USD,XTZBEAR/USDT,XTZBULL/USD,XTZBULL/USDT,XTZHALF/USD,XTZHALF/USDT,XTZHEDGE/USD",
+ "requestFormat": {
+ "uppercase": true,
+ "delimiter": "/"
+ },
+ "configFormat": {
+ "uppercase": true,
+ "delimiter": "/"
+ }
+ }
+ }
+ },
+ "api": {
+ "authenticatedSupport": false,
+ "authenticatedWebsocketApiSupport": true,
+ "endpoints": {
+ "url": "",
+ "urlSecondary": "",
+ "websocketURL": ""
+ },
+ "credentials": {
+ "key": "Key",
+ "secret": "Secret"
+ }
+ },
+ "features": null
}
],
"bankAccounts": [