/data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/resolver.py.before-sl004-20260910-181036:23: disagrees by more than `tolerance_pct` (default 0.5%) — small rounding/currency-conversion /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/eodhd.py:4:Deliberately a partial implementation: auth wiring, the fundamentals-profile call, and the /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/eodhd.py:30: ProviderCompanyProfile, ProviderDividendRow, ProviderEstimateRow, ProviderFinancialPeriod, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/eodhd.py:66: def get_company_profile(self, ticker: str, exchange_mic: Optional[str] = None) -> ProviderCompanyProfile: /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/eodhd.py:70: return ProviderCompanyProfile( /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/eodhd.py:73: sector=general.get("Sector"), industry=general.get("Industry"), currency=general.get("CurrencyCode", "USD"), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/eodhd.py:104: volume=clean["volume"], currency="USD", /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/schemas.py:15:class ProviderCompanyProfile: /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/schemas.py:23: currency: str /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/schemas.py:39: currency: str /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/schemas.py:52: currency: str /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/schemas.py:69: currency: str /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/resolver.py.before-sl004-20260910-144102:23: disagrees by more than `tolerance_pct` (default 0.5%) — small rounding/currency-conversion /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/base.py:14: ProviderCompanyProfile, ProviderDividendRow, ProviderEstimateRow, ProviderFinancialPeriod, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/base.py:40: def get_company_profile(self, ticker: str, exchange_mic: Optional[str] = None) -> ProviderCompanyProfile: ... /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:10:generated from a small seed profile per company, with a fixed random seed per ticker so the /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:22: ProviderCompanyProfile, ProviderDividendRow, ProviderEstimateRow, ProviderFinancialPeriod, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:30:class DemoSeedProfile: /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:37: currency: str /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:55:DEMO_SEED_PROFILES: list[DemoSeedProfile] = [ /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:56: DemoSeedProfile("AAPL", "XNAS", "Apple Inc. (DEMO)", "US", "TECHNOLOGY", "CONSUMER_ELECTRONICS", "USD", 180000, 0.08, 0.42, 0.30, 0.0, 0.15, 0.02, 0.5, 0.15, 0.75, 15500, 1.25, 28), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:57: DemoSeedProfile("MSFT", "XNAS", "Microsoft Corp. (DEMO)", "US", "TECHNOLOGY", "SOFTWARE", "USD", 90000, 0.13, 0.68, 0.42, 0.0, 0.17, 0.10, 1.0, 0.28, 0.25, 7400, 0.90, 32), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:58: DemoSeedProfile("NVDA", "XNAS", "NVIDIA Corp. (DEMO)", "US", "TECHNOLOGY", "SEMICONDUCTORS", "USD", 11000, 0.35, 0.72, 0.45, 0.0, 0.16, 0.05, 0.1, 0.02, 0.10, 24500, 1.7, 45), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:59: DemoSeedProfile("JNJ", "XNYS", "Johnson & Johnson (DEMO)", "US", "HEALTHCARE", "PHARMACEUTICALS", "USD", 70000, 0.04, 0.66, 0.24, 0.0, 0.15, 0.05, 1.2, 0.55, 0.20, 2600, 0.55, 17), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:60: DemoSeedProfile("JPM", "XNYS", "JPMorgan Chase (DEMO)", "US", "FINANCIALS", "BANKS", "USD", 100000, 0.06, None, 0.35, 0.0, 0.22, None, None, 0.28, 0.20, 2900, 1.1, 12), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:61: DemoSeedProfile("PG", "XNYS", "Procter & Gamble (DEMO)", "US", "CONSUMER_STAPLES", "HOUSEHOLD_PRODUCTS", "USD", 65000, 0.03, 0.49, 0.22, 0.0, 0.19, 0.04, 1.0, 0.58, 0.20, 2350, 0.45, 24), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:62: DemoSeedProfile("XOM", "XNYS", "ExxonMobil (DEMO)", "US", "ENERGY", "OIL_GAS", "USD", 240000, 0.02, 0.22, 0.10, 0.0, 0.20, 0.07, 1.0, 0.40, 0.15, 4000, 1.15, 13), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:63: DemoSeedProfile("SAP", "XETR", "SAP SE (DEMO)", "DE", "TECHNOLOGY", "SOFTWARE", "EUR", 24000, 0.07, 0.72, 0.24, 0.0, 0.24, 0.03, 1.5, 0.35, 0.10, 1230, 0.95, 26), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:64: DemoSeedProfile("ASML", "XAMS", "ASML Holding (DEMO)", "NL", "TECHNOLOGY", "SEMICONDUCTOR_EQUIPMENT", "EUR", 11000, 0.15, 0.50, 0.32, 0.0, 0.16, 0.06, 0.3, 0.20, 0.05, 393, 1.15, 33), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:65: DemoSeedProfile("NESN", "XSWX", "Nestle S.A. (DEMO)", "CH", "CONSUMER_STAPLES", "PACKAGED_FOODS", "CHF", 88000, 0.02, 0.47, 0.16, 0.0, 0.20, 0.05, 1.3, 0.65, 0.15, 2650, 0.55, 20), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:66: DemoSeedProfile("MC", "XPAR", "LVMH (DEMO)", "FR", "CONSUMER_DISCRETIONARY", "LUXURY_GOODS", "EUR", 35000, 0.10, 0.68, 0.26, 0.0, 0.23, 0.05, 0.9, 0.45, 0.05, 500, 1.05, 24), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:67: DemoSeedProfile("NOVO-B", "XCSE", "Novo Nordisk (DEMO)", "DK", "HEALTHCARE", "PHARMACEUTICALS", "DKK", 90000, 0.14, 0.83, 0.44, 0.0, 0.20, 0.06, 0.6, 0.45, 0.05, 4480, 0.35, 34), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:68: DemoSeedProfile("SHEL", "XLON", "Shell plc (DEMO)", "GB", "ENERGY", "OIL_GAS", "USD", 260000, 0.02, 0.20, 0.09, 0.0, 0.30, 0.06, 1.0, 0.40, 0.20, 6900, 1.05, 11), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:69: DemoSeedProfile("7203", "XTKS", "Toyota Motor (DEMO)", "JP", "CONSUMER_DISCRETIONARY", "AUTOMOBILES", "JPY", 25000000, 0.03, 0.19, 0.08, 0.0, 0.24, 0.05, 1.8, 0.30, 0.05, 14300, 0.75, 10), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:70: DemoSeedProfile("005930", "XKRX", "Samsung Electronics (DEMO)", "KR", "TECHNOLOGY", "SEMICONDUCTORS", "KRW", 240000000, 0.05, 0.38, 0.16, 0.0, 0.20, 0.14, 0.4, 0.25, 0.02, 5970000, 1.0, 14), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:71: DemoSeedProfile("0700", "XHKG", "Tencent Holdings (DEMO)", "HK", "TECHNOLOGY", "INTERNET_SERVICES", "HKD", 400000, 0.14, 0.48, 0.32, 0.0, 0.15, 0.06, 0.5, 0.0, 0.10, 9400, 1.1, 22), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:72: DemoSeedProfile("005380", "XKRX", "Hyundai Motor (DEMO)", "KR", "CONSUMER_DISCRETIONARY", "AUTOMOBILES", "KRW", 100000000, 0.04, 0.20, 0.08, 0.0, 0.23, 0.04, 1.2, 0.25, 0.05, 208000, 1.1, 6), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:73: DemoSeedProfile("RELIANCE", "XBOM", "Reliance Industries (DEMO)", "IN", "ENERGY", "CONGLOMERATE", "INR", 4500000, 0.12, 0.28, 0.14, 0.0, 0.24, 0.09, 1.5, 0.10, 0.02, 6770, 0.95, 25), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:74: DemoSeedProfile("CBA", "XASX", "Commonwealth Bank (DEMO)", "AU", "FINANCIALS", "BANKS", "AUD", 20000, 0.04, None, 0.42, 0.0, 0.28, None, None, 0.70, 0.05, 1700, 0.85, 17), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:75: DemoSeedProfile("4SBK", "XBUL", "First Investment Bank (DEMO)", "BG", "FINANCIALS", "BANKS", "EUR", 500, 0.05, None, 0.30, 0.0, 0.10, None, None, 0.15, 0.0, 176, 0.90, 8), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:78:_INDEX_BY_TICKER = {p.ticker: p for p in DEMO_SEED_PROFILES} /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:89:def _generate_annual_series(profile: DemoSeedProfile) -> list[dict]: /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:91: rng = _seeded_random(profile.ticker) /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:92: is_bank = profile.sector_code == "FINANCIALS" /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:93: revenue = profile.starting_revenue_musd /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:99: revenue = revenue * (1 + _jitter(rng, profile.revenue_cagr)) /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:100: gross_margin = profile.gross_margin if profile.gross_margin is not None else None /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:103: operating_income = revenue * _jitter(rng, profile.operating_margin, 0.04) /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:109: tax_expense = max(pretax_income, 0) * profile.tax_rate /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:112: shares = profile.shares_outstanding_musd_equiv * ((1 - profile.buyback_pct_of_fcf * 0.01) ** i) /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:115: capex = revenue * profile.capex_pct_revenue if profile.capex_pct_revenue else revenue * 0.03 /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:118: dividends_paid = max(net_income, 0) * profile.dividend_payout_pct * 0.5 if profile.dividend_payout_pct else 0.0 /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:119: buybacks = max(fcf, 0) * profile.buyback_pct_of_fcf if profile.buyback_pct_of_fcf else 0.0 /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:122: total_debt = ebitda_for_debt * profile.debt_to_ebitda_target if profile.debt_to_ebitda_target is not None else revenue * 0.6 /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:137: # A conventional maturity split; the demo profiles carry no maturity schedule of their own. /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:144: currency=profile.currency, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:172: def _profile(self, ticker: str) -> DemoSeedProfile: /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:175: raise ProviderNotFoundError(f"No DEMO seed profile for {ticker}. Available: {sorted(_INDEX_BY_TICKER)}") /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:178: def get_company_profile(self, ticker: str, exchange_mic: Optional[str] = None) -> ProviderCompanyProfile: /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:179: p = self._profile(ticker) /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:180: return ProviderCompanyProfile( /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:182: country_iso2=p.country_iso2, sector=p.sector_code, industry=p.industry_code, currency=p.currency, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:187: return _generate_annual_series(self._profile(ticker)) /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:200: p = self._profile(ticker) /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:204: currency=p.currency, line_items=r, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:210: p = self._profile(ticker) /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:222: close=price, adjusted_close=price, volume=1_000_000, currency=p.currency)) /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:226: base_price, base_price, bars[-1].volume, p.currency) /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:232: p = self._profile(ticker) /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:242: p = self._profile(ticker) /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:252: amount_per_share=round(dps, 4), currency=p.currency, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/demo.py:258: p.ticker for p in DEMO_SEED_PROFILES /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/resolver.py:23: disagrees by more than `tolerance_pct` (default 0.5%) — small rounding/currency-conversion /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/fmp.py:22: ProviderCompanyProfile, ProviderDividendRow, ProviderEstimateRow, ProviderFinancialPeriod, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/fmp.py:96: def get_company_profile(self, ticker: str, exchange_mic: Optional[str] = None) -> ProviderCompanyProfile: /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/fmp.py:97: data = self._get(f"/profile/{ticker}") /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/fmp.py:99: raise ProviderNotFoundError(f"No FMP profile for {ticker}") /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/fmp.py:101: return ProviderCompanyProfile( /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/fmp.py:104: sector=row.get("sector"), industry=row.get("industry"), currency=row.get("currency", "USD"), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/fmp.py:125: currency=row.get("reportedCurrency", "USD"), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/fmp.py:164: volume=clean["volume"], currency="USD", /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/fmp.py:188: amount_per_share=r.get("dividend", 0.0), currency="USD", /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/adapters/resolver.py.before-sl004-20260910-180740:23: disagrees by more than `tolerance_pct` (default 0.5%) — small rounding/currency-conversion /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:110:def _resolve_required_reference_data(db: Session, ticker: str, profile) -> tuple: /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:124: db.query(Country).filter_by(iso2=profile.country_iso2).one_or_none() /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:125: if profile.country_iso2 else None /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:130: f"{ticker}: cannot resolve a company country. The provider profile reported " /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:131: f"country_iso2={profile.country_iso2!r} and no `countries` row matches it. " /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:137: if not profile.exchange_mic: /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:142: f"{ticker}: the provider profile carries no exchange MIC. `securities.exchange_id` is " /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:144: f"provider profile rather than attaching the security to an arbitrary exchange." /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:147: identity = resolve_company_identity(profile.country_iso2, profile.exchange_mic) /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:151: mic = canonical_mic(profile.exchange_mic) /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:164: "ingest.exchange.country_unknown", ticker=ticker, mic=profile.exchange_mic, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:170: f"{ticker}: exchange MIC {profile.exchange_mic!r} is not in " /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:177: f"{ticker}: exchange MIC {profile.exchange_mic!r} resolves to country " /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:186: profile = adapter.get_company_profile(ticker) /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:188: db, ticker, profile /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:191: sector = db.query(Sector).filter_by(code=(profile.sector or "DEFAULT").upper()).one_or_none() /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:193: sector = Sector(code=(profile.sector or "DEFAULT").upper(), name=profile.sector or "Unclassified") /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:196: industry_code = (profile.industry or profile.sector or "DEFAULT").upper() /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:199: industry = Industry(code=industry_code, name=profile.industry or "Unclassified", sector_id=sector.id) /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:246: legal_name=profile.legal_name, display_name=profile.display_name, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:248: website=profile.website, description=profile.description, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:255: isin=profile.isin, currency=profile.currency, discovery_status="QUALIFIED", /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:332: currency=bar.currency, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:344: row.currency = bar.currency /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/workers/ingest.py:462: filing_date=p.filing_date, currency=p.currency, source_id=source.id, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/types.py:77: All monetary values in the security's reporting currency, absolute (not per-share) unless /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/types.py:85: currency: str /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:2:Currency normalization (final master pass, §17). /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:6:§17 requires that StockLab be able to work across currencies: keep the original currency, the /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:7:normalized currency, and the FX source / date / rate; **never convert currency-independent /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:8:ratios**; and check currency consistency across the DCF, the multiples, Fair Value, market cap, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:20:listed as **NOT IMPLEMENTED** in `docs/AUDIT_CURRENCY_IDENTITY.md`. /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:24:**A ratio must never be converted.** A P/E is a currency-free number: price and EPS are in the same /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:25:currency, and the units cancel. Converting the price and forgetting the EPS — or converting the /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:27:rates, yields, and leverage multiples where both sides share a currency. /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:30:USD" with a loop over every numeric field. `CURRENCY_INDEPENDENT_FIELDS` below is the guard, and /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:50:#: Monetary `LineItems` fields: absolute amounts in the reporting currency. These MAY be converted. /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:67:#: Counts and unitless quantities. Never converted: a share is a share in every currency. /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:70:#: Metric keys that are ratios or rates. Converting any of these is always a bug — the currency /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:72:CURRENCY_INDEPENDENT_FIELDS: frozenset[str] = frozenset({ /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:85:class CurrencyMismatchError(ValueError): /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:90: """Raised when a currency-independent value is offered for conversion.""" /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:97: `rate` converts ONE unit of `from_currency` into `to_currency`: an amount in `from_currency` is /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:102: from_currency: str /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:103: to_currency: str /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:113: if self.from_currency == self.to_currency and self.rate != 1.0: /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:123: original_currency: str /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:124: normalized_currency: str /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:131: return self.original_currency != self.normalized_currency /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:137: "original_currency": self.original_currency, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:138: "normalized_currency": self.normalized_currency, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:146:def identity_rate(currency: str, as_of: date) -> FxRate: /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:147: """The no-op rate. Explicit rather than implicit, so a same-currency value still records that /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:149: return FxRate(currency, currency, 1.0, as_of, source="identity") /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:153: amount: Optional[float], from_currency: str, rate: Optional[FxRate], /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:158: original amount labelled as the target currency is the silent-fallback failure this project /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:162: return ConvertedValue(None, amount, from_currency, from_currency, None, None, None) /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:163: if rate.from_currency != from_currency: /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:164: raise CurrencyMismatchError( /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:165: f"rate converts {rate.from_currency}, but the amount is in {from_currency}" /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:169: value=converted, original_value=amount, original_currency=from_currency, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:170: normalized_currency=rate.to_currency, fx_rate=rate.rate, fx_as_of=rate.as_of, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:175:def convert_metric_value(metric_key: str, value: Optional[float], from_currency: str, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:177: """Convert a metric's value, refusing outright for currency-independent metrics.""" /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:178: if metric_key in CURRENCY_INDEPENDENT_FIELDS: /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:180: f"{metric_key!r} is a ratio or rate: its currency cancels between numerator and " /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:183: return convert_amount(value, from_currency, rate) /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:194: if li.currency != rate.from_currency: /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:195: raise CurrencyMismatchError( /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:196: f"period is in {li.currency}, rate converts {rate.from_currency}" /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:198: updates: dict = {"currency": rate.to_currency} /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:206:def assert_consistent_currency(periods: Iterable[LineItems]) -> str: /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:207: """Return the single currency shared by every period, or raise. /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:209: §17 requires currency consistency across the DCF, multiples, Fair Value, market cap, FCF, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:211: `FinancialSnapshot`, so checking the snapshot's periods share one currency is where that /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:214: currencies = {p.currency for p in periods if p is not None} /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:216: raise CurrencyMismatchError("no periods supplied") /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/currency.py:218: raise CurrencyMismatchError( /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/ttm.py:44:5. **Mixed currencies are refused.** Cross-currency summation is silently wrong. There is no FX /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/ttm.py:99: "security_id", "period_end", "period_type", "filing_date", "currency", /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/ttm.py:184: currencies = {q.currency for q in window} /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/normalize.py:16:_LINE_ITEMS_FIELDS = {f for f in LineItems.__dataclass_fields__ if f not in ("security_id", "period_end", "period_type", "filing_date", "currency")} /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/normalize.py:25: filing_date=p.filing_date, currency=p.currency, price=price, market_cap=market_cap, **kwargs, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/identity.py:36: repository fetches either. FMP's company profile returns one `country` field whose meaning is /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/identity.py:132: `provider_country_iso2` is the single country string a provider profile returns. It is treated /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/identity.py:142: "the provider profile carried no country; deliberately NOT defaulted to the exchange's " /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/reference_data.py:43:- **`currency`** — the country's current official currency. Note **Bulgaria: EUR, not BGN.** /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/reference_data.py:64: currency: str /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/reference_data.py:271:#: The sector code ingestion falls back to when a provider profile carries no sector, and the one /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/reference_data.py:303: Pure: takes pairs, not profiles and not a database, so the exact rows `seed_reference_data()` /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/__init__.py:2: BlendedFairValue, BusinessProfile, blend_fair_values, detect_business_profile, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/__init__.py:16: "BlendedFairValue", "BusinessProfile", "blend_fair_values", "detect_business_profile", /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/wacc.py:22: risk_free_rate: float # e.g. 0.045 for 4.5% — ASSUMPTION, configurable per currency /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/blend.py:10:class BusinessProfile(str, Enum): /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/blend.py:18:DEFAULT_PROFILE_WEIGHTS: dict[BusinessProfile, tuple[float, float]] = { /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/blend.py:19: BusinessProfile.STABLE_FCF: (0.6, 0.4), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/blend.py:20: BusinessProfile.CYCLICAL: (0.3, 0.7), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/blend.py:21: BusinessProfile.FINANCIALS: (0.1, 0.9), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/blend.py:22: BusinessProfile.PRE_FCF: (0.0, 1.0), /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/blend.py:26:def detect_business_profile(sector_code: str, fcf_history: list[Optional[float]]) -> BusinessProfile: /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/blend.py:28: return BusinessProfile.FINANCIALS /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/blend.py:30: return BusinessProfile.CYCLICAL /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/blend.py:33: return BusinessProfile.PRE_FCF /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/blend.py:34: return BusinessProfile.STABLE_FCF /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/blend.py:45: profile: BusinessProfile /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/blend.py:95: profile: BusinessProfile, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/blend.py:99: dcf_w, mult_w = weights_override or DEFAULT_PROFILE_WEIGHTS[profile] /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/blend.py:106: return BlendedFairValue(None, None, None, None, (None, None), 0.0, profile, dcf_w, mult_w, multiples_used) /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/blend.py:128: # Confidence: data completeness, DCF/multiples agreement, business-profile complexity penalty. /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/blend.py:134: BusinessProfile.STABLE_FCF: 0, BusinessProfile.CYCLICAL: 5, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/blend.py:135: BusinessProfile.FINANCIALS: 15, BusinessProfile.PRE_FCF: 20, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/blend.py:136: }[profile] /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/valuation/blend.py:143: confidence=confidence, profile=profile, dcf_weight=dcf_w_eff, multiples_weight=mult_w_eff, /data/files/bmw/stocklab-audit-remediation-SL006-SL007-SL008-SL009-20260910-223000/app/app/engines/quality/accounting.py:211: # tests/fixtures/company_profiles.py; verified by running them).