main
py 34 lines 2.11 KB
Raw
1 """Pure, provider-neutral sector leaderboard projection."""
2 from __future__ import annotations
3
4 from collections import defaultdict
5 from typing import Iterable
6
7
8 def normalize_sector(value: str) -> tuple[str, str]:
9 key = " ".join(str(value).casefold().split())
10 aliases = {"financial services": ("financials", "Financials"), "financials": ("financials", "Financials"), "consumer cyclical": ("consumer-discretionary", "Consumer Discretionary"), "consumer discretionary": ("consumer-discretionary", "Consumer Discretionary"), "health care": ("healthcare", "Healthcare"), "healthcare": ("healthcare", "Healthcare")}
11 return aliases.get(key, (key.replace(" ", "-"), value.strip().title()))
12
13
14 def build_sector_leaderboard(candidates: Iterable[dict]) -> dict:
15 # Candidate selection happens by authoritative global identity before a
16 # duplicate can occupy a ranked sector slot.
17 winners: dict[str, dict] = {}
18 for candidate in candidates:
19 if not candidate.get("globalInstrumentId") or not candidate.get("sector") or candidate.get("score") is None:
20 continue
21 identity = str(candidate["globalInstrumentId"])
22 prior = winners.get(identity)
23 key = (-candidate["score"], -int(candidate.get("evidenceCoverage") or 0), str(candidate.get("ticker") or "").upper(), identity)
24 if prior is None or key < (-prior["score"], -int(prior.get("evidenceCoverage") or 0), str(prior.get("ticker") or "").upper(), identity):
25 winners[identity] = dict(candidate)
26 grouped: dict[str, list[dict]] = defaultdict(list)
27 labels: dict[str, str] = {}
28 for candidate in winners.values():
29 normalized, label = normalize_sector(candidate["sector"]); grouped[normalized].append(candidate); labels.setdefault(normalized, label)
30 sectors=[]
31 for normalized in sorted(grouped):
32 stocks = sorted(grouped[normalized], key=lambda c: (-c["score"], -int(c.get("evidenceCoverage") or 0), str(c.get("ticker") or "").upper(), str(c["globalInstrumentId"])))[:5]
33 sectors.append({"sector": labels[normalized], "normalizedSector": normalized, "stocks": stocks})
34 return {"sectors": sectors}