Files
worldquant-alpha-system/backend/app/research/comparisons.py
T

53 lines
1.9 KiB
Python

"""Read-only baseline comparison over explicit local Alpha and PnL snapshots."""
import math
from fastapi import HTTPException
from ..models import Alpha, Pnl
from .serialization import encode_snapshot as jsonable_encoder
async def compare(db, alpha_ids):
if len(set(alpha_ids)) != len(alpha_ids):
raise HTTPException(422, "比较项不能重复")
rows, by_id = [], {}
for alpha_id in alpha_ids:
alpha = await db.get(Alpha, alpha_id)
if not alpha:
raise HTTPException(404, f"Alpha {alpha_id} 尚未同步")
pnl = await db.get(Pnl, alpha_id)
by_id[alpha_id] = (
{
p["date"][:10]: p["value"]
for p in pnl.points
if type(p.get("value")) in (int, float) and math.isfinite(p["value"])
}
if pnl
else {}
)
rows.append(
{
"alpha_id": alpha.id,
"expression": alpha.expression,
"settings": alpha.settings,
"metrics": alpha.is_metrics,
"observed_at": alpha.synced_at,
"pnl_fetched_at": pnl.fetched_at if pnl else None,
}
)
common = sorted(set.intersection(*(set(points) for points in by_id.values())))
for row in rows:
points = by_id[row["alpha_id"]]
row["pnl"] = [{"date": date, "value": points[date] - points[common[0]]} for date in common]
return jsonable_encoder(
{
"baseline_alpha_id": alpha_ids[0],
"items": rows,
"common_dates": common,
"window": {"from": common[0], "to": common[-1]} if common else None,
"different_settings": any(row["settings"] != rows[0]["settings"] for row in rows[1:]),
"note": "PnL 按共同日期展示并从窗口起点归零;缓存缺失时请在 Alpha 详情获取 PnL",
}
)