用户故事:单资产级别收益归因表格

创建日期: 2025-01-11
更新日期: 2025-01-11
状态: 待开发
优先级: P1
依赖: 真实收益追踪功能(已完成)


1. 用户故事

作为个人投资者,
我希望在收益分析页面看到每个资产的真实收益明细,包括每期收益变化,
以便我能清楚地知道哪些资产在真正赚钱,哪些在亏钱,以及收益是如何随时间变化的。


2. 背景与问题

用户场景

  • 用户每月盘点一次资产
  • 每次盘点时既有新资金投入,也有市场涨跌导致的收益变化
  • 用户想知道:
    1. 单期收益:这个月我的某个资产赚了还是亏了?
    2. 累计收益:我买的这个资产从开始到现在一共赚了多少?
    3. 对比分析:哪个资产表现好,哪个差?

当前状态

目前收益分析页只支持"两点比较"模式:

  • 选择起始快照和终止快照
  • 计算累计收益

缺失的能力

  • 无法看到每期(每月)的收益变化
  • 无法追踪单个资产的收益历史

用户痛点

问题影响
只有累计收益,没有分期明细不知道某个月是赚是亏
类别级别的收益会相互抵消无法识别表现差的个别资产
没有收益趋势无法判断资产表现是在改善还是恶化

3. 功能需求

3.1 功能一:资产收益对比表(横向对比)

展示选定时间段内,所有资产的收益对比

核心字段

字段说明
资产名称资产标识
期初市值起始快照时的市值
期末市值终止快照时的市值
账面变动期末 - 期初
累计流入期间所有快照的 net_inflow 之和
真实收益账面变动 - 累计流入
真实收益率真实收益 / 平均资本基础

⚠️ 注意:当选择跨多个快照的时间段时,需要累加中间所有快照的 net_inflow

UI 设计(桌面端)

┌─────────────────────────────────────────────────────────────────────────┐
│ 📊 资产收益对比                                        [按类别筛选 ▾]   │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│ ┌───────────────┬────────┬────────┬────────┬────────┬────────┬───────┐ │
│ │ 资产          │ 期初   │ 期末   │ 账面   │ 流入   │ 真实收益│ 收益率│ │
│ ├───────────────┼────────┼────────┼────────┼────────┼────────┼───────┤ │
│ │ 🟢 沪深300ETF │ ¥10,000│ ¥15,000│ +¥5,000│ ¥3,000 │ +¥2,000│ +17.4%│ │
│ │ 🟢 余额宝     │ ¥50,000│ ¥50,100│ +¥100  │ ¥0     │ +¥100  │ +0.2% │ │
│ │ ⚪ 朝朝宝     │ ¥20,000│ ¥45,700│+¥25,700│ ¥25,700│ ¥0     │ 0%    │ │
│ │ 🔴 中证500ETF │ ¥10,000│ ¥13,000│ +¥3,000│ ¥5,000 │ -¥2,000│ -16.0%│ │
│ └───────────────┴────────┴────────┴────────┴────────┴────────┴───────┘ │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

3.2 功能二:收益时间序列表(纵向追踪)⭐ 核心需求

展示单个资产在每个盘点周期的收益变化

用户场景:用户想知道"我的沪深300ETF每个月赚了多少"。

核心字段

字段说明
盘点日期快照日期
期末市值该快照时的市值
本期投入该快照的 net_inflow
本期收益(本期市值 - 上期市值) - 本期投入
累计投入历史 net_inflow 之和
累计收益从第一期到当前的真实收益累计

UI 设计

┌─────────────────────────────────────────────────────────────────────────┐
│ 📈 沪深300ETF 收益历史                                      [← 返回]    │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│ ┌──────────┬────────┬────────┬────────┬────────┬────────┐              │
│ │ 盘点日期  │ 期末市值│ 本期投入│ 本期收益│ 累计投入│ 累计收益│              │
│ ├──────────┼────────┼────────┼────────┼────────┼────────┤              │
│ │ 2025-01  │ ¥10,000│ ¥10,000│   -    │ ¥10,000│   -    │ ← 首次建仓   │
│ │ 2025-02  │ ¥14,000│ ¥3,000 │ +¥1,000│ ¥13,000│ +¥1,000│              │
│ │ 2025-03  │ ¥17,500│ ¥2,000 │ +¥1,500│ ¥15,000│ +¥2,500│              │
│ │ 2025-04  │ ¥16,000│ ¥0     │ -¥1,500│ ¥15,000│ +¥1,000│ ← 本月亏损   │
│ │ 2025-05  │ ¥20,000│ ¥1,000 │ +¥3,000│ ¥16,000│ +¥4,000│              │
│ └──────────┴────────┴────────┴────────┴────────┴────────┘              │
│                                                                         │
│ 📊 统计摘要                                                              │
│ ├─ 累计投入: ¥16,000                                                    │
│ ├─ 当前市值: ¥20,000                                                    │
│ ├─ 累计收益: +¥4,000 (+25.0%)                                           │
│ └─ 盈利月份: 3/4 (75%)                                                  │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

移动端适配:使用可滚动的卡片时间线

