# 跟单回测功能产品需求文档 (PRD)

## 一、功能概述

跟单回测功能允许用户对历史数据进行模拟跟单交易,评估不同跟单策略的收益表现,帮助用户在实际投入资金前验证策略的有效性。

## 二、用户故事

### 主要用户场景

1. **作为用户**,我希望能够创建回测任务,选择特定的 Leader 和跟单配置,以便评估在过去一段时间内使用该策略的收益情况
2. **作为用户**,我希望能够查看所有历史回测结果,并按收益额或收益率排序,以便找到最优策略
3. **作为用户**,我希望能够按 Leader 筛选回测记录,以便比较不同 Leader 的表现
4. **作为用户**,我希望回测结果能够展示详细的交易记录和收益变化,以便深入分析策略表现
5. **作为用户**,我希望回测在资金不足时能够自动停止,模拟真实交易场景

## 三、功能需求

### 3.1 回测管理页面

#### 3.1.1 页面布局

- **页面位置**: 在跟单管理模块下新增"回测管理"菜单项
- **页面标题**: "回测管理" / "Backtest Management"

#### 3.1.2 列表功能

**筛选功能**:
- Leader 筛选下拉框,支持按 Leader 过滤回测记录
- 状态筛选: 全部 / 运行中 / 已完成 / 已停止

**排序功能**:
- 按收益额排序 (升序/降序)
- 按收益率排序 (升序/降序)
- 按创建时间排序 (默认降序)

**列表字段**:
| 字段名 | 说明 | 示例 |
|-------|------|------|
| 回测ID | 唯一标识 | #12345 |
| 配置名称 | 回测任务名称 | "激进策略-Leader A" |
| Leader名称 | 跟单的Leader | "Smart Trader" |
| 初始金额 | 回测起始资金 | $1000 |
| 最终金额 | 回测结束时资金 | $1250 |
| 收益额 | 最终金额 - 初始金额 | +$250 |
| 收益率 | (收益额/初始金额) × 100% | +25% |
| 回测天数 | 回测的时间跨度 | 30天 |
| 交易笔数 | 回测期间执行的交易数量 | 45笔 |
| 状态 | 运行中/已完成/已停止 | 已完成 |
| 开始时间 | 回测开始时间 | 2026-01-01 10:00 |
| 结束时间 | 回测结束时间 | 2026-01-31 15:30 |
| 操作 | 查看详情/删除 | - |

#### 3.1.3 列表操作

- **查看详情**: 点击后展开详细信息,包括:
  - 回测配置参数
  - 详细交易记录
  - 资金变化曲线图
  - 收益统计
- **删除**: 删除回测记录(确认后不可恢复)

### 3.2 新增回测任务

#### 3.2.1 创建入口

- 列表页面右上角"新增回测"按钮
- 点击后弹出创建对话框或跳转到创建页面

#### 3.2.2 配置表单

**基本配置**:
- **回测名称** (必填): 用户自定义名称,方便识别
- **选择Leader** (必填): 下拉框选择已添加的Leader
- **初始投入金额** (必填): 模拟起始资金,单位USDC,范围: 1 - 1,000,000
- **回测天数** (必填): 选择回测的历史天数,范围: 1 - 30天

**跟单配置** (参照现有跟单配置参数):

| 配置项 | 字段名 | 说明 | 默认值 |
|-------|-------|------|-------|
| 跟单模式 | copyMode | RATIO(比例模式) / FIXED(固定金额) | RATIO |
| 跟单比例 | copyRatio | 比例模式下生效,相对Leader订单金额的比例 | 1.0 |
| 固定金额 | fixedAmount | 固定金额模式下生效,每笔固定投入金额 | null |
| 最大单笔订单 | maxOrderSize | 单笔订单最大金额限制 | 1000 |
| 最小单笔订单 | minOrderSize | 单笔订单最小金额限制 | 1 |
| 最大每日亏损 | maxDailyLoss | 每日最大亏损限制 | 10000 |
| 最大每日订单数 | maxDailyOrders | 每日最大订单数量限制 | 100 |
| 价格容忍度 | priceTolerance | 价格偏差容忍百分比 | 5% |
| 延迟秒数 | delaySeconds | 跟单延迟时间 | 0 |
| 支持卖出 | supportSell | 是否跟随卖出 | true |
| 最小订单深度 | minOrderDepth | 订单簿深度要求 | null |
| 最大价差 | maxSpread | 买卖价差限制 | null |
| 最低价格 | minPrice | 最低价格限制 | null |
| 最高价格 | maxPrice | 最高价格限制 | null |
| 最大仓位金额 | maxPositionValue | 最大持仓总金额 | null |
| 关键字过滤模式 | keywordFilterMode | DISABLED/WHITELIST/BLACKLIST | DISABLED |
| 关键字列表 | keywords | 关键字数组 | [] |
| 市场截止时间限制 | maxMarketEndDate | 市场结束时间限制 | null |

