Skip to content

[Feature] UK equities (LSE .L) market support in market data + backtest routing #1205

Description

@cgycorey

Feature request: UK market support

The loader layer and MCP get_market_data support US, HK, India, Korea, Canada, Vietnam, and crypto — but not UK equities. LSE (.L) symbols currently resolve to _unresolved.

Current failure (the motivation)

// get_market_data({codes: ["VOD.L"], ...}) with source=auto
{ "_unresolved": ["VOD.L"] }

Root cause: _SOURCE_PATTERNS has no .L rule, so UK symbols fall through to the tushare default. _detect_market also defaults them to a_share. The symbol is then routed down the China fallback chain (tencent → mootdx → eastmoney → baostock → akshare → tushare → local), which cannot serve LSE — several seconds of network attempts later it surfaces _unresolved, indistinguishable from a genuinely dead ticker.

Forcing source=yahoo works today:

VOD.L  → OK 15 bars (close 116.60, 2026-08-04)
SHEL.L → OK 15 bars
HSBA.L → OK 15 bars

Proposal (working fix, locally verified)

Treat UK as a first-class market with the same parity as Canada/US:

  1. src/market_data.py_SOURCE_PATTERNS: add ^[A-Z0-9&.\-]+\.L$yahoo (mirrors Canada .TO/.V); yahoo_loader _is_supported gate: add .L/.IL

  2. backtest/engines/_market_hooks.py_MARKET_PATTERNS: add .Luk_equity; _MARKET_CURRENCY: uk_equity: GBP (currently returns UNKNOWN:uk_equity)

  3. backtest/loaders/registry.pyFALLBACK_CHAINS: add "uk_equity": ["yahoo", "yfinance", "local"] (mirrors ca_equity)

  4. backtest/runner.py_MARKET_TO_SOURCE: add uk_equity: yahoo (currently backtests silently fall to the tushare engine); composite dispatch → GlobalEquityEngine(market="uk")

  5. Price units — Yahoo serves LSE names in pence (GBp): VOD.L close is ~117p, HSBA.L ~1500p; without normalization the engine prices a £1.17 share at £117 (100× notional error on positions, and on SDRT). Normalize OHLC ÷100 at the loader boundary (Yahoo chart-meta currency == "GBp"; yfinance fallback suffix rule), declare currency_conversion in provenance, bump loader cache version so pre-normalization parquet is never served

  6. SDRT cost model — 0.5% Stamp Duty Reserve Tax on purchases, penny rounding with exact-½p rounds-up (HMRC FA86/S99(13), STSM014030)

    Exemptions scope (deliberate): we model the statutory Main Market rate only. AIM shares, UCITS ETFs, gilts and new issues are not auto-detected — the engine has no symbol-aware asset-class classification (per-symbol tax databases drift; a wrong exemption is worse than the flat rate). Caller's control surface: uk_stamp_tax: 0.0 exempts a whole run (exempt-only baskets). The known boundary: a mixed Main-Market + exempt basket in one run pays statutory on everything. Possible follow-up (not built): per-symbol exempt config (uk_stamp_tax_exempt: [...]), which requires widening calc_commission to carry the symbol; AIM auto-detection via Yahoo exchange metadata is feasible, UCITS/new-issue auto-detection is not recommended.

  7. Journal/shadow consumers_infer_market_from_symbol: .L"uk" (journal trades currently land in "other" and shadow analysis silently degrades to NaN price features); shadow _MARKET_KEY_MAP/labels: ukuk_equity, "UK equity"

  8. Financial statements — `get_financial_statements`: `.L` → Yahoo quoteSummary (income/balance/cashflow annual histories + indicators); `get_fundamentals` stays US-only by contract (SEC XBRL PIT panels — no honest UK source for filed-date PIT), fails loud for UK

  9. Tests: source/market/submarket/currency; yahoo + yfinance GBp scaling; market-data provenance; engine economics incl. HMRC rounding cases; journal inference; registry chain contract

Question to maintainers

Is UK support in scope for the project? Happy to adjust scope per your call. Remaining candidates for follow-ups: London market-open spec in MARKET_SPECS (missing market fails loud today, never silent) and symbol-search UK suffix hints.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions