From 833a8665064e9fa266eb9ad2633bbbb7219b6b93 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 2 Sep 2019 09:58:23 +1000 Subject: [PATCH 1/2] (Engine) Logger - fixes issues with multiwriter go routine (#347) * Fixes goroutine leak and comments * passed error down from newLogEvent to each sub logger to handle and display * Update loggers.go --- logger/logger.go | 8 ++++++-- logger/logger_multiwriter.go | 11 +++++------ logger/logger_rotate.go | 8 ++++---- logger/logger_setup.go | 1 - logger/logger_types.go | 14 ++++++++++---- logger/loggers.go | 23 +++++++++++++++-------- logger/sublogger_types.go | 1 + 7 files changed, 41 insertions(+), 25 deletions(-) diff --git a/logger/logger.go b/logger/logger.go index 7dfdc323..a7a43de3 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -22,6 +22,7 @@ func (l *Logger) newLogEvent(data, header string, w io.Writer) error { if w == nil { return errors.New("io.Writer not set") } + e := eventPool.Get().(*LogEvent) e.output = w e.data = append(e.data, []byte(header)...) @@ -34,11 +35,12 @@ func (l *Logger) newLogEvent(data, header string, w io.Writer) error { if data == "" || data[len(data)-1] != '\n' { e.data = append(e.data, '\n') } - e.output.Write(e.data) + _, err := e.output.Write(e.data) + e.data = e.data[:0] eventPool.Put(e) - return nil + return err } // CloseLogger is called on shutdown of application @@ -58,6 +60,7 @@ func validSubLogger(s string) (bool, *subLogger) { return false, nil } +// Level retries the current sublogger levels func Level(s string) (*Levels, error) { found, logger := validSubLogger(s) if !found { @@ -67,6 +70,7 @@ func Level(s string) (*Levels, error) { return &logger.Levels, nil } +// SetLevel sets sublogger levels func SetLevel(s, level string) (*Levels, error) { found, logger := validSubLogger(s) if !found { diff --git a/logger/logger_multiwriter.go b/logger/logger_multiwriter.go index af07cfff..2d59fe62 100644 --- a/logger/logger_multiwriter.go +++ b/logger/logger_multiwriter.go @@ -11,7 +11,7 @@ func (mw *multiWriter) Add(writer io.Writer) { mw.mu.Unlock() } -// Remove removes exisiting writer from multiwriter slice +// Remove removes existing writer from multiwriter slice func (mw *multiWriter) Remove(writer io.Writer) { mw.mu.Lock() @@ -37,8 +37,10 @@ func (mw *multiWriter) Write(p []byte) (n int, err error) { n int err error } + mw.mu.RLock() + defer mw.mu.RUnlock() - results := make(chan data) + results := make(chan data, len(mw.writers)) for _, wr := range mw.writers { go func(w io.Writer, p []byte, ch chan data) { @@ -51,10 +53,7 @@ func (mw *multiWriter) Write(p []byte) (n int, err error) { ch <- data{n, io.ErrShortWrite} return } - select { - case ch <- data{n, nil}: - default: - } + ch <- data{n, nil} }(wr, p, results) } diff --git a/logger/logger_rotate.go b/logger/logger_rotate.go index dc4a0f3d..985c4c70 100644 --- a/logger/logger_rotate.go +++ b/logger/logger_rotate.go @@ -72,13 +72,12 @@ func (r *Rotate) openOrCreateFile(n int64) error { func (r *Rotate) openNew() error { name := filepath.Join(LogPath, r.FileName) - - t := time.Now() - timestamp := t.Format("2006-01-02T15-04-05") - newName := filepath.Join(LogPath, timestamp+"-"+r.FileName) _, err := os.Stat(name) if err == nil { + timestamp := time.Now().Format("2006-01-02T15-04-05") + newName := filepath.Join(LogPath, timestamp+"-"+r.FileName) + err = os.Rename(name, newName) if err != nil { return fmt.Errorf("can't rename log file: %s", err) @@ -105,6 +104,7 @@ func (r *Rotate) close() (err error) { return err } +// Close handler for open file func (r *Rotate) Close() error { r.mu.Lock() defer r.mu.Unlock() diff --git a/logger/logger_setup.go b/logger/logger_setup.go index bf391b3c..14ac5a07 100644 --- a/logger/logger_setup.go +++ b/logger/logger_setup.go @@ -27,7 +27,6 @@ func getWriters(s *SubLoggerConfig) io.Writer { m.Add(ioutil.Discard) } } - return m } diff --git a/logger/logger_types.go b/logger/logger_types.go index 5ec424da..a9608744 100644 --- a/logger/logger_types.go +++ b/logger/logger_types.go @@ -51,6 +51,7 @@ type Logger struct { Spacer string } +// Levels flags for each sub logger type type Levels struct { Info, Debug, Warn, Error bool } @@ -61,6 +62,7 @@ type subLogger struct { output io.Writer } +// LogEvent holds the data sent to the log and which multiwriter to send to type LogEvent struct { data []byte output io.Writer @@ -68,14 +70,17 @@ type LogEvent struct { type multiWriter struct { writers []io.Writer - mu sync.Mutex + mu sync.RWMutex } var ( - logger = &Logger{} + logger = &Logger{} + // FileLoggingConfiguredCorrectly flag set during config check if file logging meets requirements FileLoggingConfiguredCorrectly bool - GlobalLogConfig = &Config{} // GlobalLogConfig hold global configuration options for logger - GlobalLogFile = &Rotate{} + // GlobalLogConfig holds global configuration options for logger + GlobalLogConfig = &Config{} + // GlobalLogFile hold global configuration options for file logger + GlobalLogFile = &Rotate{} eventPool = &sync.Pool{ New: func() interface{} { @@ -85,5 +90,6 @@ var ( }, } + // LogPath system path to store log files in LogPath string ) diff --git a/logger/loggers.go b/logger/loggers.go index e8fdfb45..4173b437 100644 --- a/logger/loggers.go +++ b/logger/loggers.go @@ -2,6 +2,7 @@ package logger import ( "fmt" + "log" ) // Info takes a pointer subLogger struct and string sends to newLogEvent @@ -14,7 +15,7 @@ func Info(sl *subLogger, data string) { return } - logger.newLogEvent(data, logger.InfoHeader, sl.output) + displayError(logger.newLogEvent(data, logger.InfoHeader, sl.output)) } // Infoln takes a pointer subLogger struct and interface sends to newLogEvent @@ -27,7 +28,7 @@ func Infoln(sl *subLogger, v ...interface{}) { return } - logger.newLogEvent(fmt.Sprintln(v...), logger.InfoHeader, sl.output) + displayError(logger.newLogEvent(fmt.Sprintln(v...), logger.InfoHeader, sl.output)) } // Infof takes a pointer subLogger struct, string & interface formats and sends to Info() @@ -53,7 +54,7 @@ func Debug(sl *subLogger, data string) { return } - logger.newLogEvent(data, logger.DebugHeader, sl.output) + displayError(logger.newLogEvent(data, logger.DebugHeader, sl.output)) } // Debugln takes a pointer subLogger struct, string and interface sends to newLogEvent @@ -66,7 +67,7 @@ func Debugln(sl *subLogger, v ...interface{}) { return } - logger.newLogEvent(fmt.Sprintln(v...), logger.DebugHeader, sl.output) + displayError(logger.newLogEvent(fmt.Sprintln(v...), logger.DebugHeader, sl.output)) } // Debugf takes a pointer subLogger struct, string & interface formats and sends to Info() @@ -92,7 +93,7 @@ func Warn(sl *subLogger, data string) { return } - logger.newLogEvent(data, logger.WarnHeader, sl.output) + displayError(logger.newLogEvent(data, logger.WarnHeader, sl.output)) } // Warnln takes a pointer subLogger struct & interface formats and sends to newLogEvent() @@ -105,7 +106,7 @@ func Warnln(sl *subLogger, v ...interface{}) { return } - logger.newLogEvent(fmt.Sprintln(v...), logger.WarnHeader, sl.output) + displayError(logger.newLogEvent(fmt.Sprintln(v...), logger.WarnHeader, sl.output)) } // Warnf takes a pointer subLogger struct, string & interface formats and sends to Warn() @@ -131,7 +132,7 @@ func Error(sl *subLogger, data ...interface{}) { return } - logger.newLogEvent(fmt.Sprint(data...), logger.ErrorHeader, sl.output) + displayError(logger.newLogEvent(fmt.Sprint(data...), logger.ErrorHeader, sl.output)) } // Errorln takes a pointer subLogger struct, string & interface formats and sends to newLogEvent() @@ -144,7 +145,7 @@ func Errorln(sl *subLogger, v ...interface{}) { return } - logger.newLogEvent(fmt.Sprintln(v...), logger.ErrorHeader, sl.output) + displayError(logger.newLogEvent(fmt.Sprintln(v...), logger.ErrorHeader, sl.output)) } // Errorf takes a pointer subLogger struct, string & interface formats and sends to Debug() @@ -159,3 +160,9 @@ func Errorf(sl *subLogger, data string, v ...interface{}) { Error(sl, fmt.Sprintf(data, v...)) } + +func displayError(err error) { + if err != nil { + log.Printf("Logger write error: %v\n", err) + } +} diff --git a/logger/sublogger_types.go b/logger/sublogger_types.go index 3a406ae2..362e34d5 100644 --- a/logger/sublogger_types.go +++ b/logger/sublogger_types.go @@ -1,5 +1,6 @@ package logger +//nolint var ( subLoggers = map[string]*subLogger{} From 74d5bca03be4f5a2398e517511dbb7001f96adb2 Mon Sep 17 00:00:00 2001 From: Scott Date: Mon, 2 Sep 2019 18:05:09 +1000 Subject: [PATCH 2/2] engine: Ticker REST/Websocket improvements (#345) * Adds extra properties to Websocket ticker. Adds new properties to binance and bitfinex, but doesn't test anything yet. Changes names and properties of ticker package to make more streamlined * Adds support for coinbasepro, coinut, gateio, gitbtc, huobi, hadax, kraken, okex, okcoin. Adds quoteVolume * Adds poloniex and ZB ticker datas * Updates ANX, Binance, Bitfinex, Bistamp, Bittrex, BTCMarkets, BTSE, CoinbasePRo, Coinut, Exmo tickers. It looks like a whole bunch of stuff is wrong in how tickers are done though :/ * Updates tickers everywhere. Will revert batch ones * Re-Preseves ticker batching * Minor fixes to ticks and removal of comment * Logging errors instead of returning mid loop. Adds bitfinex batch ticker processing. Fixes unrelated okgroup wallet bug * Removes bad code I wrote preventing function from running if feature not enabled * Fixes issue with bitmex and rebase issues * Fixes bitmex iterator error, splits hitbtc ticker requests * Fixes okgroup currency pair formatting. Updates okgroup to use ticker batching. Fixes okgroup ticker issues due to assetTypes. Fixes okgroup ws pinging. Fixes Kraken's currency pairs formatting. Reverts ANXs auto parsing back to strings because ANX json makes me cry. Minor property improvements for coinut, coinbasepro, btse, exmo. Protects wshandler manageSubscriptions() from running and returning an error when feature not supported * Updates config example to reflect the underscore dash situation * Fixes a config delimiter oopsie. Simplifies ANX wrapper ticker parsing. Fixes bittrex date parsing. Simplifies okcoin switch to if. * Fixes super fun issue where kraken has updated their currency pair format and must add new delimiter support. Fixes super fun issue where okex has updated their currency pair format and must add new delimiter support. Fixes super fun issue where okcoin has updated their currency pair format and must add new delimiter support. * Updates config example for kraken * Adds lbank batch ticker support. Adds details to errors * Updates FetchTradablePairs to use the config delimiter to prevent issues if the delimiter ever changes * Fixes nil reference bug. Uses NAme not GetNAme * Fixes hardcoded delimiter in Binance. Expands bitfinex websocket ticker fields. Updates Bitstamp rate limits. Expands BTSE ticker data fields. Fixes typo in coibasepro. Expands Coinut ticker data. Renames currency to curr as it conflicts with package name. Expands GateIO websocket ticker data. Fixes ticker data implementation for huobi and huobi hadax. Reverts ticker map to string instead of assetType. Fixes real stupid bug I introduced which preveted subscriptions from running :glitch_crab: Adds Price ATH to ws ticker type. Adds quotevolume to yobit ticker. Uses delimiter in ZB rather than hardcoded field. * Fixes bug in syncer where if the websocket is already connected, then UsingWebsocket is not set * Updates broken tests * Simplifies poloniex frozen check * Updates configtest.json with new delimiters * Renames shorthand properties of structs. Fixes delimiter referencing * Fixes some bugs and nits around variable declaration, currency pairs and config upscaling * Adds config upgrade path for okcoin and okex. Reverts configtest.json. * Fixes okex futures currency formatting by no longer using global currency format. updates Run code to adapt. Removes BTSE value * Adds ":" as a delimiter for when a delimiter only shows up SOMETIMES * Adds support for optional delimiter --- config_example.json | 20 +- currency/pair.go | 2 +- engine/routines.go | 19 +- engine/syncer.go | 2 + exchanges/anx/anx_types.go | 20 +- exchanges/anx/anx_wrapper.go | 73 ++---- exchanges/binance/binance_types.go | 84 +++---- exchanges/binance/binance_websocket.go | 27 ++- exchanges/binance/binance_wrapper.go | 43 ++-- exchanges/bitfinex/bitfinex_test.go | 14 ++ exchanges/bitfinex/bitfinex_types.go | 3 + exchanges/bitfinex/bitfinex_websocket.go | 17 +- exchanges/bitfinex/bitfinex_wrapper.go | 54 +++-- exchanges/bitflyer/bitflyer_wrapper.go | 4 +- exchanges/bithumb/bithumb_wrapper.go | 27 ++- exchanges/bitmex/bitmex_types.go | 209 +++++++++--------- exchanges/bitmex/bitmex_wrapper.go | 43 ++-- exchanges/bitstamp/bitstamp.go | 4 +- exchanges/bitstamp/bitstamp_wrapper.go | 21 +- exchanges/bittrex/bittrex_types.go | 4 +- exchanges/bittrex/bittrex_wrapper.go | 37 ++-- exchanges/btcmarkets/btcmarkets_types.go | 17 +- exchanges/btcmarkets/btcmarkets_wrapper.go | 17 +- exchanges/btse/btse_types.go | 12 +- exchanges/btse/btse_websocket.go | 12 +- exchanges/btse/btse_wrapper.go | 1 + exchanges/coinbasepro/coinbasepro_types.go | 47 ++-- .../coinbasepro/coinbasepro_websocket.go | 21 +- exchanges/coinbasepro/coinbasepro_wrapper.go | 18 +- exchanges/coinut/coinut_types.go | 29 ++- exchanges/coinut/coinut_websocket.go | 39 ++-- exchanges/coinut/coinut_wrapper.go | 19 +- exchanges/exmo/exmo.go | 3 +- exchanges/exmo/exmo_test.go | 2 +- exchanges/exmo/exmo_wrapper.go | 43 ++-- exchanges/gateio/gateio_types.go | 18 +- exchanges/gateio/gateio_websocket.go | 21 +- exchanges/gateio/gateio_wrapper.go | 34 +-- exchanges/gemini/gemini.go | 46 +--- exchanges/gemini/gemini_types.go | 15 ++ exchanges/gemini/gemini_wrapper.go | 15 +- exchanges/hitbtc/hitbtc.go | 66 +----- exchanges/hitbtc/hitbtc_test.go | 14 ++ exchanges/hitbtc/hitbtc_types.go | 54 ++--- exchanges/hitbtc/hitbtc_websocket.go | 20 +- exchanges/hitbtc/hitbtc_wrapper.go | 44 ++-- exchanges/huobi/huobi.go | 8 + exchanges/huobi/huobi_test.go | 7 + exchanges/huobi/huobi_types.go | 35 ++- exchanges/huobi/huobi_websocket.go | 33 ++- exchanges/huobi/huobi_wrapper.go | 43 ++-- exchanges/huobihadax/huobihadax.go | 8 + exchanges/huobihadax/huobihadax_test.go | 7 + exchanges/huobihadax/huobihadax_types.go | 35 ++- exchanges/huobihadax/huobihadax_websocket.go | 25 ++- exchanges/huobihadax/huobihadax_wrapper.go | 43 ++-- exchanges/itbit/itbit_types.go | 36 +-- exchanges/itbit/itbit_wrapper.go | 20 +- exchanges/kraken/kraken.go | 18 +- exchanges/kraken/kraken_test.go | 2 +- exchanges/kraken/kraken_types.go | 156 ++++++------- exchanges/kraken/kraken_websocket.go | 24 +- exchanges/kraken/kraken_wrapper.go | 83 ++++--- exchanges/lakebtc/lakebtc_websocket.go | 11 +- exchanges/lakebtc/lakebtc_wrapper.go | 26 ++- exchanges/lbank/lbank.go | 9 + exchanges/lbank/lbank_test.go | 11 + exchanges/lbank/lbank_types.go | 8 +- exchanges/lbank/lbank_wrapper.go | 32 ++- .../localbitcoins/localbitcoins_types.go | 3 +- .../localbitcoins/localbitcoins_wrapper.go | 16 +- exchanges/okcoin/okcoin_wrapper.go | 79 ++++++- exchanges/okex/okex_wrapper.go | 162 +++++++++++++- exchanges/okgroup/okgroup_types.go | 68 +++--- exchanges/okgroup/okgroup_websocket.go | 22 +- exchanges/okgroup/okgroup_wrapper.go | 33 +-- exchanges/poloniex/poloniex_websocket.go | 26 +-- exchanges/poloniex/poloniex_wrapper.go | 6 +- exchanges/ticker/ticker.go | 11 +- exchanges/websocket/wshandler/wshandler.go | 4 +- .../websocket/wshandler/wshandler_types.go | 23 +- exchanges/yobit/yobit_wrapper.go | 20 +- exchanges/zb/zb_types.go | 12 +- exchanges/zb/zb_websocket.go | 18 +- exchanges/zb/zb_wrapper.go | 22 +- testdata/http_mock/gemini/gemini.json | 58 +++++ 86 files changed, 1575 insertions(+), 1042 deletions(-) diff --git a/config_example.json b/config_example.json index 143156ae..f7b336ce 100644 --- a/config_example.json +++ b/config_example.json @@ -1211,18 +1211,18 @@ "apiUrlSecondary": "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API", "proxyAddress": "", "websocketUrl": "NON_DEFAULT_HTTP_LINK_TO_WEBSOCKET_EXCHANGE_API", - "availablePairs": "BTC_USD,LTC_USD,ETH_USD,ETC_USD,TUSD_USD,USDT_USD,ZEC_USD,ADA_USD,XLM_USD,ZRX_USD,XRP_USD,BAT_USD,PAX_USD,GUSD_USD,USDC_USD,BCH_USD,BSV_USD,TRX_USD,GRIN_USD,DCR_USD,EOS_USD,BTC_TUSD,BTC_USDT,BTC_PAX,BTC_GUSD,BTC_USDC", - "enabledPairs": "BTC_USD", + "availablePairs": "BTC-USD,LTC-USD,ETH-USD,ETC-USD,TUSD-USD,USDT-USD,ZEC-USD,ADA-USD,XLM-USD,ZRX-USD,XRP-USD,BAT-USD,PAX-USD,GUSD-USD,USDC-USD,BCH-USD,BSV-USD,TRX-USD,GRIN-USD,DCR-USD,EOS-USD,BTC-TUSD,BTC-USDT,BTC-PAX,BTC-GUSD,BTC-USDC", + "enabledPairs": "BTC-USD", "baseCurrencies": "USD", "assetTypes": "SPOT", "supportsAutoPairUpdates": true, "configCurrencyPairFormat": { "uppercase": true, - "delimiter": "_" + "delimiter": "-" }, "requestCurrencyPairFormat": { - "uppercase": false, - "delimiter": "_" + "uppercase": true, + "delimiter": "-" }, "bankAccounts": [ { @@ -1256,18 +1256,18 @@ "apiUrlSecondary": "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API", "proxyAddress": "", "websocketUrl": "NON_DEFAULT_HTTP_LINK_TO_WEBSOCKET_EXCHANGE_API", - "availablePairs": "DASH_BTC,CTXC_BTC,ZIL_BTC,YOU_BTC,LBA_BTC,LSK_BTC,CAI_BTC,AE_BTC,SC_BTC,KAN_BTC,WIN_BTC,DCR_BTC,WAVES_BTC,ORS_BTC,NXT_BTC,ARDR_BTC,XAS_BTC,CVT_BTC,EGT_BTC,ZCO_BTC,LET_BTC,HPB_BTC,ADA_BTC,HYC_BTC,VITE_BTC,ABL_BTC,PAX_BTC,TUSD_BTC,USDC_BTC,GUSD_BTC,BCH_BTC,BSV_BTC,BTT_BTC,ATOM_BTC,BLOC_BTC,XRP_BTC,LRC_BTC,NULS_BTC,MCO_BTC,ELF_BTC,ZEC_BTC,CMT_BTC,ITC_BTC,SBTC_BTC,EDO_BTC,BCX_BTC,NEO_BTC,GAS_BTC,HC_BTC,QTUM_BTC,IOTA_BTC,XUC_BTC,EOS_BTC,SNT_BTC,OMG_BTC,LTC_BTC,ETH_BTC,ETC_BTC,BCD_BTC,BTG_BTC,ACT_BTC,PAY_BTC,BTM_BTC,DGD_BTC,GNT_BTC,LINK_BTC,WTC_BTC,ZRX_BTC,BNT_BTC,CVC_BTC,MANA_BTC,KNC_BTC,GNX_BTC,ICX_BTC,XEM_BTC,ARK_BTC,YOYO_BTC,FUN_BTC,ACE_BTC,TRX_BTC,DGB_BTC,SWFTC_BTC,XMR_BTC,XLM_BTC,KCASH_BTC,MDT_BTC,NAS_BTC,UGC_BTC,DPY_BTC,SSC_BTC,AAC_BTC,VIB_BTC,QUN_BTC,INT_BTC,IOST_BTC,INS_BTC,MOF_BTC,TCT_BTC,STC_BTC,THETA_BTC,PST_BTC,SNC_BTC,MKR_BTC,LIGHT_BTC,TRUE_BTC,OF_BTC,SOC_BTC,ZEN_BTC,HMC_BTC,ZIP_BTC,NANO_BTC,CIC_BTC,GTO_BTC,CHAT_BTC,INSUR_BTC,R_BTC,BEC_BTC,MITH_BTC,ABT_BTC,BKX_BTC,RFR_BTC,TRIO_BTC,DADI_BTC,ONT_BTC,OKB_BTC,CTXC_ETH,ZIL_ETH,YOU_ETH,LBA_ETH,LSK_ETH,CAI_ETH,SC_ETH,AE_ETH,KAN_ETH,WIN_ETH,DCR_ETH,WAVES_ETH,ORS_ETH,MVP_ETH,EGT_ETH,ZCO_ETH,LET_ETH,HPB_ETH,SDA_ETH,ADA_ETH,HYC_ETH,VITE_ETH,ABL_ETH,BTT_ETH,ATOM_ETH,ELF_ETH,LTC_ETH,CMT_ETH,ITC_ETH,PRA_ETH,EDO_ETH,LRC_ETH,NULS_ETH,MCO_ETH,STORJ_ETH,SNT_ETH,PAY_ETH,DGD_ETH,GNT_ETH,ACT_ETH,BTM_ETH,EOS_ETH,OMG_ETH,DASH_ETH,XRP_ETH,ZEC_ETH,NEO_ETH,GAS_ETH,HC_ETH,QTUM_ETH,IOTA_ETH,XUC_ETH,ETC_ETH,LINK_ETH,WTC_ETH,ZRX_ETH,BNT_ETH,CVC_ETH,MANA_ETH,GNX_ETH,ICX_ETH,XEM_ETH,ARK_ETH,YOYO_ETH,TRX_ETH,DGB_ETH,PPT_ETH,SWFTC_ETH,XMR_ETH,XLM_ETH,KCASH_ETH,MDT_ETH,NAS_ETH,RNT_ETH,UGC_ETH,DPY_ETH,SSC_ETH,AAC_ETH,FAIR_ETH,RCT_ETH,VIB_ETH,TOPC_ETH,QUN_ETH,INT_ETH,IOST_ETH,INS_ETH,MOF_ETH,REF_ETH,THETA_ETH,PST_ETH,SNC_ETH,MKR_ETH,LIGHT_ETH,TRUE_ETH,OF_ETH,SOC_ETH,ZEN_ETH,HMC_ETH,ZIP_ETH,NANO_ETH,CIC_ETH,GTO_ETH,INSUR_ETH,R_ETH,UCT_ETH,BEC_ETH,MITH_ETH,ABT_ETH,BKX_ETH,AUTO_ETH,RFR_ETH,TRIO_ETH,TRA_ETH,DADI_ETH,ONT_ETH,OKB_ETH,CTXC_USDT,ZIL_USDT,YOU_OKB,YOU_USDT,LBA_OKB,LBA_USDT,CAI_OKB,LSK_USDT,CAI_USDT,AE_OKB,SC_OKB,KAN_OKB,WIN_OKB,SC_USDT,AE_USDT,KAN_USDT,WIN_USDT,ORS_OKB,DCR_OKB,DCR_USDT,WAVES_OKB,WAVES_USDT,ORS_USDT,MVP_USDT,NAS_OKB,XAS_OKB,ZCO_OKB,EGT_OKB,XAS_USDT,CVT_USDT,EGT_USDT,LET_OKB,LET_USDT,HPB_OKB,HPB_USDT,SDA_OKB,ADA_OKB,ADA_USDT,HYC_USDT,VITE_OKB,TRX_OKB,PAX_USDT,TUSD_USDT,USDC_USDT,GUSD_USDT,BCH_USDT,BSV_USDT,BTT_USDT,BLOC_OKB,BLOC_USDT,ATOM_USDT,ELF_USDT,DASH_USDT,LRC_USDT,NULS_USDT,MCO_USDT,BTG_USDT,DASH_OKB,XRP_USDT,ZEC_USDT,NEO_USDT,GAS_USDT,HC_USDT,QTUM_USDT,IOTA_USDT,BTC_USDT,BCD_USDT,XUC_USDT,CMT_USDT,ITC_USDT,PRA_USDT,EDO_USDT,ETH_USDT,LTC_USDT,ETC_USDT,EOS_USDT,OMG_USDT,ACT_USDT,BTM_USDT,STORJ_USDT,PAY_USDT,DGD_USDT,GNT_USDT,SNT_USDT,LINK_USDT,WTC_USDT,ZRX_USDT,BNT_USDT,CVC_USDT,MANA_USDT,KNC_USDT,ICX_USDT,XEM_USDT,ARK_USDT,YOYO_USDT,AST_USDT,TRX_USDT,MDA_USDT,DGB_USDT,PPT_USDT,SWFTC_USDT,XMR_USDT,XLM_USDT,KCASH_USDT,MDT_USDT,NAS_USDT,RNT_USDT,UGC_USDT,DPY_USDT,SSC_USDT,AAC_USDT,FAIR_USDT,UBTC_USDT,SHOW_USDT,VIB_USDT,MOT_USDT,UTK_USDT,TOPC_USDT,QUN_USDT,INT_USDT,IPC_USDT,IOST_USDT,INS_USDT,YEE_USDT,MOF_USDT,TCT_USDT,STC_USDT,THETA_USDT,PST_USDT,MKR_USDT,LIGHT_USDT,TRUE_USDT,OF_USDT,SOC_USDT,ZEN_USDT,HMC_USDT,ZIP_USDT,NANO_USDT,CIC_USDT,GTO_USDT,CHAT_USDT,INSUR_USDT,R_USDT,BEC_USDT,MITH_USDT,ABT_USDT,BKX_USDT,RFR_USDT,TRIO_USDT,DADI_USDT,ONT_USDT,OKB_USDT,NEO_OKB,LTC_OKB,ETC_OKB,XRP_OKB,ZEC_OKB,QTUM_OKB,IOTA_OKB,EOS_OKB", - "enabledPairs": "eos_usdt", + "availablePairs": "DASH-BTC,CTXC-BTC,ZIL-BTC,YOU-BTC,LBA-BTC,LSK-BTC,CAI-BTC,AE-BTC,SC-BTC,KAN-BTC,WIN-BTC,DCR-BTC,WAVES-BTC,ORS-BTC,NXT-BTC,ARDR-BTC,XAS-BTC,CVT-BTC,EGT-BTC,ZCO-BTC,LET-BTC,HPB-BTC,ADA-BTC,HYC-BTC,VITE-BTC,ABL-BTC,PAX-BTC,TUSD-BTC,USDC-BTC,GUSD-BTC,BCH-BTC,BSV-BTC,BTT-BTC,ATOM-BTC,BLOC-BTC,XRP-BTC,LRC-BTC,NULS-BTC,MCO-BTC,ELF-BTC,ZEC-BTC,CMT-BTC,ITC-BTC,SBTC-BTC,EDO-BTC,BCX-BTC,NEO-BTC,GAS-BTC,HC-BTC,QTUM-BTC,IOTA-BTC,XUC-BTC,EOS-BTC,SNT-BTC,OMG-BTC,LTC-BTC,ETH-BTC,ETC-BTC,BCD-BTC,BTG-BTC,ACT-BTC,PAY-BTC,BTM-BTC,DGD-BTC,GNT-BTC,LINK-BTC,WTC-BTC,ZRX-BTC,BNT-BTC,CVC-BTC,MANA-BTC,KNC-BTC,GNX-BTC,ICX-BTC,XEM-BTC,ARK-BTC,YOYO-BTC,FUN-BTC,ACE-BTC,TRX-BTC,DGB-BTC,SWFTC-BTC,XMR-BTC,XLM-BTC,KCASH-BTC,MDT-BTC,NAS-BTC,UGC-BTC,DPY-BTC,SSC-BTC,AAC-BTC,VIB-BTC,QUN-BTC,INT-BTC,IOST-BTC,INS-BTC,MOF-BTC,TCT-BTC,STC-BTC,THETA-BTC,PST-BTC,SNC-BTC,MKR-BTC,LIGHT-BTC,TRUE-BTC,OF-BTC,SOC-BTC,ZEN-BTC,HMC-BTC,ZIP-BTC,NANO-BTC,CIC-BTC,GTO-BTC,CHAT-BTC,INSUR-BTC,R-BTC,BEC-BTC,MITH-BTC,ABT-BTC,BKX-BTC,RFR-BTC,TRIO-BTC,DADI-BTC,ONT-BTC,OKB-BTC,CTXC-ETH,ZIL-ETH,YOU-ETH,LBA-ETH,LSK-ETH,CAI-ETH,SC-ETH,AE-ETH,KAN-ETH,WIN-ETH,DCR-ETH,WAVES-ETH,ORS-ETH,MVP-ETH,EGT-ETH,ZCO-ETH,LET-ETH,HPB-ETH,SDA-ETH,ADA-ETH,HYC-ETH,VITE-ETH,ABL-ETH,BTT-ETH,ATOM-ETH,ELF-ETH,LTC-ETH,CMT-ETH,ITC-ETH,PRA-ETH,EDO-ETH,LRC-ETH,NULS-ETH,MCO-ETH,STORJ-ETH,SNT-ETH,PAY-ETH,DGD-ETH,GNT-ETH,ACT-ETH,BTM-ETH,EOS-ETH,OMG-ETH,DASH-ETH,XRP-ETH,ZEC-ETH,NEO-ETH,GAS-ETH,HC-ETH,QTUM-ETH,IOTA-ETH,XUC-ETH,ETC-ETH,LINK-ETH,WTC-ETH,ZRX-ETH,BNT-ETH,CVC-ETH,MANA-ETH,GNX-ETH,ICX-ETH,XEM-ETH,ARK-ETH,YOYO-ETH,TRX-ETH,DGB-ETH,PPT-ETH,SWFTC-ETH,XMR-ETH,XLM-ETH,KCASH-ETH,MDT-ETH,NAS-ETH,RNT-ETH,UGC-ETH,DPY-ETH,SSC-ETH,AAC-ETH,FAIR-ETH,RCT-ETH,VIB-ETH,TOPC-ETH,QUN-ETH,INT-ETH,IOST-ETH,INS-ETH,MOF-ETH,REF-ETH,THETA-ETH,PST-ETH,SNC-ETH,MKR-ETH,LIGHT-ETH,TRUE-ETH,OF-ETH,SOC-ETH,ZEN-ETH,HMC-ETH,ZIP-ETH,NANO-ETH,CIC-ETH,GTO-ETH,INSUR-ETH,R-ETH,UCT-ETH,BEC-ETH,MITH-ETH,ABT-ETH,BKX-ETH,AUTO-ETH,RFR-ETH,TRIO-ETH,TRA-ETH,DADI-ETH,ONT-ETH,OKB-ETH,CTXC-USDT,ZIL-USDT,YOU-OKB,YOU-USDT,LBA-OKB,LBA-USDT,CAI-OKB,LSK-USDT,CAI-USDT,AE-OKB,SC-OKB,KAN-OKB,WIN-OKB,SC-USDT,AE-USDT,KAN-USDT,WIN-USDT,ORS-OKB,DCR-OKB,DCR-USDT,WAVES-OKB,WAVES-USDT,ORS-USDT,MVP-USDT,NAS-OKB,XAS-OKB,ZCO-OKB,EGT-OKB,XAS-USDT,CVT-USDT,EGT-USDT,LET-OKB,LET-USDT,HPB-OKB,HPB-USDT,SDA-OKB,ADA-OKB,ADA-USDT,HYC-USDT,VITE-OKB,TRX-OKB,PAX-USDT,TUSD-USDT,USDC-USDT,GUSD-USDT,BCH-USDT,BSV-USDT,BTT-USDT,BLOC-OKB,BLOC-USDT,ATOM-USDT,ELF-USDT,DASH-USDT,LRC-USDT,NULS-USDT,MCO-USDT,BTG-USDT,DASH-OKB,XRP-USDT,ZEC-USDT,NEO-USDT,GAS-USDT,HC-USDT,QTUM-USDT,IOTA-USDT,BTC-USDT,BCD-USDT,XUC-USDT,CMT-USDT,ITC-USDT,PRA-USDT,EDO-USDT,ETH-USDT,LTC-USDT,ETC-USDT,EOS-USDT,OMG-USDT,ACT-USDT,BTM-USDT,STORJ-USDT,PAY-USDT,DGD-USDT,GNT-USDT,SNT-USDT,LINK-USDT,WTC-USDT,ZRX-USDT,BNT-USDT,CVC-USDT,MANA-USDT,KNC-USDT,ICX-USDT,XEM-USDT,ARK-USDT,YOYO-USDT,AST-USDT,TRX-USDT,MDA-USDT,DGB-USDT,PPT-USDT,SWFTC-USDT,XMR-USDT,XLM-USDT,KCASH-USDT,MDT-USDT,NAS-USDT,RNT-USDT,UGC-USDT,DPY-USDT,SSC-USDT,AAC-USDT,FAIR-USDT,UBTC-USDT,SHOW-USDT,VIB-USDT,MOT-USDT,UTK-USDT,TOPC-USDT,QUN-USDT,INT-USDT,IPC-USDT,IOST-USDT,INS-USDT,YEE-USDT,MOF-USDT,TCT-USDT,STC-USDT,THETA-USDT,PST-USDT,MKR-USDT,LIGHT-USDT,TRUE-USDT,OF-USDT,SOC-USDT,ZEN-USDT,HMC-USDT,ZIP-USDT,NANO-USDT,CIC-USDT,GTO-USDT,CHAT-USDT,INSUR-USDT,R-USDT,BEC-USDT,MITH-USDT,ABT-USDT,BKX-USDT,RFR-USDT,TRIO-USDT,DADI-USDT,ONT-USDT,OKB-USDT,NEO-OKB,LTC-OKB,ETC-OKB,XRP-OKB,ZEC-OKB,QTUM-OKB,IOTA-OKB,EOS-OKB", + "enabledPairs": "EOS-USDT", "baseCurrencies": "USD", "assetTypes": "SPOT", "supportsAutoPairUpdates": true, "configCurrencyPairFormat": { "uppercase": true, - "delimiter": "_" + "delimiter": "-" }, "requestCurrencyPairFormat": { - "uppercase": false, - "delimiter": "_" + "uppercase": true, + "delimiter": "-" }, "bankAccounts": [ { diff --git a/currency/pair.go b/currency/pair.go index 9c46730b..74e22195 100644 --- a/currency/pair.go +++ b/currency/pair.go @@ -62,7 +62,7 @@ func NewPairFromIndex(currencyPair, index string) (Pair, error) { // NewPairFromString converts currency string into a new CurrencyPair // with or without delimeter func NewPairFromString(currencyPair string) Pair { - delimiters := []string{"_", "-", "/"} + delimiters := []string{"_", "-", "/", ":"} var delimiter string for _, x := range delimiters { if strings.Contains(currencyPair, x) { diff --git a/engine/routines.go b/engine/routines.go index 1c553f39..54f140d4 100644 --- a/engine/routines.go +++ b/engine/routines.go @@ -428,18 +428,27 @@ func WebsocketDataHandler(ws *wshandler.Websocket) { // } tickerNew := ticker.Price{ + Last: d.Last, + High: d.High, + Low: d.Low, + Bid: d.Bid, + Ask: d.Ask, + Volume: d.Volume, + QuoteVolume: d.QuoteVolume, + PriceATH: d.PriceATH, + Open: d.Open, + Close: d.Close, Pair: d.Pair, LastUpdated: d.Timestamp, - Last: d.ClosePrice, - High: d.HighPrice, - Low: d.LowPrice, - Volume: d.Quantity, } if Bot.Settings.EnableExchangeSyncManager && Bot.ExchangeCurrencyPairManager != nil { Bot.ExchangeCurrencyPairManager.update(ws.GetName(), d.Pair, d.AssetType, SyncItemTicker, nil) } - ticker.ProcessTicker(ws.GetName(), &tickerNew, d.AssetType) + err := ticker.ProcessTicker(ws.GetName(), &tickerNew, d.AssetType) + if err != nil { + log.Errorf(log.WebsocketMgr, "routines.go exchange %s websocket error - %s", ws.GetName(), err) + } printTickerSummary(&tickerNew, tickerNew.Pair, d.AssetType, ws.GetName(), nil) case wshandler.KlineData: // Kline data diff --git a/engine/syncer.go b/engine/syncer.go index cf254f57..ff84a516 100644 --- a/engine/syncer.go +++ b/engine/syncer.go @@ -486,6 +486,8 @@ func (e *ExchangeCurrencyPairSyncer) Start() { } else { usingWebsocket = true } + } else { + usingWebsocket = true } } else if supportsREST { usingREST = true diff --git a/exchanges/anx/anx_types.go b/exchanges/anx/anx_types.go index 7c590ee8..cfcee857 100644 --- a/exchanges/anx/anx_types.go +++ b/exchanges/anx/anx_types.go @@ -161,16 +161,16 @@ type TickerComponent struct { type Ticker struct { Result string `json:"result"` Data struct { - High TickerComponent `json:"high"` - Low TickerComponent `json:"low"` - Avg TickerComponent `json:"avg"` - Vwap TickerComponent `json:"vwap"` - Vol TickerComponent `json:"vol"` - Last TickerComponent `json:"last"` - Buy TickerComponent `json:"buy"` - Sell TickerComponent `json:"sell"` - Now string `json:"now"` - UpdateTime string `json:"dataUpdateTime"` + High TickerComponent `json:"high"` + Low TickerComponent `json:"low"` + Average TickerComponent `json:"avg"` + VolumeWeightedAveragePrice TickerComponent `json:"vwap"` + Volume TickerComponent `json:"vol"` + Last TickerComponent `json:"last"` + Buy TickerComponent `json:"buy"` + Sell TickerComponent `json:"sell"` + Now int64 `json:"now,string"` + UpdateTime int64 `json:"dataUpdateTime,string"` } `json:"data"` } diff --git a/exchanges/anx/anx_wrapper.go b/exchanges/anx/anx_wrapper.go index 0cd37dd0..6dcaab39 100644 --- a/exchanges/anx/anx_wrapper.go +++ b/exchanges/anx/anx_wrapper.go @@ -2,12 +2,12 @@ package anx import ( "fmt" - "strconv" "strings" "sync" "time" "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" @@ -173,7 +173,7 @@ func (a *ANX) FetchTradablePairs(asset asset.Item) ([]string, error) { var currencies []string for x := range result.CurrencyPairs { - currencies = append(currencies, result.CurrencyPairs[x].TradedCcy+"_"+result.CurrencyPairs[x].SettlementCcy) + currencies = append(currencies, fmt.Sprintf("%v%v%v", result.CurrencyPairs[x].TradedCcy, a.GetPairFormat(asset, false).Delimiter, result.CurrencyPairs[x].SettlementCcy)) } return currencies, nil @@ -186,61 +186,22 @@ func (a *ANX) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, if err != nil { return tickerPrice, err } + last, _ := convert.FloatFromString(tick.Data.Last.Value) + high, _ := convert.FloatFromString(tick.Data.High.Value) + low, _ := convert.FloatFromString(tick.Data.Low.Value) + bid, _ := convert.FloatFromString(tick.Data.Buy.Value) + ask, _ := convert.FloatFromString(tick.Data.Sell.Value) + volume, _ := convert.FloatFromString(tick.Data.Volume.Value) - tickerPrice.Pair = p - - if tick.Data.Sell.Value != "" { - tickerPrice.Ask, err = strconv.ParseFloat(tick.Data.Sell.Value, 64) - if err != nil { - return tickerPrice, err - } - } else { - tickerPrice.Ask = 0 - } - - if tick.Data.Buy.Value != "" { - tickerPrice.Bid, err = strconv.ParseFloat(tick.Data.Buy.Value, 64) - if err != nil { - return tickerPrice, err - } - } else { - tickerPrice.Bid = 0 - } - - if tick.Data.Low.Value != "" { - tickerPrice.Low, err = strconv.ParseFloat(tick.Data.Low.Value, 64) - if err != nil { - return tickerPrice, err - } - } else { - tickerPrice.Low = 0 - } - - if tick.Data.Last.Value != "" { - tickerPrice.Last, err = strconv.ParseFloat(tick.Data.Last.Value, 64) - if err != nil { - return tickerPrice, err - } - } else { - tickerPrice.Last = 0 - } - - if tick.Data.Vol.Value != "" { - tickerPrice.Volume, err = strconv.ParseFloat(tick.Data.Vol.Value, 64) - if err != nil { - return tickerPrice, err - } - } else { - tickerPrice.Volume = 0 - } - - if tick.Data.High.Value != "" { - tickerPrice.High, err = strconv.ParseFloat(tick.Data.High.Value, 64) - if err != nil { - return tickerPrice, err - } - } else { - tickerPrice.High = 0 + tickerPrice = ticker.Price{ + Last: last, + High: high, + Low: low, + Bid: bid, + Ask: ask, + Volume: volume, + Pair: p, + LastUpdated: time.Unix(0, tick.Data.UpdateTime), } err = ticker.ProcessTicker(a.GetName(), &tickerPrice, assetType) diff --git a/exchanges/binance/binance_types.go b/exchanges/binance/binance_types.go index 276b4480..78bfc660 100644 --- a/exchanges/binance/binance_types.go +++ b/exchanges/binance/binance_types.go @@ -167,29 +167,29 @@ type KlineStream struct { // TickerStream holds the ticker stream data type TickerStream struct { - EventType string `json:"e"` - EventTime int64 `json:"E"` - Symbol string `json:"s"` - PriceChange string `json:"p"` - PriceChangePercent string `json:"P"` - WeightedAvgPrice string `json:"w"` - PrevDayClose string `json:"x"` - CurrDayClose string `json:"c"` - CloseTradeQuantity string `json:"Q"` - BestBidPrice string `json:"b"` - BestBidQuantity string `json:"B"` - BestAskPrice string `json:"a"` - BestAskQuantity string `json:"A"` - OpenPrice string `json:"o"` - HighPrice string `json:"h"` - LowPrice string `json:"l"` - TotalTradedVolume string `json:"v"` - TotalTradedQuoteVolume string `json:"q"` - OpenTime int64 `json:"O"` - CloseTime int64 `json:"C"` - FirstTradeID int64 `json:"F"` - LastTradeID int64 `json:"L"` - NumberOfTrades int64 `json:"n"` + EventType string `json:"e"` + EventTime int64 `json:"E"` + Symbol currency.Pair `json:"s"` + PriceChange float64 `json:"p,string"` + PriceChangePercent float64 `json:"P,string"` + WeightedAvgPrice float64 `json:"w,string"` + ClosePrice float64 `json:"x,string"` + LastPrice float64 `json:"c,string"` + LastPriceQuantity float64 `json:"Q,string"` + BestBidPrice float64 `json:"b,string"` + BestBidQuantity float64 `json:"B,string"` + BestAskPrice float64 `json:"a,string"` + BestAskQuantity float64 `json:"A,string"` + OpenPrice float64 `json:"o,string"` + HighPrice float64 `json:"h,string"` + LowPrice float64 `json:"l,string"` + TotalTradedVolume float64 `json:"v,string"` + TotalTradedQuoteVolume float64 `json:"q,string"` + OpenTime int64 `json:"O"` + CloseTime int64 `json:"C"` + FirstTradeID int64 `json:"F"` + LastTradeID int64 `json:"L"` + NumberOfTrades int64 `json:"n"` } // HistoricalTrade holds recent trade data @@ -239,25 +239,25 @@ type AveragePrice struct { // PriceChangeStats contains statistics for the last 24 hours trade type PriceChangeStats struct { - Symbol string `json:"symbol"` - PriceChange float64 `json:"priceChange,string"` - PriceChangePercent float64 `json:"priceChangePercent,string"` - WeightedAvgPrice float64 `json:"weightedAvgPrice,string"` - PrevClosePrice float64 `json:"prevClosePrice,string"` - LastPrice float64 `json:"lastPrice,string"` - LastQty float64 `json:"lastQty,string"` - BidPrice float64 `json:"bidPrice,string"` - AskPrice float64 `json:"askPrice,string"` - OpenPrice float64 `json:"openPrice,string"` - HighPrice float64 `json:"highPrice,string"` - LowPrice float64 `json:"lowPrice,string"` - Volume float64 `json:"volume,string"` - QuoteVolume float64 `json:"quoteVolume,string"` - OpenTime int64 `json:"openTime"` - CloseTime int64 `json:"closeTime"` - FirstID int64 `json:"fristId"` - LastID int64 `json:"lastId"` - Count int64 `json:"count"` + Symbol currency.Pair `json:"symbol"` + PriceChange float64 `json:"priceChange,string"` + PriceChangePercent float64 `json:"priceChangePercent,string"` + WeightedAvgPrice float64 `json:"weightedAvgPrice,string"` + PrevClosePrice float64 `json:"prevClosePrice,string"` + LastPrice float64 `json:"lastPrice,string"` + LastQty float64 `json:"lastQty,string"` + BidPrice float64 `json:"bidPrice,string"` + AskPrice float64 `json:"askPrice,string"` + OpenPrice float64 `json:"openPrice,string"` + HighPrice float64 `json:"highPrice,string"` + LowPrice float64 `json:"lowPrice,string"` + Volume float64 `json:"volume,string"` + QuoteVolume float64 `json:"quoteVolume,string"` + OpenTime int64 `json:"openTime"` + CloseTime int64 `json:"closeTime"` + FirstID int64 `json:"fristId"` + LastID int64 `json:"lastId"` + Count int64 `json:"count"` } // SymbolPrice holds basic symbol price diff --git a/exchanges/binance/binance_websocket.go b/exchanges/binance/binance_websocket.go index 052d53fe..32108435 100644 --- a/exchanges/binance/binance_websocket.go +++ b/exchanges/binance/binance_websocket.go @@ -147,18 +147,21 @@ func (b *Binance) WsHandleData() { continue } - var wsTicker wshandler.TickerData - wsTicker.Timestamp = time.Unix(t.EventTime/1000, 0) - wsTicker.Pair = currency.NewPairFromString(t.Symbol) - wsTicker.AssetType = asset.Spot - wsTicker.Exchange = b.GetName() - wsTicker.ClosePrice, _ = strconv.ParseFloat(t.CurrDayClose, 64) - wsTicker.Quantity, _ = strconv.ParseFloat(t.TotalTradedVolume, 64) - wsTicker.OpenPrice, _ = strconv.ParseFloat(t.OpenPrice, 64) - wsTicker.HighPrice, _ = strconv.ParseFloat(t.HighPrice, 64) - wsTicker.LowPrice, _ = strconv.ParseFloat(t.LowPrice, 64) - - b.Websocket.DataHandler <- wsTicker + b.Websocket.DataHandler <- wshandler.TickerData{ + Exchange: b.Name, + Open: t.OpenPrice, + Close: t.ClosePrice, + Volume: t.TotalTradedVolume, + QuoteVolume: t.TotalTradedQuoteVolume, + High: t.HighPrice, + Low: t.LowPrice, + Bid: t.BestBidPrice, + Ask: t.BestAskPrice, + Last: t.LastPrice, + Timestamp: time.Unix(0, t.EventTime), + AssetType: asset.Spot, + Pair: t.Symbol, + } continue case "kline": diff --git a/exchanges/binance/binance_wrapper.go b/exchanges/binance/binance_wrapper.go index 5966e596..b046889d 100644 --- a/exchanges/binance/binance_wrapper.go +++ b/exchanges/binance/binance_wrapper.go @@ -163,9 +163,9 @@ func (b *Binance) Run() { } forceUpdate := false - if !common.StringDataContains(b.GetEnabledPairs(asset.Spot).Strings(), "-") || - !common.StringDataContains(b.GetAvailablePairs(asset.Spot).Strings(), "-") { - enabledPairs := currency.NewPairsFromStrings([]string{"BTC-USDT"}) + if !common.StringDataContains(b.GetEnabledPairs(asset.Spot).Strings(), b.GetPairFormat(asset.Spot, false).Delimiter) || + !common.StringDataContains(b.GetAvailablePairs(asset.Spot).Strings(), b.GetPairFormat(asset.Spot, false).Delimiter) { + enabledPairs := currency.NewPairsFromStrings([]string{fmt.Sprintf("BTC%vUSDT", b.GetPairFormat(asset.Spot, false).Delimiter)}) log.Warn(log.ExchangeSys, "Available pairs for Binance reset due to config upgrade, please enable the ones you would like again") forceUpdate = true @@ -197,8 +197,10 @@ func (b *Binance) FetchTradablePairs(asset asset.Item) ([]string, error) { for x := range info.Symbols { if info.Symbols[x].Status == "TRADING" { - validCurrencyPairs = append(validCurrencyPairs, - info.Symbols[x].BaseAsset+"-"+info.Symbols[x].QuoteAsset) + validCurrencyPairs = append(validCurrencyPairs, fmt.Sprintf("%v%v%v", + info.Symbols[x].BaseAsset, + b.GetPairFormat(asset, false).Delimiter, + info.Symbols[x].QuoteAsset)) } } return validCurrencyPairs, nil @@ -222,21 +224,28 @@ func (b *Binance) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Pr if err != nil { return tickerPrice, err } - - for _, x := range b.GetEnabledPairs(assetType) { - curr := b.FormatExchangeCurrency(x, assetType) + pairs := b.GetEnabledPairs(assetType) + for i := range pairs { for y := range tick { - if tick[y].Symbol != curr.String() { + if !tick[y].Symbol.Equal(pairs[i]) { continue } - tickerPrice.Pair = x - tickerPrice.Ask = tick[y].AskPrice - tickerPrice.Bid = tick[y].BidPrice - tickerPrice.High = tick[y].HighPrice - tickerPrice.Last = tick[y].LastPrice - tickerPrice.Low = tick[y].LowPrice - tickerPrice.Volume = tick[y].Volume - ticker.ProcessTicker(b.Name, &tickerPrice, assetType) + tickerPrice := ticker.Price{ + Last: tick[y].LastPrice, + High: tick[y].HighPrice, + Low: tick[y].LowPrice, + Bid: tick[y].BidPrice, + Ask: tick[y].AskPrice, + Volume: tick[y].Volume, + QuoteVolume: tick[y].QuoteVolume, + Open: tick[y].OpenPrice, + Close: tick[y].PrevClosePrice, + Pair: pairs[i], + } + err = ticker.ProcessTicker(b.Name, &tickerPrice, assetType) + if err != nil { + log.Error(log.Ticker, err) + } } } return ticker.GetTicker(b.Name, p, assetType) diff --git a/exchanges/bitfinex/bitfinex_test.go b/exchanges/bitfinex/bitfinex_test.go index 4f301994..3659ca83 100644 --- a/exchanges/bitfinex/bitfinex_test.go +++ b/exchanges/bitfinex/bitfinex_test.go @@ -48,6 +48,20 @@ func TestSetup(t *testing.T) { b.Requester.SetRateLimit(false, time.Millisecond*300, 1) } +func TestAppendOptionalDelimiter(t *testing.T) { + curr1 := currency.NewPairFromString("BTCUSD") + b.appendOptionalDelimiter(&curr1) + if curr1.Delimiter != "" { + t.Errorf("Expected no delimiter, received %v", curr1.Delimiter) + } + curr2 := currency.NewPairFromString("DUSK:USD") + curr2.Delimiter = "" + b.appendOptionalDelimiter(&curr2) + if curr2.Delimiter != ":" { + t.Errorf("Expected \"-\" as a delimiter, received %v", curr2.Delimiter) + } +} + func TestGetPlatformStatus(t *testing.T) { t.Parallel() diff --git a/exchanges/bitfinex/bitfinex_types.go b/exchanges/bitfinex/bitfinex_types.go index 38a5d585..a54e7584 100644 --- a/exchanges/bitfinex/bitfinex_types.go +++ b/exchanges/bitfinex/bitfinex_types.go @@ -1,5 +1,7 @@ package bitfinex +import "time" + // Ticker holds basic ticker information from the exchange type Ticker struct { Mid float64 `json:"mid,string"` @@ -28,6 +30,7 @@ type Tickerv2 struct { Volume float64 High float64 Low float64 + Timestamp time.Time } // Tickersv2 holds the version 2 tickers information diff --git a/exchanges/bitfinex/bitfinex_websocket.go b/exchanges/bitfinex/bitfinex_websocket.go index 7b88ecd6..7503304f 100644 --- a/exchanges/bitfinex/bitfinex_websocket.go +++ b/exchanges/bitfinex/bitfinex_websocket.go @@ -271,13 +271,15 @@ func (b *Bitfinex) WsDataHandler() { } case "ticker": b.Websocket.DataHandler <- wshandler.TickerData{ - Quantity: chanData[8].(float64), - ClosePrice: chanData[7].(float64), - HighPrice: chanData[9].(float64), - LowPrice: chanData[10].(float64), - Pair: currency.NewPairFromString(chanInfo.Pair), - Exchange: b.GetName(), - AssetType: asset.Spot, + Exchange: b.Name, + Volume: chanData[8].(float64), + High: chanData[9].(float64), + Low: chanData[10].(float64), + Bid: chanData[1].(float64), + Ask: chanData[3].(float64), + Last: chanData[7].(float64), + AssetType: asset.Spot, + Pair: currency.NewPairFromString(chanInfo.Pair), } case "account": @@ -539,6 +541,7 @@ func (b *Bitfinex) GenerateDefaultSubscriptions() { for i := range channels { enabledPairs := b.GetEnabledPairs(asset.Spot) for j := range enabledPairs { + b.appendOptionalDelimiter(&enabledPairs[j]) params := make(map[string]interface{}) if channels[i] == "book" { params["prec"] = "P0" diff --git a/exchanges/bitfinex/bitfinex_wrapper.go b/exchanges/bitfinex/bitfinex_wrapper.go index a0e76ac6..4fbe2455 100644 --- a/exchanges/bitfinex/bitfinex_wrapper.go +++ b/exchanges/bitfinex/bitfinex_wrapper.go @@ -194,40 +194,39 @@ func (b *Bitfinex) UpdateTradablePairs(forceUpdate bool) error { func (b *Bitfinex) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) { var tickerPrice ticker.Price enabledPairs := b.GetEnabledPairs(assetType) - var pairs []string for x := range enabledPairs { + b.appendOptionalDelimiter(&enabledPairs[x]) pairs = append(pairs, "t"+enabledPairs[x].String()) } - tickerNew, err := b.GetTickersV2(strings.Join(pairs, ",")) if err != nil { return tickerPrice, err } - - for x := range tickerNew { - newP := currency.NewPairFromStrings(tickerNew[x].Symbol[1:4], - tickerNew[x].Symbol[4:]) - - var tick ticker.Price - tick.Pair = newP - tick.Ask = tickerNew[x].Ask - tick.Bid = tickerNew[x].Bid - tick.Low = tickerNew[x].Low - tick.Last = tickerNew[x].Last - tick.Volume = tickerNew[x].Volume - tick.High = tickerNew[x].High - + for i := range tickerNew { + newP := tickerNew[i].Symbol[1:] // Remove the "t" prefix + tick := ticker.Price{ + Last: tickerNew[i].Last, + High: tickerNew[i].High, + Low: tickerNew[i].Low, + Bid: tickerNew[i].Bid, + Ask: tickerNew[i].Ask, + Volume: tickerNew[i].Volume, + Pair: currency.NewPairFromString(newP), + LastUpdated: tickerNew[i].Timestamp, + } err = ticker.ProcessTicker(b.Name, &tick, assetType) if err != nil { - return tickerPrice, err + log.Error(log.Ticker, err) } } + return ticker.GetTicker(b.Name, p, assetType) } // FetchTicker returns the ticker for a currency pair func (b *Bitfinex) FetchTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) { + b.appendOptionalDelimiter(&p) tick, err := ticker.GetTicker(b.GetName(), p, asset.Spot) if err != nil { return b.UpdateTicker(p, assetType) @@ -237,6 +236,7 @@ func (b *Bitfinex) FetchTicker(p currency.Pair, assetType asset.Item) (ticker.Pr // FetchOrderbook returns the orderbook for a currency pair func (b *Bitfinex) FetchOrderbook(p currency.Pair, assetType asset.Item) (orderbook.Base, error) { + b.appendOptionalDelimiter(&p) ob, err := orderbook.Get(b.GetName(), p, assetType) if err != nil { return b.UpdateOrderbook(p, assetType) @@ -246,6 +246,7 @@ func (b *Bitfinex) FetchOrderbook(p currency.Pair, assetType asset.Item) (orderb // UpdateOrderbook updates and returns the orderbook for a currency pair func (b *Bitfinex) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbook.Base, error) { + b.appendOptionalDelimiter(&p) var orderBook orderbook.Base urlVals := url.Values{} urlVals.Set("limit_bids", "100") @@ -339,7 +340,7 @@ func (b *Bitfinex) SubmitOrder(order *exchange.OrderSubmission) (exchange.Submit if order.OrderSide == exchange.BuyOrderSide { isBuying = true } - + b.appendOptionalDelimiter(&order.Pair) response, err := b.NewOrder(order.Pair.String(), order.Amount, order.Price, @@ -592,6 +593,9 @@ func (b *Bitfinex) GetOrderHistory(getOrdersRequest *exchange.GetOrdersRequest) exchange.FilterOrdersBySide(&orders, getOrdersRequest.OrderSide) exchange.FilterOrdersByType(&orders, getOrdersRequest.OrderType) exchange.FilterOrdersByTickRange(&orders, getOrdersRequest.StartTicks, getOrdersRequest.EndTicks) + for i := range getOrdersRequest.Currencies { + b.appendOptionalDelimiter(&getOrdersRequest.Currencies[i]) + } exchange.FilterOrdersByCurrencies(&orders, getOrdersRequest.Currencies) return orders, nil } @@ -599,6 +603,9 @@ func (b *Bitfinex) GetOrderHistory(getOrdersRequest *exchange.GetOrdersRequest) // SubscribeToWebsocketChannels appends to ChannelsToSubscribe // which lets websocket.manageSubscriptions handle subscribing func (b *Bitfinex) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { + for i := range channels { + b.appendOptionalDelimiter(&channels[i].Currency) + } b.Websocket.SubscribeToChannels(channels) return nil } @@ -606,6 +613,9 @@ func (b *Bitfinex) SubscribeToWebsocketChannels(channels []wshandler.WebsocketCh // UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe // which lets websocket.manageSubscriptions handle unsubscribing func (b *Bitfinex) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { + for i := range channels { + b.appendOptionalDelimiter(&channels[i].Currency) + } b.Websocket.RemoveSubscribedChannels(channels) return nil } @@ -619,3 +629,11 @@ func (b *Bitfinex) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, func (b *Bitfinex) AuthenticateWebsocket() error { return b.WsSendAuth() } + +// appendOptionalDelimiter ensures that a delimiter is present for long character currencies +func (b *Bitfinex) appendOptionalDelimiter(p *currency.Pair) { + if len(p.Quote.String()) > 3 || + len(p.Base.String()) > 3 { + p.Delimiter = ":" + } +} diff --git a/exchanges/bitflyer/bitflyer_wrapper.go b/exchanges/bitflyer/bitflyer_wrapper.go index 08223ba8..ab9f7ddc 100644 --- a/exchanges/bitflyer/bitflyer_wrapper.go +++ b/exchanges/bitflyer/bitflyer_wrapper.go @@ -138,7 +138,7 @@ func (b *Bitflyer) FetchTradablePairs(assetType asset.Item) ([]string, error) { for _, info := range pairs { if info.Alias != "" && assetType == asset.Futures { products = append(products, info.Alias) - } else if info.Alias == "" && assetType == asset.Spot && strings.Contains(info.ProductCode, "_") { + } else if info.Alias == "" && assetType == asset.Spot && strings.Contains(info.ProductCode, b.GetPairFormat(assetType, false).Delimiter) { products = append(products, info.ProductCode) } } @@ -177,10 +177,8 @@ func (b *Bitflyer) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.P tickerPrice.Pair = p tickerPrice.Ask = tickerNew.BestAsk tickerPrice.Bid = tickerNew.BestBid - // tickerPrice.Low tickerPrice.Last = tickerNew.Last tickerPrice.Volume = tickerNew.Volume - // tickerPrice.High err = ticker.ProcessTicker(b.GetName(), &tickerPrice, assetType) if err != nil { return tickerPrice, err diff --git a/exchanges/bithumb/bithumb_wrapper.go b/exchanges/bithumb/bithumb_wrapper.go index 16a7138f..7871622a 100644 --- a/exchanges/bithumb/bithumb_wrapper.go +++ b/exchanges/bithumb/bithumb_wrapper.go @@ -154,24 +154,27 @@ func (b *Bithumb) UpdateTradablePairs(forceUpdate bool) error { // UpdateTicker updates and returns the ticker for a currency pair func (b *Bithumb) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) { var tickerPrice ticker.Price - tickers, err := b.GetAllTickers() if err != nil { return tickerPrice, err } - - for _, x := range b.GetEnabledPairs(assetType) { - currency := x.Base.String() - var tp ticker.Price - tp.Pair = x - tp.Low = tickers[currency].MinPrice - tp.Last = tickers[currency].ClosingPrice - tp.Volume = tickers[currency].UnitsTraded24Hr - tp.High = tickers[currency].MaxPrice - + pairs := b.GetEnabledPairs(assetType) + for i := range pairs { + curr := pairs[i].Base.String() + if _, ok := tickers[curr]; !ok { + continue + } + tp := ticker.Price{ + High: tickers[curr].MaxPrice, + Low: tickers[curr].MinPrice, + Volume: tickers[curr].UnitsTraded24Hr, + Open: tickers[curr].OpeningPrice, + Close: tickers[curr].ClosingPrice, + Pair: pairs[i], + } err = ticker.ProcessTicker(b.Name, &tp, assetType) if err != nil { - return tickerPrice, err + log.Error(log.Ticker, err) } } return ticker.GetTicker(b.Name, p, assetType) diff --git a/exchanges/bitmex/bitmex_types.go b/exchanges/bitmex/bitmex_types.go index 3a84e8fa..399ee0a9 100644 --- a/exchanges/bitmex/bitmex_types.go +++ b/exchanges/bitmex/bitmex_types.go @@ -1,6 +1,11 @@ package bitmex -import exchange "github.com/thrasher-corp/gocryptotrader/exchanges" +import ( + "time" + + "github.com/thrasher-corp/gocryptotrader/currency" + exchange "github.com/thrasher-corp/gocryptotrader/exchanges" +) // RequestError allows for a general error capture from requests type RequestError struct { @@ -117,107 +122,107 @@ type Funding struct { // Instrument Tradeable Contracts, Indices, and History type Instrument struct { - AskPrice float64 `json:"askPrice"` - BankruptLimitDownPrice float64 `json:"bankruptLimitDownPrice"` - BankruptLimitUpPrice float64 `json:"bankruptLimitUpPrice"` - BidPrice float64 `json:"bidPrice"` - BuyLeg string `json:"buyLeg"` - CalcInterval string `json:"calcInterval"` - Capped bool `json:"capped"` - ClosingTimestamp string `json:"closingTimestamp"` - Deleverage bool `json:"deleverage"` - Expiry string `json:"expiry"` - FairBasis float64 `json:"fairBasis"` - FairBasisRate float64 `json:"fairBasisRate"` - FairMethod string `json:"fairMethod"` - FairPrice float64 `json:"fairPrice"` - Front string `json:"front"` - FundingBaseSymbol string `json:"fundingBaseSymbol"` - FundingInterval string `json:"fundingInterval"` - FundingPremiumSymbol string `json:"fundingPremiumSymbol"` - FundingQuoteSymbol string `json:"fundingQuoteSymbol"` - FundingRate float64 `json:"fundingRate"` - FundingTimestamp string `json:"fundingTimestamp"` - HasLiquidity bool `json:"hasLiquidity"` - HighPrice float64 `json:"highPrice"` - ImpactAskPrice float64 `json:"impactAskPrice"` - ImpactBidPrice float64 `json:"impactBidPrice"` - ImpactMidPrice float64 `json:"impactMidPrice"` - IndicativeFundingRate float64 `json:"indicativeFundingRate"` - IndicativeSettlePrice float64 `json:"indicativeSettlePrice"` - IndicativeTaxRate float64 `json:"indicativeTaxRate"` - InitMargin float64 `json:"initMargin"` - InsuranceFee float64 `json:"insuranceFee"` - InverseLeg string `json:"inverseLeg"` - IsInverse bool `json:"isInverse"` - IsQuanto bool `json:"isQuanto"` - LastChangePcnt float64 `json:"lastChangePcnt"` - LastPrice float64 `json:"lastPrice"` - LastPriceProtected float64 `json:"lastPriceProtected"` - LastTickDirection string `json:"lastTickDirection"` - Limit float64 `json:"limit"` - LimitDownPrice float64 `json:"limitDownPrice"` - LimitUpPrice float64 `json:"limitUpPrice"` - Listing string `json:"listing"` - LotSize int64 `json:"lotSize"` - LowPrice float64 `json:"lowPrice"` - MaintMargin float64 `json:"maintMargin"` - MakerFee float64 `json:"makerFee"` - MarkMethod string `json:"markMethod"` - MarkPrice float64 `json:"markPrice"` - MaxOrderQty int64 `json:"maxOrderQty"` - MaxPrice float64 `json:"maxPrice"` - MidPrice float64 `json:"midPrice"` - Multiplier int64 `json:"multiplier"` - OpenInterest int64 `json:"openInterest"` - OpenValue int64 `json:"openValue"` - OpeningTimestamp string `json:"openingTimestamp"` - OptionMultiplier float64 `json:"optionMultiplier"` - OptionStrikePcnt float64 `json:"optionStrikePcnt"` - OptionStrikePrice float64 `json:"optionStrikePrice"` - OptionStrikeRound float64 `json:"optionStrikeRound"` - OptionUnderlyingPrice float64 `json:"optionUnderlyingPrice"` - PositionCurrency string `json:"positionCurrency"` - PrevClosePrice float64 `json:"prevClosePrice"` - PrevPrice24h float64 `json:"prevPrice24h"` - PrevTotalTurnover int64 `json:"prevTotalTurnover"` - PrevTotalVolume int64 `json:"prevTotalVolume"` - PublishInterval string `json:"publishInterval"` - PublishTime string `json:"publishTime"` - QuoteCurrency string `json:"quoteCurrency"` - QuoteToSettleMultiplier int64 `json:"quoteToSettleMultiplier"` - RebalanceInterval string `json:"rebalanceInterval"` - RebalanceTimestamp string `json:"rebalanceTimestamp"` - Reference string `json:"reference"` - ReferenceSymbol string `json:"referenceSymbol"` - RelistInterval string `json:"relistInterval"` - RiskLimit int64 `json:"riskLimit"` - RiskStep int64 `json:"riskStep"` - RootSymbol string `json:"rootSymbol"` - SellLeg string `json:"sellLeg"` - SessionInterval string `json:"sessionInterval"` - SettlCurrency string `json:"settlCurrency"` - Settle string `json:"settle"` - SettledPrice float64 `json:"settledPrice"` - SettlementFee float64 `json:"settlementFee"` - State string `json:"state"` - Symbol string `json:"symbol"` - TakerFee float64 `json:"takerFee"` - Taxed bool `json:"taxed"` - TickSize float64 `json:"tickSize"` - Timestamp string `json:"timestamp"` - TotalTurnover int64 `json:"totalTurnover"` - TotalVolume int64 `json:"totalVolume"` - Turnover int64 `json:"turnover"` - Turnover24h int64 `json:"turnover24h"` - Typ string `json:"typ"` - Underlying string `json:"underlying"` - UnderlyingSymbol string `json:"underlyingSymbol"` - UnderlyingToPositionMultiplier int64 `json:"underlyingToPositionMultiplier"` - UnderlyingToSettleMultiplier int64 `json:"underlyingToSettleMultiplier"` - Volume int64 `json:"volume"` - Volume24h int64 `json:"volume24h"` - Vwap float64 `json:"vwap"` + AskPrice float64 `json:"askPrice"` + BankruptLimitDownPrice float64 `json:"bankruptLimitDownPrice"` + BankruptLimitUpPrice float64 `json:"bankruptLimitUpPrice"` + BidPrice float64 `json:"bidPrice"` + BuyLeg string `json:"buyLeg"` + CalcInterval string `json:"calcInterval"` + Capped bool `json:"capped"` + ClosingTimestamp string `json:"closingTimestamp"` + Deleverage bool `json:"deleverage"` + Expiry string `json:"expiry"` + FairBasis float64 `json:"fairBasis"` + FairBasisRate float64 `json:"fairBasisRate"` + FairMethod string `json:"fairMethod"` + FairPrice float64 `json:"fairPrice"` + Front string `json:"front"` + FundingBaseSymbol string `json:"fundingBaseSymbol"` + FundingInterval string `json:"fundingInterval"` + FundingPremiumSymbol string `json:"fundingPremiumSymbol"` + FundingQuoteSymbol string `json:"fundingQuoteSymbol"` + FundingRate float64 `json:"fundingRate"` + FundingTimestamp string `json:"fundingTimestamp"` + HasLiquidity bool `json:"hasLiquidity"` + HighPrice float64 `json:"highPrice"` + ImpactAskPrice float64 `json:"impactAskPrice"` + ImpactBidPrice float64 `json:"impactBidPrice"` + ImpactMidPrice float64 `json:"impactMidPrice"` + IndicativeFundingRate float64 `json:"indicativeFundingRate"` + IndicativeSettlePrice float64 `json:"indicativeSettlePrice"` + IndicativeTaxRate float64 `json:"indicativeTaxRate"` + InitMargin float64 `json:"initMargin"` + InsuranceFee float64 `json:"insuranceFee"` + InverseLeg string `json:"inverseLeg"` + IsInverse bool `json:"isInverse"` + IsQuanto bool `json:"isQuanto"` + LastChangePcnt float64 `json:"lastChangePcnt"` + LastPrice float64 `json:"lastPrice"` + LastPriceProtected float64 `json:"lastPriceProtected"` + LastTickDirection string `json:"lastTickDirection"` + Limit float64 `json:"limit"` + LimitDownPrice float64 `json:"limitDownPrice"` + LimitUpPrice float64 `json:"limitUpPrice"` + Listing string `json:"listing"` + LotSize int64 `json:"lotSize"` + LowPrice float64 `json:"lowPrice"` + MaintMargin float64 `json:"maintMargin"` + MakerFee float64 `json:"makerFee"` + MarkMethod string `json:"markMethod"` + MarkPrice float64 `json:"markPrice"` + MaxOrderQty int64 `json:"maxOrderQty"` + MaxPrice float64 `json:"maxPrice"` + MidPrice float64 `json:"midPrice"` + Multiplier int64 `json:"multiplier"` + OpenInterest int64 `json:"openInterest"` + OpenValue int64 `json:"openValue"` + OpeningTimestamp string `json:"openingTimestamp"` + OptionMultiplier float64 `json:"optionMultiplier"` + OptionStrikePcnt float64 `json:"optionStrikePcnt"` + OptionStrikePrice float64 `json:"optionStrikePrice"` + OptionStrikeRound float64 `json:"optionStrikeRound"` + OptionUnderlyingPrice float64 `json:"optionUnderlyingPrice"` + PositionCurrency string `json:"positionCurrency"` + PrevClosePrice float64 `json:"prevClosePrice"` + PrevPrice24h float64 `json:"prevPrice24h"` + PrevTotalTurnover int64 `json:"prevTotalTurnover"` + PrevTotalVolume int64 `json:"prevTotalVolume"` + PublishInterval string `json:"publishInterval"` + PublishTime string `json:"publishTime"` + QuoteCurrency string `json:"quoteCurrency"` + QuoteToSettleMultiplier int64 `json:"quoteToSettleMultiplier"` + RebalanceInterval string `json:"rebalanceInterval"` + RebalanceTimestamp string `json:"rebalanceTimestamp"` + Reference string `json:"reference"` + ReferenceSymbol string `json:"referenceSymbol"` + RelistInterval string `json:"relistInterval"` + RiskLimit int64 `json:"riskLimit"` + RiskStep int64 `json:"riskStep"` + RootSymbol string `json:"rootSymbol"` + SellLeg string `json:"sellLeg"` + SessionInterval string `json:"sessionInterval"` + SettlCurrency string `json:"settlCurrency"` + Settle string `json:"settle"` + SettledPrice float64 `json:"settledPrice"` + SettlementFee float64 `json:"settlementFee"` + State string `json:"state"` + Symbol currency.Pair `json:"symbol"` + TakerFee float64 `json:"takerFee"` + Taxed bool `json:"taxed"` + TickSize float64 `json:"tickSize"` + Timestamp time.Time `json:"timestamp"` + TotalTurnover int64 `json:"totalTurnover"` + TotalVolume int64 `json:"totalVolume"` + Turnover int64 `json:"turnover"` + Turnover24h int64 `json:"turnover24h"` + Typ string `json:"typ"` + Underlying string `json:"underlying"` + UnderlyingSymbol string `json:"underlyingSymbol"` + UnderlyingToPositionMultiplier int64 `json:"underlyingToPositionMultiplier"` + UnderlyingToSettleMultiplier int64 `json:"underlyingToSettleMultiplier"` + Volume float64 `json:"volume"` + Volume24h float64 `json:"volume24h"` + Vwap float64 `json:"vwap"` } // InstrumentInterval instrument interval diff --git a/exchanges/bitmex/bitmex_wrapper.go b/exchanges/bitmex/bitmex_wrapper.go index af31e895..167be7e4 100644 --- a/exchanges/bitmex/bitmex_wrapper.go +++ b/exchanges/bitmex/bitmex_wrapper.go @@ -2,7 +2,6 @@ package bitmex import ( "errors" - "fmt" "math" "strings" "sync" @@ -93,7 +92,7 @@ func (b *Bitmex) SetDefaults() { Websocket: true, RESTCapabilities: exchange.ProtocolFeatures{ AutoPairUpdates: true, - TickerBatching: false, + TickerBatching: true, }, WithdrawPermissions: exchange.AutoWithdrawCryptoWithAPIPermission | exchange.WithdrawCryptoWithEmail | @@ -205,7 +204,7 @@ func (b *Bitmex) FetchTradablePairs(asset asset.Item) ([]string, error) { var products []string for x := range marketInfo { - products = append(products, marketInfo[x].Symbol) + products = append(products, marketInfo[x].Symbol.String()) } return products, nil @@ -260,24 +259,34 @@ func (b *Bitmex) UpdateTradablePairs(forceUpdate bool) error { // UpdateTicker updates and returns the ticker for a currency pair func (b *Bitmex) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) { var tickerPrice ticker.Price - currency := b.FormatExchangeCurrency(p, assetType) - - tick, err := b.GetTrade(&GenericRequestParams{ - Symbol: currency.String(), - Reverse: true, - Count: 1}) + tick, err := b.GetActiveInstruments(&GenericRequestParams{}) if err != nil { return tickerPrice, err } - - if len(tick) == 0 { - return tickerPrice, fmt.Errorf("%s REST error: no ticker return", b.Name) + pairs := b.GetEnabledPairs(assetType) + for i := range pairs { + for j := range tick { + if !pairs[i].Equal(tick[j].Symbol) { + continue + } + tickerPrice = ticker.Price{ + Last: tick[j].LastPrice, + High: tick[j].HighPrice, + Low: tick[j].LowPrice, + Bid: tick[j].BidPrice, + Ask: tick[j].AskPrice, + Volume: tick[j].Volume24h, + Close: tick[j].PrevClosePrice, + Pair: tick[j].Symbol, + LastUpdated: tick[j].Timestamp, + } + err = ticker.ProcessTicker(b.Name, &tickerPrice, assetType) + if err != nil { + log.Error(log.Ticker, err) + } + } } - - tickerPrice.Pair = p - tickerPrice.Last = tick[0].Price - tickerPrice.Volume = float64(tick[0].Size) - return tickerPrice, ticker.ProcessTicker(b.Name, &tickerPrice, assetType) + return ticker.GetTicker(b.Name, p, assetType) } // FetchTicker returns the ticker for a currency pair diff --git a/exchanges/bitstamp/bitstamp.go b/exchanges/bitstamp/bitstamp.go index 7a91c74b..612769ee 100644 --- a/exchanges/bitstamp/bitstamp.go +++ b/exchanges/bitstamp/bitstamp.go @@ -51,8 +51,8 @@ const ( bitstampAPIReturnType = "string" bitstampAPITradingPairsInfo = "trading-pairs-info" - bitstampAuthRate = 600 - bitstampUnauthRate = 600 + bitstampAuthRate = 8000 + bitstampUnauthRate = 8000 ) // Bitstamp is the overarching type across the bitstamp package diff --git a/exchanges/bitstamp/bitstamp_wrapper.go b/exchanges/bitstamp/bitstamp_wrapper.go index c07f84d5..790ca785 100644 --- a/exchanges/bitstamp/bitstamp_wrapper.go +++ b/exchanges/bitstamp/bitstamp_wrapper.go @@ -71,7 +71,6 @@ func (b *Bitstamp) SetDefaults() { Websocket: true, RESTCapabilities: exchange.ProtocolFeatures{ AutoPairUpdates: true, - TickerBatching: false, }, WithdrawPermissions: exchange.AutoWithdrawCrypto | exchange.AutoWithdrawFiat, @@ -206,15 +205,19 @@ func (b *Bitstamp) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.P tick, err := b.GetTicker(p.String(), false) if err != nil { return tickerPrice, err - } - tickerPrice.Pair = p - tickerPrice.Ask = tick.Ask - tickerPrice.Bid = tick.Bid - tickerPrice.Low = tick.Low - tickerPrice.Last = tick.Last - tickerPrice.Volume = tick.Volume - tickerPrice.High = tick.High + + tickerPrice = ticker.Price{ + Last: tick.Last, + High: tick.High, + Low: tick.Low, + Bid: tick.Bid, + Ask: tick.Ask, + Volume: tick.Volume, + Open: tick.Open, + Pair: p, + LastUpdated: time.Unix(tick.Timestamp, 0), + } err = ticker.ProcessTicker(b.GetName(), &tickerPrice, assetType) if err != nil { diff --git a/exchanges/bittrex/bittrex_types.go b/exchanges/bittrex/bittrex_types.go index 854281a5..be400fd5 100644 --- a/exchanges/bittrex/bittrex_types.go +++ b/exchanges/bittrex/bittrex_types.go @@ -1,6 +1,8 @@ package bittrex -import "encoding/json" +import ( + "encoding/json" +) // Response is the generalised response type for Bittrex type Response struct { diff --git a/exchanges/bittrex/bittrex_wrapper.go b/exchanges/bittrex/bittrex_wrapper.go index ffa41e59..6985f74f 100644 --- a/exchanges/bittrex/bittrex_wrapper.go +++ b/exchanges/bittrex/bittrex_wrapper.go @@ -196,27 +196,36 @@ func (b *Bittrex) GetAccountInfo() (exchange.AccountInfo, error) { // UpdateTicker updates and returns the ticker for a currency pair func (b *Bittrex) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) { var tickerPrice ticker.Price - tick, err := b.GetMarketSummaries() + ticks, err := b.GetMarketSummaries() if err != nil { return tickerPrice, err } - - for _, x := range b.GetEnabledPairs(assetType) { - curr := b.FormatExchangeCurrency(x, assetType) - for y := range tick.Result { - if tick.Result[y].MarketName != curr.String() { + pairs := b.GetEnabledPairs(assetType) + for i := range pairs { + for j := range ticks.Result { + if !strings.EqualFold(ticks.Result[j].MarketName, pairs[i].String()) { continue } - tickerPrice.Pair = x - tickerPrice.High = tick.Result[y].High - tickerPrice.Low = tick.Result[y].Low - tickerPrice.Ask = tick.Result[y].Ask - tickerPrice.Bid = tick.Result[y].Bid - tickerPrice.Last = tick.Result[y].Last - tickerPrice.Volume = tick.Result[y].Volume - ticker.ProcessTicker(b.GetName(), &tickerPrice, assetType) + tickerTime, _ := time.Parse(time.RFC3339, ticks.Result[j].TimeStamp) + tickerPrice = ticker.Price{ + Last: ticks.Result[j].Last, + High: ticks.Result[j].High, + Low: ticks.Result[j].Low, + Bid: ticks.Result[j].Bid, + Ask: ticks.Result[j].Ask, + Volume: ticks.Result[j].BaseVolume, + QuoteVolume: ticks.Result[j].Volume, + Close: ticks.Result[j].PrevDay, + Pair: pairs[i], + LastUpdated: tickerTime, + } + err = ticker.ProcessTicker(b.GetName(), &tickerPrice, assetType) + if err != nil { + log.Error(log.Ticker, err) + } } } + return ticker.GetTicker(b.Name, p, assetType) } diff --git a/exchanges/btcmarkets/btcmarkets_types.go b/exchanges/btcmarkets/btcmarkets_types.go index 896cf221..4f23cfe4 100644 --- a/exchanges/btcmarkets/btcmarkets_types.go +++ b/exchanges/btcmarkets/btcmarkets_types.go @@ -30,13 +30,16 @@ type Market struct { // Ticker holds ticker information type Ticker struct { - BestBID float64 `json:"bestBid"` - BestAsk float64 `json:"bestAsk"` - LastPrice float64 `json:"lastPrice"` - Currency string `json:"currency"` - Instrument string `json:"instrument"` - Timestamp int64 `json:"timestamp"` - Volume float64 `json:"volume24h"` + BestAsk float64 `json:"bestAsk"` + BestBid float64 `json:"bestBid"` + Currency currency.Code `json:"currency"` + High24h float64 `json:"high24h"` + Instrument currency.Pair `json:"instrument"` + LastPrice float64 `json:"lastPrice"` + Low24h float64 `json:"low24h"` + Price24h float64 `json:"price24h"` + Timestamp int64 `json:"timestamp"` + Volume24h float64 `json:"volume24h"` } // Orderbook holds current orderbook information returned from the exchange diff --git a/exchanges/btcmarkets/btcmarkets_wrapper.go b/exchanges/btcmarkets/btcmarkets_wrapper.go index a7f8e7d2..b8d0b116 100644 --- a/exchanges/btcmarkets/btcmarkets_wrapper.go +++ b/exchanges/btcmarkets/btcmarkets_wrapper.go @@ -147,7 +147,7 @@ func (b *BTCMarkets) FetchTradablePairs(asset asset.Item) ([]string, error) { var pairs []string for x := range markets { - pairs = append(pairs, markets[x].Instrument+"-"+markets[x].Currency) + pairs = append(pairs, fmt.Sprintf("%v%v%v", markets[x].Instrument, b.GetPairFormat(asset, false).Delimiter, markets[x].Currency)) } return pairs, nil @@ -171,10 +171,17 @@ func (b *BTCMarkets) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker if err != nil { return tickerPrice, err } - tickerPrice.Pair = p - tickerPrice.Ask = tick.BestAsk - tickerPrice.Bid = tick.BestBID - tickerPrice.Last = tick.LastPrice + + tickerPrice = ticker.Price{ + Last: tick.LastPrice, + High: tick.High24h, + Low: tick.Low24h, + Bid: tick.BestBid, + Ask: tick.BestAsk, + Volume: tick.Volume24h, + Pair: p, + LastUpdated: time.Unix(tick.Timestamp, 0), + } err = ticker.ProcessTicker(b.GetName(), &tickerPrice, assetType) if err != nil { diff --git a/exchanges/btse/btse_types.go b/exchanges/btse/btse_types.go index 6ac5781c..0cfc2d26 100644 --- a/exchanges/btse/btse_types.go +++ b/exchanges/btse/btse_types.go @@ -43,12 +43,12 @@ type Ticker struct { // MarketStatistics stores market statistics for a particular product type MarketStatistics struct { - Open float64 `json:"open,string"` - Low float64 `json:"low,string"` - High float64 `json:"high,string"` - Close float64 `json:"close,string"` - Volume float64 `json:"volume,string"` - Time string `json:"time"` + Open float64 `json:"open,string"` + Low float64 `json:"low,string"` + High float64 `json:"high,string"` + Close float64 `json:"close,string"` + Volume float64 `json:"volume,string"` + Time time.Time `json:"time"` } // ServerTime stores the server time data diff --git a/exchanges/btse/btse_websocket.go b/exchanges/btse/btse_websocket.go index dbaa6009..1ce85e65 100644 --- a/exchanges/btse/btse_websocket.go +++ b/exchanges/btse/btse_websocket.go @@ -94,12 +94,12 @@ func (b *BTSE) WsHandleData() { } b.Websocket.DataHandler <- wshandler.TickerData{ - Timestamp: time.Now(), - Pair: currency.NewPairDelimiter(t.ProductID, "-"), - AssetType: asset.Spot, - Exchange: b.GetName(), - ClosePrice: price, - Quantity: t.LastSize, + Exchange: b.Name, + Close: price, + Bid: t.BestBids, + Ask: t.BestAsk, + AssetType: asset.Spot, + Pair: currency.NewPairDelimiter(t.ProductID, b.GetPairFormat(asset.Spot, false).Delimiter), } case "snapshot": snapshot := websocketOrderbookSnapshot{} diff --git a/exchanges/btse/btse_wrapper.go b/exchanges/btse/btse_wrapper.go index 674a89ae..8066eea1 100644 --- a/exchanges/btse/btse_wrapper.go +++ b/exchanges/btse/btse_wrapper.go @@ -219,6 +219,7 @@ func (b *BTSE) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price tickerPrice.Last = t.Price tickerPrice.Volume = s.Volume tickerPrice.High = s.High + tickerPrice.LastUpdated = s.Time err = ticker.ProcessTicker(b.GetName(), &tickerPrice, assetType) if err != nil { diff --git a/exchanges/coinbasepro/coinbasepro_types.go b/exchanges/coinbasepro/coinbasepro_types.go index 23b75ea2..52287efd 100644 --- a/exchanges/coinbasepro/coinbasepro_types.go +++ b/exchanges/coinbasepro/coinbasepro_types.go @@ -1,6 +1,10 @@ package coinbasepro -import "time" +import ( + "time" + + "github.com/thrasher-corp/gocryptotrader/currency" +) // Product holds product information type Product struct { @@ -15,10 +19,13 @@ type Product struct { // Ticker holds basic ticker information type Ticker struct { - TradeID int64 `json:"trade_id"` - Price float64 `json:"price,string"` - Size float64 `json:"size,string"` - Time string `json:"time"` + TradeID int64 `json:"trade_id"` + Ask float64 `json:"ask,string"` + Bid float64 `json:"bid,string"` + Price float64 `json:"price,string"` + Size float64 `json:"size,string"` + Volume float64 `json:"volume,string"` + Time time.Time `json:"time"` } // Trade holds executed trade information @@ -435,21 +442,21 @@ type WebsocketHeartBeat struct { // WebsocketTicker defines ticker websocket response type WebsocketTicker struct { - Type string `json:"type"` - Sequence int64 `json:"sequence"` - ProductID string `json:"product_id"` - Price float64 `json:"price,string"` - Open24H float64 `json:"open_24h,string"` - Volume24H float64 `json:"volumen_24h,string"` - Low24H float64 `json:"low_24h,string"` - High24H float64 `json:"high_24h,string"` - Volume30D float64 `json:"volume_30d,string"` - BestBid float64 `json:"best_bid,string"` - BestAsk float64 `json:"best_ask,string"` - Side string `json:"side"` - Time time.Time `json:"time,string"` - TradeID int64 `json:"trade_id"` - LastSize float64 `json:"last_size,string"` + Type string `json:"type"` + Sequence int64 `json:"sequence"` + ProductID currency.Pair `json:"product_id"` + Price float64 `json:"price,string"` + Open24H float64 `json:"open_24h,string"` + Volume24H float64 `json:"volume_24h,string"` + Low24H float64 `json:"low_24h,string"` + High24H float64 `json:"high_24h,string"` + Volume30D float64 `json:"volume_30d,string"` + BestBid float64 `json:"best_bid,string"` + BestAsk float64 `json:"best_ask,string"` + Side string `json:"side"` + Time time.Time `json:"time,string"` + TradeID int64 `json:"trade_id"` + LastSize float64 `json:"last_size,string"` } // WebsocketOrderbookSnapshot defines a snapshot response diff --git a/exchanges/coinbasepro/coinbasepro_websocket.go b/exchanges/coinbasepro/coinbasepro_websocket.go index 610d9cbb..c6471bcb 100644 --- a/exchanges/coinbasepro/coinbasepro_websocket.go +++ b/exchanges/coinbasepro/coinbasepro_websocket.go @@ -89,15 +89,18 @@ func (c *CoinbasePro) WsHandleData() { } c.Websocket.DataHandler <- wshandler.TickerData{ - Timestamp: ticker.Time, - Pair: currency.NewPairFromString(ticker.ProductID), - AssetType: asset.Spot, - Exchange: c.GetName(), - OpenPrice: ticker.Open24H, - HighPrice: ticker.High24H, - LowPrice: ticker.Low24H, - ClosePrice: ticker.Price, - Quantity: ticker.Volume24H, + Timestamp: ticker.Time, + Pair: ticker.ProductID, + AssetType: asset.Spot, + Exchange: c.Name, + Open: ticker.Open24H, + High: ticker.High24H, + Low: ticker.Low24H, + Close: ticker.Price, + Volume: ticker.Volume24H, + Bid: ticker.BestBid, + Ask: ticker.BestAsk, + Last: ticker.LastSize, } case "snapshot": diff --git a/exchanges/coinbasepro/coinbasepro_wrapper.go b/exchanges/coinbasepro/coinbasepro_wrapper.go index 04af5c16..560211e0 100644 --- a/exchanges/coinbasepro/coinbasepro_wrapper.go +++ b/exchanges/coinbasepro/coinbasepro_wrapper.go @@ -232,18 +232,22 @@ func (c *CoinbasePro) UpdateTicker(p currency.Pair, assetType asset.Item) (ticke if err != nil { return ticker.Price{}, err } - stats, err := c.GetStats(c.FormatExchangeCurrency(p, assetType).String()) - if err != nil { return ticker.Price{}, err } - tickerPrice.Pair = p - tickerPrice.Volume = stats.Volume - tickerPrice.Last = tick.Price - tickerPrice.High = stats.High - tickerPrice.Low = stats.Low + tickerPrice = ticker.Price{ + Last: tick.Size, + High: stats.High, + Low: stats.Low, + Bid: tick.Bid, + Ask: tick.Ask, + Volume: tick.Volume, + Open: stats.Open, + Pair: p, + LastUpdated: tick.Time, + } err = ticker.ProcessTicker(c.GetName(), &tickerPrice, assetType) if err != nil { diff --git a/exchanges/coinut/coinut_types.go b/exchanges/coinut/coinut_types.go index b4bbff32..900e9fd0 100644 --- a/exchanges/coinut/coinut_types.go +++ b/exchanges/coinut/coinut_types.go @@ -33,7 +33,7 @@ type Ticker struct { Last float64 `json:"last,string"` LowestSell float64 `json:"lowest_sell,string"` OpenInterest float64 `json:"open_interest,string"` - Timestamp float64 `json:"timestamp"` + Timestamp int64 `json:"timestamp"` TransID int64 `json:"trans_id"` Volume float64 `json:"volume,string"` Volume24 float64 `json:"volume24,string"` @@ -280,16 +280,23 @@ type wsHeartbeatResp struct { // WsTicker defines the resp for ticker updates from the websocket connection type WsTicker struct { - HighestBuy float64 `json:"highest_buy,string"` - InstID int64 `json:"inst_id"` - Last float64 `json:"last,string"` - LowestSell float64 `json:"lowest_sell,string"` - OpenInterest float64 `json:"open_interest,string"` - Reply string `json:"reply"` - Timestamp int64 `json:"timestamp"` - TransID int64 `json:"trans_id"` - Volume float64 `json:"volume,string"` - Volume24H float64 `json:"volume24,string"` + High24 float64 `json:"high24,string"` + HighestBuy float64 `json:"highest_buy,string"` + InstID int64 `json:"inst_id"` + Last float64 `json:"last,string"` + Low24 float64 `json:"low24,string"` + LowestSell float64 `json:"lowest_sell,string"` + Nonce int64 `json:"nonce"` + PrevTransID int64 `json:"prev_trans_id"` + PriceChange24 float64 `json:"price_change_24,string"` + Reply string `json:"reply"` + Status []string `json:"status"` + Timestamp int64 `json:"timestamp"` + TransID int64 `json:"trans_id"` + Volume float64 `json:"volume,string"` + Volume24 float64 `json:"volume24,string"` + Volume24Quote float64 `json:"volume24_quote,string"` + VolumeQuote float64 `json:"volume_quote,string"` } // WsOrderbookSnapshot defines the resp for orderbook snapshot updates from diff --git a/exchanges/coinut/coinut_websocket.go b/exchanges/coinut/coinut_websocket.go index d5616549..cd50a9e6 100644 --- a/exchanges/coinut/coinut_websocket.go +++ b/exchanges/coinut/coinut_websocket.go @@ -139,14 +139,15 @@ func (c *COINUT) wsProcessResponse(resp []byte) { } currencyPair := instrumentListByCode[ticker.InstID] c.Websocket.DataHandler <- wshandler.TickerData{ - Timestamp: time.Unix(0, ticker.Timestamp), - Pair: currency.NewPairFromString(currencyPair), - Exchange: c.GetName(), - AssetType: asset.Spot, - HighPrice: ticker.HighestBuy, - LowPrice: ticker.LowestSell, - ClosePrice: ticker.Last, - Quantity: ticker.Volume, + Exchange: c.Name, + Volume: ticker.Volume, + QuoteVolume: ticker.VolumeQuote, + High: ticker.HighestBuy, + Low: ticker.LowestSell, + Last: ticker.Last, + Timestamp: time.Unix(0, ticker.Timestamp), + AssetType: asset.Spot, + Pair: currency.NewPairFromString(currencyPair), } case "inst_order_book": @@ -245,9 +246,9 @@ func (c *COINUT) WsSetInstrumentList() error { if err != nil { return err } - for currency, data := range list.Spot { - instrumentListByString[currency] = data[0].InstID - instrumentListByCode[data[0].InstID] = currency + for curr, data := range list.Spot { + instrumentListByString[curr] = data[0].InstID + instrumentListByCode[data[0].InstID] = curr } if len(instrumentListByString) == 0 || len(instrumentListByCode) == 0 { return errors.New("instrument lists failed to populate") @@ -414,11 +415,11 @@ func (c *COINUT) wsSubmitOrder(order *WsSubmitOrderParameters) (*WsStandardOrder if !c.Websocket.CanUseAuthenticatedEndpoints() { return nil, fmt.Errorf("%v not authorised to submit order", c.Name) } - currency := c.FormatExchangeCurrency(order.Currency, asset.Spot).String() + curr := c.FormatExchangeCurrency(order.Currency, asset.Spot).String() var orderSubmissionRequest WsSubmitOrderRequest orderSubmissionRequest.Request = "new_order" orderSubmissionRequest.Nonce = c.WebsocketConn.GenerateMessageID(false) - orderSubmissionRequest.InstID = instrumentListByString[currency] + orderSubmissionRequest.InstID = instrumentListByString[curr] orderSubmissionRequest.Qty = order.Amount orderSubmissionRequest.Price = order.Price orderSubmissionRequest.Side = string(order.Side) @@ -523,13 +524,13 @@ func (c *COINUT) wsSubmitOrders(orders []WsSubmitOrderParameters) ([]WsStandardO } orderRequest := WsSubmitOrdersRequest{} for i := range orders { - currency := c.FormatExchangeCurrency(orders[i].Currency, asset.Spot).String() + curr := c.FormatExchangeCurrency(orders[i].Currency, asset.Spot).String() orderRequest.Orders = append(orderRequest.Orders, WsSubmitOrdersRequestData{ Qty: orders[i].Amount, Price: orders[i].Price, Side: string(orders[i].Side), - InstID: instrumentListByString[currency], + InstID: instrumentListByString[curr], ClientOrdID: i + 1, }) } @@ -582,11 +583,11 @@ func (c *COINUT) wsGetOpenOrders(p currency.Pair) error { if !c.Websocket.CanUseAuthenticatedEndpoints() { return fmt.Errorf("%v not authorised to get open orders", c.Name) } - currency := c.FormatExchangeCurrency(p, asset.Spot).String() + curr := c.FormatExchangeCurrency(p, asset.Spot).String() var openOrdersRequest WsGetOpenOrdersRequest openOrdersRequest.Request = "user_open_orders" openOrdersRequest.Nonce = c.WebsocketConn.GenerateMessageID(false) - openOrdersRequest.InstID = instrumentListByString[currency] + openOrdersRequest.InstID = instrumentListByString[curr] resp, err := c.WebsocketConn.SendMessageReturnResponse(openOrdersRequest.Nonce, openOrdersRequest) if err != nil { @@ -679,10 +680,10 @@ func (c *COINUT) wsGetTradeHistory(p currency.Pair, start, limit int64) error { if !c.Websocket.CanUseAuthenticatedEndpoints() { return fmt.Errorf("%v not authorised to get trade history", c.Name) } - currency := c.FormatExchangeCurrency(p, asset.Spot).String() + curr := c.FormatExchangeCurrency(p, asset.Spot).String() var request WsTradeHistoryRequest request.Request = "trade_history" - request.InstID = instrumentListByString[currency] + request.InstID = instrumentListByString[curr] request.Nonce = c.WebsocketConn.GenerateMessageID(false) request.Start = start request.Limit = limit diff --git a/exchanges/coinut/coinut_wrapper.go b/exchanges/coinut/coinut_wrapper.go index 06e3f1f6..cb59e59a 100644 --- a/exchanges/coinut/coinut_wrapper.go +++ b/exchanges/coinut/coinut_wrapper.go @@ -278,17 +278,18 @@ func (c *COINUT) GetAccountInfo() (exchange.AccountInfo, error) { // UpdateTicker updates and returns the ticker for a currency pair func (c *COINUT) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) { var tickerPrice ticker.Price - tick, err := c.GetInstrumentTicker(c.InstrumentMap[p.String()]) + tick, err := c.GetInstrumentTicker(c.InstrumentMap[c.FormatExchangeCurrency(p, assetType).String()]) if err != nil { - return ticker.Price{}, err + return tickerPrice, err + } + tickerPrice = ticker.Price{ + Last: tick.Last, + High: tick.HighestBuy, + Low: tick.LowestSell, + Volume: tick.Volume24, + Pair: p, + LastUpdated: time.Unix(0, tick.Timestamp), } - - tickerPrice.Pair = p - tickerPrice.Volume = tick.Volume - tickerPrice.Last = tick.Last - tickerPrice.High = tick.HighestBuy - tickerPrice.Low = tick.LowestSell - err = ticker.ProcessTicker(c.GetName(), &tickerPrice, assetType) if err != nil { return tickerPrice, err diff --git a/exchanges/exmo/exmo.go b/exchanges/exmo/exmo.go index f95bdf40..e4f41b26 100644 --- a/exchanges/exmo/exmo.go +++ b/exchanges/exmo/exmo.go @@ -68,9 +68,8 @@ func (e *EXMO) GetOrderbook(symbol string) (map[string]Orderbook, error) { } // GetTicker returns the ticker for a symbol or symbols -func (e *EXMO) GetTicker(symbol string) (map[string]Ticker, error) { +func (e *EXMO) GetTicker() (map[string]Ticker, error) { v := url.Values{} - v.Set("pair", symbol) result := make(map[string]Ticker) urlPath := fmt.Sprintf("%s/v%s/%s", e.API.Endpoints.URL, exmoAPIVersion, exmoTicker) return result, e.SendHTTPRequest(common.EncodeURLValues(urlPath, v), &result) diff --git a/exchanges/exmo/exmo_test.go b/exchanges/exmo/exmo_test.go index 3e341d61..65fac92e 100644 --- a/exchanges/exmo/exmo_test.go +++ b/exchanges/exmo/exmo_test.go @@ -56,7 +56,7 @@ func TestGetOrderbook(t *testing.T) { func TestGetTicker(t *testing.T) { t.Parallel() - _, err := e.GetTicker("BTC_USD") + _, err := e.GetTicker() if err != nil { t.Errorf("Test failed. Err: %s", err) } diff --git a/exchanges/exmo/exmo_wrapper.go b/exchanges/exmo/exmo_wrapper.go index 3eccf7ab..abccfa43 100644 --- a/exchanges/exmo/exmo_wrapper.go +++ b/exchanges/exmo/exmo_wrapper.go @@ -73,7 +73,7 @@ func (e *EXMO) SetDefaults() { Websocket: false, RESTCapabilities: exchange.ProtocolFeatures{ AutoPairUpdates: true, - TickerBatching: false, + TickerBatching: true, }, WithdrawPermissions: exchange.AutoWithdrawCryptoWithSetup | exchange.NoFiatWithdrawals, @@ -156,31 +156,32 @@ func (e *EXMO) UpdateTradablePairs(forceUpdate bool) error { // UpdateTicker updates and returns the ticker for a currency pair func (e *EXMO) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) { var tickerPrice ticker.Price - pairsCollated, err := e.FormatExchangeCurrencies(e.GetEnabledPairs(assetType), assetType) + result, err := e.GetTicker() if err != nil { return tickerPrice, err } - - result, err := e.GetTicker(pairsCollated) - if err != nil { + if _, ok := result[p.String()]; !ok { return tickerPrice, err } - - for _, x := range e.GetEnabledPairs(assetType) { - currency := e.FormatExchangeCurrency(x, assetType).String() - var tickerPrice ticker.Price - tickerPrice.Pair = x - tickerPrice.Last = result[currency].Last - tickerPrice.Ask = result[currency].Sell - tickerPrice.High = result[currency].High - tickerPrice.Bid = result[currency].Buy - tickerPrice.Last = result[currency].Last - tickerPrice.Low = result[currency].Low - tickerPrice.Volume = result[currency].Volume - - err = ticker.ProcessTicker(e.Name, &tickerPrice, assetType) - if err != nil { - return tickerPrice, err + pairs := e.GetEnabledPairs(assetType) + for i := range pairs { + for j := range result { + if !strings.EqualFold(pairs[i].String(), j) { + continue + } + tickerPrice = ticker.Price{ + Pair: pairs[i], + Last: result[j].Last, + Ask: result[j].Sell, + High: result[j].High, + Bid: result[j].Buy, + Low: result[j].Low, + Volume: result[j].Volume, + } + err = ticker.ProcessTicker(e.Name, &tickerPrice, assetType) + if err != nil { + log.Error(log.Ticker, err) + } } } return ticker.GetTicker(e.Name, p, assetType) diff --git a/exchanges/gateio/gateio_types.go b/exchanges/gateio/gateio_types.go index 7d692dd8..585bf48f 100644 --- a/exchanges/gateio/gateio_types.go +++ b/exchanges/gateio/gateio_types.go @@ -70,15 +70,15 @@ type KLineResponse struct { // TickerResponse holds the ticker response data type TickerResponse struct { - Result string `json:"result"` - Volume float64 `json:"baseVolume,string"` // Trading volume - High float64 `json:"high24hr,string"` // 24 hour high price - Open float64 `json:"highestBid,string"` // Openening price - Last float64 `json:"last,string"` // Last price - Low float64 `json:"low24hr,string"` // 24 hour low price - Close float64 `json:"lowestAsk,string"` // Closing price - PercentChange float64 `json:"percentChange,string"` // Percentage change - QuoteVolume float64 `json:"quoteVolume,string"` // Quote currency volume + Period int64 `json:"period"` + BaseVolume float64 `json:"baseVolume,string"` + Change float64 `json:"change,string"` + Close float64 `json:"close,string"` + High float64 `json:"high,string"` + Last float64 `json:"last,string"` + Low float64 `json:"low,string"` + Open float64 `json:"open,string"` + QuoteVolume float64 `json:"quoteVolume,string"` } // OrderbookResponse stores the orderbook data diff --git a/exchanges/gateio/gateio_websocket.go b/exchanges/gateio/gateio_websocket.go index d2c82988..0d8ad76e 100644 --- a/exchanges/gateio/gateio_websocket.go +++ b/exchanges/gateio/gateio_websocket.go @@ -136,15 +136,16 @@ func (g *Gateio) WsHandleData() { } g.Websocket.DataHandler <- wshandler.TickerData{ - Timestamp: time.Now(), - Pair: currency.NewPairFromString(c), - AssetType: asset.Spot, - Exchange: g.GetName(), - ClosePrice: ticker.Close, - Quantity: ticker.BaseVolume, - OpenPrice: ticker.Open, - HighPrice: ticker.High, - LowPrice: ticker.Low, + Exchange: g.Name, + Open: ticker.Open, + Close: ticker.Close, + Volume: ticker.BaseVolume, + QuoteVolume: ticker.QuoteVolume, + High: ticker.High, + Low: ticker.Low, + Last: ticker.Last, + AssetType: asset.Spot, + Pair: currency.NewPairFromString(c), } case strings.Contains(result.Method, "trades"): @@ -238,7 +239,7 @@ func (g *Gateio) WsHandleData() { newOrderBook.Pair = currency.NewPairFromString(c) err = g.Websocket.Orderbook.LoadSnapshot(&newOrderBook, - false) + true) if err != nil { g.Websocket.DataHandler <- err } diff --git a/exchanges/gateio/gateio_wrapper.go b/exchanges/gateio/gateio_wrapper.go index f07893c1..7bd1db10 100644 --- a/exchanges/gateio/gateio_wrapper.go +++ b/exchanges/gateio/gateio_wrapper.go @@ -198,20 +198,26 @@ func (g *Gateio) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Pri if err != nil { return tickerPrice, err } - - for _, x := range g.GetEnabledPairs(assetType) { - currency := g.FormatExchangeCurrency(x, assetType).String() - var tp ticker.Price - tp.Pair = x - tp.High = result[currency].High - tp.Last = result[currency].Last - tp.Last = result[currency].Last - tp.Low = result[currency].Low - tp.Volume = result[currency].Volume - - err = ticker.ProcessTicker(g.Name, &tp, assetType) - if err != nil { - return tickerPrice, err + pairs := g.GetEnabledPairs(assetType) + for i := range pairs { + for k := range result { + if !strings.EqualFold(k, pairs[i].String()) { + continue + } + tickerPrice = ticker.Price{ + Last: result[k].Last, + High: result[k].High, + Low: result[k].Low, + Volume: result[k].BaseVolume, + QuoteVolume: result[k].QuoteVolume, + Open: result[k].Open, + Close: result[k].Close, + Pair: pairs[i], + } + err = ticker.ProcessTicker(g.Name, &tickerPrice, assetType) + if err != nil { + log.Error(log.Ticker, err) + } } } diff --git a/exchanges/gemini/gemini.go b/exchanges/gemini/gemini.go index beddc304..0e11a441 100644 --- a/exchanges/gemini/gemini.go +++ b/exchanges/gemini/gemini.go @@ -74,48 +74,20 @@ func (g *Gemini) GetSymbols() ([]string, error) { } // GetTicker returns information about recent trading activity for the symbol -func (g *Gemini) GetTicker(currencyPair string) (Ticker, error) { - type TickerResponse struct { - Ask float64 `json:"ask,string"` - Bid float64 `json:"bid,string"` - Last float64 `json:"last,string"` - Volume map[string]interface{} - Message string `json:"message"` - } - - ticker := Ticker{} - resp := TickerResponse{} - path := fmt.Sprintf("%s/v%s/%s/%s", g.API.Endpoints.URL, geminiAPIVersion, geminiTicker, currencyPair) - - err := g.SendHTTPRequest(path, &resp) +func (g *Gemini) GetTicker(currencyPair string) (TickerV2, error) { + ticker := TickerV2{} + path := fmt.Sprintf("%s/v2/ticker/%s", g.API.Endpoints.URL, currencyPair) + err := g.SendHTTPRequest(path, &ticker) if err != nil { return ticker, err } - - if resp.Message != "" { - return ticker, errors.New(resp.Message) + if ticker.Result == "error" { + return ticker, fmt.Errorf("%v %v %v", + g.Name, + ticker.Reason, + ticker.Message) } - ticker.Ask = resp.Ask - ticker.Bid = resp.Bid - ticker.Last = resp.Last - ticker.Volume.Currency, _ = strconv.ParseFloat(resp.Volume[currencyPair[0:3]].(string), 64) - - if strings.Contains(currencyPair, "USD") { - ticker.Volume.USD, _ = strconv.ParseFloat(resp.Volume["USD"].(string), 64) - } else { - if resp.Volume["ETH"] != nil { - ticker.Volume.ETH, _ = strconv.ParseFloat(resp.Volume["ETH"].(string), 64) - } - - if resp.Volume["BTC"] != nil { - ticker.Volume.BTC, _ = strconv.ParseFloat(resp.Volume["BTC"].(string), 64) - } - } - - time, _ := resp.Volume["timestamp"].(float64) - ticker.Volume.Timestamp = int64(time) - return ticker, nil } diff --git a/exchanges/gemini/gemini_types.go b/exchanges/gemini/gemini_types.go index ebe461ec..de687b43 100644 --- a/exchanges/gemini/gemini_types.go +++ b/exchanges/gemini/gemini_types.go @@ -16,6 +16,21 @@ type Ticker struct { } } +// TickerV2 holds returned ticker data from the exchange +type TickerV2 struct { + Ask float64 `json:"ask,string"` + Bid float64 `json:"bid,string"` + Changes []string `json:"changes"` + Close float64 `json:"close,string"` + High float64 `json:"high,string"` + Low float64 `json:"low,string"` + Open float64 `json:"open,string"` + Message string `json:"message,omitempty"` + Reason string `json:"reason,omitempty"` + Result string `json:"result,omitempty"` + Symbol currency.Pair `json:"symbol"` +} + // Orderbook contains orderbook information for both bid and ask side type Orderbook struct { Bids []OrderbookEntry `json:"bids"` diff --git a/exchanges/gemini/gemini_wrapper.go b/exchanges/gemini/gemini_wrapper.go index 43f6cda0..0e4d76f9 100644 --- a/exchanges/gemini/gemini_wrapper.go +++ b/exchanges/gemini/gemini_wrapper.go @@ -222,12 +222,15 @@ func (g *Gemini) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Pri if err != nil { return tickerPrice, err } - tickerPrice.Pair = p - tickerPrice.Ask = tick.Ask - tickerPrice.Bid = tick.Bid - tickerPrice.Last = tick.Last - tickerPrice.Volume = tick.Volume.USD - + tickerPrice = ticker.Price{ + High: tick.High, + Low: tick.Low, + Bid: tick.Bid, + Ask: tick.Ask, + Open: tick.Open, + Close: tick.Close, + Pair: p, + } err = ticker.ProcessTicker(g.GetName(), &tickerPrice, assetType) if err != nil { return tickerPrice, err diff --git a/exchanges/hitbtc/hitbtc.go b/exchanges/hitbtc/hitbtc.go index e187209f..f6145843 100644 --- a/exchanges/hitbtc/hitbtc.go +++ b/exchanges/hitbtc/hitbtc.go @@ -116,65 +116,17 @@ func (h *HitBTC) GetSymbolsDetailed() ([]Symbol, error) { } // GetTicker returns ticker information -func (h *HitBTC) GetTicker(symbol string) (map[string]Ticker, error) { - var resp1 []TickerResponse - resp2 := TickerResponse{} - ret := make(map[string]TickerResponse) - result := make(map[string]Ticker) +func (h *HitBTC) GetTicker(symbol string) (TickerResponse, error) { + var resp TickerResponse path := fmt.Sprintf("%s/%s/%s", h.API.Endpoints.URL, apiV2Ticker, symbol) - var err error + return resp, h.SendHTTPRequest(path, &resp) +} - if symbol == "" { - err = h.SendHTTPRequest(path, &resp1) - if err != nil { - return nil, err - } - - for i := range resp1 { - if resp1[i].Symbol != "" { - ret[resp1[i].Symbol] = resp1[i] - } - } - } else { - err = h.SendHTTPRequest(path, &resp2) - ret[resp2.Symbol] = resp2 - } - - if err == nil { - for i := range ret { - tick := Ticker{} - - ask, _ := strconv.ParseFloat(ret[i].Ask, 64) - tick.Ask = ask - - bid, _ := strconv.ParseFloat(ret[i].Bid, 64) - tick.Bid = bid - - high, _ := strconv.ParseFloat(ret[i].High, 64) - tick.High = high - - last, _ := strconv.ParseFloat(ret[i].Last, 64) - tick.Last = last - - low, _ := strconv.ParseFloat(ret[i].Low, 64) - tick.Low = low - - open, _ := strconv.ParseFloat(ret[i].Open, 64) - tick.Open = open - - vol, _ := strconv.ParseFloat(ret[i].Volume, 64) - tick.Volume = vol - - volQuote, _ := strconv.ParseFloat(ret[i].VolumeQuote, 64) - tick.VolumeQuote = volQuote - - tick.Symbol = ret[i].Symbol - tick.Timestamp = ret[i].Timestamp - result[i] = tick - } - } - - return result, err +// GetTickers returns ticker information +func (h *HitBTC) GetTickers() ([]TickerResponse, error) { + var resp []TickerResponse + path := fmt.Sprintf("%s/%s/", h.API.Endpoints.URL, apiV2Ticker) + return resp, h.SendHTTPRequest(path, &resp) } // GetTrades returns trades from hitbtc diff --git a/exchanges/hitbtc/hitbtc_test.go b/exchanges/hitbtc/hitbtc_test.go index 962956f9..71482667 100644 --- a/exchanges/hitbtc/hitbtc_test.go +++ b/exchanges/hitbtc/hitbtc_test.go @@ -97,6 +97,20 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { } } +func TestGetAllTickers(t *testing.T) { + _, err := h.GetTickers() + if err != nil { + t.Error(err) + } +} + +func TestGetSingularTicker(t *testing.T) { + _, err := h.GetTicker("BTCUSD") + if err != nil { + t.Error(err) + } +} + func TestGetFee(t *testing.T) { h.SetDefaults() TestSetup(t) diff --git a/exchanges/hitbtc/hitbtc_types.go b/exchanges/hitbtc/hitbtc_types.go index 251c46e5..131ee00f 100644 --- a/exchanges/hitbtc/hitbtc_types.go +++ b/exchanges/hitbtc/hitbtc_types.go @@ -6,32 +6,18 @@ import ( "github.com/thrasher-corp/gocryptotrader/currency" ) -// Ticker holds ticker information -type Ticker struct { - Last float64 - Ask float64 - Bid float64 - Timestamp time.Time - Volume float64 - VolumeQuote float64 - Symbol string - High float64 - Low float64 - Open float64 -} - // TickerResponse is the response type type TickerResponse struct { - Last string `json:"last"` // Last trade price - Ask string `json:"ask"` // Best ask price - Bid string `json:"bid"` // Best bid price - Timestamp time.Time `json:"timestamp,string"` // Last update or refresh ticker timestamp - Volume string `json:"volume"` // Total trading amount within 24 hours in base currency - VolumeQuote string `json:"volumeQuote"` // Total trading amount within 24 hours in quote currency - Symbol string `json:"symbol"` - High string `json:"high"` // Highest trade price within 24 hours - Low string `json:"low"` // Lowest trade price within 24 hours - Open string `json:"open"` // Last trade price 24 hours ago + Ask float64 `json:"ask,string"` + Bid float64 `json:"bid,string"` + High float64 `json:"high,string"` + Last float64 `json:"last,string"` + Low float64 `json:"low,string"` + Open float64 `json:"open,string"` + Volume float64 `json:"volume,string"` + VolumeQuote float64 `json:"volumeQuote,string"` + Symbol currency.Pair `json:"symbol"` + Timestamp time.Time `json:"timestamp"` } // Symbol holds symbol data @@ -336,16 +322,16 @@ type params struct { // WsTicker defines websocket ticker feed return params type WsTicker struct { Params struct { - Ask float64 `json:"ask,string"` - Bid float64 `json:"bid,string"` - Last float64 `json:"last,string"` - Open float64 `json:"open,string"` - Low float64 `json:"low,string"` - High float64 `json:"high,string"` - Volume float64 `json:"volume,string"` - VolumeQuote float64 `json:"volumeQuote,string"` - Timestamp string `json:"timestamp"` - Symbol string `json:"symbol"` + Ask float64 `json:"ask,string"` + Bid float64 `json:"bid,string"` + Last float64 `json:"last,string"` + Open float64 `json:"open,string"` + Low float64 `json:"low,string"` + High float64 `json:"high,string"` + Volume float64 `json:"volume,string"` + VolumeQuote float64 `json:"volumeQuote,string"` + Timestamp string `json:"timestamp"` + Symbol currency.Pair `json:"symbol"` } `json:"params"` } diff --git a/exchanges/hitbtc/hitbtc_websocket.go b/exchanges/hitbtc/hitbtc_websocket.go index 68c8a94d..d979998e 100644 --- a/exchanges/hitbtc/hitbtc_websocket.go +++ b/exchanges/hitbtc/hitbtc_websocket.go @@ -116,14 +116,18 @@ func (h *HitBTC) handleSubscriptionUpdates(resp wshandler.WebsocketResponse, ini return } h.Websocket.DataHandler <- wshandler.TickerData{ - Exchange: h.GetName(), - AssetType: asset.Spot, - Pair: currency.NewPairFromString(ticker.Params.Symbol), - Quantity: ticker.Params.Volume, - Timestamp: ts, - OpenPrice: ticker.Params.Open, - HighPrice: ticker.Params.High, - LowPrice: ticker.Params.Low, + Exchange: h.Name, + Open: ticker.Params.Open, + Volume: ticker.Params.Volume, + QuoteVolume: ticker.Params.VolumeQuote, + High: ticker.Params.High, + Low: ticker.Params.Low, + Bid: ticker.Params.Bid, + Ask: ticker.Params.Ask, + Last: ticker.Params.Last, + Timestamp: ts, + AssetType: asset.Spot, + Pair: ticker.Params.Symbol, } case "snapshotOrderbook": var obSnapshot WsOrderbook diff --git a/exchanges/hitbtc/hitbtc_wrapper.go b/exchanges/hitbtc/hitbtc_wrapper.go index a76fc734..0a3ec744 100644 --- a/exchanges/hitbtc/hitbtc_wrapper.go +++ b/exchanges/hitbtc/hitbtc_wrapper.go @@ -196,7 +196,7 @@ func (h *HitBTC) FetchTradablePairs(asset asset.Item) ([]string, error) { var pairs []string for x := range symbols { - pairs = append(pairs, symbols[x].BaseCurrency+"-"+symbols[x].QuoteCurrency) + pairs = append(pairs, fmt.Sprintf("%v%v%v", symbols[x].BaseCurrency, h.GetPairFormat(asset, false).Delimiter, symbols[x].QuoteCurrency)) } return pairs, nil } @@ -214,25 +214,33 @@ func (h *HitBTC) UpdateTradablePairs(forceUpdate bool) error { // UpdateTicker updates and returns the ticker for a currency pair func (h *HitBTC) UpdateTicker(currencyPair currency.Pair, assetType asset.Item) (ticker.Price, error) { - tick, err := h.GetTicker("") + var tickerPrice ticker.Price + tick, err := h.GetTickers() if err != nil { - return ticker.Price{}, err + return tickerPrice, err } - - for _, x := range h.GetEnabledPairs(assetType) { - var tp ticker.Price - curr := h.FormatExchangeCurrency(x, assetType).String() - tp.Pair = x - tp.Ask = tick[curr].Ask - tp.Bid = tick[curr].Bid - tp.High = tick[curr].High - tp.Last = tick[curr].Last - tp.Low = tick[curr].Low - tp.Volume = tick[curr].Volume - - err = ticker.ProcessTicker(h.GetName(), &tp, assetType) - if err != nil { - return ticker.Price{}, err + pairs := h.GetEnabledPairs(assetType) + for i := range pairs { + for j := range tick { + if !tick[j].Symbol.Equal(pairs[i]) { + continue + } + tickerPrice := ticker.Price{ + Last: tick[j].Last, + High: tick[j].High, + Low: tick[j].Low, + Bid: tick[j].Bid, + Ask: tick[j].Ask, + Volume: tick[j].Volume, + QuoteVolume: tick[j].VolumeQuote, + Open: tick[j].Open, + Pair: pairs[i], + LastUpdated: tick[j].Timestamp, + } + err = ticker.ProcessTicker(h.GetName(), &tickerPrice, assetType) + if err != nil { + log.Error(log.Ticker, err) + } } } return ticker.GetTicker(h.Name, currencyPair, assetType) diff --git a/exchanges/huobi/huobi.go b/exchanges/huobi/huobi.go index 826fe3a7..f01482ea 100644 --- a/exchanges/huobi/huobi.go +++ b/exchanges/huobi/huobi.go @@ -32,6 +32,7 @@ const ( huobiMarketDetailMerged = "market/detail/merged" huobiMarketDepth = "market/depth" huobiMarketTrade = "market/trade" + huobiMarketTickers = "market/tickers" huobiMarketTradeHistory = "market/history/trade" huobiSymbols = "common/symbols" huobiCurrencies = "common/currencys" @@ -95,6 +96,13 @@ func (h *HUOBI) GetSpotKline(arg KlinesRequestParams) ([]KlineItem, error) { return result.Data, err } +// GetTickers returns the ticker for the specified symbol +func (h *HUOBI) GetTickers() (Tickers, error) { + var result Tickers + urlPath := fmt.Sprintf("%s/%s", h.API.Endpoints.URL, huobiMarketTickers) + return result, h.SendHTTPRequest(urlPath, &result) +} + // GetMarketDetailMerged returns the ticker for the specified symbol func (h *HUOBI) GetMarketDetailMerged(symbol string) (DetailMerged, error) { vals := url.Values{} diff --git a/exchanges/huobi/huobi_test.go b/exchanges/huobi/huobi_test.go index 5786d817..7f4b401e 100644 --- a/exchanges/huobi/huobi_test.go +++ b/exchanges/huobi/huobi_test.go @@ -170,6 +170,13 @@ func TestGetCurrencies(t *testing.T) { } } +func TestGetTicker(t *testing.T) { + _, err := h.GetTickers() + if err != nil { + t.Error(err) + } +} + func TestGetTimestamp(t *testing.T) { t.Parallel() _, err := h.GetTimestamp() diff --git a/exchanges/huobi/huobi_types.go b/exchanges/huobi/huobi_types.go index 7e2d3ee0..d40fe90e 100644 --- a/exchanges/huobi/huobi_types.go +++ b/exchanges/huobi/huobi_types.go @@ -19,7 +19,7 @@ type KlineItem struct { Low float64 `json:"low"` High float64 `json:"high"` Amount float64 `json:"amount"` - Vol float64 `json:"vol"` + Volume float64 `json:"vol"` Count int `json:"count"` } @@ -42,6 +42,23 @@ type DetailMerged struct { Bid []float64 `json:"bid"` } +// Tickers contain all tickers +type Tickers struct { + Data []Ticker `json:"data"` +} + +// Ticker latest ticker data +type Ticker struct { + Amount float64 `json:"amount"` + Close float64 `json:"close"` + Count int64 `json:"count"` + High float64 `json:"high"` + Low float64 `json:"low"` + Open float64 `json:"open"` + Symbol currency.Pair `json:"symbol"` + Volume float64 `json:"vol"` +} + // OrderBookDataRequestParamsType var for request param types type OrderBookDataRequestParamsType string @@ -322,6 +339,22 @@ type WsKline struct { } } +type WsTick struct { + Channel string `json:"ch"` + Timestamp int64 `json:"ts"` + Tick struct { + Amount float64 `json:"amount"` + Close float64 `json:"close"` + Count float64 `json:"count"` + High float64 `json:"high"` + ID float64 `json:"id"` + Low float64 `json:"low"` + Open float64 `json:"open"` + Timestamp float64 `json:"ts"` + Volume float64 `json:"vol"` + } `json:"tick"` +} + // WsTrade defines market trade websocket response type WsTrade struct { Channel string `json:"ch"` diff --git a/exchanges/huobi/huobi_websocket.go b/exchanges/huobi/huobi_websocket.go index daef0a08..3bbca6cc 100644 --- a/exchanges/huobi/huobi_websocket.go +++ b/exchanges/huobi/huobi_websocket.go @@ -23,10 +23,11 @@ import ( const ( baseWSURL = "wss://api.huobi.pro" - wsMarketURL = baseWSURL + "/ws" - wsMarketKline = "market.%s.kline.1min" - wsMarketDepth = "market.%s.depth.step0" - wsMarketTrade = "market.%s.trade.detail" + wsMarketURL = baseWSURL + "/ws" + wsMarketKline = "market.%s.kline.1min" + wsMarketDepth = "market.%s.depth.step0" + wsMarketTrade = "market.%s.trade.detail" + wsMarketTicker = "market.%s.detail" wsAccountsOrdersEndPoint = "/ws/v1" wsAccountsList = "accounts.list" @@ -253,7 +254,7 @@ func (h *HUOBI) wsHandleMarketData(resp WsMessage) { LowPrice: kline.Tick.Low, Volume: kline.Tick.Volume, } - case strings.Contains(init.Channel, "trade"): + case strings.Contains(init.Channel, "trade.detail"): var trade WsTrade err := common.JSONDecode(resp.Raw, &trade) if err != nil { @@ -267,6 +268,26 @@ func (h *HUOBI) wsHandleMarketData(resp WsMessage) { CurrencyPair: currency.NewPairFromString(data[1]), Timestamp: time.Unix(0, trade.Tick.Timestamp), } + case strings.Contains(init.Channel, "detail"): + var ticker WsTick + err := common.JSONDecode(resp.Raw, &ticker) + if err != nil { + h.Websocket.DataHandler <- err + return + } + data := strings.Split(ticker.Channel, ".") + h.Websocket.DataHandler <- wshandler.TickerData{ + Exchange: h.Name, + Open: ticker.Tick.Open, + Close: ticker.Tick.Close, + Volume: ticker.Tick.Amount, + QuoteVolume: ticker.Tick.Volume, + High: ticker.Tick.High, + Low: ticker.Tick.Low, + Timestamp: time.Unix(0, ticker.Timestamp), + AssetType: asset.Spot, + Pair: currency.NewPairFromString(data[1]), + } } } @@ -303,7 +324,7 @@ func (h *HUOBI) WsProcessOrderbook(update *WsDepth, symbol string) error { // GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions() func (h *HUOBI) GenerateDefaultSubscriptions() { - var channels = []string{wsMarketKline, wsMarketDepth, wsMarketTrade} + var channels = []string{wsMarketKline, wsMarketDepth, wsMarketTrade, wsMarketTicker} var subscriptions []wshandler.WebsocketChannelSubscription if h.Websocket.CanUseAuthenticatedEndpoints() { channels = append(channels, "orders.%v", "orders.%v.update") diff --git a/exchanges/huobi/huobi_wrapper.go b/exchanges/huobi/huobi_wrapper.go index 69c4c2fa..db57407c 100644 --- a/exchanges/huobi/huobi_wrapper.go +++ b/exchanges/huobi/huobi_wrapper.go @@ -72,7 +72,7 @@ func (h *HUOBI) SetDefaults() { Websocket: true, RESTCapabilities: exchange.ProtocolFeatures{ AutoPairUpdates: true, - TickerBatching: false, + TickerBatching: true, }, WithdrawPermissions: exchange.AutoWithdrawCryptoWithSetup | exchange.NoFiatWithdrawals, @@ -228,7 +228,7 @@ func (h *HUOBI) FetchTradablePairs(asset asset.Item) ([]string, error) { var pairs []string for x := range symbols { - pairs = append(pairs, symbols[x].BaseCurrency+"-"+symbols[x].QuoteCurrency) + pairs = append(pairs, fmt.Sprintf("%v%v%v", symbols[x].BaseCurrency, h.GetPairFormat(asset, false).Delimiter, symbols[x].QuoteCurrency)) } return pairs, nil @@ -248,28 +248,29 @@ func (h *HUOBI) UpdateTradablePairs(forceUpdate bool) error { // UpdateTicker updates and returns the ticker for a currency pair func (h *HUOBI) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) { var tickerPrice ticker.Price - tick, err := h.GetMarketDetailMerged(h.FormatExchangeCurrency(p, assetType).String()) + tickers, err := h.GetTickers() if err != nil { return tickerPrice, err } - - tickerPrice.Pair = p - tickerPrice.Low = tick.Low - tickerPrice.Last = tick.Close - tickerPrice.Volume = tick.Volume - tickerPrice.High = tick.High - - if len(tick.Ask) > 0 { - tickerPrice.Ask = tick.Ask[0] - } - - if len(tick.Bid) > 0 { - tickerPrice.Bid = tick.Bid[0] - } - - err = ticker.ProcessTicker(h.GetName(), &tickerPrice, assetType) - if err != nil { - return tickerPrice, err + pairs := h.GetEnabledPairs(assetType) + for i := range pairs { + for j := range tickers.Data { + if !pairs[i].Equal(tickers.Data[j].Symbol) { + continue + } + tickerPrice := ticker.Price{ + High: tickers.Data[j].High, + Low: tickers.Data[j].Low, + Volume: tickers.Data[j].Volume, + Open: tickers.Data[j].Open, + Close: tickers.Data[j].Close, + Pair: tickers.Data[j].Symbol, + } + err = ticker.ProcessTicker(h.GetName(), &tickerPrice, assetType) + if err != nil { + log.Error(log.Ticker, err) + } + } } return ticker.GetTicker(h.Name, p, assetType) diff --git a/exchanges/huobihadax/huobihadax.go b/exchanges/huobihadax/huobihadax.go index bb2ccd5d..662b219e 100644 --- a/exchanges/huobihadax/huobihadax.go +++ b/exchanges/huobihadax/huobihadax.go @@ -28,6 +28,7 @@ const ( huobihadaxMarketDetail = "market/detail" huobihadaxMarketDetailMerged = "market/detail/merged" huobihadaxMarketDepth = "market/depth" + huobihadaxMarketTicker = "market/tickers" huobihadaxMarketTrade = "market/trade" huobihadaxMarketTradeHistory = "market/history/trade" huobihadaxSymbols = "common/symbols" @@ -252,6 +253,13 @@ func (h *HUOBIHADAX) GetCurrencies() ([]string, error) { return result.Currencies, err } +// GetTickers returns the ticker for the specified symbol +func (h *HUOBIHADAX) GetTickers() (Tickers, error) { + var result Tickers + urlPath := fmt.Sprintf("%s/%s", h.API.Endpoints.URL, huobihadaxMarketTicker) + return result, h.SendHTTPRequest(urlPath, &result) +} + // GetTimestamp returns the Huobi server time func (h *HUOBIHADAX) GetTimestamp() (int64, error) { type response struct { diff --git a/exchanges/huobihadax/huobihadax_test.go b/exchanges/huobihadax/huobihadax_test.go index 66092405..2ea71a8b 100644 --- a/exchanges/huobihadax/huobihadax_test.go +++ b/exchanges/huobihadax/huobihadax_test.go @@ -215,6 +215,13 @@ func TestGetCurrencies(t *testing.T) { } } +func TestGetTicker(t *testing.T) { + _, err := h.GetTickers() + if err != nil { + t.Error(err) + } +} + func TestGetTimestamp(t *testing.T) { t.Parallel() _, err := h.GetTimestamp() diff --git a/exchanges/huobihadax/huobihadax_types.go b/exchanges/huobihadax/huobihadax_types.go index fd8149c9..518ebd9d 100644 --- a/exchanges/huobihadax/huobihadax_types.go +++ b/exchanges/huobihadax/huobihadax_types.go @@ -21,10 +21,43 @@ type KlineItem struct { Low float64 `json:"low"` High float64 `json:"high"` Amount float64 `json:"amount"` - Vol float64 `json:"vol"` + Volume float64 `json:"vol"` Count int `json:"count"` } +type WsTick struct { + Channel string `json:"ch"` + Timestamp int64 `json:"ts"` + Tick struct { + Amount float64 `json:"amount"` + Close float64 `json:"close"` + Count float64 `json:"count"` + High float64 `json:"high"` + ID float64 `json:"id"` + Low float64 `json:"low"` + Open float64 `json:"open"` + Timestamp float64 `json:"ts"` + Volume float64 `json:"vol"` + } `json:"tick"` +} + +// Tickers contain all tickers +type Tickers struct { + Data []Ticker `json:"data"` +} + +// Ticker latest ticker data +type Ticker struct { + Amount float64 `json:"amount"` + Close float64 `json:"close"` + Count int64 `json:"count"` + High float64 `json:"high"` + Low float64 `json:"low"` + Open float64 `json:"open"` + Symbol currency.Pair `json:"symbol"` + Volume float64 `json:"vol"` +} + // DetailMerged stores the ticker detail merged data type DetailMerged struct { Detail diff --git a/exchanges/huobihadax/huobihadax_websocket.go b/exchanges/huobihadax/huobihadax_websocket.go index d5f57cfc..bf1c803c 100644 --- a/exchanges/huobihadax/huobihadax_websocket.go +++ b/exchanges/huobihadax/huobihadax_websocket.go @@ -25,6 +25,7 @@ const ( wsMarketKline = "market.%s.kline.1min" wsMarketDepth = "market.%s.depth.step0" wsMarketTrade = "market.%s.trade.detail" + wsMarketTicker = "market.%s.detail" wsAccountsOrdersBaseURL = "wss://api.huobi.pro" wsAccountsOrdersEndPoint = "/ws/v1" @@ -254,7 +255,7 @@ func (h *HUOBIHADAX) wsHandleMarketData(resp WsMessage) { LowPrice: kline.Tick.Low, Volume: kline.Tick.Volume, } - case strings.Contains(init.Channel, "trade"): + case strings.Contains(init.Channel, "trade.detail"): var trade WsTrade err := common.JSONDecode(resp.Raw, &trade) if err != nil { @@ -268,6 +269,26 @@ func (h *HUOBIHADAX) wsHandleMarketData(resp WsMessage) { CurrencyPair: currency.NewPairFromString(data[1]), Timestamp: time.Unix(0, trade.Tick.Timestamp), } + case strings.Contains(init.Channel, "detail"): + var ticker WsTick + err := common.JSONDecode(resp.Raw, &ticker) + if err != nil { + h.Websocket.DataHandler <- err + return + } + data := strings.Split(ticker.Channel, ".") + h.Websocket.DataHandler <- wshandler.TickerData{ + Exchange: h.Name, + Open: ticker.Tick.Open, + Close: ticker.Tick.Close, + Volume: ticker.Tick.Amount, + QuoteVolume: ticker.Tick.Volume, + High: ticker.Tick.High, + Low: ticker.Tick.Low, + Timestamp: time.Unix(0, ticker.Timestamp), + AssetType: asset.Spot, + Pair: currency.NewPairFromString(data[1]), + } } } @@ -304,7 +325,7 @@ func (h *HUOBIHADAX) WsProcessOrderbook(update *WsDepth, symbol string) error { // GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions() func (h *HUOBIHADAX) GenerateDefaultSubscriptions() { - var channels = []string{wsMarketKline, wsMarketDepth, wsMarketTrade} + var channels = []string{wsMarketKline, wsMarketDepth, wsMarketTrade, wsMarketTicker} var subscriptions []wshandler.WebsocketChannelSubscription if h.Websocket.CanUseAuthenticatedEndpoints() { channels = append(channels, "orders.%v", "orders.%v.update") diff --git a/exchanges/huobihadax/huobihadax_wrapper.go b/exchanges/huobihadax/huobihadax_wrapper.go index e1b53229..59a9f02f 100644 --- a/exchanges/huobihadax/huobihadax_wrapper.go +++ b/exchanges/huobihadax/huobihadax_wrapper.go @@ -72,7 +72,7 @@ func (h *HUOBIHADAX) SetDefaults() { Websocket: true, RESTCapabilities: exchange.ProtocolFeatures{ AutoPairUpdates: true, - TickerBatching: false, + TickerBatching: true, }, WithdrawPermissions: exchange.AutoWithdrawCryptoWithSetup | exchange.NoFiatWithdrawals, @@ -191,7 +191,7 @@ func (h *HUOBIHADAX) FetchTradablePairs(asset asset.Item) ([]string, error) { var pairs []string for x := range symbols { - pairs = append(pairs, symbols[x].BaseCurrency+"-"+symbols[x].QuoteCurrency) + pairs = append(pairs, fmt.Sprintf("%v%v%v", symbols[x].BaseCurrency, h.GetPairFormat(asset, false).Delimiter, symbols[x].QuoteCurrency)) } return pairs, nil @@ -211,28 +211,29 @@ func (h *HUOBIHADAX) UpdateTradablePairs(forceUpdate bool) error { // UpdateTicker updates and returns the ticker for a currency pair func (h *HUOBIHADAX) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) { var tickerPrice ticker.Price - tick, err := h.GetMarketDetailMerged(h.FormatExchangeCurrency(p, assetType).String()) + tickers, err := h.GetTickers() if err != nil { return tickerPrice, err } - - tickerPrice.Pair = p - tickerPrice.Low = tick.Low - tickerPrice.Last = tick.Close - tickerPrice.Volume = tick.Volume - tickerPrice.High = tick.High - - if len(tick.Ask) > 0 { - tickerPrice.Ask = tick.Ask[0] - } - - if len(tick.Bid) > 0 { - tickerPrice.Bid = tick.Bid[0] - } - - err = ticker.ProcessTicker(h.GetName(), &tickerPrice, assetType) - if err != nil { - return tickerPrice, err + pairs := h.GetEnabledPairs(assetType) + for i := range pairs { + for j := range tickers.Data { + if !pairs[i].Equal(tickers.Data[j].Symbol) { + continue + } + tickerPrice := ticker.Price{ + High: tickers.Data[j].High, + Low: tickers.Data[j].Low, + Volume: tickers.Data[j].Volume, + Open: tickers.Data[j].Open, + Close: tickers.Data[j].Close, + Pair: tickers.Data[j].Symbol, + } + err = ticker.ProcessTicker(h.GetName(), &tickerPrice, assetType) + if err != nil { + log.Error(log.Ticker, err) + } + } } return ticker.GetTicker(h.Name, p, assetType) diff --git a/exchanges/itbit/itbit_types.go b/exchanges/itbit/itbit_types.go index c3d06b07..6408c3cf 100644 --- a/exchanges/itbit/itbit_types.go +++ b/exchanges/itbit/itbit_types.go @@ -1,5 +1,7 @@ package itbit +import "time" + // GeneralReturn is a generalized return type to capture any errors type GeneralReturn struct { Code int `json:"code"` @@ -9,23 +11,23 @@ type GeneralReturn struct { // Ticker holds returned ticker information type Ticker struct { - Pair string `json:"pair"` - Bid float64 `json:"bid,string"` - BidAmt float64 `json:"bidAmt,string"` - Ask float64 `json:"ask,string"` - AskAmt float64 `json:"askAmt,string"` - LastPrice float64 `json:"lastPrice,string"` - LastAmt float64 `json:"lastAmt,string"` - Volume24h float64 `json:"volume24h,string"` - VolumeToday float64 `json:"volumeToday,string"` - High24h float64 `json:"high24h,string"` - Low24h float64 `json:"low24h,string"` - HighToday float64 `json:"highToday,string"` - LowToday float64 `json:"lowToday,string"` - OpenToday float64 `json:"openToday,string"` - VwapToday float64 `json:"vwapToday,string"` - Vwap24h float64 `json:"vwap24h,string"` - ServertimeUTC string `json:"serverTimeUTC"` + Pair string `json:"pair"` + Bid float64 `json:"bid,string"` + BidAmt float64 `json:"bidAmt,string"` + Ask float64 `json:"ask,string"` + AskAmt float64 `json:"askAmt,string"` + LastPrice float64 `json:"lastPrice,string"` + LastAmt float64 `json:"lastAmt,string"` + Volume24h float64 `json:"volume24h,string"` + VolumeToday float64 `json:"volumeToday,string"` + High24h float64 `json:"high24h,string"` + Low24h float64 `json:"low24h,string"` + HighToday float64 `json:"highToday,string"` + LowToday float64 `json:"lowToday,string"` + OpenToday float64 `json:"openToday,string"` + VwapToday float64 `json:"vwapToday,string"` + Vwap24h float64 `json:"vwap24h,string"` + ServertimeUTC time.Time `json:"serverTimeUTC"` } // OrderbookResponse contains multi-arrayed strings of bid and ask side diff --git a/exchanges/itbit/itbit_wrapper.go b/exchanges/itbit/itbit_wrapper.go index efdff51a..092baa9e 100644 --- a/exchanges/itbit/itbit_wrapper.go +++ b/exchanges/itbit/itbit_wrapper.go @@ -133,15 +133,17 @@ func (i *ItBit) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Pric if err != nil { return tickerPrice, err } - - tickerPrice.Pair = p - tickerPrice.Ask = tick.Ask - tickerPrice.Bid = tick.Bid - tickerPrice.Last = tick.LastPrice - tickerPrice.High = tick.High24h - tickerPrice.Low = tick.Low24h - tickerPrice.Volume = tick.Volume24h - + tickerPrice = ticker.Price{ + Last: tick.LastPrice, + High: tick.High24h, + Low: tick.Low24h, + Bid: tick.Bid, + Ask: tick.Ask, + Volume: tick.Volume24h, + Open: tick.OpenToday, + Pair: p, + LastUpdated: tick.ServertimeUTC, + } err = ticker.ProcessTicker(i.GetName(), &tickerPrice, assetType) if err != nil { return tickerPrice, err diff --git a/exchanges/kraken/kraken.go b/exchanges/kraken/kraken.go index e301d3f7..b3dd7633 100644 --- a/exchanges/kraken/kraken.go +++ b/exchanges/kraken/kraken.go @@ -51,6 +51,8 @@ const ( krakenUnauthRate = 0 ) +var assetPairMap map[string]string + // Kraken is the overarching type across the alphapoint package type Kraken struct { exchange.Base @@ -102,6 +104,12 @@ func (k *Kraken) GetAssetPairs() (map[string]AssetPairs, error) { if err := k.SendHTTPRequest(path, &response); err != nil { return response.Result, err } + for i := range response.Result { + if assetPairMap == nil { + assetPairMap = make(map[string]string) + } + assetPairMap[i] = response.Result[i].Altname + } return response.Result, GetError(response.Error) } @@ -134,7 +142,7 @@ func (k *Kraken) GetTicker(symbol string) (Ticker, error) { tick.Bid, _ = strconv.ParseFloat(resp.Data[i].Bid[0], 64) tick.Last, _ = strconv.ParseFloat(resp.Data[i].Last[0], 64) tick.Volume, _ = strconv.ParseFloat(resp.Data[i].Volume[1], 64) - tick.VWAP, _ = strconv.ParseFloat(resp.Data[i].VWAP[1], 64) + tick.VolumeWeightedAveragePrice, _ = strconv.ParseFloat(resp.Data[i].VolumeWeightedAveragePrice[1], 64) tick.Trades = resp.Data[i].Trades[1] tick.Low, _ = strconv.ParseFloat(resp.Data[i].Low[1], 64) tick.High, _ = strconv.ParseFloat(resp.Data[i].High[1], 64) @@ -175,7 +183,7 @@ func (k *Kraken) GetTickers(pairList string) (Tickers, error) { tick.Bid, _ = strconv.ParseFloat(resp.Data[i].Bid[0], 64) tick.Last, _ = strconv.ParseFloat(resp.Data[i].Last[0], 64) tick.Volume, _ = strconv.ParseFloat(resp.Data[i].Volume[1], 64) - tick.VWAP, _ = strconv.ParseFloat(resp.Data[i].VWAP[1], 64) + tick.VolumeWeightedAveragePrice, _ = strconv.ParseFloat(resp.Data[i].VolumeWeightedAveragePrice[1], 64) tick.Trades = resp.Data[i].Trades[1] tick.Low, _ = strconv.ParseFloat(resp.Data[i].Low[1], 64) tick.High, _ = strconv.ParseFloat(resp.Data[i].High[1], 64) @@ -224,7 +232,7 @@ func (k *Kraken) GetOHLC(symbol string) ([]OpenHighLowClose, error) { case 4: o.Close, _ = strconv.ParseFloat(x.(string), 64) case 5: - o.Vwap, _ = strconv.ParseFloat(x.(string), 64) + o.VolumeWeightedAveragePrice, _ = strconv.ParseFloat(x.(string), 64) case 6: o.Volume, _ = strconv.ParseFloat(x.(string), 64) case 7: @@ -767,8 +775,8 @@ func (k *Kraken) AddOrder(symbol, side, orderType string, volume, price, price2, params.Set("leverage", strconv.FormatFloat(leverage, 'f', -1, 64)) } - if args.Oflags != "" { - params.Set("oflags", args.Oflags) + if args.OrderFlags != "" { + params.Set("oflags", args.OrderFlags) } if args.StartTm != "" { diff --git a/exchanges/kraken/kraken_test.go b/exchanges/kraken/kraken_test.go index ef09cb61..2b4ce98f 100644 --- a/exchanges/kraken/kraken_test.go +++ b/exchanges/kraken/kraken_test.go @@ -235,7 +235,7 @@ func TestGetTradeVolume(t *testing.T) { // TestAddOrder API endpoint test func TestAddOrder(t *testing.T) { t.Parallel() - args := AddOrderOptions{Oflags: "fcib"} + args := AddOrderOptions{OrderFlags: "fcib"} _, err := k.AddOrder("XXBTZUSD", exchange.SellOrderSide.ToLower().ToString(), exchange.LimitOrderType.ToLower().ToString(), 0.00000001, 0, 0, 0, &args) diff --git a/exchanges/kraken/kraken_types.go b/exchanges/kraken/kraken_types.go index e815f651..e816afd4 100644 --- a/exchanges/kraken/kraken_types.go +++ b/exchanges/kraken/kraken_types.go @@ -38,15 +38,15 @@ type AssetPairs struct { // Ticker is a standard ticker type type Ticker struct { - Ask float64 - Bid float64 - Last float64 - Volume float64 - VWAP float64 - Trades int64 - Low float64 - High float64 - Open float64 + Ask float64 + Bid float64 + Last float64 + Volume float64 + VolumeWeightedAveragePrice float64 + Trades int64 + Low float64 + High float64 + Open float64 } // Tickers stores a map of tickers @@ -54,27 +54,27 @@ type Tickers map[string]Ticker // TickerResponse holds ticker information before its put into the Ticker struct type TickerResponse struct { - Ask []string `json:"a"` - Bid []string `json:"b"` - Last []string `json:"c"` - Volume []string `json:"v"` - VWAP []string `json:"p"` - Trades []int64 `json:"t"` - Low []string `json:"l"` - High []string `json:"h"` - Open string `json:"o"` + Ask []string `json:"a"` + Bid []string `json:"b"` + Last []string `json:"c"` + Volume []string `json:"v"` + VolumeWeightedAveragePrice []string `json:"p"` + Trades []int64 `json:"t"` + Low []string `json:"l"` + High []string `json:"h"` + Open string `json:"o"` } // OpenHighLowClose contains ticker event information type OpenHighLowClose struct { - Time float64 - Open float64 - High float64 - Low float64 - Close float64 - Vwap float64 - Volume float64 - Count float64 + Time float64 + Open float64 + High float64 + Low float64 + Close float64 + VolumeWeightedAveragePrice float64 + Volume float64 + Count float64 } // RecentTrades holds recent trade data @@ -125,13 +125,13 @@ type TradeBalanceInfo struct { // OrderInfo type type OrderInfo struct { - RefID string `json:"refid"` - UserRef int32 `json:"userref"` - Status string `json:"status"` - OpenTm float64 `json:"opentm"` - StartTm float64 `json:"starttm"` - ExpireTm float64 `json:"expiretm"` - Descr struct { + RefID string `json:"refid"` + UserRef int32 `json:"userref"` + Status string `json:"status"` + OpenTime float64 `json:"opentm"` + StartTime float64 `json:"starttm"` + ExpireTime float64 `json:"expiretm"` + Description struct { Pair string `json:"pair"` Type string `json:"type"` OrderType string `json:"ordertype"` @@ -141,16 +141,16 @@ type OrderInfo struct { Order string `json:"order"` Close string `json:"close"` } `json:"descr"` - Vol float64 `json:"vol,string"` - VolExec float64 `json:"vol_exec,string"` - Cost float64 `json:"cost,string"` - Fee float64 `json:"fee,string"` - Price float64 `json:"price,string"` - StopPrice float64 `json:"stopprice,string"` - LimitPrice float64 `json:"limitprice,string"` - Misc string `json:"misc"` - Oflags string `json:"oflags"` - Trades []string `json:"trades"` + Volume float64 `json:"vol,string"` + VolumeExecuted float64 `json:"vol_exec,string"` + Cost float64 `json:"cost,string"` + Fee float64 `json:"fee,string"` + Price float64 `json:"price,string"` + StopPrice float64 `json:"stopprice,string"` + LimitPrice float64 `json:"limitprice,string"` + Misc string `json:"misc"` + OrderFlags string `json:"oflags"` + Trades []string `json:"trades"` } // OpenOrders type @@ -198,44 +198,44 @@ type TradesHistory struct { // TradeInfo type type TradeInfo struct { - OrderTxID string `json:"ordertxid"` - Pair string `json:"pair"` - Time float64 `json:"time"` - Type string `json:"type"` - OrderType string `json:"ordertype"` - Price float64 `json:"price,string"` - Cost float64 `json:"cost,string"` - Fee float64 `json:"fee,string"` - Vol float64 `json:"vol,string"` - Margin float64 `json:"margin,string"` - Misc string `json:"misc"` - PosTxID string `json:"postxid"` - Cprice float64 `json:"cprice,string"` - Cfee float64 `json:"cfee,string"` - Cvol float64 `json:"cvol,string"` - Cmargin float64 `json:"cmargin,string"` - Trades []string `json:"trades"` - PosStatus string `json:"posstatus"` + OrderTxID string `json:"ordertxid"` + Pair string `json:"pair"` + Time float64 `json:"time"` + Type string `json:"type"` + OrderType string `json:"ordertype"` + Price float64 `json:"price,string"` + Cost float64 `json:"cost,string"` + Fee float64 `json:"fee,string"` + Volume float64 `json:"vol,string"` + Margin float64 `json:"margin,string"` + Misc string `json:"misc"` + PosTxID string `json:"postxid"` + ClosedPositionAveragePrice float64 `json:"cprice,string"` + ClosedPositionFee float64 `json:"cfee,string"` + ClosedPositionVolume float64 `json:"cvol,string"` + ClosedPositionMargin float64 `json:"cmargin,string"` + Trades []string `json:"trades"` + PosStatus string `json:"posstatus"` } // Position holds the opened position type Position struct { - Ordertxid string `json:"ordertxid"` - Pair string `json:"pair"` - Time float64 `json:"time"` - Type string `json:"type"` - OrderType string `json:"ordertype"` - Cost float64 `json:"cost,string"` - Fee float64 `json:"fee,string"` - Vol float64 `json:"vol,string"` - VolClosed float64 `json:"vol_closed,string"` - Margin float64 `json:"margin,string"` - Rollovertm int64 `json:"rollovertm,string"` - Misc string `json:"misc"` - Oflags string `json:"oflags"` - PosStatus string `json:"posstatus"` - Net string `json:"net"` - Terms string `json:"terms"` + Ordertxid string `json:"ordertxid"` + Pair string `json:"pair"` + Time float64 `json:"time"` + Type string `json:"type"` + OrderType string `json:"ordertype"` + Cost float64 `json:"cost,string"` + Fee float64 `json:"fee,string"` + Volume float64 `json:"vol,string"` + VolumeClosed float64 `json:"vol_closed,string"` + Margin float64 `json:"margin,string"` + RolloverTime int64 `json:"rollovertm,string"` + Misc string `json:"misc"` + OrderFlags string `json:"oflags"` + PositionStatus string `json:"posstatus"` + Net string `json:"net"` + Terms string `json:"terms"` } // GetLedgersOptions type @@ -314,7 +314,7 @@ type OrderDescription struct { // AddOrderOptions represents the AddOrder options type AddOrderOptions struct { UserRef int32 - Oflags string + OrderFlags string StartTm string ExpireTm string CloseOrderType string diff --git a/exchanges/kraken/kraken_websocket.go b/exchanges/kraken/kraken_websocket.go index 0fe9456a..ddb3c98c 100644 --- a/exchanges/kraken/kraken_websocket.go +++ b/exchanges/kraken/kraken_websocket.go @@ -241,6 +241,8 @@ func getSubscriptionChannelData(id int64) WebsocketChannelData { // wsProcessTickers converts ticker data and sends it to the datahandler func (k *Kraken) wsProcessTickers(channelData *WebsocketChannelData, data interface{}) { tickerData := data.(map[string]interface{}) + askData := tickerData["a"].([]interface{}) + bidData := tickerData["b"].([]interface{}) closeData := tickerData["c"].([]interface{}) openData := tickerData["o"].([]interface{}) lowData := tickerData["l"].([]interface{}) @@ -251,17 +253,21 @@ func (k *Kraken) wsProcessTickers(channelData *WebsocketChannelData, data interf highPrice, _ := strconv.ParseFloat(highData[0].(string), 64) lowPrice, _ := strconv.ParseFloat(lowData[0].(string), 64) quantity, _ := strconv.ParseFloat(volumeData[0].(string), 64) + ask, _ := strconv.ParseFloat(askData[0].(string), 64) + bid, _ := strconv.ParseFloat(bidData[0].(string), 64) k.Websocket.DataHandler <- wshandler.TickerData{ - Timestamp: time.Now(), - Exchange: k.Name, - AssetType: asset.Spot, - Pair: channelData.Pair, - ClosePrice: closePrice, - OpenPrice: openPrice, - HighPrice: highPrice, - LowPrice: lowPrice, - Quantity: quantity, + Exchange: k.Name, + Open: openPrice, + Close: closePrice, + Volume: quantity, + High: highPrice, + Low: lowPrice, + Bid: bid, + Ask: ask, + Timestamp: time.Now(), + AssetType: asset.Spot, + Pair: channelData.Pair, } } diff --git a/exchanges/kraken/kraken_wrapper.go b/exchanges/kraken/kraken_wrapper.go index 4baa3fc4..1b5158ea 100644 --- a/exchanges/kraken/kraken_wrapper.go +++ b/exchanges/kraken/kraken_wrapper.go @@ -62,8 +62,9 @@ func (k *Kraken) SetDefaults() { Separator: ",", }, ConfigFormat: ¤cy.PairFormat{ - Delimiter: "-", Uppercase: true, + Delimiter: "-", + Separator: ",", }, } @@ -167,9 +168,9 @@ func (k *Kraken) Run() { } forceUpdate := false - if !common.StringDataContains(k.GetEnabledPairs(asset.Spot).Strings(), "-") || - !common.StringDataContains(k.GetAvailablePairs(asset.Spot).Strings(), "-") { - enabledPairs := currency.NewPairsFromStrings([]string{"XBT-USD"}) + if !common.StringDataContains(k.GetEnabledPairs(asset.Spot).Strings(), k.GetPairFormat(asset.Spot, false).Delimiter) || + !common.StringDataContains(k.GetAvailablePairs(asset.Spot).Strings(), k.GetPairFormat(asset.Spot, false).Delimiter) { + enabledPairs := currency.NewPairsFromStrings([]string{fmt.Sprintf("BTC%vUSD", k.GetPairFormat(asset.Spot, false).Delimiter)}) log.Warn(log.ExchangeSys, "Available pairs for Kraken reset due to config upgrade, please enable the ones you would like again") forceUpdate = true @@ -210,7 +211,7 @@ func (k *Kraken) FetchTradablePairs(asset asset.Item) ([]string, error) { if v.Quote[0] == 'Z' || v.Quote[0] == 'X' { v.Quote = v.Quote[1:] } - products = append(products, v.Base+"-"+v.Quote) + products = append(products, fmt.Sprintf("%v%v%v", v.Base, k.GetPairFormat(asset, false).Delimiter, v.Quote)) } return products, nil } @@ -239,21 +240,33 @@ func (k *Kraken) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Pri return tickerPrice, err } - for _, x := range pairs { - for y, z := range tickers { - if !strings.Contains(y, x.Base.Upper().String()) || - !strings.Contains(y, x.Quote.Upper().String()) { - continue + for i := range pairs { + for curr, v := range tickers { + if !strings.EqualFold(pairs[i].String(), curr) { + var altCurrency string + var ok bool + if altCurrency, ok = assetPairMap[curr]; !ok { + continue + } + if !strings.EqualFold(pairs[i].String(), altCurrency) { + continue + } + } + + tickerPrice = ticker.Price{ + Last: v.Last, + High: v.High, + Low: v.Low, + Bid: v.Bid, + Ask: v.Ask, + Volume: v.Volume, + Open: v.Open, + Pair: pairs[i], + } + err = ticker.ProcessTicker(k.Name, &tickerPrice, assetType) + if err != nil { + log.Error(log.Ticker, err) } - var tp ticker.Price - tp.Pair = x - tp.Last = z.Last - tp.Ask = z.Ask - tp.Bid = z.Bid - tp.High = z.High - tp.Low = z.Low - tp.Volume = z.Volume - ticker.ProcessTicker(k.GetName(), &tp, assetType) } } return ticker.GetTicker(k.GetName(), p, assetType) @@ -473,19 +486,19 @@ func (k *Kraken) GetActiveOrders(getOrdersRequest *exchange.GetOrdersRequest) ([ var orders []exchange.OrderDetail for i := range resp.Open { - symbol := currency.NewPairFromString(resp.Open[i].Descr.Pair) - orderDate := time.Unix(int64(resp.Open[i].StartTm), 0) - side := exchange.OrderSide(strings.ToUpper(resp.Open[i].Descr.Type)) - orderType := exchange.OrderType(strings.ToUpper(resp.Open[i].Descr.OrderType)) + symbol := currency.NewPairFromString(resp.Open[i].Description.Pair) + orderDate := time.Unix(int64(resp.Open[i].StartTime), 0) + side := exchange.OrderSide(strings.ToUpper(resp.Open[i].Description.Type)) + orderType := exchange.OrderType(strings.ToUpper(resp.Open[i].Description.OrderType)) orders = append(orders, exchange.OrderDetail{ ID: i, - Amount: resp.Open[i].Vol, - RemainingAmount: (resp.Open[i].Vol - resp.Open[i].VolExec), - ExecutedAmount: resp.Open[i].VolExec, + Amount: resp.Open[i].Volume, + RemainingAmount: (resp.Open[i].Volume - resp.Open[i].VolumeExecuted), + ExecutedAmount: resp.Open[i].VolumeExecuted, Exchange: k.Name, OrderDate: orderDate, - Price: resp.Open[i].Descr.Price, + Price: resp.Open[i].Description.Price, OrderSide: side, OrderType: orderType, CurrencyPair: symbol, @@ -517,19 +530,19 @@ func (k *Kraken) GetOrderHistory(getOrdersRequest *exchange.GetOrdersRequest) ([ var orders []exchange.OrderDetail for i := range resp.Closed { - symbol := currency.NewPairFromString(resp.Closed[i].Descr.Pair) - orderDate := time.Unix(int64(resp.Closed[i].StartTm), 0) - side := exchange.OrderSide(strings.ToUpper(resp.Closed[i].Descr.Type)) - orderType := exchange.OrderType(strings.ToUpper(resp.Closed[i].Descr.OrderType)) + symbol := currency.NewPairFromString(resp.Closed[i].Description.Pair) + orderDate := time.Unix(int64(resp.Closed[i].StartTime), 0) + side := exchange.OrderSide(strings.ToUpper(resp.Closed[i].Description.Type)) + orderType := exchange.OrderType(strings.ToUpper(resp.Closed[i].Description.OrderType)) orders = append(orders, exchange.OrderDetail{ ID: i, - Amount: resp.Closed[i].Vol, - RemainingAmount: (resp.Closed[i].Vol - resp.Closed[i].VolExec), - ExecutedAmount: resp.Closed[i].VolExec, + Amount: resp.Closed[i].Volume, + RemainingAmount: (resp.Closed[i].Volume - resp.Closed[i].VolumeExecuted), + ExecutedAmount: resp.Closed[i].VolumeExecuted, Exchange: k.Name, OrderDate: orderDate, - Price: resp.Closed[i].Descr.Price, + Price: resp.Closed[i].Description.Price, OrderSide: side, OrderType: orderType, CurrencyPair: symbol, diff --git a/exchanges/lakebtc/lakebtc_websocket.go b/exchanges/lakebtc/lakebtc_websocket.go index 94f1dff2..162ebaf6 100644 --- a/exchanges/lakebtc/lakebtc_websocket.go +++ b/exchanges/lakebtc/lakebtc_websocket.go @@ -239,13 +239,12 @@ func (l *LakeBTC) processTicker(ticker string) error { continue } l.Websocket.DataHandler <- wshandler.TickerData{ - Timestamp: time.Now(), - Pair: currency.NewPairFromString(k), + Exchange: l.Name, + Volume: vol, + High: high, + Low: low, AssetType: asset.Spot, - Exchange: l.GetName(), - Quantity: vol, - HighPrice: high, - LowPrice: low, + Pair: currency.NewPairFromString(k), } } return nil diff --git a/exchanges/lakebtc/lakebtc_wrapper.go b/exchanges/lakebtc/lakebtc_wrapper.go index 5934bd0e..f865d0e6 100644 --- a/exchanges/lakebtc/lakebtc_wrapper.go +++ b/exchanges/lakebtc/lakebtc_wrapper.go @@ -186,25 +186,29 @@ func (l *LakeBTC) UpdateTradablePairs(forceUpdate bool) error { // UpdateTicker updates and returns the ticker for a currency pair func (l *LakeBTC) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) { - tick, err := l.GetTicker() + ticks, err := l.GetTicker() if err != nil { return ticker.Price{}, err } - for _, x := range l.GetEnabledPairs(assetType) { - currency := l.FormatExchangeCurrency(x, assetType).String() + pairs := l.GetEnabledPairs(assetType) + for i := range pairs { + currency := l.FormatExchangeCurrency(pairs[i], assetType).String() + if _, ok := ticks[currency]; !ok { + continue + } var tickerPrice ticker.Price - tickerPrice.Pair = x - tickerPrice.Ask = tick[currency].Ask - tickerPrice.Bid = tick[currency].Bid - tickerPrice.Volume = tick[currency].Volume - tickerPrice.High = tick[currency].High - tickerPrice.Low = tick[currency].Low - tickerPrice.Last = tick[currency].Last + tickerPrice.Pair = pairs[i] + tickerPrice.Ask = ticks[currency].Ask + tickerPrice.Bid = ticks[currency].Bid + tickerPrice.Volume = ticks[currency].Volume + tickerPrice.High = ticks[currency].High + tickerPrice.Low = ticks[currency].Low + tickerPrice.Last = ticks[currency].Last err = ticker.ProcessTicker(l.GetName(), &tickerPrice, assetType) if err != nil { - return tickerPrice, err + log.Error(log.Ticker, err) } } return ticker.GetTicker(l.Name, p, assetType) diff --git a/exchanges/lbank/lbank.go b/exchanges/lbank/lbank.go index 72130ff0..d7589e15 100644 --- a/exchanges/lbank/lbank.go +++ b/exchanges/lbank/lbank.go @@ -68,6 +68,15 @@ func (l *Lbank) GetTicker(symbol string) (TickerResponse, error) { return t, l.SendHTTPRequest(path, &t) } +// GetTickers returns all tickers +func (l *Lbank) GetTickers() ([]TickerResponse, error) { + var t []TickerResponse + params := url.Values{} + params.Set("symbol", "all") + path := fmt.Sprintf("%s/v%s/%s?%s", l.API.Endpoints.URL, lbankAPIVersion, lbankTicker, params.Encode()) + return t, l.SendHTTPRequest(path, &t) +} + // GetCurrencyPairs returns a list of supported currency pairs by the exchange func (l *Lbank) GetCurrencyPairs() ([]string, error) { path := fmt.Sprintf("%s/v%s/%s", l.API.Endpoints.URL, lbankAPIVersion, diff --git a/exchanges/lbank/lbank_test.go b/exchanges/lbank/lbank_test.go index e9067980..5d16b128 100644 --- a/exchanges/lbank/lbank_test.go +++ b/exchanges/lbank/lbank_test.go @@ -59,6 +59,17 @@ func TestGetTicker(t *testing.T) { } } +func TestGetTickers(t *testing.T) { + TestSetup(t) + tickers, err := l.GetTickers() + if err != nil { + t.Errorf("test failed: %v", err) + } + if len(tickers) <= 1 { + t.Errorf("Expected multiple tickers, received %v", len(tickers)) + } +} + func TestGetCurrencyPairs(t *testing.T) { TestSetup(t) _, err := l.GetCurrencyPairs() diff --git a/exchanges/lbank/lbank_types.go b/exchanges/lbank/lbank_types.go index cb6653ac..71ce1df2 100644 --- a/exchanges/lbank/lbank_types.go +++ b/exchanges/lbank/lbank_types.go @@ -2,6 +2,8 @@ package lbank import ( "encoding/json" + + "github.com/thrasher-corp/gocryptotrader/currency" ) // Ticker stores the ticker price data for a currency pair @@ -16,9 +18,9 @@ type Ticker struct { // TickerResponse stores the ticker price data and timestamp for a currency pair type TickerResponse struct { - Symbol string `json:"symbol"` - Timestamp int64 `json:"timestamp"` - Ticker Ticker `json:"ticker"` + Symbol currency.Pair `json:"symbol"` + Timestamp int64 `json:"timestamp"` + Ticker Ticker `json:"ticker"` } // MarketDepthResponse stores arrays for asks, bids and a timestamp for a currecy pair diff --git a/exchanges/lbank/lbank_wrapper.go b/exchanges/lbank/lbank_wrapper.go index b69e1965..e6c5c4df 100644 --- a/exchanges/lbank/lbank_wrapper.go +++ b/exchanges/lbank/lbank_wrapper.go @@ -69,6 +69,7 @@ func (l *Lbank) SetDefaults() { REST: true, RESTCapabilities: exchange.ProtocolFeatures{ AutoPairUpdates: true, + TickerBatching: true, }, WithdrawPermissions: exchange.AutoWithdrawCryptoWithAPIPermission | exchange.NoFiatWithdrawals, @@ -157,21 +158,30 @@ func (l *Lbank) UpdateTradablePairs(forceUpdate bool) error { // UpdateTicker updates and returns the ticker for a currency pair func (l *Lbank) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) { var tickerPrice ticker.Price - tickerInfo, err := l.GetTicker(l.FormatExchangeCurrency(p, assetType).String()) + tickerInfo, err := l.GetTickers() if err != nil { return tickerPrice, err } - tickerPrice.Pair = p - tickerPrice.Last = tickerInfo.Ticker.Latest - tickerPrice.High = tickerInfo.Ticker.High - tickerPrice.Volume = tickerInfo.Ticker.Volume - tickerPrice.Low = tickerInfo.Ticker.Low - - err = ticker.ProcessTicker(l.GetName(), &tickerPrice, assetType) - if err != nil { - return tickerPrice, err + pairs := l.GetEnabledPairs(assetType) + for i := range pairs { + for j := range tickerInfo { + if !pairs[i].Equal(tickerInfo[j].Symbol) { + continue + } + tickerPrice = ticker.Price{ + Last: tickerInfo[j].Ticker.Latest, + High: tickerInfo[j].Ticker.High, + Low: tickerInfo[j].Ticker.Low, + Volume: tickerInfo[j].Ticker.Volume, + Pair: tickerInfo[j].Symbol, + LastUpdated: time.Unix(0, tickerInfo[j].Timestamp), + } + err = ticker.ProcessTicker(l.GetName(), &tickerPrice, assetType) + if err != nil { + log.Error(log.Ticker, err) + } + } } - return ticker.GetTicker(l.Name, p, assetType) } diff --git a/exchanges/localbitcoins/localbitcoins_types.go b/exchanges/localbitcoins/localbitcoins_types.go index d0b3e337..763c48ea 100644 --- a/exchanges/localbitcoins/localbitcoins_types.go +++ b/exchanges/localbitcoins/localbitcoins_types.go @@ -312,7 +312,8 @@ type WalletBalanceInfo struct { // Ticker contains ticker information type Ticker struct { Avg12h float64 `json:"avg_12h,string"` - Avg1h float64 `json:"avg_1h,string"` + Avg1h float64 `json:"avg_1h,string,omitempty"` + Avg6h float64 `json:"avg_6h,string,omitempty"` Avg24h float64 `json:"avg_24h,string"` Rates struct { Last float64 `json:"last,string"` diff --git a/exchanges/localbitcoins/localbitcoins_wrapper.go b/exchanges/localbitcoins/localbitcoins_wrapper.go index 4c63780b..67671a14 100644 --- a/exchanges/localbitcoins/localbitcoins_wrapper.go +++ b/exchanges/localbitcoins/localbitcoins_wrapper.go @@ -160,16 +160,20 @@ func (l *LocalBitcoins) UpdateTicker(p currency.Pair, assetType asset.Item) (tic return tickerPrice, err } - for _, x := range l.GetEnabledPairs(assetType) { - currency := x.Quote.String() + pairs := l.GetEnabledPairs(assetType) + for i := range pairs { + curr := pairs[i].Quote.String() + if _, ok := tick[curr]; !ok { + continue + } var tp ticker.Price - tp.Pair = x - tp.Last = tick[currency].Avg24h - tp.Volume = tick[currency].VolumeBTC + tp.Pair = pairs[i] + tp.Last = tick[curr].Avg24h + tp.Volume = tick[curr].VolumeBTC err = ticker.ProcessTicker(l.GetName(), &tp, assetType) if err != nil { - return tickerPrice, err + log.Error(log.Ticker, err) } } diff --git a/exchanges/okcoin/okcoin_wrapper.go b/exchanges/okcoin/okcoin_wrapper.go index f0aea1f0..10579e76 100644 --- a/exchanges/okcoin/okcoin_wrapper.go +++ b/exchanges/okcoin/okcoin_wrapper.go @@ -1,6 +1,7 @@ package okcoin import ( + "fmt" "sync" "time" @@ -10,6 +11,7 @@ import ( exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" log "github.com/thrasher-corp/gocryptotrader/logger" ) @@ -57,13 +59,13 @@ func (o *OKCoin) SetDefaults() { UseGlobalFormat: true, RequestFormat: ¤cy.PairFormat{ - Uppercase: false, - Delimiter: "_", + Uppercase: true, + Delimiter: "-", }, ConfigFormat: ¤cy.PairFormat{ Uppercase: true, - Delimiter: "_", + Delimiter: "-", }, } @@ -73,7 +75,7 @@ func (o *OKCoin) SetDefaults() { Websocket: true, RESTCapabilities: exchange.ProtocolFeatures{ AutoPairUpdates: true, - TickerBatching: false, + TickerBatching: true, }, WithdrawPermissions: exchange.AutoWithdrawCrypto | exchange.NoFiatWithdrawals, @@ -122,6 +124,28 @@ func (o *OKCoin) Run() { log.Debugf(log.ExchangeSys, "%s Websocket: %s. (url: %s).\n", o.GetName(), common.IsEnabled(o.Websocket.IsEnabled()), o.WebsocketURL) } + if o.Config.CurrencyPairs.ConfigFormat.Delimiter != o.CurrencyPairs.ConfigFormat.Delimiter { + o.Config.CurrencyPairs.ConfigFormat.Delimiter = o.CurrencyPairs.ConfigFormat.Delimiter + } + if o.Config.CurrencyPairs.RequestFormat.Uppercase != o.CurrencyPairs.RequestFormat.Uppercase { + o.Config.CurrencyPairs.RequestFormat.Uppercase = true + } + if o.Config.CurrencyPairs.RequestFormat.Delimiter != o.CurrencyPairs.RequestFormat.Delimiter { + o.Config.CurrencyPairs.RequestFormat.Delimiter = o.CurrencyPairs.RequestFormat.Delimiter + } + + if !common.StringDataContains(o.Config.CurrencyPairs.Pairs[asset.Spot].Enabled.Strings(), o.CurrencyPairs.RequestFormat.Delimiter) { + enabledPairs := currency.NewPairsFromStrings([]string{"BTC-USD"}) + log.Warnf(log.ExchangeSys, + "Enabled pairs for %v reset due to config upgrade, please enable the ones you would like again.", o.Name) + + err := o.UpdatePairs(enabledPairs, asset.Spot, true, true) + if err != nil { + log.Errorf(log.ExchangeSys, "%s failed to update currencies.\n", o.GetName()) + return + } + } + if !o.GetEnabledFeatures().AutoPairUpdates { return } @@ -141,7 +165,7 @@ func (o *OKCoin) FetchTradablePairs(asset asset.Item) ([]string, error) { var pairs []string for x := range prods { - pairs = append(pairs, prods[x].BaseCurrency+"_"+prods[x].QuoteCurrency) + pairs = append(pairs, fmt.Sprintf("%v%v%v", prods[x].BaseCurrency, o.GetPairFormat(asset, false).Delimiter, prods[x].QuoteCurrency)) } return pairs, nil @@ -158,3 +182,48 @@ func (o *OKCoin) UpdateTradablePairs(forceUpdate bool) error { return o.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpdate) } + +// UpdateTicker updates and returns the ticker for a currency pair +func (o *OKCoin) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) { + var tickerData ticker.Price + if assetType == asset.Spot { + resp, err := o.GetSpotAllTokenPairsInformation() + if err != nil { + return tickerData, err + } + pairs := o.GetEnabledPairs(assetType) + for i := range pairs { + for j := range resp { + if !pairs[i].Equal(resp[j].InstrumentID) { + continue + } + tickerData = ticker.Price{ + Last: resp[j].Last, + High: resp[j].High24h, + Low: resp[j].Low24h, + Bid: resp[j].BestBid, + Ask: resp[j].BestAsk, + Volume: resp[j].BaseVolume24h, + QuoteVolume: resp[j].QuoteVolume24h, + Open: resp[j].Open24h, + Pair: pairs[i], + LastUpdated: resp[j].Timestamp, + } + err = ticker.ProcessTicker(o.Name, &tickerData, assetType) + if err != nil { + log.Error(log.Ticker, err) + } + } + } + } + return ticker.GetTicker(o.GetName(), p, assetType) +} + +// FetchTicker returns the ticker for a currency pair +func (o *OKCoin) FetchTicker(p currency.Pair, assetType asset.Item) (tickerData ticker.Price, err error) { + tickerData, err = ticker.GetTicker(o.GetName(), p, assetType) + if err != nil { + return o.UpdateTicker(p, assetType) + } + return +} diff --git a/exchanges/okex/okex_wrapper.go b/exchanges/okex/okex_wrapper.go index b0d7c541..f8c7ea20 100644 --- a/exchanges/okex/okex_wrapper.go +++ b/exchanges/okex/okex_wrapper.go @@ -11,6 +11,7 @@ import ( exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" log "github.com/thrasher-corp/gocryptotrader/logger" ) @@ -56,17 +57,34 @@ func (o *OKEX) SetDefaults() { asset.PerpetualSwap, asset.Index, }, - UseGlobalFormat: true, + UseGlobalFormat: false, + } + // Same format used for perpetual swap and futures + fmt1 := currency.PairStore{ RequestFormat: ¤cy.PairFormat{ - Uppercase: false, - Delimiter: "_", + Uppercase: true, + Delimiter: "-", }, - ConfigFormat: ¤cy.PairFormat{ Uppercase: true, Delimiter: "_", }, } + o.CurrencyPairs.Store(asset.PerpetualSwap, fmt1) + o.CurrencyPairs.Store(asset.Futures, fmt1) + + fmt2 := currency.PairStore{ + RequestFormat: ¤cy.PairFormat{ + Uppercase: true, + Delimiter: "-", + }, + ConfigFormat: ¤cy.PairFormat{ + Uppercase: true, + Delimiter: "-", + }, + } + o.CurrencyPairs.Store(asset.Spot, fmt2) + o.CurrencyPairs.Store(asset.Index, fmt2) o.Features = exchange.Features{ Supports: exchange.FeaturesSupported{ @@ -74,6 +92,7 @@ func (o *OKEX) SetDefaults() { Websocket: true, RESTCapabilities: exchange.ProtocolFeatures{ AutoPairUpdates: true, + TickerBatching: true, }, WithdrawPermissions: exchange.AutoWithdrawCrypto | exchange.NoFiatWithdrawals, @@ -121,6 +140,32 @@ func (o *OKEX) Run() { if o.Verbose { log.Debugf(log.ExchangeSys, "%s Websocket: %s. (url: %s).\n", o.GetName(), common.IsEnabled(o.Websocket.IsEnabled()), o.API.Endpoints.WebsocketURL) } + if o.Config.CurrencyPairs.Pairs[asset.Spot].ConfigFormat == nil || o.Config.CurrencyPairs.Pairs[asset.Spot].RequestFormat == nil { + fmt := currency.PairStore{ + RequestFormat: ¤cy.PairFormat{ + Uppercase: true, + Delimiter: "-", + }, + ConfigFormat: ¤cy.PairFormat{ + Uppercase: true, + Delimiter: "-", + }, + } + o.CurrencyPairs.Store(asset.Spot, fmt) + o.Config.CurrencyPairs.Store(asset.Spot, fmt) + } + + if !common.StringDataContains(o.Config.CurrencyPairs.Pairs[asset.Spot].Enabled.Strings(), o.CurrencyPairs.Pairs[asset.Spot].RequestFormat.Delimiter) { + enabledPairs := currency.NewPairsFromStrings([]string{"EOS-USDT"}) + log.Warnf(log.ExchangeSys, + "Enabled pairs for %v reset due to config upgrade, please enable the ones you would like again.", o.Name) + + err := o.UpdatePairs(enabledPairs, asset.Spot, true, true) + if err != nil { + log.Errorf(log.ExchangeSys, "%s failed to update currencies.\n", o.GetName()) + return + } + } if !o.GetEnabledFeatures().AutoPairUpdates { return @@ -143,7 +188,7 @@ func (o *OKEX) FetchTradablePairs(i asset.Item) ([]string, error) { } for x := range prods { - pairs = append(pairs, prods[x].BaseCurrency+"_"+prods[x].QuoteCurrency) + pairs = append(pairs, fmt.Sprintf("%v%v%v", prods[x].BaseCurrency, o.GetPairFormat(i, false).Delimiter, prods[x].QuoteCurrency)) } return pairs, nil case asset.Futures: @@ -154,7 +199,7 @@ func (o *OKEX) FetchTradablePairs(i asset.Item) ([]string, error) { var pairs []string for x := range prods { - pairs = append(pairs, prods[x].UnderlyingIndex+prods[x].QuoteCurrency+"_"+prods[x].Delivery) + pairs = append(pairs, fmt.Sprintf("%v%v%v", prods[x].UnderlyingIndex+prods[x].QuoteCurrency, o.GetPairFormat(i, false).Delimiter, prods[x].Delivery)) } return pairs, nil @@ -166,11 +211,11 @@ func (o *OKEX) FetchTradablePairs(i asset.Item) ([]string, error) { var pairs []string for x := range prods { - pairs = append(pairs, prods[x].UnderlyingIndex+"_"+prods[x].QuoteCurrency+"_SWAP") + pairs = append(pairs, fmt.Sprintf("%v%v%v%vSWAP", prods[x].UnderlyingIndex, o.GetPairFormat(i, false).Delimiter, prods[x].QuoteCurrency, o.GetPairFormat(i, false).Delimiter)) } return pairs, nil case asset.Index: - return []string{"BTC_USD"}, nil + return []string{fmt.Sprintf("BTC%vUSD", o.GetPairFormat(i, false).Delimiter)}, nil } return nil, fmt.Errorf("%s invalid asset type", o.Name) @@ -193,3 +238,104 @@ func (o *OKEX) UpdateTradablePairs(forceUpdate bool) error { } return nil } + +// UpdateTicker updates and returns the ticker for a currency pair +func (o *OKEX) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) { + var tickerData ticker.Price + switch assetType { + case asset.Spot: + resp, err := o.GetSpotAllTokenPairsInformation() + if err != nil { + return tickerData, err + } + pairs := o.GetEnabledPairs(assetType) + for i := range pairs { + for j := range resp { + if !pairs[i].Equal(resp[j].InstrumentID) { + continue + } + tickerData = ticker.Price{ + Last: resp[j].Last, + High: resp[j].High24h, + Low: resp[j].Low24h, + Bid: resp[j].BestBid, + Ask: resp[j].BestAsk, + Volume: resp[j].BaseVolume24h, + QuoteVolume: resp[j].QuoteVolume24h, + Open: resp[j].Open24h, + Pair: pairs[i], + LastUpdated: resp[j].Timestamp, + } + err = ticker.ProcessTicker(o.Name, &tickerData, assetType) + if err != nil { + log.Error(log.Ticker, err) + } + } + } + case asset.PerpetualSwap: + resp, err := o.GetAllSwapTokensInformation() + if err != nil { + return tickerData, err + } + pairs := o.GetEnabledPairs(assetType) + for i := range pairs { + for j := range resp { + if !pairs[i].Equal(resp[j].InstrumentID) { + continue + } + tickerData = ticker.Price{ + Last: resp[j].Last, + High: resp[j].High24H, + Low: resp[j].Low24H, + Bid: resp[j].BestBid, + Ask: resp[j].BestAsk, + Volume: resp[j].Volume24H, + Pair: resp[j].InstrumentID, + LastUpdated: resp[j].Timestamp, + } + err = ticker.ProcessTicker(o.Name, &tickerData, assetType) + if err != nil { + log.Error(log.Ticker, err) + } + } + } + case asset.Futures: + resp, err := o.GetAllFuturesTokenInfo() + if err != nil { + return tickerData, err + } + pairs := o.GetEnabledPairs(assetType) + for i := range pairs { + for j := range resp { + if !pairs[i].Equal(resp[j].InstrumentID) { + continue + } + tickerData = ticker.Price{ + Last: resp[j].Last, + High: resp[j].High24h, + Low: resp[j].Low24h, + Bid: resp[j].BestBid, + Ask: resp[j].BestAsk, + Volume: resp[j].Volume24h, + Pair: resp[j].InstrumentID, + LastUpdated: resp[j].Timestamp, + } + err = ticker.ProcessTicker(o.Name, &tickerData, assetType) + if err != nil { + log.Error(log.Ticker, err) + } + } + } + } + + return ticker.GetTicker(o.GetName(), p, assetType) +} + +// FetchTicker returns the ticker for a currency pair +func (o *OKEX) FetchTicker(p currency.Pair, assetType asset.Item) (tickerData ticker.Price, err error) { + tickerData, err = ticker.GetTicker(o.GetName(), p, assetType) + if err != nil { + return o.UpdateTicker(p, assetType) + } + return +} diff --git a/exchanges/okgroup/okgroup_types.go b/exchanges/okgroup/okgroup_types.go index bf495f52..4eb8493e 100644 --- a/exchanges/okgroup/okgroup_types.go +++ b/exchanges/okgroup/okgroup_types.go @@ -2,6 +2,8 @@ package okgroup import ( "time" + + "github.com/thrasher-corp/gocryptotrader/currency" ) // GetAccountCurrenciesResponse response data for GetAccountCurrencies @@ -288,16 +290,16 @@ type GetSpotOrderBookResponse struct { // GetSpotTokenPairsInformationResponse response data for GetSpotTokenPairsInformation type GetSpotTokenPairsInformationResponse struct { - BaseVolume24h float64 `json:"base_volume_24h,string"` // 24 trading volume of the base currency - BestAsk float64 `json:"best_ask,string"` // best ask price - BestBid float64 `json:"best_bid,string"` // best bid price - High24h float64 `json:"high_24h,string"` // 24 hour high - InstrumentID string `json:"instrument_id"` // trading pair - Last float64 `json:"last,string"` // last traded price - Low24h float64 `json:"low_24h,string"` // 24 hour low - Open24h float64 `json:"open_24h,string"` // 24 hour open - QuoteVolume24h float64 `json:"quote_volume_24h,string"` // 24 trading volume of the quote currency - Timestamp time.Time `json:"timestamp"` + BaseVolume24h float64 `json:"base_volume_24h,string"` // 24 trading volume of the base currency + BestAsk float64 `json:"best_ask,string"` // best ask price + BestBid float64 `json:"best_bid,string"` // best bid price + High24h float64 `json:"high_24h,string"` // 24 hour high + InstrumentID currency.Pair `json:"instrument_id"` // trading pair + Last float64 `json:"last,string"` // last traded price + Low24h float64 `json:"low_24h,string"` // 24 hour low + Open24h float64 `json:"open_24h,string"` // 24 hour open + QuoteVolume24h float64 `json:"quote_volume_24h,string"` // 24 trading volume of the quote currency + Timestamp time.Time `json:"timestamp"` } // GetSpotFilledOrdersInformationRequest request data for GetSpotFilledOrdersInformation @@ -699,14 +701,14 @@ type GetFuturesOrderBookResponse struct { // GetFuturesTokenInfoResponse response data for GetFuturesOrderBook type GetFuturesTokenInfoResponse struct { - BestAsk float64 `json:"best_ask,string"` - BestBid float64 `json:"best_bid,string"` - High24h float64 `json:"high_24h,string"` - InstrumentID string `json:"instrument_id"` - Last float64 `json:"last,string"` - Low24h float64 `json:"low_24h,string"` - Timestamp time.Time `json:"timestamp"` - Volume24h int64 `json:"volume_24h,string"` + BestAsk float64 `json:"best_ask,string"` + BestBid float64 `json:"best_bid,string"` + High24h float64 `json:"high_24h,string"` + InstrumentID currency.Pair `json:"instrument_id"` + Last float64 `json:"last,string"` + Low24h float64 `json:"low_24h,string"` + Timestamp time.Time `json:"timestamp"` + Volume24h float64 `json:"volume_24h,string"` } // GetFuturesFilledOrderRequest request data for GetFuturesFilledOrder @@ -1057,14 +1059,14 @@ type GetSwapOrderBookResponse struct { // GetAllSwapTokensInformationResponse response data for GetAllSwapTokensInformation type GetAllSwapTokensInformationResponse struct { - InstrumentID string `json:"instrument_id"` - Last float64 `json:"last,string"` - High24H float64 `json:"high_24h,string"` - Low24H float64 `json:"low_24h,string"` - BestBid float64 `json:"best_bid,string"` - BestAsk float64 `json:"best_ask,string"` - Volume24H float64 `json:"volume_24h,string"` - Timestamp time.Time `json:"timestamp"` + InstrumentID currency.Pair `json:"instrument_id"` + Last float64 `json:"last,string"` + High24H float64 `json:"high_24h,string"` + Low24H float64 `json:"low_24h,string"` + BestBid float64 `json:"best_bid,string"` + BestAsk float64 `json:"best_ask,string"` + Volume24H float64 `json:"volume_24h,string"` + Timestamp time.Time `json:"timestamp"` } // GetSwapFilledOrdersDataRequest request data for GetSwapFilledOrdersData @@ -1340,12 +1342,14 @@ type WebsocketDataWrapper struct { // WebsocketTickerData contains formatted data for ticker related websocket responses type WebsocketTickerData struct { - High24H float64 `json:"high_24h,string,omitempty"` - Last float64 `json:"last,string,omitempty"` - BestBid float64 `json:"best_bid,string,omitempty"` - BestAsk float64 `json:"best_ask,string,omitempty"` - Low24H float64 `json:"low_24h,string,omitempty"` - Volume24H float64 `json:"volume_24h,string,omitempty"` + BaseVolume24h float64 `json:"base_volume_24h,string,omitempty"` + BestAsk float64 `json:"best_ask,string,omitempty"` + BestBid float64 `json:"best_bid,string,omitempty"` + High24h float64 `json:"high_24h,string,omitempty"` + Last float64 `json:"last,string,omitempty"` + Low24h float64 `json:"low_24h,string,omitempty"` + Open24h float64 `json:"open_24h,string,omitempty"` + QuoteVolume24h float64 `json:"quote_volume_24h,string,omitempty"` } // WebsocketTradeResponse contains formatted data for trade related websocket responses diff --git a/exchanges/okgroup/okgroup_websocket.go b/exchanges/okgroup/okgroup_websocket.go index b55b5f71..558252d6 100644 --- a/exchanges/okgroup/okgroup_websocket.go +++ b/exchanges/okgroup/okgroup_websocket.go @@ -193,7 +193,7 @@ func (o *OKGroup) wsPingHandler(wg *sync.WaitGroup) { return case <-ticker.C: - err := o.WebsocketConn.SendMessage("ping") + err := o.WebsocketConn.Connection.WriteMessage(websocket.TextMessage, []byte("ping")) if o.Verbose { log.Debugf(log.ExchangeSys, "%v sending ping", o.GetName()) } @@ -361,13 +361,19 @@ func (o *OKGroup) wsProcessTickers(response *WebsocketDataResponse) { for i := range response.Data { instrument := currency.NewPairDelimiter(response.Data[i].InstrumentID, "-") o.Websocket.DataHandler <- wshandler.TickerData{ - Timestamp: response.Data[i].Timestamp, - Exchange: o.GetName(), - AssetType: o.GetAssetTypeFromTableName(response.Table), - HighPrice: response.Data[i].High24H, - LowPrice: response.Data[i].Low24H, - ClosePrice: response.Data[i].Last, - Pair: instrument, + Exchange: o.Name, + Open: response.Data[i].Open24h, + Close: response.Data[i].Last, + Volume: response.Data[i].BaseVolume24h, + QuoteVolume: response.Data[i].QuoteVolume24h, + High: response.Data[i].High24h, + Low: response.Data[i].Low24h, + Bid: response.Data[i].BestBid, + Ask: response.Data[i].BestAsk, + Last: response.Data[i].Last, + Timestamp: response.Data[i].Timestamp, + AssetType: o.GetAssetTypeFromTableName(response.Table), + Pair: instrument, } } } diff --git a/exchanges/okgroup/okgroup_wrapper.go b/exchanges/okgroup/okgroup_wrapper.go index da95dfe7..863af600 100644 --- a/exchanges/okgroup/okgroup_wrapper.go +++ b/exchanges/okgroup/okgroup_wrapper.go @@ -11,7 +11,6 @@ import ( exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" - "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" log "github.com/thrasher-corp/gocryptotrader/logger" ) @@ -65,36 +64,6 @@ func (o *OKGroup) Setup(exch *config.ExchangeConfig) error { return nil } -// UpdateTicker updates and returns the ticker for a currency pair -func (o *OKGroup) UpdateTicker(p currency.Pair, assetType asset.Item) (tickerData ticker.Price, err error) { - resp, err := o.GetSpotAllTokenPairsInformationForCurrency(o.FormatExchangeCurrency(p, assetType).String()) - if err != nil { - return - } - tickerData = ticker.Price{ - Ask: resp.BestAsk, - Bid: resp.BestBid, - High: resp.High24h, - Last: resp.Last, - LastUpdated: resp.Timestamp, - Low: resp.Low24h, - Pair: o.FormatExchangeCurrency(p, assetType), - Volume: resp.BaseVolume24h, - } - - err = ticker.ProcessTicker(o.Name, &tickerData, assetType) - return -} - -// FetchTicker returns the ticker for a currency pair -func (o *OKGroup) FetchTicker(p currency.Pair, assetType asset.Item) (tickerData ticker.Price, err error) { - tickerData, err = ticker.GetTicker(o.GetName(), p, assetType) - if err != nil { - return o.UpdateTicker(p, assetType) - } - return -} - // FetchOrderbook returns orderbook base on the currency pair func (o *OKGroup) FetchOrderbook(p currency.Pair, assetType asset.Item) (resp orderbook.Base, err error) { ob, err := orderbook.Get(o.GetName(), p, assetType) @@ -339,7 +308,7 @@ func (o *OKGroup) GetOrderInfo(orderID string) (resp exchange.OrderDetail, err e // GetDepositAddress returns a deposit address for a specified currency func (o *OKGroup) GetDepositAddress(p currency.Code, accountID string) (_ string, err error) { wallet, err := o.GetAccountDepositAddressForCurrency(p.Lower().String()) - if err != nil { + if err != nil || len(wallet) == 0 { return } return wallet[0].Address, nil diff --git a/exchanges/poloniex/poloniex_websocket.go b/exchanges/poloniex/poloniex_websocket.go index 7331898b..a6ef2ed1 100644 --- a/exchanges/poloniex/poloniex_websocket.go +++ b/exchanges/poloniex/poloniex_websocket.go @@ -210,23 +210,22 @@ func (p *Poloniex) wsHandleTickerData(data []interface{}) { t.PercentageChange, _ = strconv.ParseFloat(tickerData[4].(string), 64) t.BaseCurrencyVolume24H, _ = strconv.ParseFloat(tickerData[5].(string), 64) t.QuoteCurrencyVolume24H, _ = strconv.ParseFloat(tickerData[6].(string), 64) - isFrozen := false - if tickerData[7].(float64) == 1 { - isFrozen = true - } - t.IsFrozen = isFrozen + t.IsFrozen = tickerData[7].(float64) == 1 t.HighestTradeIn24H, _ = strconv.ParseFloat(tickerData[8].(string), 64) t.LowestTradePrice24H, _ = strconv.ParseFloat(tickerData[9].(string), 64) p.Websocket.DataHandler <- wshandler.TickerData{ - Timestamp: time.Now(), - Pair: currency.NewPairDelimiter(currencyPair, "_"), - Exchange: p.GetName(), - AssetType: asset.Spot, - ClosePrice: t.LastPrice, - LowPrice: t.LowestAsk, - HighPrice: t.HighestBid, - Quantity: t.QuoteCurrencyVolume24H, + Exchange: p.Name, + Volume: t.BaseCurrencyVolume24H, + QuoteVolume: t.QuoteCurrencyVolume24H, + High: t.HighestBid, + Low: t.LowestAsk, + Bid: t.HighestBid, + Ask: t.LowestAsk, + Last: t.LastPrice, + Timestamp: time.Now(), + AssetType: asset.Spot, + Pair: currency.NewPairDelimiter(currencyPair, "_"), } } @@ -362,7 +361,6 @@ func (p *Poloniex) WsProcessOrderbookUpdate(sequenceNumber int64, target []inter // GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions() func (p *Poloniex) GenerateDefaultSubscriptions() { var subscriptions []wshandler.WebsocketChannelSubscription - // Tickerdata is its own channel subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ Channel: fmt.Sprintf("%v", wsTickerDataID), }) diff --git a/exchanges/poloniex/poloniex_wrapper.go b/exchanges/poloniex/poloniex_wrapper.go index 3bcf3d64..13611997 100644 --- a/exchanges/poloniex/poloniex_wrapper.go +++ b/exchanges/poloniex/poloniex_wrapper.go @@ -215,6 +215,9 @@ func (p *Poloniex) UpdateTicker(currencyPair currency.Pair, assetType asset.Item for _, x := range p.GetEnabledPairs(assetType) { var tp ticker.Price curr := p.FormatExchangeCurrency(x, assetType).String() + if _, ok := tick[curr]; !ok { + continue + } tp.Pair = x tp.Ask = tick[curr].LowestAsk tp.Bid = tick[curr].HighestBid @@ -222,10 +225,11 @@ func (p *Poloniex) UpdateTicker(currencyPair currency.Pair, assetType asset.Item tp.Last = tick[curr].Last tp.Low = tick[curr].Low24Hr tp.Volume = tick[curr].BaseVolume + tp.QuoteVolume = tick[curr].QuoteVolume err = ticker.ProcessTicker(p.GetName(), &tp, assetType) if err != nil { - return tickerPrice, err + log.Error(log.Ticker, err) } } return ticker.GetTicker(p.Name, currencyPair, assetType) diff --git a/exchanges/ticker/ticker.go b/exchanges/ticker/ticker.go index 2c7ad8cc..4087f31d 100644 --- a/exchanges/ticker/ticker.go +++ b/exchanges/ticker/ticker.go @@ -2,6 +2,7 @@ package ticker import ( "errors" + "fmt" "strconv" "strings" "sync" @@ -28,14 +29,17 @@ var ( // Price struct stores the currency pair and pricing information type Price struct { - Pair currency.Pair `json:"Pair"` Last float64 `json:"Last"` High float64 `json:"High"` Low float64 `json:"Low"` Bid float64 `json:"Bid"` Ask float64 `json:"Ask"` Volume float64 `json:"Volume"` + QuoteVolume float64 `json:"QuoteVolume"` PriceATH float64 `json:"PriceATH"` + Open float64 `json:"Open"` + Close float64 `json:"Close"` + Pair currency.Pair `json:"Pair"` LastUpdated time.Time } @@ -151,11 +155,11 @@ func CreateNewTicker(exchangeName string, tickerNew *Price, tickerType asset.Ite // list func ProcessTicker(exchangeName string, tickerNew *Price, assetType asset.Item) error { if tickerNew.Pair.IsEmpty() { - return errors.New(errPairNotSet) + return fmt.Errorf("%v %v", exchangeName, errPairNotSet) } if assetType == "" { - return errors.New(errAssetTypeNotSet) + return fmt.Errorf("%v %v %v", exchangeName, tickerNew.Pair.String(), errAssetTypeNotSet) } if tickerNew.LastUpdated.IsZero() { @@ -178,6 +182,7 @@ func ProcessTicker(exchangeName string, tickerNew *Price, assetType asset.Item) } m.Lock() + a := make(map[string]map[string]Price) b := make(map[string]Price) b[assetType.String()] = *tickerNew diff --git a/exchanges/websocket/wshandler/wshandler.go b/exchanges/websocket/wshandler/wshandler.go index 3089969d..931ee5c8 100644 --- a/exchanges/websocket/wshandler/wshandler.go +++ b/exchanges/websocket/wshandler/wshandler.go @@ -103,7 +103,9 @@ func (w *Websocket) Connect() error { if !w.connectionMonitorRunning { go w.connectionMonitor() } - go w.manageSubscriptions() + if w.SupportsFunctionality(WebsocketSubscribeSupported) || w.SupportsFunctionality(WebsocketUnsubscribeSupported) { + go w.manageSubscriptions() + } return nil } diff --git a/exchanges/websocket/wshandler/wshandler_types.go b/exchanges/websocket/wshandler/wshandler_types.go index 1d5a1b76..529c124d 100644 --- a/exchanges/websocket/wshandler/wshandler_types.go +++ b/exchanges/websocket/wshandler/wshandler_types.go @@ -144,15 +144,20 @@ type TradeData struct { // TickerData defines ticker feed type TickerData struct { - Timestamp time.Time - Pair currency.Pair - AssetType asset.Item - Exchange string - ClosePrice float64 - Quantity float64 - OpenPrice float64 - HighPrice float64 - LowPrice float64 + Exchange string + Open float64 + Close float64 + Volume float64 + QuoteVolume float64 + High float64 + Low float64 + Bid float64 + Ask float64 + Last float64 + PriceATH float64 + Timestamp time.Time + AssetType asset.Item + Pair currency.Pair } // KlineData defines kline feed diff --git a/exchanges/yobit/yobit_wrapper.go b/exchanges/yobit/yobit_wrapper.go index a9a2b290..b2f960a1 100644 --- a/exchanges/yobit/yobit_wrapper.go +++ b/exchanges/yobit/yobit_wrapper.go @@ -170,19 +170,23 @@ func (y *Yobit) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Pric } for _, x := range y.GetEnabledPairs(assetType) { - currency := y.FormatExchangeCurrency(x, assetType).Lower().String() + curr := y.FormatExchangeCurrency(x, assetType).Lower().String() + if _, ok := result[curr]; !ok { + continue + } var tickerPrice ticker.Price tickerPrice.Pair = x - tickerPrice.Last = result[currency].Last - tickerPrice.Ask = result[currency].Sell - tickerPrice.Bid = result[currency].Buy - tickerPrice.Last = result[currency].Last - tickerPrice.Low = result[currency].Low - tickerPrice.Volume = result[currency].VolumeCurrent + tickerPrice.Last = result[curr].Last + tickerPrice.Ask = result[curr].Sell + tickerPrice.Bid = result[curr].Buy + tickerPrice.Last = result[curr].Last + tickerPrice.Low = result[curr].Low + tickerPrice.QuoteVolume = result[curr].VolumeCurrent + tickerPrice.Volume = result[curr].Vol err = ticker.ProcessTicker(y.Name, &tickerPrice, assetType) if err != nil { - return tickerPrice, err + log.Error(log.Ticker, err) } } return ticker.GetTicker(y.Name, p, assetType) diff --git a/exchanges/zb/zb_types.go b/exchanges/zb/zb_types.go index 0e062207..c1613da6 100644 --- a/exchanges/zb/zb_types.go +++ b/exchanges/zb/zb_types.go @@ -72,12 +72,12 @@ type TickerResponse struct { // TickerChildResponse holds the ticker child response data type TickerChildResponse struct { - Vol float64 `json:"vol,string"` // 成交量(最近的24小时) - Last float64 `json:"last,string"` // 最新成交价 - Sell float64 `json:"sell,string"` // 卖一价 - Buy float64 `json:"buy,string"` // 买一价 - High float64 `json:"high,string"` // 最高价 - Low float64 `json:"low,string"` // 最低价 + Volume float64 `json:"vol,string"` // 成交量(最近的24小时) + Last float64 `json:"last,string"` // 最新成交价 + Sell float64 `json:"sell,string"` // 卖一价 + Buy float64 `json:"buy,string"` // 买一价 + High float64 `json:"high,string"` // 最高价 + Low float64 `json:"low,string"` // 最低价 } // SpotNewOrderRequestParamsType ZB 交易类型 diff --git a/exchanges/zb/zb_websocket.go b/exchanges/zb/zb_websocket.go index 35bfe5e9..7cd23866 100644 --- a/exchanges/zb/zb_websocket.go +++ b/exchanges/zb/zb_websocket.go @@ -96,13 +96,17 @@ func (z *ZB) WsHandleData() { } z.Websocket.DataHandler <- wshandler.TickerData{ - Timestamp: time.Unix(0, ticker.Date), - Pair: currency.NewPairFromString(cPair[0]), - AssetType: asset.Spot, - Exchange: z.GetName(), - ClosePrice: ticker.Data.Last, - HighPrice: ticker.Data.High, - LowPrice: ticker.Data.Low, + Exchange: z.Name, + Close: ticker.Data.Last, + Volume: ticker.Data.Volume24Hr, + High: ticker.Data.High, + Low: ticker.Data.Low, + Last: ticker.Data.Last, + Bid: ticker.Data.Buy, + Ask: ticker.Data.Sell, + Timestamp: time.Unix(0, ticker.Date), + AssetType: asset.Spot, + Pair: currency.NewPairFromString(cPair[0]), } case strings.Contains(result.Channel, "depth"): diff --git a/exchanges/zb/zb_wrapper.go b/exchanges/zb/zb_wrapper.go index faf19c92..34b46ded 100644 --- a/exchanges/zb/zb_wrapper.go +++ b/exchanges/zb/zb_wrapper.go @@ -204,21 +204,23 @@ func (z *ZB) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, } for _, x := range z.GetEnabledPairs(assetType) { - currencySplit := strings.Split(z.FormatExchangeCurrency(x, assetType).String(), "_") - currency := currencySplit[0] + currencySplit[1] + currencySplit := strings.Split(z.FormatExchangeCurrency(x, assetType).String(), z.GetPairFormat(assetType, false).Delimiter) + curr := currencySplit[0] + currencySplit[1] + if _, ok := result[curr]; !ok { + continue + } var tp ticker.Price tp.Pair = x - tp.High = result[currency].High - tp.Last = result[currency].Last - tp.Ask = result[currency].Sell - tp.Bid = result[currency].Buy - tp.Last = result[currency].Last - tp.Low = result[currency].Low - tp.Volume = result[currency].Vol + tp.High = result[curr].High + tp.Last = result[curr].Last + tp.Ask = result[curr].Sell + tp.Bid = result[curr].Buy + tp.Low = result[curr].Low + tp.Volume = result[curr].Volume err = ticker.ProcessTicker(z.Name, &tp, assetType) if err != nil { - return tickerPrice, err + log.Error(log.Ticker, err) } } diff --git a/testdata/http_mock/gemini/gemini.json b/testdata/http_mock/gemini/gemini.json index 317e5701..b40bab4a 100644 --- a/testdata/http_mock/gemini/gemini.json +++ b/testdata/http_mock/gemini/gemini.json @@ -2729,6 +2729,64 @@ } } ] + }, + "/v2/ticker/BTCUSD": { + "GET": [ + { + "data": { + "ask": "9663.28", + "bid": "9662.94", + "changes": [ + "9719", + "9730.13", + "9718.58", + "9672.54", + "9668.57", + "9701.67", + "10191.27", + "10225.8", + "10238.78", + "10210.5", + "10171.2", + "10156.31", + "10138.69", + "10121.71", + "10147.31", + "10120.74", + "10149.82", + "10185.68", + "10128.28", + "10070.56", + "10082.86", + "10114", + "10089.25", + "10142.29" + ], + "close": "9662.94", + "high": "11000", + "low": "9210", + "open": "10148.67", + "symbol": "BTCUSD" + }, + "queryString": "", + "bodyParams": "", + "headers": {} + } + ] + }, + "/v2/ticker/bla": { + "GET": [ + { + "data": { + "message": "Supplied value 'bla' is not a valid symbol. Please correct your API request to use one of the supported symbols: [zecbch, bchbtc, zecusd, ethusd, zecbtc, bcheth, zecltc, ltcbch, bchusd, ethbtc, ltcbtc, ltceth, zeceth, ltcusd, btcusd]", + "reason": "Bad Request", + "result": "error" + }, + "queryString": "", + "bodyParams": "", + "headers": {} + } + ] } } } \ No newline at end of file