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
- Confirm the exact SECID on iss.moex.com/iss/securities?q={symbol} and use that value verbatim (e.g. SBER, not SBER.ME).
- Resolve user-supplied symbols through the provider's search endpoint (which returns proper SECIDs) instead of trusting raw input.
- Normalize input with normalize_secid and strip suffixes like .ME before resolving.
- 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
- Always resolve symbols through the provider's search endpoint instead of trusting external-vendor tickers.
- Normalize away foreign suffixes (.ME, exchange codes) before resolving a SECID.
- Retire (don't retry) securities whose boards disappear — delisting is permanent.
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
- API error: #{parsed["Error Message"]}
- API error: #{response.code}
- API error: #{parsed['message'] || parsed['status']}
- No price found for #{symbol} on #{date}
- No MOEX FX rate for #{from}/#{to} on #{date}
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/6d0dbd759bd319d5.
Report an issue: GitHub.