You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

67 lines
2.4 KiB
Python

"""
Gainer snapshot collector — fetches top 200 day_gainers every 5-min slot.
Uses direct Yahoo Finance API via yahoo_client (no yfinance_plus).
"""
import asyncio
import logging
from datetime import datetime, timezone
logger = logging.getLogger(__name__)
def _floor_to_5min(dt: datetime) -> datetime:
return dt.replace(minute=dt.minute - (dt.minute % 5), second=0, microsecond=0)
async def collect_gainer_snapshot() -> int:
"""Fetch 200 day_gainers and bulk-insert into gainer_snapshots. Returns inserted count."""
from app.services.gainers.yahoo_client import fetch_day_gainers_sync, _parse_quote
from app.core.database import AsyncSessionLocal
from app.models.gainer_snapshot import GainerSnapshot
from sqlalchemy.dialects.postgresql import insert as pg_insert
snapshot_at = _floor_to_5min(datetime.now(timezone.utc))
loop = asyncio.get_event_loop()
raw_quotes, total, error = await loop.run_in_executor(
None, fetch_day_gainers_sync, 200, 0
)
if error:
logger.error("[Gainers] fetch failed: %s", error)
return 0
if not raw_quotes:
logger.warning("[Gainers] No stocks returned (total=%d)", total)
return 0
rows = [
{
"snapshot_at": snapshot_at,
"rank": rank,
"symbol": s["symbol"],
"name": s.get("name"),
"exchange": s.get("exchange"),
"price": s.get("price"),
"change_percent": s.get("change_percent"),
"volume": s.get("volume"),
"avg_volume_3m": s.get("avg_volume_3m"),
"market_cap": s.get("market_cap"),
"pe_ratio": s.get("pe_ratio"),
"forward_pe": s.get("forward_pe"),
"eps_ttm": s.get("eps_ttm"),
"dividend_yield": s.get("dividend_yield"),
"fifty_two_week_high": s.get("fifty_two_week_high"),
"fifty_two_week_low": s.get("fifty_two_week_low"),
}
for rank, s in enumerate((_parse_quote(q) for q in raw_quotes), start=1)
]
async with AsyncSessionLocal() as db:
stmt = pg_insert(GainerSnapshot).values(rows)
stmt = stmt.on_conflict_do_nothing(constraint="uq_gainer_snapshot_symbol")
await db.execute(stmt)
await db.commit()
logger.info("[Gainers] snapshot %s%d/%d rows", snapshot_at.isoformat(), len(rows), total)
return len(rows)