feat: add copy trading safety and leader pool
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-05-03
|
||||
@@ -0,0 +1,384 @@
|
||||
## Context
|
||||
|
||||
PolyHermes 当前把跟单流程拆成两层:
|
||||
|
||||
- `copy_trading_leaders` 保存可被跟单的钱包地址,前端入口是 `Leader 管理`。
|
||||
- `copy_trading` 保存真实跟单配置,前端入口是 `跟单配置`。
|
||||
|
||||
这两层之间缺少“候选池/配仓台”。用户如果想用小仓位摊大饼,只能靠脑子或外部笔记维护候选状态,然后手动创建真实跟单配置。这对真钱交易不够安全:观察、试跟、冷却、淘汰没有独立状态,配置也容易沿用过松默认值。
|
||||
|
||||
本设计新增 `Leader 池`,把它定位为 `Leader 管理` 与 `跟单配置` 中间的决策层。它不替代现有 leader 地址库,也不替代真实跟单配置;它负责帮助用户决定“谁进入候选、谁小额试跟、谁冷却或淘汰”。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- 新增左侧菜单入口 `跟单交易 -> Leader 池`,路径为 `/leader-pool`。
|
||||
- 支持把已有 leader 加入池子,并维护池子状态。
|
||||
- 支持从 `Leader 管理` 一键加入池子。
|
||||
- 支持从池子创建保守小额跟单配置。
|
||||
- 在页面上展示池子人数、试跟人数、估算最坏暴露、待处理风险。
|
||||
- 复用现有 leader、账户、跟单配置和统计能力,避免重复业务逻辑。
|
||||
- 用显式确认和保守默认值保护真钱交易安全。
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- 不接入 Polymarket leaderboard 自动发现。
|
||||
- 不自动启用大额跟单。
|
||||
- 不自动加仓。
|
||||
- 不做 AI 预测评分。
|
||||
- 不做复杂多账户资金托管。
|
||||
- 不修改真实 PnL 统计口径。统计中仍必须区分真实归零仓位和行情/持仓数据不可用。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. 新增独立 `copy_trading_leader_pool` 表
|
||||
|
||||
采用独立表,不把池子字段塞进 `copy_trading_leaders`。
|
||||
|
||||
原因:
|
||||
|
||||
- `copy_trading_leaders` 是地址库,leader 可以存在但不在当前策略池中。
|
||||
- 池子有状态、来源、建议配置、冷却时间、复核时间,这些是策略决策数据,不是 leader 基础资料。
|
||||
- 后续 leaderboard radar、手动观察、亏损诊断都可以写入同一池子表,而不污染 leader 地址库。
|
||||
|
||||
建议迁移:
|
||||
|
||||
```text
|
||||
backend/src/main/resources/db/migration/V41__create_copy_trading_leader_pool.sql
|
||||
```
|
||||
|
||||
建议字段:
|
||||
|
||||
```text
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY
|
||||
leader_id BIGINT NOT NULL
|
||||
status VARCHAR(20) NOT NULL
|
||||
source VARCHAR(50) NOT NULL DEFAULT 'MANUAL'
|
||||
source_rank INT NULL
|
||||
score DECIMAL(20, 8) NULL
|
||||
reason TEXT NULL
|
||||
notes TEXT NULL
|
||||
suggested_fixed_amount DECIMAL(20, 8) NOT NULL DEFAULT 1.00000000
|
||||
suggested_max_daily_orders INT NOT NULL DEFAULT 10
|
||||
suggested_max_daily_loss DECIMAL(20, 8) NOT NULL DEFAULT 5.00000000
|
||||
suggested_min_price DECIMAL(20, 8) NULL DEFAULT 0.10000000
|
||||
suggested_max_price DECIMAL(20, 8) NULL DEFAULT 0.80000000
|
||||
suggested_max_position_value DECIMAL(20, 8) NULL DEFAULT 5.00000000
|
||||
last_reviewed_at BIGINT NULL
|
||||
last_promoted_at BIGINT NULL
|
||||
cooldown_until BIGINT NULL
|
||||
locked BOOLEAN NOT NULL DEFAULT FALSE
|
||||
created_at BIGINT NOT NULL
|
||||
updated_at BIGINT NOT NULL
|
||||
```
|
||||
|
||||
约束与索引:
|
||||
|
||||
```text
|
||||
UNIQUE KEY uk_leader_pool_leader_id (leader_id)
|
||||
INDEX idx_leader_pool_status (status)
|
||||
INDEX idx_leader_pool_source (source)
|
||||
FOREIGN KEY (leader_id) REFERENCES copy_trading_leaders(id) ON DELETE CASCADE
|
||||
```
|
||||
|
||||
v1 池子是全局策略池,不按账户拆分。账户只在创建小额试跟配置时选择;同一个 leader 在池子里只出现一次,避免把“候选观察状态”拆成多份互相打架的笔记。若后续需要多账户独立池,再新增 `account_id` 并迁移为 `UNIQUE(account_id, leader_id)`,本次不做。
|
||||
|
||||
### 2. 状态机后端完整、前端 v1 精简
|
||||
|
||||
后端状态枚举预留:
|
||||
|
||||
```text
|
||||
CANDIDATE
|
||||
WATCH
|
||||
PAPER
|
||||
TRIAL
|
||||
ACTIVE
|
||||
COOLDOWN
|
||||
RETIRED
|
||||
```
|
||||
|
||||
前端 v1 展示:
|
||||
|
||||
```text
|
||||
候选
|
||||
观察
|
||||
小额试跟
|
||||
冷却
|
||||
淘汰
|
||||
```
|
||||
|
||||
原因:
|
||||
|
||||
- 后端预留可以让后续 radar 或自动规则不需要迁移状态字段。
|
||||
- 前端不一次暴露太多状态,避免用户每天面对一堆概念。
|
||||
- 锁定是独立布尔字段 `locked`,不是状态。这样 `TRIAL + locked`、`COOLDOWN + locked` 都能表达,不会把“交易阶段”和“自动任务是否可改”混在一起。
|
||||
|
||||
状态更新规则:
|
||||
|
||||
- `RETIRED` 不删除 leader 地址,只让池子项退出关注。
|
||||
- `COOLDOWN` 可以设置 `cooldownUntil`。
|
||||
- `locked=true` 的池子项不允许被后续自动任务改状态。v1 没有自动任务,但字段先保留。
|
||||
|
||||
状态流:
|
||||
|
||||
```text
|
||||
+---------+
|
||||
| RETIRED |
|
||||
+---------+
|
||||
^
|
||||
|
|
||||
CANDIDATE -> WATCH -> TRIAL -> ACTIVE
|
||||
| | |
|
||||
+----------+--------+
|
||||
|
|
||||
v
|
||||
COOLDOWN
|
||||
|
||||
locked=true 是覆盖层,不是状态:
|
||||
任何状态 + locked=true => 后续自动任务不得改状态
|
||||
```
|
||||
|
||||
### 3. 创建试跟配置必须复用 `CopyTradingService.createCopyTrading`
|
||||
|
||||
新增 `LeaderPoolService.createTrialConfig`,内部组装 `CopyTradingCreateRequest`,然后调用现有 `CopyTradingService.createCopyTrading`。
|
||||
|
||||
原因:
|
||||
|
||||
- 现有服务已经处理账户、leader、模板、监听更新、字段校验。
|
||||
- 不复制跟单创建逻辑,避免两套规则不同步。
|
||||
|
||||
默认请求建议:
|
||||
|
||||
```text
|
||||
enableImmediately = false
|
||||
confirm = false
|
||||
enabled = false
|
||||
copyMode = FIXED
|
||||
fixedAmount = suggestedFixedAmount 默认 1
|
||||
maxOrderSize = suggestedFixedAmount 默认 1
|
||||
minOrderSize = 1
|
||||
maxDailyOrders = suggestedMaxDailyOrders 默认 10
|
||||
maxDailyLoss = suggestedMaxDailyLoss 默认 5
|
||||
priceTolerance = 1
|
||||
minPrice = 0.10
|
||||
maxPrice = 0.80
|
||||
maxPositionValue = 5
|
||||
supportSell = true
|
||||
pushFailedOrders = true
|
||||
pushFilteredOrders = true
|
||||
configName = "Leader池-<leaderName或地址后6位>"
|
||||
```
|
||||
|
||||
`enabled=false` 是推荐默认。若前端允许立即启用,必须弹出确认,并明确展示固定金额、最大日单数、最大日亏损和最大持仓。
|
||||
|
||||
后端也必须执行同一条安全规则:当 `enableImmediately=true` 时,`confirm` 必须为 `true`,否则拒绝创建。真钱交易不能只靠前端弹窗保护。
|
||||
|
||||
创建前还必须检查同一 `accountId + leaderId` 是否已经存在跟单配置。默认拒绝重复创建,并返回已有配置提示。PolyHermes 允许同一 leader 多个跟单配置,但 Leader 池的一键试跟不应该制造重复配置;真要高级配置,用户应该走 `跟单配置` 页面手动创建。
|
||||
|
||||
建议配置必须在后端校验,不只靠前端输入框:
|
||||
|
||||
```text
|
||||
suggestedFixedAmount > 0
|
||||
suggestedMaxDailyOrders between 1 and 100
|
||||
suggestedMaxDailyLoss > 0
|
||||
suggestedMinPrice / suggestedMaxPrice in [0, 1] when present
|
||||
suggestedMinPrice <= suggestedMaxPrice when both present
|
||||
suggestedMaxPositionValue > 0 when present
|
||||
```
|
||||
|
||||
这是“雨露均沾”的安全边界:小额可以分散,但不能因为一个坏输入把单个 leader 变成隐形重仓。
|
||||
|
||||
### 4. 新增 leader 池 API,路径归在 copy-trading 下
|
||||
|
||||
新增控制器:
|
||||
|
||||
```text
|
||||
backend/src/main/kotlin/com/wrbug/polymarketbot/controller/copytrading/leaderpool/LeaderPoolController.kt
|
||||
```
|
||||
|
||||
API:
|
||||
|
||||
```text
|
||||
POST /api/copy-trading/leader-pool/list
|
||||
POST /api/copy-trading/leader-pool/add
|
||||
POST /api/copy-trading/leader-pool/update-status
|
||||
POST /api/copy-trading/leader-pool/update-plan
|
||||
POST /api/copy-trading/leader-pool/create-trial-config
|
||||
POST /api/copy-trading/leader-pool/remove
|
||||
```
|
||||
|
||||
`list` 响应应聚合:
|
||||
|
||||
- leader 基础信息。
|
||||
- 池子状态和建议配置。
|
||||
- 该 leader 的跟单配置数量。
|
||||
- 该 leader 是否已有启用中的跟单配置。
|
||||
- 估算最坏暴露字段。
|
||||
|
||||
聚合必须使用批量查询或聚合查询,不能对每个池子项逐个查 leader 和跟单配置。计划新增 repository 方法,例如:
|
||||
|
||||
```text
|
||||
LeaderRepository.findAllById(...)
|
||||
CopyTradingRepository.findByLeaderIdIn(...)
|
||||
CopyTradingRepository.countByLeaderIdInGrouped(...) 或用 findByLeaderIdIn 后内存分组
|
||||
```
|
||||
|
||||
这保持 v1 足够简单,同时避免池子 50 人时打出 100 多次 SQL。软件会在你最懒得排查的时候提醒你什么叫 N+1。
|
||||
|
||||
主要数据流:
|
||||
|
||||
```text
|
||||
Leader 管理
|
||||
-> POST /leader-pool/add
|
||||
-> LeaderPoolService.addToPool
|
||||
-> copy_trading_leader_pool
|
||||
|
||||
Leader 池页面
|
||||
-> POST /leader-pool/list
|
||||
-> LeaderPoolService.getPoolList
|
||||
-> batch read leaders + copy_trading
|
||||
-> summary cards + table
|
||||
|
||||
Leader 池创建小额试跟
|
||||
-> POST /leader-pool/create-trial-config
|
||||
-> LeaderPoolService.createTrialConfig
|
||||
-> CopyTradingService.createCopyTrading
|
||||
-> copy_trading
|
||||
-> pool.status = TRIAL only after success
|
||||
```
|
||||
|
||||
### 5. 前端页面独立,Leader 管理只放轻入口
|
||||
|
||||
新增页面:
|
||||
|
||||
```text
|
||||
frontend/src/pages/LeaderPool.tsx
|
||||
```
|
||||
|
||||
修改路由:
|
||||
|
||||
```text
|
||||
frontend/src/App.tsx
|
||||
```
|
||||
|
||||
修改菜单:
|
||||
|
||||
```text
|
||||
frontend/src/components/Layout.tsx
|
||||
```
|
||||
|
||||
菜单位置:
|
||||
|
||||
```text
|
||||
跟单交易
|
||||
跟单配置
|
||||
Leader 池
|
||||
Leader 管理
|
||||
跟单模板
|
||||
回测
|
||||
```
|
||||
|
||||
`LeaderList.tsx` 只新增“加入 Leader 池”操作。不要把池子状态和预算管理塞进 Leader 管理页。
|
||||
|
||||
`LeaderPool.tsx` 不默认逐行查询 leader 余额。Leader 管理页当前有逐个加载余额的重型交互,但池子页的核心任务是筛选、配仓和创建小额试跟配置;默认列表只展示后端一次返回的聚合字段,profile 和详情可以按需打开。
|
||||
|
||||
### 6. 预算概览只做提示,不做资金托管
|
||||
|
||||
页面顶部显示:
|
||||
|
||||
```text
|
||||
池子人数
|
||||
试跟中人数
|
||||
估算最坏暴露
|
||||
待处理风险
|
||||
```
|
||||
|
||||
估算方式:
|
||||
|
||||
```text
|
||||
TRIAL/ACTIVE 状态的池子项数量 * suggestedMaxPositionValue
|
||||
```
|
||||
|
||||
如果某个池子项没有 `suggestedMaxPositionValue`,使用默认 5。
|
||||
|
||||
v1 可以先不持久化“总试验预算”。页面可用默认 50 显示提示,或提供前端输入但不存库。若实现持久化预算,优先使用现有系统配置能力,不新增复杂账户资金模型。
|
||||
|
||||
### 7. 错误态与空态
|
||||
|
||||
前端必须覆盖:
|
||||
|
||||
- 池子为空:提示从 `Leader 管理` 加入 leader,或在池子页选择已有 leader 加入。
|
||||
- leader 已在池子:加入动作返回明确提示,不创建重复项。
|
||||
- leader 不存在:提示先添加 leader。
|
||||
- 没有账户:创建试跟配置前提示先添加账户。
|
||||
- 创建配置失败:展示后端错误,不更新池子状态。
|
||||
- 创建配置成功:池子状态更新为 `TRIAL`,并给出跳转到 `跟单配置` 的入口。
|
||||
|
||||
### 8. 调度与自动化
|
||||
|
||||
本次不新增定时任务。
|
||||
|
||||
后续 leaderboard radar 可以把候选写入 `copy_trading_leader_pool`,但必须遵守:
|
||||
|
||||
- 不自动创建真实跟单配置。
|
||||
- 不改 `locked=true` 的池子项。
|
||||
- 不自动把 `COOLDOWN` 或 `RETIRED` 改回试跟。
|
||||
|
||||
### 9. 可观测性
|
||||
|
||||
后端日志至少记录:
|
||||
|
||||
- leader 加入池子。
|
||||
- 池子状态变化。
|
||||
- 创建试跟配置成功/失败。
|
||||
- 被拒绝的危险操作,例如重复加入、leader 不存在、账户不存在。
|
||||
|
||||
未来如果接入审计表,可把这些操作迁入审计事件。本次不强制新增审计表。
|
||||
|
||||
### 10. 错误码与国际化
|
||||
|
||||
Leader 池需要独立错误码,不要全部塞进 `BUSINESS_ERROR`。建议在现有错误码体系中新增:
|
||||
|
||||
```text
|
||||
LEADER_POOL_NOT_FOUND(4251)
|
||||
LEADER_POOL_ALREADY_EXISTS(4252)
|
||||
LEADER_POOL_DUPLICATE_TRIAL_CONFIG(4253)
|
||||
LEADER_POOL_CONFIRM_REQUIRED(4254)
|
||||
SERVER_LEADER_POOL_LIST_FAILED(5451)
|
||||
SERVER_LEADER_POOL_UPDATE_FAILED(5452)
|
||||
SERVER_LEADER_POOL_CREATE_TRIAL_FAILED(5453)
|
||||
```
|
||||
|
||||
这些编号避开当前 `ErrorCode` 中已经存在的 `4601` 冲突。实现时必须确认没有新增重复 code。
|
||||
|
||||
同时补齐 `messages_zh_CN.properties`、`messages_zh_TW.properties`、`messages_en.properties`。前端需要能看到明确错误,而不是一个泛泛的“业务逻辑错误”。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Risk] 用户误以为加入池子等于已经跟单 → Mitigation:页面文案明确区分 `观察`、`小额试跟` 和真实 `跟单配置`,创建配置后给出配置状态。
|
||||
- [Risk] 一键创建配置触发真钱交易 → Mitigation:默认 `enabled=false`;若支持立即启用,必须二次确认。
|
||||
- [Risk] 双击或多标签重复创建试跟配置 → Mitigation:前端提交时进入 loading 并禁用按钮;后端按 `accountId + leaderId` 检查已有配置,默认拒绝重复创建。
|
||||
- [Risk] 池子状态和跟单配置状态不一致 → Mitigation:list 响应聚合当前跟单配置数量和启用状态;不把池子状态当作真实交易状态。
|
||||
- [Risk] 同一 leader 重复加入池子 → Mitigation:数据库唯一约束 `leader_id`,服务层返回幂等提示。
|
||||
- [Risk] 并发加入同一 leader 时先查再写仍然冲突 → Mitigation:保留数据库唯一约束,并捕获唯一约束异常,返回“已在池子中”的明确结果。
|
||||
- [Risk] 建议配置输入为负数、超大值或无效价格区间 → Mitigation:后端校验所有建议配置字段,拒绝危险配置,不只依赖前端表单。
|
||||
- [Risk] 池子列表照搬 Leader 管理逐行余额查询导致页面变慢 → Mitigation:池子列表默认只用后端聚合字段,不逐行拉余额。
|
||||
- [Risk] 页面诱导追涨 → Mitigation:v1 不做收益率排行榜主导,不做 AI 评分;重点展示状态、预算、风险和保守配置。
|
||||
- [Risk] 预算只是估算,不等于真实账户风险 → Mitigation:页面文案使用“估算最坏暴露”,不展示为账户余额或保证金。
|
||||
- [Risk] 后续自动 radar 覆盖人工判断 → Mitigation:预留 `locked` 字段,自动任务不得修改锁定项,不得复活冷却/淘汰项。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. 新增 Flyway 迁移 `V41__create_copy_trading_leader_pool.sql`。
|
||||
2. 部署后 Flyway 自动创建新表,不改动现有表数据。
|
||||
3. 新增 API 和前端入口上线后,池子默认为空。
|
||||
4. 用户可以从现有 `Leader 管理` 手动加入 leader。
|
||||
5. 回滚时可隐藏前端菜单和 API;新表保留不会影响现有跟单流程。
|
||||
6. 若必须数据库回滚,可删除 `copy_trading_leader_pool` 表;不会影响 `copy_trading_leaders` 和 `copy_trading`。
|
||||
|
||||
## Locked v1 Decisions
|
||||
|
||||
- v1 不持久化“总试验预算”。页面只展示默认 50 的试验预算提示和估算最坏暴露,不新增账户资金模型。
|
||||
- v1 UI 不提供“创建后立即启用”开关。池子创建的小额试跟配置默认 `enabled=false`;`enableImmediately` 与 `confirm` 只作为后端防御和后续扩展预留。
|
||||
- v1 UI 只暴露“候选、观察、小额试跟、冷却、淘汰”。`PAPER` 和 `ACTIVE` 保留在后端枚举中,第一版不进入筛选标签和主操作流。
|
||||
@@ -0,0 +1,35 @@
|
||||
## Why
|
||||
|
||||
PolyHermes 现在有 `Leader 管理` 和 `跟单配置`,但缺少中间的决策层:用户只能先保存 leader,再直接创建真实跟单配置。对于“小仓位、雨露均沾”的摊大饼策略,这会把“观察候选”和“真钱下单”混在一起,容易因为手动判断和配置过松扩大亏损。
|
||||
|
||||
这次变更要新增一个 `Leader 池` 入口,让用户先管理候选、观察、小额试跟、冷却和淘汰,再明确决定是否创建保守的小额跟单配置。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 新增 `Leader 池` 页面入口,放在左侧 `跟单交易` 分组下,位于 `跟单配置` 与 `Leader 管理` 之间。
|
||||
- 新增 leader 池能力,支持把已有 leader 加入池子,并维护候选、观察、小额试跟、冷却、淘汰等状态。
|
||||
- 新增池子预算与风险概览,帮助用户看到试跟人数、建议最坏暴露、待处理风险等组合级信息。
|
||||
- 新增从池子创建保守小额跟单配置的动作,默认使用固定金额、小日单数、小日亏损、价格区间和单 leader 持仓上限。
|
||||
- 在 `Leader 管理` 中增加“加入 Leader 池”的辅助入口,避免用户来回复制地址。
|
||||
- 保留现有 `Leader 管理`、`跟单配置`、统计页和风险安全带,不替换现有工作流。
|
||||
- 不做自动加仓、不做自动启用大额跟单、不做 AI 预测评分。
|
||||
- 不在 v1 自动抓取 Polymarket leaderboard。leaderboard 自动发现后续可接入池子,但不属于本次实现。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `leader-pool`: 管理用于小仓位摊大饼策略的 leader 候选池,包括状态流转、保守试跟配置创建、组合预算提示和与现有 leader/跟单配置的联动。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
无。
|
||||
|
||||
## Impact
|
||||
|
||||
- 后端新增 leader 池实体、迁移、仓库、DTO、服务和控制器。
|
||||
- 后端复用现有 `LeaderService`、`CopyTradingService`、`CopyTradingRepository`,避免重复实现 leader 校验和跟单配置创建逻辑。
|
||||
- 前端新增 `LeaderPool` 页面、路由、菜单项、API service、类型定义和中英文菜单文案。
|
||||
- 前端 `LeaderList` 增加“加入 Leader 池”操作。
|
||||
- 数据库新增 `copy_trading_leader_pool` 表,不修改现有 leader 和跟单配置表的语义。
|
||||
- 交易安全影响:本功能可能创建真实跟单配置,因此必须使用保守默认值、显式确认和非自动加仓策略。
|
||||
@@ -0,0 +1,167 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 提供 Leader 池入口
|
||||
系统 SHALL 在受保护的 Web 应用中提供 `Leader 池` 页面入口,用于管理小仓位摊大饼策略的 leader 候选池。
|
||||
|
||||
#### Scenario: 左侧菜单展示 Leader 池
|
||||
- **WHEN** 已登录用户打开任意受保护页面
|
||||
- **THEN** 左侧 `跟单交易` 菜单中 MUST 展示 `Leader 池`,并位于 `跟单配置` 与 `Leader 管理` 之间
|
||||
|
||||
#### Scenario: 打开 Leader 池页面
|
||||
- **WHEN** 用户点击左侧菜单中的 `Leader 池`
|
||||
- **THEN** 系统 MUST 导航到 `/leader-pool` 并展示 Leader 池页面
|
||||
|
||||
### Requirement: 管理池子成员
|
||||
系统 SHALL 支持把已有 leader 加入 Leader 池,并保证同一个 leader 在池子中最多只有一个池子项。
|
||||
|
||||
#### Scenario: 从 Leader 管理加入池子
|
||||
- **WHEN** 用户在 `Leader 管理` 页面点击某个 leader 的 `加入 Leader 池`
|
||||
- **THEN** 系统 MUST 为该 leader 创建池子项,并默认状态为 `CANDIDATE`
|
||||
|
||||
#### Scenario: 重复加入同一 leader
|
||||
- **WHEN** 用户尝试把已经在池子中的 leader 再次加入池子
|
||||
- **THEN** 系统 MUST 不创建重复池子项,并 MUST 返回明确提示
|
||||
|
||||
#### Scenario: 并发重复加入同一 leader
|
||||
- **WHEN** 两个请求同时把同一个 leader 加入池子
|
||||
- **THEN** 系统 MUST 最多创建一个池子项,并 MUST 对冲突请求返回已在池子中的明确提示
|
||||
|
||||
#### Scenario: 加入不存在的 leader
|
||||
- **WHEN** 用户请求把不存在的 leader 加入池子
|
||||
- **THEN** 系统 MUST 拒绝请求,并 MUST 返回 leader 不存在的错误
|
||||
|
||||
### Requirement: 维护池子状态
|
||||
系统 SHALL 支持维护 leader 池状态,用于区分候选、观察、小额试跟、冷却和淘汰。
|
||||
|
||||
#### Scenario: 更新池子状态
|
||||
- **WHEN** 用户把某个池子项从 `CANDIDATE` 更新为 `WATCH`、`TRIAL`、`COOLDOWN` 或 `RETIRED`
|
||||
- **THEN** 系统 MUST 保存新状态并更新该池子项的更新时间
|
||||
|
||||
#### Scenario: 冷却状态包含冷却截止时间
|
||||
- **WHEN** 用户把某个池子项设置为 `COOLDOWN` 并提供冷却截止时间
|
||||
- **THEN** 系统 MUST 保存 `cooldownUntil`
|
||||
|
||||
#### Scenario: 淘汰不删除 leader 地址
|
||||
- **WHEN** 用户把某个池子项设置为 `RETIRED`
|
||||
- **THEN** 系统 MUST 保留对应 `copy_trading_leaders` 记录,不得删除 leader 地址
|
||||
|
||||
### Requirement: 展示池子概览
|
||||
系统 SHALL 在 Leader 池页面展示组合级概览,帮助用户理解小仓位策略的风险暴露。
|
||||
|
||||
#### Scenario: 展示统计卡片
|
||||
- **WHEN** 用户打开 Leader 池页面
|
||||
- **THEN** 页面 MUST 展示池子人数、试跟中人数、估算最坏暴露和待处理风险
|
||||
|
||||
#### Scenario: 估算最坏暴露
|
||||
- **WHEN** 池子中存在 `TRIAL` 或 `ACTIVE` 状态项
|
||||
- **THEN** 系统 MUST 使用这些项的建议最大持仓值合计估算最坏暴露
|
||||
|
||||
#### Scenario: 池子为空
|
||||
- **WHEN** 池子没有任何成员
|
||||
- **THEN** 页面 MUST 展示空态,并 MUST 提示用户可以从 `Leader 管理` 加入 leader
|
||||
|
||||
### Requirement: 展示池子列表信息
|
||||
系统 SHALL 在 Leader 池列表中展示池子状态、leader 基础信息、建议配置和现有跟单配置状态。
|
||||
|
||||
#### Scenario: 列表展示 leader 和状态
|
||||
- **WHEN** 用户打开 Leader 池页面且池子中存在成员
|
||||
- **THEN** 列表 MUST 展示 leader 名称或地址、池子状态、来源、建议固定金额、建议每日最大单数、建议每日最大亏损和最后复核时间
|
||||
|
||||
#### Scenario: 展示现有跟单配置状态
|
||||
- **WHEN** 某个池子 leader 已经存在跟单配置
|
||||
- **THEN** 列表 MUST 展示该 leader 的跟单配置数量,并 MUST 标明是否存在启用中的跟单配置
|
||||
|
||||
#### Scenario: 打开外部 profile
|
||||
- **WHEN** 用户点击池子项中的 Polymarket profile 操作
|
||||
- **THEN** 系统 MUST 打开该 leader 对应的 Polymarket profile 地址
|
||||
|
||||
#### Scenario: 池子列表不默认逐行查询余额
|
||||
- **WHEN** 用户打开 Leader 池页面且池子中存在多个成员
|
||||
- **THEN** 页面 MUST NOT 为每个池子项默认逐行请求 leader 余额接口
|
||||
|
||||
### Requirement: 创建保守小额试跟配置
|
||||
系统 SHALL 支持从 Leader 池为某个 leader 创建保守的小额跟单配置,并复用现有跟单配置创建逻辑。
|
||||
|
||||
#### Scenario: 创建默认禁用的试跟配置
|
||||
- **WHEN** 用户从池子项点击创建小额试跟配置并完成确认
|
||||
- **THEN** 系统 MUST 创建 `FIXED` 模式跟单配置,默认固定金额为 1,默认启用状态为禁用
|
||||
|
||||
#### Scenario: 使用保守风控默认值
|
||||
- **WHEN** 系统从池子创建小额试跟配置
|
||||
- **THEN** 创建请求 MUST 使用保守默认值,包括每日最大单数 10、每日最大亏损 5、价格容忍度 1、最低价格 0.10、最高价格 0.80、最大持仓 5
|
||||
|
||||
#### Scenario: 创建成功后更新池子状态
|
||||
- **WHEN** 小额试跟配置创建成功
|
||||
- **THEN** 系统 MUST 把池子项状态更新为 `TRIAL`,并 MUST 记录最后晋升时间
|
||||
|
||||
#### Scenario: 创建失败不更新状态
|
||||
- **WHEN** 小额试跟配置创建失败
|
||||
- **THEN** 系统 MUST 保留原池子状态,并 MUST 向用户展示失败原因
|
||||
|
||||
### Requirement: 保护真钱交易安全
|
||||
系统 SHALL 防止 Leader 池功能在没有明确用户动作的情况下扩大真实交易风险。
|
||||
|
||||
#### Scenario: 不自动创建跟单配置
|
||||
- **WHEN** 用户仅把 leader 加入池子或更新池子状态
|
||||
- **THEN** 系统 MUST NOT 自动创建真实跟单配置
|
||||
|
||||
#### Scenario: 不自动启用大额跟单
|
||||
- **WHEN** 系统从池子创建小额试跟配置
|
||||
- **THEN** 系统 MUST NOT 自动创建大额配置,并 MUST 使用保守小额参数
|
||||
|
||||
#### Scenario: 立即启用需要确认
|
||||
- **WHEN** UI 提供创建后立即启用选项
|
||||
- **THEN** UI MUST 在提交前展示固定金额、每日最大单数、每日最大亏损和最大持仓,并 MUST 要求用户确认
|
||||
|
||||
#### Scenario: 后端拒绝未确认的立即启用
|
||||
- **WHEN** 创建试跟配置请求要求立即启用但没有提供确认标记
|
||||
- **THEN** 后端 MUST 拒绝请求,并 MUST NOT 创建跟单配置
|
||||
|
||||
### Requirement: 支持更新建议配置
|
||||
系统 SHALL 支持用户更新池子项的建议固定金额和建议风控参数,但不得直接修改已有真实跟单配置。
|
||||
|
||||
#### Scenario: 更新建议配置
|
||||
- **WHEN** 用户更新池子项的建议固定金额、每日最大单数、每日最大亏损、价格区间或最大持仓
|
||||
- **THEN** 系统 MUST 保存这些建议字段,并 MUST 更新池子项的更新时间
|
||||
|
||||
#### Scenario: 建议配置不影响已有跟单
|
||||
- **WHEN** 用户只更新池子项建议配置
|
||||
- **THEN** 系统 MUST NOT 修改任何已有 `copy_trading` 记录
|
||||
|
||||
#### Scenario: 拒绝无效建议配置
|
||||
- **WHEN** 用户提交负数固定金额、无效每日最大单数、负数每日亏损、无效价格区间或负数最大持仓
|
||||
- **THEN** 系统 MUST 拒绝保存,并 MUST 保留原建议配置不变
|
||||
|
||||
### Requirement: 提供后端 API
|
||||
系统 SHALL 提供 Leader 池后端 API,用于列表查询、加入池子、状态更新、建议配置更新、创建试跟配置和移除池子项。
|
||||
|
||||
#### Scenario: 查询池子列表
|
||||
- **WHEN** 前端请求 `/api/copy-trading/leader-pool/list`
|
||||
- **THEN** 后端 MUST 返回池子项列表、汇总信息和每个池子项的 leader/跟单配置聚合信息
|
||||
|
||||
#### Scenario: 池子列表使用批量聚合
|
||||
- **WHEN** 后端生成池子列表响应
|
||||
- **THEN** 后端 MUST 使用批量查询或聚合查询获取 leader 与跟单配置信息,不得对每个池子项逐条查询 leader 和跟单配置
|
||||
|
||||
#### Scenario: 创建试跟配置 API
|
||||
- **WHEN** 前端请求 `/api/copy-trading/leader-pool/create-trial-config`
|
||||
- **THEN** 后端 MUST 校验池子项、账户和 leader,并 MUST 通过现有跟单配置服务创建配置
|
||||
|
||||
#### Scenario: 已存在同账户同 leader 跟单配置
|
||||
- **WHEN** 用户为某个账户和 leader 创建试跟配置且该账户已存在该 leader 的跟单配置
|
||||
- **THEN** 系统 MUST 拒绝默认试跟创建,并 MUST 返回已有配置提示
|
||||
|
||||
#### Scenario: 移除池子项
|
||||
- **WHEN** 用户请求移除某个池子项
|
||||
- **THEN** 系统 MUST 只移除池子项,不得删除 leader 地址,不得删除已有跟单配置
|
||||
|
||||
### Requirement: 记录关键操作日志
|
||||
系统 SHALL 对 Leader 池关键操作记录后端日志,便于排查交易相关行为。
|
||||
|
||||
#### Scenario: 记录加入和状态变化
|
||||
- **WHEN** leader 被加入池子或池子状态发生变化
|
||||
- **THEN** 后端 MUST 记录包含 leaderId、池子项 ID 和目标状态的日志
|
||||
|
||||
#### Scenario: 记录试跟配置创建结果
|
||||
- **WHEN** 系统尝试从池子创建小额试跟配置
|
||||
- **THEN** 后端 MUST 记录创建成功或失败日志,并包含 leaderId、accountId 和错误原因
|
||||
@@ -0,0 +1,95 @@
|
||||
## 1. 后端数据模型
|
||||
|
||||
- [x] 1.1 新增 Flyway 迁移 `V41__create_copy_trading_leader_pool.sql`,创建 `copy_trading_leader_pool` 表、`leader_id` 唯一约束、状态/来源索引和 leader 外键。
|
||||
- [x] 1.2 新增 `LeaderPool` JPA 实体,字段覆盖状态、来源、建议配置、复核时间、晋升时间、冷却时间、锁定标记和时间戳。
|
||||
- [x] 1.3 新增 `LeaderPoolRepository`,支持按 leaderId 查询、判断是否存在、按状态查询、按创建时间排序和删除池子项。
|
||||
- [x] 1.4 新增池子状态常量或枚举,覆盖 `CANDIDATE`、`WATCH`、`PAPER`、`TRIAL`、`ACTIVE`、`COOLDOWN`、`RETIRED`;锁定使用独立 `locked` 布尔字段,不作为状态。
|
||||
- [x] 1.5 扩展 `CopyTradingRepository` 或新增查询方法,支持按 leaderId 批量查询/聚合跟单配置,避免 Leader 池列表 N+1 查询。
|
||||
|
||||
## 2. 后端 DTO 与服务
|
||||
|
||||
- [x] 2.1 新增 Leader 池请求/响应 DTO,包括列表请求、加入请求、状态更新请求、建议配置更新请求、创建试跟配置请求和移除请求。
|
||||
- [x] 2.2 为创建试跟配置请求增加 `enableImmediately` 与 `confirm` 字段;当 `enableImmediately=true` 且未确认时后端必须拒绝创建。
|
||||
- [x] 2.3 新增 `LeaderPoolService.addToPool`,校验 leader 存在、处理重复加入,并默认创建 `CANDIDATE` 状态池子项。
|
||||
- [x] 2.4 在 `addToPool` 中捕获数据库唯一约束冲突,并返回“已在池子中”的明确业务结果,覆盖并发重复加入。
|
||||
- [x] 2.5 新增 `LeaderPoolService.getPoolList`,使用批量查询聚合 leader 信息、池子状态、建议配置、跟单配置数量、是否存在启用跟单和汇总概览。
|
||||
- [x] 2.6 新增 `LeaderPoolService.updateStatus`,支持状态更新、冷却截止时间保存、更新时间刷新,并确保淘汰不删除 leader 地址。
|
||||
- [x] 2.7 新增 `LeaderPoolService.updatePlan`,只更新建议固定金额、每日最大单数、每日最大亏损、价格区间和最大持仓,不修改已有 `copy_trading`。
|
||||
- [x] 2.8 新增 `LeaderPoolService.createTrialConfig`,先检查同一 `accountId + leaderId` 是否已有跟单配置,默认拒绝重复创建。
|
||||
- [x] 2.9 `createTrialConfig` 复用 `CopyTradingService.createCopyTrading` 创建默认禁用的保守 `FIXED` 小额跟单配置。
|
||||
- [x] 2.10 创建试跟配置成功后更新池子项为 `TRIAL` 并记录 `lastPromotedAt`;失败时保持原状态并返回错误。
|
||||
- [x] 2.11 在 Leader 池服务中记录加入池子、状态变化、建议配置更新、试跟配置创建成功/失败的关键日志。
|
||||
- [x] 2.12 在 `updatePlan` 和 `createTrialConfig` 中校验建议配置安全边界:固定金额大于 0、每日最大单数 1-100、每日最大亏损大于 0、价格区间在 0-1 且最小价不大于最大价、最大持仓大于 0。
|
||||
- [x] 2.13 新增 Leader 池专用错误码和中英繁三套 i18n 文案,使用不冲突编号 `4251-4254` 与 `5451-5453`,覆盖池子不存在、已存在、重复试跟、立即启用未确认和服务失败。
|
||||
|
||||
## 3. 后端 API
|
||||
|
||||
- [x] 3.1 新增 `LeaderPoolController`,路径为 `/api/copy-trading/leader-pool`。
|
||||
- [x] 3.2 实现 `POST /list`,返回池子列表和汇总概览。
|
||||
- [x] 3.3 实现 `POST /add`,支持从已有 leader 加入池子。
|
||||
- [x] 3.4 实现 `POST /update-status`,支持候选、观察、小额试跟、冷却、淘汰等状态更新。
|
||||
- [x] 3.5 实现 `POST /update-plan`,支持更新建议配置字段。
|
||||
- [x] 3.6 实现 `POST /create-trial-config`,支持创建保守小额试跟配置。
|
||||
- [x] 3.7 实现 `POST /remove`,只移除池子项,不删除 leader 地址,不删除已有跟单配置。
|
||||
- [x] 3.8 为重复加入、并发唯一约束冲突、leader 不存在、账户不存在、池子项不存在、立即启用未确认、重复试跟配置、创建配置失败等错误返回明确业务错误。
|
||||
|
||||
## 4. 前端 API 与类型
|
||||
|
||||
- [x] 4.1 在 `frontend/src/types/index.ts` 新增 Leader 池状态、池子项、汇总、请求和响应类型。
|
||||
- [x] 4.2 在 `frontend/src/services/api.ts` 新增 `leaderPool` API 分组,覆盖 list、add、updateStatus、updatePlan、createTrialConfig、remove。
|
||||
- [x] 4.3 确保前端类型包含跟单配置数量、是否有启用跟单、建议最大持仓和估算暴露字段。
|
||||
|
||||
## 5. 前端入口与页面
|
||||
|
||||
- [x] 5.1 新增 `frontend/src/pages/LeaderPool.tsx` 页面。
|
||||
- [x] 5.2 在 `frontend/src/App.tsx` 添加受保护路由 `/leader-pool`。
|
||||
- [x] 5.3 在 `frontend/src/components/Layout.tsx` 的 `跟单交易` 子菜单中加入 `Leader 池`,位置在 `跟单配置` 和 `Leader 管理` 之间。
|
||||
- [x] 5.4 更新 `getInitialOpenKeys` 和路径变化逻辑,使 `/leader-pool` 自动展开 `跟单交易` 菜单。
|
||||
- [x] 5.5 在 `frontend/src/locales/zh-CN/common.json`、`zh-TW/common.json`、`en/common.json` 增加 Leader 池菜单和页面基础文案。
|
||||
|
||||
## 6. Leader 池页面交互
|
||||
|
||||
- [x] 6.1 页面顶部展示池子人数、试跟中人数、估算最坏暴露和待处理风险统计卡片。
|
||||
- [x] 6.2 页面列表展示 leader 名称或地址、状态、来源、建议固定金额、建议每日最大单数、建议每日最大亏损、跟单配置状态和最后复核时间。
|
||||
- [x] 6.3 支持按状态筛选池子项,至少覆盖全部、候选、观察、小额试跟、冷却、淘汰。
|
||||
- [x] 6.4 支持从页面中把已有 leader 加入池子,并处理空列表、重复加入和 leader 不存在错误。
|
||||
- [x] 6.5 支持更新池子状态,冷却状态允许填写冷却截止时间。
|
||||
- [x] 6.6 支持编辑建议配置,保存后只更新池子建议字段。
|
||||
- [x] 6.7 支持打开 leader 的 Polymarket profile。
|
||||
- [x] 6.8 支持从池子项创建小额试跟配置,提交前展示固定金额、每日最大单数、每日最大亏损、价格区间和最大持仓确认信息。
|
||||
- [x] 6.9 创建试跟配置提交期间按钮进入 loading/disabled 状态,防止双击重复提交。
|
||||
- [x] 6.10 创建试跟配置成功后刷新池子列表,并提供跳转到 `跟单配置` 的入口。
|
||||
- [x] 6.11 创建试跟配置失败时展示后端错误,不在前端假更新状态。
|
||||
- [x] 6.12 Leader 池列表不默认逐行查询 leader 余额,避免照搬 `Leader 管理` 的重型余额加载路径。
|
||||
|
||||
## 7. Leader 管理联动
|
||||
|
||||
- [x] 7.1 在 `frontend/src/pages/LeaderList.tsx` 操作列增加 `加入 Leader 池` 操作。
|
||||
- [x] 7.2 点击加入后调用 Leader 池 add API,并对已加入池子的 leader 展示明确提示。
|
||||
- [x] 7.3 加入成功后提示用户可以前往 `Leader 池` 继续设置观察或小额试跟。
|
||||
|
||||
## 8. 测试
|
||||
|
||||
- [x] 8.1 新增后端服务测试:成功加入池子、重复加入不创建重复项、leader 不存在返回错误。
|
||||
- [x] 8.2 新增后端服务测试:并发/唯一约束重复加入被转换为明确“已在池子中”结果。
|
||||
- [x] 8.3 新增后端服务测试:状态更新、冷却截止时间保存、淘汰不删除 leader 地址。
|
||||
- [x] 8.4 新增后端服务测试:建议配置更新不修改已有 `copy_trading`。
|
||||
- [x] 8.5 新增后端服务测试:无效建议配置被拒绝且不修改原池子项。
|
||||
- [x] 8.6 新增后端服务测试:创建试跟配置使用保守默认值、默认禁用、成功后状态变为 `TRIAL`。
|
||||
- [x] 8.7 新增后端服务测试:创建试跟配置失败时池子状态不变。
|
||||
- [x] 8.8 新增后端服务测试:同账户同 leader 已有跟单配置时默认拒绝重复试跟创建。
|
||||
- [x] 8.9 新增后端服务测试:立即启用但未确认时拒绝创建跟单配置。
|
||||
- [x] 8.10 新增后端服务测试:池子列表使用批量查询路径,避免每个池子项逐条查 leader 和跟单配置。
|
||||
- [x] 8.11 新增后端控制器测试或集成测试,覆盖 list、add、update-status、update-plan、create-trial-config、remove 的主要成功和失败路径。
|
||||
- [x] 8.12 前端至少通过 TypeScript 构建验证,确保新增页面、类型、API 和菜单没有编译错误。
|
||||
- [x] 8.13 手动或自动验证前端双击创建试跟配置不会发起重复提交。
|
||||
- [x] 8.14 手动或自动验证 Leader 池列表不会默认逐行请求 leader 余额接口。
|
||||
|
||||
## 9. 文档与验证
|
||||
|
||||
- [x] 9.1 新增或更新中文文档,说明 `Leader 池` 与 `Leader 管理`、`跟单配置` 的区别。
|
||||
- [x] 9.2 文档中明确 `Leader 池` 不会自动加仓、不自动启用大额跟单、不自动抓取 leaderboard。
|
||||
- [x] 9.3 运行后端测试,至少覆盖 Leader 池相关测试。
|
||||
- [x] 9.4 运行前端构建。
|
||||
- [x] 9.5 运行前端 lint。
|
||||
- [x] 9.6 手动验证页面入口、空态、加入池子、状态更新、建议配置更新和创建小额试跟配置流程。
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-04-28
|
||||
@@ -0,0 +1,113 @@
|
||||
## Context
|
||||
|
||||
当前分支已经引入真实 PnL 统计基础:`CopyTradingPnlCalculator` 可以把已实现 PnL、未实现 PnL、当前持仓成本和当前持仓估值拆开,`CopyTradingStatisticsService` 会从买入订单、卖出匹配和持仓报价计算单个跟单配置的统计结果。
|
||||
|
||||
实盘排查显示,当前亏损主要来自三类问题:leader 参与的短周期体育市场大量归零,部分 leader 留下估值为 0 的未平仓,跟单配置几乎没有使用现有风控字段。当前统计还有一个关键歧义:当持仓/报价接口失败时,服务返回空 quotes,而计算器会把未匹配报价按 0 估值;这会把“真实归零”和“数据不可用”混在一起。
|
||||
|
||||
本变更面向真实资金部署,首要目标是止血和解释,不是追求高收益自动化。本阶段只做亏损诊断、报价状态、安全配置建议和人工确认,不做 leader 池状态机、leader 自动推荐或定时榜单同步。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- 给每个跟单配置生成亏损归因:已实现亏损、未实现亏损、归零亏损、当前持仓成本/估值、top losing markets、订单数量和样本量。
|
||||
- 识别危险配置并给出保守参数建议,优先复用现有 `CopyTrading` 风控字段。
|
||||
- 明确区分真实 0 估值、无匹配报价、持仓/报价接口不可用,避免把未知数据当成亏损事实。
|
||||
- 默认只提供建议和手动确认入口,不自动新增 leader、不自动删除 leader、不自动加仓。
|
||||
- 为 Mac mini Docker 部署保留可观测日志和失败降级信息。
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- 不承诺盈利,不做收益预测或投资建议。
|
||||
- 不做 leader 池状态机、leader 推荐记录、定时 leaderboard 候选刷新、全自动 leader 轮换、全自动加仓、复杂投资组合优化或跨钱包资金调度。
|
||||
- 不引入非官方私有数据源。
|
||||
- 不重写现有跟单执行引擎;本变更只在统计、诊断、建议和操作确认层加安全带。
|
||||
|
||||
## Decisions
|
||||
|
||||
### Decision 1: 用“诊断层”扩展现有统计,而不是把归因写死在 UI
|
||||
|
||||
新增后端诊断服务,例如 `CopyTradingRiskDiagnosisService`,从订单跟踪、卖出匹配、filtered orders、当前持仓报价和现有 PnL 统计中生成结构化诊断 DTO。UI 只负责展示,不在前端重新计算核心财务口径。
|
||||
|
||||
原因:PnL 归因属于业务口径,必须可测试、可复用。只在前端计算会导致统计页、确认弹窗和后续任务口径不一致。
|
||||
|
||||
替代方案:直接在 `CopyTradingStatisticsService` 内继续堆字段。这个方案改动少,但会让统计服务同时承担列表分页、持仓报价、PnL、风险建议和诊断解释,后续很难测试和维护。
|
||||
|
||||
### Decision 2: 持仓估值改为三态语义
|
||||
|
||||
持仓估值必须携带报价状态:
|
||||
|
||||
- `AVAILABLE`: 成功拿到当前价格,价格可能大于 0,也可能等于 0。
|
||||
- `NO_MATCH`: 持仓接口成功,但未找到对应 market/outcome 的报价。
|
||||
- `UNAVAILABLE`: 持仓/报价接口失败或超时。
|
||||
|
||||
只有 `AVAILABLE` 且当前价格为 0 时,UI 和诊断才能称为“当前估值为 0”。`UNAVAILABLE` 必须显示为“报价不可用”,并且 leader 推荐任务不得用这部分未知估值直接判定 leader 归零亏损。
|
||||
|
||||
原因:Mac mini 日志里存在持仓查询 timeout;如果继续把接口失败当作 0,系统会高估浮亏并产生错误换池建议。
|
||||
|
||||
替代方案:保持当前缺 quote 等于 0。这个方案对已结算归零市场友好,但对 API 故障过于危险。
|
||||
|
||||
### Decision 3: 风险安全带优先复用已有配置字段
|
||||
|
||||
危险配置识别和保守参数建议先基于已有字段实现:
|
||||
|
||||
- `maxDailyOrders`
|
||||
- `maxDailyLoss`
|
||||
- `minPrice`
|
||||
- `maxPrice`
|
||||
- `maxPositionValue`
|
||||
- `minOrderDepth`
|
||||
- `maxSpread`
|
||||
- `priceTolerance`
|
||||
- `supportSell`
|
||||
|
||||
新增能力先提供“建议值”和“一键确认保存”,不直接修改运行中的配置。保存动作走后端显式确认入口,只允许写入白名单风控字段,并在 UI 中明确展示变更前后差异。
|
||||
|
||||
原因:当前真实亏损已经证明已有字段没有被充分使用,先把这些字段变成可理解、可确认的安全带,比新增一套风控模型更快、更稳。
|
||||
|
||||
替代方案:新增独立 risk profile 表。长期可能有价值,但第一阶段会增加迁移和配置同步复杂度。
|
||||
|
||||
### Decision 4: 本阶段明确不做 leader 池状态机
|
||||
|
||||
leader 池轮换留到第二阶段。第一阶段只在诊断里给出人能读懂的风险结论,例如“样本太小,不建议加仓”“亏损集中在归零市场”“配置过松,先收紧风控”。不创建 `ACTIVE/WATCH/COOLDOWN/RETIRED/LOCKED` 状态,不接入官方 leaderboard,不新增定时推荐任务。
|
||||
|
||||
原因:当前最危险的问题不是缺少自动推荐,而是统计口径会把报价失败和真实归零混在一起,同时配置几乎没有安全限制。先修这两个问题,才有资格让系统推荐换人。
|
||||
|
||||
替代方案:本阶段直接做 leader 状态机和候选发现。这个方案看起来完整,但会把真实资金风控、外部榜单可靠性、状态迁移和 UI 操作全混在一个 PR 里,风险太大。
|
||||
|
||||
### Decision 5: UI 做成“黑盒记录仪 + 安全带面板”
|
||||
|
||||
在跟单配置列表或统计弹窗中新增:
|
||||
|
||||
- PnL 分解卡片:已实现、未实现、总 PnL、持仓成本、持仓估值。
|
||||
- 亏损归因区块:归零亏损、top losing markets、open position 风险、报价状态。
|
||||
- 风险配置体检:危险项、建议值、为什么危险。
|
||||
- 手动确认按钮:应用保守配置,取消时不发保存请求。
|
||||
|
||||
原因:这不是普通数据大屏,而是操作者决策台。用户要快速知道“为什么亏、现在要不要停、下一步怎么更安全”。
|
||||
|
||||
替代方案:单独新增复杂 dashboard。长期可以做,但第一阶段应嵌入现有跟单配置和统计路径,降低使用成本。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [报价不可用被误判] → 所有估值结果必须携带 quote status;遇到 `UNAVAILABLE` 时降级为“数据不足”,不得生成强结论。
|
||||
- [诊断给人虚假确定性] → 诊断文案必须展示样本量、时间窗口、报价状态和数据完整性;低样本必须明确标低置信度。
|
||||
- [统计查询变慢] → 诊断服务优先新增 repository 聚合查询和必要索引,避免在大订单量下把所有订单加载到内存后过滤。
|
||||
- [scope 膨胀] → 第一阶段只做亏损归因、风险提示和手动确认;leader 状态机、leader 推荐、自动轮换、自动加仓、组合优化留到后续 change。
|
||||
- [误改真实交易配置] → “应用保守配置”必须显示 diff 并需要人工确认;后端保存前再次校验参数范围。
|
||||
- [主线 CLOB V2/pUSD 变更漂移] → 实施前需要把当前分支和最新 `origin/main` 对齐,确认金额单位、pUSD/$ 展示和 CLOB V2 持仓口径没有冲突。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. 第一阶段不新增业务表;如实现中确认需要性能索引,只新增 append-only Flyway migration。
|
||||
2. 后端先发布兼容 API:旧统计字段保留,新诊断字段可为空;报价不可用时返回状态而不是抛出页面级错误。
|
||||
3. 前端灰度展示诊断区块:没有诊断数据时显示空态,不影响现有跟单列表。
|
||||
4. “应用保守配置”必须走后端显式确认入口;取消、未确认或非白名单字段混入时不得写库。
|
||||
5. Mac mini 部署后先观察 24-72 小时日志和诊断显示,确认没有把 `UNAVAILABLE` 展示成 0 亏损。
|
||||
6. 回滚时隐藏诊断 UI,并保留旧统计字段兼容;如果只新增索引无需回滚数据。
|
||||
|
||||
## Open Questions
|
||||
|
||||
- 保守参数默认值第一版使用固定区间:`maxDailyOrders=10-20`、`maxDailyLoss=5-10`、`minPrice=0.10`、`maxPrice=0.80`、`maxPositionValue=5-10`;未来再考虑按账户余额比例动态生成。
|
||||
- “应用保守配置”第一版采用后端显式确认入口。前端展示后端返回的 diff,用户确认后请求必须携带 `confirm=true`,后端只允许保存白名单风控字段。
|
||||
- 是否要在第一版加入通知推送,还是先只在 UI 中展示诊断和安全带建议。
|
||||
@@ -0,0 +1,32 @@
|
||||
## Why
|
||||
|
||||
当前跟单亏损已经不是单纯“leader 选错”能解释的问题。实盘排查显示,RN1 和 swisstony 的亏损与短周期体育市场归零、未平仓估值归零、以及过松的风控配置共同相关;如果直接做自动 leader 轮换,系统可能只是把风险从一个 leader 换到另一个 leader。
|
||||
|
||||
现在需要先把 PolyHermes 从“忠实执行跟单”升级为“带安全带的操作者控制台”:能解释亏损、识别危险配置、给出保守参数建议,并把任何真实配置修改都放在明确确认和可审计边界内。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 新增跟单亏损归因能力:区分已实现 PnL、未实现 PnL、持仓成本、持仓估值、归零亏损、报价不可用、top losing markets 等关键原因。
|
||||
- 新增风险安全带能力:对危险跟单配置给出提示和保守参数建议,优先复用已有 `maxDailyOrders`、`maxDailyLoss`、`minPrice`、`maxPrice`、`maxPositionValue`、`minOrderDepth`、`maxSpread` 等字段。
|
||||
- 新增 operator-facing UI:在跟单统计/配置页显示亏损归因、风险提示、保守参数建议和证据来源。
|
||||
- 新增审计和降级要求:当 Polymarket 持仓/报价接口失败时,必须明确显示“数据不可用”,不能把未知估值直接当作 0 亏损。
|
||||
- 暂不做 leader 池状态机、leader 自动推荐、定时榜单同步、全自动加仓、全自动删除 leader、复杂资产组合优化、跨钱包资金调度或收益承诺。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `copy-trading-risk-seatbelt`: 覆盖跟单亏损归因、危险配置识别、保守参数建议、报价不可用处理和操作者确认边界。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- None.
|
||||
|
||||
## Impact
|
||||
|
||||
- 后端:影响 copy-trading statistics、PnL calculator、config service、repository 聚合查询、API response DTO。
|
||||
- 前端:影响跟单配置管理、统计弹窗/统计页、风险提示和建议确认 UI。
|
||||
- 数据库:第一阶段不新增业务表;如发现查询瓶颈,只追加必要索引,不创建 leader 状态/推荐记录/诊断快照表。
|
||||
- 外部数据:继续使用现有 Polymarket 持仓/报价数据;第一阶段不接入 leaderboard 候选发现。
|
||||
- 运维:Mac mini Docker 部署需要可观测日志、报价失败降级和手动确认边界。
|
||||
- 测试:需要覆盖真实 PnL 归因、报价失败、危险配置识别、UI 空态/错误态,以及不自动执行配置变更的安全约束。
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 跟单亏损归因
|
||||
系统必须为每个跟单配置提供结构化亏损归因,并拆分已实现 PnL、未实现 PnL、当前持仓成本、当前持仓估值、归零亏损、未平仓暴露、订单数量和样本量。
|
||||
|
||||
#### Scenario: 查看亏损跟单配置诊断
|
||||
- **WHEN** 用户打开一个包含买入订单、卖出匹配和未平仓的跟单配置统计或诊断页面
|
||||
- **THEN** 系统必须展示已实现 PnL、未实现 PnL、总 PnL、持仓成本、持仓估值、总订单数、已匹配订单数、未平仓数量和亏损最大的市场
|
||||
|
||||
#### Scenario: 查看小样本盈利配置
|
||||
- **WHEN** 某个跟单配置 PnL 为正,但订单数量低于配置的最小样本量
|
||||
- **THEN** 系统必须标记为低置信度,并且不能把该 leader 或配置描述成已经被验证盈利
|
||||
|
||||
### Requirement: 报价可用性状态
|
||||
系统必须区分真实归零持仓、未匹配报价数据、以及持仓或报价接口不可用。
|
||||
|
||||
#### Scenario: 报价接口成功且当前价格为零
|
||||
- **WHEN** 一个未平仓持仓匹配到报价,并且当前价格等于 0
|
||||
- **THEN** 系统必须把估值状态标记为 `AVAILABLE`,并允许该持仓计入归零亏损诊断
|
||||
|
||||
#### Scenario: 报价接口失败或超时
|
||||
- **WHEN** 账户持仓接口或报价接口失败、超时、或返回错误
|
||||
- **THEN** 系统必须把受影响的未平仓估值状态标记为 `UNAVAILABLE`,并且不能把缺失估值当作已确认的归零亏损
|
||||
|
||||
#### Scenario: 报价接口成功但没有匹配市场结果
|
||||
- **WHEN** 报价接口成功返回数据,但没有找到与跟单记录中的 market 和 outcome 对应的报价
|
||||
- **THEN** 系统必须把受影响的持仓状态标记为 `NO_MATCH`,并在诊断响应中暴露该状态
|
||||
|
||||
### Requirement: 风险配置体检
|
||||
系统必须在继续跟单或调整 leader 之前,先基于现有风控字段评估每个跟单配置是否危险。
|
||||
|
||||
#### Scenario: 配置风险限制过松
|
||||
- **WHEN** 某个跟单配置存在过高的 `maxDailyOrders`、过高的 `maxDailyLoss`、缺失的 `minPrice`、缺失的 `maxPrice`、缺失的 `maxPositionValue`、缺失的 `minOrderDepth` 或缺失的 `maxSpread`
|
||||
- **THEN** 系统必须把这些字段列为风险警告,并提供保守建议值和简短原因
|
||||
|
||||
#### Scenario: 配置已经较保守
|
||||
- **WHEN** 某个跟单配置已经设置了保守的每日订单数、每日亏损、价格区间和仓位暴露限制
|
||||
- **THEN** 系统必须显示对应风险检查已通过,并且不能无意义地要求用户继续修改这些字段
|
||||
|
||||
### Requirement: 风险配置修改必须人工确认
|
||||
系统必须在应用任何推荐风险配置之前,要求操作者明确确认。
|
||||
|
||||
#### Scenario: 用户应用保守配置
|
||||
- **WHEN** 用户选择应用推荐的保守配置
|
||||
- **THEN** 系统必须展示字段变更前后的 diff,并在保存前要求用户确认
|
||||
|
||||
#### Scenario: 用户取消应用保守配置
|
||||
- **WHEN** 用户在确认弹窗中取消操作
|
||||
- **THEN** 系统必须保持原跟单配置不变
|
||||
|
||||
### Requirement: 诊断结果可审计
|
||||
系统必须让每次诊断结果可追溯,至少暴露时间窗口、来源计数、报价状态和生成时间。
|
||||
|
||||
#### Scenario: 生成诊断结果
|
||||
- **WHEN** 系统返回一个诊断结果
|
||||
- **THEN** 响应必须包含评估时间窗口、来源订单数、来源卖出匹配数、报价状态摘要和生成时间
|
||||
|
||||
#### Scenario: 诊断数据不完整
|
||||
- **WHEN** 任一必要来源数据不可用或部分不可用
|
||||
- **THEN** 诊断必须标出缺失来源,并降级为部分结果,不能把不完整数据展示成完整结论
|
||||
@@ -0,0 +1,70 @@
|
||||
## 0. 实施前同步
|
||||
|
||||
- [x] 0.1 在开始编码前把当前分支与最新 `origin/main` 对齐,重点确认 CLOB V2、pUSD/$ 展示、持仓接口和金额单位没有冲突
|
||||
- [x] 0.2 对齐后重新运行现有 `CopyTradingPnlCalculatorTest`,确保当前真实 PnL 基线没有先坏掉
|
||||
|
||||
## 1. 报价状态和 PnL 口径
|
||||
|
||||
- [x] 1.1 重构持仓估值返回结构,支持 `AVAILABLE`、`NO_MATCH`、`UNAVAILABLE` 三种报价状态
|
||||
- [x] 1.2 调整 `CopyTradingStatisticsService.buildPositionValuationQuotes`,接口失败或超时时返回 `UNAVAILABLE` 状态,不再返回空 quotes 伪装成 0 估值
|
||||
- [x] 1.3 调整 `CopyTradingPnlCalculator` 或其调用层,只有 `AVAILABLE` 且当前价格为 0 时才计入“已确认归零”;`NO_MATCH` 和 `UNAVAILABLE` 必须从诊断中暴露出来
|
||||
- [x] 1.4 保持现有统计字段向后兼容;如果存在未知估值,旧字段可以继续返回数值,但必须新增状态字段告诉前端该数值不是完整结论
|
||||
- [x] 1.5 更新或替换当前“无报价按 0”的单元测试,覆盖报价成功为 0、报价未匹配、报价接口不可用、混合持仓四种场景
|
||||
|
||||
## 2. 亏损诊断后端
|
||||
|
||||
- [x] 2.1 新增 `CopyTradingRiskDiagnosisService`,聚合买入订单、卖出匹配、filtered orders、当前持仓估值和真实 PnL 统计
|
||||
- [x] 2.2 实现诊断 DTO:已实现 PnL、未实现 PnL、总 PnL、持仓成本、持仓估值、归零亏损、未平仓暴露、订单数量、样本量、top losing markets、报价状态摘要、数据完整性和生成时间
|
||||
- [x] 2.3 实现低样本识别逻辑,避免把 sovereign2013 这种少量盈利订单标记成高置信度盈利
|
||||
- [x] 2.4 实现诊断降级逻辑:报价或来源数据不可用时返回部分结果,并明确列出缺失来源
|
||||
- [x] 2.5 优先用 repository 聚合查询计算 top losing markets 和来源计数;如果需要索引,只新增 append-only Flyway migration,不新增诊断快照表
|
||||
|
||||
## 3. 风险安全带后端
|
||||
|
||||
- [x] 3.1 新增风险配置体检逻辑,检查 `maxDailyOrders`、`maxDailyLoss`、`minPrice`、`maxPrice`、`maxPositionValue`、`minOrderDepth`、`maxSpread`、`priceTolerance` 和 `supportSell`
|
||||
- [x] 3.2 实现保守参数建议,第一版使用固定保守区间:`maxDailyOrders=10-20`、`maxDailyLoss=5-10`、`minPrice=0.10`、`maxPrice=0.80`、`maxPositionValue=5-10`
|
||||
- [x] 3.3 在风险建议中返回字段级原因、当前值、建议值和严重程度,并标明建议只是安全带,不是收益承诺
|
||||
- [x] 3.4 新增“应用保守配置”的后端显式确认入口,请求必须带 `confirm=true`,后端只允许保存白名单风控字段
|
||||
- [x] 3.5 确保取消、未确认、参数校验失败、非白名单字段混入时不会修改任何真实跟单配置
|
||||
|
||||
## 4. API 和 DTO
|
||||
|
||||
- [x] 4.1 新增或扩展跟单诊断 API,返回 PnL 分解、亏损归因、报价状态、风险配置体检、保守配置 diff 和生成时间
|
||||
- [x] 4.2 新增应用保守配置 API,接收 `copyTradingId`、`confirm=true` 和后端生成的建议字段,成功后返回更新后的跟单配置
|
||||
- [x] 4.3 保持现有统计 API 向后兼容,旧字段继续返回,新诊断字段缺失时前端能显示空态
|
||||
- [x] 4.4 为新增或扩展 API 增加明确错误响应,区分参数错误、数据不存在、未确认、报价不可用和外部接口失败
|
||||
- [x] 4.5 不新增 leader 推荐列表 API、推荐处理 API、leader 状态更新 API;这些留到第二阶段
|
||||
|
||||
## 5. 前端界面
|
||||
|
||||
- [x] 5.1 在跟单统计页和统计弹窗中新增 PnL 分解卡片,展示已实现、未实现、总 PnL、持仓成本和持仓估值
|
||||
- [x] 5.2 新增亏损归因区块,展示归零亏损、top losing markets、未平仓风险、报价状态和数据完整性
|
||||
- [x] 5.3 新增风险配置体检区块,展示危险字段、当前值、建议值、原因和严重程度
|
||||
- [x] 5.4 新增应用保守配置确认弹窗,展示后端返回的字段变更前后 diff,取消时不发保存请求,确认时调用显式确认 API
|
||||
- [x] 5.5 补齐空态、加载态、部分数据不可用态和错误态,尤其是 `UNAVAILABLE` 报价状态不能显示成 0 亏损
|
||||
- [x] 5.6 前端文案使用中文优先,英文/繁中 i18n 可以先补基础 key,但不能让中文用户看到英文诊断主体
|
||||
|
||||
## 6. 测试
|
||||
|
||||
- [x] 6.1 为 PnL 计算增加单元测试,覆盖 `AVAILABLE` 且价格为 0、`NO_MATCH`、`UNAVAILABLE` 和混合持仓
|
||||
- [x] 6.2 为亏损诊断服务增加单元测试,覆盖归零亏损、top losing markets、低样本盈利、部分数据缺失和数据完整性字段
|
||||
- [x] 6.3 为风险配置体检增加单元测试,覆盖危险配置、保守配置和建议值生成
|
||||
- [x] 6.4 为新增或扩展 controller 增加接口测试,覆盖成功响应、参数错误、数据不存在、未确认、非白名单字段混入和外部数据不可用
|
||||
- [x] 6.5 为前端关键组件增加构建验证和交互检查,确保确认弹窗、空态和错误态可用
|
||||
|
||||
## 7. 文档、部署和验证
|
||||
|
||||
- [x] 7.1 更新 README 或运维文档,说明亏损诊断、风险安全带和手动确认流程
|
||||
- [x] 7.2 记录 Mac mini 排查命令,包括 `ssh m4`、Docker 日志、MySQL 容器和非交互 PATH 注意事项
|
||||
- [x] 7.3 明确第二阶段才做 leader 池状态机、leader 推荐、leaderboard 候选发现和任何自动暂停
|
||||
- [x] 7.4 本地运行后端测试和前端构建,记录命令与结果
|
||||
- [ ] 7.5 在 Mac mini 部署前先用测试环境或本地数据验证诊断结果与现有统计一致
|
||||
- [ ] 7.6 部署后观察 24-72 小时诊断日志和页面显示,确认没有把 `UNAVAILABLE` 展示成 0 亏损,也没有自动改动真实跟单配置
|
||||
|
||||
## 不在本阶段范围
|
||||
|
||||
- leader 池状态机:`ACTIVE`、`WATCH`、`COOLDOWN`、`RETIRED`、`LOCKED`
|
||||
- leader 推荐记录表、推荐详情页、忽略/锁定/应用推荐动作
|
||||
- 官方 Polymarket leaderboard 候选发现和定时同步
|
||||
- 自动新增、删除、启用、停用 leader 或跟单配置
|
||||
- 自动加仓、组合优化、跨钱包资金调度或收益预测
|
||||
Reference in New Issue
Block a user