add support for alternative ticker universe formatting
This commit is contained in:
+14
-3
@@ -25,13 +25,24 @@ class AlpacaOHLCVProvider:
|
||||
raise ProviderError("Alpaca API key and secret are required")
|
||||
self._client = StockHistoricalDataClient(api_key, api_secret)
|
||||
|
||||
@staticmethod
|
||||
def _to_alpaca_symbol(symbol: str) -> str:
|
||||
"""Convert internal symbol format (BRK-B) to Alpaca format (BRK.B)."""
|
||||
return symbol.replace("-", ".")
|
||||
|
||||
@staticmethod
|
||||
def _from_alpaca_symbol(symbol: str) -> str:
|
||||
"""Convert Alpaca symbol format (BRK.B) back to internal format (BRK-B)."""
|
||||
return symbol.replace(".", "-")
|
||||
|
||||
async def fetch_ohlcv(
|
||||
self, ticker: str, start_date: date, end_date: date
|
||||
) -> list[OHLCVData]:
|
||||
"""Fetch daily OHLCV bars for *ticker* between *start_date* and *end_date*."""
|
||||
alpaca_symbol = self._to_alpaca_symbol(ticker)
|
||||
try:
|
||||
request = StockBarsRequest(
|
||||
symbol_or_symbols=ticker,
|
||||
symbol_or_symbols=alpaca_symbol,
|
||||
timeframe=TimeFrame.Day,
|
||||
start=start_date,
|
||||
end=end_date,
|
||||
@@ -42,11 +53,11 @@ class AlpacaOHLCVProvider:
|
||||
bars = await asyncio.to_thread(self._client.get_stock_bars, request)
|
||||
|
||||
results: list[OHLCVData] = []
|
||||
bar_set = bars.get(ticker, []) if hasattr(bars, "get") else getattr(bars, "data", {}).get(ticker, [])
|
||||
bar_set = bars.get(alpaca_symbol, []) if hasattr(bars, "get") else getattr(bars, "data", {}).get(alpaca_symbol, [])
|
||||
for bar in bar_set:
|
||||
results.append(
|
||||
OHLCVData(
|
||||
ticker=ticker,
|
||||
ticker=ticker, # use original internal symbol
|
||||
date=bar.timestamp.date(),
|
||||
open=float(bar.open),
|
||||
high=float(bar.high),
|
||||
|
||||
@@ -38,6 +38,14 @@ def _safe_float(value: object) -> float | None:
|
||||
return None
|
||||
|
||||
|
||||
def _to_api_symbol(symbol: str) -> str:
|
||||
"""Convert internal symbol format (BRK-B) to API format (BRK.B).
|
||||
|
||||
Finnhub and Alpha Vantage use dot-separated share class notation.
|
||||
"""
|
||||
return symbol.replace("-", ".")
|
||||
|
||||
|
||||
class FinnhubFundamentalProvider:
|
||||
"""Fundamentals provider backed by Finnhub free endpoints."""
|
||||
|
||||
@@ -49,19 +57,20 @@ class FinnhubFundamentalProvider:
|
||||
|
||||
async def fetch_fundamentals(self, ticker: str) -> FundamentalData:
|
||||
unavailable: dict[str, str] = {}
|
||||
api_symbol = _to_api_symbol(ticker)
|
||||
|
||||
async with httpx.AsyncClient(timeout=30.0, verify=_CA_BUNDLE_PATH) as client:
|
||||
profile_resp = await client.get(
|
||||
f"{self._base_url}/stock/profile2",
|
||||
params={"symbol": ticker, "token": self._api_key},
|
||||
params={"symbol": api_symbol, "token": self._api_key},
|
||||
)
|
||||
metric_resp = await client.get(
|
||||
f"{self._base_url}/stock/metric",
|
||||
params={"symbol": ticker, "metric": "all", "token": self._api_key},
|
||||
params={"symbol": api_symbol, "metric": "all", "token": self._api_key},
|
||||
)
|
||||
earnings_resp = await client.get(
|
||||
f"{self._base_url}/stock/earnings",
|
||||
params={"symbol": ticker, "limit": 1, "token": self._api_key},
|
||||
params={"symbol": api_symbol, "limit": 1, "token": self._api_key},
|
||||
)
|
||||
|
||||
for resp, endpoint in (
|
||||
@@ -121,19 +130,20 @@ class AlphaVantageFundamentalProvider:
|
||||
|
||||
async def fetch_fundamentals(self, ticker: str) -> FundamentalData:
|
||||
unavailable: dict[str, str] = {}
|
||||
api_symbol = _to_api_symbol(ticker)
|
||||
|
||||
async with httpx.AsyncClient(timeout=30.0, verify=_CA_BUNDLE_PATH) as client:
|
||||
overview_resp = await client.get(
|
||||
self._base_url,
|
||||
params={"function": "OVERVIEW", "symbol": ticker, "apikey": self._api_key},
|
||||
params={"function": "OVERVIEW", "symbol": api_symbol, "apikey": self._api_key},
|
||||
)
|
||||
earnings_resp = await client.get(
|
||||
self._base_url,
|
||||
params={"function": "EARNINGS", "symbol": ticker, "apikey": self._api_key},
|
||||
params={"function": "EARNINGS", "symbol": api_symbol, "apikey": self._api_key},
|
||||
)
|
||||
income_resp = await client.get(
|
||||
self._base_url,
|
||||
params={"function": "INCOME_STATEMENT", "symbol": ticker, "apikey": self._api_key},
|
||||
params={"function": "INCOME_STATEMENT", "symbol": api_symbol, "apikey": self._api_key},
|
||||
)
|
||||
|
||||
for resp, endpoint in (
|
||||
|
||||
Reference in New Issue
Block a user