71 lines
2.7 KiB
Python
71 lines
2.7 KiB
Python
"""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
|
|
],
|
|
}
|
|
)
|