> [!IMPORTANT]
> 跟单配置表单应完全复用现有的跟单配置组件,保持参数一致性

#### 3.2.3 表单验证

- 回测名称: 不能为空,长度1-100字符
- Leader: 必须选择有效的Leader
- 初始金额: 必须大于0
- 回测天数: 必须在1-30之间
- 其他配置参数: 遵循现有跟单配置的验证规则

#### 3.2.4 提交逻辑

1. 表单验证通过后,提交到后端API
2. 后端保存回测任务到数据库,状态设置为"待执行"
3. 前端显示创建成功提示,自动跳转到列表页面
4. 回测任务由后端轮询服务自动获取并执行

### 3.3 回测详情页面

#### 3.3.1 页面布局

**顶部概览卡片**:
- 回测名称
- Leader信息
- 初始金额 / 最终金额
- 收益额 / 收益率
- 回测时间范围
- 总交易笔数
- 状态

**资金变化图表**:
- X轴: 时间
- Y轴: 账户余额
- 折线图展示资金随时间的变化

**交易记录表格**:
| 时间 | 市场 | 方向 | 数量 | 价格 | 金额 | 盈亏 | 余额 |
|-----|------|-----|------|------|------|------|------|
| 2026-01-01 10:05 | BTC > $100k | 买入 YES | 100 | 0.65 | 65 | - | 935.00 |
| 2026-01-01 14:20 | BTC > $100k | 卖出 YES | 100 | 0.72 | 72 | +7.00 | 942.00 |
| 2026-01-05 16:00 | ETH > $5k | 买入 YES | 50 | 0.80 | 40 | - | 902.00 |
| 2026-01-10 00:00 | ETH > $5k | 市场结算 YES | 50 | 1.00 | 50 | +10.00 | 952.00 |

> [!NOTE]
> **交易类型说明**:
> - **买入**: 跟随Leader买入
> - **卖出**: 跟随Leader卖出  
> - **市场结算**: 市场到期自动结算（赎回）
> 
> **手续费**: 回测不计算手续费，简化计算逻辑

**统计数据**:
- 总交易笔数
- 买入笔数 / 卖出笔数
- 胜率 (盈利交易 / 总交易)
- 平均收益
- 最大单笔盈利
- 最大单笔亏损
- 最大回撤

## 四、业务规则

### 4.1 回测执行规则

#### 4.1.1 资金检查
- **停止条件**: 当账户余额 < $1 **且无任何持仓**时，回测自动停止
- **继续条件**: 如果余额 < $1 但仍有持仓，继续处理后续交易
  - 原因: Leader 可能卖出，或市场到期结算，释放资金
  - 处理: 跳过无法执行的买入订单，继续处理卖出和结算
- **订单检查**: 每次下单前检查余额是否充足
- **不足处理**: 余额不足时跳过该买入订单，记录日志，继续监听后续交易

> [!IMPORTANT]
> 只要有持仓存在，就不应停止回测，因为后续可能通过卖出或市场结算回收资金

#### 4.1.2 历史数据获取
- 从 Polymarket API 获取 Leader 的历史交易记录
- 根据回测天数计算起始时间: `startTime = now - (backtestDays × 24 × 3600 × 1000)`
- 按时间顺序回放交易

#### 4.1.3 交易执行模拟
- 按照配置的跟单规则计算跟单金额
- 应用所有过滤条件 (价格、深度、关键字等)
- 模拟订单成交（不计算手续费）
- 更新账户余额

> [!NOTE]
> 回测不计算手续费，简化计算逻辑，避免过于复杂的精度问题

#### 4.1.3.1 余额不足的处理 ⚠️

**场景说明**:
- 当前余额不足以执行买入订单
- 但有持仓未卖出（相当于"待赎回资产"）

**处理策略**:

