we-promise/sure · error · Provider::MoexPublic::InvalidSecurityPriceError
No price found for #{symbol} on #{date}
Error message
No price found for #{symbol} on #{date} What it means
Raised by Provider::MoexPublic#fetch_security_price (an InvalidSecurityPriceError) when a same-day history query (start_date == end_date == date) for a resolved SECID returns zero candles. MOEX ISS /history/... only returns rows for trading sessions, so the single-day window is empty whenever `date` is not a session day or the board had no trades. Resolution already succeeded (resolve_instrument ran), so this is purely 'no rows in the candle history for that exact day'.
Source
Thrown at app/models/provider/moex_public.rb:138
logo_url: nil,
description: nil,
kind: instrument[:kind],
exchange_operating_mic: MOEX_MIC
)
end
end
def fetch_security_price(symbol:, exchange_operating_mic:, date:)
with_provider_response do
historical = fetch_security_prices(
symbol: symbol,
exchange_operating_mic: exchange_operating_mic,
start_date: date,
end_date: date
)
raise historical.error if historical.error.present?
raise InvalidSecurityPriceError, "No price found for #{symbol} on #{date}" if historical.data.blank?
# Exact date if present, else the nearest available close on or before it.
historical.data.find { |p| p.date == date } ||
historical.data.select { |p| p.date <= date }.max_by(&:date) ||
historical.data.first
end
end
def fetch_security_prices(symbol:, exchange_operating_mic:, start_date:, end_date:)
with_provider_response do
secid = normalize_secid(symbol)
instrument = resolve_instrument(secid)
bond = instrument[:market].to_s.downcase == "bonds"
prices = history_prices(secid, instrument, start_date, end_date, bond)
# The history endpoint does not carry the live/most-recent session, so for
# a range reaching today append the current marketdata price.View on GitHub (pinned to e69894adb9)
Solutions
- Widen the window — call fetch_security_prices(symbol:, start_date: date - 7.days, end_date: date) and pick select { |p| p.date <= date }.max_by(&:date), exactly like the MFAPI provider does.
- Skip non-trading days: check the date against the MOEX trading calendar (or fall back to 'most recent session' via the provider's current-price path) before requesting.
- Validate the date is within the instrument's listing period (from /securities/{secid}.json metadata) and not in the future.
- Catch InvalidSecurityPriceError per-date and carry forward the last known price instead of failing the valuation batch.
Example fix
# before historical = fetch_security_prices(symbol: symbol, exchange_operating_mic: exchange_operating_mic, start_date: date, end_date: date) # after historical = fetch_security_prices(symbol: symbol, exchange_operating_mic: exchange_operating_mic, start_date: date - 7.days, end_date: date) # raise only if the whole 7-day window is empty, then reuse the existing closest-previous fallback below
Defensive patterns
Strategy: validation
Validate before calling
window_start = date - 7.days # ask for a window, not one day, so non-trading dates still resolve prices = provider.fetch_security_prices(symbol: secid, exchange_operating_mic: mic, start_date: window_start, end_date: date)
Try / catch
begin price = provider.fetch_security_price(symbol: secid, exchange_operating_mic: mic, date: date) rescue Provider::MoexPublic::InvalidSecurityPriceError price = nil # carry forward previous close end
Prevention
- Never request a single trading day from a session-based exchange; always pass a lookback window.
- Skip known non-trading days (weekends, exchange holidays) in valuation jobs.
- Cache the last good price per security so empty days degrade silently.
When it happens
Trigger: Calling fetch_security_price with a Saturday, Sunday, or Russian public holiday as date; a thinly-traded bond board with no trades that session; a date after delisting; a date before the instrument listed on the resolved board; requesting a future date.
Common situations: Daily portfolio valuation jobs that run on weekends and naively ask for today's price; syncing US-style date assumptions against the MOEX calendar (Russia has its own holiday set); illiquid corporate bonds on non-primary boards.
Related errors
- No NAV found for scheme #{symbol} on or before #{date}
- Could not sign in with that passkey. Please try again or use
- No price found for #{symbol} on #{date}
- No prices found for security #{symbol} on date #{date}
- Unexpected response format from EOD API
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/23192c59c8aebef2.
Report an issue: GitHub.