diff --git a/common/common.go b/common/common.go index 556d2667..28f81664 100644 --- a/common/common.go +++ b/common/common.go @@ -29,6 +29,11 @@ import ( "time" ) +// Vars for common.go operations +var ( + HTTPClient *http.Client +) + // Const declarations for common.go operations const ( HashSHA1 = iota @@ -40,6 +45,20 @@ const ( WeiPerEther = 1000000000000000000 ) +func initialiseHTTPClient() { + // If the HTTPClient isn't set, start a new client with a default timeout of 5 seconds + if HTTPClient == nil { + HTTPClient = NewHTTPClientWithTimeout(time.Duration(time.Second * 5)) + } +} + +// NewHTTPClientWithTimeout initalises a new HTTP client with the specified +// timeout duration +func NewHTTPClientWithTimeout(t time.Duration) *http.Client { + h := &http.Client{Timeout: t} + return h +} + // GetMD5 returns a MD5 hash of a byte array func GetMD5(input []byte) []byte { hash := md5.New() @@ -155,6 +174,17 @@ func StringDataCompare(haystack []string, needle string) bool { return false } +// StringDataCompareUpper data checks the substring array with an input and returns +// a bool irrespective of lower or upper case strings +func StringDataCompareUpper(haystack []string, needle string) bool { + for x := range haystack { + if StringToUpper(haystack[x]) == StringToUpper(needle) { + return true + } + } + return false +} + // StringDataContainsUpper checks the substring array with an input and returns // a bool irrespective of lower or upper case strings func StringDataContainsUpper(haystack []string, needle string) bool { @@ -288,6 +318,8 @@ func SendHTTPRequest(method, path string, headers map[string]string, body io.Rea return "", errors.New("invalid HTTP method specified") } + initialiseHTTPClient() + req, err := http.NewRequest(method, path, body) if err != nil { @@ -298,8 +330,7 @@ func SendHTTPRequest(method, path string, headers map[string]string, body io.Rea req.Header.Add(k, v) } - httpClient := &http.Client{} - resp, err := httpClient.Do(req) + resp, err := HTTPClient.Do(req) if err != nil { return "", err @@ -323,7 +354,9 @@ func SendHTTPGetRequest(url string, jsonDecode, isVerbose bool, result interface log.Println("Raw URL: ", url) } - res, err := http.Get(url) + initialiseHTTPClient() + + res, err := HTTPClient.Get(url) if err != nil { return err } @@ -346,7 +379,6 @@ func SendHTTPGetRequest(url string, jsonDecode, isVerbose bool, result interface if jsonDecode { err := JSONDecode(contents, result) if err != nil { - log.Println(string(contents[:])) return err } } diff --git a/common/common_test.go b/common/common_test.go index c70da38f..f97a26e8 100644 --- a/common/common_test.go +++ b/common/common_test.go @@ -281,6 +281,26 @@ func TestStringDataCompare(t *testing.T) { } } +func TestStringDataCompareUpper(t *testing.T) { + t.Parallel() + originalHaystack := []string{"hello", "WoRld", "USDT", "Contains", "string"} + originalNeedle := "WoRld" + anotherNeedle := "WoRldD" + expectedOutput := true + expectedOutputTwo := false + actualResult := StringDataCompareUpper(originalHaystack, originalNeedle) + if actualResult != expectedOutput { + t.Errorf("Test failed. Expected '%v'. Actual '%v'", + expectedOutput, actualResult) + } + + actualResult = StringDataCompareUpper(originalHaystack, anotherNeedle) + if actualResult != expectedOutputTwo { + t.Errorf("Test failed. Expected '%v'. Actual '%v'", + expectedOutput, actualResult) + } +} + func TestStringDataContainsUpper(t *testing.T) { t.Parallel() originalHaystack := []string{"bLa", "BrO", "sUp"} diff --git a/config/config.go b/config/config.go index d4329d94..07fcc22c 100644 --- a/config/config.go +++ b/config/config.go @@ -28,6 +28,7 @@ const ( configFileEncryptionEnabled = 1 configFileEncryptionDisabled = -1 configPairsLastUpdatedWarningThreshold = 30 // 30 days + configDefaultHTTPTimeout = time.Duration(time.Second * 15) ) // Variables here are mainly alerts and a configuration object @@ -95,6 +96,7 @@ type Config struct { CurrencyExchangeProvider string CurrencyPairFormat *CurrencyPairFormatConfig `json:"CurrencyPairFormat"` FiatDisplayCurrency string + GlobalHTTPTimeout time.Duration Portfolio portfolio.Base `json:"PortfolioAddresses"` SMS SMSGlobalConfig `json:"SMSGlobal"` Webserver WebserverConfig `json:"Webserver"` @@ -110,6 +112,7 @@ type ExchangeConfig struct { Websocket bool UseSandbox bool RESTPollingDelay time.Duration + HTTPTimeout time.Duration AuthenticatedAPISupport bool APIKey string APISecret string @@ -304,6 +307,11 @@ func (c *Config) CheckExchangeConfigValues() error { log.Printf(WarningPairsLastUpdatedThresholdExceeded, exch.Name, configPairsLastUpdatedWarningThreshold) } } + + if exch.HTTPTimeout <= 0 { + log.Printf("Exchange %s HTTP Timeout value not set, defaulting to %v.", exch.Name, configDefaultHTTPTimeout) + c.Exchanges[i].HTTPTimeout = configDefaultHTTPTimeout + } exchanges++ } } @@ -552,6 +560,11 @@ func (c *Config) LoadConfig(configPath string) error { c.FiatDisplayCurrency = "USD" } + if c.GlobalHTTPTimeout <= 0 { + log.Printf("Global HTTP Timeout value not set, defaulting to %v.", configDefaultHTTPTimeout) + c.GlobalHTTPTimeout = configDefaultHTTPTimeout + } + return nil } diff --git a/config_example.json b/config_example.json index 014477fd..5eddb8d9 100644 --- a/config_example.json +++ b/config_example.json @@ -8,18 +8,19 @@ "Delimiter": "-" }, "FiatDisplayCurrency": "USD", + "GlobalHTTPTimeout": 15000000000, "PortfolioAddresses": { "Addresses": [ { "Address": "1JCe8z4jJVNXSjohjM4i9Hh813dLCNx2Sy", "CoinType": "BTC", - "Balance": 53000.01245183, + "Balance": 53000.01252609, "Description": "" }, { "Address": "3Nxwenay9Z8Lc9JBiywExpnEFiLp6Afp8v", "CoinType": "BTC", - "Balance": 99848.27232691, + "Balance": 103848.27285787, "Description": "" }, { @@ -64,6 +65,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -89,10 +91,11 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", - "AvailablePairs": "ETH-BTC,LTC-BTC,BNB-BTC,NEO-BTC,123-456,QTUM-ETH,EOS-ETH,SNT-ETH,BNT-ETH,BCC-BTC,GAS-BTC,BNB-ETH,BTC-USDT,ETH-USDT,HSR-BTC,OAX-ETH,DNT-ETH,MCO-ETH,ICN-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,SNGLS-ETH,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,CTR-BTC,CTR-ETH,SALT-BTC,SALT-ETH,MDA-BTC,MDA-ETH,MTL-BTC,MTL-ETH,SUB-BTC,SUB-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,ICN-BTC,BTG-BTC,BTG-ETH,EVX-BTC,EVX-ETH,REQ-BTC,REQ-ETH,VIB-BTC,VIB-ETH,HSR-ETH,TRX-BTC,TRX-ETH,POWR-BTC,POWR-ETH,ARK-BTC,ARK-ETH,YOYO-ETH,XRP-BTC,XRP-ETH,MOD-BTC,MOD-ETH,ENJ-BTC,ENJ-ETH,STORJ-BTC,STORJ-ETH,BNB-USDT,VEN-BNB,YOYO-BNB,POWR-BNB,VEN-BTC,VEN-ETH,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,BCC-ETH,BCC-USDT,BCC-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,BTS-BNB,XZC-BTC,XZC-ETH,XZC-BNB,LSK-BTC,LSK-ETH,LSK-BNB,TNT-BTC,TNT-ETH,FUEL-BTC,FUEL-ETH,MANA-BTC,MANA-ETH,BCD-BTC,BCD-ETH,DGD-BTC,DGD-ETH,IOTA-BNB,ADX-BTC,ADX-ETH,ADX-BNB,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,WINGS-BTC,WINGS-ETH,NAV-BTC,NAV-ETH,NAV-BNB,LUN-BTC,LUN-ETH,TRIG-BTC,TRIG-ETH,TRIG-BNB,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,CHAT-BTC,CHAT-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,RPX-BTC,RPX-ETH,RPX-BNB,NCASH-BTC,NCASH-ETH,NCASH-BNB,POA-BTC,POA-ETH,POA-BNB,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,QLC-BTC,QLC-ETH", + "AvailablePairs": "ETH-BTC,LTC-BTC,BNB-BTC,NEO-BTC,QTUM-ETH,EOS-ETH,SNT-ETH,BNT-ETH,BCC-BTC,GAS-BTC,BNB-ETH,BTC-USDT,ETH-USDT,HSR-BTC,OAX-ETH,DNT-ETH,MCO-ETH,ICN-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,SNGLS-ETH,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,SALT-BTC,SALT-ETH,MDA-BTC,MDA-ETH,MTL-BTC,MTL-ETH,SUB-BTC,SUB-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,ICN-BTC,BTG-BTC,BTG-ETH,EVX-BTC,EVX-ETH,REQ-BTC,REQ-ETH,VIB-BTC,VIB-ETH,HSR-ETH,TRX-BTC,TRX-ETH,POWR-BTC,POWR-ETH,ARK-BTC,ARK-ETH,YOYO-ETH,XRP-BTC,XRP-ETH,MOD-BTC,MOD-ETH,ENJ-BTC,ENJ-ETH,STORJ-BTC,STORJ-ETH,BNB-USDT,VEN-BNB,YOYO-BNB,POWR-BNB,VEN-BTC,VEN-ETH,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,BCC-ETH,BCC-USDT,BCC-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,BTS-BNB,XZC-BTC,XZC-ETH,XZC-BNB,LSK-BTC,LSK-ETH,LSK-BNB,TNT-BTC,TNT-ETH,FUEL-BTC,FUEL-ETH,MANA-BTC,MANA-ETH,BCD-BTC,BCD-ETH,DGD-BTC,DGD-ETH,IOTA-BNB,ADX-BTC,ADX-ETH,ADX-BNB,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,WINGS-BTC,WINGS-ETH,NAV-BTC,NAV-ETH,NAV-BNB,LUN-BTC,LUN-ETH,TRIG-BTC,TRIG-ETH,TRIG-BNB,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,CHAT-BTC,CHAT-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,RPX-BTC,RPX-ETH,RPX-BNB,NCASH-BTC,NCASH-ETH,NCASH-BNB,POA-BTC,POA-ETH,POA-BNB,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,CLOAK-BTC,CLOAK-ETH,GNT-BTC,GNT-ETH,GNT-BNB,LOOM-BTC,LOOM-ETH,LOOM-BNB", "EnabledPairs": "BTC-USDT", "BaseCurrencies": "USD", "AssetTypes": "SPOT", @@ -112,10 +115,11 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", - "AvailablePairs": "BTCUSD,LTCUSD,LTCBTC,ETHUSD,ETHBTC,ETCBTC,ETCUSD,RRTUSD,RRTBTC,ZECUSD,ZECBTC,XMRUSD,XMRBTC,DSHUSD,DSHBTC,BTCEUR,XRPUSD,XRPBTC,IOTUSD,IOTBTC,IOTETH,EOSUSD,EOSBTC,EOSETH,SANUSD,SANBTC,SANETH,OMGUSD,OMGBTC,OMGETH,BCHUSD,BCHBTC,BCHETH,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", + "AvailablePairs": "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,BCHUSD,BCHBTC,BCHETH,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,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,CFIUSD,CFIBTC,CFIETH,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", "EnabledPairs": "BTCUSD,LTCUSD,LTCBTC,ETHUSD,ETHBTC", "BaseCurrencies": "USD", "AssetTypes": "SPOT", @@ -134,6 +138,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -159,6 +164,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -184,6 +190,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -207,10 +214,11 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", - "AvailablePairs": "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-AUR,BTC-EFL,BTC-GLD,BTC-SLR,BTC-PTC,BTC-GRS,BTC-NLG,BTC-RBY,BTC-XWC,BTC-MONA,BTC-THC,BTC-ENRG,BTC-ERC,BTC-VRC,BTC-CURE,BTC-XMR,BTC-CLOAK,BTC-START,BTC-KORE,BTC-XDN,BTC-TRUST,BTC-NAV,BTC-XST,BTC-VIA,BTC-PINK,BTC-IOC,BTC-CANN,BTC-SYS,BTC-NEOS,BTC-DGB,BTC-BURST,BTC-EXCL,BTC-SWIFT,BTC-DOPE,BTC-BLOCK,BTC-ABY,BTC-BYC,BTC-XMG,BTC-BLITZ,BTC-BAY,BTC-FAIR,BTC-SPR,BTC-VTR,BTC-XRP,BTC-GAME,BTC-COVAL,BTC-NXS,BTC-XCP,BTC-BITB,BTC-GEO,BTC-FLDC,BTC-GRC,BTC-FLO,BTC-NBT,BTC-MUE,BTC-XEM,BTC-CLAM,BTC-DMD,BTC-GAM,BTC-SPHR,BTC-OK,BTC-SNRG,BTC-PKB,BTC-AEON,BTC-ETH,BTC-TX,BTC-BCY,BTC-EXP,BTC-OMNI,BTC-AMP,BTC-XLM,USDT-BTC,BTC-RVR,BTC-EMC,BTC-FCT,BTC-EGC,BTC-SLS,BTC-RADS,BTC-DCR,BTC-BSD,BTC-XVG,BTC-PIVX,BTC-MEME,BTC-STEEM,BTC-2GIVE,BTC-LSK,BTC-BRK,BTC-WAVES,BTC-LBC,BTC-SBD,BTC-BRX,BTC-ETC,ETH-ETC,BTC-STRAT,BTC-UNB,BTC-SYNX,BTC-EBST,BTC-VRM,BTC-SEQ,BTC-REP,BTC-SHIFT,BTC-ARDR,BTC-XZC,BTC-NEO,BTC-ZEC,BTC-ZCL,BTC-IOP,BTC-GOLOS,BTC-UBQ,BTC-KMD,BTC-GBG,BTC-SIB,BTC-ION,BTC-LMC,BTC-QWARK,BTC-CRW,BTC-SWT,BTC-MLN,BTC-ARK,BTC-DYN,BTC-TKS,BTC-MUSIC,BTC-DTB,BTC-INCNT,BTC-GBYTE,BTC-GNT,BTC-NXC,BTC-EDG,BTC-LGD,BTC-TRST,ETH-GNT,ETH-REP,USDT-ETH,ETH-WINGS,BTC-WINGS,BTC-RLC,BTC-GNO,BTC-GUP,BTC-LUN,ETH-GUP,ETH-RLC,ETH-LUN,ETH-GNO,BTC-HMQ,ETH-HMQ,BTC-ANT,ETH-TRST,ETH-ANT,BTC-SC,ETH-BAT,BTC-BAT,BTC-ZEN,BTC-QRL,ETH-QRL,BTC-CRB,ETH-CRB,ETH-LGD,BTC-PTOY,ETH-PTOY,BTC-CFI,ETH-CFI,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,ETH-ADT,BTC-PAY,ETH-PAY,BTC-STORJ,ETH-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-BCC,USDT-BCC,BTC-BCC,BTC-DNT,ETH-DNT,USDT-NEO,ETH-WAVES,ETH-STRAT,ETH-DGB,ETH-FCT,USDT-OMG,BTC-ADA,BTC-MANA,ETH-MANA,BTC-SALT,ETH-SALT,BTC-TIX,ETH-TIX,BTC-RCN,ETH-RCN,BTC-VIB,ETH-VIB,BTC-MER,BTC-POWR,ETH-POWR,BTC-BTG,ETH-BTG,USDT-BTG,ETH-ADA,BTC-ENG,ETH-ENG,USDT-ADA,USDT-XVG,USDT-NXT,BTC-UKG,ETH-UKG,BTC-IGNIS,BTC-SRN,ETH-SRN,BTC-WAX,ETH-WAX,BTC-ZRX,ETH-ZRX,BTC-VEE,ETH-VEE,BTC-BCPT,ETH-BCPT,BTC-TRX,ETH-TRX,BTC-TUSD,BTC-LRC,ETH-LRC,ETH-TUSD,BTC-UP,ETH-UP", + "AvailablePairs": "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-AUR,BTC-EFL,BTC-GLD,BTC-SLR,BTC-PTC,BTC-GRS,BTC-NLG,BTC-RBY,BTC-XWC,BTC-MONA,BTC-THC,BTC-ENRG,BTC-ERC,BTC-VRC,BTC-CURE,BTC-XMR,BTC-CLOAK,BTC-KORE,BTC-XDN,BTC-TRUST,BTC-NAV,BTC-XST,BTC-VIA,BTC-PINK,BTC-IOC,BTC-CANN,BTC-SYS,BTC-NEOS,BTC-DGB,BTC-BURST,BTC-EXCL,BTC-SWIFT,BTC-DOPE,BTC-BLOCK,BTC-ABY,BTC-BYC,BTC-XMG,BTC-BLITZ,BTC-BAY,BTC-SPR,BTC-VTR,BTC-XRP,BTC-GAME,BTC-COVAL,BTC-NXS,BTC-XCP,BTC-BITB,BTC-GEO,BTC-FLDC,BTC-GRC,BTC-FLO,BTC-NBT,BTC-MUE,BTC-XEM,BTC-CLAM,BTC-DMD,BTC-GAM,BTC-SPHR,BTC-OK,BTC-AEON,BTC-ETH,BTC-TX,BTC-BCY,BTC-EXP,BTC-OMNI,BTC-AMP,BTC-XLM,USDT-BTC,BTC-RVR,BTC-EMC,BTC-FCT,BTC-EGC,BTC-SLS,BTC-RADS,BTC-DCR,BTC-BSD,BTC-XVG,BTC-PIVX,BTC-MEME,BTC-STEEM,BTC-2GIVE,BTC-LSK,BTC-BRK,BTC-WAVES,BTC-LBC,BTC-SBD,BTC-BRX,BTC-ETC,ETH-ETC,BTC-STRAT,BTC-UNB,BTC-SYNX,BTC-EBST,BTC-VRM,BTC-SEQ,BTC-REP,BTC-SHIFT,BTC-ARDR,BTC-XZC,BTC-NEO,BTC-ZEC,BTC-ZCL,BTC-IOP,BTC-GOLOS,BTC-UBQ,BTC-KMD,BTC-GBG,BTC-SIB,BTC-ION,BTC-LMC,BTC-QWARK,BTC-CRW,BTC-SWT,BTC-MLN,BTC-ARK,BTC-DYN,BTC-TKS,BTC-MUSIC,BTC-DTB,BTC-INCNT,BTC-GBYTE,BTC-GNT,BTC-NXC,BTC-EDG,BTC-LGD,BTC-TRST,ETH-GNT,ETH-REP,USDT-ETH,ETH-WINGS,BTC-WINGS,BTC-RLC,BTC-GNO,BTC-GUP,BTC-LUN,ETH-GUP,ETH-RLC,ETH-LUN,ETH-GNO,BTC-HMQ,ETH-HMQ,BTC-ANT,ETH-TRST,ETH-ANT,BTC-SC,ETH-BAT,BTC-BAT,BTC-ZEN,BTC-QRL,ETH-QRL,BTC-CRB,ETH-CRB,ETH-LGD,BTC-PTOY,ETH-PTOY,BTC-CFI,ETH-CFI,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,ETH-ADT,BTC-PAY,ETH-PAY,BTC-STORJ,ETH-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-BCC,USDT-BCC,BTC-BCC,BTC-DNT,ETH-DNT,USDT-NEO,ETH-WAVES,ETH-STRAT,ETH-DGB,ETH-FCT,USDT-OMG,BTC-ADA,BTC-MANA,ETH-MANA,BTC-SALT,ETH-SALT,BTC-TIX,ETH-TIX,BTC-RCN,ETH-RCN,BTC-VIB,ETH-VIB,BTC-MER,BTC-POWR,ETH-POWR,BTC-BTG,ETH-BTG,USDT-BTG,ETH-ADA,BTC-ENG,ETH-ENG,USDT-ADA,USDT-XVG,USDT-NXT,BTC-UKG,ETH-UKG,BTC-IGNIS,BTC-SRN,ETH-SRN,BTC-WAX,ETH-WAX,BTC-ZRX,ETH-ZRX,BTC-VEE,ETH-VEE,BTC-BCPT,ETH-BCPT,BTC-TRX,ETH-TRX,BTC-TUSD,BTC-LRC,ETH-LRC,ETH-TUSD,BTC-UP,ETH-UP,BTC-DMT,ETH-DMT,USDT-TUSD,BTC-POLY,ETH-POLY,BTC-PRO,ETH-PRO,USDT-SC,USDT-TRX", "EnabledPairs": "USDT-BTC", "BaseCurrencies": "USD", "AssetTypes": "SPOT", @@ -231,6 +239,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -253,6 +262,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -275,11 +285,12 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", "ClientID": "ClientID", - "AvailablePairs": "ETCBTC,ETHUSDT,LTCSGD,BTCUSD,ETCLTC,LTCUSD,XMRBTC,ETCUSDT,LTCBTC,LTCUSDT,USDTSGD,USDTUSD,XMRLTC,BTCSGD,BTCUSDT,ETHBTC,ETHLTC,XMRUSDT", + "AvailablePairs": "LTCUSDT,BTCSGD,ETHUSDT,LTCSGD,ETHBTC,ETHSGD,LTCBTC,XMRBTC,ZECBTC,BTCUSD,ETCBTC,ETCUSDT,XMRUSDT,ETHLTC,LTCUSD,USDTUSD,XMRLTC,ZECLTC,ZECUSDT,BTCUSDT,ETCLTC,USDTSGD", "EnabledPairs": "LTCBTC,ETCBTC,ETHBTC", "BaseCurrencies": "USD", "AssetTypes": "SPOT", @@ -298,10 +309,11 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", - "AvailablePairs": "XRP_USD,XMR_USD,BTC_USD,ETH_USD,ETH_UAH,ETC_BTC,ETC_USD,XRP_BTC,ETH_USDT,USD_RUB,XMR_EUR,BTC_USDT,BTC_PLN,BCH_ETH,DASH_BTC,ETH_PLN,ZEC_BTC,ZEC_EUR,BCH_BTC,LTC_BTC,WAVES_RUB,KICK_ETH,ETH_EUR,ETH_RUB,USDT_USD,ZEC_RUB,USDT_RUB,DOGE_BTC,ETH_BTC,LTC_EUR,BTC_EUR,BTC_UAH,BCH_USD,BCH_RUB,DASH_USD,DASH_RUB,ZEC_USD,XMR_BTC,BTC_RUB,KICK_BTC,ETH_LTC,ETC_RUB,LTC_USD,LTC_RUB,XRP_RUB,WAVES_BTC", + "AvailablePairs": "ZEC_EUR,XRP_RUB,XMR_EUR,USDT_RUB,DOGE_BTC,BCH_BTC,BCH_ETH,ETH_RUB,ETH_USD,ETH_USDT,USD_RUB,KICK_BTC,BTC_RUB,BTG_BTC,DASH_RUB,LTC_USD,ZEC_BTC,XMR_USD,USDT_USD,BTC_USD,BTC_UAH,ETH_PLN,ETH_BTC,ETH_UAH,ETC_BTC,LTC_RUB,XMR_BTC,BTG_USD,BCH_USD,DASH_USD,BTC_EUR,ETH_EUR,WAVES_RUB,ZEC_USD,BTC_USDT,BTC_PLN,BCH_RUB,DASH_BTC,WAVES_BTC,ETH_LTC,ETC_RUB,XRP_USD,ZEC_RUB,XRP_BTC,KICK_ETH,ETC_USD,LTC_BTC,LTC_EUR", "EnabledPairs": "BTC_USD,LTC_USD", "BaseCurrencies": "USD,EUR,RUB,PLN,UAH", "AssetTypes": "SPOT", @@ -323,6 +335,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -347,6 +360,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -369,10 +383,11 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", - "AvailablePairs": "BCN-BTC,BTC-USD,DASH-BTC,DOGE-BTC,DOGE-USD,DSH-BTC,EMC-BTC,ETH-BTC,FCN-BTC,LSK-BTC,LTC-BTC,LTC-USD,NXT-BTC,QCN-BTC,SBD-BTC,SC-BTC,STEEM-BTC,XDN-BTC,XEM-BTC,XMR-BTC,ARDR-BTC,ZEC-BTC,WAVES-BTC,MAID-BTC,AMP-BTC,BUS-BTC,DGD-BTC,ICN-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,TAAS-BTC,NXC-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,CFI-BTC,PLBT-BTC,BNT-BTC,XDNCO-BTC,FYN-ETH,SNM-BTC,SNM-ETH,SNT-ETH,CVC-USD,PAY-ETH,OAX-ETH,OMG-ETH,BQX-ETH,XTZ-BTC,DICE-BTC,CFI-ETH,PTOY-ETH,1ST-ETH,XAUR-ETH,TAAS-ETH,TIME-ETH,DICE-ETH,SWT-ETH,XMR-ETH,ETC-ETH,DASH-ETH,ZEC-ETH,PLU-ETH,GNO-ETH,XRP-BTC,NET-ETH,STRAT-USD,STRAT-BTC,SNC-ETH,ADX-ETH,BET-ETH,EOS-ETH,DENT-ETH,SAN-ETH,EOS-BTC,EOS-USD,MNE-BTC,MSP-ETH,DDF-ETH,XTZ-ETH,XTZ-USD,UET-ETH,MYB-ETH,SUR-ETH,IXT-ETH,PLR-ETH,TIX-ETH,NDC-ETH,PRO-ETH,AVT-ETH,COSS-ETH,EVX-USD,DLT-BTC,BNT-ETH,BNT-USD,QAU-BTC,QAU-ETH,MANA-USD,DNT-BTC,FYP-BTC,OPT-BTC,TNT-ETH,IFT-BTC,STX-BTC,STX-ETH,STX-USD,TNT-USD,TNT-BTC,CAT-BTC,CAT-ETH,CAT-USD,BCH-BTC,BCH-ETH,BCH-USD,ENG-ETH,XUC-USD,SNC-BTC,SNC-USD,OAX-USD,OAX-BTC,BAS-ETH,ZRX-BTC,ZRX-ETH,ZRX-USD,RVT-BTC,ICOS-BTC,ICOS-ETH,ICOS-USD,PPC-BTC,PPC-USD,QTUM-ETH,VERI-BTC,VERI-ETH,VERI-USD,IGNIS-ETH,PRG-BTC,PRG-ETH,PRG-USD,BMC-BTC,BMC-ETH,BMC-USD,CND-BTC,CND-ETH,CND-USD,SKIN-BTC,EMGO-BTC,EMGO-USD,CDT-ETH,CDT-USD,FUN-BTC,FUN-ETH,FUN-USD,HVN-BTC,HVN-ETH,FUEL-BTC,FUEL-ETH,FUEL-USD,POE-BTC,POE-ETH,MCAP-BTC,AIR-BTC,AIR-ETH,AIR-USD,AMB-USD,AMB-ETH,AMB-BTC,NTO-BTC,ICO-BTC,PING-BTC,RKC-ETH,GAME-BTC,TKR-ETH,HPC-BTC,PPT-ETH,MTH-BTC,MTH-ETH,WMGO-BTC,WMGO-USD,LRC-BTC,LRC-ETH,ICX-BTC,ICX-ETH,NEO-BTC,NEO-ETH,NEO-USD,CSNO-BTC,ORME-BTC,ICX-USD,PIX-BTC,PIX-ETH,IND-ETH,KICK-BTC,YOYOW-BTC,MIPS-BTC,CDT-BTC,XVG-BTC,XVG-ETH,XVG-USD,DGB-BTC,DGB-ETH,DGB-USD,DCN-BTC,DCN-ETH,DCN-USD,LAT-BTC,CCT-ETH,EBET-ETH,VIBE-BTC,VOISE-BTC,ENJ-BTC,ENJ-ETH,ENJ-USD,ZSC-BTC,ZSC-ETH,ZSC-USD,ETBS-BTC,TRX-BTC,TRX-ETH,TRX-USD,VEN-BTC,VEN-ETH,VEN-USD,ART-BTC,EVX-BTC,EVX-ETH,QVT-ETH,EBTCOLD-BTC,EBTCOLD-ETH,EBTCOLD-USD,BKB-BTC,EXN-BTC,TGT-BTC,ATS-ETH,CTR-BTC,CTR-ETH,CTR-USD,BMT-BTC,BMT-ETH,SUB-BTC,SUB-ETH,SUB-USD,WTC-BTC,CNX-BTC,ATB-BTC,ATB-ETH,ATB-USD,ODN-BTC,BTM-BTC,BTM-ETH,BTM-USD,B2X-BTC,B2X-ETH,B2X-USD,ATM-BTC,ATM-ETH,ATM-USD,LIFE-BTC,VIB-BTC,VIB-ETH,VIB-USD,DRT-ETH,STU-USD,OMG-BTC,PAY-BTC,COSS-BTC,PPT-BTC,SNT-BTC,BTG-BTC,BTG-ETH,BTG-USD,SMART-BTC,SMART-ETH,SMART-USD,XUC-ETH,XUC-BTC,CL-BTC,CL-ETH,CL-USD,LA-ETH,CLD-BTC,CLD-ETH,CLD-USD,ELM-BTC,EDO-BTC,EDO-ETH,EDO-USD,HGT-ETH,POLL-BTC,IXT-BTC,ATS-BTC,SCL-BTC,ATL-BTC,EBTC-BTC,EBTC-ETH,EBTC-USD,ETP-BTC,ETP-ETH,ETP-USD,OTX-BTC,CDX-ETH,DRPU-BTC,NEBL-BTC,NEBL-ETH,HAC-BTC,CTX-BTC,CTX-ETH,ELE-BTC,ARN-BTC,ARN-ETH,SISA-BTC,SISA-ETH,STU-BTC,STU-ETH,GVT-ETH,INDI-BTC,BTX-BTC,BTX-USD,LTC-ETH,BCN-ETH,MAID-ETH,NXT-ETH,STRAT-ETH,XDN-ETH,XEM-ETH,PLR-BTC,SUR-BTC,BQX-BTC,DOGE-ETH,ITS-BTC,AMM-BTC,AMM-ETH,AMM-USD,DBIX-BTC,PRE-BTC,KBR-BTC,TBT-BTC,ERO-BTC,SMS-BTC,SMS-ETH,SMS-USD,ZAP-BTC,DOV-BTC,DOV-ETH,FRD-BTC,DRPU-ETH,OTN-BTC,XRP-ETH,XRP-USD,HSR-BTC,LEND-BTC,LEND-ETH,SPF-ETH,SBTC-BTC,SBTC-ETH,BTCA-BTC,BTCA-ETH,BTCA-USD,WRC-BTC,WRC-ETH,WRC-USD,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,ECH-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,TIO-BTC,TIO-ETH,TIO-USD,WAX-BTC,WAX-ETH,WAX-USD,ULTC-BTC,EET-BTC,EET-ETH,EET-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,CVH-ETH,CVH-USD,W3C-BTC,W3C-ETH,CAS-BTC,CAS-ETH,CAS-USD,CHAT-BTC,CHAT-ETH,CHAT-USD,GRMD-BTC,AVH-BTC,TRAC-ETH,JNT-ETH,PCL-BTC,PCL-ETH,CLOUT-BTC,UTK-BTC,UTK-ETH,UTK-USD,GNX-ETH,CHSB-BTC,CHSB-ETH,AVH-ETH,NEU-BTC,NEU-ETH,NEU-USD,AVH-USD,CLOUT-ETH,CLOUT-USD,TAU-BTC,MEK-BTC,BAR-BTC,BAR-ETH,BAR-USD,FLP-BTC,FLP-ETH,FLP-USD,R-BTC,R-ETH,EKO-USD,BCPT-ETH,BCPT-USD,PKT-BTC,PKT-ETH,WLK-BTC,WLK-ETH,WLK-USD,EVN-BTC,CPG-BTC,CPG-ETH,BPTN-BTC,BPTN-ETH,BPTN-USD,BETR-BTC,BETR-ETH,ARCT-BTC,ARCT-USD,DBET-BTC,DBET-ETH,DBET-USD,RNTB-ETH,HAND-ETH,HAND-USD,BEZ-BTC,BEZ-ETH,BEZ-USD,ACO-ETH,CTE-BTC,CTE-ETH,CTE-USD,UTNP-BTC,UTNP-ETH,UTNP-USD,CPY-BTC,CPY-ETH,CHP-ETH,BCPT-BTC,ACT-BTC,ACT-ETH,ACT-USD,HIRE-ETH,ADA-BTC,ADA-ETH,ADA-USD,SIG-BTC,RPM-BTC,RPM-ETH,MTX-BTC,MTX-ETH,MTX-USD,BGG-BTC,BGG-ETH,BGG-USD,SETH-ETH,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,NOAH-BTC,SOC-BTC,WILD-BTC,INSUR-BTC,INSUR-ETH,OCN-BTC,OCN-ETH,STQ-BTC,STQ-ETH,XLM-BTC,XLM-ETH,XLM-USD,IOTA-BTC,IOTA-ETH,IOTA-USD,DRT-BTC,BETR-USD,CGC-ETH,ERT-BTC,MESH-BTC,MESH-ETH,MESH-USD,CRPT-BTC,CRPT-USD,HLW-ETH,IHT-BTC,IHT-ETH,IHT-USD,SCC-BTC,YCC-BTC", + "AvailablePairs": "BCN-BTC,BTC-USD,DASH-BTC,DOGE-BTC,DOGE-USD,DSH-BTC,EMC-BTC,ETH-BTC,FCN-BTC,LSK-BTC,LTC-BTC,LTC-USD,NXT-BTC,QCN-BTC,SBD-BTC,SC-BTC,STEEM-BTC,XDN-BTC,XEM-BTC,XMR-BTC,ARDR-BTC,ZEC-BTC,WAVES-BTC,MAID-BTC,AMP-BTC,BUS-BTC,DGD-BTC,ICN-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,TAAS-BTC,NXC-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,CFI-BTC,PLBT-BTC,BNT-BTC,XDNCO-BTC,FYN-ETH,SNM-BTC,SNM-ETH,SNT-ETH,CVC-USD,PAY-ETH,OAX-ETH,OMG-ETH,BQX-ETH,XTZ-BTC,DICE-BTC,CFI-ETH,PTOY-ETH,1ST-ETH,XAUR-ETH,TAAS-ETH,TIME-ETH,DICE-ETH,SWT-ETH,XMR-ETH,ETC-ETH,DASH-ETH,ZEC-ETH,PLU-ETH,GNO-ETH,XRP-BTC,NET-ETH,STRAT-USD,STRAT-BTC,SNC-ETH,ADX-ETH,BET-ETH,EOS-ETH,DENT-ETH,SAN-ETH,EOS-BTC,EOS-USD,MNE-BTC,MSP-ETH,DDF-ETH,XTZ-ETH,XTZ-USD,UET-ETH,MYB-ETH,SUR-ETH,IXT-ETH,PLR-ETH,TIX-ETH,NDC-ETH,PRO-ETH,AVT-ETH,COSS-ETH,EVX-USD,DLT-BTC,BNT-ETH,BNT-USD,QAU-BTC,QAU-ETH,MANA-USD,DNT-BTC,FYP-BTC,OPT-BTC,TNT-ETH,IFT-BTC,STX-BTC,STX-ETH,STX-USD,TNT-USD,TNT-BTC,CAT-BTC,CAT-ETH,CAT-USD,BCH-BTC,BCH-ETH,BCH-USD,ENG-ETH,XUC-USD,SNC-BTC,SNC-USD,OAX-USD,OAX-BTC,BAS-ETH,ZRX-BTC,ZRX-ETH,ZRX-USD,RVT-BTC,ICOS-BTC,ICOS-ETH,ICOS-USD,PPC-BTC,PPC-USD,QTUM-ETH,VERI-BTC,VERI-ETH,VERI-USD,IGNIS-ETH,PRG-BTC,PRG-ETH,PRG-USD,BMC-BTC,BMC-ETH,BMC-USD,CND-BTC,CND-ETH,CND-USD,SKIN-BTC,EMGO-BTC,EMGO-USD,CDT-ETH,CDT-USD,FUN-BTC,FUN-ETH,FUN-USD,HVN-BTC,HVN-ETH,FUEL-BTC,FUEL-ETH,FUEL-USD,POE-BTC,POE-ETH,MCAP-BTC,AIR-BTC,AIR-ETH,AIR-USD,AMB-USD,AMB-ETH,AMB-BTC,NTO-BTC,ICO-BTC,PING-BTC,GAME-BTC,TKR-ETH,HPC-BTC,PPT-ETH,MTH-BTC,MTH-ETH,WMGO-BTC,WMGO-USD,LRC-BTC,LRC-ETH,NEO-BTC,NEO-ETH,NEO-USD,CSNO-BTC,ORME-BTC,PIX-BTC,PIX-ETH,IND-ETH,KICK-BTC,YOYOW-BTC,MIPS-BTC,CDT-BTC,XVG-BTC,XVG-ETH,XVG-USD,DGB-BTC,DGB-ETH,DGB-USD,DCN-BTC,DCN-ETH,DCN-USD,LAT-BTC,CCT-ETH,EBET-ETH,VIBE-BTC,VOISE-BTC,ENJ-BTC,ENJ-ETH,ENJ-USD,ZSC-BTC,ZSC-ETH,ZSC-USD,ETBS-BTC,TRX-BTC,TRX-ETH,TRX-USD,VEN-BTC,VEN-ETH,VEN-USD,ART-BTC,EVX-BTC,EVX-ETH,QVT-ETH,EBTCOLD-BTC,EBTCOLD-ETH,EBTCOLD-USD,BKB-BTC,EXN-BTC,TGT-BTC,ATS-ETH,CTR-BTC,CTR-ETH,CTR-USD,BMT-BTC,BMT-ETH,SUB-BTC,SUB-ETH,SUB-USD,WTC-BTC,CNX-BTC,ATB-BTC,ATB-ETH,ATB-USD,ODN-BTC,BTM-BTC,BTM-ETH,BTM-USD,B2X-BTC,B2X-ETH,B2X-USD,ATM-BTC,ATM-ETH,ATM-USD,LIFE-BTC,VIB-BTC,VIB-ETH,VIB-USD,DRT-ETH,STU-USD,OMG-BTC,PAY-BTC,COSS-BTC,PPT-BTC,SNT-BTC,BTG-BTC,BTG-ETH,BTG-USD,SMART-BTC,SMART-ETH,SMART-USD,XUC-ETH,XUC-BTC,CL-BTC,CL-ETH,CL-USD,LA-ETH,CLD-BTC,CLD-ETH,CLD-USD,ELM-BTC,EDO-BTC,EDO-ETH,EDO-USD,HGT-ETH,POLL-BTC,IXT-BTC,ATS-BTC,SCL-BTC,ATL-BTC,EBTC-BTC,EBTC-ETH,EBTC-USD,ETP-BTC,ETP-ETH,ETP-USD,OTX-BTC,CDX-ETH,DRPU-BTC,NEBL-BTC,NEBL-ETH,HAC-BTC,CTX-BTC,CTX-ETH,ELE-BTC,ARN-BTC,ARN-ETH,SISA-BTC,SISA-ETH,STU-BTC,STU-ETH,GVT-ETH,INDI-BTC,BTX-BTC,LTC-ETH,BCN-ETH,MAID-ETH,NXT-ETH,STRAT-ETH,XDN-ETH,XEM-ETH,PLR-BTC,SUR-BTC,BQX-BTC,DOGE-ETH,ITS-BTC,AMM-BTC,AMM-ETH,AMM-USD,DBIX-BTC,PRE-BTC,KBR-BTC,TBT-BTC,ERO-BTC,SMS-BTC,SMS-ETH,SMS-USD,ZAP-BTC,DOV-BTC,DOV-ETH,FRD-BTC,DRPU-ETH,OTN-BTC,XRP-ETH,XRP-USD,HSR-BTC,LEND-BTC,LEND-ETH,SPF-ETH,SBTC-BTC,SBTC-ETH,BTCA-BTC,BTCA-ETH,BTCA-USD,WRC-BTC,WRC-ETH,WRC-USD,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,ECH-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,TIO-BTC,TIO-ETH,TIO-USD,WAX-BTC,WAX-ETH,WAX-USD,EET-BTC,EET-ETH,EET-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,CVH-ETH,CVH-USD,CAS-BTC,CAS-ETH,CAS-USD,CHAT-BTC,CHAT-ETH,CHAT-USD,GRMD-BTC,AVH-BTC,TRAC-ETH,JNT-ETH,PCL-BTC,PCL-ETH,CLOUT-BTC,UTK-BTC,UTK-ETH,UTK-USD,GNX-ETH,CHSB-BTC,CHSB-ETH,AVH-ETH,DAY-BTC,DAY-ETH,DAY-USD,NEU-BTC,NEU-ETH,NEU-USD,AVH-USD,CLOUT-ETH,CLOUT-USD,TAU-BTC,MEK-BTC,BAR-BTC,BAR-ETH,BAR-USD,FLP-BTC,FLP-ETH,FLP-USD,R-BTC,R-ETH,EKO-USD,BCPT-ETH,BCPT-USD,PKT-BTC,PKT-ETH,WLK-BTC,WLK-ETH,WLK-USD,EVN-BTC,CPG-BTC,CPG-ETH,BPTN-BTC,BPTN-ETH,BPTN-USD,BETR-BTC,BETR-ETH,ARCT-BTC,ARCT-USD,DBET-BTC,DBET-ETH,DBET-USD,RNTB-ETH,HAND-ETH,HAND-USD,BEZ-BTC,BEZ-ETH,BEZ-USD,ACO-ETH,CTE-BTC,CTE-ETH,CTE-USD,UTNP-BTC,UTNP-ETH,UTNP-USD,CPY-BTC,CPY-ETH,CHP-ETH,BCPT-BTC,ACT-BTC,ACT-ETH,ACT-USD,HIRE-ETH,ADA-BTC,ADA-ETH,ADA-USD,SIG-BTC,RPM-BTC,RPM-ETH,MTX-BTC,MTX-ETH,MTX-USD,BGG-BTC,BGG-ETH,BGG-USD,SETH-ETH,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,NOAH-BTC,SOC-BTC,WILD-BTC,INSUR-BTC,INSUR-ETH,OCN-BTC,OCN-ETH,STQ-BTC,STQ-ETH,XLM-BTC,XLM-ETH,XLM-USD,IOTA-BTC,IOTA-ETH,IOTA-USD,DRT-BTC,MLD-BTC,MLD-ETH,MLD-USD,BETR-USD,CGC-ETH,ERT-BTC,CRPT-BTC,CRPT-USD,MESH-BTC,MESH-ETH,MESH-USD,HLW-ETH,IHT-BTC,IHT-ETH,IHT-USD,SCC-BTC,YCC-BTC,DAN-BTC,TEL-BTC,TEL-ETH,BUBO-BTC,BUBO-ETH,BUBO-USD,VIT-BTC,VIT-ETH,VIT-USD,CLR-BTC,NCT-BTC,NCT-ETH,NCT-USD,AXP-BTC,AXP-ETH,BMH-BTC,BANCA-USD,NOAH-ETH,NOAH-USD,HQX-BTC,LDC-BTC,XMO-BTC,XMO-USD,XMO-ETH,BERRY-BTC,BERRY-ETH,BERRY-USD,BSTN-BTC,BSTN-ETH,BSTN-USD,GBX-BTC,GBX-ETH,GBX-USD,SHIP-BTC,SHIP-ETH,LNC-BTC,UNC-BTC,UNC-ETH,RPX-BTC,RPX-ETH,RPX-USD,KIN-ETH,ARDR-USD,DAXT-BTC,DAXT-ETH,FOTA-ETH,FOTA-BTC,SETH-BTC,CVT-BTC,CVT-ETH,CVT-USD,STQ-USD,GNT-BTC,GNT-ETH,GNT-USD,ADH-BTC,ADH-ETH,BBC-BTC,BBC-ETH,GET-BTC,MITH-BTC,MITH-ETH,MITH-USD,SUNC-ETH,DADI-USD,BTX-USD,WIKI-USD,WIKI-BTC,WIKI-ETH,ICX-BTC,ICX-ETH,ICX-USD,VIO-ETH,NANO-BTC,NANO-ETH,NANO-USD,FTX-BTC,FTX-ETH,FREC-BTC,FREC-USD,FREC-ETH,ONT-BTC,ONT-ETH,ONT-USD,NAVI-BTC,NAVI-ETH,VME-ETH,LND-ETH,CSM-BTC,BTCP-BTC", "EnabledPairs": "BTC-USD", "BaseCurrencies": "USD", "AssetTypes": "SPOT", @@ -392,10 +407,11 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", - "AvailablePairs": "NAS-ETH,EOS-ETH,ZEC-USDT,EVX-BTC,MDS-ETH,SMT-ETH,TRX-ETH,THETA-USDT,LUN-ETH,SMT-USDT,BCH-BTC,LET-USDT,GNX-ETH,MTL-BTC,CHAT-ETH,QTUM-USDT,SNT-BTC,WPR-BTC,ELF-ETH,ZIL-BTC,UTK-BTC,SBTC-BTC,TNT-BTC,NEO-USDT,MCO-BTC,OST-ETH,BT2-BTC,HSR-ETH,TOPC-ETH,SALT-ETH,AIDOC-ETH,WAX-BTC,DTA-ETH,BTC-USDT,GAS-ETH,NEO-BTC,BTM-BTC,BLZ-ETH,BAT-ETH,APPC-BTC,CMT-BTC,ONT-BTC,QTUM-ETH,IOST-BTC,REQ-BTC,BTM-ETH,RUFF-BTC,ZEC-BTC,DGD-ETH,DAT-ETH,STK-ETH,HT-ETH,QUN-ETH,ELF-BTC,CMT-ETH,SNT-USDT,MDS-USDT,STORJ-USDT,WAX-ETH,POWR-BTC,SNC-BTC,VEN-USDT,TNB-ETH,SWFTC-ETH,EOS-BTC,LINK-ETH,HT-USDT,RDN-BTC,LUN-BTC,GNX-BTC,ELA-BTC,LET-ETH,IOST-USDT,EVX-ETH,ACT-ETH,BCH-USDT,ICX-ETH,BCX-BTC,MTN-ETH,PROPY-ETH,XRP-USDT,ICX-BTC,THETA-ETH,SNC-ETH,DBC-ETH,ITC-USDT,SMT-BTC,SRN-ETH,ETH-USDT,ITC-BTC,OMG-BTC,STK-BTC,MDS-BTC,TOPC-BTC,ADX-BTC,ETC-BTC,KNC-BTC,CVC-BTC,QSP-ETH,BTG-BTC,EDU-BTC,ZLA-ETH,MTX-ETH,EOS-USDT,RCN-BTC,UTK-ETH,RCN-ETH,GNT-USDT,APPC-ETH,WICC-BTC,YEE-BTC,YEE-ETH,OMG-USDT,LINK-BTC,XEM-USDT,HSR-USDT,DASH-BTC,QUN-BTC,QASH-ETH,DTA-BTC,AIDOC-BTC,DAT-BTC,RUFF-ETH,SALT-BTC,ELA-ETH,IOST-ETH,THETA-BTC,LET-BTC,DTA-USDT,SOC-ETH,ELA-USDT,ZIL-USDT,MANA-BTC,XRP-BTC,ONT-ETH,LTC-USDT,DBC-BTC,BCD-BTC,SWFTC-BTC,CVC-USDT,NAS-USDT,GNT-ETH,TRX-USDT,HT-BTC,ENG-ETH,PAY-ETH,CVC-ETH,TNB-BTC,MEE-ETH,POWR-ETH,BLZ-BTC,PAY-BTC,ADX-ETH,EKO-ETH,SRN-BTC,OCN-ETH,VEN-ETH,ABT-ETH,BIFI-BTC,ACT-BTC,ETC-USDT,OST-BTC,MCO-ETH,ABT-BTC,STORJ-BTC,VEN-BTC,GNT-BTC,LSK-BTC,EKO-BTC,LTC-BTC,OCN-BTC,RUFF-USDT,WPR-ETH,AST-BTC,DASH-USDT,DGD-BTC,ZIL-ETH,ZRX-BTC,ETH-BTC,REQ-ETH,TRX-BTC,WICC-ETH,NAS-BTC,MEE-BTC,ENG-BTC,LSK-ETH,RPX-BTC,TNT-ETH,MTX-BTC,SOC-BTC,EDU-ETH,HSR-BTC,QTUM-BTC,QSP-BTC,BAT-BTC,QASH-BTC,ITC-ETH,XEM-BTC,MANA-ETH,GAS-BTC,MTN-BTC,CHAT-BTC,BT1-BTC,ZLA-BTC,OMG-ETH,PROPY-BTC,RDN-ETH,ELF-USDT", + "AvailablePairs": "BTC-USDT,BCH-USDT,ETH-USDT,ETC-USDT,LTC-USDT,EOS-USDT,XRP-USDT,OMG-USDT,DASH-USDT,ZEC-USDT,ADA-USDT,STEEM-USDT,CTXC-USDT,ACT-USDT,BTM-USDT,BTS-USDT,ONT-USDT,IOST-USDT,HT-USDT,TRX-USDT,DTA-USDT,NEO-USDT,QTUM-USDT,ELA-USDT,VEN-USDT,THETA-USDT,SNT-USDT,ZIL-USDT,XEM-USDT,NAS-USDT,RUFF-USDT,HSR-USDT,LET-USDT,MDS-USDT,STORJ-USDT,ELF-USDT,ITC-USDT,CVC-USDT,GNT-USDT,BCH-BTC,ETH-BTC,LTC-BTC,ETC-BTC,EOS-BTC,OMG-BTC,XRP-BTC,DASH-BTC,ZEC-BTC,ADA-BTC,STEEM-BTC,BFT-BTC,BTM-BTC,ONT-BTC,IOST-BTC,HT-BTC,TRX-BTC,ELA-BTC,WICC-BTC,OCN-BTC,ZLA-BTC,ABT-BTC,MTX-BTC,NAS-BTC,VEN-BTC,DTA-BTC,NEO-BTC,WAX-BTC,BTS-BTC,ZIL-BTC,THETA-BTC,CTXC-BTC,SRN-BTC,XEM-BTC,EDU-BTC,ICX-BTC,DGD-BTC,CHAT-BTC,WPR-BTC,LUN-BTC,SWFTC-BTC,SNT-BTC,MEET-BTC,YEE-BTC,ELF-BTC,LET-BTC,QTUM-BTC,LSK-BTC,ITC-BTC,SOC-BTC,QASH-BTC,MDS-BTC,EKO-BTC,TOPC-BTC,MTN-BTC,ACT-BTC,HSR-BTC,STK-BTC,STORJ-BTC,GNX-BTC,DBC-BTC,SNC-BTC,CMT-BTC,TNB-BTC,RUFF-BTC,QUN-BTC,ZRX-BTC,KNC-BTC,BLZ-BTC,PROPY-BTC,RPX-BTC,APPC-BTC,AIDOC-BTC,POWR-BTC,CVC-BTC,PAY-BTC,QSP-BTC,DAT-BTC,RDN-BTC,MCO-BTC,RCN-BTC,MANA-BTC,UTK-BTC,TNT-BTC,GAS-BTC,BAT-BTC,OST-BTC,LINK-BTC,GNT-BTC,MTL-BTC,EVX-BTC,REQ-BTC,ADX-BTC,AST-BTC,ENG-BTC,SALT-BTC,BIFI-BTC,BCX-BTC,BCD-BTC,SBTC-BTC,BTG-BTC,EOS-ETH,OMG-ETH,ADA-ETH,STEEM-ETH,BFT-ETH,ZRX-ETH,AST-ETH,KNC-ETH,ONT-ETH,HT-ETH,BTM-ETH,IOST-ETH,ELA-ETH,TRX-ETH,ABT-ETH,NAS-ETH,OCN-ETH,WICC-ETH,ZIL-ETH,CTXC-ETH,ZLA-ETH,WPR-ETH,DTA-ETH,MTX-ETH,THETA-ETH,SRN-ETH,VEN-ETH,BTS-ETH,EDU-ETH,WAX-ETH,HSR-ETH,ICX-ETH,MTN-ETH,ACT-ETH,BLZ-ETH,QASH-ETH,RUFF-ETH,CMT-ETH,ELF-ETH,MEET-ETH,SOC-ETH,QTUM-ETH,ITC-ETH,SWFTC-ETH,YEE-ETH,LSK-ETH,LUN-ETH,LET-ETH,GNX-ETH,CHAT-ETH,EKO-ETH,TOPC-ETH,DGD-ETH,STK-ETH,MDS-ETH,DBC-ETH,SNC-ETH,PAY-ETH,QUN-ETH,AIDOC-ETH,TNB-ETH,APPC-ETH,RDN-ETH,UTK-ETH,POWR-ETH,BAT-ETH,PROPY-ETH,MANA-ETH,REQ-ETH,CVC-ETH,QSP-ETH,EVX-ETH,DAT-ETH,MCO-ETH,GNT-ETH,GAS-ETH,OST-ETH,LINK-ETH,RCN-ETH,TNT-ETH,ENG-ETH,SALT-ETH,ADX-ETH", "EnabledPairs": "BTC-USDT", "BaseCurrencies": "USD", "AssetTypes": "SPOT", @@ -415,6 +431,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -439,6 +456,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -463,6 +481,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -486,10 +505,11 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", - "AvailablePairs": "EDG_USDT,MCO_USDT,OMG_ETH,STX_USDT,GUP_USDT,AE_USDT,GNT_ETH,MYST_USDT,SNGLS_USDT,KNC_USDT,MCO_BTC,DNT_USDT,IND_USDT,REP_ETH,AST_BTC,LDC_USDT,LTC_BTC,TKN_BTC,MGO_BTC,PAY_BTC,ENG_ETH,ADX_ETH,WAVES_BTC,GNO_USDT,BAT_USDT,MGO_ETH,IND_BTC,INS_USDT,RLC_BTC,OAX_ETH,SALT_USDT,SRN_ETH,ADX_USDT,EOS_USDT,REP_BTC,ANT_USDT,MGO_USDT,VEN_ETH,DASH_BTC,QRL_ETH,NET_ETH,TNT_USDT,ICN_ETH,QRL_USDT,SNM_USDT,XID_ETH,DGD_ETH,NET_USDT,VEN_USDT,AION_USDT,SNM_BTC,MCO_ETH,SAN_USDT,SRN_USDT,REP_USDT,MYST_ETH,MANA_ETH,NEU_BTC,TIME_BTC,STX_ETH,AST_USDT,WINGS_USDT,TIME_ETH,BMC_USDT,DASH_ETH,WAVES_ETH,TRST_ETH,SAN_BTC,BCC_BTC,BNT_ETH,SNGLS_BTC,PTOY_BTC,SNT_BTC,AE_ETH,WINGS_ETH,CFI_BTC,BCC_USDT,ZRX_ETH,MANA_USDT,NEU_USDT,XID_BTC,OAX_USDT,KNC_BTC,REQ_USDT,CFI_USDT,OMG_USDT,BAT_ETH,TNT_BTC,VEN_BTC,SALT_ETH,TNT_ETH,INS_BTC,REN_USDT,WINGS_BTC,SNGLS_ETH,SNT_ETH,LTC_USDT,GNO_BTC,GUP_BTC,SNT_USDT,WPR_ETH,NET_BTC,NEU_ETH,MLN_USDT,STORJ_USDT,XID_USDT,PRO_BTC,GNT_USDT,SAN_ETH,DGD_USDT,BCC_ETH,STX_BTC,RLC_USDT,TAAS_BTC,CVC_ETH,EDG_ETH,REQ_ETH,SRN_BTC,AE_BTC,TRX_BTC,STORJ_BTC,STORJ_ETH,TRX_ETH,AST_ETH,QRL_BTC,OMG_BTC,DGD_BTC,ZRX_USDT,PTOY_USDT,DNT_ETH,LTC_ETH,ADX_BTC,BAT_BTC,GNO_ETH,ANT_BTC,ETH_USDT,LDC_ETH,TAAS_USDT,TRX_USDT,TRST_USDT,PTOY_ETH,MLN_BTC,BNT_BTC,BNT_USDT,KNC_ETH,IND_ETH,WPR_BTC,INS_ETH,DASH_USDT,WAVES_USDT,EOS_BTC,BMC_BTC,REQ_BTC,TIME_USDT,CVC_USDT,ENG_BTC,AION_BTC,MLN_ETH,SNM_ETH,ICN_USDT,RLC_ETH,TKN_ETH,DNT_BTC,BTC_USDT,CFI_ETH,OAX_BTC,SALT_BTC,TKN_USDT,EOS_ETH,LDC_BTC,EDG_BTC,ANT_ETH,ENG_USDT,ETH_BTC,PAY_ETH,BMC_ETH,PRO_ETH,AION_ETH,REN_ETH,GUP_ETH,PAY_USDT,WPR_USDT,GNT_BTC,CVC_BTC,ZRX_BTC,REN_BTC,TRST_BTC,MYST_BTC,MANA_BTC,ICN_BTC,TAAS_ETH,PRO_USDT", + "AvailablePairs": "LDC_BTC,OAX_USDT,ETH_USDT,TRX_BTC,DNT_USDT,AE_USDT,BMC_BTC,WAVES_ETH,BCC_ETH,NET_BTC,KNC_BTC,MCO_BTC,XID_ETH,REQ_BTC,TAAS_BTC,TRST_BTC,RLC_BTC,TRST_USDT,AE_BTC,SALT_USDT,TRST_ETH,BNT_USDT,AST_BTC,AION_BTC,AION_ETH,AE_ETH,VEN_USDT,STORJ_BTC,EDG_ETH,KNC_USDT,ENJ_USDT,GNO_BTC,INS_BTC,TIME_BTC,REN_ETH,PAY_BTC,ANT_USDT,PRO_ETH,EDG_BTC,TIME_USDT,TNT_BTC,IND_USDT,ENJ_ETH,WAVES_BTC,SNM_BTC,QRL_ETH,QRL_USDT,SNT_USDT,NET_ETH,BMC_ETH,SRN_BTC,SRN_USDT,ICN_USDT,PTOY_USDT,EOS_USDT,NET_USDT,NEU_USDT,AION_USDT,ENJ_BTC,MLN_ETH,ICN_ETH,GNT_USDT,OMG_BTC,OAX_BTC,MANA_BTC,BNT_BTC,AST_ETH,SNM_USDT,EOS_BTC,REQ_USDT,TAAS_ETH,REP_BTC,TKN_USDT,EOS_ETH,PAY_ETH,OMG_USDT,LTC_BTC,VEN_ETH,DNT_ETH,TKN_ETH,ANT_BTC,XID_BTC,OMG_ETH,ETH_BTC,ZRX_USDT,IND_BTC,REP_ETH,GNO_ETH,SAN_BTC,LDC_ETH,CFI_USDT,RLC_USDT,TIME_ETH,MYST_ETH,SNM_ETH,OAX_ETH,PRO_BTC,ADX_ETH,PAY_USDT,TRX_ETH,WINGS_USDT,SNGLS_USDT,TNT_ETH,WPR_BTC,DASH_BTC,EDG_USDT,VEN_BTC,LTC_ETH,STORJ_ETH,ZRX_ETH,REN_USDT,PTOY_ETH,TRX_USDT,ANT_ETH,CFI_ETH,DNT_BTC,WPR_USDT,WAVES_USDT,QRL_BTC,STX_USDT,BCC_BTC,MANA_USDT,AST_USDT,GUP_USDT,GNO_USDT,MGO_BTC,SNT_BTC,MCO_ETH,REQ_ETH,REP_USDT,SALT_BTC,SALT_ETH,IND_ETH,LTC_USDT,CFI_BTC,MCO_USDT,ENG_ETH,TKN_BTC,TAAS_USDT,ADX_USDT,TNT_USDT,INS_USDT,DASH_ETH,BMC_USDT,DGD_BTC,KNC_ETH,BAT_BTC,DASH_USDT,CVC_BTC,STX_ETH,REN_BTC,MYST_BTC,SAN_ETH,GUP_ETH,SNGLS_BTC,WINGS_ETH,RLC_ETH,NEU_BTC,GNT_ETH,WINGS_BTC,ZRX_BTC,MANA_ETH,ICN_BTC,MGO_ETH,MGO_USDT,SNGLS_ETH,SAN_USDT,DGD_ETH,SRN_ETH,BAT_ETH,CVC_USDT,MLN_USDT,SNT_ETH,BTC_USDT,BAT_USDT,PTOY_BTC,STORJ_USDT,CVC_ETH,GNT_BTC,BNT_ETH,MYST_USDT,XID_USDT,STX_BTC,GUP_BTC,ADX_BTC,DGD_USDT,BCC_USDT,ENG_BTC,WPR_ETH,MLN_BTC,ENG_USDT,NEU_ETH,INS_ETH,LDC_USDT,PRO_USDT", "EnabledPairs": "ETH_BTC,LTC_BTC,DASH_BTC", "BaseCurrencies": "USD", "AssetTypes": "SPOT", @@ -511,11 +531,12 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", "AvailablePairs": "BTCARS,BTCAUD,BTCBRL,BTCCAD,BTCCHF,BTCCZK,BTCDKK,BTCEUR,BTCGBP,BTCHKD,BTCILS,BTCINR,BTCMXN,BTCNOK,BTCNZD,BTCPLN,BTCRUB,BTCSEK,BTCSGD,BTCTHB,BTCUSD,BTCZAR", - "EnabledPairs": "BTCARS,BTCAUD,BTCBRL,BTCCAD,BTCCHF,BTCCZK,BTCDKK,BTCEUR,BTCGBP,BTCHKD,BTCILS,BTCINR,BTCMXN,BTCNOK,BTCNZD,BTCPLN,BTCRUB,BTCSEK,BTCSGD,BTCTHB,BTCUSD,BTCZAR", + "EnabledPairs": "BTCAUD,BTCUSD", "BaseCurrencies": "ARS,AUD,BRL,CAD,CHF,CZK,DKK,EUR,GBP,HKD,ILS,INR,MXN,NOK,NZD,PLN,RUB,SEK,SGD,THB,USD,ZAR", "AssetTypes": "SPOT", "SupportsAutoPairUpdates": false, @@ -534,6 +555,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -558,6 +580,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -582,6 +605,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -607,6 +631,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -631,6 +656,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -656,6 +682,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", diff --git a/currency/pair/pair.go b/currency/pair/pair.go index f1a68e55..e82bbc3f 100644 --- a/currency/pair/pair.go +++ b/currency/pair/pair.go @@ -195,3 +195,25 @@ func CopyPairFormat(p CurrencyPair, pairs []CurrencyPair, exact bool) CurrencyPa } return CurrencyPair{} } + +// FindPairDifferences returns pairs which are new or have been removed +func FindPairDifferences(oldPairs, newPairs []string) ([]string, []string) { + var newPs, removedPs []string + for x := range newPairs { + if newPairs[x] == "" { + continue + } + if !common.StringDataCompareUpper(oldPairs, newPairs[x]) { + newPs = append(newPs, newPairs[x]) + } + } + for x := range oldPairs { + if oldPairs[x] == "" { + continue + } + if !common.StringDataCompareUpper(newPairs, oldPairs[x]) { + removedPs = append(removedPs, oldPairs[x]) + } + } + return newPs, removedPs +} diff --git a/currency/pair/pair_test.go b/currency/pair/pair_test.go index 22cb8ce1..09ffa720 100644 --- a/currency/pair/pair_test.go +++ b/currency/pair/pair_test.go @@ -329,3 +329,32 @@ func TestCopyPairFormat(t *testing.T) { t.Error("Test failed. TestCopyPairFormat: Unexpected non empty pair returned") } } + +func TestFindPairDifferences(t *testing.T) { + pairList := []string{"BTC-USD", "ETH-USD", "LTC-USD"} + + // Test new pair update + newPairs, removedPairs := FindPairDifferences(pairList, []string{"DASH-USD"}) + if len(newPairs) != 1 && len(removedPairs) != 3 { + t.Error("Test failed. TestFindPairDifferences: Unexpected values") + } + + // Test that we don't allow empty strings for new pairs + newPairs, removedPairs = FindPairDifferences(pairList, []string{""}) + if len(newPairs) != 0 && len(removedPairs) != 3 { + t.Error("Test failed. TestFindPairDifferences: Unexpected values") + } + + // Test that we don't allow empty strings for new pairs + newPairs, removedPairs = FindPairDifferences([]string{""}, pairList) + if len(newPairs) != 3 && len(removedPairs) != 0 { + t.Error("Test failed. TestFindPairDifferences: Unexpected values") + } + + // Test that the supplied pair lists are the same, so + // no newPairs or removedPairs + newPairs, removedPairs = FindPairDifferences(pairList, pairList) + if len(newPairs) != 0 && len(removedPairs) != 0 { + t.Error("Test failed. TestFindPairDifferences: Unexpected values") + } +} diff --git a/exchange.go b/exchange.go index ea068276..2d2ad3a7 100644 --- a/exchange.go +++ b/exchange.go @@ -3,6 +3,7 @@ package main import ( "errors" "log" + "sync" "github.com/thrasher-/gocryptotrader/common" exchange "github.com/thrasher-/gocryptotrader/exchanges" @@ -120,7 +121,7 @@ func UnloadExchange(name string) error { } // LoadExchange loads an exchange by name -func LoadExchange(name string) error { +func LoadExchange(name string, useWG bool, wg *sync.WaitGroup) error { nameLower := common.StringToLower(name) var exch exchange.IBotExchange @@ -200,12 +201,20 @@ func LoadExchange(name string) error { exchCfg.Enabled = true exch.Setup(exchCfg) - exch.Start() + + if useWG { + exch.Start(wg) + } else { + wg := sync.WaitGroup{} + exch.Start(&wg) + wg.Wait() + } return nil } // SetupExchanges sets up the exchanges used by the bot func SetupExchanges() { + var wg sync.WaitGroup for _, exch := range bot.config.Exchanges { if CheckExchangeExists(exch.Name) { e := GetExchangeByName(exch.Name) @@ -231,7 +240,7 @@ func SetupExchanges() { log.Printf("%s: Exchange support: Disabled", exch.Name) continue } else { - err := LoadExchange(exch.Name) + err := LoadExchange(exch.Name, true, &wg) if err != nil { log.Printf("LoadExchange %s failed: %s", exch.Name, err) continue @@ -244,4 +253,5 @@ func SetupExchanges() { common.IsEnabled(exch.Verbose), ) } + wg.Wait() } diff --git a/exchange_test.go b/exchange_test.go index 951e6573..60662da1 100644 --- a/exchange_test.go +++ b/exchange_test.go @@ -21,7 +21,7 @@ func SetupTest(t *testing.T) { if CheckExchangeExists("Bitfinex") { return } - err := LoadExchange("Bitfinex") + err := LoadExchange("Bitfinex", false, nil) if err != nil { t.Errorf("Test failed. SetupTest: Failed to load exchange: %s", err) } diff --git a/exchanges/alphapoint/alphapoint.go b/exchanges/alphapoint/alphapoint.go index a47be286..ddca63f3 100644 --- a/exchanges/alphapoint/alphapoint.go +++ b/exchanges/alphapoint/alphapoint.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "log" - "net/http" "strconv" "time" @@ -39,15 +38,14 @@ const ( alphapointOrderFee = "GetOrderFee" // alphapoint rate times - alphapointAuthRate = 1200 - alphapointUnauthRate = 1200 + alphapointAuthRate = 500 + alphapointUnauthRate = 500 ) // Alphapoint is the overarching type across the alphapoint package type Alphapoint struct { exchange.Base WebsocketConn *websocket.Conn - *request.Handler } // SetDefaults sets current default settings @@ -56,8 +54,8 @@ func (a *Alphapoint) SetDefaults() { a.WebsocketURL = alphapointDefaultWebsocketURL a.AssetTypes = []string{ticker.Spot} a.SupportsAutoPairUpdating = false - a.Handler = new(request.Handler) - a.SetRequestHandler(a.Name, alphapointAuthRate, alphapointUnauthRate, new(http.Client)) + a.SupportsRESTTickerBatching = false + a.Requester = request.New(a.Name, request.NewRateLimit(time.Minute*10, alphapointAuthRate), request.NewRateLimit(time.Minute*10, alphapointUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // GetTicker returns current ticker information from Alphapoint for a selected diff --git a/exchanges/anx/anx.go b/exchanges/anx/anx.go index 9ec894df..723c35cc 100644 --- a/exchanges/anx/anx.go +++ b/exchanges/anx/anx.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "log" - "net/http" "strconv" "time" @@ -31,14 +30,13 @@ const ( anxDepth = "money/depth/full" // ANX rate limites for authenticated and unauthenticated requests - anxAuthRate = 1000 - anxUnauthRate = 1000 + anxAuthRate = 0 + anxUnauthRate = 0 ) // ANX is the overarching type across the alphapoint package type ANX struct { exchange.Base - *request.Handler } // SetDefaults sets current default settings @@ -58,8 +56,8 @@ func (a *ANX) SetDefaults() { a.ConfigCurrencyPairFormat.Index = "BTC" a.AssetTypes = []string{ticker.Spot} a.SupportsAutoPairUpdating = false - a.Handler = new(request.Handler) - a.SetRequestHandler(a.Name, anxAuthRate, anxUnauthRate, new(http.Client)) + a.SupportsRESTTickerBatching = false + a.Requester = request.New(a.Name, request.NewRateLimit(time.Second, anxAuthRate), request.NewRateLimit(time.Second, anxUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } //Setup is run on startup to setup exchange with config values @@ -70,6 +68,7 @@ func (a *ANX) Setup(exch config.ExchangeConfig) { a.Enabled = true a.AuthenticatedAPISupport = exch.AuthenticatedAPISupport a.SetAPIKeys(exch.APIKey, exch.APISecret, "", true) + a.SetHTTPClientTimeout(exch.HTTPTimeout) a.RESTPollingDelay = exch.RESTPollingDelay a.Verbose = exch.Verbose a.Websocket = exch.Websocket diff --git a/exchanges/anx/anx_wrapper.go b/exchanges/anx/anx_wrapper.go index edbc176f..b0f6903d 100644 --- a/exchanges/anx/anx_wrapper.go +++ b/exchanges/anx/anx_wrapper.go @@ -4,6 +4,7 @@ import ( "errors" "log" "strconv" + "sync" "github.com/thrasher-/gocryptotrader/currency/pair" "github.com/thrasher-/gocryptotrader/exchanges" @@ -12,8 +13,12 @@ import ( ) // Start starts the ANX go routine -func (a *ANX) Start() { - go a.Run() +func (a *ANX) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + a.Run() + wg.Done() + }() } // Run implements the ANX wrapper diff --git a/exchanges/binance/binance.go b/exchanges/binance/binance.go index 99f085de..6742d32d 100644 --- a/exchanges/binance/binance.go +++ b/exchanges/binance/binance.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "log" - "net/http" "net/url" "strconv" "time" @@ -24,7 +23,6 @@ type Binance struct { // valid string list that a required by the exchange validLimits []string validIntervals []string - *request.Handler } const ( @@ -47,8 +45,9 @@ const ( queryOrder = "/api/v3/order" // binance authenticated and unauthenticated limit rates - binanceAuthRate = 1000 - binanceUnauthRate = 1000 + // to-do + binanceAuthRate = 0 + binanceUnauthRate = 0 ) // SetDefaults sets the basic defaults for Binance @@ -64,9 +63,9 @@ func (b *Binance) SetDefaults() { b.ConfigCurrencyPairFormat.Uppercase = true b.AssetTypes = []string{ticker.Spot} b.SupportsAutoPairUpdating = true + b.SupportsRESTTickerBatching = true b.SetValues() - b.Handler = new(request.Handler) - b.SetRequestHandler(b.Name, binanceAuthRate, binanceUnauthRate, new(http.Client)) + b.Requester = request.New(b.Name, request.NewRateLimit(time.Second, binanceAuthRate), request.NewRateLimit(time.Second, binanceUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup takes in the supplied exchange configuration details and sets params @@ -77,6 +76,7 @@ func (b *Binance) Setup(exch config.ExchangeConfig) { b.Enabled = true b.AuthenticatedAPISupport = exch.AuthenticatedAPISupport b.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) + b.SetHTTPClientTimeout(exch.HTTPTimeout) b.RESTPollingDelay = exch.RESTPollingDelay b.Verbose = exch.Verbose b.Websocket = exch.Websocket diff --git a/exchanges/binance/binance_wrapper.go b/exchanges/binance/binance_wrapper.go index b7187f28..ba48efcb 100644 --- a/exchanges/binance/binance_wrapper.go +++ b/exchanges/binance/binance_wrapper.go @@ -3,6 +3,7 @@ package binance import ( "errors" "log" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -12,8 +13,12 @@ import ( ) // Start starts the OKEX go routine -func (b *Binance) Start() { - go b.Run() +func (b *Binance) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + b.Run() + wg.Done() + }() } // Run implements the OKEX wrapper @@ -37,12 +42,12 @@ func (b *Binance) Run() { enabledPairs := []string{"BTC-USDT"} log.Println("WARNING: Available pairs for Binance reset due to config upgrade, please enable the ones you would like again") - err = b.UpdateEnabledCurrencies(enabledPairs, true) + err = b.UpdateCurrencies(enabledPairs, true, true) if err != nil { log.Printf("%s Failed to get config.\n", b.GetName()) } } - err = b.UpdateAvailableCurrencies(symbols, forceUpgrade) + err = b.UpdateCurrencies(symbols, false, forceUpgrade) if err != nil { log.Printf("%s Failed to get config.\n", b.GetName()) } diff --git a/exchanges/bitfinex/bitfinex.go b/exchanges/bitfinex/bitfinex.go index dd627195..63186507 100644 --- a/exchanges/bitfinex/bitfinex.go +++ b/exchanges/bitfinex/bitfinex.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "log" - "net/http" "net/url" "strconv" "time" @@ -67,9 +66,9 @@ const ( bitfinexActiveCredits = "credits" bitfinexPlatformStatus = "platform/status" - // stable times in millisecond per request - bitfinexAuthRate = 2750 - bitfinexUnauthRate = 2750 + // requests per minute + bitfinexAuthRate = 10 + bitfinexUnauthRate = 10 // Bitfinex platform status values // When the platform is marked in maintenance mode bots should stop trading @@ -86,7 +85,6 @@ type Bitfinex struct { exchange.Base WebsocketConn *websocket.Conn WebsocketSubdChannels map[int]WebsocketChanInfo - *request.Handler } // SetDefaults sets the basic defaults for bitfinex @@ -103,8 +101,8 @@ func (b *Bitfinex) SetDefaults() { b.ConfigCurrencyPairFormat.Uppercase = true b.AssetTypes = []string{ticker.Spot} b.SupportsAutoPairUpdating = true - b.Handler = new(request.Handler) - b.SetRequestHandler(b.Name, bitfinexAuthRate, bitfinexUnauthRate, new(http.Client)) + b.SupportsRESTTickerBatching = true + b.Requester = request.New(b.Name, request.NewRateLimit(time.Second*60, bitfinexAuthRate), request.NewRateLimit(time.Second*60, bitfinexUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup takes in the supplied exchange configuration details and sets params @@ -115,6 +113,7 @@ func (b *Bitfinex) Setup(exch config.ExchangeConfig) { b.Enabled = true b.AuthenticatedAPISupport = exch.AuthenticatedAPISupport b.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) + b.SetHTTPClientTimeout(exch.HTTPTimeout) b.RESTPollingDelay = exch.RESTPollingDelay b.Verbose = exch.Verbose b.Websocket = exch.Websocket diff --git a/exchanges/bitfinex/bitfinex_test.go b/exchanges/bitfinex/bitfinex_test.go index 984e30bb..73b5d6f4 100644 --- a/exchanges/bitfinex/bitfinex_test.go +++ b/exchanges/bitfinex/bitfinex_test.go @@ -34,8 +34,9 @@ func TestSetup(t *testing.T) { t.Error("Test Failed - Bitfinex Setup values not set correctly") } b.AuthenticatedAPISupport = true - // not worried about rate limit on test - b.SetRateLimit(0, 0) + // custom rate limit for testing + b.Requester.SetRateLimit(true, time.Second*30, 3) + b.Requester.SetRateLimit(false, time.Second*30, 3) } func TestGetPlatformStatus(t *testing.T) { diff --git a/exchanges/bitfinex/bitfinex_wrapper.go b/exchanges/bitfinex/bitfinex_wrapper.go index cdaf22ce..c1d22fa1 100644 --- a/exchanges/bitfinex/bitfinex_wrapper.go +++ b/exchanges/bitfinex/bitfinex_wrapper.go @@ -4,6 +4,7 @@ import ( "errors" "log" "net/url" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -13,8 +14,12 @@ import ( ) // Start starts the Bitfinex go routine -func (b *Bitfinex) Start() { - go b.Run() +func (b *Bitfinex) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + b.Run() + wg.Done() + }() } // Run implements the Bitfinex wrapper @@ -33,9 +38,9 @@ func (b *Bitfinex) Run() { if err != nil { log.Printf("%s Failed to get available symbols.\n", b.GetName()) } else { - err = b.UpdateAvailableCurrencies(exchangeProducts, false) + err = b.UpdateCurrencies(exchangeProducts, false, false) if err != nil { - log.Printf("%s Failed to get config.\n", b.GetName()) + log.Printf("%s Failed to update available symbols.\n", b.GetName()) } } } diff --git a/exchanges/bitfinex/bitfinex_wrapper_test.go b/exchanges/bitfinex/bitfinex_wrapper_test.go index 0f629383..e2e2871c 100644 --- a/exchanges/bitfinex/bitfinex_wrapper_test.go +++ b/exchanges/bitfinex/bitfinex_wrapper_test.go @@ -2,7 +2,7 @@ package bitfinex // func TestStart(t *testing.T) { // start := Bitfinex{} -// start.Start() +// start.Start(wg *sync.WaitGroup) // } // // func TestRun(t *testing.T) { diff --git a/exchanges/bitflyer/bitflyer.go b/exchanges/bitflyer/bitflyer.go index b6fa54d7..7acad6a8 100644 --- a/exchanges/bitflyer/bitflyer.go +++ b/exchanges/bitflyer/bitflyer.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "log" - "net/http" "net/url" "strconv" "time" @@ -66,14 +65,13 @@ const ( privMarginChange = "/me/getcollateralhistory" privTradingCommission = "/me/gettradingcommission" - bitflyerAuthRate = 1000 - bitflyerUnauthRate = 1000 + bitflyerAuthRate = 200 + bitflyerUnauthRate = 500 ) // Bitflyer is the overarching type across this package type Bitflyer struct { exchange.Base - *request.Handler } // SetDefaults sets the basic defaults for Bitflyer @@ -89,8 +87,8 @@ func (b *Bitflyer) SetDefaults() { b.ConfigCurrencyPairFormat.Uppercase = true b.AssetTypes = []string{ticker.Spot} b.SupportsAutoPairUpdating = false - b.Handler = new(request.Handler) - b.SetRequestHandler(b.Name, bitflyerAuthRate, bitflyerUnauthRate, new(http.Client)) + b.SupportsRESTTickerBatching = false + b.Requester = request.New(b.Name, request.NewRateLimit(time.Minute, bitflyerAuthRate), request.NewRateLimit(time.Minute, bitflyerUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup takes in the supplied exchange configuration details and sets params @@ -101,6 +99,7 @@ func (b *Bitflyer) Setup(exch config.ExchangeConfig) { b.Enabled = true b.AuthenticatedAPISupport = exch.AuthenticatedAPISupport b.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) + b.SetHTTPClientTimeout(exch.HTTPTimeout) b.RESTPollingDelay = exch.RESTPollingDelay b.Verbose = exch.Verbose b.Websocket = exch.Websocket diff --git a/exchanges/bitflyer/bitflyer_wrapper.go b/exchanges/bitflyer/bitflyer_wrapper.go index c0f48703..95030d8e 100644 --- a/exchanges/bitflyer/bitflyer_wrapper.go +++ b/exchanges/bitflyer/bitflyer_wrapper.go @@ -3,6 +3,7 @@ package bitflyer import ( "errors" "log" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -12,8 +13,12 @@ import ( ) // Start starts the Bitfinex go routine -func (b *Bitflyer) Start() { - go b.Run() +func (b *Bitflyer) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + b.Run() + wg.Done() + }() } // Run implements the Bitfinex wrapper diff --git a/exchanges/bithumb/bithumb.go b/exchanges/bithumb/bithumb.go index 4a7a504e..f2ffa0fc 100644 --- a/exchanges/bithumb/bithumb.go +++ b/exchanges/bithumb/bithumb.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "log" - "net/http" "net/url" "strconv" "time" @@ -47,14 +46,13 @@ const ( privateMarketBuy = "/trade/market_buy" privateMarketSell = "/trade/market_sell" - bithumbAuthRate = 100 - bithumbUnathRate = 100 + bithumbAuthRate = 10 + bithumbUnauthRate = 20 ) // Bithumb is the overarching type across the Bithumb package type Bithumb struct { exchange.Base - *request.Handler } // SetDefaults sets the basic defaults for Bithumb @@ -71,8 +69,8 @@ func (b *Bithumb) SetDefaults() { b.ConfigCurrencyPairFormat.Index = "KRW" b.AssetTypes = []string{ticker.Spot} b.SupportsAutoPairUpdating = false - b.Handler = new(request.Handler) - b.SetRequestHandler(b.Name, bithumbAuthRate, bithumbUnathRate, new(http.Client)) + b.SupportsRESTTickerBatching = false + b.Requester = request.New(b.Name, request.NewRateLimit(time.Second, bithumbAuthRate), request.NewRateLimit(time.Second, bithumbUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup takes in the supplied exchange configuration details and sets params @@ -83,6 +81,7 @@ func (b *Bithumb) Setup(exch config.ExchangeConfig) { b.Enabled = true b.AuthenticatedAPISupport = exch.AuthenticatedAPISupport b.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) + b.SetHTTPClientTimeout(exch.HTTPTimeout) b.RESTPollingDelay = exch.RESTPollingDelay b.Verbose = exch.Verbose b.Websocket = exch.Websocket diff --git a/exchanges/bithumb/bithumb_wrapper.go b/exchanges/bithumb/bithumb_wrapper.go index 38ea884a..bb6b1015 100644 --- a/exchanges/bithumb/bithumb_wrapper.go +++ b/exchanges/bithumb/bithumb_wrapper.go @@ -3,6 +3,7 @@ package bithumb import ( "errors" "log" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -12,8 +13,12 @@ import ( ) // Start starts the OKEX go routine -func (b *Bithumb) Start() { - go b.Run() +func (b *Bithumb) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + b.Run() + wg.Done() + }() } // Run implements the OKEX wrapper diff --git a/exchanges/bitstamp/bitstamp.go b/exchanges/bitstamp/bitstamp.go index 98b5dff6..8849ebb5 100644 --- a/exchanges/bitstamp/bitstamp.go +++ b/exchanges/bitstamp/bitstamp.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "log" - "net/http" "net/url" "reflect" "strconv" @@ -50,15 +49,14 @@ const ( bitstampAPIReturnType = "string" bitstampAPITradingPairsInfo = "trading-pairs-info" - bitstampAuthRate = 0 - bitstampUnauthRate = 0 + bitstampAuthRate = 600 + bitstampUnauthRate = 600 ) // Bitstamp is the overarching type across the bitstamp package type Bitstamp struct { exchange.Base Balance Balances - *request.Handler } // SetDefaults sets default for Bitstamp @@ -74,8 +72,8 @@ func (b *Bitstamp) SetDefaults() { b.ConfigCurrencyPairFormat.Uppercase = true b.AssetTypes = []string{ticker.Spot} b.SupportsAutoPairUpdating = true - b.Handler = new(request.Handler) - b.SetRequestHandler(b.Name, bitstampAuthRate, bitstampUnauthRate, new(http.Client)) + b.SupportsRESTTickerBatching = false + b.Requester = request.New(b.Name, request.NewRateLimit(time.Minute*10, bitstampAuthRate), request.NewRateLimit(time.Minute*10, bitstampUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup sets configuration values to bitstamp @@ -86,6 +84,7 @@ func (b *Bitstamp) Setup(exch config.ExchangeConfig) { b.Enabled = true b.AuthenticatedAPISupport = exch.AuthenticatedAPISupport b.SetAPIKeys(exch.APIKey, exch.APISecret, exch.ClientID, false) + b.SetHTTPClientTimeout(exch.HTTPTimeout) b.RESTPollingDelay = exch.RESTPollingDelay b.Verbose = exch.Verbose b.Websocket = exch.Websocket @@ -207,7 +206,7 @@ func (b *Bitstamp) GetOrderbook(currency string) (Orderbook, error) { func (b *Bitstamp) GetTradingPairs() ([]TradingPair, error) { var result []TradingPair path := fmt.Sprintf("%s/v%s/%s", bitstampAPIURL, bitstampAPIVersion, bitstampAPITradingPairsInfo) - return result, common.SendHTTPGetRequest(path, true, b.Verbose, &result) + return result, b.SendHTTPRequest(path, &result) } // GetTransactions returns transaction information diff --git a/exchanges/bitstamp/bitstamp_test.go b/exchanges/bitstamp/bitstamp_test.go index f31b2fd4..0252d6cb 100644 --- a/exchanges/bitstamp/bitstamp_test.go +++ b/exchanges/bitstamp/bitstamp_test.go @@ -85,7 +85,6 @@ func TestGetOrderbook(t *testing.T) { func TestGetTradingPairs(t *testing.T) { t.Parallel() - b := Bitstamp{} _, err := b.GetTradingPairs() if err != nil { t.Error("Test Failed - GetTradingPairs() error", err) diff --git a/exchanges/bitstamp/bitstamp_wrapper.go b/exchanges/bitstamp/bitstamp_wrapper.go index 66178326..82932e51 100644 --- a/exchanges/bitstamp/bitstamp_wrapper.go +++ b/exchanges/bitstamp/bitstamp_wrapper.go @@ -4,6 +4,7 @@ import ( "errors" "log" "strings" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -13,8 +14,12 @@ import ( ) // Start starts the Bitstamp go routine -func (b *Bitstamp) Start() { - go b.Run() +func (b *Bitstamp) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + b.Run() + wg.Done() + }() } // Run implements the Bitstamp wrapper @@ -41,7 +46,7 @@ func (b *Bitstamp) Run() { pair := strings.Split(pairs[x].Name, "/") currencies = append(currencies, pair[0]+pair[1]) } - err = b.UpdateAvailableCurrencies(currencies, false) + err = b.UpdateCurrencies(currencies, false, false) if err != nil { log.Printf("%s Failed to update available currencies.\n", b.Name) } diff --git a/exchanges/bittrex/bittrex.go b/exchanges/bittrex/bittrex.go index 185e4a3f..8960f03a 100644 --- a/exchanges/bittrex/bittrex.go +++ b/exchanges/bittrex/bittrex.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "log" - "net/http" "net/url" "strconv" "time" @@ -61,7 +60,6 @@ const ( // Bittrex is the overaching type across the bittrex methods type Bittrex struct { exchange.Base - *request.Handler } // SetDefaults method assignes the default values for Bittrex @@ -77,8 +75,8 @@ func (b *Bittrex) SetDefaults() { b.ConfigCurrencyPairFormat.Uppercase = true b.AssetTypes = []string{ticker.Spot} b.SupportsAutoPairUpdating = true - b.Handler = new(request.Handler) - b.SetRequestHandler(b.Name, bittrexAuthRate, bittrexUnauthRate, new(http.Client)) + b.SupportsRESTTickerBatching = true + b.Requester = request.New(b.Name, request.NewRateLimit(time.Second, bittrexAuthRate), request.NewRateLimit(time.Second, bittrexUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup method sets current configuration details if enabled @@ -89,6 +87,7 @@ func (b *Bittrex) Setup(exch config.ExchangeConfig) { b.Enabled = true b.AuthenticatedAPISupport = exch.AuthenticatedAPISupport b.SetAPIKeys(exch.APIKey, exch.APISecret, exch.ClientID, false) + b.SetHTTPClientTimeout(exch.HTTPTimeout) b.RESTPollingDelay = exch.RESTPollingDelay b.Verbose = exch.Verbose b.Websocket = exch.Websocket diff --git a/exchanges/bittrex/bittrex_wrapper.go b/exchanges/bittrex/bittrex_wrapper.go index a7e2745b..156c9c35 100644 --- a/exchanges/bittrex/bittrex_wrapper.go +++ b/exchanges/bittrex/bittrex_wrapper.go @@ -3,6 +3,7 @@ package bittrex import ( "errors" "log" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -12,8 +13,12 @@ import ( ) // Start starts the Bittrex go routine -func (b *Bittrex) Start() { - go b.Run() +func (b *Bittrex) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + b.Run() + wg.Done() + }() } // Run implements the Bittrex wrapper @@ -43,12 +48,12 @@ func (b *Bittrex) Run() { enabledPairs := []string{"USDT-BTC"} log.Println("WARNING: Available pairs for Bittrex reset due to config upgrade, please enable the ones you would like again") - err = b.UpdateEnabledCurrencies(enabledPairs, true) + err = b.UpdateCurrencies(enabledPairs, true, true) if err != nil { log.Printf("%s Failed to get config.\n", b.GetName()) } } - err = b.UpdateAvailableCurrencies(currencies, forceUpgrade) + err = b.UpdateCurrencies(currencies, false, forceUpgrade) if err != nil { log.Printf("%s Failed to get config.\n", b.GetName()) } diff --git a/exchanges/btcc/btcc.go b/exchanges/btcc/btcc.go index ba1f4cbc..2cbb1b0f 100644 --- a/exchanges/btcc/btcc.go +++ b/exchanges/btcc/btcc.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "log" - "net/http" "net/url" "strconv" "strings" @@ -51,7 +50,6 @@ const ( // BTCC is the main overaching type across the BTCC package type BTCC struct { exchange.Base - *request.Handler } // SetDefaults sets default values for the exchange @@ -68,8 +66,8 @@ func (b *BTCC) SetDefaults() { b.ConfigCurrencyPairFormat.Uppercase = true b.AssetTypes = []string{ticker.Spot} b.SupportsAutoPairUpdating = true - b.Handler = new(request.Handler) - b.SetRequestHandler(b.Name, btccAuthRate, btccUnauthRate, new(http.Client)) + b.SupportsRESTTickerBatching = false + b.Requester = request.New(b.Name, request.NewRateLimit(time.Second, btccAuthRate), request.NewRateLimit(time.Second, btccUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup is run on startup to setup exchange with config values @@ -80,6 +78,7 @@ func (b *BTCC) Setup(exch config.ExchangeConfig) { b.Enabled = true b.AuthenticatedAPISupport = exch.AuthenticatedAPISupport b.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) + b.SetHTTPClientTimeout(exch.HTTPTimeout) b.RESTPollingDelay = exch.RESTPollingDelay b.Verbose = exch.Verbose b.Websocket = exch.Websocket diff --git a/exchanges/btcc/btcc_wrapper.go b/exchanges/btcc/btcc_wrapper.go index 086effc2..6ad23c46 100644 --- a/exchanges/btcc/btcc_wrapper.go +++ b/exchanges/btcc/btcc_wrapper.go @@ -3,6 +3,7 @@ package btcc import ( "errors" "log" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" @@ -13,8 +14,12 @@ import ( ) // Start starts the BTCC go routine -func (b *BTCC) Start() { - go b.Run() +func (b *BTCC) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + b.Run() + wg.Done() + }() } // Run implements the BTCC wrapper @@ -44,12 +49,12 @@ func (b *BTCC) Run() { exchCfg.EnabledPairs = pairs[0] b.BaseCurrencies = []string{"USD"} - err = b.UpdateAvailableCurrencies(pairs, true) + err = b.UpdateCurrencies(pairs, false, true) if err != nil { log.Printf("%s failed to update available currencies. %s\n", b.Name, err) } - err = b.UpdateEnabledCurrencies(pairs, true) + err = b.UpdateCurrencies(pairs, true, true) if err != nil { log.Printf("%s failed to update enabled currencies. %s\n", b.Name, err) } diff --git a/exchanges/btcmarkets/btcmarkets.go b/exchanges/btcmarkets/btcmarkets.go index af6c421d..b50ae263 100644 --- a/exchanges/btcmarkets/btcmarkets.go +++ b/exchanges/btcmarkets/btcmarkets.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "log" - "net/http" "net/url" "time" @@ -39,15 +38,14 @@ const ( orderStatusFullyMatched = "Fully Matched" orderStatusPartiallyMatched = "Partially Matched" - btcmarketsAuthLimit = 0 - btcmarketsUnauthLimit = 0 + btcmarketsAuthLimit = 10 + btcmarketsUnauthLimit = 25 ) // BTCMarkets is the overarching type across the BTCMarkets package type BTCMarkets struct { exchange.Base Ticker map[string]Ticker - *request.Handler } // SetDefaults sets basic defaults @@ -65,8 +63,8 @@ func (b *BTCMarkets) SetDefaults() { b.ConfigCurrencyPairFormat.Uppercase = true b.AssetTypes = []string{ticker.Spot} b.SupportsAutoPairUpdating = true - b.Handler = new(request.Handler) - b.SetRequestHandler(b.Name, btcmarketsAuthLimit, btcmarketsUnauthLimit, new(http.Client)) + b.SupportsRESTTickerBatching = false + b.Requester = request.New(b.Name, request.NewRateLimit(time.Second*10, btcmarketsAuthLimit), request.NewRateLimit(time.Second*10, btcmarketsUnauthLimit), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup takes in an exchange configuration and sets all parameters @@ -77,6 +75,7 @@ func (b *BTCMarkets) Setup(exch config.ExchangeConfig) { b.Enabled = true b.AuthenticatedAPISupport = exch.AuthenticatedAPISupport b.SetAPIKeys(exch.APIKey, exch.APISecret, "", true) + b.SetHTTPClientTimeout(exch.HTTPTimeout) b.RESTPollingDelay = exch.RESTPollingDelay b.Verbose = exch.Verbose b.Websocket = exch.Websocket diff --git a/exchanges/btcmarkets/btcmarkets_wrapper.go b/exchanges/btcmarkets/btcmarkets_wrapper.go index aa31bf55..206ae1af 100644 --- a/exchanges/btcmarkets/btcmarkets_wrapper.go +++ b/exchanges/btcmarkets/btcmarkets_wrapper.go @@ -3,6 +3,7 @@ package btcmarkets import ( "errors" "log" + "sync" "github.com/thrasher-/gocryptotrader/common" @@ -13,8 +14,12 @@ import ( ) // Start starts the BTC Markets go routine -func (b *BTCMarkets) Start() { - go b.Run() +func (b *BTCMarkets) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + b.Run() + wg.Done() + }() } // Run implements the BTC Markets wrapper @@ -37,13 +42,13 @@ func (b *BTCMarkets) Run() { log.Println("BTCMarkets: Upgrading available and enabled pairs") - err := b.UpdateEnabledCurrencies(enabledPairs, true) + err := b.UpdateCurrencies(enabledPairs, true, true) if err != nil { log.Printf("%s Failed to get config.\n", b.GetName()) return } - err = b.UpdateAvailableCurrencies(availablePairs, true) + err = b.UpdateCurrencies(availablePairs, false, true) if err != nil { log.Printf("%s Failed to get config.\n", b.GetName()) return diff --git a/exchanges/coinut/coinut.go b/exchanges/coinut/coinut.go index 5c9d13ef..cd467c2b 100644 --- a/exchanges/coinut/coinut.go +++ b/exchanges/coinut/coinut.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "log" - "net/http" "time" "github.com/gorilla/websocket" @@ -44,7 +43,6 @@ type COINUT struct { exchange.Base WebsocketConn *websocket.Conn InstrumentMap map[string]int - *request.Handler } // SetDefaults sets current default values @@ -63,8 +61,8 @@ func (c *COINUT) SetDefaults() { c.ConfigCurrencyPairFormat.Uppercase = true c.AssetTypes = []string{ticker.Spot} c.SupportsAutoPairUpdating = true - c.Handler = new(request.Handler) - c.SetRequestHandler(c.Name, coinutAuthRate, coinutUnauthRate, new(http.Client)) + c.SupportsRESTTickerBatching = false + c.Requester = request.New(c.Name, request.NewRateLimit(time.Second, coinutAuthRate), request.NewRateLimit(time.Second, coinutUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup sets the current exchange configuration @@ -75,6 +73,7 @@ func (c *COINUT) Setup(exch config.ExchangeConfig) { c.Enabled = true c.AuthenticatedAPISupport = exch.AuthenticatedAPISupport c.SetAPIKeys(exch.APIKey, exch.APISecret, exch.ClientID, true) + c.SetHTTPClientTimeout(exch.HTTPTimeout) c.RESTPollingDelay = exch.RESTPollingDelay c.Verbose = exch.Verbose c.Websocket = exch.Websocket diff --git a/exchanges/coinut/coinut_wrapper.go b/exchanges/coinut/coinut_wrapper.go index 74bd87fb..b681c6f6 100644 --- a/exchanges/coinut/coinut_wrapper.go +++ b/exchanges/coinut/coinut_wrapper.go @@ -3,6 +3,7 @@ package coinut import ( "errors" "log" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -12,8 +13,12 @@ import ( ) // Start starts the COINUT go routine -func (c *COINUT) Start() { - go c.Run() +func (c *COINUT) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + c.Run() + wg.Done() + }() } // Run implements the COINUT wrapper @@ -41,9 +46,9 @@ func (c *COINUT) Run() { currencies = append(currencies, x) } - err = c.UpdateAvailableCurrencies(currencies, false) + err = c.UpdateCurrencies(currencies, false, false) if err != nil { - log.Printf("%s Failed to get config.\n", c.GetName()) + log.Printf("%s Failed to update available currencies.\n", c.GetName()) } } diff --git a/exchanges/exchange.go b/exchanges/exchange.go index ccec1859..02423b50 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -2,6 +2,8 @@ package exchange import ( "log" + "net/http" + "sync" "time" "github.com/thrasher-/gocryptotrader/common" @@ -9,6 +11,7 @@ import ( "github.com/thrasher-/gocryptotrader/currency/pair" "github.com/thrasher-/gocryptotrader/exchanges/nonce" "github.com/thrasher-/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-/gocryptotrader/exchanges/request" "github.com/thrasher-/gocryptotrader/exchanges/ticker" ) @@ -19,6 +22,8 @@ const ( WarningAuthenticatedRequestWithoutCredentialsSet = "WARNING -- Exchange %s authenticated HTTP request called but not supported due to unset/default API keys." // ErrExchangeNotFound is a constant for an error message ErrExchangeNotFound = "Exchange not found in dataset." + // DefaultHTTPTimeout is the default HTTP/HTTPS Timeout for exchange requests + DefaultHTTPTimeout = time.Second * 15 ) // AccountInfo is a Generic type to hold each exchange's holdings in @@ -62,17 +67,20 @@ type Base struct { AssetTypes []string PairsLastUpdated int64 SupportsAutoPairUpdating bool + SupportsRESTTickerBatching bool + HTTPTimeout time.Duration WebsocketURL string APIUrl string RequestCurrencyPairFormat config.CurrencyPairFormatConfig ConfigCurrencyPairFormat config.CurrencyPairFormatConfig + *request.Requester } // IBotExchange enforces standard functions for all exchanges supported in // GoCryptoTrader type IBotExchange interface { Setup(exch config.ExchangeConfig) - Start() + Start(wg *sync.WaitGroup) SetDefaults() GetName() string IsEnabled() bool @@ -89,6 +97,38 @@ type IBotExchange interface { GetExchangeHistory(pair.CurrencyPair, string) ([]TradeHistory, error) SupportsAutoPairUpdates() bool GetLastPairsUpdateTime() int64 + SupportsRESTTickerBatchUpdates() bool +} + +// SupportsRESTTickerBatchUpdates returns whether or not the +// exhange supports REST batch ticker fetching +func (e *Base) SupportsRESTTickerBatchUpdates() bool { + return e.SupportsRESTTickerBatching +} + +// SetHTTPClientTimeout sets the timeout value for the exchanges +// HTTP Client +func (e *Base) SetHTTPClientTimeout(t time.Duration) { + if e.Requester == nil { + e.Requester = request.New(e.Name, request.NewRateLimit(time.Second, 0), request.NewRateLimit(time.Second, 0), new(http.Client)) + } + e.Requester.HTTPClient.Timeout = t +} + +// SetHTTPClient sets exchanges HTTP client +func (e *Base) SetHTTPClient(h *http.Client) { + if e.Requester == nil { + e.Requester = request.New(e.Name, request.NewRateLimit(time.Second, 0), request.NewRateLimit(time.Second, 0), new(http.Client)) + } + e.Requester.HTTPClient = h +} + +// GetHTTPClient gets the exchanges HTTP client +func (e *Base) GetHTTPClient() *http.Client { + if e.Requester == nil { + e.Requester = request.New(e.Name, request.NewRateLimit(time.Second, 0), request.NewRateLimit(time.Second, 0), new(http.Client)) + } + return e.Requester.HTTPClient } // SetAutoPairDefaults sets the default values for whether or not the exchange @@ -378,12 +418,28 @@ func (e *Base) SetCurrencies(pairs []pair.CurrencyPair, enabledPairs bool) error return cfg.UpdateExchangeConfig(exchCfg) } -// UpdateEnabledCurrencies is a method that sets new pairs to the current -// exchange. Setting force to true upgrades the enabled currencies -func (e *Base) UpdateEnabledCurrencies(exchangeProducts []string, force bool) error { +// UpdateCurrencies updates the exchange currency pairs for either enabledPairs or +// availablePairs +func (e *Base) UpdateCurrencies(exchangeProducts []string, enabled, force bool) error { exchangeProducts = common.SplitStrings(common.StringToUpper(common.JoinStrings(exchangeProducts, ",")), ",") - diff := common.StringSliceDifference(e.EnabledPairs, exchangeProducts) - if force || len(diff) > 0 { + var products []string + + for x := range exchangeProducts { + if exchangeProducts[x] == "" { + continue + } + products = append(products, exchangeProducts[x]) + } + + var newPairs, removedPairs []string + + if enabled { + newPairs, removedPairs = pair.FindPairDifferences(e.EnabledPairs, products) + } else { + newPairs, removedPairs = pair.FindPairDifferences(e.AvailablePairs, products) + } + + if force || len(newPairs) > 0 || len(removedPairs) > 0 { cfg := config.GetConfig() exch, err := cfg.GetExchangeConfig(e.Name) if err != nil { @@ -393,34 +449,21 @@ func (e *Base) UpdateEnabledCurrencies(exchangeProducts []string, force bool) er if force { log.Printf("%s forced update of enabled pairs.", e.Name) } else { - log.Printf("%s Updating available pairs. Difference: %s.\n", e.Name, diff) - } - exch.EnabledPairs = common.JoinStrings(exchangeProducts, ",") - e.EnabledPairs = exchangeProducts - return cfg.UpdateExchangeConfig(exch) - } - return nil -} - -// UpdateAvailableCurrencies is a method that sets new pairs to the current -// exchange. Setting force to true upgrades the available currencies -func (e *Base) UpdateAvailableCurrencies(exchangeProducts []string, force bool) error { - exchangeProducts = common.SplitStrings(common.StringToUpper(common.JoinStrings(exchangeProducts, ",")), ",") - diff := common.StringSliceDifference(e.AvailablePairs, exchangeProducts) - if force || len(diff) > 0 { - cfg := config.GetConfig() - exch, err := cfg.GetExchangeConfig(e.Name) - if err != nil { - return err + if len(newPairs) > 0 { + log.Printf("%s Updating pairs - New: %s.\n", e.Name, newPairs) + } + if len(removedPairs) > 0 { + log.Printf("%s Updating pairs - Removed: %s.\n", e.Name, removedPairs) + } } - if force { - log.Printf("%s forced update of available pairs.", e.Name) + if enabled { + exch.EnabledPairs = common.JoinStrings(products, ",") + e.EnabledPairs = products } else { - log.Printf("%s Updating available pairs. Difference: %s.\n", e.Name, diff) + exch.AvailablePairs = common.JoinStrings(products, ",") + e.AvailablePairs = products } - exch.AvailablePairs = common.JoinStrings(exchangeProducts, ",") - e.AvailablePairs = exchangeProducts return cfg.UpdateExchangeConfig(exch) } return nil diff --git a/exchanges/exchange_test.go b/exchanges/exchange_test.go index 16075d77..28028858 100644 --- a/exchanges/exchange_test.go +++ b/exchanges/exchange_test.go @@ -1,15 +1,66 @@ package exchange import ( + "net/http" "testing" "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" "github.com/thrasher-/gocryptotrader/currency/pair" + "github.com/thrasher-/gocryptotrader/exchanges/request" "github.com/thrasher-/gocryptotrader/exchanges/ticker" ) +func TestSupportsRESTTickerBatchUpdates(t *testing.T) { + b := Base{ + Name: "RAWR", + SupportsRESTTickerBatching: true, + } + + if !b.SupportsRESTTickerBatchUpdates() { + t.Fatal("Test failed. TestSupportsRESTTickerBatchUpdates returned false") + } +} + +func TestHTTPClient(t *testing.T) { + r := Base{Name: "asdf"} + r.SetHTTPClientTimeout(time.Duration(time.Second * 5)) + + if r.GetHTTPClient().Timeout != time.Second*5 { + t.Fatalf("Test failed. TestHTTPClient unexpected value") + } + + r.Requester = nil + newClient := new(http.Client) + newClient.Timeout = time.Duration(time.Second * 10) + + r.SetHTTPClient(newClient) + if r.GetHTTPClient().Timeout != time.Second*10 { + t.Fatalf("Test failed. TestHTTPClient unexpected value") + } + + r.Requester = nil + if r.GetHTTPClient() == nil { + t.Fatalf("Test failed. TestHTTPClient unexpected value") + } + + b := Base{Name: "RAWR"} + b.Requester = request.New(b.Name, request.NewRateLimit(time.Second, 1), request.NewRateLimit(time.Second, 1), new(http.Client)) + + b.SetHTTPClientTimeout(time.Second * 5) + if b.GetHTTPClient().Timeout != time.Second*5 { + t.Fatalf("Test failed. TestHTTPClient unexpected value") + } + + newClient = new(http.Client) + newClient.Timeout = time.Duration(time.Second * 10) + + b.SetHTTPClient(newClient) + if b.GetHTTPClient().Timeout != time.Second*10 { + t.Fatalf("Test failed. TestHTTPClient unexpected value") + } +} func TestSetAutoPairDefaults(t *testing.T) { cfg := config.GetConfig() err := cfg.LoadConfig(config.ConfigTestFile) @@ -613,7 +664,7 @@ func TestSetCurrencies(t *testing.T) { } } -func TestUpdateEnabledCurrencies(t *testing.T) { +func TestUpdateCurrencies(t *testing.T) { cfg := config.GetConfig() err := cfg.LoadConfig(config.ConfigTestFile) if err != nil { @@ -621,72 +672,69 @@ func TestUpdateEnabledCurrencies(t *testing.T) { } UAC := Base{Name: "ANX"} - exchangeProducts := []string{"ltc", "btc", "usd", "aud"} + exchangeProducts := []string{"ltc", "btc", "usd", "aud", ""} // Test updating exchange products for an exchange which doesn't exist UAC.Name = "Blah" - err = UAC.UpdateEnabledCurrencies(exchangeProducts, false) + err = UAC.UpdateCurrencies(exchangeProducts, true, false) if err == nil { - t.Errorf("Test Failed - Exchange TestUpdateEnabledCurrencies succeeded on an exchange which doesn't exist") + t.Errorf("Test Failed - Exchange TestUpdateCurrencies succeeded on an exchange which doesn't exist") } // Test updating exchange products UAC.Name = "ANX" - err = UAC.UpdateEnabledCurrencies(exchangeProducts, false) + err = UAC.UpdateCurrencies(exchangeProducts, true, false) if err != nil { - t.Errorf("Test Failed - Exchange TestUpdateEnabledCurrencies error: %s", err) + t.Errorf("Test Failed - Exchange TestUpdateCurrencies error: %s", err) } // Test updating the same new products, diff should be 0 UAC.Name = "ANX" - err = UAC.UpdateEnabledCurrencies(exchangeProducts, false) + err = UAC.UpdateCurrencies(exchangeProducts, true, false) if err != nil { - t.Errorf("Test Failed - Exchange TestUpdateEnabledCurrencies error: %s", err) + t.Errorf("Test Failed - Exchange TestUpdateCurrencies error: %s", err) } // Test force updating to only one product exchangeProducts = []string{"btc"} - err = UAC.UpdateEnabledCurrencies(exchangeProducts, true) + err = UAC.UpdateCurrencies(exchangeProducts, true, true) if err != nil { - t.Errorf("Test Failed - Forced Exchange TestUpdateEnabledCurrencies error: %s", err) - } -} - -func TestUpdateAvailableCurrencies(t *testing.T) { - cfg := config.GetConfig() - err := cfg.LoadConfig(config.ConfigTestFile) - if err != nil { - t.Fatal("Test failed. TestUpdateAvailableCurrencies failed to load config") + t.Errorf("Test Failed - Forced Exchange TestUpdateCurrencies error: %s", err) } - UAC := Base{Name: "ANX"} - exchangeProducts := []string{"ltc", "btc", "usd", "aud"} - + exchangeProducts = []string{"ltc", "btc", "usd", "aud"} // Test updating exchange products for an exchange which doesn't exist UAC.Name = "Blah" - err = UAC.UpdateAvailableCurrencies(exchangeProducts, false) + err = UAC.UpdateCurrencies(exchangeProducts, false, false) if err == nil { - t.Errorf("Test Failed - Exchange UpdateAvailableCurrencies() succeeded on an exchange which doesn't exist") + t.Errorf("Test Failed - Exchange UpdateCurrencies() succeeded on an exchange which doesn't exist") } // Test updating exchange products UAC.Name = "ANX" - err = UAC.UpdateAvailableCurrencies(exchangeProducts, false) + err = UAC.UpdateCurrencies(exchangeProducts, false, false) if err != nil { - t.Errorf("Test Failed - Exchange UpdateAvailableCurrencies() error: %s", err) + t.Errorf("Test Failed - Exchange UpdateCurrencies() error: %s", err) } // Test updating the same new products, diff should be 0 UAC.Name = "ANX" - err = UAC.UpdateAvailableCurrencies(exchangeProducts, false) + err = UAC.UpdateCurrencies(exchangeProducts, false, false) if err != nil { - t.Errorf("Test Failed - Exchange UpdateAvailableCurrencies() error: %s", err) + t.Errorf("Test Failed - Exchange UpdateCurrencies() error: %s", err) } // Test force updating to only one product exchangeProducts = []string{"btc"} - err = UAC.UpdateAvailableCurrencies(exchangeProducts, true) + err = UAC.UpdateCurrencies(exchangeProducts, false, true) if err != nil { - t.Errorf("Test Failed - Forced Exchange UpdateAvailableCurrencies() error: %s", err) + t.Errorf("Test Failed - Forced Exchange UpdateCurrencies() error: %s", err) + } + + // Test update currency pairs with btc excluded + exchangeProducts = []string{"ltc", "eth"} + err = UAC.UpdateCurrencies(exchangeProducts, false, false) + if err != nil { + t.Errorf("Test Failed - Forced Exchange UpdateCurrencies() error: %s", err) } } diff --git a/exchanges/exmo/exmo.go b/exchanges/exmo/exmo.go index c3d3a88c..a7693216 100644 --- a/exchanges/exmo/exmo.go +++ b/exchanges/exmo/exmo.go @@ -3,7 +3,6 @@ package exmo import ( "fmt" "log" - "net/http" "net/url" "reflect" "strconv" @@ -42,14 +41,13 @@ const ( exmoWalletHistory = "wallet_history" // Rate limit: 180 per/minute - exmoAuthRate = 333 - exmoUnauthRate = 333 + exmoAuthRate = 180 + exmoUnauthRate = 180 ) // EXMO exchange struct type EXMO struct { exchange.Base - *request.Handler } // SetDefaults sets the basic defaults for exmo @@ -66,8 +64,8 @@ func (e *EXMO) SetDefaults() { e.ConfigCurrencyPairFormat.Uppercase = true e.AssetTypes = []string{ticker.Spot} e.SupportsAutoPairUpdating = true - e.Handler = new(request.Handler) - e.SetRequestHandler(e.Name, exmoAuthRate, exmoUnauthRate, new(http.Client)) + e.SupportsRESTTickerBatching = true + e.Requester = request.New(e.Name, request.NewRateLimit(time.Minute, exmoAuthRate), request.NewRateLimit(time.Minute, exmoUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup takes in the supplied exchange configuration details and sets params @@ -78,6 +76,7 @@ func (e *EXMO) Setup(exch config.ExchangeConfig) { e.Enabled = true e.AuthenticatedAPISupport = exch.AuthenticatedAPISupport e.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) + e.SetHTTPClientTimeout(exch.HTTPTimeout) e.RESTPollingDelay = exch.RESTPollingDelay e.Verbose = exch.Verbose e.Websocket = exch.Websocket diff --git a/exchanges/exmo/exmo_wrapper.go b/exchanges/exmo/exmo_wrapper.go index ca887ed4..54f92fec 100644 --- a/exchanges/exmo/exmo_wrapper.go +++ b/exchanges/exmo/exmo_wrapper.go @@ -4,6 +4,7 @@ import ( "errors" "log" "strconv" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -13,8 +14,12 @@ import ( ) // Start starts the EXMO go routine -func (e *EXMO) Start() { - go e.Run() +func (e *EXMO) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + e.Run() + wg.Done() + }() } // Run implements the EXMO wrapper @@ -32,7 +37,7 @@ func (e *EXMO) Run() { for x := range exchangeProducts { currencies = append(currencies, x) } - err = e.UpdateAvailableCurrencies(currencies, false) + err = e.UpdateCurrencies(currencies, false, false) if err != nil { log.Printf("%s Failed to update available currencies.\n", e.GetName()) } diff --git a/exchanges/gdax/gdax.go b/exchanges/gdax/gdax.go index 6b1e8b07..c2c1f22b 100644 --- a/exchanges/gdax/gdax.go +++ b/exchanges/gdax/gdax.go @@ -5,9 +5,9 @@ import ( "errors" "fmt" "log" - "net/http" "net/url" "strconv" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" @@ -49,8 +49,8 @@ const ( gdaxCoinbaseAccounts = "coinbase-accounts" gdaxTrailingVolume = "users/self/trailing-volume" - gdaxAuthRate = 0 - gdaxUnauthRate = 0 + gdaxAuthRate = 5 + gdaxUnauthRate = 3 ) var sometin []string @@ -58,7 +58,6 @@ var sometin []string // GDAX is the overarching type across the GDAX package type GDAX struct { exchange.Base - *request.Handler } // SetDefaults sets default values for the exchange @@ -77,8 +76,8 @@ func (g *GDAX) SetDefaults() { g.AssetTypes = []string{ticker.Spot} g.APIUrl = gdaxAPIURL g.SupportsAutoPairUpdating = true - g.Handler = new(request.Handler) - g.SetRequestHandler(g.Name, gdaxAuthRate, gdaxUnauthRate, new(http.Client)) + g.SupportsRESTTickerBatching = false + g.Requester = request.New(g.Name, request.NewRateLimit(time.Second, gdaxAuthRate), request.NewRateLimit(time.Second, gdaxUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup initialises the exchange parameters with the current configuration @@ -89,6 +88,7 @@ func (g *GDAX) Setup(exch config.ExchangeConfig) { g.Enabled = true g.AuthenticatedAPISupport = exch.AuthenticatedAPISupport g.SetAPIKeys(exch.APIKey, exch.APISecret, exch.ClientID, true) + g.SetHTTPClientTimeout(exch.HTTPTimeout) g.RESTPollingDelay = exch.RESTPollingDelay g.Verbose = exch.Verbose g.Websocket = exch.Websocket diff --git a/exchanges/gdax/gdax_wrapper.go b/exchanges/gdax/gdax_wrapper.go index 2edbc563..bd578d60 100644 --- a/exchanges/gdax/gdax_wrapper.go +++ b/exchanges/gdax/gdax_wrapper.go @@ -3,6 +3,7 @@ package gdax import ( "errors" "log" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -12,8 +13,12 @@ import ( ) // Start starts the GDAX go routine -func (g *GDAX) Start() { - go g.Run() +func (g *GDAX) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + g.Run() + wg.Done() + }() } // Run implements the GDAX wrapper @@ -38,9 +43,9 @@ func (g *GDAX) Run() { currencies = append(currencies, x.ID[0:3]+x.ID[4:]) } } - err = g.UpdateAvailableCurrencies(currencies, false) + err = g.UpdateCurrencies(currencies, false, false) if err != nil { - log.Printf("%s Failed to get config.\n", g.GetName()) + log.Printf("%s Failed to update available currencies.\n", g.GetName()) } } } diff --git a/exchanges/gemini/gemini.go b/exchanges/gemini/gemini.go index 12aa4e19..729ae697 100644 --- a/exchanges/gemini/gemini.go +++ b/exchanges/gemini/gemini.go @@ -4,10 +4,10 @@ import ( "errors" "fmt" "log" - "net/http" "net/url" "strconv" "strings" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" @@ -42,8 +42,8 @@ const ( geminiHeartbeat = "heartbeat" // gemini limit rates - geminiAuthRate = 100 - geminiUnauthRate = 500 + geminiAuthRate = 600 + geminiUnauthRate = 120 // Too many requests returns this geminiRateError = "429" @@ -55,8 +55,7 @@ const ( var ( // Session manager - Session map[int]*Gemini - gHandler *request.Handler + Session map[int]*Gemini ) // Gemini is the overarching type across the Gemini package, create multiple @@ -68,7 +67,6 @@ type Gemini struct { exchange.Base Role string RequiresHeartBeat bool - *request.Handler } // AddSession adds a new session to the gemini base @@ -76,9 +74,6 @@ func AddSession(g *Gemini, sessionID int, apiKey, apiSecret, role string, needsH if Session == nil { Session = make(map[int]*Gemini) } - if gHandler == nil { - gHandler = new(request.Handler) - } _, ok := Session[sessionID] if ok { @@ -113,14 +108,8 @@ func (g *Gemini) SetDefaults() { g.ConfigCurrencyPairFormat.Uppercase = true g.AssetTypes = []string{ticker.Spot} g.SupportsAutoPairUpdating = true - if gHandler != nil { - g.Handler = gHandler - } else { - g.Handler = new(request.Handler) - } - if g.Handler.Client == nil { - g.SetRequestHandler(g.Name, geminiAuthRate, geminiUnauthRate, new(http.Client)) - } + g.SupportsRESTTickerBatching = false + g.Requester = request.New(g.Name, request.NewRateLimit(time.Minute, geminiAuthRate), request.NewRateLimit(time.Minute, geminiUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup sets exchange configuration parameters @@ -131,6 +120,7 @@ func (g *Gemini) Setup(exch config.ExchangeConfig) { g.Enabled = true g.AuthenticatedAPISupport = exch.AuthenticatedAPISupport g.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) + g.SetHTTPClientTimeout(exch.HTTPTimeout) g.RESTPollingDelay = exch.RESTPollingDelay g.Verbose = exch.Verbose g.Websocket = exch.Websocket diff --git a/exchanges/gemini/gemini_wrapper.go b/exchanges/gemini/gemini_wrapper.go index 4730813b..2e3af646 100644 --- a/exchanges/gemini/gemini_wrapper.go +++ b/exchanges/gemini/gemini_wrapper.go @@ -4,6 +4,7 @@ import ( "errors" "log" "net/url" + "sync" "github.com/thrasher-/gocryptotrader/currency/pair" "github.com/thrasher-/gocryptotrader/exchanges" @@ -12,8 +13,12 @@ import ( ) // Start starts the Gemini go routine -func (g *Gemini) Start() { - go g.Run() +func (g *Gemini) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + g.Run() + wg.Done() + }() } // Run implements the Gemini wrapper @@ -27,9 +32,9 @@ func (g *Gemini) Run() { if err != nil { log.Printf("%s Failed to get available symbols.\n", g.GetName()) } else { - err = g.UpdateAvailableCurrencies(exchangeProducts, false) + err = g.UpdateCurrencies(exchangeProducts, false, false) if err != nil { - log.Printf("%s Failed to get config.\n", g.GetName()) + log.Printf("%s Failed to update available currencies.\n", g.GetName()) } } } diff --git a/exchanges/hitbtc/hitbtc.go b/exchanges/hitbtc/hitbtc.go index 6eaebe0e..d3f9726f 100644 --- a/exchanges/hitbtc/hitbtc.go +++ b/exchanges/hitbtc/hitbtc.go @@ -5,9 +5,9 @@ import ( "errors" "fmt" "log" - "net/http" "net/url" "strconv" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" @@ -49,7 +49,6 @@ const ( // HitBTC is the overarching type across the hitbtc package type HitBTC struct { exchange.Base - *request.Handler } // SetDefaults sets default settings for hitbtc @@ -66,8 +65,8 @@ func (p *HitBTC) SetDefaults() { p.ConfigCurrencyPairFormat.Uppercase = true p.AssetTypes = []string{ticker.Spot} p.SupportsAutoPairUpdating = true - p.Handler = new(request.Handler) - p.SetRequestHandler(p.Name, hitbtcAuthRate, hitbtcUnauthRate, new(http.Client)) + p.SupportsRESTTickerBatching = true + p.Requester = request.New(p.Name, request.NewRateLimit(time.Second, hitbtcAuthRate), request.NewRateLimit(time.Second, hitbtcUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup sets user exchange configuration settings @@ -78,6 +77,7 @@ func (p *HitBTC) Setup(exch config.ExchangeConfig) { p.Enabled = true p.AuthenticatedAPISupport = exch.AuthenticatedAPISupport p.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) + p.SetHTTPClientTimeout(exch.HTTPTimeout) p.RESTPollingDelay = exch.RESTPollingDelay // Max 60000ms p.Verbose = exch.Verbose p.Websocket = exch.Websocket diff --git a/exchanges/hitbtc/hitbtc_wrapper.go b/exchanges/hitbtc/hitbtc_wrapper.go index d5489cd7..5252e5f7 100644 --- a/exchanges/hitbtc/hitbtc_wrapper.go +++ b/exchanges/hitbtc/hitbtc_wrapper.go @@ -3,6 +3,7 @@ package hitbtc import ( "errors" "log" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -12,8 +13,12 @@ import ( ) // Start starts the HitBTC go routine -func (h *HitBTC) Start() { - go h.Run() +func (h *HitBTC) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + h.Run() + wg.Done() + }() } // Run implements the HitBTC wrapper @@ -45,12 +50,12 @@ func (h *HitBTC) Run() { enabledPairs := []string{"BTC-USD"} log.Println("WARNING: Available pairs for HitBTC reset due to config upgrade, please enable the ones you would like again.") - err = h.UpdateEnabledCurrencies(enabledPairs, true) + err = h.UpdateCurrencies(enabledPairs, true, true) if err != nil { log.Printf("%s Failed to update enabled currencies.\n", h.GetName()) } } - err = h.UpdateAvailableCurrencies(currencies, forceUpgrade) + err = h.UpdateCurrencies(currencies, false, forceUpgrade) if err != nil { log.Printf("%s Failed to update available currencies.\n", h.GetName()) } diff --git a/exchanges/huobi/huobi.go b/exchanges/huobi/huobi.go index f21ffd1d..a9878193 100644 --- a/exchanges/huobi/huobi.go +++ b/exchanges/huobi/huobi.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "log" - "net/http" "net/url" "strconv" "time" @@ -48,14 +47,13 @@ const ( huobiWithdrawCreate = "dw/withdraw/api/create" huobiWithdrawCancel = "dw/withdraw-virtual/%s/cancel" - huobiAuthRate = 0 - huobiUnauthRate = 0 + huobiAuthRate = 100 + huobiUnauthRate = 100 ) // HUOBI is the overarching type across this package type HUOBI struct { exchange.Base - *request.Handler } // SetDefaults sets default values for the exchange @@ -72,8 +70,8 @@ func (h *HUOBI) SetDefaults() { h.ConfigCurrencyPairFormat.Uppercase = true h.AssetTypes = []string{ticker.Spot} h.SupportsAutoPairUpdating = true - h.Handler = new(request.Handler) - h.SetRequestHandler(h.Name, huobiAuthRate, huobiUnauthRate, new(http.Client)) + h.SupportsRESTTickerBatching = false + h.Requester = request.New(h.Name, request.NewRateLimit(time.Second*10, huobiAuthRate), request.NewRateLimit(time.Second*10, huobiUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup sets user configuration @@ -84,6 +82,7 @@ func (h *HUOBI) Setup(exch config.ExchangeConfig) { h.Enabled = true h.AuthenticatedAPISupport = exch.AuthenticatedAPISupport h.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) + h.SetHTTPClientTimeout(exch.HTTPTimeout) h.RESTPollingDelay = exch.RESTPollingDelay h.Verbose = exch.Verbose h.Websocket = exch.Websocket diff --git a/exchanges/huobi/huobi_wrapper.go b/exchanges/huobi/huobi_wrapper.go index f70623fb..37b0c1ad 100644 --- a/exchanges/huobi/huobi_wrapper.go +++ b/exchanges/huobi/huobi_wrapper.go @@ -3,6 +3,7 @@ package huobi import ( "errors" "log" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" @@ -13,8 +14,12 @@ import ( ) // Start starts the HUOBI go routine -func (h *HUOBI) Start() { - go h.Run() +func (h *HUOBI) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + h.Run() + wg.Done() + }() } // Run implements the HUOBI wrapper @@ -65,12 +70,12 @@ func (h *HUOBI) Run() { enabledPairs := []string{"btc-usdt"} log.Println("WARNING: Available and enabled pairs for Huobi reset due to config upgrade, please enable the ones you would like again") - err = h.UpdateEnabledCurrencies(enabledPairs, true) + err = h.UpdateCurrencies(enabledPairs, true, true) if err != nil { log.Printf("%s Failed to update enabled currencies.\n", h.GetName()) } } - err = h.UpdateAvailableCurrencies(currencies, forceUpgrade) + err = h.UpdateCurrencies(currencies, false, forceUpgrade) if err != nil { log.Printf("%s Failed to update available currencies.\n", h.GetName()) } diff --git a/exchanges/itbit/itbit.go b/exchanges/itbit/itbit.go index 85596f18..7854fd45 100644 --- a/exchanges/itbit/itbit.go +++ b/exchanges/itbit/itbit.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "log" - "net/http" "net/url" "strconv" "time" @@ -38,7 +37,6 @@ const ( // ItBit is the overarching type across the ItBit package type ItBit struct { exchange.Base - *request.Handler } // SetDefaults sets the defaults for the exchange @@ -56,8 +54,8 @@ func (i *ItBit) SetDefaults() { i.ConfigCurrencyPairFormat.Uppercase = true i.AssetTypes = []string{ticker.Spot} i.SupportsAutoPairUpdating = false - i.Handler = new(request.Handler) - i.SetRequestHandler(i.Name, itbitAuthRate, itbitUnauthRate, new(http.Client)) + i.SupportsRESTTickerBatching = false + i.Requester = request.New(i.Name, request.NewRateLimit(time.Second, itbitAuthRate), request.NewRateLimit(time.Second, itbitUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup sets the exchange parameters from exchange config @@ -68,6 +66,7 @@ func (i *ItBit) Setup(exch config.ExchangeConfig) { i.Enabled = true i.AuthenticatedAPISupport = exch.AuthenticatedAPISupport i.SetAPIKeys(exch.APIKey, exch.APISecret, exch.ClientID, false) + i.SetHTTPClientTimeout(exch.HTTPTimeout) i.RESTPollingDelay = exch.RESTPollingDelay i.Verbose = exch.Verbose i.Websocket = exch.Websocket diff --git a/exchanges/itbit/itbit_wrapper.go b/exchanges/itbit/itbit_wrapper.go index 6d547e8d..51881788 100644 --- a/exchanges/itbit/itbit_wrapper.go +++ b/exchanges/itbit/itbit_wrapper.go @@ -4,6 +4,7 @@ import ( "errors" "log" "strconv" + "sync" "github.com/thrasher-/gocryptotrader/currency/pair" "github.com/thrasher-/gocryptotrader/exchanges" @@ -12,8 +13,12 @@ import ( ) // Start starts the ItBit go routine -func (i *ItBit) Start() { - go i.Run() +func (i *ItBit) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + i.Run() + wg.Done() + }() } // Run implements the ItBit wrapper diff --git a/exchanges/kraken/kraken.go b/exchanges/kraken/kraken.go index 4bbbd4b3..2e4f1979 100644 --- a/exchanges/kraken/kraken.go +++ b/exchanges/kraken/kraken.go @@ -3,7 +3,6 @@ package kraken import ( "fmt" "log" - "net/http" "net/url" "strconv" "strings" @@ -50,7 +49,6 @@ type Kraken struct { exchange.Base CryptoFee, FiatFee float64 Ticker map[string]Ticker - *request.Handler } // SetDefaults sets current default settings @@ -70,8 +68,8 @@ func (k *Kraken) SetDefaults() { k.ConfigCurrencyPairFormat.Uppercase = true k.AssetTypes = []string{ticker.Spot} k.SupportsAutoPairUpdating = true - k.Handler = new(request.Handler) - k.SetRequestHandler(k.Name, krakenAuthRate, krakenUnauthRate, new(http.Client)) + k.SupportsRESTTickerBatching = true + k.Requester = request.New(k.Name, request.NewRateLimit(time.Second, krakenAuthRate), request.NewRateLimit(time.Second, krakenUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup sets current exchange configuration @@ -82,6 +80,7 @@ func (k *Kraken) Setup(exch config.ExchangeConfig) { k.Enabled = true k.AuthenticatedAPISupport = exch.AuthenticatedAPISupport k.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) + k.SetHTTPClientTimeout(exch.HTTPTimeout) k.RESTPollingDelay = exch.RESTPollingDelay k.Verbose = exch.Verbose k.Websocket = exch.Websocket diff --git a/exchanges/kraken/kraken_wrapper.go b/exchanges/kraken/kraken_wrapper.go index 536fb1db..6763e399 100644 --- a/exchanges/kraken/kraken_wrapper.go +++ b/exchanges/kraken/kraken_wrapper.go @@ -6,6 +6,7 @@ import ( "log" "net/url" "strconv" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -15,8 +16,12 @@ import ( ) // Start starts the Kraken go routine -func (k *Kraken) Start() { - go k.Run() +func (k *Kraken) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + k.Run() + wg.Done() + }() } // Run implements the Kraken wrapper @@ -53,12 +58,12 @@ func (k *Kraken) Run() { enabledPairs := []string{"XBT-USD"} log.Println("WARNING: Available pairs for Kraken reset due to config upgrade, please enable the ones you would like again") - err = k.UpdateEnabledCurrencies(enabledPairs, true) + err = k.UpdateCurrencies(enabledPairs, true, true) if err != nil { log.Printf("%s Failed to get config.\n", k.GetName()) } } - err = k.UpdateAvailableCurrencies(exchangeProducts, forceUpgrade) + err = k.UpdateCurrencies(exchangeProducts, false, forceUpgrade) if err != nil { log.Printf("%s Failed to get config.\n", k.GetName()) } @@ -109,7 +114,7 @@ func (k *Kraken) SetTicker(symbol string) error { resp := Response{} path := fmt.Sprintf("%s/%s/public/%s?%s", krakenAPIURL, krakenAPIVersion, krakenTicker, values.Encode()) - err := common.SendHTTPGetRequest(path, true, k.Verbose, &resp) + err := k.SendHTTPRequest(path, &resp) if err != nil { return err } diff --git a/exchanges/lakebtc/lakebtc.go b/exchanges/lakebtc/lakebtc.go index b18a0719..1f3ea04b 100644 --- a/exchanges/lakebtc/lakebtc.go +++ b/exchanges/lakebtc/lakebtc.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "log" - "net/http" "strconv" "strings" "time" @@ -39,7 +38,6 @@ const ( // LakeBTC is the overarching type across the LakeBTC package type LakeBTC struct { exchange.Base - *request.Handler } // SetDefaults sets LakeBTC defaults @@ -57,8 +55,8 @@ func (l *LakeBTC) SetDefaults() { l.ConfigCurrencyPairFormat.Uppercase = true l.AssetTypes = []string{ticker.Spot} l.SupportsAutoPairUpdating = false - l.Handler = new(request.Handler) - l.SetRequestHandler(l.Name, lakeBTCAuthRate, lakeBTCUnauth, new(http.Client)) + l.SupportsRESTTickerBatching = true + l.Requester = request.New(l.Name, request.NewRateLimit(time.Second, lakeBTCAuthRate), request.NewRateLimit(time.Second, lakeBTCUnauth), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup sets exchange configuration profile @@ -69,6 +67,7 @@ func (l *LakeBTC) Setup(exch config.ExchangeConfig) { l.Enabled = true l.AuthenticatedAPISupport = exch.AuthenticatedAPISupport l.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) + l.SetHTTPClientTimeout(exch.HTTPTimeout) l.RESTPollingDelay = exch.RESTPollingDelay l.Verbose = exch.Verbose l.Websocket = exch.Websocket diff --git a/exchanges/lakebtc/lakebtc_wrapper.go b/exchanges/lakebtc/lakebtc_wrapper.go index cda61565..360337a2 100644 --- a/exchanges/lakebtc/lakebtc_wrapper.go +++ b/exchanges/lakebtc/lakebtc_wrapper.go @@ -4,6 +4,7 @@ import ( "errors" "log" "strconv" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -13,8 +14,12 @@ import ( ) // Start starts the LakeBTC go routine -func (l *LakeBTC) Start() { - go l.Run() +func (l *LakeBTC) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + l.Run() + wg.Done() + }() } // Run implements the LakeBTC wrapper diff --git a/exchanges/liqui/liqui.go b/exchanges/liqui/liqui.go index dc4e3136..eab0d3f1 100644 --- a/exchanges/liqui/liqui.go +++ b/exchanges/liqui/liqui.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "log" - "net/http" "net/url" "strconv" "strings" @@ -43,7 +42,6 @@ type Liqui struct { exchange.Base Ticker map[string]Ticker Info Info - *request.Handler } // SetDefaults sets current default values for liqui @@ -62,8 +60,8 @@ func (l *Liqui) SetDefaults() { l.ConfigCurrencyPairFormat.Uppercase = true l.AssetTypes = []string{ticker.Spot} l.SupportsAutoPairUpdating = true - l.Handler = new(request.Handler) - l.SetRequestHandler(l.Name, liquiAuthRate, liquiUnauthRate, new(http.Client)) + l.SupportsRESTTickerBatching = true + l.Requester = request.New(l.Name, request.NewRateLimit(time.Second, liquiAuthRate), request.NewRateLimit(time.Second, liquiUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup sets exchange configuration parameters for liqui @@ -74,6 +72,7 @@ func (l *Liqui) Setup(exch config.ExchangeConfig) { l.Enabled = true l.AuthenticatedAPISupport = exch.AuthenticatedAPISupport l.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) + l.SetHTTPClientTimeout(exch.HTTPTimeout) l.RESTPollingDelay = exch.RESTPollingDelay l.Verbose = exch.Verbose l.Websocket = exch.Websocket diff --git a/exchanges/liqui/liqui_wrapper.go b/exchanges/liqui/liqui_wrapper.go index 01325504..d5281661 100644 --- a/exchanges/liqui/liqui_wrapper.go +++ b/exchanges/liqui/liqui_wrapper.go @@ -3,6 +3,7 @@ package liqui import ( "errors" "log" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -12,8 +13,12 @@ import ( ) // Start starts the Liqui go routine -func (l *Liqui) Start() { - go l.Run() +func (l *Liqui) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + l.Run() + wg.Done() + }() } // Run implements the Liqui wrapper @@ -29,7 +34,7 @@ func (l *Liqui) Run() { log.Printf("%s Unable to fetch info.\n", l.GetName()) } else { exchangeProducts := l.GetAvailablePairs(true) - err = l.UpdateAvailableCurrencies(exchangeProducts, false) + err = l.UpdateCurrencies(exchangeProducts, false, false) if err != nil { log.Printf("%s Failed to get config.\n", l.GetName()) } diff --git a/exchanges/localbitcoins/localbitcoins.go b/exchanges/localbitcoins/localbitcoins.go index 0e2b7352..c1abca92 100644 --- a/exchanges/localbitcoins/localbitcoins.go +++ b/exchanges/localbitcoins/localbitcoins.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "log" - "net/http" "net/url" "strconv" "time" @@ -109,7 +108,6 @@ var ( // LocalBitcoins is the overarching type across the localbitcoins package type LocalBitcoins struct { exchange.Base - *request.Handler } // SetDefaults sets the package defaults for localbitcoins @@ -125,8 +123,8 @@ func (l *LocalBitcoins) SetDefaults() { l.ConfigCurrencyPairFormat.Delimiter = "" l.ConfigCurrencyPairFormat.Uppercase = true l.SupportsAutoPairUpdating = false - l.Handler = new(request.Handler) - l.SetRequestHandler(l.Name, localbitcoinsAuthRate, localbitcoinsUnauthRate, new(http.Client)) + l.SupportsRESTTickerBatching = true + l.Requester = request.New(l.Name, request.NewRateLimit(time.Second*0, localbitcoinsAuthRate), request.NewRateLimit(time.Second*0, localbitcoinsUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup sets exchange configuration parameters @@ -137,6 +135,7 @@ func (l *LocalBitcoins) Setup(exch config.ExchangeConfig) { l.Enabled = true l.AuthenticatedAPISupport = exch.AuthenticatedAPISupport l.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) + l.SetHTTPClientTimeout(exch.HTTPTimeout) l.RESTPollingDelay = exch.RESTPollingDelay l.Verbose = exch.Verbose l.Websocket = exch.Websocket diff --git a/exchanges/localbitcoins/localbitcoins_wrapper.go b/exchanges/localbitcoins/localbitcoins_wrapper.go index 72d3c859..4ccc3ec1 100644 --- a/exchanges/localbitcoins/localbitcoins_wrapper.go +++ b/exchanges/localbitcoins/localbitcoins_wrapper.go @@ -3,6 +3,7 @@ package localbitcoins import ( "errors" "log" + "sync" "github.com/thrasher-/gocryptotrader/currency/pair" "github.com/thrasher-/gocryptotrader/exchanges" @@ -11,8 +12,12 @@ import ( ) // Start starts the LocalBitcoins go routine -func (l *LocalBitcoins) Start() { - go l.Run() +func (l *LocalBitcoins) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + l.Run() + wg.Done() + }() } // Run implements the LocalBitcoins wrapper diff --git a/exchanges/okcoin/okcoin.go b/exchanges/okcoin/okcoin.go index 378fdeeb..1c2454e8 100644 --- a/exchanges/okcoin/okcoin.go +++ b/exchanges/okcoin/okcoin.go @@ -4,10 +4,10 @@ import ( "errors" "fmt" "log" - "net/http" "net/url" "strconv" "strings" + "time" "github.com/gorilla/websocket" "github.com/thrasher-/gocryptotrader/common" @@ -83,7 +83,6 @@ type OKCoin struct { WebsocketErrors map[string]string FuturesValues []string WebsocketConn *websocket.Conn - *request.Handler } // setCurrencyPairFormats sets currency pair formatting for this package @@ -105,7 +104,7 @@ func (o *OKCoin) SetDefaults() { o.FuturesValues = []string{"this_week", "next_week", "quarter"} o.AssetTypes = []string{ticker.Spot} o.SupportsAutoPairUpdating = false - o.Handler = new(request.Handler) + o.SupportsRESTTickerBatching = false if okcoinDefaultsSet { o.AssetTypes = append(o.AssetTypes, o.FuturesValues...) @@ -113,14 +112,14 @@ func (o *OKCoin) SetDefaults() { o.Name = "OKCOIN International" o.WebsocketURL = okcoinWebsocketURL o.setCurrencyPairFormats() - o.SetRequestHandler(o.Name, okcoinAuthRate, okcoinUnauthRate, new(http.Client)) + o.Requester = request.New(o.Name, request.NewRateLimit(time.Second, okcoinAuthRate), request.NewRateLimit(time.Second, okcoinUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } else { o.APIUrl = okcoinAPIURLChina o.Name = "OKCOIN China" o.WebsocketURL = okcoinWebsocketURLChina okcoinDefaultsSet = true o.setCurrencyPairFormats() - o.SetRequestHandler(o.Name, okcoinAuthRate, okcoinUnauthRate, new(http.Client)) + o.Requester = request.New(o.Name, request.NewRateLimit(time.Second, okcoinAuthRate), request.NewRateLimit(time.Second, okcoinUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } } @@ -132,6 +131,7 @@ func (o *OKCoin) Setup(exch config.ExchangeConfig) { o.Enabled = true o.AuthenticatedAPISupport = exch.AuthenticatedAPISupport o.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) + o.SetHTTPClientTimeout(exch.HTTPTimeout) o.RESTPollingDelay = exch.RESTPollingDelay o.Verbose = exch.Verbose o.Websocket = exch.Websocket diff --git a/exchanges/okcoin/okcoin_wrapper.go b/exchanges/okcoin/okcoin_wrapper.go index cfe10a33..73091291 100644 --- a/exchanges/okcoin/okcoin_wrapper.go +++ b/exchanges/okcoin/okcoin_wrapper.go @@ -3,6 +3,7 @@ package okcoin import ( "errors" "log" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -12,8 +13,12 @@ import ( ) // Start starts the OKCoin go routine -func (o *OKCoin) Start() { - go o.Run() +func (o *OKCoin) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + o.Run() + wg.Done() + }() } // Run implements the OKCoin wrapper diff --git a/exchanges/okex/okex.go b/exchanges/okex/okex.go index f36c372f..42750126 100644 --- a/exchanges/okex/okex.go +++ b/exchanges/okex/okex.go @@ -4,11 +4,11 @@ import ( "errors" "fmt" "log" - "net/http" "net/url" "reflect" "strconv" "strings" + "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" @@ -87,8 +87,6 @@ type OKEX struct { CurrencyPairs []string ContractPosition []string Types []string - - *request.Handler } // SetDefaults method assignes the default values for Bittrex @@ -105,8 +103,8 @@ func (o *OKEX) SetDefaults() { o.ConfigCurrencyPairFormat.Delimiter = "_" o.ConfigCurrencyPairFormat.Uppercase = false o.SupportsAutoPairUpdating = false - o.Handler = new(request.Handler) - o.SetRequestHandler(o.Name, okexAuthRate, okexUnauthRate, new(http.Client)) + o.SupportsRESTTickerBatching = false + o.Requester = request.New(o.Name, request.NewRateLimit(time.Second, okexAuthRate), request.NewRateLimit(time.Second, okexUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup method sets current configuration details if enabled @@ -117,6 +115,7 @@ func (o *OKEX) Setup(exch config.ExchangeConfig) { o.Enabled = true o.AuthenticatedAPISupport = exch.AuthenticatedAPISupport o.SetAPIKeys(exch.APIKey, exch.APISecret, exch.ClientID, false) + o.SetHTTPClientTimeout(exch.HTTPTimeout) o.RESTPollingDelay = exch.RESTPollingDelay o.Verbose = exch.Verbose o.Websocket = exch.Websocket @@ -618,7 +617,7 @@ func (o *OKEX) GetSpotTicker(symbol string) (SpotPrice, error) { values.Set("symbol", symbol) path := fmt.Sprintf("%s%s%s.do?%s", apiURL, apiVersion, "ticker", values.Encode()) - err := common.SendHTTPGetRequest(path, true, o.Verbose, &resp) + err := o.SendHTTPRequest(path, &resp) if err != nil { return resp, err } @@ -640,7 +639,7 @@ func (o *OKEX) GetSpotMarketDepth(symbol, size string) (ActualSpotDepth, error) path := fmt.Sprintf("%s%s%s.do?%s", apiURL, apiVersion, "depth", values.Encode()) - err := common.SendHTTPGetRequest(path, true, o.Verbose, &resp) + err := o.SendHTTPRequest(path, &resp) if err != nil { return fullDepth, err } @@ -697,7 +696,7 @@ func (o *OKEX) GetSpotRecentTrades(symbol, since string) ([]ActualSpotTradeHisto path := fmt.Sprintf("%s%s%s.do?%s", apiURL, apiVersion, "trades", values.Encode()) - err := common.SendHTTPGetRequest(path, true, o.Verbose, &resp) + err := o.SendHTTPRequest(path, &resp) if err != nil { return actualTradeHistory, err } @@ -735,7 +734,7 @@ func (o *OKEX) GetSpotCandleStick(symbol, typeInput string, size, since int) ([] path := fmt.Sprintf("%s%s%s.do?%s", apiURL, apiVersion, "kline", values.Encode()) var resp interface{} - if err := common.SendHTTPGetRequest(path, true, o.Verbose, &resp); err != nil { + if err := o.SendHTTPRequest(path, &resp); err != nil { return candleData, err } diff --git a/exchanges/okex/okex_wrapper.go b/exchanges/okex/okex_wrapper.go index 3ceeb607..609fc499 100644 --- a/exchanges/okex/okex_wrapper.go +++ b/exchanges/okex/okex_wrapper.go @@ -3,6 +3,7 @@ package okex import ( "errors" "log" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -12,8 +13,12 @@ import ( ) // Start starts the OKEX go routine -func (o *OKEX) Start() { - go o.Run() +func (o *OKEX) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + o.Run() + wg.Done() + }() } // Run implements the OKEX wrapper diff --git a/exchanges/orderbook/orderbook.go b/exchanges/orderbook/orderbook.go index 0236bc87..bad3b49d 100644 --- a/exchanges/orderbook/orderbook.go +++ b/exchanges/orderbook/orderbook.go @@ -2,6 +2,7 @@ package orderbook import ( "errors" + "sync" "time" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -19,6 +20,7 @@ const ( // Vars for the orderbook package var ( Orderbooks []Orderbook + m sync.Mutex ) // Item stores the amount and price values @@ -94,6 +96,8 @@ func GetOrderbook(exchange string, p pair.CurrencyPair, orderbookType string) (B // GetOrderbookByExchange returns an exchange orderbook func GetOrderbookByExchange(exchange string) (*Orderbook, error) { + m.Lock() + defer m.Unlock() for _, y := range Orderbooks { if y.ExchangeName == exchange { return &y, nil @@ -105,6 +109,8 @@ func GetOrderbookByExchange(exchange string) (*Orderbook, error) { // FirstCurrencyExists checks to see if the first currency of the orderbook map // exists func FirstCurrencyExists(exchange string, currency pair.CurrencyItem) bool { + m.Lock() + defer m.Unlock() for _, y := range Orderbooks { if y.ExchangeName == exchange { if _, ok := y.Orderbook[currency]; ok { @@ -118,6 +124,8 @@ func FirstCurrencyExists(exchange string, currency pair.CurrencyItem) bool { // SecondCurrencyExists checks to see if the second currency of the orderbook // map exists func SecondCurrencyExists(exchange string, p pair.CurrencyPair) bool { + m.Lock() + defer m.Unlock() for _, y := range Orderbooks { if y.ExchangeName == exchange { if _, ok := y.Orderbook[p.GetFirstCurrency()]; ok { @@ -132,6 +140,8 @@ func SecondCurrencyExists(exchange string, p pair.CurrencyPair) bool { // CreateNewOrderbook creates a new orderbook func CreateNewOrderbook(exchangeName string, p pair.CurrencyPair, orderbookNew Base, orderbookType string) Orderbook { + m.Lock() + defer m.Unlock() orderbook := Orderbook{} orderbook.ExchangeName = exchangeName orderbook.Orderbook = make(map[pair.CurrencyItem]map[pair.CurrencyItem]map[string]Base) @@ -163,18 +173,22 @@ func ProcessOrderbook(exchangeName string, p pair.CurrencyPair, orderbookNew Bas if FirstCurrencyExists(exchangeName, p.GetFirstCurrency()) { if !SecondCurrencyExists(exchangeName, p) { + m.Lock() a := orderbook.Orderbook[p.FirstCurrency] b := make(map[string]Base) b[orderbookType] = orderbookNew a[p.SecondCurrency] = b orderbook.Orderbook[p.FirstCurrency] = a + m.Unlock() return } } + m.Lock() a := make(map[pair.CurrencyItem]map[string]Base) b := make(map[string]Base) b[orderbookType] = orderbookNew a[p.SecondCurrency] = b orderbook.Orderbook[p.FirstCurrency] = a + m.Unlock() } diff --git a/exchanges/poloniex/poloniex.go b/exchanges/poloniex/poloniex.go index 5d9c1d73..952d8650 100644 --- a/exchanges/poloniex/poloniex.go +++ b/exchanges/poloniex/poloniex.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "log" - "net/http" "net/url" "strconv" "time" @@ -49,14 +48,13 @@ const ( poloniexLendingHistory = "returnLendingHistory" poloniexAutoRenew = "toggleAutoRenew" - poloniexAuthRate = 0 - poloniexUnauthRate = 0 + poloniexAuthRate = 6 + poloniexUnauthRate = 6 ) // Poloniex is the overarching type across the poloniex package type Poloniex struct { exchange.Base - *request.Handler } // SetDefaults sets default settings for poloniex @@ -73,8 +71,8 @@ func (p *Poloniex) SetDefaults() { p.ConfigCurrencyPairFormat.Uppercase = true p.AssetTypes = []string{ticker.Spot} p.SupportsAutoPairUpdating = true - p.Handler = new(request.Handler) - p.SetRequestHandler(p.Name, poloniexAuthRate, poloniexUnauthRate, new(http.Client)) + p.SupportsRESTTickerBatching = true + p.Requester = request.New(p.Name, request.NewRateLimit(time.Second, poloniexAuthRate), request.NewRateLimit(time.Second, poloniexUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup sets user exchange configuration settings @@ -85,6 +83,7 @@ func (p *Poloniex) Setup(exch config.ExchangeConfig) { p.Enabled = true p.AuthenticatedAPISupport = exch.AuthenticatedAPISupport p.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) + p.SetHTTPClientTimeout(exch.HTTPTimeout) p.RESTPollingDelay = exch.RESTPollingDelay p.Verbose = exch.Verbose p.Websocket = exch.Websocket diff --git a/exchanges/poloniex/poloniex_wrapper.go b/exchanges/poloniex/poloniex_wrapper.go index dc0b6eb1..2817da6e 100644 --- a/exchanges/poloniex/poloniex_wrapper.go +++ b/exchanges/poloniex/poloniex_wrapper.go @@ -3,6 +3,7 @@ package poloniex import ( "errors" "log" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -12,8 +13,12 @@ import ( ) // Start starts the Poloniex go routine -func (po *Poloniex) Start() { - go po.Run() +func (po *Poloniex) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + po.Run() + wg.Done() + }() } // Run implements the Poloniex wrapper @@ -38,7 +43,7 @@ func (po *Poloniex) Run() { po.GetName()) forceUpdate = true } - err = po.UpdateAvailableCurrencies(exchangeCurrencies, forceUpdate) + err = po.UpdateCurrencies(exchangeCurrencies, false, forceUpdate) if err != nil { log.Printf("%s Failed to update available currencies %s.\n", po.GetName(), err) } diff --git a/exchanges/request/request.go b/exchanges/request/request.go index fb8b88e1..85f3169b 100644 --- a/exchanges/request/request.go +++ b/exchanges/request/request.go @@ -2,259 +2,306 @@ package request import ( "errors" + "fmt" "io" "io/ioutil" "log" "net/http" - "strings" "sync" "time" "github.com/thrasher-/gocryptotrader/common" ) -const ( - maxJobQueue = 100 - maxHandles = 27 -) +var supportedMethods = []string{"GET", "POST", "HEAD", "PUT", "DELETE", "OPTIONS", "CONNECT"} -var request service - -type service struct { - exchangeHandlers []*Handler +// Requester struct for the request client +type Requester struct { + HTTPClient *http.Client + UnauthLimit RateLimit + AuthLimit RateLimit + Name string + Cycle time.Time + m sync.Mutex } -// checkHandles checks to see if there is a handle monitored by the service -func (s *service) checkHandles(exchName string, h *Handler) bool { - for _, handle := range s.exchangeHandlers { - if exchName == handle.exchName || handle == h { +// RateLimit struct +type RateLimit struct { + Duration time.Duration + Rate int + Requests int + Mutex sync.Mutex +} + +// NewRateLimit creates a new RateLimit +func NewRateLimit(d time.Duration, rate int) RateLimit { + return RateLimit{Duration: d, Rate: rate} +} + +// ToString returns the rate limiter in string notation +func (r *RateLimit) ToString() string { + return fmt.Sprintf("Rate limiter set to %d requests per %v", r.Rate, r.Duration) +} + +// GetRate returns the ratelimit rate +func (r *RateLimit) GetRate() int { + r.Mutex.Lock() + defer r.Mutex.Unlock() + return r.Rate +} + +// SetRate sets the ratelimit rate +func (r *RateLimit) SetRate(rate int) { + r.Mutex.Lock() + defer r.Mutex.Unlock() + r.Rate = rate +} + +// GetRequests returns the number of requests for the ratelimit +func (r *RateLimit) GetRequests() int { + r.Mutex.Lock() + defer r.Mutex.Unlock() + return r.Requests +} + +// SetRequests sets requests counter for the rateliit +func (r *RateLimit) SetRequests(l int) { + r.Mutex.Lock() + defer r.Mutex.Unlock() + r.Requests = l +} + +// SetDuration sets the duration for the ratelimit +func (r *RateLimit) SetDuration(d time.Duration) { + r.Mutex.Lock() + defer r.Mutex.Unlock() + r.Duration = d +} + +// GetDuration gets the duration for the ratelimit +func (r *RateLimit) GetDuration() time.Duration { + r.Mutex.Lock() + defer r.Mutex.Unlock() + return r.Duration +} + +// StartCycle restarts the cycle time and requests counters +func (r *Requester) StartCycle() { + r.Cycle = time.Now() + r.AuthLimit.SetRequests(0) + r.UnauthLimit.SetRequests(0) +} + +// IsRateLimited returns whether or not the request Requester is rate limited +func (r *Requester) IsRateLimited(auth bool) bool { + if auth { + if r.AuthLimit.GetRequests() >= r.AuthLimit.GetRate() && r.IsValidCycle(auth) { + return true + } + } else { + if r.UnauthLimit.GetRequests() >= r.UnauthLimit.GetRate() && r.IsValidCycle(auth) { return true } } return false } -// removeHandle releases handle from service -func (s *service) removeHandle(exchName string) bool { - for i, handle := range s.exchangeHandlers { - if exchName == handle.exchName { - handle.shutdown = true - handle.wg.Wait() - new := append(s.exchangeHandlers[:i-1], s.exchangeHandlers[i+1:]...) - s.exchangeHandlers = new +// RequiresRateLimiter returns whether or not the request Requester requires a rate limiter +func (r *Requester) RequiresRateLimiter() bool { + if r.AuthLimit.GetRate() != 0 || r.UnauthLimit.GetRate() != 0 { + return true + } + return false +} + +// IncrementRequests increments the ratelimiter request counter for either auth or unauth +// requests +func (r *Requester) IncrementRequests(auth bool) { + if auth { + reqs := r.AuthLimit.GetRequests() + reqs++ + r.AuthLimit.SetRequests(reqs) + return + } + + reqs := r.AuthLimit.GetRequests() + reqs++ + r.UnauthLimit.SetRequests(reqs) +} + +// DecrementRequests decrements the ratelimiter request counter for either auth or unauth +// requests +func (r *Requester) DecrementRequests(auth bool) { + if auth { + reqs := r.AuthLimit.GetRequests() + reqs-- + r.AuthLimit.SetRequests(reqs) + return + } + + reqs := r.AuthLimit.GetRequests() + reqs-- + r.UnauthLimit.SetRequests(reqs) +} + +// SetRateLimit sets the request Requester ratelimiter +func (r *Requester) SetRateLimit(auth bool, duration time.Duration, rate int) { + if auth { + r.AuthLimit.SetRate(rate) + r.AuthLimit.SetDuration(duration) + return + } + r.UnauthLimit.SetRate(rate) + r.UnauthLimit.SetDuration(duration) +} + +// GetRateLimit gets the request Requester ratelimiter +func (r *Requester) GetRateLimit(auth bool) RateLimit { + if auth { + return r.AuthLimit + } + return r.UnauthLimit +} + +// New returns a new Requester +func New(name string, authLimit, unauthLimit RateLimit, httpRequester *http.Client) *Requester { + r := &Requester{HTTPClient: httpRequester, UnauthLimit: unauthLimit, AuthLimit: authLimit, Name: name} + return r +} + +// IsValidMethod returns whether the supplied method is supported +func IsValidMethod(method string) bool { + return common.StringDataCompareUpper(supportedMethods, method) +} + +// IsValidCycle checks to see whether the current request cycle is valid or not +func (r *Requester) IsValidCycle(auth bool) bool { + if auth { + if time.Since(r.Cycle) < r.AuthLimit.GetDuration() { + return true + } + } else { + if time.Since(r.Cycle) < r.UnauthLimit.GetDuration() { return true } } return false } -// limit contains the limit rate value which has a Mutex -type limit struct { - Val time.Duration - sync.Mutex -} - -// getLimitRate returns limit rate with a protected call -func (l *limit) getLimitRate() time.Duration { - l.Lock() - defer l.Unlock() - return l.Val -} - -// setLimitRates sets initial limit rates with a protected call -func (l *limit) setLimitRate(rate int) { - l.Lock() - l.Val = time.Duration(rate) * time.Millisecond - l.Unlock() -} - -// Handler is a generic exchange specific request handler. -type Handler struct { - exchName string - Client *http.Client - shutdown bool - LimitAuth *limit - LimitUnauth *limit - requests chan *exchRequest - responses chan *exchResponse - timeLockAuth chan int - timeLock chan int - wg sync.WaitGroup -} - -// SetRequestHandler sets initial variables for the request handler and returns -// an error -func (h *Handler) SetRequestHandler(exchName string, authRate, unauthRate int, client *http.Client) error { - if request.checkHandles(exchName, h) { - return errors.New("handler already registered for an exchange") - } - - h.exchName = exchName - h.Client = client - h.shutdown = false - h.LimitAuth = new(limit) - h.LimitAuth.setLimitRate(authRate) - h.LimitUnauth = new(limit) - h.LimitUnauth.setLimitRate(unauthRate) - h.requests = make(chan *exchRequest, maxJobQueue) - h.responses = make(chan *exchResponse, 1) - h.timeLockAuth = make(chan int, 1) - h.timeLock = make(chan int, 1) - - request.exchangeHandlers = append(request.exchangeHandlers, h) - h.startWorkers() - - return nil -} - -// SetRateLimit sets limit rates for exchange requests -func (h *Handler) SetRateLimit(authRate, unauthRate int) { - h.LimitAuth.setLimitRate(authRate) - h.LimitUnauth.setLimitRate(unauthRate) -} - -// SendPayload packages a request, sends it to a channel, then a worker executes it -func (h *Handler) SendPayload(method, path string, headers map[string]string, body io.Reader, result interface{}, authRequest, verbose bool) error { - if h.exchName == "" { - return errors.New("request handler not initialised") - } - - method = strings.ToUpper(method) - - if method != "POST" && method != "GET" && method != "DELETE" { - return errors.New("incorrect method - either POST, GET or DELETE") - } - - if verbose { - log.Printf("%s exchange request path: %s", h.exchName, path) - } - +func (r *Requester) checkRequest(method, path string, body io.Reader, headers map[string]string) (*http.Request, error) { req, err := http.NewRequest(method, path, body) if err != nil { - return err + return nil, err } for k, v := range headers { req.Header.Add(k, v) } - err = h.attachJob(req, path, authRequest) - if err != nil { - return err - } - - contents, err := h.getResponse() - if err != nil { - return err - } + return req, nil +} +// DoRequest performs a HTTP/HTTPS request with the supplied params +func (r *Requester) DoRequest(req *http.Request, method, path string, headers map[string]string, body io.Reader, result interface{}, authRequest, verbose bool) error { if verbose { - log.Printf("%s exchange raw response: %s", h.exchName, string(contents[:])) + log.Printf("%s exchange request path: %s", r.Name, path) } - return common.JSONDecode(contents, result) -} + var resp *http.Response + var err error -func (h *Handler) startWorkers() { - h.wg.Add(3) - go h.requestWorker() - - // routine to monitor Autheticated limit rates - go func() { - h.timeLockAuth <- 1 - for !h.shutdown { - <-h.timeLockAuth - time.Sleep(h.LimitAuth.getLimitRate()) - h.timeLockAuth <- 1 - } - h.wg.Done() - }() - // routine to monitor Unauthenticated limit rates - go func() { - h.timeLock <- 1 - for !h.shutdown { - <-h.timeLock - time.Sleep(h.LimitUnauth.getLimitRate()) - h.timeLock <- 1 - } - h.wg.Done() - }() -} - -// requestWorker handles the request queue -func (h *Handler) requestWorker() { - for job := range h.requests { - if h.shutdown { - break - } - - var httpResponse *http.Response - var err error - - if job.Auth { - <-h.timeLockAuth - if job.Request.Method != "GET" { - httpResponse, err = h.Client.Do(job.Request) - } else { - httpResponse, err = h.Client.Get(job.Path) - } - h.timeLockAuth <- 1 - } else { - <-h.timeLock - if job.Request.Method != "GET" { - httpResponse, err = h.Client.Do(job.Request) - } else { - httpResponse, err = h.Client.Get(job.Path) - } - h.timeLock <- 1 - } - - for b := false; !b; { - select { - case h.responses <- &exchResponse{Response: httpResponse, ResError: err}: - b = true - default: - continue - } - } - } - h.wg.Done() -} - -// exchRequest is the request type -type exchRequest struct { - Request *http.Request - Path string - Auth bool -} - -// attachJob sends a request using the http package to the request channel -func (h *Handler) attachJob(req *http.Request, path string, isAuth bool) error { - select { - case h.requests <- &exchRequest{Request: req, Path: path, Auth: isAuth}: - return nil - default: - return errors.New("job queue exceeded") - } -} - -// exchResponse is the main response type for requests -type exchResponse struct { - Response *http.Response - ResError error -} - -// getResponse monitors the current resp channel and returns the contents -func (h *Handler) getResponse() ([]byte, error) { - resp := <-h.responses - if resp.ResError != nil { - return []byte(""), resp.ResError + if method != "GET" { + resp, err = r.HTTPClient.Do(req) + } else { + resp, err = r.HTTPClient.Get(path) } - defer resp.Response.Body.Close() - contents, err := ioutil.ReadAll(resp.Response.Body) if err != nil { - return []byte(""), err + if r.RequiresRateLimiter() { + r.DecrementRequests(authRequest) + } + return err } - return contents, nil + + if resp == nil { + if r.RequiresRateLimiter() { + r.DecrementRequests(authRequest) + } + return errors.New("resp is nil") + } + + contents, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + + resp.Body.Close() + if verbose { + log.Printf("%s exchange raw response: %s", r.Name, string(contents[:])) + } + + if result != nil { + return common.JSONDecode(contents, result) + } + + return nil +} + +// SendPayload handles sending HTTP/HTTPS requests +func (r *Requester) SendPayload(method, path string, headers map[string]string, body io.Reader, result interface{}, authRequest, verbose bool) error { + if r == nil || r.Name == "" { + return errors.New("not initiliased, SetDefaults() called before making request?") + } + + if !IsValidMethod(method) { + return fmt.Errorf("incorrect method supplied %s: supported %s", method, supportedMethods) + } + + if path == "" { + return errors.New("invalid path") + } + + var req *http.Request + var err error + + if method != "GET" { + req, err = r.checkRequest(method, path, body, headers) + if err != nil { + return err + } + } + + if !r.RequiresRateLimiter() { + return r.DoRequest(req, method, path, headers, body, result, authRequest, verbose) + } + + r.m.Lock() + if r.Cycle.IsZero() || !r.IsValidCycle(authRequest) { + r.StartCycle() + } + r.m.Unlock() + + if !r.IsRateLimited(authRequest) && r.IsValidCycle(authRequest) { + r.IncrementRequests(authRequest) + return r.DoRequest(req, method, path, headers, body, result, authRequest, verbose) + } + + r.m.Lock() + for r.IsRateLimited(authRequest) { + limit := r.GetRateLimit(authRequest) + diff := limit.GetDuration() - time.Since(r.Cycle) + log.Printf("%s IS RATE LIMITED. SLEEPING FOR %v", r.Name, diff) + time.Sleep(diff) + + if !r.IsValidCycle(authRequest) { + r.StartCycle() + } + + if !r.IsRateLimited(authRequest) && r.IsValidCycle(authRequest) { + r.IncrementRequests(authRequest) + r.m.Unlock() + return r.DoRequest(req, method, path, headers, body, result, authRequest, verbose) + } + } + return nil } diff --git a/exchanges/request/request_test.go b/exchanges/request/request_test.go index 33f16878..c6219368 100644 --- a/exchanges/request/request_test.go +++ b/exchanges/request/request_test.go @@ -2,83 +2,286 @@ package request import ( "net/http" - "sync" "testing" + "time" ) -var ( - wg sync.WaitGroup - bitfinex *Handler - BTCMarkets *Handler -) +func TestNewRateLimit(t *testing.T) { + r := NewRateLimit(time.Second*10, 5) -func TestSetRequestHandler(t *testing.T) { - bitfinex = new(Handler) - err := bitfinex.SetRequestHandler("bitfinex", 1000, 1000, new(http.Client)) + if r.Duration != time.Second*10 && r.Rate != 5 { + t.Fatal("unexpected values") + } +} + +func TestSetRate(t *testing.T) { + r := NewRateLimit(time.Second*10, 5) + + r.SetRate(40) + if r.GetRate() != 40 { + t.Fatal("unexpected values") + } +} + +func TestSetDuration(t *testing.T) { + r := NewRateLimit(time.Second*10, 5) + + r.SetDuration(time.Second) + if r.GetDuration() != time.Second { + t.Fatal("unexpected values") + } +} + +func TestDecerementRequests(t *testing.T) { + r := New("bitfinex", NewRateLimit(time.Second*10, 5), NewRateLimit(time.Second*20, 100), new(http.Client)) + + r.AuthLimit.SetRequests(99) + r.DecrementRequests(true) + + if r.AuthLimit.GetRequests() != 98 { + t.Fatal("unexpected values") + } +} +func TestStartCycle(t *testing.T) { + r := New("bitfinex", NewRateLimit(time.Second*10, 5), NewRateLimit(time.Second*20, 100), new(http.Client)) + + if r.AuthLimit.Duration != time.Second*10 && r.AuthLimit.Rate != 5 { + t.Fatal("unexpected values") + } + + if r.UnauthLimit.Duration != time.Second*20 && r.UnauthLimit.Rate != 100 { + t.Fatal("unexpected values") + } + + r.AuthLimit.SetRequests(1) + r.UnauthLimit.SetRequests(1) + r.StartCycle() + if r.Cycle.IsZero() || r.AuthLimit.GetRequests() != 0 || r.UnauthLimit.GetRequests() != 0 { + t.Fatal("unexpcted values") + } +} + +func TestIsRateLimited(t *testing.T) { + r := New("bitfinex", NewRateLimit(time.Second*10, 5), NewRateLimit(time.Second*20, 100), new(http.Client)) + r.StartCycle() + + if r.AuthLimit.ToString() != "Rate limiter set to 5 requests per 10s" { + t.Fatal("unexcpted values") + } + + if r.UnauthLimit.ToString() != "Rate limiter set to 100 requests per 20s" { + t.Fatal("unexpected values") + } + + if r.AuthLimit.ToString() != "Rate limiter set to 5 requests per 10s" { + t.Fatal("unexcpted values") + } + + // FIXME: Need to account for unauth/auth/total requests + r.AuthLimit.SetRequests(4) + if r.AuthLimit.GetRequests() != 4 { + t.Fatal("unexpected values") + } + + // test that we're not rate limited since 4 < 5 + if r.IsRateLimited(true) { + t.Fatal("unexpected values") + } + + // bump requests counter to 6 which would exceed the rate limiter + r.AuthLimit.SetRequests(6) + if !r.IsRateLimited(true) { + t.Fatal("unexpected values") + } + + // FIXME: Need to account for unauth/auth/total requests + r.UnauthLimit.SetRequests(99) + if r.UnauthLimit.GetRequests() != 99 { + t.Fatal("unexpected values") + } + + // test that we're not rate limited since 99 < 100 + if r.IsRateLimited(false) { + t.Fatal("unexpected values") + } + + // bump requests counter to 100 which would exceed the rate limiter + r.UnauthLimit.SetRequests(100) + if !r.IsRateLimited(false) { + t.Fatal("unexpected values") + } +} + +func TestRequiresRateLimiter(t *testing.T) { + r := New("bitfinex", NewRateLimit(time.Second*10, 5), NewRateLimit(time.Second*20, 100), new(http.Client)) + if !r.RequiresRateLimiter() { + t.Fatal("unexpected values") + } + + r.AuthLimit.Rate = 0 + r.UnauthLimit.Rate = 0 + + if r.RequiresRateLimiter() { + t.Fatal("unexpected values") + } +} + +func TestSetLimit(t *testing.T) { + r := New("bitfinex", NewRateLimit(time.Second*10, 5), NewRateLimit(time.Second*20, 100), new(http.Client)) + + r.SetRateLimit(true, time.Minute, 20) + if r.AuthLimit.Rate != 20 && r.AuthLimit.Duration != time.Minute*20 { + t.Fatal("unexpected values") + } + + r.SetRateLimit(false, time.Minute, 40) + if r.UnauthLimit.Rate != 40 && r.UnauthLimit.Duration != time.Minute { + t.Fatal("unexpected values") + } +} + +func TestGetLimit(t *testing.T) { + r := New("bitfinex", NewRateLimit(time.Second*10, 5), NewRateLimit(time.Second*20, 100), new(http.Client)) + + if r.GetRateLimit(true).Duration != time.Second*10 && r.GetRateLimit(true).Rate != 5 { + t.Fatal("unexpected values") + } + + if r.GetRateLimit(false).Duration != time.Second*10 && r.GetRateLimit(false).Rate != 100 { + t.Fatal("unexpected values") + } +} + +func TestIsValidMethod(t *testing.T) { + for x := range supportedMethods { + if !IsValidMethod(supportedMethods[x]) { + t.Fatal("unexpected values") + } + } + + if IsValidMethod("BLAH") { + t.Fatal("unexpected values") + } +} + +func TestIsValidCycle(t *testing.T) { + r := New("bitfinex", NewRateLimit(time.Second*10, 5), NewRateLimit(time.Second*20, 100), new(http.Client)) + r.Cycle = time.Now().Add(-9 * time.Second) + + if !r.IsValidCycle(true) { + t.Fatal("unexpected values") + } + + r.Cycle = time.Now().Add(-11 * time.Second) + if r.IsValidCycle(true) { + t.Fatal("unexpected values") + } + + r.Cycle = time.Now().Add(-19 * time.Second) + + if !r.IsValidCycle(false) { + t.Fatal("unexpected values") + } + + r.Cycle = time.Now().Add(-21 * time.Second) + if r.IsValidCycle(false) { + t.Fatal("unexpected values") + } +} + +func TestCheckRequest(t *testing.T) { + r := New("", NewRateLimit(time.Second*10, 5), NewRateLimit(time.Second*20, 100), new(http.Client)) + _, err := r.checkRequest("bad method, bad", "http://www.google.com", nil, nil) + if err == nil { + t.Fatal("unexpected values") + } +} + +func TestDoRequest(t *testing.T) { + var test *Requester + err := test.SendPayload("GET", "https://www.google.com", nil, nil, nil, false, true) + if err == nil { + t.Fatal("not iniitalised") + } + + r := New("", NewRateLimit(time.Second*10, 5), NewRateLimit(time.Second*20, 100), new(http.Client)) + if err == nil { + t.Fatal("unexpected values") + } + + r.Name = "bitfinex" + err = r.SendPayload("BLAH", "https://www.google.com", nil, nil, nil, false, true) + if err == nil { + t.Fatal("unexpected values") + } + + err = r.SendPayload("GET", "", nil, nil, nil, false, true) + if err == nil { + t.Fatal("unexpected values") + } + + err = r.SendPayload("GET", "https://www.google.com", nil, nil, nil, false, true) if err != nil { - t.Error("Test failed - request SetRequestHandler()", err) + t.Fatal("unexpected values") } - err = bitfinex.SetRequestHandler("bitfinex", 1000, 1000, new(http.Client)) - if err == nil { - t.Error("Test failed - request SetRequestHandler()", err) - } - err = bitfinex.SetRequestHandler("bla", 1000, 1000, new(http.Client)) - if err == nil { - t.Error("Test failed - request SetRequestHandler()", err) - } - BTCMarkets = new(Handler) - BTCMarkets.SetRequestHandler("btcmarkets", 1000, 1000, new(http.Client)) - if len(request.exchangeHandlers) != 2 { - t.Error("test failed - request GetRequestHandler() error") + if !r.RequiresRateLimiter() { + t.Fatal("unexpcted values") } - wg.Add(2) -} -func TestSetRateLimit(t *testing.T) { - bitfinex.SetRateLimit(0, 0) - BTCMarkets.SetRateLimit(0, 0) -} + r.SetRateLimit(false, time.Second, 0) + r.SetRateLimit(true, time.Second, 0) -func TestSend(t *testing.T) { - for i := 0; i < 1; i++ { - go func() { - var v interface{} - err := bitfinex.SendPayload("GET", - "https://api.bitfinex.com/v1/pubticker/BTCUSD", - nil, - nil, - &v, - false, - false, - ) - if err != nil { - t.Error("test failed - send error", err) - } - wg.Done() - }() - go func() { - var v interface{} - err := BTCMarkets.SendPayload("GET", - "https://api.btcmarkets.net/market/BTC/AUD/tick", - nil, - nil, - &v, - false, - false, - ) - if err != nil { - t.Error("test failed - send error", err) - } - wg.Done() - }() + err = r.SendPayload("GET", "https://www.google.com", nil, nil, nil, false, true) + if err != nil { + t.Fatal("unexpected values") } - wg.Wait() - newHandler := new(Handler) - err := newHandler.SendPayload("GET", "https://api.bitfinex.com/v1/pubticker/BTCUSD", - nil, nil, nil, false, false) - if err == nil { - t.Error("test failed - request Send() error", err) + if r.RequiresRateLimiter() { + t.Fatal("unexpected values") + } + + r.SetRateLimit(false, time.Millisecond*200, 100) + r.SetRateLimit(true, time.Millisecond*100, 100) + r.Cycle = time.Now().Add(time.Millisecond * -201) + + if r.IsValidCycle(false) { + t.Fatal("unexepcted values") + } + + err = r.SendPayload("GET", "https://www.google.com", nil, nil, nil, false, true) + if err != nil { + t.Fatal("unexpected values") + } + + r.Cycle = time.Now().Add(time.Millisecond * -101) + + if r.IsValidCycle(true) { + t.Fatal("unexepcted values") + } + + err = r.SendPayload("GET", "https://www.google.com", nil, nil, nil, true, true) + if err != nil { + t.Fatal("unexpected values") + } + + var result interface{} + err = r.SendPayload("GET", "https://www.google.com", nil, nil, result, false, true) + if err != nil { + t.Fatal(err) + } + + headers := make(map[string]string) + headers["content-type"] = "content/text" + err = r.SendPayload("POST", "https://api.bitfinex.com", headers, nil, result, false, true) + if err != nil { + t.Fatal(err) + } + + r.StartCycle() + r.UnauthLimit.SetRequests(100) + err = r.SendPayload("GET", "https://www.google.com", nil, nil, result, false, false) + if err != nil { + t.Fatal("unexpected values") } } diff --git a/exchanges/ticker/ticker.go b/exchanges/ticker/ticker.go index 3d2c23c7..3d5e012d 100644 --- a/exchanges/ticker/ticker.go +++ b/exchanges/ticker/ticker.go @@ -3,6 +3,7 @@ package ticker import ( "errors" "strconv" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -20,6 +21,7 @@ const ( // Vars for the ticker package var ( Tickers []Ticker + m sync.Mutex ) // Price struct stores the currency pair and pricing information @@ -85,6 +87,8 @@ func GetTicker(exchange string, p pair.CurrencyPair, tickerType string) (Price, // GetTickerByExchange returns an exchange Ticker func GetTickerByExchange(exchange string) (*Ticker, error) { + m.Lock() + defer m.Unlock() for _, y := range Tickers { if y.ExchangeName == exchange { return &y, nil @@ -96,6 +100,8 @@ func GetTickerByExchange(exchange string) (*Ticker, error) { // FirstCurrencyExists checks to see if the first currency of the Price map // exists func FirstCurrencyExists(exchange string, currency pair.CurrencyItem) bool { + m.Lock() + defer m.Unlock() for _, y := range Tickers { if y.ExchangeName == exchange { if _, ok := y.Price[currency]; ok { @@ -109,6 +115,8 @@ func FirstCurrencyExists(exchange string, currency pair.CurrencyItem) bool { // SecondCurrencyExists checks to see if the second currency of the Price map // exists func SecondCurrencyExists(exchange string, p pair.CurrencyPair) bool { + m.Lock() + defer m.Unlock() for _, y := range Tickers { if y.ExchangeName == exchange { if _, ok := y.Price[p.GetFirstCurrency()]; ok { @@ -123,6 +131,8 @@ func SecondCurrencyExists(exchange string, p pair.CurrencyPair) bool { // CreateNewTicker creates a new Ticker func CreateNewTicker(exchangeName string, p pair.CurrencyPair, tickerNew Price, tickerType string) Ticker { + m.Lock() + defer m.Unlock() ticker := Ticker{} ticker.ExchangeName = exchangeName ticker.Price = make(map[pair.CurrencyItem]map[pair.CurrencyItem]map[string]Price) @@ -152,18 +162,22 @@ func ProcessTicker(exchangeName string, p pair.CurrencyPair, tickerNew Price, ti if FirstCurrencyExists(exchangeName, p.FirstCurrency) { if !SecondCurrencyExists(exchangeName, p) { + m.Lock() a := ticker.Price[p.FirstCurrency] b := make(map[string]Price) b[tickerType] = tickerNew a[p.SecondCurrency] = b ticker.Price[p.FirstCurrency] = a + m.Unlock() return } } + m.Lock() a := make(map[pair.CurrencyItem]map[string]Price) b := make(map[string]Price) b[tickerType] = tickerNew a[p.SecondCurrency] = b ticker.Price[p.FirstCurrency] = a + m.Unlock() } diff --git a/exchanges/wex/wex.go b/exchanges/wex/wex.go index 6a8f1551..407a001c 100644 --- a/exchanges/wex/wex.go +++ b/exchanges/wex/wex.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "log" - "net/http" "net/url" "strconv" "strings" @@ -46,7 +45,6 @@ const ( type WEX struct { exchange.Base Ticker map[string]Ticker - *request.Handler } // SetDefaults sets current default value for WEX @@ -65,8 +63,8 @@ func (w *WEX) SetDefaults() { w.ConfigCurrencyPairFormat.Uppercase = true w.AssetTypes = []string{ticker.Spot} w.SupportsAutoPairUpdating = false - w.Handler = new(request.Handler) - w.SetRequestHandler(w.Name, wexAuthRate, wexUnauthRate, new(http.Client)) + w.SupportsRESTTickerBatching = true + w.Requester = request.New(w.Name, request.NewRateLimit(time.Second, wexAuthRate), request.NewRateLimit(time.Second, wexUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup sets exchange configuration parameters for WEX @@ -77,6 +75,7 @@ func (w *WEX) Setup(exch config.ExchangeConfig) { w.Enabled = true w.AuthenticatedAPISupport = exch.AuthenticatedAPISupport w.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) + w.SetHTTPClientTimeout(exch.HTTPTimeout) w.RESTPollingDelay = exch.RESTPollingDelay w.Verbose = exch.Verbose w.Websocket = exch.Websocket diff --git a/exchanges/wex/wex_wrapper.go b/exchanges/wex/wex_wrapper.go index 05b27c28..e4e37a25 100644 --- a/exchanges/wex/wex_wrapper.go +++ b/exchanges/wex/wex_wrapper.go @@ -3,6 +3,7 @@ package wex import ( "errors" "log" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -12,8 +13,12 @@ import ( ) // Start starts the WEX go routine -func (w *WEX) Start() { - go w.Run() +func (w *WEX) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + w.Run() + wg.Done() + }() } // Run implements the WEX wrapper diff --git a/exchanges/yobit/yobit.go b/exchanges/yobit/yobit.go index 5c4be7c8..af797031 100644 --- a/exchanges/yobit/yobit.go +++ b/exchanges/yobit/yobit.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "log" - "net/http" "net/url" "strconv" "strings" @@ -44,7 +43,6 @@ const ( type Yobit struct { exchange.Base Ticker map[string]Ticker - *request.Handler } // SetDefaults sets current default value for Yobit @@ -65,8 +63,8 @@ func (y *Yobit) SetDefaults() { y.ConfigCurrencyPairFormat.Uppercase = true y.AssetTypes = []string{ticker.Spot} y.SupportsAutoPairUpdating = false - y.Handler = new(request.Handler) - y.SetRequestHandler(y.Name, yobitAuthRate, yobitUnauthRate, new(http.Client)) + y.SupportsRESTTickerBatching = true + y.Requester = request.New(y.Name, request.NewRateLimit(time.Second, yobitAuthRate), request.NewRateLimit(time.Second, yobitUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) } // Setup sets exchange configuration parameters for Yobit @@ -83,6 +81,7 @@ func (y *Yobit) Setup(exch config.ExchangeConfig) { y.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",") y.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",") y.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",") + y.SetHTTPClientTimeout(exch.HTTPTimeout) err := y.SetCurrencyPairFormat() if err != nil { log.Fatal(err) diff --git a/exchanges/yobit/yobit_wrapper.go b/exchanges/yobit/yobit_wrapper.go index 3bb6fa63..714555d6 100644 --- a/exchanges/yobit/yobit_wrapper.go +++ b/exchanges/yobit/yobit_wrapper.go @@ -3,6 +3,7 @@ package yobit import ( "errors" "log" + "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" @@ -12,8 +13,12 @@ import ( ) // Start starts the WEX go routine -func (y *Yobit) Start() { - go y.Run() +func (y *Yobit) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + y.Run() + wg.Done() + }() } // Run implements the Yobit wrapper diff --git a/helpers_test.go b/helpers_test.go index d331ecef..a45bee61 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -229,7 +229,7 @@ func TestGetExchangeNamesByCurrency(t *testing.T) { func TestGetSpecificOrderbook(t *testing.T) { SetupTestHelpers(t) - LoadExchange("Bitstamp") + LoadExchange("Bitstamp", false, nil) p := pair.NewCurrencyPair("BTC", "USD") bids := []orderbook.Item{} bids = append(bids, orderbook.Item{Price: 1000, Amount: 1}) @@ -255,7 +255,7 @@ func TestGetSpecificOrderbook(t *testing.T) { func TestGetSpecificTicker(t *testing.T) { SetupTestHelpers(t) - LoadExchange("Bitstamp") + LoadExchange("Bitstamp", false, nil) p := pair.NewCurrencyPair("BTC", "USD") ticker.ProcessTicker("Bitstamp", p, ticker.Price{Last: 1000}, ticker.Spot) diff --git a/main.go b/main.go index f9b763ea..f9b1ffcf 100644 --- a/main.go +++ b/main.go @@ -10,7 +10,6 @@ import ( "runtime" "strconv" "syscall" - "time" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" @@ -74,7 +73,7 @@ func main() { AdjustGoMaxProcs() log.Printf("Bot '%s' started.\n", bot.config.Name) log.Printf("Fiat display currency: %s.", bot.config.FiatDisplayCurrency) - log.Printf("Bot dry run mode: %v\n", common.IsEnabled(bot.dryRun)) + log.Printf("Bot dry run mode: %v.\n", common.IsEnabled(bot.dryRun)) if bot.config.SMS.Enabled { bot.smsglobal = smsglobal.New(bot.config.SMS.Username, bot.config.SMS.Password, @@ -92,12 +91,13 @@ func main() { len(bot.config.Exchanges), bot.config.CountEnabledExchanges(), ) + common.HTTPClient = common.NewHTTPClientWithTimeout(bot.config.GlobalHTTPTimeout) + log.Printf("Global HTTP request timeout: %v.\n", common.HTTPClient.Timeout) + SetupExchanges() if len(bot.exchanges) == 0 { log.Fatalf("No exchanges were able to be loaded. Exiting") } - // TODO: Fix hack, allow 5 seconds to update exchange settings - time.Sleep(time.Second * 5) if bot.config.CurrencyExchangeProvider == "yahoo" { currency.SetProvider(true) @@ -124,7 +124,7 @@ func main() { SeedExchangeAccountInfo(GetAllEnabledExchangeAccountInfo().Data) go portfolio.StartPortfolioWatcher() - log.Println("Starting websocket handler") + log.Println("Starting websocket handler.") go WebsocketHandler() go TickerUpdaterRoutine() go OrderbookUpdaterRoutine() diff --git a/routines.go b/routines.go index 6b7a690c..df040dba 100644 --- a/routines.go +++ b/routines.go @@ -51,7 +51,7 @@ func printConvertCurrencyFormat(origCurrency string, origPrice float64) string { ) } -func printSummary(result ticker.Price, p pair.CurrencyPair, assetType, exchangeName string, err error) { +func printTickerSummary(result ticker.Price, p pair.CurrencyPair, assetType, exchangeName string, err error) { if err != nil { log.Printf("Failed to get %s %s ticker. Error: %s", p.Pair().String(), @@ -63,7 +63,7 @@ func printSummary(result ticker.Price, p pair.CurrencyPair, assetType, exchangeN stats.Add(exchangeName, p, assetType, result.Last, result.Volume) if currency.IsFiatCurrency(p.SecondCurrency.String()) && p.SecondCurrency.String() != bot.config.FiatDisplayCurrency { origCurrency := p.SecondCurrency.Upper().String() - log.Printf("%s %s %s: Last %s Ask %s Bid %s High %s Low %s Volume %.8f", + log.Printf("%s %s %s: TICKER: Last %s Ask %s Bid %s High %s Low %s Volume %.8f", exchangeName, exchange.FormatCurrency(p).String(), assetType, @@ -75,7 +75,7 @@ func printSummary(result ticker.Price, p pair.CurrencyPair, assetType, exchangeN result.Volume) } else { if currency.IsFiatCurrency(p.SecondCurrency.String()) && p.SecondCurrency.Upper().String() == bot.config.FiatDisplayCurrency { - log.Printf("%s %s %s: Last %s Ask %s Bid %s High %s Low %s Volume %.8f", + log.Printf("%s %s %s: TICKER: Last %s Ask %s Bid %s High %s Low %s Volume %.8f", exchangeName, exchange.FormatCurrency(p).String(), assetType, @@ -86,7 +86,7 @@ func printSummary(result ticker.Price, p pair.CurrencyPair, assetType, exchangeN printCurrencyFormat(result.Low), result.Volume) } else { - log.Printf("%s %s %s: Last %.8f Ask %.8f Bid %.8f High %.8f Low %.8f Volume %.8f", + log.Printf("%s %s %s: TICKER: Last %.8f Ask %.8f Bid %.8f High %.8f Low %.8f Volume %.8f", exchangeName, exchange.FormatCurrency(p).String(), assetType, @@ -113,7 +113,7 @@ func printOrderbookSummary(result orderbook.Base, p pair.CurrencyPair, assetType if currency.IsFiatCurrency(p.SecondCurrency.String()) && p.SecondCurrency.String() != bot.config.FiatDisplayCurrency { origCurrency := p.SecondCurrency.Upper().String() - log.Printf("%s %s %s: Orderbook Bids len: %d Amount: %f %s. Total value: %s Asks len: %d Amount: %f %s. Total value: %s", + log.Printf("%s %s %s: ORDERBOOK: Bids len: %d Amount: %f %s. Total value: %s Asks len: %d Amount: %f %s. Total value: %s", exchangeName, exchange.FormatCurrency(p).String(), assetType, @@ -128,7 +128,7 @@ func printOrderbookSummary(result orderbook.Base, p pair.CurrencyPair, assetType ) } else { if currency.IsFiatCurrency(p.SecondCurrency.String()) && p.SecondCurrency.Upper().String() == bot.config.FiatDisplayCurrency { - log.Printf("%s %s %s: Orderbook Bids len: %d Amount: %f %s. Total value: %s Asks len: %d Amount: %f %s. Total value: %s", + log.Printf("%s %s %s: ORDERBOOK: Bids len: %d Amount: %f %s. Total value: %s Asks len: %d Amount: %f %s. Total value: %s", exchangeName, exchange.FormatCurrency(p).String(), assetType, @@ -142,7 +142,7 @@ func printOrderbookSummary(result orderbook.Base, p pair.CurrencyPair, assetType printCurrencyFormat(asksValue), ) } else { - log.Printf("%s %s %s: Orderbook Bids len: %d Amount: %f %s. Total value: %f Asks len: %d Amount: %f %s. Total value: %f", + log.Printf("%s %s %s: ORDERBOOK: Bids len: %d Amount: %f %s. Total value: %f Asks len: %d Amount: %f %s. Total value: %f", exchangeName, exchange.FormatCurrency(p).String(), assetType, @@ -177,68 +177,53 @@ func relayWebsocketEvent(result interface{}, event, assetType, exchangeName stri // TickerUpdaterRoutine fetches and updates the ticker for all enabled // currency pairs and exchanges func TickerUpdaterRoutine() { - log.Println("Starting ticker updater routine") - var waitExchanges sync.WaitGroup - + log.Println("Starting ticker updater routine.") + var wg sync.WaitGroup for { - waitExchanges.Add(len(bot.exchanges)) + wg.Add(len(bot.exchanges)) for x := range bot.exchanges { - if bot.exchanges[x] == nil { - continue - } - exchangeName := bot.exchanges[x].GetName() - enabledCurrencies := bot.exchanges[x].GetEnabledCurrencies() - - var result ticker.Price - var err error - var assetTypes []string - - assetTypes, err = exchange.GetExchangeAssetTypes(exchangeName) - if err != nil { - log.Printf("failed to get %s exchange asset types. Error: %s", - exchangeName, err) - } - - blocker := make(chan int, 1) - - go func(c chan int, l int, wg *sync.WaitGroup) { - for i := 0; i < l; i++ { - <-c + go func(x int, wg *sync.WaitGroup) { + defer wg.Done() + if bot.exchanges[x] == nil { + return + } + exchangeName := bot.exchanges[x].GetName() + enabledCurrencies := bot.exchanges[x].GetEnabledCurrencies() + supportsBatching := bot.exchanges[x].SupportsRESTTickerBatchUpdates() + assetTypes, err := exchange.GetExchangeAssetTypes(exchangeName) + if err != nil { + log.Printf("failed to get %s exchange asset types. Error: %s", + exchangeName, err) + return } - log.Printf("Finished exchange %s ticker fetching for enabled currencies", exchangeName) - wg.Done() - }(blocker, len(enabledCurrencies), &waitExchanges) - for y := range enabledCurrencies { - - go func(x, y int, c chan int) { - currency := enabledCurrencies[y] - if len(assetTypes) > 1 { - for z := range assetTypes { - result, err = bot.exchanges[x].UpdateTicker(currency, assetTypes[z]) - printSummary(result, currency, assetTypes[z], exchangeName, err) - if err == nil { - relayWebsocketEvent(result, "ticker_update", assetTypes[z], exchangeName) - } - } + processTicker := func(exch exchange.IBotExchange, update bool, c pair.CurrencyPair, assetType string) { + var result ticker.Price + var err error + if update { + result, err = exch.UpdateTicker(c, assetType) } else { - result, err = bot.exchanges[x].UpdateTicker(currency, - assetTypes[0]) - printSummary(result, currency, assetTypes[0], exchangeName, err) - if err == nil { - relayWebsocketEvent(result, "ticker_update", assetTypes[0], exchangeName) + result, err = exch.GetTickerPrice(c, assetType) + } + printTickerSummary(result, c, assetType, exchangeName, err) + if err == nil { + relayWebsocketEvent(result, "ticker_update", assetType, exchangeName) + } + } + + for y := range assetTypes { + for z := range enabledCurrencies { + if supportsBatching && z > 0 { + processTicker(bot.exchanges[x], false, enabledCurrencies[z], assetTypes[y]) + continue } + processTicker(bot.exchanges[x], true, enabledCurrencies[z], assetTypes[y]) } - select { - case c <- 1: - default: - log.Fatal("channel blocked in ticker monitoring routine") - } - }(x, y, blocker) - } + } + }(x, &wg) } - waitExchanges.Wait() - log.Println("All enabled currency tickers fetched") + wg.Wait() + log.Println("All enabled currency tickers fetched.") time.Sleep(time.Second * 10) } } @@ -246,89 +231,43 @@ func TickerUpdaterRoutine() { // OrderbookUpdaterRoutine fetches and updates the orderbooks for all enabled // currency pairs and exchanges func OrderbookUpdaterRoutine() { - log.Println("Starting orderbook updater routine") - var waitExchanges sync.WaitGroup - + log.Println("Starting orderbook updater routine.") + var wg sync.WaitGroup for { - waitExchanges.Add(len(bot.exchanges)) + wg.Add(len(bot.exchanges)) for x := range bot.exchanges { - if bot.exchanges[x] == nil { - continue - } - exchangeName := bot.exchanges[x].GetName() - enabledCurrencies := bot.exchanges[x].GetEnabledCurrencies() - var result orderbook.Base - var err error - var assetTypes []string + go func(x int, wg *sync.WaitGroup) { + defer wg.Done() - assetTypes, err = exchange.GetExchangeAssetTypes(exchangeName) - if err != nil { - log.Printf("failed to get %s exchange asset types. Error: %s", - exchangeName, err) - } - - blocker := make(chan int, 1) - - go func(c chan int, l int, wg *sync.WaitGroup) { - for i := 0; i < l; i++ { - <-c + if bot.exchanges[x] == nil { + return + } + exchangeName := bot.exchanges[x].GetName() + enabledCurrencies := bot.exchanges[x].GetEnabledCurrencies() + assetTypes, err := exchange.GetExchangeAssetTypes(exchangeName) + if err != nil { + log.Printf("failed to get %s exchange asset types. Error: %s", + exchangeName, err) + return } - log.Printf("Finished exchange %s orderbook fetching for enabled currencies", exchangeName) - wg.Done() - }(blocker, len(enabledCurrencies), &waitExchanges) - for y := range enabledCurrencies { - go func(y int, x int, assetTypes []string, c chan int) { - currency := enabledCurrencies[y] - var subWg sync.WaitGroup - - if len(assetTypes) > 1 { - subBlocker := make(chan int, 1) - - subWg.Add(len(assetTypes)) - - go func(c chan int, l int, wg *sync.WaitGroup) { - for i := 0; i < l; i++ { - <-c - } - wg.Done() - }(subBlocker, len(assetTypes), &subWg) - - for z := range assetTypes { - go func(z int, x int, c chan int) { - result, err = bot.exchanges[x].UpdateOrderbook(currency, - assetTypes[z]) - printOrderbookSummary(result, currency, assetTypes[z], exchangeName, err) - if err == nil { - relayWebsocketEvent(result, "orderbook_update", assetTypes[z], exchangeName) - } - select { - case subBlocker <- 1: - default: - log.Fatal("channel blocked in subroutine assetTypes monitoring") - } - }(z, x, subBlocker) - } - - } else { - result, err = bot.exchanges[x].UpdateOrderbook(currency, - assetTypes[0]) - printOrderbookSummary(result, currency, assetTypes[0], exchangeName, err) - if err == nil { - relayWebsocketEvent(result, "orderbook_update", assetTypes[0], exchangeName) - } + processOrderbook := func(exch exchange.IBotExchange, c pair.CurrencyPair, assetType string) { + result, err := exch.UpdateOrderbook(c, assetType) + printOrderbookSummary(result, c, assetType, exchangeName, err) + if err == nil { + relayWebsocketEvent(result, "orderbook_update", assetType, exchangeName) } - select { - case c <- 1: - default: - log.Fatal("channel blocked in orderbook monitoring routine") + } + + for y := range assetTypes { + for z := range enabledCurrencies { + processOrderbook(bot.exchanges[x], enabledCurrencies[z], assetTypes[y]) } - subWg.Wait() - }(y, x, assetTypes, blocker) - } + } + }(x, &wg) } - waitExchanges.Wait() - log.Println("All enabled currency orderbooks fetched") + wg.Wait() + log.Println("All enabled currency orderbooks fetched.") time.Sleep(time.Second * 10) } } diff --git a/testdata/configtest.json b/testdata/configtest.json index fe157718..edbe18c1 100644 --- a/testdata/configtest.json +++ b/testdata/configtest.json @@ -8,18 +8,19 @@ "Delimiter": "-" }, "FiatDisplayCurrency": "USD", + "GlobalHTTPTimeout": 15000000000, "PortfolioAddresses": { "Addresses": [ { "Address": "1JCe8z4jJVNXSjohjM4i9Hh813dLCNx2Sy", "CoinType": "BTC", - "Balance": 53000.01245183, + "Balance": 53000.01252609, "Description": "" }, { "Address": "3Nxwenay9Z8Lc9JBiywExpnEFiLp6Afp8v", "CoinType": "BTC", - "Balance": 99848.27232691, + "Balance": 103848.27285787, "Description": "" }, { @@ -64,6 +65,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -89,10 +91,11 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", - "AvailablePairs": "ETH-BTC,LTC-BTC,BNB-BTC,NEO-BTC,123-456,QTUM-ETH,EOS-ETH,SNT-ETH,BNT-ETH,BCC-BTC,GAS-BTC,BNB-ETH,BTC-USDT,ETH-USDT,HSR-BTC,OAX-ETH,DNT-ETH,MCO-ETH,ICN-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,SNGLS-ETH,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,CTR-BTC,CTR-ETH,SALT-BTC,SALT-ETH,MDA-BTC,MDA-ETH,MTL-BTC,MTL-ETH,SUB-BTC,SUB-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,ICN-BTC,BTG-BTC,BTG-ETH,EVX-BTC,EVX-ETH,REQ-BTC,REQ-ETH,VIB-BTC,VIB-ETH,HSR-ETH,TRX-BTC,TRX-ETH,POWR-BTC,POWR-ETH,ARK-BTC,ARK-ETH,YOYO-ETH,XRP-BTC,XRP-ETH,MOD-BTC,MOD-ETH,ENJ-BTC,ENJ-ETH,STORJ-BTC,STORJ-ETH,BNB-USDT,VEN-BNB,YOYO-BNB,POWR-BNB,VEN-BTC,VEN-ETH,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,BCC-ETH,BCC-USDT,BCC-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,BTS-BNB,XZC-BTC,XZC-ETH,XZC-BNB,LSK-BTC,LSK-ETH,LSK-BNB,TNT-BTC,TNT-ETH,FUEL-BTC,FUEL-ETH,MANA-BTC,MANA-ETH,BCD-BTC,BCD-ETH,DGD-BTC,DGD-ETH,IOTA-BNB,ADX-BTC,ADX-ETH,ADX-BNB,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,WINGS-BTC,WINGS-ETH,NAV-BTC,NAV-ETH,NAV-BNB,LUN-BTC,LUN-ETH,TRIG-BTC,TRIG-ETH,TRIG-BNB,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,CHAT-BTC,CHAT-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,RPX-BTC,RPX-ETH,RPX-BNB,NCASH-BTC,NCASH-ETH,NCASH-BNB,POA-BTC,POA-ETH,POA-BNB,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,QLC-BTC,QLC-ETH", + "AvailablePairs": "ETH-BTC,LTC-BTC,BNB-BTC,NEO-BTC,QTUM-ETH,EOS-ETH,SNT-ETH,BNT-ETH,BCC-BTC,GAS-BTC,BNB-ETH,BTC-USDT,ETH-USDT,HSR-BTC,OAX-ETH,DNT-ETH,MCO-ETH,ICN-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,SNGLS-ETH,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,SALT-BTC,SALT-ETH,MDA-BTC,MDA-ETH,MTL-BTC,MTL-ETH,SUB-BTC,SUB-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,ICN-BTC,BTG-BTC,BTG-ETH,EVX-BTC,EVX-ETH,REQ-BTC,REQ-ETH,VIB-BTC,VIB-ETH,HSR-ETH,TRX-BTC,TRX-ETH,POWR-BTC,POWR-ETH,ARK-BTC,ARK-ETH,YOYO-ETH,XRP-BTC,XRP-ETH,MOD-BTC,MOD-ETH,ENJ-BTC,ENJ-ETH,STORJ-BTC,STORJ-ETH,BNB-USDT,VEN-BNB,YOYO-BNB,POWR-BNB,VEN-BTC,VEN-ETH,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,BCC-ETH,BCC-USDT,BCC-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,BTS-BNB,XZC-BTC,XZC-ETH,XZC-BNB,LSK-BTC,LSK-ETH,LSK-BNB,TNT-BTC,TNT-ETH,FUEL-BTC,FUEL-ETH,MANA-BTC,MANA-ETH,BCD-BTC,BCD-ETH,DGD-BTC,DGD-ETH,IOTA-BNB,ADX-BTC,ADX-ETH,ADX-BNB,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,WINGS-BTC,WINGS-ETH,NAV-BTC,NAV-ETH,NAV-BNB,LUN-BTC,LUN-ETH,TRIG-BTC,TRIG-ETH,TRIG-BNB,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,CHAT-BTC,CHAT-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,RPX-BTC,RPX-ETH,RPX-BNB,NCASH-BTC,NCASH-ETH,NCASH-BNB,POA-BTC,POA-ETH,POA-BNB,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,CLOAK-BTC,CLOAK-ETH,GNT-BTC,GNT-ETH,GNT-BNB,LOOM-BTC,LOOM-ETH,LOOM-BNB", "EnabledPairs": "BTC-USDT", "BaseCurrencies": "USD", "AssetTypes": "SPOT", @@ -112,10 +115,11 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", - "AvailablePairs": "BTCUSD,LTCUSD,LTCBTC,ETHUSD,ETHBTC,ETCBTC,ETCUSD,RRTUSD,RRTBTC,ZECUSD,ZECBTC,XMRUSD,XMRBTC,DSHUSD,DSHBTC,BTCEUR,XRPUSD,XRPBTC,IOTUSD,IOTBTC,IOTETH,EOSUSD,EOSBTC,EOSETH,SANUSD,SANBTC,SANETH,OMGUSD,OMGBTC,OMGETH,BCHUSD,BCHBTC,BCHETH,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", + "AvailablePairs": "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,BCHUSD,BCHBTC,BCHETH,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,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,CFIUSD,CFIBTC,CFIETH,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", "EnabledPairs": "BTCUSD,LTCUSD,LTCBTC,ETHUSD,ETHBTC", "BaseCurrencies": "USD", "AssetTypes": "SPOT", @@ -134,6 +138,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -159,6 +164,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -184,6 +190,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -207,10 +214,11 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", - "AvailablePairs": "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-AUR,BTC-EFL,BTC-GLD,BTC-SLR,BTC-PTC,BTC-GRS,BTC-NLG,BTC-RBY,BTC-XWC,BTC-MONA,BTC-THC,BTC-ENRG,BTC-ERC,BTC-VRC,BTC-CURE,BTC-XMR,BTC-CLOAK,BTC-START,BTC-KORE,BTC-XDN,BTC-TRUST,BTC-NAV,BTC-XST,BTC-VIA,BTC-PINK,BTC-IOC,BTC-CANN,BTC-SYS,BTC-NEOS,BTC-DGB,BTC-BURST,BTC-EXCL,BTC-SWIFT,BTC-DOPE,BTC-BLOCK,BTC-ABY,BTC-BYC,BTC-XMG,BTC-BLITZ,BTC-BAY,BTC-FAIR,BTC-SPR,BTC-VTR,BTC-XRP,BTC-GAME,BTC-COVAL,BTC-NXS,BTC-XCP,BTC-BITB,BTC-GEO,BTC-FLDC,BTC-GRC,BTC-FLO,BTC-NBT,BTC-MUE,BTC-XEM,BTC-CLAM,BTC-DMD,BTC-GAM,BTC-SPHR,BTC-OK,BTC-SNRG,BTC-PKB,BTC-AEON,BTC-ETH,BTC-TX,BTC-BCY,BTC-EXP,BTC-OMNI,BTC-AMP,BTC-XLM,USDT-BTC,BTC-RVR,BTC-EMC,BTC-FCT,BTC-EGC,BTC-SLS,BTC-RADS,BTC-DCR,BTC-BSD,BTC-XVG,BTC-PIVX,BTC-MEME,BTC-STEEM,BTC-2GIVE,BTC-LSK,BTC-BRK,BTC-WAVES,BTC-LBC,BTC-SBD,BTC-BRX,BTC-ETC,ETH-ETC,BTC-STRAT,BTC-UNB,BTC-SYNX,BTC-EBST,BTC-VRM,BTC-SEQ,BTC-REP,BTC-SHIFT,BTC-ARDR,BTC-XZC,BTC-NEO,BTC-ZEC,BTC-ZCL,BTC-IOP,BTC-GOLOS,BTC-UBQ,BTC-KMD,BTC-GBG,BTC-SIB,BTC-ION,BTC-LMC,BTC-QWARK,BTC-CRW,BTC-SWT,BTC-MLN,BTC-ARK,BTC-DYN,BTC-TKS,BTC-MUSIC,BTC-DTB,BTC-INCNT,BTC-GBYTE,BTC-GNT,BTC-NXC,BTC-EDG,BTC-LGD,BTC-TRST,ETH-GNT,ETH-REP,USDT-ETH,ETH-WINGS,BTC-WINGS,BTC-RLC,BTC-GNO,BTC-GUP,BTC-LUN,ETH-GUP,ETH-RLC,ETH-LUN,ETH-GNO,BTC-HMQ,ETH-HMQ,BTC-ANT,ETH-TRST,ETH-ANT,BTC-SC,ETH-BAT,BTC-BAT,BTC-ZEN,BTC-QRL,ETH-QRL,BTC-CRB,ETH-CRB,ETH-LGD,BTC-PTOY,ETH-PTOY,BTC-CFI,ETH-CFI,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,ETH-ADT,BTC-PAY,ETH-PAY,BTC-STORJ,ETH-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-BCC,USDT-BCC,BTC-BCC,BTC-DNT,ETH-DNT,USDT-NEO,ETH-WAVES,ETH-STRAT,ETH-DGB,ETH-FCT,USDT-OMG,BTC-ADA,BTC-MANA,ETH-MANA,BTC-SALT,ETH-SALT,BTC-TIX,ETH-TIX,BTC-RCN,ETH-RCN,BTC-VIB,ETH-VIB,BTC-MER,BTC-POWR,ETH-POWR,BTC-BTG,ETH-BTG,USDT-BTG,ETH-ADA,BTC-ENG,ETH-ENG,USDT-ADA,USDT-XVG,USDT-NXT,BTC-UKG,ETH-UKG,BTC-IGNIS,BTC-SRN,ETH-SRN,BTC-WAX,ETH-WAX,BTC-ZRX,ETH-ZRX,BTC-VEE,ETH-VEE,BTC-BCPT,ETH-BCPT,BTC-TRX,ETH-TRX,BTC-TUSD,BTC-LRC,ETH-LRC,ETH-TUSD,BTC-UP,ETH-UP", + "AvailablePairs": "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-AUR,BTC-EFL,BTC-GLD,BTC-SLR,BTC-PTC,BTC-GRS,BTC-NLG,BTC-RBY,BTC-XWC,BTC-MONA,BTC-THC,BTC-ENRG,BTC-ERC,BTC-VRC,BTC-CURE,BTC-XMR,BTC-CLOAK,BTC-KORE,BTC-XDN,BTC-TRUST,BTC-NAV,BTC-XST,BTC-VIA,BTC-PINK,BTC-IOC,BTC-CANN,BTC-SYS,BTC-NEOS,BTC-DGB,BTC-BURST,BTC-EXCL,BTC-SWIFT,BTC-DOPE,BTC-BLOCK,BTC-ABY,BTC-BYC,BTC-XMG,BTC-BLITZ,BTC-BAY,BTC-SPR,BTC-VTR,BTC-XRP,BTC-GAME,BTC-COVAL,BTC-NXS,BTC-XCP,BTC-BITB,BTC-GEO,BTC-FLDC,BTC-GRC,BTC-FLO,BTC-NBT,BTC-MUE,BTC-XEM,BTC-CLAM,BTC-DMD,BTC-GAM,BTC-SPHR,BTC-OK,BTC-AEON,BTC-ETH,BTC-TX,BTC-BCY,BTC-EXP,BTC-OMNI,BTC-AMP,BTC-XLM,USDT-BTC,BTC-RVR,BTC-EMC,BTC-FCT,BTC-EGC,BTC-SLS,BTC-RADS,BTC-DCR,BTC-BSD,BTC-XVG,BTC-PIVX,BTC-MEME,BTC-STEEM,BTC-2GIVE,BTC-LSK,BTC-BRK,BTC-WAVES,BTC-LBC,BTC-SBD,BTC-BRX,BTC-ETC,ETH-ETC,BTC-STRAT,BTC-UNB,BTC-SYNX,BTC-EBST,BTC-VRM,BTC-SEQ,BTC-REP,BTC-SHIFT,BTC-ARDR,BTC-XZC,BTC-NEO,BTC-ZEC,BTC-ZCL,BTC-IOP,BTC-GOLOS,BTC-UBQ,BTC-KMD,BTC-GBG,BTC-SIB,BTC-ION,BTC-LMC,BTC-QWARK,BTC-CRW,BTC-SWT,BTC-MLN,BTC-ARK,BTC-DYN,BTC-TKS,BTC-MUSIC,BTC-DTB,BTC-INCNT,BTC-GBYTE,BTC-GNT,BTC-NXC,BTC-EDG,BTC-LGD,BTC-TRST,ETH-GNT,ETH-REP,USDT-ETH,ETH-WINGS,BTC-WINGS,BTC-RLC,BTC-GNO,BTC-GUP,BTC-LUN,ETH-GUP,ETH-RLC,ETH-LUN,ETH-GNO,BTC-HMQ,ETH-HMQ,BTC-ANT,ETH-TRST,ETH-ANT,BTC-SC,ETH-BAT,BTC-BAT,BTC-ZEN,BTC-QRL,ETH-QRL,BTC-CRB,ETH-CRB,ETH-LGD,BTC-PTOY,ETH-PTOY,BTC-CFI,ETH-CFI,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,ETH-ADT,BTC-PAY,ETH-PAY,BTC-STORJ,ETH-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-BCC,USDT-BCC,BTC-BCC,BTC-DNT,ETH-DNT,USDT-NEO,ETH-WAVES,ETH-STRAT,ETH-DGB,ETH-FCT,USDT-OMG,BTC-ADA,BTC-MANA,ETH-MANA,BTC-SALT,ETH-SALT,BTC-TIX,ETH-TIX,BTC-RCN,ETH-RCN,BTC-VIB,ETH-VIB,BTC-MER,BTC-POWR,ETH-POWR,BTC-BTG,ETH-BTG,USDT-BTG,ETH-ADA,BTC-ENG,ETH-ENG,USDT-ADA,USDT-XVG,USDT-NXT,BTC-UKG,ETH-UKG,BTC-IGNIS,BTC-SRN,ETH-SRN,BTC-WAX,ETH-WAX,BTC-ZRX,ETH-ZRX,BTC-VEE,ETH-VEE,BTC-BCPT,ETH-BCPT,BTC-TRX,ETH-TRX,BTC-TUSD,BTC-LRC,ETH-LRC,ETH-TUSD,BTC-UP,ETH-UP,BTC-DMT,ETH-DMT,USDT-TUSD,BTC-POLY,ETH-POLY,BTC-PRO,ETH-PRO,USDT-SC,USDT-TRX", "EnabledPairs": "USDT-BTC", "BaseCurrencies": "USD", "AssetTypes": "SPOT", @@ -231,6 +239,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -253,6 +262,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -278,6 +288,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -300,11 +311,12 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", "ClientID": "ClientID", - "AvailablePairs": "ETCUSDT,ETHLTC,LTCUSDT,USDTUSD,XMRBTC,BTCSGD,ETCBTC,ETCLTC,LTCBTC,XMRLTC,XMRUSDT,BTCUSD,BTCUSDT,ETHBTC,USDTSGD,ETHUSDT,LTCSGD,LTCUSD", + "AvailablePairs": "ETHSGD,LTCSGD,XMRLTC,ZECBTC,ZECUSDT,BTCUSDT,ETCBTC,ETHUSDT,USDTSGD,BTCUSD,ETHLTC,LTCUSD,XMRBTC,XMRUSDT,ZECLTC,BTCSGD,ETCLTC,ETCUSDT,ETHBTC,LTCBTC,LTCUSDT,USDTUSD", "EnabledPairs": "LTCBTC,ETCBTC,ETHBTC", "BaseCurrencies": "USD", "AssetTypes": "SPOT", @@ -323,10 +335,11 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", - "AvailablePairs": "XRP_USD,XMR_USD,BTC_USD,ETH_USD,ETH_UAH,ETC_BTC,ETC_USD,XRP_BTC,ETH_USDT,USD_RUB,XMR_EUR,BTC_USDT,BTC_PLN,BCH_ETH,DASH_BTC,ETH_PLN,ZEC_BTC,ZEC_EUR,BCH_BTC,LTC_BTC,WAVES_RUB,KICK_ETH,ETH_EUR,ETH_RUB,USDT_USD,ZEC_RUB,USDT_RUB,DOGE_BTC,ETH_BTC,LTC_EUR,BTC_EUR,BTC_UAH,BCH_USD,BCH_RUB,DASH_USD,DASH_RUB,ZEC_USD,XMR_BTC,BTC_RUB,KICK_BTC,ETH_LTC,ETC_RUB,LTC_USD,LTC_RUB,XRP_RUB,WAVES_BTC", + "AvailablePairs": "ZEC_EUR,XRP_USD,XRP_RUB,XMR_BTC,USD_RUB,ETC_RUB,BTC_EUR,BCH_BTC,BTG_USD,BCH_USD,ETH_USD,ETH_RUB,ETH_PLN,KICK_BTC,BTG_BTC,BCH_ETH,DASH_BTC,DASH_RUB,ETH_EUR,LTC_EUR,BTC_USDT,LTC_RUB,XMR_USD,KICK_ETH,DASH_USD,ETH_UAH,ZEC_RUB,XRP_BTC,XMR_EUR,USDT_RUB,WAVES_BTC,BTC_RUB,BCH_RUB,ETH_BTC,LTC_BTC,ETH_USDT,BTC_UAH,ETC_USD,LTC_USD,ZEC_BTC,ZEC_USD,DOGE_BTC,BTC_USD,BTC_PLN,ETH_LTC,ETC_BTC,USDT_USD,WAVES_RUB", "EnabledPairs": "BTC_USD,LTC_USD", "BaseCurrencies": "USD,EUR,RUB,PLN,UAH", "AssetTypes": "SPOT", @@ -348,6 +361,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -372,6 +386,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -394,10 +409,11 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", - "AvailablePairs": "BCN-BTC,BTC-USD,DASH-BTC,DOGE-BTC,DOGE-USD,DSH-BTC,EMC-BTC,ETH-BTC,FCN-BTC,LSK-BTC,LTC-BTC,LTC-USD,NXT-BTC,QCN-BTC,SBD-BTC,SC-BTC,STEEM-BTC,XDN-BTC,XEM-BTC,XMR-BTC,ARDR-BTC,ZEC-BTC,WAVES-BTC,MAID-BTC,AMP-BTC,BUS-BTC,DGD-BTC,ICN-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,TAAS-BTC,NXC-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,CFI-BTC,PLBT-BTC,BNT-BTC,XDNCO-BTC,FYN-ETH,SNM-BTC,SNM-ETH,SNT-ETH,CVC-USD,PAY-ETH,OAX-ETH,OMG-ETH,BQX-ETH,XTZ-BTC,DICE-BTC,CFI-ETH,PTOY-ETH,1ST-ETH,XAUR-ETH,TAAS-ETH,TIME-ETH,DICE-ETH,SWT-ETH,XMR-ETH,ETC-ETH,DASH-ETH,ZEC-ETH,PLU-ETH,GNO-ETH,XRP-BTC,NET-ETH,STRAT-USD,STRAT-BTC,SNC-ETH,ADX-ETH,BET-ETH,EOS-ETH,DENT-ETH,SAN-ETH,EOS-BTC,EOS-USD,MNE-BTC,MSP-ETH,DDF-ETH,XTZ-ETH,XTZ-USD,UET-ETH,MYB-ETH,SUR-ETH,IXT-ETH,PLR-ETH,TIX-ETH,NDC-ETH,PRO-ETH,AVT-ETH,COSS-ETH,EVX-USD,DLT-BTC,BNT-ETH,BNT-USD,QAU-BTC,QAU-ETH,MANA-USD,DNT-BTC,FYP-BTC,OPT-BTC,TNT-ETH,IFT-BTC,STX-BTC,STX-ETH,STX-USD,TNT-USD,TNT-BTC,CAT-BTC,CAT-ETH,CAT-USD,BCH-BTC,BCH-ETH,BCH-USD,ENG-ETH,XUC-USD,SNC-BTC,SNC-USD,OAX-USD,OAX-BTC,BAS-ETH,ZRX-BTC,ZRX-ETH,ZRX-USD,RVT-BTC,ICOS-BTC,ICOS-ETH,ICOS-USD,PPC-BTC,PPC-USD,QTUM-ETH,VERI-BTC,VERI-ETH,VERI-USD,IGNIS-ETH,PRG-BTC,PRG-ETH,PRG-USD,BMC-BTC,BMC-ETH,BMC-USD,CND-BTC,CND-ETH,CND-USD,SKIN-BTC,EMGO-BTC,EMGO-USD,CDT-ETH,CDT-USD,FUN-BTC,FUN-ETH,FUN-USD,HVN-BTC,HVN-ETH,FUEL-BTC,FUEL-ETH,FUEL-USD,POE-BTC,POE-ETH,MCAP-BTC,AIR-BTC,AIR-ETH,AIR-USD,AMB-USD,AMB-ETH,AMB-BTC,NTO-BTC,ICO-BTC,PING-BTC,RKC-ETH,GAME-BTC,TKR-ETH,HPC-BTC,PPT-ETH,MTH-BTC,MTH-ETH,WMGO-BTC,WMGO-USD,LRC-BTC,LRC-ETH,ICX-BTC,ICX-ETH,NEO-BTC,NEO-ETH,NEO-USD,CSNO-BTC,ORME-BTC,ICX-USD,PIX-BTC,PIX-ETH,IND-ETH,KICK-BTC,YOYOW-BTC,MIPS-BTC,CDT-BTC,XVG-BTC,XVG-ETH,XVG-USD,DGB-BTC,DGB-ETH,DGB-USD,DCN-BTC,DCN-ETH,DCN-USD,LAT-BTC,CCT-ETH,EBET-ETH,VIBE-BTC,VOISE-BTC,ENJ-BTC,ENJ-ETH,ENJ-USD,ZSC-BTC,ZSC-ETH,ZSC-USD,ETBS-BTC,TRX-BTC,TRX-ETH,TRX-USD,VEN-BTC,VEN-ETH,VEN-USD,ART-BTC,EVX-BTC,EVX-ETH,QVT-ETH,EBTCOLD-BTC,EBTCOLD-ETH,EBTCOLD-USD,BKB-BTC,EXN-BTC,TGT-BTC,ATS-ETH,CTR-BTC,CTR-ETH,CTR-USD,BMT-BTC,BMT-ETH,SUB-BTC,SUB-ETH,SUB-USD,WTC-BTC,CNX-BTC,ATB-BTC,ATB-ETH,ATB-USD,ODN-BTC,BTM-BTC,BTM-ETH,BTM-USD,B2X-BTC,B2X-ETH,B2X-USD,ATM-BTC,ATM-ETH,ATM-USD,LIFE-BTC,VIB-BTC,VIB-ETH,VIB-USD,DRT-ETH,STU-USD,OMG-BTC,PAY-BTC,COSS-BTC,PPT-BTC,SNT-BTC,BTG-BTC,BTG-ETH,BTG-USD,SMART-BTC,SMART-ETH,SMART-USD,XUC-ETH,XUC-BTC,CL-BTC,CL-ETH,CL-USD,LA-ETH,CLD-BTC,CLD-ETH,CLD-USD,ELM-BTC,EDO-BTC,EDO-ETH,EDO-USD,HGT-ETH,POLL-BTC,IXT-BTC,ATS-BTC,SCL-BTC,ATL-BTC,EBTC-BTC,EBTC-ETH,EBTC-USD,ETP-BTC,ETP-ETH,ETP-USD,OTX-BTC,CDX-ETH,DRPU-BTC,NEBL-BTC,NEBL-ETH,HAC-BTC,CTX-BTC,CTX-ETH,ELE-BTC,ARN-BTC,ARN-ETH,SISA-BTC,SISA-ETH,STU-BTC,STU-ETH,GVT-ETH,INDI-BTC,BTX-BTC,BTX-USD,LTC-ETH,BCN-ETH,MAID-ETH,NXT-ETH,STRAT-ETH,XDN-ETH,XEM-ETH,PLR-BTC,SUR-BTC,BQX-BTC,DOGE-ETH,ITS-BTC,AMM-BTC,AMM-ETH,AMM-USD,DBIX-BTC,PRE-BTC,KBR-BTC,TBT-BTC,ERO-BTC,SMS-BTC,SMS-ETH,SMS-USD,ZAP-BTC,DOV-BTC,DOV-ETH,FRD-BTC,DRPU-ETH,OTN-BTC,XRP-ETH,XRP-USD,HSR-BTC,LEND-BTC,LEND-ETH,SPF-ETH,SBTC-BTC,SBTC-ETH,BTCA-BTC,BTCA-ETH,BTCA-USD,WRC-BTC,WRC-ETH,WRC-USD,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,ECH-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,TIO-BTC,TIO-ETH,TIO-USD,WAX-BTC,WAX-ETH,WAX-USD,ULTC-BTC,EET-BTC,EET-ETH,EET-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,CVH-ETH,CVH-USD,W3C-BTC,W3C-ETH,CAS-BTC,CAS-ETH,CAS-USD,CHAT-BTC,CHAT-ETH,CHAT-USD,GRMD-BTC,AVH-BTC,TRAC-ETH,JNT-ETH,PCL-BTC,PCL-ETH,CLOUT-BTC,UTK-BTC,UTK-ETH,UTK-USD,GNX-ETH,CHSB-BTC,CHSB-ETH,AVH-ETH,NEU-BTC,NEU-ETH,NEU-USD,AVH-USD,CLOUT-ETH,CLOUT-USD,TAU-BTC,MEK-BTC,BAR-BTC,BAR-ETH,BAR-USD,FLP-BTC,FLP-ETH,FLP-USD,R-BTC,R-ETH,EKO-USD,BCPT-ETH,BCPT-USD,PKT-BTC,PKT-ETH,WLK-BTC,WLK-ETH,WLK-USD,EVN-BTC,CPG-BTC,CPG-ETH,BPTN-BTC,BPTN-ETH,BPTN-USD,BETR-BTC,BETR-ETH,ARCT-BTC,ARCT-USD,DBET-BTC,DBET-ETH,DBET-USD,RNTB-ETH,HAND-ETH,HAND-USD,BEZ-BTC,BEZ-ETH,BEZ-USD,ACO-ETH,CTE-BTC,CTE-ETH,CTE-USD,UTNP-BTC,UTNP-ETH,UTNP-USD,CPY-BTC,CPY-ETH,CHP-ETH,BCPT-BTC,ACT-BTC,ACT-ETH,ACT-USD,HIRE-ETH,ADA-BTC,ADA-ETH,ADA-USD,SIG-BTC,RPM-BTC,RPM-ETH,MTX-BTC,MTX-ETH,MTX-USD,BGG-BTC,BGG-ETH,BGG-USD,SETH-ETH,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,NOAH-BTC,SOC-BTC,WILD-BTC,INSUR-BTC,INSUR-ETH,OCN-BTC,OCN-ETH,STQ-BTC,STQ-ETH,XLM-BTC,XLM-ETH,XLM-USD,IOTA-BTC,IOTA-ETH,IOTA-USD,DRT-BTC,BETR-USD,CGC-ETH,ERT-BTC,MESH-BTC,MESH-ETH,MESH-USD,CRPT-BTC,CRPT-USD,HLW-ETH,IHT-BTC,IHT-ETH,IHT-USD,SCC-BTC,YCC-BTC", + "AvailablePairs": "BCN-BTC,BTC-USD,DASH-BTC,DOGE-BTC,DOGE-USD,DSH-BTC,EMC-BTC,ETH-BTC,FCN-BTC,LSK-BTC,LTC-BTC,LTC-USD,NXT-BTC,QCN-BTC,SBD-BTC,SC-BTC,STEEM-BTC,XDN-BTC,XEM-BTC,XMR-BTC,ARDR-BTC,ZEC-BTC,WAVES-BTC,MAID-BTC,AMP-BTC,BUS-BTC,DGD-BTC,ICN-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,TAAS-BTC,NXC-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,CFI-BTC,PLBT-BTC,BNT-BTC,XDNCO-BTC,FYN-ETH,SNM-BTC,SNM-ETH,SNT-ETH,CVC-USD,PAY-ETH,OAX-ETH,OMG-ETH,BQX-ETH,XTZ-BTC,DICE-BTC,CFI-ETH,PTOY-ETH,1ST-ETH,XAUR-ETH,TAAS-ETH,TIME-ETH,DICE-ETH,SWT-ETH,XMR-ETH,ETC-ETH,DASH-ETH,ZEC-ETH,PLU-ETH,GNO-ETH,XRP-BTC,NET-ETH,STRAT-USD,STRAT-BTC,SNC-ETH,ADX-ETH,BET-ETH,EOS-ETH,DENT-ETH,SAN-ETH,EOS-BTC,EOS-USD,MNE-BTC,MSP-ETH,DDF-ETH,XTZ-ETH,XTZ-USD,UET-ETH,MYB-ETH,SUR-ETH,IXT-ETH,PLR-ETH,TIX-ETH,NDC-ETH,PRO-ETH,AVT-ETH,COSS-ETH,EVX-USD,DLT-BTC,BNT-ETH,BNT-USD,QAU-BTC,QAU-ETH,MANA-USD,DNT-BTC,FYP-BTC,OPT-BTC,TNT-ETH,IFT-BTC,STX-BTC,STX-ETH,STX-USD,TNT-USD,TNT-BTC,CAT-BTC,CAT-ETH,CAT-USD,BCH-BTC,BCH-ETH,BCH-USD,ENG-ETH,XUC-USD,SNC-BTC,SNC-USD,OAX-USD,OAX-BTC,BAS-ETH,ZRX-BTC,ZRX-ETH,ZRX-USD,RVT-BTC,ICOS-BTC,ICOS-ETH,ICOS-USD,PPC-BTC,PPC-USD,QTUM-ETH,VERI-BTC,VERI-ETH,VERI-USD,IGNIS-ETH,PRG-BTC,PRG-ETH,PRG-USD,BMC-BTC,BMC-ETH,BMC-USD,CND-BTC,CND-ETH,CND-USD,SKIN-BTC,EMGO-BTC,EMGO-USD,CDT-ETH,CDT-USD,FUN-BTC,FUN-ETH,FUN-USD,HVN-BTC,HVN-ETH,FUEL-BTC,FUEL-ETH,FUEL-USD,POE-BTC,POE-ETH,MCAP-BTC,AIR-BTC,AIR-ETH,AIR-USD,AMB-USD,AMB-ETH,AMB-BTC,NTO-BTC,ICO-BTC,PING-BTC,GAME-BTC,TKR-ETH,HPC-BTC,PPT-ETH,MTH-BTC,MTH-ETH,WMGO-BTC,WMGO-USD,LRC-BTC,LRC-ETH,NEO-BTC,NEO-ETH,NEO-USD,CSNO-BTC,ORME-BTC,PIX-BTC,PIX-ETH,IND-ETH,KICK-BTC,YOYOW-BTC,MIPS-BTC,CDT-BTC,XVG-BTC,XVG-ETH,XVG-USD,DGB-BTC,DGB-ETH,DGB-USD,DCN-BTC,DCN-ETH,DCN-USD,LAT-BTC,CCT-ETH,EBET-ETH,VIBE-BTC,VOISE-BTC,ENJ-BTC,ENJ-ETH,ENJ-USD,ZSC-BTC,ZSC-ETH,ZSC-USD,ETBS-BTC,TRX-BTC,TRX-ETH,TRX-USD,VEN-BTC,VEN-ETH,VEN-USD,ART-BTC,EVX-BTC,EVX-ETH,QVT-ETH,EBTCOLD-BTC,EBTCOLD-ETH,EBTCOLD-USD,BKB-BTC,EXN-BTC,TGT-BTC,ATS-ETH,CTR-BTC,CTR-ETH,CTR-USD,BMT-BTC,BMT-ETH,SUB-BTC,SUB-ETH,SUB-USD,WTC-BTC,CNX-BTC,ATB-BTC,ATB-ETH,ATB-USD,ODN-BTC,BTM-BTC,BTM-ETH,BTM-USD,B2X-BTC,B2X-ETH,B2X-USD,ATM-BTC,ATM-ETH,ATM-USD,LIFE-BTC,VIB-BTC,VIB-ETH,VIB-USD,DRT-ETH,STU-USD,OMG-BTC,PAY-BTC,COSS-BTC,PPT-BTC,SNT-BTC,BTG-BTC,BTG-ETH,BTG-USD,SMART-BTC,SMART-ETH,SMART-USD,XUC-ETH,XUC-BTC,CL-BTC,CL-ETH,CL-USD,LA-ETH,CLD-BTC,CLD-ETH,CLD-USD,ELM-BTC,EDO-BTC,EDO-ETH,EDO-USD,HGT-ETH,POLL-BTC,IXT-BTC,ATS-BTC,SCL-BTC,ATL-BTC,EBTC-BTC,EBTC-ETH,EBTC-USD,ETP-BTC,ETP-ETH,ETP-USD,OTX-BTC,CDX-ETH,DRPU-BTC,NEBL-BTC,NEBL-ETH,HAC-BTC,CTX-BTC,CTX-ETH,ELE-BTC,ARN-BTC,ARN-ETH,SISA-BTC,SISA-ETH,STU-BTC,STU-ETH,GVT-ETH,INDI-BTC,BTX-BTC,LTC-ETH,BCN-ETH,MAID-ETH,NXT-ETH,STRAT-ETH,XDN-ETH,XEM-ETH,PLR-BTC,SUR-BTC,BQX-BTC,DOGE-ETH,ITS-BTC,AMM-BTC,AMM-ETH,AMM-USD,DBIX-BTC,PRE-BTC,KBR-BTC,TBT-BTC,ERO-BTC,SMS-BTC,SMS-ETH,SMS-USD,ZAP-BTC,DOV-BTC,DOV-ETH,FRD-BTC,DRPU-ETH,OTN-BTC,XRP-ETH,XRP-USD,HSR-BTC,LEND-BTC,LEND-ETH,SPF-ETH,SBTC-BTC,SBTC-ETH,BTCA-BTC,BTCA-ETH,BTCA-USD,WRC-BTC,WRC-ETH,WRC-USD,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,ECH-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,TIO-BTC,TIO-ETH,TIO-USD,WAX-BTC,WAX-ETH,WAX-USD,EET-BTC,EET-ETH,EET-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,CVH-ETH,CVH-USD,CAS-BTC,CAS-ETH,CAS-USD,CHAT-BTC,CHAT-ETH,CHAT-USD,GRMD-BTC,AVH-BTC,TRAC-ETH,JNT-ETH,PCL-BTC,PCL-ETH,CLOUT-BTC,UTK-BTC,UTK-ETH,UTK-USD,GNX-ETH,CHSB-BTC,CHSB-ETH,AVH-ETH,DAY-BTC,DAY-ETH,DAY-USD,NEU-BTC,NEU-ETH,NEU-USD,AVH-USD,CLOUT-ETH,CLOUT-USD,TAU-BTC,MEK-BTC,BAR-BTC,BAR-ETH,BAR-USD,FLP-BTC,FLP-ETH,FLP-USD,R-BTC,R-ETH,EKO-USD,BCPT-ETH,BCPT-USD,PKT-BTC,PKT-ETH,WLK-BTC,WLK-ETH,WLK-USD,EVN-BTC,CPG-BTC,CPG-ETH,BPTN-BTC,BPTN-ETH,BPTN-USD,BETR-BTC,BETR-ETH,ARCT-BTC,ARCT-USD,DBET-BTC,DBET-ETH,DBET-USD,RNTB-ETH,HAND-ETH,HAND-USD,BEZ-BTC,BEZ-ETH,BEZ-USD,ACO-ETH,CTE-BTC,CTE-ETH,CTE-USD,UTNP-BTC,UTNP-ETH,UTNP-USD,CPY-BTC,CPY-ETH,CHP-ETH,BCPT-BTC,ACT-BTC,ACT-ETH,ACT-USD,HIRE-ETH,ADA-BTC,ADA-ETH,ADA-USD,SIG-BTC,RPM-BTC,RPM-ETH,MTX-BTC,MTX-ETH,MTX-USD,BGG-BTC,BGG-ETH,BGG-USD,SETH-ETH,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,NOAH-BTC,SOC-BTC,WILD-BTC,INSUR-BTC,INSUR-ETH,OCN-BTC,OCN-ETH,STQ-BTC,STQ-ETH,XLM-BTC,XLM-ETH,XLM-USD,IOTA-BTC,IOTA-ETH,IOTA-USD,DRT-BTC,MLD-BTC,MLD-ETH,MLD-USD,BETR-USD,CGC-ETH,ERT-BTC,CRPT-BTC,CRPT-USD,MESH-BTC,MESH-ETH,MESH-USD,HLW-ETH,IHT-BTC,IHT-ETH,IHT-USD,SCC-BTC,YCC-BTC,DAN-BTC,TEL-BTC,TEL-ETH,BUBO-BTC,BUBO-ETH,BUBO-USD,VIT-BTC,VIT-ETH,VIT-USD,CLR-BTC,NCT-BTC,NCT-ETH,NCT-USD,AXP-BTC,AXP-ETH,BMH-BTC,BANCA-USD,NOAH-ETH,NOAH-USD,HQX-BTC,LDC-BTC,XMO-BTC,XMO-USD,XMO-ETH,BERRY-BTC,BERRY-ETH,BERRY-USD,BSTN-BTC,BSTN-ETH,BSTN-USD,GBX-BTC,GBX-ETH,GBX-USD,SHIP-BTC,SHIP-ETH,LNC-BTC,UNC-BTC,UNC-ETH,RPX-BTC,RPX-ETH,RPX-USD,KIN-ETH,ARDR-USD,DAXT-BTC,DAXT-ETH,FOTA-ETH,FOTA-BTC,SETH-BTC,CVT-BTC,CVT-ETH,CVT-USD,STQ-USD,GNT-BTC,GNT-ETH,GNT-USD,ADH-BTC,ADH-ETH,BBC-BTC,BBC-ETH,GET-BTC,MITH-BTC,MITH-ETH,MITH-USD,SUNC-ETH,DADI-USD,BTX-USD,WIKI-BTC,WIKI-ETH,WIKI-USD,ICX-BTC,ICX-ETH,ICX-USD,VIO-ETH,NANO-BTC,NANO-ETH,NANO-USD,FTX-BTC,FTX-ETH,FREC-BTC,FREC-USD,FREC-ETH,ONT-BTC,ONT-ETH,ONT-USD,NAVI-BTC,NAVI-ETH,VME-ETH,LND-ETH,CSM-BTC,BTCP-BTC", "EnabledPairs": "BTC-USD", "BaseCurrencies": "USD", "AssetTypes": "SPOT", @@ -417,10 +433,11 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", - "AvailablePairs": "NAS-ETH,EOS-ETH,ZEC-USDT,EVX-BTC,MDS-ETH,SMT-ETH,TRX-ETH,THETA-USDT,LUN-ETH,SMT-USDT,BCH-BTC,LET-USDT,GNX-ETH,MTL-BTC,CHAT-ETH,QTUM-USDT,SNT-BTC,WPR-BTC,ELF-ETH,ZIL-BTC,UTK-BTC,SBTC-BTC,TNT-BTC,NEO-USDT,MCO-BTC,OST-ETH,BT2-BTC,HSR-ETH,TOPC-ETH,SALT-ETH,AIDOC-ETH,WAX-BTC,DTA-ETH,BTC-USDT,GAS-ETH,NEO-BTC,BTM-BTC,BLZ-ETH,BAT-ETH,APPC-BTC,CMT-BTC,ONT-BTC,QTUM-ETH,IOST-BTC,REQ-BTC,BTM-ETH,RUFF-BTC,ZEC-BTC,DGD-ETH,DAT-ETH,STK-ETH,HT-ETH,QUN-ETH,ELF-BTC,CMT-ETH,SNT-USDT,MDS-USDT,STORJ-USDT,WAX-ETH,POWR-BTC,SNC-BTC,VEN-USDT,TNB-ETH,SWFTC-ETH,EOS-BTC,LINK-ETH,HT-USDT,RDN-BTC,LUN-BTC,GNX-BTC,ELA-BTC,LET-ETH,IOST-USDT,EVX-ETH,ACT-ETH,BCH-USDT,ICX-ETH,BCX-BTC,MTN-ETH,PROPY-ETH,XRP-USDT,ICX-BTC,THETA-ETH,SNC-ETH,DBC-ETH,ITC-USDT,SMT-BTC,SRN-ETH,ETH-USDT,ITC-BTC,OMG-BTC,STK-BTC,MDS-BTC,TOPC-BTC,ADX-BTC,ETC-BTC,KNC-BTC,CVC-BTC,QSP-ETH,BTG-BTC,EDU-BTC,ZLA-ETH,MTX-ETH,EOS-USDT,RCN-BTC,UTK-ETH,RCN-ETH,GNT-USDT,APPC-ETH,WICC-BTC,YEE-BTC,YEE-ETH,OMG-USDT,LINK-BTC,XEM-USDT,HSR-USDT,DASH-BTC,QUN-BTC,QASH-ETH,DTA-BTC,AIDOC-BTC,DAT-BTC,RUFF-ETH,SALT-BTC,ELA-ETH,IOST-ETH,THETA-BTC,LET-BTC,DTA-USDT,SOC-ETH,ELA-USDT,ZIL-USDT,MANA-BTC,XRP-BTC,ONT-ETH,LTC-USDT,DBC-BTC,BCD-BTC,SWFTC-BTC,CVC-USDT,NAS-USDT,GNT-ETH,TRX-USDT,HT-BTC,ENG-ETH,PAY-ETH,CVC-ETH,TNB-BTC,MEE-ETH,POWR-ETH,BLZ-BTC,PAY-BTC,ADX-ETH,EKO-ETH,SRN-BTC,OCN-ETH,VEN-ETH,ABT-ETH,BIFI-BTC,ACT-BTC,ETC-USDT,OST-BTC,MCO-ETH,ABT-BTC,STORJ-BTC,VEN-BTC,GNT-BTC,LSK-BTC,EKO-BTC,LTC-BTC,OCN-BTC,RUFF-USDT,WPR-ETH,AST-BTC,DASH-USDT,DGD-BTC,ZIL-ETH,ZRX-BTC,ETH-BTC,REQ-ETH,TRX-BTC,WICC-ETH,NAS-BTC,MEE-BTC,ENG-BTC,LSK-ETH,RPX-BTC,TNT-ETH,MTX-BTC,SOC-BTC,EDU-ETH,HSR-BTC,QTUM-BTC,QSP-BTC,BAT-BTC,QASH-BTC,ITC-ETH,XEM-BTC,MANA-ETH,GAS-BTC,MTN-BTC,CHAT-BTC,BT1-BTC,ZLA-BTC,OMG-ETH,PROPY-BTC,RDN-ETH,ELF-USDT", + "AvailablePairs": "BTC-USDT,BCH-USDT,ETH-USDT,ETC-USDT,LTC-USDT,EOS-USDT,XRP-USDT,OMG-USDT,DASH-USDT,ZEC-USDT,ADA-USDT,STEEM-USDT,CTXC-USDT,ACT-USDT,BTM-USDT,BTS-USDT,ONT-USDT,IOST-USDT,HT-USDT,TRX-USDT,DTA-USDT,NEO-USDT,QTUM-USDT,ELA-USDT,VEN-USDT,THETA-USDT,SNT-USDT,ZIL-USDT,XEM-USDT,NAS-USDT,RUFF-USDT,HSR-USDT,LET-USDT,MDS-USDT,STORJ-USDT,ELF-USDT,ITC-USDT,CVC-USDT,GNT-USDT,BCH-BTC,ETH-BTC,LTC-BTC,ETC-BTC,EOS-BTC,OMG-BTC,XRP-BTC,DASH-BTC,ZEC-BTC,ADA-BTC,STEEM-BTC,BFT-BTC,BTM-BTC,ONT-BTC,IOST-BTC,HT-BTC,TRX-BTC,ELA-BTC,WICC-BTC,OCN-BTC,ZLA-BTC,ABT-BTC,MTX-BTC,NAS-BTC,VEN-BTC,DTA-BTC,NEO-BTC,WAX-BTC,BTS-BTC,ZIL-BTC,THETA-BTC,CTXC-BTC,SRN-BTC,XEM-BTC,EDU-BTC,ICX-BTC,DGD-BTC,CHAT-BTC,WPR-BTC,LUN-BTC,SWFTC-BTC,SNT-BTC,MEET-BTC,YEE-BTC,ELF-BTC,LET-BTC,QTUM-BTC,LSK-BTC,ITC-BTC,SOC-BTC,QASH-BTC,MDS-BTC,EKO-BTC,TOPC-BTC,MTN-BTC,ACT-BTC,HSR-BTC,STK-BTC,STORJ-BTC,GNX-BTC,DBC-BTC,SNC-BTC,CMT-BTC,TNB-BTC,RUFF-BTC,QUN-BTC,ZRX-BTC,KNC-BTC,BLZ-BTC,PROPY-BTC,RPX-BTC,APPC-BTC,AIDOC-BTC,POWR-BTC,CVC-BTC,PAY-BTC,QSP-BTC,DAT-BTC,RDN-BTC,MCO-BTC,RCN-BTC,MANA-BTC,UTK-BTC,TNT-BTC,GAS-BTC,BAT-BTC,OST-BTC,LINK-BTC,GNT-BTC,MTL-BTC,EVX-BTC,REQ-BTC,ADX-BTC,AST-BTC,ENG-BTC,SALT-BTC,BIFI-BTC,BCX-BTC,BCD-BTC,SBTC-BTC,BTG-BTC,EOS-ETH,OMG-ETH,ADA-ETH,STEEM-ETH,BFT-ETH,ZRX-ETH,AST-ETH,KNC-ETH,ONT-ETH,HT-ETH,BTM-ETH,IOST-ETH,ELA-ETH,TRX-ETH,ABT-ETH,NAS-ETH,OCN-ETH,WICC-ETH,ZIL-ETH,CTXC-ETH,ZLA-ETH,WPR-ETH,DTA-ETH,MTX-ETH,THETA-ETH,SRN-ETH,VEN-ETH,BTS-ETH,EDU-ETH,WAX-ETH,HSR-ETH,ICX-ETH,MTN-ETH,ACT-ETH,BLZ-ETH,QASH-ETH,RUFF-ETH,CMT-ETH,ELF-ETH,MEET-ETH,SOC-ETH,QTUM-ETH,ITC-ETH,SWFTC-ETH,YEE-ETH,LSK-ETH,LUN-ETH,LET-ETH,GNX-ETH,CHAT-ETH,EKO-ETH,TOPC-ETH,DGD-ETH,STK-ETH,MDS-ETH,DBC-ETH,SNC-ETH,PAY-ETH,QUN-ETH,AIDOC-ETH,TNB-ETH,APPC-ETH,RDN-ETH,UTK-ETH,POWR-ETH,BAT-ETH,PROPY-ETH,MANA-ETH,REQ-ETH,CVC-ETH,QSP-ETH,EVX-ETH,DAT-ETH,MCO-ETH,GNT-ETH,GAS-ETH,OST-ETH,LINK-ETH,RCN-ETH,TNT-ETH,ENG-ETH,SALT-ETH,ADX-ETH", "EnabledPairs": "BTC-USDT", "BaseCurrencies": "USD", "AssetTypes": "SPOT", @@ -440,6 +457,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -464,6 +482,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -488,6 +507,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -511,10 +531,11 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", - "AvailablePairs": "TAAS_BTC,ETH_BTC,ADX_USDT,PAY_USDT,VEN_BTC,KNC_ETH,RLC_BTC,ZRX_USDT,NEU_USDT,PTOY_BTC,PRO_ETH,SRN_USDT,DASH_ETH,WINGS_USDT,MGO_BTC,REP_ETH,SAN_USDT,SALT_USDT,LDC_BTC,EDG_USDT,ADX_BTC,ADX_ETH,PAY_BTC,SAN_ETH,PAY_ETH,REN_USDT,WAVES_USDT,ANT_USDT,DNT_BTC,GNT_USDT,GUP_BTC,DNT_USDT,BMC_ETH,STORJ_USDT,SRN_ETH,MLN_USDT,PTOY_ETH,TNT_USDT,AST_USDT,CFI_BTC,MCO_BTC,OMG_ETH,LTC_BTC,QRL_ETH,MYST_USDT,STORJ_ETH,AE_BTC,AE_ETH,BNT_USDT,CVC_BTC,WPR_USDT,NET_USDT,REN_ETH,MGO_USDT,DNT_ETH,TRX_USDT,ENG_ETH,ICN_ETH,WINGS_ETH,TKN_ETH,TIME_ETH,TKN_BTC,PTOY_USDT,CFI_ETH,WINGS_BTC,ANT_BTC,REQ_ETH,AION_BTC,BNT_ETH,TRX_BTC,AST_ETH,RLC_ETH,NET_BTC,BMC_USDT,TRX_ETH,CFI_USDT,SAN_BTC,CVC_ETH,SNM_USDT,XID_BTC,DGD_BTC,TNT_BTC,PRO_USDT,NEU_ETH,WAVES_BTC,DASH_USDT,SNM_BTC,NET_ETH,DGD_ETH,REN_BTC,TRST_ETH,OAX_BTC,INS_ETH,AION_USDT,LDC_ETH,GNT_ETH,RLC_USDT,SNGLS_ETH,BMC_BTC,BAT_BTC,QRL_USDT,MANA_BTC,STX_BTC,IND_USDT,BAT_ETH,OAX_ETH,KNC_USDT,INS_BTC,LDC_USDT,MLN_BTC,GNO_USDT,CVC_USDT,MCO_ETH,SNT_USDT,STX_ETH,REQ_BTC,TRST_USDT,GUP_ETH,QRL_BTC,IND_BTC,ICN_USDT,SNGLS_BTC,SNT_BTC,ENG_USDT,TRST_BTC,GNO_ETH,OMG_USDT,AE_USDT,MLN_ETH,SNGLS_USDT,XID_USDT,ANT_ETH,EOS_USDT,MANA_ETH,AST_BTC,EOS_BTC,DGD_USDT,TAAS_ETH,SNT_ETH,XID_ETH,ZRX_BTC,KNC_BTC,GNT_BTC,ETH_USDT,REP_BTC,MGO_ETH,STX_USDT,STORJ_BTC,SRN_BTC,INS_USDT,PRO_BTC,IND_ETH,ENG_BTC,WAVES_ETH,EDG_ETH,LTC_USDT,TIME_USDT,EDG_BTC,VEN_USDT,BAT_USDT,MYST_ETH,MCO_USDT,REQ_USDT,NEU_BTC,WPR_BTC,GUP_USDT,MYST_BTC,SNM_ETH,SALT_BTC,BCC_BTC,TNT_ETH,AION_ETH,VEN_ETH,LTC_ETH,REP_USDT,TAAS_USDT,TKN_USDT,OMG_BTC,OAX_USDT,MANA_USDT,DASH_BTC,GNO_BTC,BTC_USDT,EOS_ETH,ZRX_ETH,TIME_BTC,BCC_ETH,WPR_ETH,BNT_BTC,BCC_USDT,SALT_ETH,ICN_BTC", + "AvailablePairs": "INS_USDT,SNM_BTC,PRO_ETH,CFI_BTC,REQ_BTC,SNT_ETH,TAAS_USDT,ANT_BTC,QRL_USDT,ENG_BTC,SRN_BTC,EDG_USDT,RLC_BTC,GUP_USDT,CVC_ETH,PAY_BTC,DNT_ETH,SALT_ETH,TRX_BTC,TRX_USDT,RLC_USDT,ANT_USDT,BNT_ETH,WPR_USDT,GNT_BTC,OMG_BTC,LTC_USDT,REP_ETH,KNC_USDT,REN_BTC,MLN_ETH,TRST_ETH,EOS_ETH,ENG_USDT,BAT_USDT,SNGLS_ETH,VEN_BTC,IND_USDT,LDC_ETH,AE_USDT,MANA_BTC,RLC_ETH,MCO_USDT,PTOY_ETH,KNC_BTC,GNT_ETH,MGO_ETH,EDG_ETH,ADX_ETH,ICN_USDT,OAX_BTC,STX_ETH,BMC_ETH,EDG_BTC,ZRX_USDT,TNT_ETH,AST_ETH,CVC_BTC,MANA_ETH,IND_BTC,MCO_BTC,NEU_BTC,LTC_BTC,DASH_ETH,MLN_USDT,OAX_ETH,AION_ETH,GNO_BTC,PTOY_BTC,MCO_ETH,SNM_ETH,DGD_BTC,ENJ_ETH,TRST_USDT,CFI_USDT,SAN_ETH,SAN_USDT,ETH_BTC,VEN_ETH,NEU_ETH,ICN_BTC,WAVES_USDT,BCC_ETH,BCC_BTC,BMC_USDT,IND_ETH,TIME_BTC,ADX_BTC,DNT_BTC,PRO_BTC,SALT_BTC,DASH_USDT,TNT_BTC,LDC_USDT,WINGS_USDT,ZRX_BTC,MANA_USDT,BNT_USDT,WPR_BTC,OMG_ETH,TKN_ETH,MGO_BTC,QRL_BTC,STX_USDT,ENJ_BTC,WAVES_ETH,ETH_USDT,TKN_USDT,REP_USDT,TAAS_BTC,WPR_ETH,TIME_USDT,BNT_BTC,NET_ETH,DNT_USDT,WAVES_BTC,MGO_USDT,REQ_ETH,TIME_ETH,ADX_USDT,XID_USDT,TRST_BTC,STORJ_ETH,DASH_BTC,GUP_BTC,PRO_USDT,AST_USDT,BAT_ETH,NET_BTC,OAX_USDT,ENG_ETH,SRN_USDT,NET_USDT,REQ_USDT,ENJ_USDT,EOS_USDT,DGD_ETH,BMC_BTC,SALT_USDT,INS_BTC,BAT_BTC,TNT_USDT,LDC_BTC,SNT_USDT,OMG_USDT,DGD_USDT,TKN_BTC,WINGS_ETH,AST_BTC,TAAS_ETH,MYST_ETH,REP_BTC,TRX_ETH,AION_BTC,SNT_BTC,STORJ_USDT,AE_BTC,SRN_ETH,PAY_USDT,REN_USDT,CFI_ETH,BCC_USDT,REN_ETH,MYST_USDT,SNGLS_BTC,AE_ETH,MLN_BTC,BTC_USDT,ANT_ETH,SNM_USDT,STX_BTC,MYST_BTC,SNGLS_USDT,GNT_USDT,EOS_BTC,INS_ETH,GUP_ETH,XID_ETH,KNC_ETH,QRL_ETH,SAN_BTC,WINGS_BTC,LTC_ETH,ICN_ETH,CVC_USDT,AION_USDT,GNO_ETH,STORJ_BTC,XID_BTC,GNO_USDT,VEN_USDT,ZRX_ETH,NEU_USDT,PTOY_USDT,PAY_ETH", "EnabledPairs": "ETH_BTC,LTC_BTC,DASH_BTC", "BaseCurrencies": "USD", "AssetTypes": "SPOT", @@ -536,6 +557,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -559,6 +581,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -583,6 +606,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -607,6 +631,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -632,6 +657,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", @@ -656,6 +682,7 @@ "Websocket": false, "UseSandbox": false, "RESTPollingDelay": 10, + "HTTPTimeout": 15000000000, "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", diff --git a/tools/exchange_template/wrapper_file.tmpl b/tools/exchange_template/wrapper_file.tmpl index 7e0a4411..6ebf983c 100644 --- a/tools/exchange_template/wrapper_file.tmpl +++ b/tools/exchange_template/wrapper_file.tmpl @@ -13,8 +13,12 @@ import ( ) // Start starts the {{.CapitalName}} go routine -func ({{.Variable}} *{{.CapitalName}}) Start() { - go {{.Variable}}.Run() +func ({{.Variable}} *{{.CapitalName}}) Start(wg *sync.WaitGroup) { + wg.Add(1) + go func() { + {{.Variable}}.Run() + wg.Done() + }() } // Run implements the {{.CapitalName}} wrapper