┌─────────────────────────────┐
│ 📈 沪深300ETF               │
│ 累计收益: +¥4,000 (+25.0%)  │
├─────────────────────────────┤
│                             │
│ ○ 2025-05                   │
│ │ 市值 ¥20,000              │
│ │ 投入 ¥1,000               │
│ │ 本期 🟢 +¥3,000           │
│ │                           │
│ ○ 2025-04                   │
│ │ 市值 ¥16,000              │
│ │ 投入 ¥0                   │
│ │ 本期 🔴 -¥1,500           │
│ │                           │
│ ○ 2025-03                   │
│ │ ...                       │
│                             │
└─────────────────────────────┘

3.3 交互流程

收益分析页
    │
    ├── 总览卡片(账面变动 / 真实收益)
    │
    ├── 类别归因表格(已有)
    │
    └── 资产收益对比表(新增 - 3.1)
            │
            └── 点击某个资产行
                    │
                    └── 展开/跳转到 → 收益时间序列表(新增 - 3.2)

4. 数据模型

4.1 复用已有结构

#![allow(unused)]
fn main() {
/// 单个资产的真实收益(两点比较)
pub struct AssetTrueReturn {
    pub asset_id: Uuid,
    pub asset_name: String,
    pub category: Category,
    pub start_value: Decimal,
    pub end_value: Decimal,
    pub book_return: Decimal,
    pub net_inflow: Decimal,      // 注意:跨多期时需要累加
    pub true_return: Decimal,
    pub true_rate: Decimal,
}
}

4.2 新增:收益时间序列结构

#![allow(unused)]
fn main() {
/// 单个资产的收益时间序列
pub struct AssetReturnSeries {
    pub asset_id: Uuid,
    pub asset_name: String,
    pub category: Category,
    pub periods: Vec<PeriodReturn>,
    pub total_inflow: Decimal,      // 累计投入
    pub total_return: Decimal,      // 累计收益
    pub total_rate: Decimal,        // 累计收益率
    pub winning_periods: usize,     // 盈利期数
    pub total_periods: usize,       // 总期数
}

/// 单期收益
pub struct PeriodReturn {
    pub snapshot_date: NaiveDate,
    pub end_value: Decimal,         // 期末市值
    pub period_inflow: Decimal,     // 本期投入
    pub period_return: Decimal,     // 本期收益
    pub cumulative_inflow: Decimal, // 累计投入
    pub cumulative_return: Decimal, // 累计收益
}
}

5. 计算逻辑

5.1 资产收益对比(跨多期)

#![allow(unused)]
fn main() {
fn calculate_asset_returns_multi_period(
    snapshots: &[Snapshot],  // 按时间排序,包含起始和终止之间的所有快照
    asset_id: Uuid,
) -> AssetTrueReturn {
    let start = snapshots.first();
    let end = snapshots.last();
    
    // 关键:累加中间所有快照的 net_inflow
    let total_inflow: Decimal = snapshots.iter()
        .skip(1)  // 跳过起始快照
        .filter_map(|s| s.items.iter().find(|i| i.asset_id == asset_id))
        .map(|i| i.net_inflow)
        .sum();
    
    // ... 计算真实收益
}
}

5.2 收益时间序列

#![allow(unused)]
fn main() {
fn calculate_asset_return_series(
    snapshots: &[Snapshot],  // 按时间正序
    asset_id: Uuid,
) -> AssetReturnSeries {
    let mut periods = Vec::new();
    let mut cumulative_inflow = Decimal::ZERO;
    let mut cumulative_return = Decimal::ZERO;
    let mut prev_value = Decimal::ZERO;
    
    for (i, snapshot) in snapshots.iter().enumerate() {
        if let Some(item) = snapshot.items.iter().find(|i| i.asset_id == asset_id) {
            let period_inflow = item.net_inflow;
            let period_return = if i == 0 {
                Decimal::ZERO  // 首期无收益计算
            } else {
                item.value - prev_value - period_inflow
            };
            
            cumulative_inflow += period_inflow;
            cumulative_return += period_return;
            
            periods.push(PeriodReturn {
                snapshot_date: snapshot.snapshot_date,
                end_value: item.value,
                period_inflow,
                period_return,
                cumulative_inflow,
                cumulative_return,
            });
            
            prev_value = item.value;
        }
    }
    
    // ... 构建 AssetReturnSeries
}
}

6. 验收标准

6.1 资产收益对比表

  • 展示所有资产的期初、期末、账面变动、累计流入、真实收益、收益率
  • 正确累加跨多期的 net_inflow
  • 支持按真实收益排序
  • 支持按类别筛选
  • 正确处理新增/删除资产

6.2 收益时间序列表

  • 点击资产可查看该资产的收益历史
  • 展示每期的本期收益和累计收益
  • 显示统计摘要(累计收益、盈利月份比例)
  • 移动端有良好的时间线展示体验
  • 首期(首次建仓)正确标记,不计算收益

7. 工作量估算

任务估算
数据模型新增(AssetReturnSeries, PeriodReturn)0.25 天
计算逻辑(多期累加、时间序列)0.5 天
资产收益对比表组件0.5 天
收益时间序列表组件0.75 天
移动端适配0.5 天
集成与交互0.25 天
测试与优化0.25 天
总计3 天

8. 技术风险

风险缓解措施
资产 ID 跨快照不一致严格按 asset_id 匹配,不依赖 asset_name
首期资产没有期初值首期特殊处理,不计算收益
资产中途删除又重新添加按 asset_id 追踪,中间缺失期视为断档

9. 后续扩展

  • 收益趋势折线图(可视化每期收益变化)
  • 与大盘指数对比(相对收益)
  • 导出收益报表(Excel/PDF)