**方案A: 严格模式**（推荐）
- ✅ 仅使用当前可用余额（`currentBalance`）
- ✅ 余额不足时跳过该订单，不考虑持仓价值
- ✅ 理由: 更保守，模拟真实场景（持仓未卖出前资金不可用）

**方案B: 宽松模式**（可选）
- 计算"潜在可用资金" = `currentBalance + 持仓市值`
- 允许"透支"买入，后续通过卖出或结算平衡
- 风险: 可能产生不切实际的回测结果

**推荐实现**:
```kotlin
// 严格检查余额
if (totalCost > currentBalance) {
    logger.info("余额不足以执行买入订单: 需要 $totalCost, 可用 $currentBalance")
    logger.debug("当前持仓价值: ${calculatePositionValue(positions)}, 但不计入可用余额")
    continue  // 跳过该订单
}
```

**特殊情况: 市场即将结算**
- 如果持仓市场在接下来很短时间内会结算，可以提前释放资金
- 实现: 在每次交易前先执行市场结算检查（已在4.1.5实现）

> [!IMPORTANT]
> 采用**严格模式**更符合真实跟单场景，避免回测结果过于乐观

#### 4.1.4 卖出跟随
- 如果 `supportSell = true`,跟随 Leader 的卖出操作
- 按照买入时的比例卖出持仓
- 计算盈亏并更新余额

#### 4.1.5 市场结算处理 ⭐
- **触发时机**: 
  - **实时检查**: 在处理每笔Leader交易前,检查所有持仓市场的`endDate`
  - **到期即结算**: 如果市场结束时间 ≤ 当前交易时间,立即结算该持仓
  - **兜底处理**: 回测结束时,结算所有剩余持仓
- **结算规则**:
  - 获取市场最终结果 (通过Polymarket API)
  - 持仓方向为胜出方: 按 **1.0** 价格结算
  - 持仓方向为失败方: 按 **0.0** 价格结算
  - 市场未结算或无法获取结果: 按**成本价**结算 (保守估计)
- **资金流转**: 结算后的资金立即计入余额,可用于后续交易
- **交易记录**: 生成"市场结算"类型的交易记录,用于详情展示

> [!IMPORTANT]
> **实时结算的优势**:
> - ✅ 模拟真实场景: 市场结束时资金会自动返还
> - ✅ 提高资金利用率: 结算后的资金可以参与后续交易
> - ✅ 更准确的收益计算: 反映实际的资金周转情况

### 4.2 数据持久化

#### 4.2.1 回测任务表
- 保存回测基本信息和配置
- 记录执行状态和结果

#### 4.2.2 回测交易记录表
- 保存每笔模拟交易的详细信息
- 用于详情页面展示和分析

### 4.3 并发控制

- 同一时间最多支持 5 个回测任务并发执行
- 新任务排队等待,FIFO策略
- 前端显示任务队列位置

## 五、UI/UX 要求

### 5.1 响应式设计
- 支持桌面和移动端浏览
- 表格在小屏幕上支持横向滚动

### 5.2 国际化
- 支持中文和英文
- 所有文案提供双语版本

### 5.3 交互体验
- 创建回测: 表单提交时显示Loading状态
- 回测执行中: 显示进度条或百分比
- 数据加载: Skeleton占位符
- 操作反馈: Toast提示 (成功/失败/警告)

### 5.4 数据可视化
- 资金变化图表使用 ECharts 或 Recharts
- 支持图表缩放和数据点Tooltip
- 图表颜色: 盈利绿色,亏损红色

## 六、非功能需求

### 6.1 性能要求
- 回测列表页面加载时间 < 2秒
- 单个回测任务执行时间 < 5分钟 (30天数据)
- 详情页图表渲染时间 < 1秒

### 6.2 数据准确性
- 回测结果误差 < 0.1%
- 余额计算使用 BigDecimal 避免精度丢失
- 价格和数量精确到小数点后8位

### 6.3 安全性
- 回测数据仅用户本人可见
- API接口需要身份认证
- 防止SQL注入和XSS攻击

## 七、后续迭代规划

### Phase 2 (可选)
- 支持批量创建回测任务
- 回测结果对比功能
- 导出回测报告 (PDF/Excel)
- AI策略推荐

### Phase 3 (可选)
- 实时回测 (边交易边回测)
- 社区策略分享
- 策略市场

---

## 附录: 页面路由规划

- 回测列表: `/copy-trading/backtest`
- 新增回测: `/copy-trading/backtest/create`
- 回测详情: `/copy-trading/backtest/:id`
