用 TypeScript 实现贷款 IRR:从还款现金流计算年化利率

简介: 本文详解如何用 TypeScript 实现贷款真实年化利率(IRR)计算:基于实际到账金额与各期还款现金流,通过二分法求解月度 IRR,并换算为复利年化利率((1+r)¹²−1),强调手续费、时间价值与监管口径(央行〔2021〕3号文),避免常见误算。

用 TypeScript 实现贷款 IRR:从还款现金流计算年化利率

贷款计算器里,一个容易写错的功能是“年化利率”。

假设借款 10,000 元,分 12 个月偿还,每月还 900 元。不考虑其他费用,总还款为 10,800 元。

直接计算:

(10800 - 10000) / 10000 = 8%

这只能得到总利息与初始本金的比例,不能直接当作这笔分期贷款的年化利率。因为借款人在逐月归还本金,并没有完整占用 10,000 元一整年。

如果放款时还扣了手续费,计算结果又会不同。

本文用一个具体例子,说明如何整理还款现金流,再用 TypeScript 求出月度 IRR 和复利口径的年化利率。

一、先确定计算口径

“真实年化率”是常见说法,但在程序界面和计算结果里,最好明确标注口径,例如:

  • 月度 IRR;
  • 月度 IRR × 12,线性年化口径;
  • (1 + 月度 IRR)^12 - 1,复利年化口径。

这几项不能混着展示。

中国人民银行公告〔2021〕第3号指出,贷款成本应包括利息及与贷款直接相关的各类费用;年化利率可以采用复利或单利方法计算,采用单利方法的应说明是单利。公告附件也提供了内部收益率法的计算示例。来源:中国人民银行

本文计算的是:根据输入的借款和还款现金流,先求月度 IRR,再换算为复利年化利率。是否遗漏费用,会直接影响结果。

二、把借款拆成现金流

以下是一组假设数据:

项目 金额或安排
合同本金 10,000 元
放款时扣除的手续费 200 元
实际到账 9,800 元
还款期数 12 期
每期还款 900 元
首期还款时间 放款后一个月末

从借款人视角看,收到的钱是正数,支付的钱是负数:

第 0 期:+9800
第 1 期:-900
第 2 期:-900
……
第 12 期:-900

这里有两个细节。

第一,初始现金流用的是实际到账的 9,800 元,而不是合同本金 10,000 元。

第二,200 元手续费已经从初始现金流里扣除,后续不能再加一次,否则会重复计费。如果还有每月收取的贷款相关费用,应计入对应月份的还款金额。

三、IRR 求解的是什么

设实际到账金额为 P,第 t 个月的还款金额为 Aₜ,月度 IRR 为 r,需要求解:

P = A₁ / (1+r) + A₂ / (1+r)² + … + Aₙ / (1+r)ⁿ

也就是寻找一个月利率,让所有未来还款折算到放款时点后的现值,等于实际到账金额。

求出 r 后:

线性年化 = r × 12

复利年化 = (1 + r)¹² - 1

这里的复利换算是统一年化口径,不表示合同一定按“利滚利”收款。

四、用二分法实现

对于“一次到账、之后只还款”的现金流,随着折现率提高,还款现值会下降,可以用二分法求解。

下面的函数限定为:

  • 每期代表一个月,首期在一个月末;
  • 只有一次初始到账,后续还款金额不能为负;
  • 总还款不少于实际到账,即只处理非负利率;
  • 最多支持 1,200 期,并设置求解边界。

它不是适用于任意投资现金流的通用 IRR 函数。

/**
 * 返回月度 IRR,例如 0.015 表示月度 1.5%。
 * payments 为每个月末支付的金额,使用正数。
 */
function monthlyIrr(
  netReceived: number,
  payments: readonly number[]
): number {
   
  if (!Number.isFinite(netReceived) || netReceived <= 0) {
   
    throw new Error("实际到账金额必须大于 0");
  }

  if (payments.length === 0 || payments.length > 1200) {
   
    throw new Error("还款期数必须在 1~1200 之间");
  }

  let total = 0;

  for (const amount of payments) {
   
    if (!Number.isFinite(amount) || amount < 0) {
   
      throw new Error("还款金额必须是非负有限数");
    }
    total += amount;
  }

  if (!Number.isFinite(total)) {
   
    throw new Error("金额超出计算范围");
  }

  if (total < netReceived) {
   
    throw new Error("此示例不支持负利率");
  }

  if (total === netReceived) return 0;

  // 未来还款现值减去实际到账金额。
  const residual = (rate: number): number => {
   
    let pv = 0;
    let discount = 1;

    for (const amount of payments) {
   
      discount /= 1 + rate;
      pv += amount * discount;
    }

    return pv - netReceived;
  };

  let low = 0;
  let high = 1;

  // 扩大上界,直到覆盖根;上界设置硬限制。
  while (residual(high) > 0 && high < 1048576) {
   
    high *= 2;
  }

  if (residual(high) > 0) {
   
    throw new Error("利率超出求解范围");
  }

  for (let i = 0; i < 160; i++) {
   
    const mid = (low + high) / 2;

    if (residual(mid) > 0) {
   
      low = mid;
    } else {
   
      high = mid;
    }
  }

  return (low + high) / 2;
}

