we-promise/sure · warning · Provider::Eodhd::InvalidSecurityPriceError

No prices found for security #{symbol} on date #{date}

Error message

No prices found for security #{symbol} on date #{date}

What it means

Raised as Provider::Eodhd::InvalidSecurityPriceError when fetch_security_price requests a single day ('GET /api/eod/<ticker>' with from=to=date) and the successful response contains zero price rows. The upstream call itself succeeded — this error means EODHD returned an empty array for that ticker/date, which happens for non-trading days or symbols the exchange has no data for on that date. It signals 'no data', not a transport or auth failure.

Source

Thrown at app/models/provider/eodhd.rb:193

      SecurityInfo.new(
        symbol: symbol,
        name: general.dig("Name"),
        links: general.dig("WebURL"),
        logo_url: general.dig("LogoURL"),
        description: general.dig("Description"),
        kind: general.dig("Type"),
        exchange_operating_mic: exchange_operating_mic
      )
    end
  end

  def fetch_security_price(symbol:, exchange_operating_mic: nil, date:)
    with_provider_response do
      historical_data = fetch_security_prices(symbol:, exchange_operating_mic:, start_date: date, end_date: date)

      raise historical_data.error if historical_data.error.present?
      raise InvalidSecurityPriceError, "No prices found for security #{symbol} on date #{date}" if historical_data.data.blank?

      historical_data.data.first
    end
  end

  def fetch_security_prices(symbol:, exchange_operating_mic: nil, start_date:, end_date:)
    with_provider_response do
      enforce_daily_limit!
      throttle_request

      ticker = eodhd_symbol(symbol, exchange_operating_mic)

      response = client.get("#{base_url}/api/eod/#{CGI.escape(ticker)}") do |req|
        req.params["api_token"] = api_key
        req.params["fmt"] = "json"
        req.params["from"] = start_date.to_s
        req.params["to"] = end_date.to_s
      end

View on GitHub (pinned to e69894adb9)

Solutions

  1. Check whether the requested date is a weekend or market holiday for that exchange; if so, request the most recent prior trading day instead
  2. Verify the ticker exists on EODHD for that date: curl 'https://eodhd.com/api/eod/AAPL.US?api_token=TOKEN&from=2024-01-01&to=2024-01-02&fmt=json'
  3. Confirm the exchange_operating_mic maps to the right EODHD suffix (eodhd_symbol translation) so you are not querying the wrong exchange
  4. For backfills, iterate over trading days only (e.g. use a market calendar) rather than every calendar day
  5. If the security is delisted or listed later than the date, handle 'no price' as expected and skip rather than treat as an outage

Example fix

# before: blows up on weekends/holidays
price = provider.fetch_security_price(symbol: "AAPL.US", date: date)

# after: roll back to the previous trading day (Sat/Sun only; add a holiday calendar for full correctness)
request_date = date
while request_date.saturday? || request_date.sunday?
  request_date = request_date.prev_day
end
price = provider.fetch_security_price(symbol: "AAPL.US", date: request_date)
Defensive patterns

Strategy: validation

Validate before calling

# Skip non-trading days before calling fetch_security_price
TRADING_CENTERS_HOLIDAYS = {} # populate from a market calendar gem/source

def tradable_date?(date, market)
  return false if date.saturday? || date.sunday?
  !TRADING_CENTERS_HOLIDAYS.dig(market, date)
end

return unless tradable_date?(date, "US")

Try / catch

begin
  price = provider.fetch_security_price(symbol: sym, date: date)
rescue Provider::Eodhd::InvalidSecurityPriceError
  # expected for weekends/holidays/delisted — record absence, do not alert
  mark_price_missing(sym, date)
end

Prevention

When it happens

Trigger: Calling fetch_security_price(symbol:, date:) where date falls on a weekend or market holiday, the symbol/exchange MIC maps to an EODHD ticker with no quotation that day (delisted security, IPO later than the date), or the exchange was closed (e.g. asking for a US equity price on a Saturday). Any date prior to the security's listing also triggers it.

Common situations: Backfilling historical prices over calendar date ranges that include weekends/holidays without skipping them, requesting prices for delisted tickers, timezone misalignment between the app's Date.current and the exchange's trading day, and new securities with no trading history yet.

Related errors


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