diff --git a/app/providers/alpaca.py b/app/providers/alpaca.py index 0d37008..c7f04df 100644 --- a/app/providers/alpaca.py +++ b/app/providers/alpaca.py @@ -9,6 +9,7 @@ from datetime import date from alpaca.data.historical import StockHistoricalDataClient from alpaca.data.requests import StockBarsRequest from alpaca.data.timeframe import TimeFrame +from alpaca.data.enums import Adjustment from app.exceptions import ProviderError, RateLimitError from app.providers.protocol import OHLCVData @@ -34,6 +35,7 @@ class AlpacaOHLCVProvider: timeframe=TimeFrame.Day, start=start_date, end=end_date, + adjustment=Adjustment.SPLIT, ) # alpaca-py's client is synchronous — run in a thread diff --git a/app/routers/ingestion.py b/app/routers/ingestion.py index 2ae79be..50e500a 100644 --- a/app/routers/ingestion.py +++ b/app/routers/ingestion.py @@ -11,11 +11,15 @@ import logging from datetime import date from fastapi import APIRouter, Depends, Query +from sqlalchemy import delete, select from sqlalchemy.ext.asyncio import AsyncSession from app.config import settings from app.dependencies import get_db, require_access from app.exceptions import ProviderError +from app.models.ohlcv import OHLCVRecord +from app.models.settings import IngestionProgress +from app.models.ticker import Ticker from app.models.user import User from app.providers.alpaca import AlpacaOHLCVProvider from app.providers.fundamentals_chain import build_fundamental_provider_chain @@ -47,6 +51,7 @@ async def fetch_symbol( symbol: str, start_date: date | None = Query(None, description="Start date (YYYY-MM-DD)"), end_date: date | None = Query(None, description="End date (YYYY-MM-DD)"), + force_refetch: bool = Query(False, description="Delete existing OHLCV data and re-fetch split-adjusted history"), _user: User = Depends(require_access), db: AsyncSession = Depends(get_db), ): @@ -58,6 +63,26 @@ async def fetch_symbol( symbol_upper = symbol.strip().upper() sources: dict[str, dict] = {} + # If force_refetch is requested, clear old OHLCV and ingestion progress + # so the backfill logic pulls a full year of fresh split-adjusted data. + if force_refetch: + try: + result = await db.execute( + select(Ticker).where(Ticker.symbol == symbol_upper) + ) + ticker_obj = result.scalar_one_or_none() + if ticker_obj: + await db.execute( + delete(OHLCVRecord).where(OHLCVRecord.ticker_id == ticker_obj.id) + ) + await db.execute( + delete(IngestionProgress).where(IngestionProgress.ticker_id == ticker_obj.id) + ) + await db.commit() + logger.info("force_refetch: cleared OHLCV and progress for %s", symbol_upper) + except Exception as exc: + logger.error("force_refetch cleanup failed for %s: %s", symbol_upper, exc) + # --- OHLCV --- try: provider = _get_provider()