Files
worldquant-alpha-system/backend/app/backtests/ai_tools.py
T
yuxuanhui 69c19ed25f
Deploy production / deploy (push) Successful in 51s
Refactor project components and workflows
2026-09-20 11:20:51 +08:00

204 lines
7.3 KiB
Python

"""Domain-owned AI capabilities; caller owns authorization and transactions."""
from typing import Literal
from fastapi import HTTPException
from pydantic import Field
from ..ai.capabilities import Capability, EmptyArgs
from ..schemas import Contract
from .contracts import ControlInput, PreviewInput, RerunInput, StartInput
class BacktestRunArgs(Contract):
run_id: str = Field(min_length=1, max_length=36)
class BacktestListArgs(Contract):
limit: int = Field(default=20, ge=1, le=100)
offset: int = Field(default=0, ge=0)
source: str | None = Field(default=None, max_length=100)
reference: str | None = Field(default=None, max_length=200)
research_id: str | None = Field(default=None, max_length=200)
class BacktestDraftArgs(Contract):
draft_id: str = Field(min_length=1, max_length=36)
limit: int = Field(default=25, ge=1, le=100)
offset: int = Field(default=0, ge=0)
class BacktestResultsArgs(BacktestRunArgs):
limit: int = Field(default=20, ge=1, le=100)
offset: int = Field(default=0, ge=0)
class BacktestPreviewArgs(Contract):
preview_id: str = Field(min_length=1, max_length=36)
limit: int = Field(default=20, ge=1, le=100)
offset: int = Field(default=0, ge=0)
class BacktestControlArgs(BacktestRunArgs):
action: Literal["pause", "resume", "stop", "recover"]
class BacktestRerunArgs(BacktestRunArgs):
item_ids: list[str] = Field(min_length=1, max_length=100)
async def draft(ctx, args):
data = await ctx.business.backtests.draft(args.draft_id)
candidates = data.pop("candidates")
return {
**data,
"items": candidates[args.offset : args.offset + args.limit],
"total": len(candidates),
"limit": args.limit,
"offset": args.offset,
"has_more": args.offset + args.limit < len(candidates),
}
async def results(ctx, args):
data = await ctx.business.backtests.results(**args.model_dump())
# Keep the established card shape; complete raw snapshots remain available by business reference.
for item in data["items"]:
if item["result"]:
snapshot = item["result"].pop("snapshot")
item["result"].update({k: snapshot.get(k) for k in ("is", "os", "checks", "dateCreated")})
return data
async def preview_start(ctx, args):
return {"backtest": await ctx.business.backtests.get_preview(args.preview_id)}
async def start(ctx, args, preview):
current = await ctx.business.backtests.get_preview(args.preview_id)
if current["digest"] != preview["backtest"]["digest"] or current["version"] != args.version:
raise HTTPException(409, "回测预览不匹配,请重新确认")
return await ctx.business.backtests.start(args)
async def preview_control(ctx, args):
return {"backtest_run": await ctx.business.backtests.run(args.run_id), "action": args.action}
async def control(ctx, args, preview):
return await ctx.business.backtests.control(
args.run_id, ControlInput(action=args.action, version=preview["backtest_run"]["version"])
)
async def wake_backtests(runner, result):
"""Only committed, confirmed runs can wake the existing backtest lane."""
runner.backtests.wake.set()
INSTRUCTIONS = "模板集合使用 start_template_backtest 直接请求确认;其他回测先读取能力再准备固定候选预览。每次运行确认一次;后续候选新建预览。停止生成不取消回测。\n回测结果追问用 get_backtest/get_backtest_results。上下文或历史没有运行 ID 时,可用 list_backtests 按 source=chatbox 和会话 reference 找回;不得把启动返回当作结果。"
CAPABILITIES = (
Capability(
name="get_backtest_draft",
schema=BacktestDraftArgs,
description="分页读取已有候选草稿、版本和来源,随后按 draft_id/draft_version 准备预览。",
label="读取候选草稿",
renderer="catalog",
effect="query",
handler=draft,
),
Capability(
name="get_backtest_capabilities",
schema=EmptyArgs,
description="读取回测输入 schema、支持类型和本地调度配置,不代表平台剩余额度。",
label="读取回测能力",
renderer="backtest",
effect="query",
handler=lambda ctx, args: ctx.business.backtests.capabilities(),
),
Capability(
name="prepare_backtest",
schema=PreviewInput,
description="准备服务端固定回测预览,可用 inline 候选或草稿引用;只保存预览,不提交平台,不需要执行确认。",
label="准备回测预览",
renderer="backtest",
effect="prepare",
handler=lambda ctx, args: ctx.business.backtests.preview(args),
refresh=("backtests",),
),
Capability(
name="get_backtest_preview",
schema=BacktestPreviewArgs,
description="分页读取完整固定预览,确认前核对表达式和最终参数。",
label="查看回测预览",
renderer="backtest",
effect="query",
handler=lambda ctx, args: ctx.business.backtests.get_preview(**args.model_dump()),
),
Capability(
name="start_backtest",
schema=StartInput,
description="对已保存预览请求一次用户确认,确认后后台运行全部固定候选,立即返回运行 ID;禁止循环等待。",
label="启动固定回测",
renderer="backtest",
effect="confirm",
preview=preview_start,
execute=start,
after_commit=wake_backtests,
refresh=("backtests",),
),
Capability(
name="list_backtests",
schema=BacktestListArgs,
description="分页查询回测运行与统计,可按来源筛选。",
label="查询回测运行",
renderer="backtest",
effect="query",
handler=lambda ctx, args: ctx.business.backtests.runs(**args.model_dump()),
),
Capability(
name="get_backtest",
schema=BacktestRunArgs,
description="查询指定运行的真实进度,不循环等待完成。",
label="查看回测进度",
renderer="backtest",
effect="query",
handler=lambda ctx, args: ctx.business.backtests.run(args.run_id),
),
Capability(
name="get_backtest_results",
schema=BacktestResultsArgs,
description="分页读取逐项状态、历史指标和错误;未知结果不能推测为成功。",
label="读取回测结果",
renderer="backtest",
effect="query",
handler=results,
),
Capability(
name="control_backtest",
schema=BacktestControlArgs,
description="预览并确认暂停/继续/停止剩余项/找回原任务;不远端取消,不重新提交。",
label="控制回测运行",
renderer="backtest",
effect="confirm",
preview=preview_control,
execute=control,
after_commit=wake_backtests,
refresh=("backtests",),
),
Capability(
name="prepare_backtest_rerun",
schema=BacktestRerunArgs,
description="从明确指定的已结束回测项准备新预览,保留来源;不会自动启动。",
label="准备重跑预览",
renderer="backtest",
effect="prepare",
handler=lambda ctx, args: ctx.business.backtests.rerun(
args.run_id, RerunInput(item_ids=args.item_ids)
),
refresh=("backtests",),
),
)