"""Read provenance from saved results, retaining every experiment for an Alpha.""" from collections import defaultdict from fastapi import HTTPException from fastapi.encoders import jsonable_encoder from sqlalchemy import func, select from ..models import Alpha, BacktestItem, BacktestResult, BacktestRun def saved_sources(): """Only persisted results establish provenance; pending platform IDs do not.""" return ( select(BacktestResult, BacktestItem, BacktestRun) .select_from(BacktestResult) .join(BacktestItem, BacktestResult.item_id == BacktestItem.id) .join(BacktestRun, BacktestItem.run_id == BacktestRun.id) ) def source_alpha_ids(source=None, source_reference=None, research_id=None, backtest_run_id=None): """An IN subquery keeps list counts and exports independent of source multiplicity.""" query = saved_sources().with_only_columns(BacktestResult.alpha_id) for key, value in (("kind", source), ("reference", source_reference), ("research_id", research_id)): if value: query = query.where(BacktestRun.source[key].as_string() == value) if backtest_run_id: query = query.where(BacktestRun.id == backtest_run_id) return query async def source_kinds(db, alpha_ids=None): query = saved_sources().with_only_columns(BacktestResult.alpha_id, BacktestRun.source["kind"].as_string()) if alpha_ids is not None: query = query.where(BacktestResult.alpha_id.in_(alpha_ids)) values = defaultdict(list) for alpha_id, kind in await db.execute(query.distinct()): if kind: values[alpha_id].append(kind) return {alpha_id: sorted(kinds) for alpha_id, kinds in values.items()} async def alpha_sources(db, alpha_id, limit=25, offset=0): if not await db.get(Alpha, alpha_id): raise HTTPException(404, "Alpha 尚未同步") query = saved_sources().where(BacktestResult.alpha_id == alpha_id) total = await db.scalar(select(func.count()).select_from(query.subquery())) rows = await db.execute( query.order_by(BacktestResult.observed_at.desc(), BacktestResult.item_id).limit(limit).offset(offset) ) return jsonable_encoder( { "alpha_id": alpha_id, "total": total, "limit": limit, "offset": offset, "items": [ { "backtest_run_id": run.id, "name": run.name, "source": run.source, "item_id": item.id, "client_item_id": item.client_item_id, "observed_at": result.observed_at, } for result, item, run in rows ], } )