we-promise/sure · error · Provider::MoexPublic::InvalidSecurityPriceError

Unknown MOEX security: #{secid}

Error message

Unknown MOEX security: #{secid}

What it means

Raised by Provider::MoexPublic#resolve_instrument (an InvalidSecurityPriceError) when the ISS /securities/{secid}.json response's boards block is empty — i.e. MOEX knows no trading board for that SECID. Resolution happens inside a Rails.cache.fetch with a 24h TTL, but a raise skips the cache write, so failures are re-queried every time. This fires before any price fetch, for both search and price paths.

Source

Thrown at app/models/provider/moex_public.rb:322

      case market.to_s.downcase
      when "bonds" then "bond"
      when /index/ then "index"
      else "stock"
      end
    end

    # ================================
    #     Board / engine resolution
    # ================================

    # Resolves a SECID to its primary trading board plus engine/market, currency,
    # display name, and kind. Cached 24h — reference data that rarely changes.
    def resolve_instrument(secid)
      cached = Rails.cache.fetch("moex_public:instrument:#{secid}", expires_in: INSTRUMENT_CACHE_TTL) do
        body = get_json("/securities/#{secid}.json", "iss.meta" => "off")
        desc = description_map(body)
        boards = rows_from(body, "boards")
        raise InvalidSecurityPriceError, "Unknown MOEX security: #{secid}" if boards.empty?

        board = choose_board(boards)
        kind = security_kind(desc["GROUP"] || desc["TYPE"], desc["TYPE"]) || market_kind(board["market"])

        {
          secid: secid,
          engine: board["engine"].to_s,
          market: board["market"].to_s,
          board: board["boardid"].to_s,
          currency: normalize_currency(board["currencyid"].presence || desc["FACEUNIT"].presence || desc["CURRENCYID"]),
          name: (desc["SHORTNAME"].presence || desc["NAME"].presence || secid).to_s,
          kind: kind
        }
      end

      cached.symbolize_keys
    end

View on GitHub (pinned to e69894adb9)

Solutions

  1. Confirm the exact SECID on iss.moex.com/iss/securities?q={symbol} and use that value verbatim (e.g. SBER, not SBER.ME).
  2. Resolve user-supplied symbols through the provider's search endpoint (which returns proper SECIDs) instead of trusting raw input.
  3. Normalize input with normalize_secid and strip suffixes like .ME before resolving.
  4. Handle delisted securities by retiring the Security record rather than re-resolving on every sync.

Example fix

# before
provider.fetch_security_prices(symbol: "SBER.ME", ...)

# after
secid = symbol.delete_suffix(".ME").upcase # or resolve via provider.search_securities first
provider.fetch_security_prices(symbol: secid, ...)
Defensive patterns

Strategy: validation

Validate before calling

secid = symbol.to_s.upcase.delete_suffix(".ME")
resolved = provider.search_securities(secid)&.data&.first # confirm the SECID exists before price calls
raise ArgumentError, "unresolvable MOEX symbol #{symbol}" if resolved.nil?

Try / catch

begin
  provider.fetch_security_prices(symbol: secid, exchange_operating_mic: mic, start_date: from, end_date: to)
rescue Provider::MoexPublic::InvalidSecurityPriceError => e
  retire_security(symbol) if e.message.start_with?("Unknown MOEX security")
end

Prevention

When it happens

Trigger: Passing a Yahoo-style symbol (SBER.ME, GAZP.ME) instead of the MOEX SECID (SBER, GAZP); a typo'd or case-mangled SECID; a delisted security whose boards were retired; SECIDs for instruments the provider filters (indices, futures, FX like USD000UTSTOM can lack usable boards depending on shape); OTC-only instruments with no board membership.

Common situations: Importing a symbol list sourced from another data vendor without normalization; migrating tickers after a company rename (old SECID stops resolving); user free-text symbol entry; cached-good instruments going stale after delisting (the 24h positive cache masks it for up to a day).

Related errors


AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21). Data as JSON: /api/errors/6d0dbd759bd319d5. Report an issue: GitHub.