等额本金、等额本息工具类(Java版)
最近写了一个关于金融的项目,在这遇到的一些概念和算法不是很懂,于是就问别人和百度了一翻。其实关键是要理解什么是“等额本金、等额本息”,然后用数学的算法算一下,再转化成java代码去计算【算法】
以下就是用最通俗的语句来理解“等额本金、等额本息”:
等额本金定义:本金保持相同,利息逐月递减,月还款数递减。适合于有计划提前还贷。
等额本息定义:本金逐月递增,利息逐月递减,月还款数不变。
等额本息还款法:
每月月供额=贷款本金×月利率×(1+月利率)还款月数/[(1+月利率)还款月数-1]
每月应还利息=贷款本金×月利率×〔(1+月利率)^还款月数-(1+月利率)^(还款月序号-1)〕÷〔(1+月利率)^还款月数-1〕
每月应还本金=贷款本金×月利率×(1+月利率)^(还款月序号-1)÷〔(1+月利率)^还款月数-1〕
总利息=还款月数×每月月供额-贷款本金
等额本金还款法:
每月月供额=(贷款本金÷还款月数)+(贷款本金-已归还本金累计额)×月利率
每月应还本金=贷款本金÷还款月数
每月应还利息=剩余本金×月利率=(贷款本金-已归还本金累计额)×月利率
每月月供递减额=每月应还本金×月利率=贷款本金÷还款月数×月利率
总利息=〔(总贷款额÷还款月数+总贷款额×月利率)+总贷款额÷还款月数×(1+月利率)〕÷2×还款月数-总贷款额
等额本金:
package com.utils;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class AverageCapitalUtils {
private static final Integer month = 12;
/ * @param investMoney
* 总借款额(贷款本金)
* @param yearRate
* 总年利率
* @param otherRate
* 加息年利率
* @param unit
* 单位:年,月
* @param month
* 还款总月数
* @return 每月偿还本金和利息,不四舍五入,直接截取小数点最后两*/
public static List
等额本息:
package com.util;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import com.LoanProductStageVo;
import com.TimeUnits;
/**
* 本息计算器
* @Description: 本息算法
*/
public class PrincipalAndInterestEquals {
/**
* 调试
* @param args
*/
public static void main(String[] args) {
int periods = 4;
TimeUnits unit = TimeUnits.day;
RoundingMode roundingMode = RoundingMode.UP;
BigDecimal rate = new BigDecimal("0.12");
BigDecimal money = new BigDecimal("10000");
// equalPI_stage(periods, rate, unit, money, roundingMode);
// System.out.println("每月本息和(等额本息):"+equalPI_everyMonthPrincipalAddInterset(periods, rate, TimeUnits.month, money, RoundingMode.UP));
// System.out.println("每月本息和:"+pEqualI_stage(periods, rate,unit, money, RoundingMode.UP));
// pEqualI_stageSumNum(15 ,periods, rate, unit, money, roundingMode);
// equalP_stage(periods, rate, money, roundingMode);
// List stages = equalPI_stage(periods, rate, TimeUnits.month, money, RoundingMode.UP);
// for(int i = 0; i < stages.size(); i++) {
// System.out.println("------------" + (i+1) + "期");
// System.out.println("本金:" + stages.get(i).getPrincipal());
// System.out.println("利息:" + stages.get(i).getInterset());
// }
}
/**
* 月转年,乘以12
*/
private static final BigDecimal MONTH_YEAR = new BigDecimal("12");
/**
* 天转月,乘以30,一月按30天算
*/
private static final BigDecimal DAY_MONTH = new BigDecimal("30");
// private static final BigDecimal DAY_YEWR = new BigDecimal("365");
/**
* 利率百分数转换
*/
private static final BigDecimal RATE_CONVERT_PERCENT = new BigDecimal("100");
///-------------------------------等额本息
/**
* 等额本息 —— 计算每期还款本息和
* 设贷款额为a,月利率为i,年利率为I,还款月数为n,每月还款额为b,还款利息总和为Y
* 月均还款:b=a×i×(1+i)^n÷〔(1+i)^n-1〕
* @param periods 期数,单位:月
* @param rate 年利率
* @param unit 期数单位(目前仅支持年、月、日)
* @param money 要分期的金额
* @param roundingMode 小数位处理
* @return 每期还款额,如单位年,则为每年还款额
*/
public static BigDecimal equalPI_everyMonthPrincipalAddInterset(Integer periods, BigDecimal rate, TimeUnits unit, BigDecimal money, RoundingMode roundingMode) {
rate = rate.divide(RATE_CONVERT_PERCENT, roundingMode).divide(MONTH_YEAR, roundingMode);//将年利率转换成月利率
/*
* 设贷款额为a,月利率为i,年利率为I,还款月数为n,每月还款额为b,还款利息总和为Y
* 月均还款:b=a×i×(1+i)^n÷〔(1+i)^n-1〕
*/
BigDecimal newrate = rate.add(new BigDecimal("1")).pow(periods);//1+月利率
BigDecimal ch = newrate.subtract(new BigDecimal("1"));
BigDecimal monthMoney = money.multiply(rate).multiply(newrate).divide(ch, 2, roundingMode);//每月还款本息和
// System.out.println("每月还款本息:"+monthMoney);
// System.out.println("通过每月还款本息计算的还款总和:"+monthMoney.multiply(new BigDecimal(stage.getPeriods())));
if(TimeUnits.year == unit) {
return money.multiply(MONTH_YEAR).setScale(2, roundingMode);
} else if(TimeUnits.day == unit) {
return money.divide(DAY_MONTH, 2, roundingMode);
}
return monthMoney;
}
/**
* 等额本息 —— 计算各个月份的还款额(仅支持月)
* 设贷款额为a,月利率为i,年利率为I,还款月数为n,每月还款额为b,还款利息总和为Y
* 第n月还款利息为:(a×i-b)×(1+i)^(n-1)+b
* 未完成:单位为年和日的,间隔不是1的情况
* @param periods 分期数,单位:月
* @param rate 年利率
* @param unit 单位(仅支持月)
* @param money 贷款总金额
* @param roundingMode 小数位处理
* @return
*/
public static List equalPI_stage(Integer periods, BigDecimal rate, TimeUnits unit, BigDecimal money, RoundingMode roundingMode) {
rate = rate.divide(RATE_CONVERT_PERCENT, roundingMode).divide(MONTH_YEAR, roundingMode);//计算月利率
/*
* 设贷款额为a,月利率为i,年利率为I,还款月数为n,每月还款额为b,还款利息总和为Y
* 第n月还款利息为:(a×i-b)×(1+i)^(n-1)+b
*/
List stageResult = new ArrayList<>();
//BigDecimal principalAddInterset = equalPI_everyMonthPrincipalAddInterset(periods, rate, unit, money,roundingMode);
BigDecimal principalAddInterset = equalPI_everyMonthPrincipalAddInterset(periods, rate, TimeUnits.month, money,roundingMode);
BigDecimal totalLixi = new BigDecimal(0);//临时:利息总额
BigDecimal totalBenjin = new BigDecimal(0);//临时:本金总额
for(int i = 0; i < periods; i++) {
// System.out.println("\n--------" + (i+1) + "期");
LoanProductStageVo stageVo = new LoanProductStageVo();
stageVo.setPrincipalAddInterset(principalAddInterset);
if(i + 1 == periods) {
//最后一期的计算,将计算中因小数位导致数据不对称的补充回来
stageVo.setInterset(principalAddInterset.multiply(new BigDecimal(periods)).subtract(money).subtract(totalLixi));
stageVo.setPrincipal(money.subtract(totalBenjin));
} else {
BigDecimal currentMonthRate = rate.add(new BigDecimal(1)).pow(i);
BigDecimal interset = money.multiply(rate).subtract(principalAddInterset).multiply(currentMonthRate).add(principalAddInterset).setScale(2, roundingMode);
stageVo.setInterset(interset);
stageVo.setPrincipal(principalAddInterset.subtract(interset));
}
totalLixi = totalLixi.add(stageVo.getInterset());
totalBenjin = totalBenjin.add(stageVo.getPrincipal());
stageResult.add(stageVo);
}
// System.out.println("还款利息总额:" + totalLixi);
// System.out.println("还款本金总额:" + totalBenjin);
return stageResult;
}
/**
* 等额本息 —— 按照公式计算第n期的信息
* @param n 第n期
* @param periods 分期数
* @param rate 月利率
* @param unit 单位(目前仅支持年、月)
* @param money 总本金额
* @param roundingMode 小数位取值方式
* @return
*/
public static LoanProductStageVo equalPI_stageAtNum(int n, int periods, BigDecimal rate, BigDecimal money, RoundingMode roundingMode) {
if(n <= 0) {
return null;
}
if(n > periods) {
return null;
}
rate = rate.divide(RATE_CONVERT_PERCENT, roundingMode).divide(MONTH_YEAR, roundingMode);
/*
* 设贷款额为a,月利率为i,年利率为I,还款月数为n,每月还款额为b,还款利息总和为Y
* 第n月还款利息为:(a×i-b)×(1+i)^(n-1)+b
*/
LoanProductStageVo stageVo = new LoanProductStageVo();
BigDecimal principalAddInterset = equalPI_everyMonthPrincipalAddInterset(periods, rate, TimeUnits.month, money,roundingMode);
BigDecimal currentMonthRate = rate.add(new BigDecimal(1)).pow(n - 1);
BigDecimal interset = money.multiply(rate).subtract(principalAddInterset).multiply(currentMonthRate).add(principalAddInterset).setScale(2, roundingMode);
stageVo.setPrincipalAddInterset(principalAddInterset);
stageVo.setInterset(interset);
stageVo.setPrincipal(principalAddInterset.subtract(interset));
return stageVo;
}
///-------------------------------等额本金
/**
* 等额本息 —— 计算每期信息
* 公式:每月还款金额 = (贷款本金 / 还款月数)+(本金 — 已归还本金累计额)×每月利率
*
* @param periods 分期数
* @param rate 年利率
* @param money 本金总额
* @param roundingMode 小数位处理
* @return
*/
public static List equalP_stage(int periods, BigDecimal rate, BigDecimal money, RoundingMode roundingMode) {
final BigDecimal PERIODS = new BigDecimal(periods + "");
rate = rate.divide(RATE_CONVERT_PERCENT, roundingMode).divide(MONTH_YEAR, roundingMode);
List stageResult = new ArrayList<>();
//每月还款本金
BigDecimal everyMonthPrincipal = money.divide(PERIODS, 2, roundingMode);
BigDecimal totalBenjin = new BigDecimal("0");
BigDecimal totalLixi = new BigDecimal("0");
for(int i = 0; i < periods; i++) {
// System.out.println("\n--------" + (i+1) + "期");
LoanProductStageVo stageVo = new LoanProductStageVo();
if(i+1 == periods) {
stageVo.setPrincipal(money.subtract(totalBenjin));
stageVo.setInterset(stageVo.getPrincipal().multiply(rate).setScale(2, roundingMode));
} else {
stageVo.setPrincipal(everyMonthPrincipal);
stageVo.setInterset(money.subtract(totalBenjin).multiply(rate).setScale(2, roundingMode));
}
// System.out.println("本金:"+stageVo.getPrincipal());
// System.out.println("利息:"+stageVo.getInterset());
totalBenjin = totalBenjin.add(stageVo.getPrincipal());
totalLixi = totalLixi.add(stageVo.getInterset());
stageResult.add(stageVo);
}
// System.out.println("本金和:"+totalBenjin);
// System.out.println("利息和:"+totalLixi);
return stageResult;
}
}
等本等息为民间借贷、分期购物、银行分期中的一种还款方式。
月还款为:本金+利息。
举例说明,假设借款额3万元,借款期限12个月,预计年化借款利率12%,等本等息还款,每月月末归还本金及利息,则公式如下:每月归还本金=贷款3万÷12期=2500元,预计年利息=3万×12%=3600元,为月息300元,那就是每个月需还2500+300=2800元。
package com.zhonglian.jinju.pub.service.support;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import com.LoanProductStageVo;
import com.TimeUnits;
/**
*等 本等息计算器
* @Description: 本息算法
*/
public class PrincipalAndInterestEquals2 {
/**
* 调试
* @param args
*/
public static void main(String[] args) {
int periods = 4;
TimeUnits unit = TimeUnits.day;
RoundingMode roundingMode = RoundingMode.UP;
BigDecimal rate = new BigDecimal("0.12");
BigDecimal money = new BigDecimal("10000");
pEqualI_stageSumNum(15 ,periods, rate, unit, money, roundingMode);
// equalP_stage(periods, rate, money, roundingMode);
// List stages = equalPI_stage(periods, rate, TimeUnits.month, money, RoundingMode.UP);
// for(int i = 0; i < stages.size(); i++) {
// System.out.println("------------" + (i+1) + "期");
// System.out.println("本金:" + stages.get(i).getPrincipal());
// System.out.println("利息:" + stages.get(i).getInterset());
// }
}
/**
* 月转年,乘以12
*/
private static final BigDecimal MONTH_YEAR = new BigDecimal("12");
/**
* 天转月,乘以30,一月按30天算
*/
private static final BigDecimal DAY_MONTH = new BigDecimal("30");
// private static final BigDecimal DAY_YEWR = new BigDecimal("365");
/**
* 利率百分数转换
*/
private static final BigDecimal RATE_CONVERT_PERCENT = new BigDecimal("100");
///-------------------------------等本等息
/**
* 等本等息 —— 计算每期信息
* 算法:第n月还款利息为:贷款总额 * 年利率 / 分期数
* @param periods 分期数,单位月
* @param rate 利率
* @param money 贷款总额
* @param roundingMode 小数位处理
* @return
*/
public static List pEqualI_stage(int periods, BigDecimal rate, BigDecimal money, RoundingMode roundingMode) {
rate = rate.divide(RATE_CONVERT_PERCENT, roundingMode);
List result = new ArrayList<>();
BigDecimal everyMonthPrincipal = money.divide(new BigDecimal(periods+""), 2, roundingMode);
BigDecimal everyMonthInterset = money.multiply(rate).divide(MONTH_YEAR, roundingMode);
BigDecimal totalInterset = everyMonthInterset.multiply(new BigDecimal(periods+""));
BigDecimal principalAddInterset = everyMonthPrincipal.add(everyMonthInterset);
BigDecimal totalBenjin = new BigDecimal("0");
BigDecimal totalLixi = new BigDecimal("0");
for(int i = 0; i < periods; i++) {
// System.out.println("\n--------" + (i+1) + "期");
LoanProductStageVo stageVo = new LoanProductStageVo();
if(i + 1 == periods) {
//TODO 算法疑问:最后一期会与前面的不一样
// System.out.println("最后");
stageVo.setPrincipal(money.subtract(totalBenjin));
stageVo.setInterset(totalInterset.subtract(totalLixi).setScale(2, roundingMode));
stageVo.setPrincipalAddInterset(principalAddInterset);
} else {
stageVo.setInterset(everyMonthInterset);
stageVo.setPrincipal(everyMonthPrincipal);
stageVo.setPrincipalAddInterset(principalAddInterset);
}
result.add(stageVo);
totalBenjin = totalBenjin.add(stageVo.getPrincipal());
totalLixi = totalLixi.add(stageVo.getInterset());
// System.out.println("本金:"+stageVo.getPrincipal());
// System.out.println("利息:"+stageVo.getInterset());
// System.out.println("月本息和:"+stageVo.getPrincipalAddInterset());
}
// System.out.println("本金和:"+totalBenjin);
// System.out.println("利息和:"+totalLixi);
return result;
}
/**
* 等本等息 —— 计算前n期之和,时间单位支持年、月、日(按照公式计算)
*
但对最后一期本金或利息不会“多退少补”计算,本方法完全按照公式走
* @param n 要计算的期数,单位:月
* @param periods 分期数
* @param rate 年利率
* @param unit 时间单位
* @param money 总金额
* @param roundingMode 小数位取值方式
* @return 如果要获取的期数超过分期期数,则返回总本息信息
*/
public static LoanProductStageVo pEqualI_stageSumNum(int n, int periods, BigDecimal rate, TimeUnits unit, BigDecimal money, RoundingMode roundingMode) {
rate = rate.divide(RATE_CONVERT_PERCENT, roundingMode);
LoanProductStageVo stageVo = new LoanProductStageVo();
if(n <= 0) {
stageVo.setInterset(new BigDecimal("0"));
stageVo.setPrincipal(new BigDecimal("0"));
return stageVo;
}
final BigDecimal PERIODS = new BigDecimal(periods+"");
final BigDecimal N = new BigDecimal(n + "");
BigDecimal monthPrincipal = money.divide(PERIODS, 2, roundingMode);//月本金
BigDecimal monthInterset = money.multiply(rate).divide(MONTH_YEAR, 2, roundingMode);//月利息
BigDecimal nSumPrincipal = null, nSumInterset = null;
boolean nEp = true;//期数校验
if(TimeUnits.year == unit) {
if(n * 12 <= periods) {
nSumPrincipal = monthPrincipal.multiply(N).multiply(MONTH_YEAR);
nSumInterset = monthInterset.multiply(N).multiply(MONTH_YEAR);
nEp = false;
}
} else if(TimeUnits.month == unit) {
if(n <= periods) {
nSumPrincipal = monthPrincipal.multiply(N);
nSumInterset = monthInterset.multiply(N);
nEp = false;
}
} else if(TimeUnits.day == unit) {
if(n / 30 <= periods) {
nSumPrincipal = monthPrincipal.multiply(N).divide(DAY_MONTH, 2, roundingMode);
nSumInterset = monthInterset.multiply(N).divide(DAY_MONTH, 2, roundingMode);
nEp = false;
}
} else {
return null;
}
//n的时间大于分期数情况,计算总和
if(nEp) {
nSumPrincipal = monthPrincipal.multiply(PERIODS);
nSumInterset = monthInterset.multiply(PERIODS);
}
stageVo.setPrincipal(nSumPrincipal);
stageVo.setInterset(nSumInterset);
// System.out.println("等本等息:前"+n+unit.getText()+"信息");
// System.out.println("本金和:"+stageVo.getPrincipal());
// System.out.println("利息和:"+stageVo.getInterset());
return stageVo;
}
}
TimeUnits类
package com.enums;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
@JsonSerialize(as = StringValueTextEnum.class)
public enum TimeUnits implements StringValueTextEnum {
/** 分 */
minute("minute", "分"),
/** 时 */
hour("hour", "时"),
/** 天 */
day("day", "天"),
/** 周 */
week("week", "周"),
/** 月 */
month("month", "月"),
/** 年 */
year("year", "年");
private String value;
private String text;
private TimeUnits(String value, String text) {
this.value = value;
this.text = text;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
LoanProductStageVo公共类
package com.bo;
import java.math.BigDecimal;
/**
* 分期计算结果类
*
*/
public class LoanProductStageVo {
//本息和
private BigDecimal principalAddInterset;
//本金
private BigDecimal principal;
//利息
private BigDecimal interset;
/**
* 本息和(单独的公式计算,每月相同)
* @return
*/
public BigDecimal getPrincipalAddInterset() {
if(principalAddInterset == null && principal != null && interset != null) {
principalAddInterset = principal.add(interset);
}
return principalAddInterset;
}
/**
* 本息和(单独的公式计算,每月相同)
* @param principalAddInterset
*/
public void setPrincipalAddInterset(BigDecimal principalAddInterset) {
this.principalAddInterset = principalAddInterset;
}
/**
* 本金
* @return
*/
public BigDecimal getPrincipal() {
return principal;
}
/**
* 本金
* @param principal
*/
public void setPrincipal(BigDecimal principal) {
this.principal = principal;
}
/**
* 利息
* @return
*/
public BigDecimal getInterset() {
return interset;
}
/**
* 利息
* @param interset
*/
public void setInterset(BigDecimal interset) {
this.interset = interset;
}
}
就到这了,希望这些能帮到大家!