# 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 不能作为 Cookie,也不能访问网页管理接口。无需添加 `X-WQ-Request`。若请求包含 Origin,须与 PUBLIC_ORIGIN 一致。 先在网页连接并确认 WorldQuant 账户身份。进入侧栏 **系统管理 → MCP Key**,填写名称、有效期并选择权限,即可创建 Key。默认只读、90 天;创建后明文仅显示一次,关闭、刷新或离开页面后无法找回。列表支持分页查看权限、到期时间和状态,并可撤销 Key。页面同时显示连接地址和 MCP 服务是否启用;管理页面不会自动打开服务开关。 管理接口沿用管理员 Cookie 会话和网页请求保护,MCP PAT 不能创建、查看或撤销 Key。明文不写入浏览器存储,也不传给网页研究助手。 也可在所需环境执行管理命令。以下是命令模板,不会由文档自动执行: ```bash # 默认只读,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 | | 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 | | 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 或同步基准列表。 1. 调用 `check_self_correlation`,例如 `{"alpha_ids":["LL977PqL"]}`,获取 `job_id`。目标去重排序,相同目标集合的活动任务会复用;完成后再次调用会重新计算。 2. 用 `get_refresh_job` 查询进度、失败原因与分页错误。受理后客户端断开不影响后台任务;取消及重试可在网页任务面板执行。 3. 完成后调用 `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 中的结果为准。读取工具不自动访问平台。 ## 一轮研究示例 先读取能力和设置快照,发现字段并检查历史。用户授权本批执行后,提交以下形态的固定输入;设置仅为结构示例,实际范围需依据平台选项选择: ```json { "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: ```bash 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): ```json { "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` 数据库: ```bash # 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}`。提交时核对版本、范围和字段,固定独立快照并保存到回测来源;空集合或版本冲突不会创建运行。后续编辑或删除集合不影响回测。无需旧输入草稿接口。