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

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

Error message

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

What it means

fetch_security_price narrows to a single day by calling fetch_security_prices(symbol:, start_date: date, end_date: date) and raising InvalidSecurityPriceError when the returned data array is empty. An empty array means Tiingo's daily-prices endpoint accepted the request (no API error was raised) but has no candle rows for that symbol on that date. The most common legitimate cause is a non-trading day; it can also mean the symbol has no end-of-day data (unsupported asset class) or was listed after the date.

Source

Thrown at app/models/provider/tiingo.rb:163

      SecurityInfo.new(
        symbol: parsed["ticker"] || symbol,
        name: parsed["name"],
        links: nil,
        logo_url: nil,
        description: parsed["description"],
        kind: nil,
        exchange_operating_mic: resolved_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
      throttle_request
      track_symbol(symbol)

      response = client.get("#{base_url}/tiingo/daily/#{CGI.escape(symbol)}/prices") do |req|
        req.params["startDate"] = start_date.to_s
        req.params["endDate"] = end_date.to_s
      end

      parsed = JSON.parse(response.body)
      check_api_error!(parsed)

View on GitHub (pinned to e69894adb9)

Solutions

  1. Validate the date is an exchange trading day before calling, or walk back to the previous trading day
  2. Confirm the symbol actually returns data: fetch_security_prices over a wider range and inspect which dates exist
  3. If the symbol is not an equity EOD series (forex/crypto/fund), route it to the provider/endpoint that supports it instead of /tiingo/daily

Example fix

# before
price = provider.fetch_security_price(symbol: symbol, date: date)

# after
trading_day = TradeDate.previous_on_or_before(date, exchange_mic: exchange_operating_mic)
price = provider.fetch_security_price(symbol: symbol, exchange_operating_mic: exchange_operating_mic, date: trading_day)
Defensive patterns

Strategy: fallback

Validate before calling

# Check the calendar before asking for a price
date = TradingCalendar.previous_trading_day(date) unless TradingCalendar.trading_day?(date)

Try / catch

begin
  price = provider.fetch_security_price(symbol: sym, date: date)
rescue Provider::Tiingo::InvalidSecurityPriceError
  price = nil # caller falls back to last known price
end

Prevention

When it happens

Trigger: Calling fetch_security_price(symbol: 'AAPL', date: Date.new(2026, 8, 22)) where that date is a Saturday/Sunday or market holiday; requesting a date before the security's IPO; requesting a forex/crypto symbol on the /tiingo/daily/ equities endpoint which only covers EOD equity data.

Common situations: Manual valuation flows letting users pick arbitrary calendar dates; job that prices securities 'as of today' running on weekends/holidays; portfolios with delisted tickers whose history was trimmed; symbols from other exchanges Tiingo daily doesn't cover.

Related errors


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