Bump last checked available currencies timestamp and expand automatic updating of available currencies for various exchanges

This commit is contained in:
Adrian Gallagher
2018-06-15 15:56:01 +10:00
parent 44810abb4a
commit 9b2ac9a6dc
19 changed files with 432 additions and 104 deletions

View File

@@ -496,7 +496,7 @@ func (c *Config) CheckExchangeConfigValues() error {
}
if !exch.SupportsAutoPairUpdates {
lastUpdated := common.UnixTimestampToTime(exch.PairsLastUpdated)
lastUpdated.AddDate(0, 0, configPairsLastUpdatedWarningThreshold)
lastUpdated = lastUpdated.AddDate(0, 0, configPairsLastUpdatedWarningThreshold)
if lastUpdated.Unix() <= time.Now().Unix() {
log.Printf(WarningPairsLastUpdatedThresholdExceeded, exch.Name, configPairsLastUpdatedWarningThreshold)
}

File diff suppressed because one or more lines are too long

View File

@@ -19,6 +19,7 @@ const (
anxAPIURL = "https://anxpro.com/"
anxAPIVersion = "3"
anxAPIKey = "apiKey"
anxCurrencies = "currencyStatic"
anxDataToken = "dataToken"
anxOrderNew = "order/new"
anxOrderInfo = "order/info"
@@ -50,12 +51,12 @@ func (a *ANX) SetDefaults() {
a.RESTPollingDelay = 10
a.RequestCurrencyPairFormat.Delimiter = ""
a.RequestCurrencyPairFormat.Uppercase = true
a.RequestCurrencyPairFormat.Index = "BTC"
a.ConfigCurrencyPairFormat.Delimiter = ""
a.RequestCurrencyPairFormat.Index = ""
a.ConfigCurrencyPairFormat.Delimiter = "_"
a.ConfigCurrencyPairFormat.Uppercase = true
a.ConfigCurrencyPairFormat.Index = "BTC"
a.ConfigCurrencyPairFormat.Index = ""
a.AssetTypes = []string{ticker.Spot}
a.SupportsAutoPairUpdating = false
a.SupportsAutoPairUpdating = true
a.SupportsRESTTickerBatching = false
a.Requester = request.New(a.Name, request.NewRateLimit(time.Second, anxAuthRate), request.NewRateLimit(time.Second, anxUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
}
@@ -90,6 +91,20 @@ func (a *ANX) Setup(exch config.ExchangeConfig) {
}
}
// GetCurrencies returns a list of supported currencies (both fiat
// and cryptocurrencies)
func (a *ANX) GetCurrencies() (CurrenciesStore, error) {
var result CurrenciesStaticResponse
path := fmt.Sprintf("%sapi/3/%s", anxAPIURL, anxCurrencies)
err := a.SendHTTPRequest(path, &result)
if err != nil {
return CurrenciesStore{}, err
}
return result.CurrenciesResponse, nil
}
// GetFee returns maker or taker fees
func (a *ANX) GetFee(maker bool) float64 {
if maker {

View File

@@ -75,6 +75,20 @@ func TestSetup(t *testing.T) {
}
}
func TestGetCurrencies(t *testing.T) {
_, err := anx.GetCurrencies()
if err != nil {
t.Fatalf("Test failed. TestGetCurrencies failed. Err: %s", err)
}
}
func TestGetTradablePairs(t *testing.T) {
_, err := anx.GetTradablePairs()
if err != nil {
t.Fatalf("Test failed. TestGetTradablePairs failed. Err: %s", err)
}
}
func TestGetFee(t *testing.T) {
makerFeeExpected, takerFeeExpected := 0.3, 0.6

View File

@@ -1,5 +1,77 @@
package anx
// Currency holds the currency information
type Currency struct {
Decimals int `json:"decimals"`
MinOrderSize float64 `json:"minOrderSize"`
MaxOrderSize float64 `json:"maxOrderSize"`
DisplayDenominator float64 `json:"displayDenominator"`
SummaryDecimals int `json:"summaryDecimals"`
DisplayUnit string `json:"displayUnit"`
Symbol string `json:"symbol"`
Type string `json:"type"`
ConfirmationThresholds []struct {
ConfosRequired int `json:"confosRequired"`
} `json:"confirmationThresholds"`
NetworkFee float64 `json:"networkFee"`
EngineSettings struct {
DepositsEnabled bool `json:"depositsEnabled"`
WithdrawalsEnabled bool `json:"withdrawalsEnabled"`
DisplayEnabled bool `json:"displayEnabled"`
MobileAccessEnabled bool `json:"mobileAccessEnabled"`
} `json:"engineSettings"`
MinOrderValue float64 `json:"minOrderValue"`
MaxOrderValue float64 `json:"maxOrderValue"`
MaxMarketOrderValue float64 `json:"maxMarketOrderValue"`
MaxMarketOrderSize float64 `json:"maxMarketOrderSize"`
DigitalCurrencyType string `json:"digitalCurrencyType"`
AssetName string `json:"assetName"`
AssetDivisibility int `json:"assetDivisibility"`
AssetIcon string `json:"assetIcon"`
AssetIssueQuantity string `json:"assetIssueQuantity"`
}
// Currencies stores a list of currencies
type Currencies map[string]Currency
// CurrencyPair holds the currency information
type CurrencyPair struct {
PriceDecimals int `json:"priceDecimals"`
EngineSettings struct {
TradingEnabled bool `json:"tradingEnabled"`
DisplayEnabled bool `json:"displayEnabled"`
CancelOnly bool `json:"cancelOnly"`
VerifyRequired bool `json:"verifyRequired"`
RestrictedBuy bool `json:"restrictedBuy"`
RestrictedSell bool `json:"restrictedSell"`
} `json:"engineSettings"`
MinOrderRate float64 `json:"minOrderRate"`
MaxOrderRate float64 `json:"maxOrderRate"`
DisplayPriceDecimals int `json:"displayPriceDecimals"`
TradedCcy string `json:"tradedCcy"`
SettlementCcy string `json:"settlementCcy"`
PreferredMarket string `json:"preferredMarket"`
ChartEnabled bool `json:"chartEnabled"`
SimpleTradeEnabled bool `json:"simpleTradeEnabled"`
}
// CurrencyPairs stores currency pair info
type CurrencyPairs map[string]CurrencyPair
// CurrenciesStore stores the available cryptocurrencies
// and fiat currencies
type CurrenciesStore struct {
Currencies Currencies `json:"currencies"`
CurrencyPairs CurrencyPairs `json:"currencyPairs"`
Timestamp string `json:"timestamp"`
ResultCode string `json:"resultCode"`
}
// CurrenciesStaticResponse stores the currencyStatic response
type CurrenciesStaticResponse struct {
CurrenciesResponse CurrenciesStore `json:"CurrencyStatic"`
}
// Order holds order information
type Order struct {
OrderType string `json:"orderType"`

View File

@@ -6,6 +6,7 @@ import (
"strconv"
"sync"
"github.com/thrasher-/gocryptotrader/common"
"github.com/thrasher-/gocryptotrader/currency/pair"
"github.com/thrasher-/gocryptotrader/exchanges"
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
@@ -27,6 +28,45 @@ func (a *ANX) Run() {
log.Printf("%s polling delay: %ds.\n", a.GetName(), a.RESTPollingDelay)
log.Printf("%s %d currencies enabled: %s.\n", a.GetName(), len(a.EnabledPairs), a.EnabledPairs)
}
exchangeProducts, err := a.GetTradablePairs()
if err != nil {
log.Printf("%s Failed to get available symbols.\n", a.GetName())
} else {
forceUpgrade := false
if !common.StringDataContains(a.EnabledPairs, "_") || !common.StringDataContains(a.AvailablePairs, "_") {
forceUpgrade = true
}
if forceUpgrade {
enabledPairs := []string{"BTC_USD,BTC_HKD,BTC_EUR,BTC_CAD,BTC_AUD,BTC_SGD,BTC_JPY,BTC_GBP,BTC_NZD,LTC_BTC,DOG_EBTC,STR_BTC,XRP_BTC"}
log.Println("WARNING: Enabled pairs for ANX reset due to config upgrade, please enable the ones you would like again.")
err = a.UpdateCurrencies(enabledPairs, true, true)
if err != nil {
log.Printf("%s Failed to get config.\n", a.GetName())
}
}
err = a.UpdateCurrencies(exchangeProducts, false, forceUpgrade)
if err != nil {
log.Printf("%s Failed to get config.\n", a.GetName())
}
}
}
// GetTradablePairs returns a list of available
func (a *ANX) GetTradablePairs() ([]string, error) {
result, err := a.GetCurrencies()
if err != nil {
return nil, err
}
var currencies []string
for x := range result.CurrencyPairs {
currencies = append(currencies, result.CurrencyPairs[x].TradedCcy+"_"+result.CurrencyPairs[x].SettlementCcy)
}
return currencies, nil
}
// UpdateTicker updates and returns the ticker for a currency pair

View File

@@ -68,8 +68,8 @@ func (b *Bithumb) SetDefaults() {
b.ConfigCurrencyPairFormat.Uppercase = true
b.ConfigCurrencyPairFormat.Index = "KRW"
b.AssetTypes = []string{ticker.Spot}
b.SupportsAutoPairUpdating = false
b.SupportsRESTTickerBatching = false
b.SupportsAutoPairUpdating = true
b.SupportsRESTTickerBatching = true
b.Requester = request.New(b.Name, request.NewRateLimit(time.Second, bithumbAuthRate), request.NewRateLimit(time.Second, bithumbUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
}
@@ -103,6 +103,20 @@ func (b *Bithumb) Setup(exch config.ExchangeConfig) {
}
}
// GetTradablePairs returns a list of tradable currencies
func (b *Bithumb) GetTradablePairs() ([]string, error) {
result, err := b.GetAllTickers()
if err != nil {
return nil, err
}
var currencies []string
for x := range result {
currencies = append(currencies, x)
}
return currencies, nil
}
// GetTicker returns ticker information
//
// symbol e.g. "btc"
@@ -113,6 +127,44 @@ func (b *Bithumb) GetTicker(symbol string) (Ticker, error) {
return response, b.SendHTTPRequest(path, &response)
}
// GetAllTickers returns all ticker information
func (b *Bithumb) GetAllTickers() (map[string]Ticker, error) {
type Response struct {
Data map[string]interface{}
}
response := Response{}
path := fmt.Sprintf("%s%s%s", apiURL, publicTicker, "all")
err := b.SendHTTPRequest(path, &response)
if err != nil {
return nil, err
}
result := make(map[string]Ticker)
for k, v := range response.Data {
if k == "date" {
continue
}
data := v.(map[string]interface{})
var t Ticker
t.AveragePrice, _ = strconv.ParseFloat(data["average_price"].(string), 64)
t.BuyPrice, _ = strconv.ParseFloat(data["buy_price"].(string), 64)
t.ClosingPrice, _ = strconv.ParseFloat(data["closing_price"].(string), 64)
t.MaxPrice, _ = strconv.ParseFloat(data["max_price"].(string), 64)
t.MinPrice, _ = strconv.ParseFloat(data["min_price"].(string), 64)
t.OpeningPrice, _ = strconv.ParseFloat(data["opening_price"].(string), 64)
t.SellPrice, _ = strconv.ParseFloat(data["sell_price"].(string), 64)
t.UnitsTraded, _ = strconv.ParseFloat(data["units_traded"].(string), 64)
t.Volume1Day, _ = strconv.ParseFloat(data["volume_1day"].(string), 64)
t.Volume7Day, _ = strconv.ParseFloat(data["volume_7day"].(string), 64)
result[k] = t
}
return result, nil
}
// GetOrderBook returns current orderbook
//
// symbol e.g. "btc"

View File

@@ -33,6 +33,14 @@ func TestSetup(t *testing.T) {
b.Setup(bitConfig)
}
func TestGetTradablePairs(t *testing.T) {
t.Parallel()
_, err := b.GetTradablePairs()
if err != nil {
t.Error("test failed - Bithumb GetTradablePairs() error", err)
}
}
func TestGetTicker(t *testing.T) {
t.Parallel()
_, err := b.GetTicker("btc")
@@ -41,6 +49,14 @@ func TestGetTicker(t *testing.T) {
}
}
func TestGetAllTickers(t *testing.T) {
t.Parallel()
_, err := b.GetAllTickers()
if err != nil {
t.Error("test failed - Bithumb GetAllTickers() error", err)
}
}
func TestGetOrderBook(t *testing.T) {
t.Parallel()
_, err := b.GetOrderBook("btc")

View File

@@ -1,24 +1,34 @@
package bithumb
// Ticker holds the standard ticker information
// Ticker holds ticker data
type Ticker struct {
Status string `json:"status"`
Data struct {
OpeningPrice float64 `json:"opening_price,string"`
ClosingPrice float64 `json:"closing_price,string"`
MinPrice float64 `json:"min_price,string"`
MaxPrice float64 `json:"max_price,string"`
AveragePrice float64 `json:"average_price,string"`
UnitsTraded float64 `json:"units_traded,string"`
Volume1Day float64 `json:"volume_1day,string"`
Volume7Day float64 `json:"volume_7day,string"`
BuyPrice float64 `json:"buy_price,string"`
SellPrice float64 `json:"sell_price,string"`
Date int64 `json:"date,string"`
} `json:"data"`
OpeningPrice float64 `json:"opening_price,string"`
ClosingPrice float64 `json:"closing_price,string"`
MinPrice float64 `json:"min_price,string"`
MaxPrice float64 `json:"max_price,string"`
AveragePrice float64 `json:"average_price,string"`
UnitsTraded float64 `json:"units_traded,string"`
Volume1Day float64 `json:"volume_1day,string"`
Volume7Day float64 `json:"volume_7day,string"`
BuyPrice float64 `json:"buy_price,string"`
SellPrice float64 `json:"sell_price,string"`
// Date int64 `json:"date,string"`
}
// TickerResponse holds the standard ticker response
type TickerResponse struct {
Status string `json:"status"`
Data Ticker `json:"data"`
Message string `json:"message"`
}
// TickersResponse holds the standard ticker response
type TickersResponse struct {
Status string `json:"status"`
Data map[string]Ticker `json:"data"`
Message string `json:"message"`
}
// Orderbook holds full range of order book information
type Orderbook struct {
Status string `json:"status"`

View File

@@ -28,26 +28,53 @@ func (b *Bithumb) Run() {
log.Printf("%s polling delay: %ds.\n", b.GetName(), b.RESTPollingDelay)
log.Printf("%s %d currencies enabled: %s.\n", b.GetName(), len(b.EnabledPairs), b.EnabledPairs)
}
exchangeProducts, err := b.GetTradingPairs()
if err != nil {
log.Printf("%s Failed to get available symbols.\n", b.GetName())
} else {
err = b.UpdateCurrencies(exchangeProducts, false, false)
if err != nil {
log.Printf("%s Failed to update available symbols.\n", b.GetName())
}
}
}
// GetTradingPairs gets the available trading currencies
func (b *Bithumb) GetTradingPairs() ([]string, error) {
currencies, err := b.GetTradablePairs()
if err != nil {
return nil, err
}
for x := range currencies {
currencies[x] = currencies[x] + "KRW"
}
return currencies, nil
}
// UpdateTicker updates and returns the ticker for a currency pair
func (b *Bithumb) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
var tickerPrice ticker.Price
item := p.GetFirstCurrency().String()
tick, err := b.GetTicker(item)
tickers, err := b.GetAllTickers()
if err != nil {
return tickerPrice, err
}
tickerPrice.Pair = p
tickerPrice.Ask = tick.Data.SellPrice
tickerPrice.Bid = tick.Data.BuyPrice
tickerPrice.Low = tick.Data.MinPrice
tickerPrice.Last = tick.Data.ClosingPrice
tickerPrice.Volume = tick.Data.Volume1Day
tickerPrice.High = tick.Data.MaxPrice
ticker.ProcessTicker(b.GetName(), p, tickerPrice, assetType)
for _, x := range b.GetEnabledCurrencies() {
currency := x.GetFirstCurrency().String()
var tp ticker.Price
tp.Pair = x
tp.Ask = tickers[currency].SellPrice
tp.Bid = tickers[currency].BuyPrice
tp.Low = tickers[currency].MinPrice
tp.Last = tickers[currency].ClosingPrice
tp.Volume = tickers[currency].Volume1Day
tp.High = tickers[currency].MaxPrice
ticker.ProcessTicker(b.Name, x, tp, assetType)
}
return ticker.GetTicker(b.Name, p, assetType)
}

View File

@@ -152,6 +152,7 @@ func (e *Base) SetAutoPairDefaults() error {
if e.SupportsAutoPairUpdating {
if !exch.SupportsAutoPairUpdates {
exch.SupportsAutoPairUpdates = true
exch.PairsLastUpdated = 0
update = true
}
} else {
@@ -251,7 +252,7 @@ func (e *Base) SetCurrencyPairFormat() error {
exch.RequestCurrencyPairFormat) {
e.RequestCurrencyPairFormat = *exch.RequestCurrencyPairFormat
} else {
*exch.RequestCurrencyPairFormat = e.ConfigCurrencyPairFormat
*exch.RequestCurrencyPairFormat = e.RequestCurrencyPairFormat
update = true
}
}
@@ -440,11 +441,14 @@ func (e *Base) UpdateCurrencies(exchangeProducts []string, enabled, force bool)
}
var newPairs, removedPairs []string
var updateType string
if enabled {
newPairs, removedPairs = pair.FindPairDifferences(e.EnabledPairs, products)
updateType = "enabled"
} else {
newPairs, removedPairs = pair.FindPairDifferences(e.AvailablePairs, products)
updateType = "available"
}
if force || len(newPairs) > 0 || len(removedPairs) > 0 {
@@ -455,7 +459,7 @@ func (e *Base) UpdateCurrencies(exchangeProducts []string, enabled, force bool)
}
if force {
log.Printf("%s forced update of enabled pairs.", e.Name)
log.Printf("%s forced update of %s pairs.", e.Name, updateType)
} else {
if len(newPairs) > 0 {
log.Printf("%s Updating pairs - New: %s.\n", e.Name, newPairs)

View File

@@ -645,14 +645,20 @@ func TestSetCurrencies(t *testing.T) {
UAC := Base{Name: "ASDF"}
UAC.AvailablePairs = []string{"ETHLTC", "LTCBTC"}
UAC.EnabledPairs = []string{"ETHLTC"}
newPair := pair.NewCurrencyPair("ETH", "USDT")
newPair := pair.NewCurrencyPairDelimiter("ETH_USDT", "_")
err = UAC.SetCurrencies([]pair.CurrencyPair{newPair}, true)
if err == nil {
t.Fatal("Test failed. TestSetCurrencies returned nil error on non-existent exchange")
}
anxCfg, err := cfg.GetExchangeConfig("ANX")
if err != nil {
t.Fatal("Test failed. TestSetCurrencies failed to load config")
}
UAC.Name = "ANX"
UAC.ConfigCurrencyPairFormat.Delimiter = anxCfg.ConfigCurrencyPairFormat.Delimiter
UAC.SetCurrencies([]pair.CurrencyPair{newPair}, true)
if !pair.Contains(UAC.GetEnabledCurrencies(), newPair, true) {
t.Fatal("Test failed. TestSetCurrencies failed to set currencies")

View File

@@ -54,7 +54,7 @@ func (l *LakeBTC) SetDefaults() {
l.ConfigCurrencyPairFormat.Delimiter = ""
l.ConfigCurrencyPairFormat.Uppercase = true
l.AssetTypes = []string{ticker.Spot}
l.SupportsAutoPairUpdating = false
l.SupportsAutoPairUpdating = true
l.SupportsRESTTickerBatching = true
l.Requester = request.New(l.Name, request.NewRateLimit(time.Second, lakeBTCAuthRate), request.NewRateLimit(time.Second, lakeBTCUnauth), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
}
@@ -89,6 +89,21 @@ func (l *LakeBTC) Setup(exch config.ExchangeConfig) {
}
}
// GetTradablePairs returns a list of available pairs from the exchange
func (l *LakeBTC) GetTradablePairs() ([]string, error) {
result, err := l.GetTicker()
if err != nil {
return nil, err
}
var currencies []string
for x := range result {
currencies = append(currencies, common.StringToUpper(x))
}
return currencies, nil
}
// GetFee returns maker or taker fee
func (l *LakeBTC) GetFee(maker bool) float64 {
if maker {

View File

@@ -33,6 +33,14 @@ func TestSetup(t *testing.T) {
l.Setup(lakebtcConfig)
}
func TestGetTradablePairs(t *testing.T) {
t.Parallel()
_, err := l.GetTradablePairs()
if err != nil {
t.Fatalf("Test failed. GetTradablePairs err: %s", err)
}
}
func TestGetFee(t *testing.T) {
t.Parallel()
if l.GetFee(false) != 0.2 {

View File

@@ -28,6 +28,16 @@ func (l *LakeBTC) Run() {
log.Printf("%s polling delay: %ds.\n", l.GetName(), l.RESTPollingDelay)
log.Printf("%s %d currencies enabled: %s.\n", l.GetName(), len(l.EnabledPairs), l.EnabledPairs)
}
exchangeProducts, err := l.GetTradablePairs()
if err != nil {
log.Printf("%s Failed to get available products.\n", l.GetName())
} else {
err = l.UpdateCurrencies(exchangeProducts, false, false)
if err != nil {
log.Printf("%s Failed to update available currencies.\n", l.GetName())
}
}
}
// UpdateTicker updates and returns the ticker for a currency pair

View File

@@ -59,10 +59,10 @@ func (w *WEX) SetDefaults() {
w.RequestCurrencyPairFormat.Delimiter = "_"
w.RequestCurrencyPairFormat.Uppercase = false
w.RequestCurrencyPairFormat.Separator = "-"
w.ConfigCurrencyPairFormat.Delimiter = ""
w.ConfigCurrencyPairFormat.Delimiter = "_"
w.ConfigCurrencyPairFormat.Uppercase = true
w.AssetTypes = []string{ticker.Spot}
w.SupportsAutoPairUpdating = false
w.SupportsAutoPairUpdating = true
w.SupportsRESTTickerBatching = true
w.Requester = request.New(w.Name, request.NewRateLimit(time.Second, wexAuthRate), request.NewRateLimit(time.Second, wexUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
}
@@ -97,6 +97,21 @@ func (w *WEX) Setup(exch config.ExchangeConfig) {
}
}
// GetTradablePairs returns a list of available pairs from the exchange
func (w *WEX) GetTradablePairs() ([]string, error) {
result, err := w.GetInfo()
if err != nil {
return nil, err
}
var currencies []string
for x := range result.Pairs {
currencies = append(currencies, common.StringToUpper(x))
}
return currencies, nil
}
// GetFee returns the exchange fee
func (w *WEX) GetFee() float64 {
return w.Fee

View File

@@ -32,6 +32,14 @@ func TestSetup(t *testing.T) {
w.Setup(conf)
}
func TestGetTradablePairs(t *testing.T) {
t.Parallel()
_, err := w.GetTradablePairs()
if err != nil {
t.Errorf("Test failed. GetTradablePairs err: %s", err)
}
}
func TestGetFee(t *testing.T) {
t.Parallel()
if w.GetFee() != 0.2 {

View File

@@ -28,6 +28,30 @@ func (w *WEX) Run() {
log.Printf("%s polling delay: %ds.\n", w.GetName(), w.RESTPollingDelay)
log.Printf("%s %d currencies enabled: %s.\n", w.GetName(), len(w.EnabledPairs), w.EnabledPairs)
}
exchangeProducts, err := w.GetTradablePairs()
if err != nil {
log.Printf("%s Failed to get available symbols.\n", w.GetName())
} else {
forceUpgrade := false
if !common.StringDataContains(w.EnabledPairs, "_") || !common.StringDataContains(w.AvailablePairs, "_") {
forceUpgrade = true
}
if forceUpgrade {
enabledPairs := []string{"BTC_USD", "LTC_USD", "LTC_BTC", "ETH_USD"}
log.Println("WARNING: Enabled pairs for WEX reset due to config upgrade, please enable the ones you would like again.")
err = w.UpdateCurrencies(enabledPairs, true, true)
if err != nil {
log.Printf("%s Failed to get config.\n", w.GetName())
}
}
err = w.UpdateCurrencies(exchangeProducts, false, forceUpgrade)
if err != nil {
log.Printf("%s Failed to get config.\n", w.GetName())
}
}
}
// UpdateTicker updates and returns the ticker for a currency pair

File diff suppressed because one or more lines are too long