用户故事:真实收益追踪

创建日期: 2025-01-11
状态: ✅ 已实现
优先级: P0


1. 背景与问题

当前问题

现有的"快照式盘点"模式无法区分"资产增值收益"和"资金流入流出"。

示例场景

  • 期初:沪深300ETF 10,000元
  • 期末:沪深300ETF 15,000元
  • 账面变化:+5,000元

但这5,000元可能是:

  • 用户加仓投入:+3,000元
  • 实际投资收益:+2,000元

用户需求

"我想知道我的每一个具体资产(比如'沪深300ETF')到底赚了多少钱,而不是被新投入的资金'污染'。"


2. 解决方案

核心思路

在盘点时,除了记录资产当前市值,还可以选填"本期资金变动"(净流入/流出)。

真实收益计算公式

真实收益 = 期末市值 - 期初市值 - 净流入
真实收益率 = 真实收益 / (期初市值 + 净流入/2)

设计原则

  1. 输入负担最小化:只有资金变动的资产需要额外填写
  2. 向后兼容:历史数据默认 net_inflow = 0
  3. 精确到单个资产:每个资产独立追踪
  4. 不需要交易流水:只在盘点时汇总

3. 数据模型改动

3.1 SnapshotItem 增加字段

#![allow(unused)]
fn main() {
// src/models/snapshot.rs
pub struct SnapshotItem {
    pub asset_id: Uuid,
    pub asset_name: String,
    pub scope: AssetScope,
    pub category: Category,
    pub sub_asset_class: String,
    pub vehicle_type: VehicleType,
    pub value: Decimal,
    
    // 新增:本期净流入(正=投入,负=取出)
    pub net_inflow: Decimal,  // 默认 Decimal::ZERO
}
}

3.2 数据库表结构

-- snapshot_items 表新增列
ALTER TABLE snapshot_items ADD COLUMN net_inflow TEXT NOT NULL DEFAULT '0';

3.3 Snapshot 新增辅助方法

#![allow(unused)]
fn main() {
impl Snapshot {
    /// 计算单个资产的真实收益(相对于上一个快照)
    pub fn asset_true_return(&self, asset_id: Uuid, prev_snapshot: &Snapshot) -> Option<TrueReturn> {
        let curr_item = self.items.iter().find(|i| i.asset_id == asset_id)?;
        let prev_item = prev_snapshot.items.iter().find(|i| i.asset_id == asset_id)?;
        
        let book_return = curr_item.value - prev_item.value;
        let true_return = book_return - curr_item.net_inflow;
        let base = prev_item.value + curr_item.net_inflow / dec!(2);
        let true_rate = if base > Decimal::ZERO {
            true_return / base * dec!(100)
        } else {
            Decimal::ZERO
        };
        
        Some(TrueReturn {
            asset_id,
            book_return,
            net_inflow: curr_item.net_inflow,
            true_return,
            true_rate,
        })
    }
}

pub struct TrueReturn {
    pub asset_id: Uuid,
    pub book_return: Decimal,
    pub net_inflow: Decimal,
    pub true_return: Decimal,
    pub true_rate: Decimal,
}
}

4. UI 改动

4.1 盘点模式

在每个资产条目下方增加"本期投入"输入框(可选,默认收起)。

布局设计

┌────────────────────────────────────────────────────────────────┐
│ 沪深300ETF                              原值: ¥10,000          │
│ ┌──────────────────────────────────────────────────────────┐   │
│ │ 当前市值  [  ¥15,000  ]              变化: +¥5,000       │   │
│ └──────────────────────────────────────────────────────────┘   │
│                                                                │
│ 📥 本期投入/取出 [  ¥3,000  ]  (可选,取出填负数)            │
│                                                                │
│ 💡 真实收益: +¥2,000 (+17.4%)                                  │
└────────────────────────────────────────────────────────────────┘

交互设计

  • 默认隐藏"本期投入"行
  • 点击某个资产的"+"按钮展开填写
  • 填写后实时计算并显示"真实收益"
  • 空值或0表示没有资金变动

4.2 收益分析页

增加"真实收益"视角,显示剔除资金进出后的收益。

概览卡片

┌─────────────────────────────┬─────────────────────────────┐
│ 账面变动                     │ 真实收益                     │
│ +¥ 100,000                  │ +¥ 45,000                   │
│ +10.5%                      │ +4.85%                      │
│ 含资金流入 ¥55,000          │ 剔除资金进出后               │
└─────────────────────────────┴─────────────────────────────┘

归因表格扩展(按单个资产):

┌──────────────┬────────────┬────────────┬────────────┬──────────┐
│ 资产         │ 账面变化    │ 资金进出    │ 真实收益   │ 收益率   │
├──────────────┼────────────┼────────────┼────────────┼──────────┤
│ 沪深300ETF   │ +¥5,000    │ +¥3,000    │ +¥2,000   │ +17.4%  │
│ 中证500ETF   │ +¥3,000    │ +¥5,000    │ -¥2,000   │ -6.7%   │
│ 余额宝       │ +¥100      │ ¥0         │ +¥100     │ +0.2%   │
│ 朝朝宝       │ +¥25,700   │ +¥25,700   │ ¥0        │ 0%      │
├──────────────┼────────────┼────────────┼────────────┼──────────┤
│ 合计         │ +¥33,800   │ +¥33,700   │ +¥100     │ +0.1%   │
└──────────────┴────────────┴────────────┴────────────┴──────────┘

5. 实现计划

Phase 1:数据模型与数据库(预估 0.5 天)

  1. SnapshotItem 增加 net_inflow 字段
  2. 数据库迁移脚本
  3. snapshot_repo.rs 读写支持

Phase 2:盘点模式 UI(预估 1 天)

  1. InventoryMode 组件增加"本期投入"输入
  2. 实时计算真实收益预览
  3. 保存时存储 net_inflow

Phase 3:收益分析页(预估 1 天)

  1. 收益概览卡片增加"真实收益"
  2. 归因表格支持按资产展开
  3. 计算逻辑更新

Phase 4:测试与优化(预估 0.5 天)

  1. 单元测试
  2. 边界情况处理
  3. UI 细节优化

总预估:3 天


6. 验收标准

  • 盘点时可以为每个资产填写"本期投入/取出"金额
  • 收益分析页显示"账面收益"和"真实收益"双指标
  • 支持按单个资产查看真实收益归因
  • 历史数据向后兼容(net_inflow 默认为 0)
  • 输入体验流畅,大部分资产不需要额外输入

7. 术语定义

术语定义
账面收益期末市值 - 期初市值
净流入本期投入资金 - 本期取出资金
真实收益账面收益 - 净流入
真实收益率真实收益 / (期初市值 + 净流入/2) × 100%

8. 附录:用户操作示例

场景:用户月初工资入账,定投了两只基金

操作流程

  1. 打开资产管理 → 发起盘点
  2. 更新各资产当前市值
  3. 对于"朝朝宝",填写本期投入 +25,700(工资入账)
  4. 对于"沪深300ETF",填写本期投入 +3,000(定投)
  5. 对于"中证500ETF",填写本期投入 +5,000(定投)
  6. 完成盘点

结果:收益分析页显示

  • 朝朝宝真实收益 ≈ 0(纯粹是工资入账)
  • 沪深300ETF真实收益 = 实际涨跌
  • 中证500ETF真实收益 = 实际涨跌(可能是负的)

这样用户就能清楚看到:哪些资产在真正赚钱,哪些在亏钱