"""Aurelius adapter — Finance (ingested mode): the financial knowledge graph. Not just companies any more: the ingested graph is heterogeneous — companies, ETFs, sectors, executives, countries and macro indicators, wired together by typed, weighted relationships (node kind rides in features.kind): sector_member / has_member company ↔ sector holds / held_by ETF ↔ constituent company led_by / leads company ↔ chief executive based_in / headquarters_of company ↔ country supplies / supplied_by supplier ↔ customer (curated seed set) competes_with rivals (curated, symmetric) owns_stake / stake_held_by investor ↔ holding co_moves return-correlation kNN (computed) macro_correlates macro indicator ↔ company (computed) The computed edges carry the actual correlation as weight, so every path step through them is evidence ("co-moves, corr 0.72"), not assertion — format_edge below is what the UI shows on paths and expansions. Ingestion (ingest/finance.py) pulls real daily closes from Yahoo's chart endpoint for companies, ETFs and macro symbols, merges the curated relationship seed set, and writes everything into core.store. Tickers are almost pure structure, hence FUSION_ALPHA["finance"] = 0.8. """ from __future__ import annotations import random from typing import Optional from core.source import register from core.store import StoreBackedSource _PHRASES = { "sector_member": "in sector", "has_member": "sector includes", "holds": "holds", "held_by": "held by", "led_by": "led by", "leads": "leads", "based_in": "headquartered in", "headquarters_of": "home market of", "supplies": "supplies", "supplied_by": "supplied by", "competes_with": "competes with", "owns_stake": "owns a stake in", "stake_held_by": "stake held by", } _DEMO_PAIRS = [ ("NVDA", "Warren Buffett"), ("Tim Cook", "XOM"), ("TSLA", "Crude Oil"), ("SPY", "Taiwan"), ("JPM", "Jensen Huang"), ("F", "US 10-Year Treasury Yield"), ] class FinanceSource(StoreBackedSource): name = "finance" description = ("Financial knowledge graph — companies, ETFs, sectors, " "executives & macro indicators with typed relationships.") edge_types = ("sector_member", "holds", "led_by", "based_in", "supplies", "competes_with", "owns_stake", "co_moves", "macro_correlates") supports_backlinks = True def format_edge(self, typ: str, weight: float) -> str: if typ in ("co_moves", "macro_correlates"): label = "co-moves" if typ == "co_moves" else "macro-linked" return f"{label} (corr {weight:.2f})" return _PHRASES.get(typ, typ.replace("_", " ")) async def resolve(self, query: str) -> Optional[object]: # Tickers are case-insensitive; StoreBackedSource.find_nodes already # matches on id and title case-insensitively, and ingest uppercases # ids, so a lowercased query still resolves. Just normalize obvious # "$NVDA" / "nvda" forms first. return await super().resolve(query.strip().lstrip("$").upper() if len(query.strip()) <= 6 else query) async def sample_pair(self): if not self.ingested(): return None return random.choice(_DEMO_PAIRS) register(FinanceSource())