feat: add leader research agent

This commit is contained in:
codyhhchen
2026-05-05 18:17:51 +08:00
committed by codychen123
parent 82ecf31867
commit a3f74b8567
64 changed files with 8023 additions and 12 deletions
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-05-04
@@ -0,0 +1,282 @@
## Context
PolyHermes 当前已有三层跟单工作流:
- `Leader 管理` 保存可被跟单的钱包地址,对应 `copy_trading_leaders`
- `Leader 池` 管理人工候选、观察、小额试跟、冷却、淘汰,对应 `copy_trading_leader_pool`
- `跟单配置` 管理真实账户、leader 和风控参数,对应 `copy_trading`
这个结构已经把“地址库”和“真钱跟单”拆开,但仍需要用户自己去找 leader、判断是否值得观察、跟踪表现、决定是否小额试跟。用户的目标不是手动维护池子,而是让系统自动发现优秀且可复制的 leader,先纸跟验证,再由用户授权真钱动作。
本设计把新增能力定位为 Leader Research Agent。它是研究代理,不是自动交易代理。它可以自动发现、自动纸跟、自动建议、自动冷却;它不能自动启用真钱跟单。
## Goals / Non-Goals
**Goals:**
- 提供多源候选发现,第一阶段支持 watchlist、已有 Leader 管理记录和 activity-derived candidates。
- 为研究运行、候选、分数、事件、纸跟 session、纸跟交易、纸跟持仓建立独立数据模型。
- 持久化原始或规范化 activity event,使纸跟可以基于可重放事件运行,而不是复用真实订单表。
- 用透明规则计算 copyability score,并保存分项分数和 score version。
- 自动推进研究状态,但研究状态必须与 Leader 池状态分离。
- 在不锁定的情况下,自动化只能保守地增强 Leader 池记录,不能自动改成真实 `TRIAL``ACTIVE`
- 提供操作台展示运行状态、候选详情、纸跟表现、待处理决策和源健康。
- 提供手动审批动作,创建保守的 `enabled=false` 真实跟单配置。
- 为源失败、quote 不可用、valuation unknown、重复事件、锁定候选、非法真钱启用等关键路径提供可见错误和测试。
**Non-Goals:**
- 不自动启用真钱跟单。
- 不自动加仓、不自动增加 fixed amount、不自动放宽风控参数。
- 不做黑盒 AI/ML 评分。
- 不在第一阶段依赖公开 leaderboard 端点。
- 不复用 `copy_order_tracking` 作为纸跟账本。
- 不把 unknown valuation 当成 confirmed zero。
- 不替换现有 Leader 池、Leader 管理、跟单配置和真实 PnL 统计。
## Decisions
### 1. 研究状态独立于 Leader 池状态
研究状态回答“系统证明了什么”,Leader 池状态回答“用户当前怎么处理这个 leader”。两者不能合并。
研究状态:
```text
DISCOVERED -> CANDIDATE -> PAPER -> TRIAL_READY
\-> COOLDOWN -> RETIRED
```
Leader 池状态:
```text
CANDIDATE -> WATCH -> TRIAL -> ACTIVE
\-> COOLDOWN -> RETIRED
```
映射规则:
```text
research DISCOVERED
-> 可以只存在于 leader_research_candidate,不强制创建 Leader 池项
research CANDIDATE
-> 展示给用户时可创建或更新 Leader 池 CANDIDATE
research PAPER
-> Leader 池保持 WATCH,或后续 UI 暴露 PAPER
research TRIAL_READY
-> Leader 池保持 WATCH,并展示“建议小额试跟” badge
-> 不能自动变成 Leader 池 TRIAL
research COOLDOWN
-> 未锁定时可以同步 Leader 池 COOLDOWN
research RETIRED
-> 只有 agent 拥有且未锁定的候选,才可同步 Leader 池 RETIRED
```
`TRIAL_READY` 只是建议。只有用户创建了真实禁用试跟配置后,Leader 池才可以进入 `TRIAL`
### 2. 纸跟必须独立建账
`enabled=false` 的真实跟单配置不会产生模拟成交、模拟持仓、过滤原因、quote confidence 和纸跟 PnL。把它伪装成纸跟会让后续晋升建议没有证据链。
新增建议表:
```text
leader_paper_session
leader_paper_trade
leader_paper_position
```
每笔纸跟交易至少记录:
```text
leader_trade_id
leader_price
leader_size
simulated_price
simulated_size
simulated_amount
fill_assumption: LEADER_PRICE | BEST_ASK_AT_EVENT | MID_PRICE | UNKNOWN
quote_confidence: HIGH | MEDIUM | LOW | UNKNOWN
quote_source
quote_timestamp
filter_result: PASSED | FILTERED
filter_reason
valuation_status: AVAILABLE | NO_MATCH | UNAVAILABLE | CONFIRMED_ZERO
```
`UNAVAILABLE``UNKNOWN` 不得计为真实归零。这个约束必须沿用到纸跟 PnL 和前端展示。
### 3. 第一阶段先做可重放事件和内部来源
第一阶段候选来源:
```text
watchlist
已有 Leader 管理记录
已持久化 activity event 中可归因的钱包
```
公开 leaderboard 保留为后续来源,不作为第一阶段 apply-ready 的依赖。原因是 leaderboard 端点可能不稳定或未文档化,不能让第一版核心闭环被外部端点卡住。
如果当前 app 没有持久化 raw activity event,需要新增 append-only 表:
```text
leader_activity_event
```
推荐唯一键:
```text
source + event_id
```
如果 event_id 缺失,必须生成稳定 fallback key,避免重启或重放时重复纸跟。
### 4. 评分必须可解释且可版本化
不要只保存一个总分。保存分项分数、总分、score version 和 reason。
分项:
```text
profit_signal
repeatability
liquidity_fit
entry_price_fit
slippage_risk
holding_period_fit
market_type_risk
drawdown_risk
exit_liquidity_risk
data_freshness
filter_pass_rate
```
研究状态迁移必须记录 rule version,避免以后调整阈值后无法解释旧决策。
默认阈值:
```text
DISCOVERED -> CANDIDATE
条件:候选来自可信来源,地址格式有效
CANDIDATE -> PAPER
条件:score >= 60,未锁定,未退休,source 48 小时内新鲜
PAPER -> TRIAL_READY
条件:
纸跟时间 >= 7 天
模拟交易数 >= 10
copyable PnL > 0
最大回撤 >= -15%
UNKNOWN quote 暴露占比 <= 20%
filtered-trade ratio < 50%
无 critical risk flag
PAPER -> COOLDOWN
条件:
最大回撤 < -20%
或 10 笔后 copyable PnL < -5
或 quote/source stale > 72 小时
或存在 thin_liquidity_exit_risk
COOLDOWN -> CANDIDATE
条件:cooldown_until 已过,source 重新新鲜,未锁定
COOLDOWN -> RETIRED
条件:3 次冷却周期,或 30 天无新鲜来源
```
这些阈值是第一版保守默认值,不是金融真理;后续可配置。
### 5. 后端强制真钱边界
研究代理可以推荐小额试跟,但不能启用真钱。
手动审批流:
```text
用户打开候选详情
-> 点击“创建禁用试跟配置”
-> 前端展示 fixed amount、max daily loss、max daily orders、price range、max position value
-> 用户确认
-> 后端组装 CopyTradingCreateRequest
-> 后端强制 enabled=false
-> 返回 created copyTrading
-> 用户必须去 跟单配置 页面手动启用
```
后端必须忽略或拒绝任何客户端传入的 `enabled=true`、更大 fixed amount 或更松风控参数。安全边界在后端,不在弹窗。
### 6. 操作台先做“晨报式”决策界面
第一版页面不做复杂量化终端。优先回答:
```text
今天有什么变化?
哪些候选需要我决策?
为什么推荐?
证据是什么?
风险是什么?
我能安全点击什么?
```
页面模块:
```text
Run Status
Candidate Detail
Paper Sessions
Pending Decisions
Source Health
```
### 7. 调度必须有 overlap guard 和 run record
新增 `LeaderResearchJobService` 使用现有 Spring `@Scheduled` 模式,但默认关闭或可配置开启。
每次运行必须创建 `leader_research_run`,记录:
```text
status
started_at
finished_at
duration
source counts
candidate counts
error class
error message
partial failure flag
```
如果上一次运行还未结束,新运行 MUST 跳过并记录 overlap 事件,不能并发推进状态。
## Risks / Trade-offs
- [纸跟精度看起来比实际更高] -> 每笔纸跟交易必须保存 fill assumption、quote confidence 和 valuation statusUI 必须展示 unknown/partial 状态。
- [source 失败导致错误降级候选] -> source failure 只更新 source health,不得静默删除候选或降低 score。
- [自动状态机误伤人工决策] -> `locked=true` 的候选和池子项不得被自动化改变状态、建议配置或 trial recommendation。
- [通知系统 Telegram-first] -> 第一阶段先落 `leader_research_event` 和控制台提示,外部通知渠道在事件流稳定后再复用/扩展通知配置。
- [表数量增加导致复杂度上升] -> 用里程碑拆分:先数据 spine,再纸跟,再评分状态机,再 UI/通知,再手动审批。
- [数据量增长影响性能] -> 为 event、candidate、paper trade、paper position、run、event 表添加唯一键和查询索引,并对详情列表分页。
## Migration Plan
1. 新增研究与纸跟相关 Flyway 迁移,所有表为空表创建,不修改现有真实订单热表。
2. 部署后端,但 research job 默认关闭。
3. 部署前端操作台,若 job 未启用则展示禁用/空态。
4. 在本地或测试环境手动运行 research job,验证 run record、source health、candidate、paper session 和 paper trade。
5. 开启 watchlist + existing leaders 来源,不启用 public leaderboard。
6. 确认内部 research events 稳定后再接外部通知渠道。
7. 最后启用手动“创建禁用试跟配置”入口。
Rollback:
- 如果 job 出错,关闭 research job 配置并保留数据表。
- 如果 UI 出错,隐藏菜单或回滚前端路由。
- 如果迁移失败,在启用 job 前停止部署;不要在热回滚中删除已有研究数据。
@@ -0,0 +1,37 @@
## Why
当前 `Leader 池` 已经解决了“候选、观察、小额试跟、冷却、淘汰”的人工决策层,但用户的真实目标更靠前:系统自动发现优秀且可复制的 leader,先用纸跟证据验证,再让用户决定是否创建真实小额跟单配置。
如果只搬运排行榜,系统会把“leader 自己赚钱”误当成“用户也能跟着赚钱”。本变更要把 PolyHermes 从手动候选池升级为 paper-first 的 Leader Research Agent:自动发现、自动纸跟、自动解释、自动建议,但真钱启用必须手动确认。
## What Changes
- 新增 Leader Research Agent 能力,提供多源候选发现、研究运行记录、源健康状态和候选评分。
- 新增独立纸跟账本,记录模拟买卖、模拟持仓、过滤原因、估值状态、quote confidence 和纸跟 PnL。
- 新增研究状态机,支持 `DISCOVERED``CANDIDATE``PAPER``TRIAL_READY``COOLDOWN``RETIRED` 等研究状态,并与现有 Leader 池状态分离。
- 新增可解释 copyability score,按收益信号、可重复性、流动性适配、入场价格适配、滑点风险、持仓周期、市场类型风险、回撤风险、退出流动性风险、数据新鲜度和过滤通过率拆分。
- 新增 Leader Research 操作台,展示运行状态、候选详情、纸跟证据、待处理决策和数据源健康。
- 新增研究事件与通知摘要,让系统主动提示新增候选、纸跟达标、建议小额试跟、冷却、数据失败和数据过期。
- 新增手动审批路径:用户从研究候选详情确认后,系统只能创建 `enabled=false` 的保守真实跟单配置;研究代理不得自动启用真钱跟单。
- 第一阶段候选来源仅包含 watchlist、已有 `Leader 管理` 记录和已持久化 activity 事件;公开 leaderboard 接入保留为后续能力,等端点契约和失败行为确认后再启用。
- 不做自动真钱启用、不做自动加仓、不做自动放大固定金额、不做黑盒 AI 评分、不删除用户已有 leader 或跟单配置。
## Capabilities
### New Capabilities
- `leader-research-agent`: 自动研究 leader 的完整能力,包括候选发现、纸跟账本、copyability 评分、研究状态机、操作台、通知事件和手动创建禁用试跟配置。
### Modified Capabilities
无。
## Impact
- 后端新增 research 相关实体、Flyway 迁移、Repository、DTO、Service、Controller、定时任务和错误码。
- 后端需要复用现有 `copy_trading_leaders``copy_trading_leader_pool``copy_trading``CopyTradingService``LeaderPoolService` 和 activity 监听链路。
- 后端需要新增 append-only activity event 持久化能力,如果当前实时 activity 事件没有落库,则纸跟必须先基于该事件表运行。
- 前端新增 Leader Research 操作台页面、路由、菜单入口、API service、类型定义和中文/英文/繁中文文案。
- 前端 Leader 池需要能展示研究推荐 badge 或跳转研究候选详情,但不得把 `TRIAL_READY` 直接表现成真实 `TRIAL`
- 通知系统先复用内部 `leader_research_event` 和控制台提示;外部通知渠道在事件流稳定后再接入现有通知配置抽象。
- 交易安全影响:本变更会靠近真钱配置创建路径,因此所有真实配置创建必须后端强制 `enabled=false`,并通过测试证明研究代理没有任何自动启用真钱的路径。
@@ -0,0 +1,228 @@
## ADDED Requirements
### Requirement: 提供 Leader Research 操作台
系统 SHALL 在受保护的 Web 应用中提供 Leader Research 操作台,用于查看自动研究运行状态、候选、纸跟证据、待处理决策和数据源健康。
#### Scenario: 打开操作台
- **WHEN** 已登录用户打开 Leader Research 页面
- **THEN** 系统 MUST 展示最近研究运行状态、候选概览、待处理决策、纸跟概览和数据源健康
#### Scenario: 研究功能未启用
- **WHEN** 研究 job 未启用或尚未运行
- **THEN** 页面 MUST 展示明确空态,并 MUST NOT 暗示系统已经完成 leader 研究
#### Scenario: 数据源部分失败
- **WHEN** 部分候选来源失败但其他来源可用
- **THEN** 页面 MUST 展示 degraded 状态,并 MUST 继续展示可用来源产生的候选和证据
### Requirement: 记录研究运行
系统 SHALL 为每次自动或手动研究运行记录 run record,确保研究结果可追溯、可排查且不会静默失败。
#### Scenario: 成功运行研究任务
- **WHEN** 研究任务完成一次正常运行
- **THEN** 系统 MUST 保存运行状态、开始时间、结束时间、耗时、来源统计、候选统计和成功状态
#### Scenario: 研究任务部分失败
- **WHEN** 研究任务中某个来源或阶段失败但整体任务仍可继续
- **THEN** 系统 MUST 将 run 标记为 partial failure,并 MUST 保存失败来源、错误类型和错误信息
#### Scenario: 防止并发运行
- **WHEN** 上一次研究任务仍在运行时触发新的研究任务
- **THEN** 系统 MUST 跳过新的任务或拒绝新的任务,并 MUST 记录 overlap 事件
### Requirement: 多源发现候选
系统 SHALL 支持从多个来源发现 leader 候选,并将候选标准化为统一研究候选记录。
#### Scenario: 从 watchlist 发现候选
- **WHEN** 系统运行 watchlist 来源
- **THEN** 系统 MUST 为 watchlist 中的有效钱包创建或更新研究候选
#### Scenario: 从已有 Leader 管理记录发现候选
- **WHEN** 系统运行已有 leader 来源
- **THEN** 系统 MUST 为 `copy_trading_leaders` 中的有效 leader 创建或更新研究候选
#### Scenario: 从 activity 事件发现候选
- **WHEN** 系统从已持久化 activity event 中识别可归因的钱包
- **THEN** 系统 MUST 创建或更新研究候选,并 MUST 记录来源为 activity-derived
#### Scenario: 来源返回空结果
- **WHEN** 某个来源成功运行但没有发现候选
- **THEN** 系统 MUST 记录该来源成功且候选数为 0,不得把它当作失败
#### Scenario: 来源失败
- **WHEN** 某个来源超时、限流、解析失败或返回异常
- **THEN** 系统 MUST 更新来源健康状态,并 MUST NOT 因本次失败删除候选或自动降级候选
### Requirement: 持久化 activity event
系统 SHALL 持久化用于纸跟的 activity event,确保纸跟可以重放、去重和排查。
#### Scenario: 保存新的 activity event
- **WHEN** 系统接收到包含交易者、市场、方向、价格、数量和时间的 activity event
- **THEN** 系统 MUST 保存该事件,并 MUST 记录 source、event id、钱包地址、市场、方向、价格、数量、时间戳和原始 payload 摘要
#### Scenario: 重复 activity event
- **WHEN** 系统再次接收到相同 source 和 event id 的 activity event
- **THEN** 系统 MUST 不创建重复事件,并 MUST 保持纸跟处理幂等
#### Scenario: activity event 缺少必需字段
- **WHEN** activity event 缺少钱包地址、市场、方向、价格或数量
- **THEN** 系统 MUST 标记该事件不可用于纸跟,并 MUST 记录原因
### Requirement: 建立独立纸跟账本
系统 SHALL 使用独立纸跟账本记录模拟买卖、模拟持仓、过滤结果和纸跟 PnL,不得使用真实订单跟踪表伪装纸跟。
#### Scenario: 启动纸跟 session
- **WHEN** 研究候选达到进入纸跟的条件
- **THEN** 系统 MUST 创建 paper session,并 MUST 将候选研究状态更新为 `PAPER`
#### Scenario: 记录纸跟买入
- **WHEN** 已进入纸跟的候选出现符合过滤条件的 BUY activity event
- **THEN** 系统 MUST 记录 paper trade 和 paper position,并 MUST 保存模拟价格、模拟数量、fill assumption 和 quote confidence
#### Scenario: 记录纸跟卖出
- **WHEN** 已进入纸跟的候选出现可匹配的 SELL activity event
- **THEN** 系统 MUST 更新对应 paper position,并 MUST 记录 realized paper PnL
#### Scenario: 记录被过滤交易
- **WHEN** 候选交易因价格区间、流动性、风控或数据缺失被过滤
- **THEN** 系统 MUST 记录 filtered paper trade 或 research event,并 MUST 保存 filter reason
#### Scenario: 估值不可用
- **WHEN** 纸跟持仓无法获得可靠 quote 或 position valuation
- **THEN** 系统 MUST 将 valuation status 标记为 `UNAVAILABLE``UNKNOWN`,并 MUST NOT 将其计为 confirmed zero
### Requirement: 计算可解释 copyability score
系统 SHALL 为研究候选计算可解释的 copyability score,并保存分项分数、总分、版本和原因。
#### Scenario: 保存分项分数
- **WHEN** 系统计算候选评分
- **THEN** 系统 MUST 保存收益信号、可重复性、流动性适配、入场价格适配、滑点风险、持仓周期、市场类型风险、回撤风险、退出流动性风险、数据新鲜度和过滤通过率
#### Scenario: 保存评分版本
- **WHEN** 系统保存候选评分
- **THEN** 系统 MUST 保存 score version,以便后续解释历史评分
#### Scenario: 单次大赢不能主导评分
- **WHEN** 候选只有单次异常盈利但样本量不足
- **THEN** 系统 MUST 限制收益信号对总分的影响,并 MUST 通过样本量或可重复性降低晋升概率
### Requirement: 自动推进研究状态
系统 SHALL 基于透明规则自动推进研究状态,并记录每次状态变化的原因。
#### Scenario: 候选进入纸跟
- **WHEN** 候选分数达到阈值、来源新鲜、未退休且未锁定
- **THEN** 系统 MUST 将研究状态从 `CANDIDATE` 推进为 `PAPER`,并 MUST 记录规则版本和原因
#### Scenario: 纸跟达标进入试跟建议
- **WHEN** 候选纸跟至少 7 天、模拟交易至少 10 笔、copyable PnL 为正、最大回撤不低于 -15%、UNKNOWN quote 暴露不超过 20%、过滤比例低于 50% 且无 critical risk flag
- **THEN** 系统 MUST 将研究状态推进为 `TRIAL_READY`,并 MUST 生成试跟建议事件
#### Scenario: 纸跟风险进入冷却
- **WHEN** 候选最大回撤低于 -20%、10 笔后 copyable PnL 小于 -5、quote 或 source stale 超过 72 小时,或出现 thin liquidity exit risk
- **THEN** 系统 MUST 将研究状态推进为 `COOLDOWN`,并 MUST 记录风险原因
#### Scenario: 冷却后恢复候选
- **WHEN** 候选冷却截止时间已过、来源重新新鲜且未锁定
- **THEN** 系统 MAY 将研究状态恢复为 `CANDIDATE`,并 MUST 记录恢复原因
#### Scenario: 多次冷却后退休
- **WHEN** 候选经历 3 次冷却周期或 30 天没有新鲜来源
- **THEN** 系统 MAY 将研究状态推进为 `RETIRED`,并 MUST 记录退休原因
### Requirement: 尊重锁定候选
系统 SHALL 支持锁定研究候选或 Leader 池项,避免自动任务覆盖人工判断。
#### Scenario: 锁定候选
- **WHEN** 用户锁定某个研究候选
- **THEN** 自动任务 MUST NOT 改变该候选的研究状态、试跟建议、建议配置或冷却/退休状态
#### Scenario: 锁定候选仍可更新证据
- **WHEN** 锁定候选出现新的来源证据或纸跟证据
- **THEN** 系统 MAY 追加只读证据,但 MUST NOT 改变人工锁定的决策字段
### Requirement: 保守增强 Leader 池
系统 SHALL 允许研究代理保守地增强现有 Leader 池记录,但不得替代 Leader 池的人工作业语义。
#### Scenario: 研究候选同步到 Leader 池
- **WHEN** 候选进入 `CANDIDATE` 或更高研究状态且需要展示给用户
- **THEN** 系统 MAY 创建或更新对应 Leader 池项,并 MUST 保留 Leader 池作为候选决策层
#### Scenario: 试跟建议不等于真实试跟状态
- **WHEN** 候选研究状态为 `TRIAL_READY`
- **THEN** 系统 MUST NOT 自动把 Leader 池状态改为 `TRIAL``ACTIVE`
#### Scenario: 不自动放大建议配置
- **WHEN** 自动任务更新已有 Leader 池记录
- **THEN** 系统 MUST NOT 自动增加 suggested fixed amount、suggested max daily loss 或 suggested max daily orders
#### Scenario: 不覆盖手工备注
- **WHEN** 自动任务写入研究摘要
- **THEN** 系统 MUST NOT 覆盖用户手工 notes,只能追加或写入独立研究摘要字段
### Requirement: 手动创建禁用试跟配置
系统 SHALL 支持用户从试跟建议手动创建禁用的保守真实跟单配置,并禁止研究代理自动启用真钱。
#### Scenario: 创建禁用试跟配置
- **WHEN** 用户从 `TRIAL_READY` 候选详情点击创建禁用试跟配置并确认
- **THEN** 后端 MUST 通过现有跟单配置服务创建 `enabled=false` 的保守 `FIXED` 配置
#### Scenario: 后端强制禁用状态
- **WHEN** 创建禁用试跟配置请求包含 `enabled=true` 或其他试图立即启用的字段
- **THEN** 后端 MUST 忽略或拒绝这些字段,并 MUST NOT 创建启用中的真实跟单配置
#### Scenario: 创建前展示风险参数
- **WHEN** 用户确认创建禁用试跟配置
- **THEN** UI MUST 展示 fixed amount、max daily loss、max daily orders、price range 和 max position value
#### Scenario: 已存在同账户同 leader 配置
- **WHEN** 用户尝试为同一账户和 leader 创建禁用试跟配置且配置已存在
- **THEN** 系统 MUST 拒绝默认创建,并 MUST 展示已有配置提示
### Requirement: 记录研究事件和通知摘要
系统 SHALL 为关键研究动作记录事件,并在事件流稳定后提供通知摘要。
#### Scenario: 记录关键研究事件
- **WHEN** 系统发现新候选、启动纸跟、晋升试跟建议、进入冷却、退休、来源失败或 valuation stale
- **THEN** 系统 MUST 写入 leader research event,并 MUST 包含候选、事件类型、原因和时间
#### Scenario: 操作台展示待处理事件
- **WHEN** 存在试跟建议或数据源失败事件
- **THEN** 操作台 MUST 展示这些事件,并 MUST 提供跳转到候选详情或源健康详情的入口
#### Scenario: 外部通知失败
- **WHEN** 外部通知渠道发送失败
- **THEN** 系统 MUST 保留 research event,并 MUST 标记通知失败,不得丢失研究事件
### Requirement: 提供研究 API
系统 SHALL 提供受保护的 Leader Research API,用于运行研究、查看运行状态、查看候选、查看纸跟详情、查看事件和创建禁用试跟配置。
#### Scenario: 手动触发研究运行
- **WHEN** 用户请求手动运行研究任务
- **THEN** 后端 MUST 校验权限和 overlap guard,并 MUST 返回 run record 或明确跳过原因
#### Scenario: 查询候选列表
- **WHEN** 前端请求候选列表
- **THEN** 后端 MUST 返回候选研究状态、来源、总分、关键风险、纸跟摘要和待处理决策状态
#### Scenario: 查询候选详情
- **WHEN** 前端请求候选详情
- **THEN** 后端 MUST 返回来源历史、score components、paper session、paper trades、paper positions、research events 和 Leader 池映射信息
#### Scenario: 查询源健康
- **WHEN** 前端请求数据源健康
- **THEN** 后端 MUST 返回每个来源的最近成功时间、最近失败时间、错误类型、错误信息和 stale 状态
### Requirement: 保证研究任务可观测和可回滚
系统 SHALL 提供研究任务的日志、指标、运行记录和开关,确保上线后可以诊断、暂停和回滚。
#### Scenario: 研究 job 默认关闭
- **WHEN** 新版本首次部署
- **THEN** 研究 job SHOULD 默认为关闭或可配置关闭,直到人工验证通过
#### Scenario: 记录结构化日志
- **WHEN** 研究任务运行、候选状态变化、纸跟交易记录、源失败或手动审批发生
- **THEN** 后端 MUST 记录包含 runId、candidateId、source、eventId、oldState、newState 或 copyTradingId 的结构化日志
#### Scenario: 关闭研究 job
- **WHEN** 运维关闭研究 job 配置
- **THEN** 系统 MUST 停止自动研究运行,但 MUST 保留已有研究数据和页面查询能力
@@ -0,0 +1,208 @@
## 1. 后端数据模型与迁移
- [x] 1.1 新增 Flyway 迁移,创建 `leader_research_run` 表,记录运行状态、时间、耗时、来源统计、候选统计、错误类型、错误信息和 partial failure 标记。
- [x] 1.2 新增 Flyway 迁移,创建 `leader_research_candidate` 表,记录钱包地址、关联 leaderId、研究状态、来源、score、reason、risk flags、锁定字段和时间戳。
- [x] 1.3 新增 Flyway 迁移,创建 `leader_research_score` 表,记录 score version、总分和所有 copyability 分项。
- [x] 1.4 新增 Flyway 迁移,创建 `leader_research_event` 表,记录研究事件类型、candidateId、runId、原因、payload 摘要、通知状态和时间戳。
- [x] 1.5 新增 Flyway 迁移,创建 `leader_research_source_state` 表或等价字段,记录每个来源的最近成功、最近失败、错误类型、错误信息和 stale 状态。
- [x] 1.6 新增 Flyway 迁移,创建 `leader_activity_event` append-only 表,若当前 app 未持久化 raw activity event,则用于纸跟重放和去重。
- [x] 1.7 新增 Flyway 迁移,创建 `leader_paper_session` 表,记录 candidateId、状态、开始/结束时间、统计摘要和 PnL 摘要。
- [x] 1.8 新增 Flyway 迁移,创建 `leader_paper_trade` 表,记录 leader trade、模拟成交、filter reason、fill assumption、quote confidence 和 valuation status。
- [x] 1.9 新增 Flyway 迁移,创建 `leader_paper_position` 表,记录 session、market、outcome、数量、成本、当前估值、realized/unrealized PnL 和 valuation status。
- [x] 1.10 为 activity event、candidate、paper trade、paper position、run、event 和 source state 添加唯一键与查询索引,覆盖去重、分页、状态筛选和候选详情查询。
- [x] 1.11 新增 JPA entity 与 repository,覆盖所有 research、activity、paper 相关表。
- [x] 1.12 新增 research 状态、paper session 状态、valuation status、quote confidence、fill assumption、event type 和 source type 枚举。
- [x] 1.13 `leader_activity_event` 必须包含 `normalized_wallet``source_event_id``stable_event_key``event_time``raw_payload_hash``payload_summary``usable_for_discovery``unusable_reason``paper_processing_status``processing_attempts``paper_processing_started_at``paper_processed_at``last_processing_error`
- [x] 1.14 `leader_research_candidate` 必须记录 agent ownership/provenance,区分 agent 创建候选、用户已有 leader、用户锁定候选和人工池子项,避免自动退休或覆盖用户资产。
- [x] 1.15 `leader_research_run` 必须支持单实例运行锁或等价约束,保证同一时间最多一个 research run 推进候选状态。
- [x] 1.16 为 `leader_activity_event(paper_processing_status, event_time)``leader_activity_event(normalized_wallet, event_time)``leader_paper_trade(session_id, leader_trade_id)``leader_research_candidate(research_state, last_source_seen_at)` 添加组合索引。
- [x] 1.17 为 paper trade 增加数据库唯一约束,至少覆盖 `session_id + leader_trade_id + side`,用数据库兜底防止重复模拟成交。
- [x] 1.18 所有新增表必须有可回滚的空表迁移策略;迁移不得修改现有真实订单热表语义。
## 2. Activity 事件持久化
- [x] 2.1 新增 `LeaderActivityIngestionService`,负责 research 专用 activity event 标准化、去重、持久化和不可用原因记录。
- [x] 2.2 保持现有 `PolymarketActivityWsService` 的真实跟单快路径不被 research 拖慢;不得依赖它当前的“已监听 leader 地址预过滤”来发现未知 leader。
- [x] 2.3 新增 bounded Data API backfill 来源,按 watchlist、已有 Leader 管理记录和 active research candidates 拉取 `/activity`,用于补齐历史 paper event。
- [x] 2.4 新增 research global activity capture 扩展点,解析全局 WS activity 时必须在地址过滤前标准化事件;该能力默认关闭,并受配置开关、每分钟写入上限、payload 截断和保留策略保护。
- [x] 2.5 如果 global capture 未启用,activity-derived source 必须在 source health 中明确显示 disabled/degraded,不得假装已经自动发现未知 leader。
- [x] 2.6 为 activity event 实现 source + eventId 幂等保存;缺失 eventId 时用 wallet、conditionId、asset、side、price、size、timestamp、transactionHash 的稳定 hash 生成 fallback key。
- [x] 2.7 对缺少钱包地址、市场、方向、价格或数量的 event 仍写入摘要,并标记不可用于 discovery/paper trading 的原因。
- [x] 2.8 确保 activity event 持久化失败不会阻断现有真实跟单处理链路;失败只写 research run/source error。
- [x] 2.9 新增 activity source health 更新,区分 Data API success empty、Data API failure、WS capture disabled、WS parse failure、write capped 和 stale。
- [x] 2.10 新增 activity event repository 查询方法,支持按钱包、时间窗口、未处理状态、来源、可发现状态和 stable key 查询。
- [x] 2.11 新增 activity ingestion 测试,覆盖正常保存、重复事件、缺失字段、fallback key、持久化失败隔离、write cap、WS capture disabled 和 Data API success empty。
- [x] 2.12 新增回归测试,证明 current copy-trading WS 的已知 leader 过滤不会阻断 research ingestion 对未知钱包的发现路径。
## 3. 候选来源与研究运行
- [x] 3.1 新增 `LeaderResearchJobService`,支持手动 runOnce 和定时运行,默认通过配置关闭自动调度。
- [x] 3.2 为研究任务实现 overlap guard,当前一 run 未结束时跳过或拒绝新 run,并写入 research event。
- [x] 3.3 新增 `LeaderResearchSourceService`,统一 watchlist、已有 Leader 管理记录和 activity-derived candidates;每个 source 返回 typed result,包含 candidates、source status、error class、error message 和 freshness。
- [x] 3.4 新增 watchlist 配置读取路径,支持配置钱包地址、备注和来源标签。
- [x] 3.5 实现 existing leaders 来源,从 `copy_trading_leaders` 创建或更新研究候选。
- [x] 3.6 实现 activity-derived 来源,从 `leader_activity_event` 中发现 fresh、usable、可归因钱包;如果 global capture 未启用,只能基于已持久化事件工作,并必须展示 source limitation。
- [x] 3.7 候选创建必须复用 `LeaderRepository` 的唯一地址语义;新 leader address 必须标准化小写并验证 42 位 EVM 地址格式。
- [x] 3.8 每个来源运行后更新 source state,区分 success with zero candidates、failure、stale、disabled、degraded 和 partial failure。
- [x] 3.9 公开 leaderboard 来源只保留接口/扩展点,不作为第一阶段启用来源;不得在实现中偷偷依赖未确认 endpoint。
- [x] 3.10 候选合并必须保留所有来源 evidence,不得因为同一钱包重复出现而覆盖更早来源、用户备注或锁定状态。
- [x] 3.11 新增 run-level dry-run 或 preview 能力,允许查看本次将新增/更新/冷却多少候选而不推进状态。
- [x] 3.12 新增候选来源测试,覆盖空结果、失败结果、disabled source、重复候选、无效地址、已有 leader 复用和用户锁定保护。
- [x] 3.13 新增研究运行测试,覆盖成功、partial failure、overlap guard、run record 完整性、source limitation 和 preview 不产生状态副作用。
- [x] 3.14 新增生产失败场景测试:某个 source 超时或抛异常时,其他 source 仍能产出候选,且不会删除或降级已有候选。
## 4. 纸跟账本与模拟成交
- [x] 4.1 新增 `LeaderPaperTradingService`,根据候选和 activity event 启动或更新 paper session。
- [x] 4.2 `LeaderPaperTradingService` 必须按 batch claim `leader_activity_event`,通过状态从 `NEW`/`RETRYABLE` 原子更新到 `PROCESSING` 后再处理,避免并发 run 重复模拟成交。
- [x] 4.3 对进入 `PAPER` 状态的候选,处理 BUY event 并记录 paper trade 与 paper position。
- [x] 4.4 对进入 `PAPER` 状态的候选,处理 SELL event 并按持仓匹配规则更新 paper position 和 realized PnL。
- [x] 4.5 将现有 `CopyTradingFilterService` 抽出或复用为 paper-safe evaluatorpaper 场景不得调用真实账户持仓检查来决定模拟持仓,必须使用 paper position provider。
- [x] 4.6 为 research 定义保守 paper config,包含 fixed amount、max daily loss、max daily orders、min/max price、max position value、support sell 和 market end guard。
- [x] 4.7 每笔 paper trade 保存 leader price、leader size、simulated price、simulated size、simulated amount、fill assumption、quote confidence、quote source、quote timestamp、filter result 和 filter reason。
- [x] 4.8 对 quote/valuation 不可用的持仓标记 `UNAVAILABLE``UNKNOWN`,并确保不计为 confirmed zero。
- [x] 4.9 `copyable PnL` 必须区分 realized PnL、available unrealized PnL、unknown valuation exposure 和 confirmed zero exposureunknown/unavailable 不得伪装成亏到 0。
- [x] 4.10 为 paper trade 实现 leaderTradeId 幂等去重,重复 activity event 不得重复产生纸跟。
- [x] 4.11 处理失败必须增加 `processing_attempts`、记录 `last_processing_error`,超过阈值进入 `FAILED` 并写 research event,不得无限重试卡住整轮 run。
- [x] 4.12 新增 paper session 汇总计算,包含交易数、过滤数、open exposure、realized/unrealized/copyable PnL、max drawdown、unknown valuation exposure、confirmed zero exposure 和 filtered ratio。
- [x] 4.13 新增纸跟服务测试,覆盖 BUY、SELL、过滤、重复 event、unknown valuation、confirmed zero、PnL 计算、max drawdown、event claim 并发、失败重试和 FAILED 隔离。
## 5. Copyability 评分与研究状态机
- [x] 5.1 新增 `LeaderResearchScoringService`,计算并保存所有 copyability 分项和总分。
- [x] 5.2 定义 `score_version = research-copyability-v1`,写明每个分项的 0-100 取值范围、权重、缺数据策略和总分计算公式。
- [x] 5.3 v1 默认权重建议:profit signal 20、repeatability 15、liquidity fit 10、entry price fit 10、slippage risk 10、drawdown risk 10、holding period fit 5、market type risk 5、exit liquidity risk 5、data freshness 5、filter pass rate 5。
- [x] 5.4 实现收益信号封顶逻辑,避免单次大赢主导评分;样本量不足时总分必须被 cap,且不能进入 `TRIAL_READY`
- [x] 5.5 实现 repeatability、liquidity fit、entry price fit、slippage risk、holding period fit、market type risk、drawdown risk、exit liquidity risk、data freshness 和 filter pass rate 分项。
- [x] 5.6 对 quote unknown、valuation unavailable、source stale、filtered ratio 过高和 critical risk flag 定义明确扣分或晋升阻断规则。
- [x] 5.7 新增 `LeaderResearchStateMachine`,实现 `DISCOVERED -> CANDIDATE -> PAPER -> TRIAL_READY -> COOLDOWN/RETIRED` 迁移。
- [x] 5.8 状态迁移必须在事务中保存 rule version、oldState、newState、trigger reason、runId 和 research event。
- [x] 5.9 实现 `CANDIDATE -> PAPER` 默认阈值:score >= 60、未锁定、未退休、source 48 小时内新鲜。
- [x] 5.10 实现 `PAPER -> TRIAL_READY` 默认阈值:纸跟 >= 7 天、交易 >= 10、copyable PnL > 0、最大回撤不低于 -15%、UNKNOWN quote 暴露 <= 20%、过滤比例 < 50%、无 critical risk flag。
- [x] 5.11 实现 `PAPER -> COOLDOWN` 默认阈值:最大回撤 < -20%、10 笔后 copyable PnL < -5、quote/source stale > 72 小时或 thin liquidity exit risk。
- [x] 5.12 实现 cooldown 恢复和退休规则。
- [x] 5.13 实现 locked 候选保护,自动任务不得改变锁定候选的状态、试跟建议、建议配置或冷却/退休状态。
- [x] 5.14 新增评分和状态机测试,覆盖全部迁移、边界阈值、锁定保护、source stale、rule version、缺数据 cap、单次大赢 cap 和 critical risk 晋升阻断。
## 6. Leader 池联动与真钱安全边界
- [x] 6.1 新增 research 与 `copy_trading_leader_pool` 的映射服务,确保研究状态与 Leader 池状态分离。
- [x] 6.2 当研究候选进入 `CANDIDATE` 或更高状态且需要展示时,保守创建或更新 Leader 池项。
- [x] 6.3 `TRIAL_READY` 只能在 Leader 池展示“建议小额试跟” badge,不得自动把 Leader 池状态改为 `TRIAL``ACTIVE`
- [x] 6.4 自动任务不得增加 existing pool 的 suggested fixed amount、suggested max daily loss 或 suggested max daily orders。
- [x] 6.5 自动任务不得覆盖用户手工 notes;研究摘要必须写入独立字段或 append-only event。
- [x] 6.6 新增 research 专用手动审批服务,从 `TRIAL_READY` 候选创建禁用试跟配置;不得原样复用当前 `LeaderPoolService.createTrialConfig`,除非先重构出 disabled-only 安全 helper。
- [x] 6.7 research approval request DTO 不允许暴露 `enabled``enableImmediately` 或放宽风控字段;后端组装 `CopyTradingCreateRequest` 时必须强制 `enabled=false`
- [x] 6.8 如果客户端篡改提交 `enabled=true`、更大 fixed amount、更高 max daily loss、更多 max daily orders 或更宽 price range,后端必须拒绝或忽略,并写入 safety research event。
- [x] 6.9 创建禁用试跟配置前检查同账户同 leader 是否已有跟单配置,默认拒绝重复创建;双击提交必须只创建一条配置。
- [x] 6.10 用户明确创建 disabled trial config 成功后,Leader 池才可以从 WATCH/建议状态进入 `TRIAL`;仅 `TRIAL_READY` recommendation 不得进入 `TRIAL`
- [x] 6.11 创建禁用试跟配置成功后写入 research event,并提供跳转 `跟单配置` 的信息。
- [x] 6.12 新增安全测试,覆盖客户端篡改 `enabled=true`、高 fixed amount、放宽风控、locked 候选、重复配置、双击创建、TRIAL_READY 不自动 TRIAL 和 disabled config 创建后 copy_trading.enabled 仍为 false。
## 7. 后端 API 与错误处理
- [x] 7.1 新增 `LeaderResearchController`,提供研究运行、候选列表、候选详情、paper session、source health、research events 和手动创建禁用试跟配置 API。
- [x] 7.2 所有 research API 必须走现有受保护路由与鉴权边界。
- [x] 7.3 新增 research DTO,覆盖 run summary、candidate summary/detail、score components、paper session、paper trade、paper position、source health、event 和 approval response。
- [x] 7.4 新增 research 错误码和中英繁 i18n 文案,覆盖 run overlap、candidate not found、candidate locked、not trial ready、source unavailable、paper valuation unavailable、real money activation forbidden。
- [x] 7.5 服务内部使用命名错误或明确 result 类型,不得只用 catch-all Exception 表达业务失败。
- [x] 7.6 所有源失败、解析失败、状态迁移失败、纸跟失败和审批失败必须写入 research event 或 run error。
- [x] 7.7 新增 controller 测试,覆盖所有 API 的成功路径、参数错误、权限/保护边界、业务错误和真钱启用拒绝。
- [x] 7.8 手动 run 和创建禁用试跟配置 API 必须有重复提交保护;重复请求返回已有 run/config 或明确拒绝,不得产生重复副作用。
- [x] 7.9 候选详情 API 必须分页返回 paper trades 和 research events,不得一次性返回完整历史。
- [x] 7.10 API 错误响应必须让前端能区分 `disabled source``degraded source``valuation unknown``candidate locked``real money activation forbidden`
## 8. 前端类型、API 与操作台
- [x] 8.1 在 `frontend/src/types` 中新增 Leader Research 类型,覆盖状态、source health、score components、paper session/trade/position、events 和 approval request/response。
- [x] 8.2 在 `frontend/src/services/api.ts` 新增 `leaderResearch` API 分组。
- [x] 8.3 新增 `LeaderResearch` 页面和受保护路由。
- [x] 8.4 在左侧菜单合适位置新增 `Leader Research``Leader 雷达` 入口,避免和现有 `Leader 池` 混淆。
- [x] 8.5 操作台顶部展示 Today summarytrial-ready 数、新候选数、新纸跟数、冷却数、source stale 数。
- [x] 8.6 实现 Run Status 模块,展示最近运行、耗时、状态、来源统计和失败信息。
- [x] 8.7 实现 Pending Decisions 模块,展示 `TRIAL_READY` 候选和手动创建禁用试跟配置入口。
- [x] 8.8 实现 Candidate Detail,展示来源历史、score components、paper session summary、risk flags、research events 和 Leader 池映射状态。
- [x] 8.9 实现 Paper Sessions/Trades 展示,明确展示 valuation status、quote confidence 和 filtered reason。
- [x] 8.10 实现 Source Health 模块,区分 success empty、failure、stale 和 degraded。
- [x] 8.11 创建禁用试跟配置前,UI 必须展示 fixed amount、max daily loss、max daily orders、price range 和 max position value,并要求确认。
- [x] 8.12 UI 不得把 `TRIAL_READY` 文案表现成已经真实试跟;必须明确“建议小额试跟,待你确认”。
- [x] 8.13 新增 zh-CN、zh-TW、en 文案。
- [x] 8.14 确保移动端操作台至少可查看 summary、pending decisions 和 candidate detail,不出现不可操作表格。
- [x] 8.15 UI 必须展示 activity source limitationglobal capture disabled、Data API backfill stale、source degraded 等状态,避免用户误以为系统已经全网自动发现。
- [x] 8.16 创建禁用试跟配置按钮必须防双击,提交中禁用,并在重复配置错误时跳转或提示已有配置。
- [x] 8.17 Candidate Detail 必须把 `UNKNOWN``UNAVAILABLE``NO_MATCH``CONFIRMED_ZERO` 用不同文案和颜色展示,不能都显示成 0。
- [x] 8.18 Leader 池或 Leader Research 的 `TRIAL_READY` badge 必须和真实 `TRIAL` 状态视觉区分,避免用户误以为真钱已开始。
## 9. 通知与内部事件
- [x] 9.1 新增 research event 查询与标记通知状态能力。
- [x] 9.2 第一阶段在操作台展示内部事件,不强依赖外部通知渠道。
- [x] 9.3 新增通知摘要生成服务,基于 research event 聚合新增候选、纸跟达标、建议试跟、冷却、source failure 和 stale data。
- [x] 9.4 外部通知发送失败时必须保留 research event,并记录通知失败原因。
- [x] 9.5 如果接入现有 Telegram 通知,必须避免硬编码新渠道 secret,并复用现有通知配置读取路径。
- [x] 9.6 新增通知测试,覆盖事件生成、摘要生成、发送失败和事件保留。
- [x] 9.7 通知摘要必须包含 safety-relevant 事件:source disabled/degraded、valuation unknown、approval rejected、duplicate approval 和 real money activation forbidden。
- [x] 9.8 通知事件必须可去重,避免同一候选每轮 research run 重复推送相同 `TRIAL_READY` 或 source failure。
## 10. 性能、索引与数据保留
- [x] 10.1 候选列表、paper trade 列表、research event 列表必须分页。
- [x] 10.2 研究任务每次运行只处理 active candidates 和新 activity events,不得全量重算全部历史。
- [x] 10.3 为所有详情查询路径增加 repository 查询或聚合,避免 N+1 查询。
- [x] 10.4 增加 paper/activity 数据保留策略配置,默认保留足够研究窗口但避免无限增长。
- [x] 10.5 新增性能相关测试或验证脚本,至少覆盖 100 个候选、1 万条 activity event、1 万条 paper trade 的列表和一次研究运行。
- [x] 10.6 research run 必须使用 cursor/checkpoint 记录上次处理到的 event time 或 stable key;失败重跑时只回看有限窗口。
- [x] 10.7 paper processing 每批必须有 batch size 上限、运行时间上限和写入上限,避免一次 run 长时间占用数据库。
- [x] 10.8 global activity capture 如果启用,必须有每分钟写入上限、payload 最大长度、失败熔断和可观测计数。
- [x] 10.9 候选详情必须用聚合 DTO 或批量查询组装 score、paper summary、source health、pool mapping,禁止对列表中每个 candidate 单独查多张表。
## 11. 文档与运维
- [x] 11.1 新增中文文档,说明 Leader Research Agent、Leader 池、Leader 管理和跟单配置的区别。
- [x] 11.2 文档明确:研究代理可以自动纸跟和推荐,但绝不会自动启用真钱跟单。
- [x] 11.3 文档说明研究状态和 Leader 池状态映射,特别是 `TRIAL_READY` 不等于 `TRIAL`
- [x] 11.4 新增运维说明,覆盖开启/关闭 research job、查看 source health、排查 unknown valuation、排查重复 event 和回滚。
- [x] 11.5 更新相关 README 或 docs 索引,指向 Leader Research 文档。
- [x] 11.6 文档明确 activity-derived discovery 的真实覆盖范围:watchlist/existing leader Data API backfill、global capture 是否启用、public leaderboard 尚未接入。
- [x] 11.7 运维说明必须包含 kill switch:关闭 scheduled research、关闭 global activity capture、隐藏 approval endpoint 或前端入口。
## 12. 验证
- [x] 12.1 运行后端 research 相关单元测试和 controller 测试。
- [x] 12.2 运行已有 Leader 池、跟单配置、PnL 统计相关测试,确保未破坏现有安全边界。
- [x] 12.3 运行前端 TypeScript 构建。
- [x] 12.4 运行前端 lint。
- [x] 12.5 手动验证 research job 默认关闭、手动 run、source health、候选列表、候选详情、纸跟详情和 pending decisions。
- [x] 12.6 手动验证创建禁用试跟配置后 `copy_trading.enabled=false`,且不会自动启用。
- [x] 12.7 手动验证 source failure 不会删除候选、不会降级 locked 候选、不会把 unknown valuation 展示成 confirmed zero。
- [x] 12.8 在本地或测试环境验证重复 activity event 不会产生重复 paper trade。
- [x] 12.9 运行 `./gradlew test --tests "*LeaderResearch*"`,覆盖 research service、state machine、paper trading、approval safety 和 controller。
- [x] 12.10 运行 `./gradlew test --tests "*LeaderPool*" --tests "*CopyTrading*" --tests "*Pnl*"`,确保现有 Leader 池、跟单配置和 PnL 语义未回退。
- [x] 12.11 使用 gstack/browser 或 Playwright 进行前端安全 QA,覆盖 `TRIAL_READY` 文案、disabled approval modal、双击提交、source degraded、valuation unknown 和移动端关键路径。
- [x] 12.12 使用 `/qa` 时优先加载 `/Users/codyhhchen/.gstack/projects/WrBug-PolyHermes/codyhhchen-feat-real-pnl-statistics-eng-review-test-plan-20260504-210925.md` 作为主测试输入。
- [x] 12.13 验证当前 `PolymarketActivityWsService` 的已知 leader 过滤不会阻断 research ingestion 的未知钱包发现路径。
- [x] 12.14 验证 research job kill switch 生效:关闭后不再推进状态、不再处理 paper events,但页面仍可读历史数据。
## 13. 一次性实施顺序与合并策略
- [x] 13.1 第一批实现数据 spine:迁移、entity、repository、enum、source state、run record、activity event 和 processing status。
- [x] 13.2 第二批实现 ingestion 与 sourceData API backfill、optional global capture、watchlist、existing leaders、activity-derived candidates 和 source health。
- [x] 13.3 第三批实现 paper tradingevent claim、paper config、BUY/SELL、filter evaluator、valuation、PnL、drawdown 和重试隔离。
- [x] 13.4 第四批实现 scoring/state machinescore v1 公式、状态迁移、locked protection、cooldown/retire 和 research events。
- [x] 13.5 第五批实现 Leader 池联动和 disabled approval safetypool mapping、recommendation badge、disabled-only config creation、duplicate protection 和 safety tests。
- [x] 13.6 第六批实现 API 和前端操作台:run status、candidate list/detail、paper sessions、source health、pending decisions、approval modal 和 i18n。
- [x] 13.7 第七批实现通知、运维文档、kill switch、性能脚本和全量 QA。
- [x] 13.8 每一批都必须先补对应测试再合并到下一批;不得把 safety tests 延后到最后。
- [x] 13.9 如果并行工作,数据 spine 是共享依赖;paper trading、frontend shell、notification summary 可以并行,但 approval safety 必须等 DTO/服务边界稳定后再接。
## 14. Review 整改任务
- [x] 14.1 修复 locked candidate 安全边界:`LeaderResearchStateMachine.advance()` 对锁定候选必须完全跳过状态、score、badge、summary、Leader 池映射和建议配置写入,不得调用会产生副作用的 `syncCandidate`;补充 locked candidate 不变更 pool/candidate/poolId 的回归测试。
- [x] 14.2 修复 `DISCOVERED` 候选污染 Leader 池:状态机或 pool mapping 只能在候选达到 `CANDIDATE` 或更高状态时创建/更新 `copy_trading_leader_pool`,原始 `DISCOVERED` 钱包不得出现在 Leader 池;补充 `DISCOVERED` 不建池、`CANDIDATE+` 才建池的测试。
- [x] 14.3 修复创建禁用试跟配置的并发重复问题:为同账户同 leader 恢复数据库级唯一保护或引入等价事务锁/幂等键,确保双击、并发请求和重试最多创建一条 copy-trading config;补充并发审批测试和迁移回归测试。
- [x] 14.4 修复 Data API backfill 失败被误报为 source success`backfillWalletActivities` 失败必须向 `captureSource` 返回 typed failure/partial failuresource health 和 research run 必须显示 failure/degraded,而不是 SUCCESS;补充 Data API failure、success empty、partial failure 的 source health 测试。
- [x] 14.5 修复前端 approval preview 硬编码风险参数:确认弹窗必须展示后端返回或候选池建议的 `fixedAmount``maxDailyLoss``maxDailyOrders``minPrice``maxPrice``maxPositionValue`,不得写死 1 USDC/5 USDC/10 orders/0.10-0.80;补充 UI 数据映射测试或浏览器 QA。
- [x] 14.6 修复 candidate 搜索只搜当前页:后端搜索必须在数据库查询层按 wallet/address/source/status 等条件过滤并返回正确 total,不得先分页再内存过滤;补充分页外命中、空结果和 total 计数测试。
- [x] 14.7 修复 candidate list N+1 查询:列表 DTO 组装必须批量加载 leader、pool mapping、score/paper summary 等依赖,禁止每个 candidate 单独查多张表;补充 50+ 候选列表查询数量或性能回归测试。
- [x] 14.8 补齐 Review 发现对应的验证任务:完成后必须运行前端 build/lint、后端 `LeaderResearch` 相关测试、Leader 池/跟单配置/PnL 回归测试,并在本机没有 Java Runtime 时先安装或切换到可运行 Java 的环境再执行后端测试。
- [x] 14.9 完成未勾选的安全和 QA 任务后再关闭 change:至少覆盖 activity source health、候选来源、研究运行、纸跟、评分状态机、approval safety、controller、通知、数据保留、手动 QA 和浏览器 QA;不得只修 review 代码而保留关键验证项未完成。