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