23 KiB
MCP 研究接入
MCP 让外部助手直接查询数据元信息、查回测历史、提交固定候选、执行本地自相关检查并读取结果,也能把研究后总结的模板保存到模板工坊。直接回测无需先建立特征、模板、变体或 QuantFlow。执行仍由网页共用的持久队列负责,回测页保存同一个运行。
启用与令牌
应用依赖官方 mcp==2.2.0,锁文件固定版本。增量迁移 0010 增加 mcp_tokens、research_requests 和 mcp_audits,不重写旧业务记录。
默认 MCP_ENABLED=false,入口返回 404。需要启用时,在目标部署配置中显式设置为 true,按该环境既有升级流程迁移并重建后端。生产仍限一个后端 worker。入口为 PUBLIC_ORIGIN/api/v1/mcp/;无尾斜杠地址会重定向。公网须使用 HTTPS,代理保留原始 Host。现有 Caddy 已保持 Host;Vite 开发代理也显式保持。
本功能不实现 OAuth;客户端必须能够自行配置 Authorization: Bearer <PAT>。PAT 不能作为 Cookie,也不能访问网页管理接口。无需添加 X-WQ-Request。若请求包含 Origin,须与 PUBLIC_ORIGIN 一致。
先在网页连接并确认 WorldQuant 账户身份。进入侧栏 系统管理 → MCP Key,填写名称、有效期并选择权限,即可创建 Key。默认只读、90 天;创建后明文仅显示一次,关闭、刷新或离开页面后无法找回。列表支持分页查看权限、到期时间和状态,并可撤销 Key。页面同时显示连接地址和 MCP 服务是否启用;管理页面不会自动打开服务开关。
管理接口沿用管理员 Cookie 会话和网页请求保护,MCP PAT 不能创建、查看或撤销 Key。明文不写入浏览器存储,也不传给网页研究助手。
也可在所需环境执行管理命令。以下是命令模板,不会由文档自动执行:
# 默认只读,90 天;--days 支持 1–365。
python -m app.cli mcp-token-create --name research-reader
# 需要对应业务权限时显式列出;必须包含 research:read。
python -m app.cli mcp-token-create --name research-executor \
--scope research:read --scope research:refresh \
--scope backtests:execute --scope backtests:control
python -m app.cli mcp-token-list
python -m app.cli mcp-token-revoke TOKEN_ID
容器中使用相应 Compose 配置的 exec backend 执行。签发成功后明文只输出一次,保管在客户端凭据存储中,不放入聊天、仓库或请求 ID。数据库只保存令牌哈希及绑定信息。每次 HTTP 请求重新验证过期、撤销、管理员和平台账户绑定。密码重置同时撤销 PAT;仅撤销 PAT 不会取消已受理的回测。
| 权限 | 可调用能力 |
|---|---|
| research:read | 能力、数据目录、元数据、历史、运行、结果、证据、刷新及自相关任务查询、自相关结果读取 |
| research:refresh | 显式更新元数据及 PnL 缓存、发起本地自相关检查、使用已保存凭据重新连接或继续认证;同时要求 read |
| research:write | 保存外部助手总结的研究模板及来源证据;不调用模型、不执行回测;同时要求 read |
| backtests:execute | 直接提交固定候选;同时要求 read |
| backtests:control | 暂停、继续、停止、恢复采集;同时要求 read |
tools/list 只显示当前令牌可用的工具,实际调用仍再次验证权限。缺失/失效令牌返回 401,缺权限返回 403。
工具输入与返回
工具 schema 由 tools/list 提供,未知参数拒绝。所有业务返回为 structuredContent,并保留等价文本 JSON。业务失败置 isError=true,error 包含 code、message、retryable、retry_after、affected_items;HTTP 认证错误不伪装为正常工具结果。
| 工具 | 输入要点 |
|---|---|
| get_worldquant_connection | {job_id?};只读连接状态及认证任务状态,不返回密码、Cookie 或验证链接 |
| authenticate_worldquant | {action?:"connect"或"verify"};默认 connect,使用已保存凭据异步认证,返回 job_id;要求 research:refresh |
| get_research_capabilities | {},含单次候选上限及完整候选 schema |
| create_research_template | {template,hypothesis,source_item_ids,idempotency_key,reference?};保存调用方生成的完整模板,要求 research:write |
| search_data_preparations | {q?,scope_key?,limit?,offset?};查询可编辑集合及当前版本 |
| get_data_preparation | {id,version,q?,limit?,offset?};按版本分页预览字段与数据集归属,版本冲突重新选择 |
| search_catalog | {filters:{region,universe,delay,...},dataset_id?};省略 dataset_id 查数据集,提供则查字段 |
| get_research_metadata | {query:{kind,...}};kind 为 scopes/settings/operators/field_availability/superalpha |
| refresh_research_data | {query:{kind,...}};kind 为 catalog/operators/settings/field_availability/pnl |
| get_submission_check | {alpha_id};读取检查上下文、snapshot 和缓存结果,不发起检查 |
| check_submission | {alpha_id,snapshot,descriptions};写回已确认描述并异步检查,绝不正式提交;要求 research:refresh |
| get_refresh_job | {job_id,limit?,offset?};查询刷新、自相关或平台检查任务,错误列表独立分页 |
| check_self_correlation | {alpha_ids:[...]},1–100 个已导入 Alpha ID;异步返回 job_id |
| get_self_correlation | {alpha_id};只读最新本地结果,含缓存和 stale 状态 |
| search_backtests | 来源、reference、status、带时区起止时间、scope、q、候选精确匹配及分页 |
| submit_backtests | {name,candidates,idempotency_key,preparation_refs?,duplicate_policy?,source?} |
| get_backtest | {run_id,after?,event_limit?},after 为事件游标 |
| get_backtest_results | {run_id,item_ids?,limit?,offset?} |
| get_backtest_artifact | {item_id,kind,limit?,offset?,date_from?,date_to?},kind 为 snapshot/pnl/components |
| control_backtest | {run_id,action,expected_version,idempotency_key} |
metadata 的 operators 支持 q/category 和分页;settings 支持分页;field_availability 要求 field_id 和 scope。refresh 的 catalog 要求 scope,可选 dataset_id;pnl 要求 alpha_ids;availability 与读取使用相同范围字段。目录和 PnL 刷新返回 job_id,查询不会隐式刷新;另外三种刷新最多等待 30 秒,成功只返回快照引用,完整内容用读取工具获取。失败不发布半成品。
列表默认 25 项、最多 100 项;返回 total、offset、limit、has_more。快照证据按顶层 key/value 分页,嵌套内容完整保留;PnL 按日期过滤和分页。元数据仅提供字段与算子资料,不提供原始财务时间序列。
连接恢复
遇到未连接时,调用 authenticate_worldquant,再以返回的 job_id 调用 get_worldquant_connection。受理不等于已连接;等待任务完成及连接状态为 connected。正在排队或执行的认证任务会复用。人工验证期间 connect 不替换验证会话:用户在返回的系统网页入口完成验证后调用 {"action":"verify"}。验证失败或缺少凭据时在网页账户面板处理。该入口不修改凭据,不签发或刷新 MCP PAT;PAT 失效仍需在管理页面处理。已有 research:refresh Key 可用,只读 Key 只能查询。
本地自相关检查
目标 Alpha 必须已导入;比较基准为本地已同步的同地区已提交 Alpha,排除自身。建议先在网页全量同步已提交 Alpha。MCP 不隐式导入 Alpha 或同步基准列表。
- 调用
check_self_correlation,例如{"alpha_ids":["LL977PqL"]},获取job_id。目标去重排序,相同目标集合的活动任务会复用;完成后再次调用会重新计算。 - 用
get_refresh_job查询进度、失败原因与分页错误。受理后客户端断开不影响后台任务;取消及重试可在网页任务面板执行。 - 完成后调用
get_self_correlation,例如{"alpha_id":"LL977PqL"}。返回source=local,status为not_cached、available或stale;没有结果时cached=false、result=null,读取不会隐式启动检查。
检查优先使用已有 PnL 缓存,缺失时由后台自动补取并落库,等待遵循平台 Retry-After。计算使用累计 PnL 的日变化、目标最新数据日前四年的共同窗口、至少 30 个有效样本,取带符号最大的 Pearson 相关系数,阈值为 0.7。结果包含比较数、跳过数、最相关 Alpha、窗口、样本和缓存时间;最多列出前 10 个匹配及前 100 个跳过原因。缺少基准或有效样本会明确报告数据不足,不能当作通过。
result.stale=true 表示缓存已待重算;新任务执行期间读取仍可能返回上一次结果,应同时查看任务状态和 calculated_at。这是本地研究规则,不调用 WorldQuant 的提交检查,也不代表平台提交资格。get_research_capabilities.self_correlation 提供工具名及当前规则。已有包含 research:refresh 的 Key 可直接发起检查;只读 Key 只能查看结果。
平台检查(不提交)
先调用 get_submission_check({alpha_id}) 读取缓存表达式、Description 和 snapshot。核对三段描述后,调用 check_submission({alpha_id, snapshot, descriptions});descriptions 为 regular 或 selection/combo 到完整文本的映射。此操作要求 research:refresh,仅写回描述并调用平台 GET /alphas/{id}/check。复用网页的本地自相关门槛、快照冲突保护和任务去重。不会调用 /submit,检查通过也不会正式提交 Alpha。
使用返回的 job_id 调用 get_refresh_job 查看执行状态,再通过 get_submission_check 读取缓存结果和 checked_at。任务 completed 表示检查执行完毕,不代表所有检查通过;以 checks 中的结果为准。读取工具不自动访问平台。
一轮研究示例
先读取能力和设置快照,发现字段并检查历史。用户授权本批执行后,提交以下形态的固定输入;设置仅为结构示例,实际范围需依据平台选项选择:
{
"name": "价格排序基线",
"idempotency_key": "research-round-001",
"duplicate_policy": "reject",
"source": {"reference": "conversation-reference", "hypothesis": "基线对照"},
"candidates": [{
"client_item_id": "baseline",
"expression": "rank(close)",
"alpha_type": "REGULAR",
"settings": {
"instrumentType": "EQUITY", "region": "USA", "universe": "TOP3000", "delay": 1,
"decay": 0, "neutralization": "INDUSTRY", "truncation": 0.08,
"pasteurization": "ON", "unitHandling": "VERIFY", "nanHandling": "OFF",
"language": "FASTEXPR", "visualization": false, "maxTrade": "OFF", "maxPosition": "OFF"
}
}]
}
单次 1–100 项,client_item_id 唯一,每项设置完整,不做批次级参数合并。输入结构及已缓存设置组合参与校验;缺设置缓存时返回 validation=unknown,不宣称验证了平台组合、表达式语义或字段可用性。可先显式刷新设置消除这类未知。
提交自动保存固定预览和运行,返回 backtest_run_id、输入摘要、数量和 web_url。打开 web_url 可定位网页详情;来源显示“MCP 研究”,可以按来源筛选。后续 get_backtest 和 get_backtest_results 读取进度与结果。PnL 未缓存时返回 not_cached,通过显式刷新再读取。下一批可设置 source.parent_run_id 关联前一批。
研究后保存模板
外部大模型先用 get_backtest_results 阅读具体候选的指标及检查,判断哪些研究结果值得继续探索,再自行总结经济假设和参数化表达式,调用 create_research_template。此工具接收模型已经思考完成的模板,不调用服务端模型。保存成功后,模板出现在网页 模板工坊,用户可选择固定输入和模拟设置,全量展开或随机采样候选,再确认批量回测。
该工具使用新的 research:write 权限。已有 Key 不自动获得写入权限;在 系统管理 → MCP Key 创建 Key 时勾选“保存研究模板”,或签发专用 Key:
python -m app.cli mcp-token-create --name research-template-writer \
--scope research:read --scope research:write
调用结构示例(候选 ID 需替换为实际读取的 items[].id,不是 client_item_id 或 Alpha ID):
{
"template": {
"name": "短期反转窗口研究",
"description": "将已研究的反转表达式提炼为字段和窗口候选;替换字段及其他窗口仍需逐项验证。",
"expression": "-rank(ts_delta({price}, {window}))",
"variables": {
"price": {"kind": "field", "field_type": "MATRIX", "values": ["close", "vwap"]},
"window": {"kind": "integer", "values": [3, 5, 10, 20]}
},
"scope": {"instrument_type": "EQUITY", "region": "USA", "universe": "TOP3000", "delay": 1}
},
"hypothesis": "来源结果支持继续比较短期价格反转;通过改变价格字段和观察窗口检验稳定性。",
"source_item_ids": ["REPLACE_WITH_BACKTEST_ITEM_ID"],
"reference": "research-round-001",
"idempotency_key": "reversal-template-001"
}
template 复用模板工坊的结构:表达式使用 {name} 占位符,variables 必须逐一对应;变量 kind 支持 field/operator/integer/number/group/string/fragment,字段变量必须声明 MATRIX/VECTOR/GROUP 类型,VECTOR 的聚合方式应明确写在表达式中。工具只创建 category=template 的完整模板。scope 可省略;指定后,后续展开须使用相同范围。字段候选应先通过目录核对,工具保存时不隐式刷新元数据。
source_item_ids 为 1–20 个不重复的本地回测候选,可来自多个运行;须完成平台回测、结果采集及持久化,且存在完整结果快照。服务端读取并固定其表达式、模拟设置、运行 ID、Alpha ID、观测时间、指标和检查摘要,不接收调用方自报的成绩。研究假设和外部 reference 是调用方提供的说明。来源存在不代表经济假设成立,completed 不代表所有检查 PASS,也不代表模板的其他参数组合已回测。
返回 template_id(同 id)、version、完整模板 content、来源 provenance、字符串形式的理论 combination_count 和模板工坊 web_url。示例有 8 种参数组合;组合数不去重,也不代表已创建实验或运行。结构校验与来源记录状态在 validation 中说明,展开候选和平台语义仍未验证。网页展开继续执行原有字段归属、类型、设置和语法校验;全量展开超过 10000 项时需缩小候选或使用随机采样。
相同账户、操作和幂等键的相同请求重放首次成功响应,跨 Key 重试和服务重启仍有效;同键不同内容返回 IDEMPOTENCY_CONFLICT。模板同名(含已删除模板)返回 TEMPLATE_NAME_CONFLICT,不覆盖任何已有版本。来源缺失返回 NOT_FOUND,未完成返回 SOURCE_NOT_READY,结构错误返回 INVALID_INPUT。失败不创建资产、不占用幂等键。创建只写入现有资产、版本、请求和审计表,无新增迁移,不启动模型、实验、队列或定时研究。
幂等、重复与状态
- 成功请求按账户、操作和幂等键固定首次响应。相同请求重试返回原响应,不重新提交;换令牌不改变该范围。同键不同内容返回 IDEMPOTENCY_CONFLICT。失败校验不占用幂等键。
- 幂等判断先于重复查询。规范化摘要包含完整候选、顺序、设置、名称、来源说明和重复策略,不包含链路请求 ID 和令牌。
- 重复按平台完整输入指纹匹配,不判断数学等价。默认 reject 整批拒绝;返回历史记录及其状态,历史失败或跳过也属于输入匹配。明确 rerun 才创建新运行,不自动复用或跳过。匹配记录超页时通过 search_backtests 继续读取。
- 返回 ID 表示已持久化受理,不表示平台完成。执行继续使用原来的限流、退避与重启恢复;客户端断开不取消已受理运行。
- 保留平台、采集和持久化三层状态。completed 不等于检查 PASS。检查包含原始非通过内容及未知状态;指标缺失为 null。
- 历史指标来自当次固定快照,后续 Alpha 同步不改写。PnL 是另行采集的缓存,必须同时阅读 fetched_at,不能视为当次回测同时抓取。
- pause 阻止后续新提交;stop 跳过未提交项,已提交模拟继续采集,不能远程取消。停止后的剩余项须明确新建重跑。
- recover 只根据已有回执恢复采集,不重发未知模拟。无回执的 submission_unknown 仍需在网页核对原模拟。
- resume 保留解除账户级无期限调度阻塞的行为,响应 impact 明确是否解除;不会清除 Retry-After 截止时间。
- 控制使用 expected_version 和独立幂等键;成功重试不重复追加控制事件。统计区分候选、尝试、POST 请求和确认/未知接受数量,实际平台额度消耗保持未知。
审计只记录身份 ID、输入摘要、哈希后的链路请求标识、业务引用、结果码和耗时。关闭 MCP 开关不删除记录,也不停止已受理队列。备份需包含新增表;恢复后旧成功幂等键仍有效。
验证和限制
本地测试只使用模拟平台。后端 uv run pytest -q tests/test_mcp.py tests/test_mcp_templates.py 包含官方 MCP ClientSession 的 HTTP 工具往返,并验证模板写入权限、幂等重放、固定来源和网页模板展开;浏览器测试在临时数据库里验证实际 MCP 提交的运行链接和来源。
独立 PostgreSQL 检查只接受本机 wq_mcp_test 数据库:
# backend 目录;变量必须指向专用、可丢弃的测试库。
MCP_TEST_DATABASE_URL=postgresql+asyncpg://USER:PASSWORD@127.0.0.1:PORT/wq_mcp_test \
uv run python -m tests.mcp_postgres
该脚本执行迁移、并发提交/控制、重启重放、回退及重升级,不用于个人库或生产库。生产启用、真实平台兼容性、真实额度和客户端实际凭据配置仍需另行授权验证。本功能不会恢复任何定时研究。
使用数据准备集合时,preparation_refs 为最多 20 个 {id,version}。提交时核对版本、范围和字段,固定独立快照并保存到回测来源;空集合或版本冲突不会创建运行。后续编辑或删除集合不影响回测。无需旧输入草稿接口。
Super Alpha 研究
迁移 0021 增加回测项类型、Selection/Combo 和独立组件快照表。历史 REGULAR 回测保留原输入及结果;已有 SUPER 记录直接进入 研究成果 → Super Alpha 管理。原 Alpha 管理固定为非 SUPER,两个页面的列表、统计、筛选、导出、保存视图和列偏好区分类型,共用同一 Alpha 记录。
研究实验 → Super Alpha 研究 支持方案版本、复制/归档、参数候选、设置候选、等权基线、Selection 预览和固定候选。候选勾选后进入通用回测预览及启动窗口。参数使用 {name};变量不接受普通 Alpha 的 field 类型。全量展开默认上限 100,可调至 10000;随机模式使用固定种子。基线另列候选,包含基线后最多 10000 项。保存与构造不调用模型或启动回测。
| 工具 | 权限与输入 |
|---|---|
search_superalpha_plans |
read;q/limit/offset |
get_superalpha_plan |
read;plan_id/version 或 experiment_id/limit/offset |
save_superalpha_plan |
write;plan/idempotency_key;更新同时提供 plan_id/version |
preview_superalpha_selection |
refresh;展开后的 selection、完整 SUPER settings,可选 plan_id/version;立即返回 job_id |
get_superalpha_selection |
read;snapshot_id 或 job_id,支持 q/limit/offset |
build_superalpha_candidates |
write;plan_id/version 或内联 plan;mode、limit、seed、selection_snapshot_ids、idempotency_key |
search_superalphas |
read;filters,服务端固定 SUPER 范围 |
get_superalpha |
read;alpha_id,读取设置、指标、两个 Description、实际组件及研究来源 |
权限名称分别为 research:read、research:write、research:refresh。get_research_metadata({query:{kind:"superalpha"}}) 返回专属设置契约、文档属性和阶段信息;settings 查询可带 alpha_type,operators 可带 stage=SELECTION/COMBO。账户未返回的设置范围保持未知,文档属性不等于实时账户授权。
外部模型自行构造方案后调用 save/build。构造返回 candidates 和 submit_source,分别传入 submit_backtests.candidates 和 .source;超过 100 项按构造记录分页读取,再按批次提交。每批使用独立幂等键,保留同一个 research_id。也可直接提交完整 SUPER 候选,不先保存方案:
{
"name": "SUPER 等权对照",
"idempotency_key": "super-round-001",
"candidates": [{
"client_item_id": "equal-weight",
"alpha_type": "SUPER",
"selection": "turnover < 0.2",
"combo": "1",
"settings": {
"instrumentType": "EQUITY", "region": "USA", "universe": "TOP3000", "delay": 1,
"decay": 0, "neutralization": "INDUSTRY", "truncation": 0.08,
"pasteurization": "ON", "unitHandling": "VERIFY", "nanHandling": "OFF",
"language": "FASTEXPR", "visualization": false, "maxTrade": "OFF", "maxPosition": "OFF",
"selectionHandling": "POSITIVE", "selectionLimit": 100, "componentActivation": "IS"
}
}]
}
这只是输入形态示例,地区和参数须依据实际元数据选择。回测沿用 submit_backtests、get_backtest、get_backtest_results、get_backtest_artifact 和 control_backtest。SUPER 每项单独 POST,共用账户并发及恢复机制;REGULAR 继续批量方式。提交回执未知时不会自动重发。search_backtests 可按 alpha_type、research_id 筛选;精确匹配覆盖类型、Selection、Combo 和完整设置。
Selection 预览在通用后台任务中执行,进度用 get_refresh_job 查询,组件用 get_superalpha_selection 分页读取。预览请求遵循现有平台资料的六个查询字段:selection/instrumentType/region/delay/selectionLimit/selectionHandling。Universe 和 Combo 不会被假装应用到该只读查询;完整设置仍保存在观察记录中。
预览和实际组件分开存储。get_backtest_artifact(kind="components") 仅返回当次实际组件证据;平台未提供完整成员列表时,complete=false、component_hash=null。未核实列表不能作为同池证据,即使预览曾返回完整组件。请求指纹覆盖实际模拟的完整输入,组件指纹覆盖排序后的完整 ID 集合;时间和来源单独记录。指标取当次固定快照,PnL 为单独采集的缓存,以 fetched_at 为准。
验证入口:uv run pytest -q tests/test_superalpha.py、前端 pnpm exec playwright test tests/superalpha.spec.ts。专用本地 PostgreSQL 可执行 SUPER_TEST_DATABASE_URL=.../wq_superalpha_test uv run python -m tests.superalpha_postgres,验证迁移、历史记录兼容、完整闭环及并发保存幂等。脚本拒绝其他数据库名称或非本机地址。所有自动化验收均使用模拟平台;真实 SUPER 模拟须另行授权。