| import requests |
| from fastapi import FastAPI, HTTPException, status |
| from pydantic import BaseModel |
| from typing import List, Dict, Optional, Any |
|
|
|
|
| from data_ingestion.api_loader import get_daily_adjusted_prices, DataIngestionError |
| import logging |
|
|
|
|
| logging.basicConfig( |
| level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" |
| ) |
| logger = logging.getLogger(__name__) |
|
|
| app = FastAPI(title="API Agent") |
|
|
|
|
| class MarketDataRequest(BaseModel): |
| tickers: List[str] |
| start_date: Optional[str] = None |
| end_date: Optional[str] = None |
| data_type: Optional[str] = "adjClose" |
|
|
|
|
| @app.post("/get_market_data") |
| def get_market_data(request: MarketDataRequest): |
| """ |
| Fetches daily adjusted market data by calling the data_ingestion layer (FMP). |
| Returns adjusted close prices per ticker per date. |
| """ |
| result: Dict[str, Dict[str, float]] = {} |
| errors: Dict[str, str] = {} |
| warnings: Dict[str, str] = {} |
|
|
| key = ( |
| request.data_type |
| if request.data_type in ["open", "high", "low", "close", "adjClose", "volume"] |
| else "adjClose" |
| ) |
|
|
| for ticker in request.tickers: |
| try: |
| raw = get_daily_adjusted_prices(ticker) |
|
|
| time_series: Dict[str, Any] = {} |
| if isinstance(raw, dict): |
| time_series = raw |
| elif isinstance(raw, list): |
| logger.warning( |
| f"Loader returned list for {ticker}; filtering dict entries." |
| ) |
| for rec in raw: |
| if isinstance(rec, dict) and "date" in rec: |
| date_val = rec["date"] |
| time_series[date_val] = rec |
| else: |
| logger.warning( |
| f"Skipping non-dict or missing-date entry for {ticker}: {rec}" |
| ) |
| else: |
| raise DataIngestionError( |
| f"Unexpected format from loader for {ticker}: {type(raw)}" |
| ) |
|
|
| ticker_prices: Dict[str, float] = {} |
| for date_str, values in time_series.items(): |
| if not isinstance(values, dict): |
| warnings.setdefault(ticker, "") |
| warnings[ticker] += f" Non-dict for {date_str}; skipped." |
| continue |
| if key not in values: |
| warnings.setdefault(ticker, "") |
| warnings[ticker] += f" Missing '{key}' on {date_str}." |
| continue |
| try: |
| ticker_prices[date_str] = float(values[key]) |
| except (TypeError, ValueError): |
| warnings.setdefault(ticker, "") |
| warnings[ticker] += f" Invalid '{key}' value on {date_str}." |
|
|
| if ticker_prices: |
| result[ticker] = ticker_prices |
| logger.info(f"Fetched {len(ticker_prices)} points for {ticker}.") |
| else: |
| warnings.setdefault(ticker, "") |
| warnings[ticker] += " No valid data points found." |
|
|
| except (requests.RequestException, DataIngestionError) as err: |
| errors[ticker] = str(err) |
| logger.error(f"Error fetching {ticker}: {err}") |
| except Exception as err: |
| errors[ticker] = f"Unexpected error for {ticker}: {err}" |
| logger.error(errors[ticker]) |
|
|
| if not result and errors: |
| raise HTTPException( |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, |
| detail="Failed to fetch market data for all tickers.", |
| ) |
|
|
| return {"result": result, "errors": errors, "warnings": warnings} |
|
|