diff --git a/exchanges/huobi/huobi_futures.go b/exchanges/huobi/huobi_futures.go index ff1ffd60570..fc0611c7499 100644 --- a/exchanges/huobi/huobi_futures.go +++ b/exchanges/huobi/huobi_futures.go @@ -1249,6 +1249,11 @@ func (h *HUOBI) formatFuturesPair(p currency.Pair, convertQuoteToExpiry bool) (s // We need this because some apis, such as ticker, use BTC_CW, NW, CQ, NQ // Other apis, such as contract_info, use contract type of this_week, next_week, quarter (sic), and next_quater func (h *HUOBI) pairFromContractExpiryCode(p currency.Pair) (currency.Pair, error) { + if h.futureContractCodes == nil { // Worst race-case here is that we fetch twice. No avoiding it. + if _, err := h.FetchTradablePairs(context.Background(), asset.Futures); err != nil { + return p, err + } + } h.futureContractCodesMutex.RLock() defer h.futureContractCodesMutex.RUnlock() exp, ok := h.futureContractCodes[p.Quote.String()] diff --git a/exchanges/huobi/huobi_wrapper.go b/exchanges/huobi/huobi_wrapper.go index f0deb80ad87..e71e34ba1eb 100644 --- a/exchanges/huobi/huobi_wrapper.go +++ b/exchanges/huobi/huobi_wrapper.go @@ -283,12 +283,19 @@ func (h *HUOBI) FetchTradablePairs(ctx context.Context, a asset.Item) (currency. pairs = append(pairs, pair) } case asset.Futures: + // We cache contract expiries on the exchange locally right now because there's no exchange base holder for them + // It's not as dangerous as it seems, because when contracts change, so would tradeable pairs, + // so by caching them in FetchTradablePairs we're not adding any extra-layer of out-of-date data + // Note: Until this runs the first time the futureContractCodes is nil, which acts as an uninitialized flag for pairFromContractExpiryCode + h.futureContractCodesMutex.Lock() + defer h.futureContractCodesMutex.Unlock() + h.futureContractCodes = map[string]currency.Code{} + symbols, err := h.FGetContractInfo(ctx, "", "", currency.EMPTYPAIR) if err != nil { return nil, err } pairs = make([]currency.Pair, 0, len(symbols.Data)) - expiryCodeDates := map[string]currency.Code{} for _, c := range symbols.Data { if c.ContractStatus != 1 { continue @@ -299,19 +306,13 @@ func (h *HUOBI) FetchTradablePairs(ctx context.Context, a asset.Item) (currency. } pairs = append(pairs, pair) if cType, ok := contractExpiryNames[c.ContractType]; ok { - if v, ok := expiryCodeDates[cType]; !ok { - expiryCodeDates[cType] = currency.NewCode(pair.Quote.String()) + if v, ok := h.futureContractCodes[cType]; !ok { + h.futureContractCodes[cType] = currency.NewCode(pair.Quote.String()) } else if v.String() != pair.Quote.String() { return nil, fmt.Errorf("%w: %s (%s vs %s)", errInconsistentContractExpiry, cType, v.String(), pair.Quote.String()) } } } - // We cache contract expiries on the exchange locally right now because there's no exchange base holder for them - // It's not as dangerous as it seems, because when contracts change, so would tradeable pairs, - // so by caching them in FetchTradablePairs we're not adding any extra-layer of out-of-date data - h.futureContractCodesMutex.Lock() - h.futureContractCodes = expiryCodeDates - h.futureContractCodesMutex.Unlock() } return pairs, nil }