计算口径说明书
每一个财务数字是怎么算出来的,以及它不包含什么。
口径版本:3.0.0
这是一份正式的 Calculation Specification。它的作用是:同一个概念在整个产品里只有一个定义。
每一条口径都对应 @wealth-os/finance-core 里的一个导出函数,函数有独立的单元测试锁定行为。页面、计算器和将来的情景引擎都调用同一个实现,不许各自实现一套。公式含义变化时,版本号必须递增,并且已经保存的情景结果会据此被标记为需要重算。
怎么读这一页
每一节的结构是:公式 → 对应的函数 → 它排除了什么 → 容易搞错的地方。「排除了什么」往往比公式本身更重要:财务计算出错很少是因为算错了乘法,而是因为把不该算进去的东西算了进去。
10.1 净资产与流动净资产
Net Worth = Total Assets − Total Liabilities
对应 netWorth(snapshot)。它同时返回:
byLiquidity:资产按cash / liquid / semi-liquid / illiquid四档分组;restricted:有使用限制或已指定用途的资产,连同原因;staleValuations:估值日期早于快照日期的资产。
房产、未上市股权、养老金都算在净资产里,但它们的流动性与使用限制必须单独显示。房产和未上市股权的估值日期往往很旧,界面有义务把这个日期摊开。
Liquid Net Worth 是另一个数字,不是净资产的近似值。对应 liquidNetWorth(snapshot, options):
- 分子:没有使用限制、没有指定用途的现金与流动资产。
semi-liquid默认不计入(它有价差、锁定期和交易周期),可以通过includeSemiLiquid打开; - 分母:
horizonMonths(默认 12 个月)内需要偿付的负债。循环信贷与欠税按全额计入——「只还最低额就安全」不成立;其余按最低还款在这段时间里的累计额计入,上限是本金。
容易搞错
把流动净资产当成净资产的一个比例去估。一个人 80% 的资产是自住房时,净资产和流动净资产之间差的不是一个系数,而是「能不能应对下个月的意外」这件事本身。
10.2 可分配现金流
Free Cash Flow = 税后现金收入 − 生活支出 − 合同债务支付 − 其他强制现金流出
对应 monthlyCashFlow(snapshot)。所有频率先折成月额再相加,one-off 不构成周期现金流(按 0 处理,需要作为事件单独建模)。
税前收入必须写明税率才能折成税后现金:afterTaxMonthly 对没写 taxRate 的税前收入直接抛错,而不是默认当成税后。这是故意的——「大概是税后」会让后面所有数字都偏。
重复计算的陷阱
这里的「合同债务支付」是现金流出的全额,含本金和利息。其中的本金部分同时减少负债,所以它在净资产变化分析里还会出现一次。同一笔钱不能既算作现金流出的损失,又算作财富积累的来源——所以它在储蓄率里要单独列(见下一节)。
10.3 储蓄率
默认口径:
Savings Rate = 储蓄与投资的净增加额 ÷ 税后收入
对应 savingsRate(input)。它返回三个数:
savingsRate:默认口径;expandedSavingsRate:(净增加额 + 本金偿还)÷ 税后收入;principalContribution:本金偿还单独贡献的百分点。
界面必须把两个口径分开显示。税后收入为零或负数时函数抛错,因为这时候储蓄率没有意义——该用 Free Cash Flow 和 Runway 描述处境。
10.4 Runway
Runway = 可动用的安全资金 ÷ 月必要现金流出
对应 runway(snapshot)。
分子由 isSafetyAsset 判定,排除四类:
- 已经指定用于近期大额支出的钱(
committedTo); - 有使用限制的资产(
restriction,如锁定期、质押、共同持有); - 退休账户(即使里面是现金);
- 流动性不是
cash或liquid的资产。
分母是 必要支出 + 最低债务支付 + 其他强制现金流出,不含可选支出。
必要流出为 0 时函数返回 null,而不是 Infinity——「无限个月」不是一个可以显示给人看的结论。
被排除的资产连同原因一起返回。这是 MVP 验收标准里的硬要求:已锁定、不可动用的资金不能直接加入安全储备,而且界面要能解释为什么。
没有计入安全资金的部分,以及原因
- 养老金账户— 退休账户,不能及时无成本取用
- 攒给首付的定存— 已指定用于明年的购房首付
把滑块里的钱从「活期」挪到「养老金账户」,总资产一分没少,Runway 会直接掉下来。这就是「有钱」和「现在能动用」的区别。
应急金目标对应 emergencyFundGap(snapshot, targetMonths):目标 = 月必要流出 × 目标月数,缺口为负说明已达标。
目标月数没有标准答案。六个月是一个被重复太多次的数字,它取决于你的收入稳定性、行业重新就业的周期、家庭有几个人依赖这份收入、有没有可以兜底的社会支持。
10.5 收入集中度
Income Dependency = 最大的经济相关收入来源 ÷ 总收入
对应 incomeDependency(snapshot)。
关键在「经济相关」:同一雇主发出的工资和奖金共用一个 dependencyGroup,所以它们合成一组,不算两个独立来源。同一个大客户的多个项目也是一组。
这一条口径的存在是因为「我有三条收入」这句话经常是假的——三条都来自同一个行业、同一个平台或同一个客户时,它们会一起消失。
10.6 所有权收入比例
Ownership Income Ratio = 自己拥有的资产或企业产生的净收入 ÷ 总净收入
对应 ownershipIncomeRatio(snapshot)。
它只认已实现的现金收入。账面估值增长和潜在未来收入不进这个比值,它们在资产页单独看。
容易搞错
把未实现的资本增值当成日常可支配收入。账面涨了 30 万,和每个月多出 2500 元可以花的钱,是两件完全不同的事。分红也不是凭空增加的财富——它会相应降低资产价值,还要考虑税务影响。
10.7 Freedom Coverage
这里有两个覆盖率,不能相加,也不能互相替代。对应 freedomCoverage(snapshot, assumptions)。
Current Cash-flow Coverage
符合条件的非劳动净现金流 ÷ 必要生活支出。对应 currentCashFlowCoverage。
分子只认已经在到账的:租金、利息、分红、版税、养老金、企业分配,以及任何标记为由自己拥有的资产产生的收入。
Modeled FI Coverage
在明确假设与期限下估算的可持续资本支持额 ÷ 必要生活支出。对应 modeledFiCoverage。
默认假设:提款率 4%、期限 30 年、计入退休账户、不计入自住房权益、通胀 2%。
默认排除三类资产,每一条都会带原因返回:
- 自住房权益——住在里面的房子不产生生活现金流;
- 未上市权益与私募股权——估值与流动性都不确定;
- 按假设排除的退休账户(可以打开)。
负债要从可支持资本里扣掉,但由已排除资产担保的负债要跟着一起排除,否则等于把房子剔掉又让房贷扣两遍。
为什么必须分开
一个人的 Modeled FI Coverage 是 1.2,Current Cash-flow Coverage 是 0.05。这意味着:在一组假设下资本够用,但现在每个月的生活费几乎全靠工作。把两者混为一谈,就会在收入中断时发现「够用」的那笔钱根本取不出来。所以 freedomCoverage 还会一起返回流动净资产。
10.8 FI Number
FI Number = 年支出 ÷ 假设提款率
对应 fiNumber(annualSpending, withdrawalRate) 与 fiNumberTable(annualSpending, rates)。
| 假设提款率 | 对应资产规模 | 相当于多少年的支出 |
|---|---|---|
| 3.0% | 4,800,000 元 | 33.3 年 |
| 3.5% | 4,114,286 元 | 28.6 年 |
| 4.0% | 3,600,000 元 | 25.0 年 |
| 5.0% | 2,880,000 元 | 20.0 年 |
3% 和 5% 之间差着近一倍的资产规模。所以「财富自由需要多少钱」这个问题,在没写明提款率、期限和支出口径之前是没法回答的。
这只是一道除法的敏感性示例。它不是任何市场的安全收益率,不是适合所有人的提款率,也不是退休保证。
实际建模还需要考虑:税务、通胀、退休期限、资产结构、市场序列风险、养老金和其他收入。其中序列风险尤其重要——同样的平均收益率,回报出现的顺序不同,结果可以差出一个量级。退休压力测试 讲的就是这件事。
债务计算
PRD 第 10 节没有单列债务口径,但验收标准要求「给定固定利率与付款计划时,计算结果可通过独立测试验证」。实现在 debt.ts:
- 月利率 = 名义年利率 ÷ 12,按月计息,期初余额计息;
- 月供先付利息,余下的冲本金;
- 月供不足以覆盖当月利息时,不返回一个假的期数,而是返回
feasible: false并给出minimumViablePayment。这种情况下本金不会下降,计划本身不成立; payoffPlan:每月先付所有最低还款,剩下的预算全部投向当前目标,一笔结清后它的最低还款并入预算;- 逾期的债务永远排在最前,无论用哪种策略。它的代价不是多付利息,而是违约后果。
这三笔债的最低还款合计 4,440 元。预算低于它,就不是「选哪种策略」的问题了。
| 顺序 | 还清需要 | 利息总额 | 第一笔结清 |
|---|---|---|---|
| 雪球法(先还余额最小的)小额网贷 → 信用卡 → 消费分期 | 20 个月 | 11,863 元 | 第 4 个月:小额网贷 |
| 雪崩法(先还利率最高的)信用卡 → 小额网贷 → 消费分期 | 20 个月 | 11,699 元 | 第 13 个月:信用卡 |
同一预算下,雪崩法少付约 164.52 的利息;雪球法先结清的笔数更多。哪一种更合适取决于你能否坚持。
复利
对应 project(input)(compounding.ts):
- 回报按年给出、按月折算:
monthlyReturn = (1 + annual) ^ (1/12) − 1; - 费用按年从资产里扣,同样折成月。费用一直在反方向复利;
- 允许负回报,曲线不保证向上;
- 同时给名义值与扣除通胀后的实际值,两者不能混用。
ruleOf72 只用于教学中的量级直觉,不作为规划依据。
| 第几年 | 余额(名义) | 本金贡献 | 回报贡献 | 回报占余额 |
|---|---|---|---|---|
| 1 | 68,818 元 | 67,200 元 | 1,618 元 | 2.4% |
| 3 | 222,183 元 | 207,708 元 | 14,474 元 | 6.5% |
| 5 | 398,540 元 | 356,774 元 | 41,766 元 | 10.5% |
| 10 | 958,105 元 | 770,373 元 | 187,733 元 | 19.6% |
| 15 | 1,728,219 元 | 1,249,847 元 | 478,372 元 | 27.7% |
| 20 | 2,772,136 元 | 1,805,689 元 | 966,447 元 | 34.9% |
储蓄率敏感性
- 储蓄率 15.0% → 期末 2,079,102 元
- 储蓄率 20.0% → 期末 2,772,136 元
- 储蓄率 25.0% → 期末 3,465,171 元
前几年余额几乎等于你投进去的钱。能更快改变结果的是收入与储蓄率,不是再调高回报假设。
货币换算
对应 money.ts。这里没有「静默换算」的函数:convert 的返回值一定带着汇率、换算方法、汇率时间与来源,界面必须显示它们。缺汇率时抛 MissingRateError,而不是当成 1:1。
sumIn 把一组金额换算到同一币种再相加;任何一笔缺汇率都会抛错,不做部分求和——一个少算了几项的总额比报错更危险。
展示用的四舍五入在 round / roundMoney 里,计算过程中不调用它们:口径里没有中间取整。
这份口径不包含什么
- 税务申报与具体税率计算;
- 社保、公积金、医保的地区规则;
- 任何具体金融产品的费用、申购赎回规则与准入条件;
- 实时市场数据与估值;
- 跨境税务居民身份的判定。
这些要么属于 Content Pack(地区相关、会过期、必须有核对日期和官方来源),要么必须由当地专业人士核实。