64 lines
2.3 KiB
Python
64 lines
2.3 KiB
Python
"""Alpaca Markets OHLCV provider using the alpaca-py SDK."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import logging
|
|
from datetime import date
|
|
|
|
from alpaca.data.historical import StockHistoricalDataClient
|
|
from alpaca.data.requests import StockBarsRequest
|
|
from alpaca.data.timeframe import TimeFrame
|
|
|
|
from app.exceptions import ProviderError, RateLimitError
|
|
from app.providers.protocol import OHLCVData
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class AlpacaOHLCVProvider:
|
|
"""Fetches daily OHLCV bars from Alpaca Markets Data API."""
|
|
|
|
def __init__(self, api_key: str, api_secret: str) -> None:
|
|
if not api_key or not api_secret:
|
|
raise ProviderError("Alpaca API key and secret are required")
|
|
self._client = StockHistoricalDataClient(api_key, api_secret)
|
|
|
|
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*."""
|
|
try:
|
|
request = StockBarsRequest(
|
|
symbol_or_symbols=ticker,
|
|
timeframe=TimeFrame.Day,
|
|
start=start_date,
|
|
end=end_date,
|
|
)
|
|
|
|
# alpaca-py's client is synchronous — run in a thread
|
|
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, [])
|
|
for bar in bar_set:
|
|
results.append(
|
|
OHLCVData(
|
|
ticker=ticker,
|
|
date=bar.timestamp.date(),
|
|
open=float(bar.open),
|
|
high=float(bar.high),
|
|
low=float(bar.low),
|
|
close=float(bar.close),
|
|
volume=int(bar.volume),
|
|
)
|
|
)
|
|
return results
|
|
|
|
except Exception as exc:
|
|
msg = str(exc).lower()
|
|
if "rate" in msg and "limit" in msg:
|
|
raise RateLimitError(f"Alpaca rate limit hit for {ticker}") from exc
|
|
logger.error("Alpaca provider error for %s: %s", ticker, exc)
|
|
raise ProviderError(f"Alpaca provider error for {ticker}: {exc}") from exc
|