二分法每轮都要遍历一次还款数组。固定迭代次数后,计算量随期数线性增长,适合这种规模较小的计算器功能。

五、运行前面的例子

const payments = Array<number>(12).fill(900);

const monthlyRate = monthlyIrr(9800, payments);
const linearAnnualRate = monthlyRate * 12;
const effectiveAnnualRate = (1 + monthlyRate) ** 12 - 1;

console.log(`月度 IRR:${
     (monthlyRate * 100).toFixed(4)}%`);
console.log(`线性年化:${
     (linearAnnualRate * 100).toFixed(4)}%`);
console.log(`复利年化:${
     (effectiveAnnualRate * 100).toFixed(4)}%`);

这组数据的计算结果为:

月度 IRR:1.5274%
线性年化:18.3292%
复利年化:19.9502%

为什么和最开始的 8% 差这么多?

8% 没有考虑本金逐月归还,也没有计入放款时扣除的 200 元手续费。IRR 则同时考虑了实际到账金额、每次还款金额和还款时点。

因此,不能只看“总共多还了多少钱”,还要看“这笔钱实际用了多久”。

六、给计算结果做交叉检查

除了检查显示出来的百分比,还可以把求得的利率代回原方程:

const presentValue = payments.reduce(
  (sum, amount, index) =>
    sum + amount / (1 + monthlyRate) ** (index + 1),
  0
);

console.log(presentValue.toFixed(2)); // 9800.00

console.assert(
  Math.abs(presentValue - 9800) < 0.000001,
  "现值校验未通过"
);

console.assert(
  monthlyIrr(1200, Array<number>(12).fill(100)) === 0,
  "零利率测试未通过"
);

console.assert(
  Math.abs(monthlyIrr(1000, [1100]) - 0.1) < 1e-10,
  "单期测试未通过"
);

单期测试很直观:到账 1,000 元,一个月后还 1,100 元,对应月度利率就是 10%。

计算过程中保留精度,最后展示时再调用 toFixed()。不要每轮迭代都四舍五入。

七、接入实际业务时的边界

不规则日期不能直接套用月度 IRR。

如果首期只有 15 天,或中途提前还款,就不再符合这里的等月间隔假设。需要使用按实际日期折现的模型,并明确一年按多少天计算。

还款数组必须包含完整现金流。

到账时收取的费用调整初始净到账;以后收取的费用放到实际支付的期次。缺少一笔费用,结果就只代表已输入的部分成本。

金额处理和利率求解分开。

业务账单可以使用整数分或十进制定点运算管理金额。将实际应付金额确定到分后,再送入求解函数。number 适合这里的数值求解,但不应承担所有账务精度处理。

不要用这个函数处理反复借还的现金流。

多次提款、退款或正负现金流多次交替,不满足本文的单调性前提,可能出现多个 IRR 或无解,需要另外设计。

结语

做贷款年化计算器,难点不只是把公式写进代码,更在于把钱的流向和时间安排放对。

先整理实际到账与每期支出,再求月度 IRR,最后明确年化口径,计算结果才方便核对和比较。结果也不能单独用于判断收费是否合法。

相关工具入口:第三方贷款计算器。这是我维护的网站;本文代码是独立教学示例,使用时应按具体还款安排核对输入和计算口径。

