phase 6: ownership holders

This commit is contained in:
shawnkim1997
2026-04-22 11:22:37 +01:00
parent 553ec5347f
commit 71e045e434
6 changed files with 350 additions and 3 deletions
+24 -1
View File
@@ -2,7 +2,7 @@
from __future__ import annotations
from server.core.data_gateway import Profile, Quote, Segment
from server.core.data_gateway import HoldersData, Profile, Quote, Segment
from server.core.providers.base import BaseProvider, ProviderError, ProviderNotConfigured, ProviderNotImplemented
from server.services import fmp_client
@@ -95,3 +95,26 @@ class FMPProvider(BaseProvider):
"periods": periods,
"line_items": line_items,
}
async def holders(self, symbol: str) -> HoldersData:
normalized = symbol.strip().upper()
inst: object = []
insider: object = []
try:
inst = await self._get_json(f"/institutional-holder/{normalized}", {})
except ProviderError:
inst = []
try:
insider = await self._get_json("/insider-trading", {"symbol": normalized, "limit": 25})
except ProviderError:
insider = []
institutions = inst if isinstance(inst, list) else []
insiders = insider if isinstance(insider, list) else []
if not institutions and not insiders:
raise ProviderError("missing holders rows")
return HoldersData(
symbol=normalized,
institutions=institutions[:25],
insiders=insiders[:25],
source=self.name,
)
@@ -5,7 +5,7 @@ from __future__ import annotations
from datetime import date
from typing import Any
from server.core.data_gateway import Fundamentals, OHLCV, OHLCVBar, Profile, Quote
from server.core.data_gateway import Fundamentals, HoldersData, OHLCV, OHLCVBar, Profile, Quote
from server.core.providers.base import BaseProvider, ProviderError
from server.utils.peer_universe import peer_symbols_for_profile
@@ -149,6 +149,24 @@ class YFinanceProvider(BaseProvider):
return await self._to_thread(fetch)
async def holders(self, symbol: str) -> HoldersData:
def fetch() -> HoldersData:
import pandas as pd
normalized = symbol.strip().upper()
ticker = self._ticker(normalized)
institutions: list[dict[str, Any]] = []
insiders: list[dict[str, Any]] = []
inst_df = getattr(ticker, "institutional_holders", None)
if isinstance(inst_df, pd.DataFrame) and not inst_df.empty:
institutions = inst_df.where(inst_df.notna(), None).to_dict(orient="records")
insider_df = getattr(ticker, "insider_roster_holders", None)
if isinstance(insider_df, pd.DataFrame) and not insider_df.empty:
insiders = insider_df.where(insider_df.notna(), None).to_dict(orient="records")
return HoldersData(symbol=normalized, institutions=institutions[:25], insiders=insiders[:25], source=self.name)
return await self._to_thread(fetch)
async def history(self, symbol: str, range: str = "1y") -> OHLCV:
def fetch() -> OHLCV:
hist = self._ticker(symbol).history(period=range)