PYTHON_OK CLASS= INIT_SOURCE def __init__( self, api_key: str, base_url: str = "https://eodhd.com/api", timeout: float = 20.0, transport: Optional[httpx.BaseTransport] = None, ): self._api_key = api_key self._base_url = base_url.rstrip("/") self._client = httpx.Client(base_url=self._base_url, timeout=timeout, transport=transport) self._profile_currency: Optional[str] = None PROFILE_SOURCE def get_company_profile(self, ticker: str, exchange_mic: Optional[str] = None) -> ProviderCompanyProfile: code = f"{ticker}.{exchange_mic}" if exchange_mic else ticker data = self._get(f"/fundamentals/{code}") general = data.get("General", {}) self._profile_currency = general.get("CurrencyCode") or "USD" return ProviderCompanyProfile( ticker=ticker, exchange_mic=general.get("Exchange"), legal_name=general.get("Name", ticker), display_name=general.get("Name", ticker), country_iso2=general.get("CountryISO"), sector=general.get("Sector"), industry=general.get("Industry"), currency=general.get("CurrencyCode", "USD"), isin=general.get("ISIN"), website=general.get("WebURL"), description=general.get("Description"), beta=None, ) PRICES_SOURCE def get_prices(self, ticker: str, start: date, end: date) -> list[ProviderPriceBar]: """AUDIT FIX (Part D — docs/AUDIT_VALIDATION_D.md). This used to raise `KeyError` on one malformed bar, discarding every well-formed bar in the same response, and to pass a non-numeric `close` straight through as a `str`. Both behaviours were pinned by `tests/test_eodhd_adapter.py` in Part A7 and deliberately left for this part, so the fix could be applied to FMP and EODHD together instead of making the two inconsistent. Now: each row is validated individually; a bad row is dropped with a recorded reason and the good rows are returned. `self.last_price_validation` holds the report.""" currency = self._profile_currency if not currency: raise ProviderNotFoundError( f"No provider currency available for {ticker}; " "get_company_profile() must succeed before get_prices()." ) rows = self._get(f"/eod/{ticker}", period="d", **{"from": start.isoformat(), "to": end.isoformat()}) report = ValidationReport() bars = [] for r in rows: clean = validate_price_bar( { "date": r.get("date"), "open": r.get("open"), "high": r.get("high"), "low": r.get("low"), "close": r.get("close"), "adjusted_close": r.get("adjusted_close"), "volume": r.get("volume"), }, report, ) if clean is None: continue bars.append(ProviderPriceBar( date=_parse_date(clean["date"]), open=clean["open"], high=clean["high"], low=clean["low"], close=clean["close"], adjusted_close=clean["adjusted_close"], volume=clean["volume"], currency=currency, )) self.last_price_validation = report return bars DIRECT_INSTANCE _profile_currency_before= None DIRECT_GET_PRICES=EXCEPTION ProviderNotFoundError No provider currency available for AAPL; get_company_profile() must succeed before get_prices(). DIAGNOSTIC_EODHD=PASS