📈 Yahoo Finance Provider (yfinance)
The Yahoo Finance provider fetches stock, ETF, crypto, and index prices using the yfinance library. It is the primary market data provider for LibreFolio.
Implementation: backend/app/services/asset_source_providers/yahoo_finance/plugin.py
📖 User Guide: Yahoo Finance — User Manual
⚙️ How it Works
- Identifier: A Yahoo Finance ticker symbol (e.g.,
AAPL,BTC-USD,IE00B4L5Y983). - Identifier Types:
TICKERandISINare both accepted. ISIN is resolved by yfinance internally. - No
provider_params: Yahoo Finance does not require any additional configuration.
💰 Current Value (get_current_value)
- The asset-source service checks its core current-value cache (120s TTL) before invoking the provider.
- Calls
ticker.info— the YahooquoteSummaryendpoint. ReturnsregularMarketPrice,currency, andregularMarketTimein a single lightweight call. - Fallback price:
currentPrice→previousClose(ifregularMarketPriceis null). - No
history()call at all — current value polling never touches the chart API.
Avoid history() and fast_info for current price
ticker.history()calls the chart API and logs noisyEntering history()lines.ticker.fast_infointernally triggers 2 heavy HTTP calls (range=1y+range=5d).ticker.infouses thequoteSummaryendpoint — lighter, returns price + currency together.
Cache TTL vs Polling Interval
Frontend polls every 30s for responsive UI, while the core cache has a 120s TTL. Repeated polls for the same provider key can therefore return without another Yahoo call.
📈 Historical Data (get_history_value)
The public method now delegates its local work to focused helpers without changing its contract:
- Acquire —
_acquire_history_data() start_date == "min"callsticker.history(period="max").- A finite range calls
ticker.history(start=start_date, end=end_date + 1 day), preserving LibreFolio's inclusive end date over yfinance's exclusiveend. - Currency metadata falls back to
USD. - Dividend and split series are acquired independently and best-effort; one failing does not suppress the other or the prices.
- Map —
_map_history_prices() - Yahoo index values are converted to UTC before extracting the calendar date.
- Rows are filtered to the requested inclusive range.
- A row with
Close = NaNis skipped. OptionalOpen,High,Low, orVolumeNaNs becomeNone. - The provider returns only actual trading days; the query layer performs backward fill.
- Events —
_parse_dividend_events()and_parse_split_events() - Positive in-range dividends and non-trivial in-range split ratios become typed asset events.
- The two parsers are also isolated best-effort, so either event family can survive a failure in the other.
Validation and data failures remain typed AssetSourceErrors: unsupported identifier types use
INVALID_IDENTIFIER_TYPE, a missing optional dependency uses NOT_AVAILABLE, empty/unusable
history uses NO_DATA, and malformed or unexpected Yahoo responses use FETCH_ERROR.
No provider-internal thread hop
Yahoo's yfinance calls remain synchronous and direct. The provider does not call
asyncio.to_thread(); the asset-source service invokes the whole provider coroutine through
asset_sources.core._run_provider_in_thread() in a dedicated thread with a new event loop.
🔎 Search (search)
- Uses
yfinance.Search(query)for real-time ticker search. - Minimum query length: 2 characters.
- Results limited to top 20 quotes.
quoteTypeis normalized to LibreFolio asset types:equity → STOCK,etf → ETF,mutualfund → FUND,cryptocurrency → CRYPTO, etc.- Currency is fetched per-symbol via
_fetch_currency()(cached separately, 24-hour TTL).
📋 Metadata (fetch_asset_metadata)
- Fetches full
ticker.infofrom Yahoo Finance. - Extracts:
asset_type,currency,short_description(fromlongBusinessSummary),sector(single-sector distribution),identifier_ticker,identifier_isin. - ISIN is obtained via
ticker.isin(not available for all markets). - Unknown sectors are logged as warnings and mapped to "Other".
🔗 get_asset_url
Returns https://finance.yahoo.com/quote/{identifier}.
📅 Asset Events
During sync, the provider generates:
DIVIDENDevents fromticker.dividends— ex-dividend date + per-share amount.SPLITevents fromticker.splits— split date + ratio value (e.g.,4.0for a 4:1 split).
Event acquisition and parsing are best-effort and independent. Events that are returned are
persisted by asset_sources/price_store.py::_upsert_asset_events(), keyed by
provider_assignment_id.
⚡ Caching Strategy
| Owner / Cache | Key | TTL | Max Size | Purpose |
|---|---|---|---|---|
| Core current value | Provider code + identifier + identifier type (refresh also includes the provider-parameter hash) | 120 sec | 300 | Avoid repeated ticker.info calls during current-price polling and refresh. |
| Core history | Provider code + identifier + identifier type + provider-parameter hash | 15 min | 500 | Reuse fetched date ranges during provider-driven refresh. |
| Core search query | Provider code + normalized query | 15 min | 500 | Avoid repeating the same provider search. |
| Yahoo currency lookup | symbol |
24 hours | 2000 | Cache the provider's fast_info currency sub-operation used by search(). |
All are in-memory, per-process TTL caches created with get_ttl_cache(). The shared result caches
are owned by asset_sources/core.py; Yahoo owns only its currency sub-operation cache.
No provider-level history cache
Yahoo has no provider-level history cache. The outer refresh layer may reuse its core history
cache and persists fetched prices to the database. get_prices_bulk() is DB-only; it never
calls Yahoo while serving a historical query (see
Architecture — Price Query).
🧪 Test Configuration
| Property | Value |
|---|---|
test_cases |
[{identifier: "AAPL", identifier_type: TICKER}] |
test_search_query |
"Apple" |
⚠️ Limitations
- Rate limits: Yahoo Finance may throttle requests. No built-in rate limiter — rely on core sync Semaphore.
- ISIN resolution: Not all ISINs are resolvable by yfinance (depends on market coverage).
- Data gaps: Some tickers may have missing days or delayed data.
📦 Dependency
- Library:
yfinance— installed viapipenv install yfinance. - Optional import: If
yfinanceis not installed, current/history/search calls raise anAssetSourceErrorwith error codeNOT_AVAILABLE; metadata refresh degrades toNone. - Transitive:
pandas(required by yfinance).
🔗 Related Documentation
- 📖 Yahoo Finance — User Guide — End-user configuration guide
- 📦 Providers Overview — All available providers
- 💰 Asset Architecture — Sync pipeline and price queries
- 📈 Asset Plugin Guide — How to create a new provider