相关文章
|
20天前
|
人工智能 缓存 前端开发
DeepSeek Harness 首发实测 + 入门教程,夯爆了!梁神我错了
DeepSeek Harness + DeepSeek V4 Pro 项目实战保姆级教程!手把手带你从零安装开源 AI 编程工具,开发架构图、知识讲解网站、3D 网页游戏、全栈 AI 应用 4 个项目,覆盖运行模式选择、插件安装与开发,看看能不能对标 Claude。
13231 90
DeepSeek Harness 首发实测 + 入门教程,夯爆了!梁神我错了
|
8天前
|
人工智能 自然语言处理 安全
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
本文聚焦阿里云2026年推出的三款自研AI办公产品,清晰拆解千问办公、Qoder Teams、Qoder CN的差异化定位与能力边界:千问办公主打职场全场景提效,支持自然语言指令一键完成PPT生成、数据分析等高频办公任务;Qoder Teams面向程序员团队,深度整合AI代码生成、团队协同与企业知识库能力;Qoder CN则专为金融、政务等强合规场景打造,实现数据不出境与VPC私有化部署。文章同步给出分场景选型指南与最新活动定价,帮助不同类型的企业按需组合产品,实现业务岗、研发岗与强合规场景的AI能力全覆盖。
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
|
3天前
|
缓存 人工智能 API
阿里云Qwen3.8‑Flash完整能力解析:模型特性、API调用实操与计费规则深度拆解
在AI应用快速落地的当下,开发者与企业选型大模型API,不再只单纯关注评测榜单分数,推理速度、上下文长度、多模态能力、工具调用稳定性以及实际调用成本,共同决定项目能否平稳上线。Qwen3.8‑Flash作为新一代多模态混合专家模型,主打高性能推理与低成本开销,面向编程开发、智能Agent工作流、超长文档解析、图文混合理解等高频场景,提供托管API服务,权重同时开放可供本地部署,兼容主流接口协议,能够无缝接入各类开发工具链。很多开发者在接入过程中,容易混淆普通按量Token计费、缓存计费、各类订阅计划之间的差异,造成实际账单超出预估。本文从模型底层架构、核心功能能力、适用场景、API调用实操、完
801 0
|
13天前
|
Web App开发 人工智能 API
16 个超火的 DeepSeek Harness 插件,大肥鱼已经落后 N 个版本了。。。
DeepSeek Harness 精选插件推荐合集,从图片识别、浏览器操控、多 Agent 协作到手机远程控制,一口气带你看完 DSH 社区热门的十几个插件,覆盖技能扩展、UI 界面增强、整活玩法三大类,让你的鲸鱼变得更强。
1792 4
|
14天前
|
人工智能 Java BI
【AI】DeepSeek Harness 安装、运行、管理插件
本文介绍了如何运行DeepSeek开源的Agent框架DeepSeek Harness(dsh)。主要内容包括:使用nvm安装适配的Node版本;通过代理加速克隆GitHub源码;使用pnpm安装依赖并启动项目;配置DeepSeek API Token;安装扩展功能的插件。该框架自带Web界面,支持模型适配、文件编辑等插件化功能
1969 1
|
人工智能 JavaScript 开发工具
DeepSeek Harness 本地安装与使用指南
DeepSeek Harness(DSH)是DeepSeek AI开源的Agent运行框架,支持本地文件操作、命令执行与工具调用。基于Cordis插件架构,具备高扩展性与强可控性,适合开发者搭建可控Agent环境或开展模型基准测试。当前为开发者预览版,需Node.js环境,推荐先用`npx @deepseek-ai/dsh web`快速体验。
5230 0
|
9天前
|
人工智能 Linux iOS开发
Ollama使用教程:Ollama官网下载、Ollama本地部署大模型(2026最新)
Ollama 是一款免费开源的本地大模型运行工具,支持在 Windows/macOS/Linux 上离线运行 Qwen、DeepSeek、Llama 等主流开源模型,数据不出本机、隐私安全。提供 OpenAI 兼容 API,命令行一键拉取/运行/管理模型,无需联网,无调用限制,是开发者与 AI 爱好者部署本地 AI 助手的理想选择。(239 字)
|
16天前
|
人工智能 JavaScript 测试技术
保姆级教程:DeepSeek Harness从安装到跑通测试,30分钟上手
DeepSeek Harness是DeepSeek开源的AI Agent运行时,主打“一行命令安装、5分钟跑通”。它让模型真正动手干活——读代码、跑测试、分析失败、生成修复方案。本文手把手教你30分钟从零上手,覆盖安装、配置、实测及避坑指南,助你快速掌握下一代AI编程范式。
|
6天前
|
人工智能 监控 测试技术
Qwen3.8-Flash 来了,100万上下文、Agent、Coding 都加强了
8月26日,通义千问发布Qwen3.8-Flash-Next:125B参数、每Token仅激活6B,原生支持26万Token、可扩展至100万上下文;Coding、Agent与工具调用能力显著增强,面向真实软件工程任务,推动大模型从“回答问题”迈向“完成工作”。

热门文章

最新文章