Files
gocryptotrader/exchanges/ticker/ticker.go
Adrian Gallagher 67a58a10bd Fix ticker empty err and improve test/consistency for orderbook/ticke… (#317)
* Fix ticker empty err and improve test/consistency for orderbook/ticker pkgs
2019-06-17 13:43:13 +10:00

190 lines
5.4 KiB
Go

package ticker
import (
"errors"
"strconv"
"sync"
"time"
"github.com/thrasher-/gocryptotrader/common"
"github.com/thrasher-/gocryptotrader/currency"
)
// const values for the ticker package
const (
errExchangeTickerNotFound = "ticker for exchange does not exist"
errPairNotSet = "ticker currency pair not set"
errAssetTypeNotSet = "ticker asset type not set"
errBaseCurrencyNotFound = "ticker base currency not found"
errQuoteCurrencyNotFound = "ticker quote currency not found"
Spot = "SPOT"
)
// Vars for the ticker package
var (
Tickers []Ticker
m sync.Mutex
)
// 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"`
PriceATH float64 `json:"PriceATH"`
LastUpdated time.Time
}
// Ticker struct holds the ticker information for a currency pair and type
type Ticker struct {
Price map[string]map[string]map[string]Price
ExchangeName string
}
// PriceToString returns the string version of a stored price field
func (t *Ticker) PriceToString(p currency.Pair, priceType, tickerType string) string {
priceType = common.StringToLower(priceType)
switch priceType {
case "last":
return strconv.FormatFloat(t.Price[p.Base.Upper().String()][p.Quote.Upper().String()][tickerType].Last, 'f', -1, 64)
case "high":
return strconv.FormatFloat(t.Price[p.Base.Upper().String()][p.Quote.Upper().String()][tickerType].High, 'f', -1, 64)
case "low":
return strconv.FormatFloat(t.Price[p.Base.Upper().String()][p.Quote.Upper().String()][tickerType].Low, 'f', -1, 64)
case "bid":
return strconv.FormatFloat(t.Price[p.Base.Upper().String()][p.Quote.Upper().String()][tickerType].Bid, 'f', -1, 64)
case "ask":
return strconv.FormatFloat(t.Price[p.Base.Upper().String()][p.Quote.Upper().String()][tickerType].Ask, 'f', -1, 64)
case "volume":
return strconv.FormatFloat(t.Price[p.Base.Upper().String()][p.Quote.Upper().String()][tickerType].Volume, 'f', -1, 64)
case "ath":
return strconv.FormatFloat(t.Price[p.Base.Upper().String()][p.Quote.Upper().String()][tickerType].PriceATH, 'f', -1, 64)
default:
return ""
}
}
// GetTicker checks and returns a requested ticker if it exists
func GetTicker(exchange string, p currency.Pair, tickerType string) (Price, error) {
ticker, err := GetTickerByExchange(exchange)
if err != nil {
return Price{}, err
}
if !BaseCurrencyExists(exchange, p.Base) {
return Price{}, errors.New(errBaseCurrencyNotFound)
}
if !QuoteCurrencyExists(exchange, p) {
return Price{}, errors.New(errQuoteCurrencyNotFound)
}
return ticker.Price[p.Base.Upper().String()][p.Quote.Upper().String()][tickerType], nil
}
// GetTickerByExchange returns an exchange Ticker
func GetTickerByExchange(exchange string) (*Ticker, error) {
m.Lock()
defer m.Unlock()
for x := range Tickers {
if Tickers[x].ExchangeName == exchange {
return &Tickers[x], nil
}
}
return nil, errors.New(errExchangeTickerNotFound)
}
// BaseCurrencyExists checks to see if the base currency of the ticker map
// exists
func BaseCurrencyExists(exchange string, currency currency.Code) bool {
m.Lock()
defer m.Unlock()
for _, y := range Tickers {
if y.ExchangeName == exchange {
if _, ok := y.Price[currency.Upper().String()]; ok {
return true
}
}
}
return false
}
// QuoteCurrencyExists checks to see if the quote currency of the ticker map
// exists
func QuoteCurrencyExists(exchange string, p currency.Pair) bool {
m.Lock()
defer m.Unlock()
for _, y := range Tickers {
if y.ExchangeName == exchange {
if _, ok := y.Price[p.Base.Upper().String()]; ok {
if _, ok := y.Price[p.Base.Upper().String()][p.Quote.Upper().String()]; ok {
return true
}
}
}
}
return false
}
// CreateNewTicker creates a new Ticker
func CreateNewTicker(exchangeName string, tickerNew *Price, tickerType string) Ticker {
m.Lock()
defer m.Unlock()
ticker := Ticker{}
ticker.ExchangeName = exchangeName
ticker.Price = make(map[string]map[string]map[string]Price)
a := make(map[string]map[string]Price)
b := make(map[string]Price)
b[tickerType] = *tickerNew
a[tickerNew.Pair.Quote.Upper().String()] = b
ticker.Price[tickerNew.Pair.Base.Upper().String()] = a
Tickers = append(Tickers, ticker)
return ticker
}
// ProcessTicker processes incoming tickers, creating or updating the Tickers
// list
func ProcessTicker(exchangeName string, tickerNew *Price, assetType string) error {
if tickerNew.Pair.IsEmpty() {
return errors.New(errPairNotSet)
}
if assetType == "" {
return errors.New(errAssetTypeNotSet)
}
if tickerNew.LastUpdated.IsZero() {
tickerNew.LastUpdated = time.Now()
}
ticker, err := GetTickerByExchange(exchangeName)
if err != nil {
CreateNewTicker(exchangeName, tickerNew, assetType)
return nil
}
if BaseCurrencyExists(exchangeName, tickerNew.Pair.Base) {
m.Lock()
a := make(map[string]Price)
a[assetType] = *tickerNew
ticker.Price[tickerNew.Pair.Base.Upper().String()][tickerNew.Pair.Quote.Upper().String()] = a
m.Unlock()
return nil
}
m.Lock()
a := make(map[string]map[string]Price)
b := make(map[string]Price)
b[assetType] = *tickerNew
a[tickerNew.Pair.Quote.Upper().String()] = b
ticker.Price[tickerNew.Pair.Base.Upper().String()] = a
m.Unlock()
return nil
}