package ticker import ( "errors" "fmt" "strings" "time" "github.com/gofrs/uuid" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/dispatch" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) func init() { service = new(Service) service.Tickers = make(map[string]map[*currency.Item]map[*currency.Item]map[asset.Item]*Ticker) service.Exchange = make(map[string]uuid.UUID) service.mux = dispatch.GetNewMux() } // SubscribeTicker subcribes to a ticker and returns a communication channel to // stream new ticker updates func SubscribeTicker(exchange string, p currency.Pair, a asset.Item) (dispatch.Pipe, error) { exchange = strings.ToLower(exchange) service.RLock() defer service.RUnlock() tick, ok := service.Tickers[exchange][p.Base.Item][p.Quote.Item][a] if !ok { return dispatch.Pipe{}, fmt.Errorf("ticker item not found for %s %s %s", exchange, p, a) } return service.mux.Subscribe(tick.Main) } // SubscribeToExchangeTickers subcribes to all tickers on an exchange func SubscribeToExchangeTickers(exchange string) (dispatch.Pipe, error) { exchange = strings.ToLower(exchange) service.RLock() defer service.RUnlock() id, ok := service.Exchange[exchange] if !ok { return dispatch.Pipe{}, fmt.Errorf("%s exchange tickers not found", exchange) } return service.mux.Subscribe(id) } // GetTicker checks and returns a requested ticker if it exists func GetTicker(exchange string, p currency.Pair, tickerType asset.Item) (*Price, error) { exchange = strings.ToLower(exchange) service.RLock() defer service.RUnlock() if service.Tickers[exchange] == nil { return nil, fmt.Errorf("no tickers for %s exchange", exchange) } if service.Tickers[exchange][p.Base.Item] == nil { return nil, fmt.Errorf("no tickers associated with base currency %s", p.Base) } if service.Tickers[exchange][p.Base.Item][p.Quote.Item] == nil { return nil, fmt.Errorf("no tickers associated with quote currency %s", p.Quote) } if service.Tickers[exchange][p.Base.Item][p.Quote.Item][tickerType] == nil { return nil, fmt.Errorf("no tickers associated with asset type %s", tickerType) } return &service.Tickers[exchange][p.Base.Item][p.Quote.Item][tickerType].Price, nil } // ProcessTicker processes incoming tickers, creating or updating the Tickers // list func ProcessTicker(tickerNew *Price) error { if tickerNew.ExchangeName == "" { return fmt.Errorf(ErrExchangeNameUnset) } if tickerNew.Pair.IsEmpty() { return fmt.Errorf("%s %s", tickerNew.ExchangeName, errPairNotSet) } if tickerNew.AssetType == "" { return fmt.Errorf("%s %s %s", tickerNew.ExchangeName, tickerNew.Pair, errAssetTypeNotSet) } if tickerNew.LastUpdated.IsZero() { tickerNew.LastUpdated = time.Now() } return service.Update(tickerNew) } // Update updates ticker price func (s *Service) Update(p *Price) error { name := strings.ToLower(p.ExchangeName) s.Lock() ticker, ok := s.Tickers[name][p.Pair.Base.Item][p.Pair.Quote.Item][p.AssetType] if ok { ticker.Last = p.Last ticker.High = p.High ticker.Low = p.Low ticker.Bid = p.Bid ticker.Ask = p.Ask ticker.Volume = p.Volume ticker.QuoteVolume = p.QuoteVolume ticker.PriceATH = p.PriceATH ticker.Open = p.Open ticker.Close = p.Close ticker.LastUpdated = p.LastUpdated ids := append(ticker.Assoc, ticker.Main) s.Unlock() return s.mux.Publish(ids, p) } switch { case s.Tickers[name] == nil: s.Tickers[name] = make(map[*currency.Item]map[*currency.Item]map[asset.Item]*Ticker) fallthrough case s.Tickers[name][p.Pair.Base.Item] == nil: s.Tickers[name][p.Pair.Base.Item] = make(map[*currency.Item]map[asset.Item]*Ticker) fallthrough case s.Tickers[name][p.Pair.Base.Item][p.Pair.Quote.Item] == nil: s.Tickers[name][p.Pair.Base.Item][p.Pair.Quote.Item] = make(map[asset.Item]*Ticker) } err := s.SetItemID(p, name) if err != nil { s.Unlock() return err } s.Unlock() return nil } // SetItemID retrieves and sets dispatch mux publish IDs func (s *Service) SetItemID(p *Price, fmtName string) error { if p == nil { return errors.New(errTickerPriceIsNil) } ids, err := s.GetAssociations(p, fmtName) if err != nil { return err } singleID, err := s.mux.GetID() if err != nil { return err } s.Tickers[fmtName][p.Pair.Base.Item][p.Pair.Quote.Item][p.AssetType] = &Ticker{Price: *p, Main: singleID, Assoc: ids} return nil } // GetAssociations links a singular book with it's dispatch associations func (s *Service) GetAssociations(p *Price, fmtName string) ([]uuid.UUID, error) { if p == nil || *p == (Price{}) { return nil, errors.New(errTickerPriceIsNil) } var ids []uuid.UUID exchangeID, ok := s.Exchange[fmtName] if !ok { var err error exchangeID, err = s.mux.GetID() if err != nil { return nil, err } s.Exchange[fmtName] = exchangeID } ids = append(ids, exchangeID) return ids, nil }