一、等额本息
也就是本金+利息每月是相等的,即每月还款额是等额的。
随着占用资金成本的减少所产生的每月利息也会对应减少,在每月还款额不变的情况下,每月需还款本金是递增的。
计算步骤如下:
1.按照推导公式优先计算每月还款额。
每月还款额=(贷款本金月利率*(1+月利率)^还款月数))/((1+月利率)^还款月数-1)
2.根据剩余本金计算当期利息。
利息=剩余本金*月利率
3.计算当期本金。
本金=每月还款额-利息。
4.最后一期使用倒推法先计算本金。
本金=贷款金额-前期合计本金
二、等额本金
本金每月是等额的,随着占用资金成本的减少所产生的每月利息也会对应减少,在本金不变的情况下,每月还款额是逐月递减的。
计算步骤如下:
1.按照推导公式优先计算每月本金。
本金=贷款金额/贷款期数
2.根据剩余本金计算当期利息。
利息=剩余本金*月利率
3.计算每月还款额。
每月还款额=本金+利息
4.最后一期使用倒推法先计算本金。
本金=贷款金额-前期合计本金
5.计算最后一期利息。
利息=剩余本金*月利率
6.计算最后一期还款额。
每月还款额=本金+利息
三、代码实现
计算工具类
public class LoanCalculator {
/**
* 等额本息计算公式
*
* @param param - 贷款信息
*/
public static Map<Integer, Loan> calcEqCapitalAndInterest(Param param) {
Map<Integer, Loan> result = new TreeMap<>(Comparator.comparingInt(o -> o));
// 总贷款期限
int loanPeriods = param.getLoanPeriods();
// 贷款月利率
BigDecimal monthRate = param.getMonthRate();
// 每月还款金额
BigDecimal monthAmount = calMonthRepayment(param);
// 剩余本金
BigDecimal balance = param.getLoanAmount();
// 出最后一期本金和
BigDecimal noneLastCapitalTotal = BigDecimal.ZERO;
Loan loan;
LocalDate date = ObjectsUtil.isNull(param.getStartRepaymentDate()) ? LocalDate.now() : param.getStartRepaymentDate();// 还款日
for (int i = 1; i <= loanPeriods; i++) {
loan = new Loan().setPeriod(i).setAmount(monthAmount);// 每月还款金额
loan.setInterest(calMonthInterest(balance, monthRate));// 利息=剩余本金*月利率
loan.setCapital(monthAmount.subtract(loan.getInterest()));// 本金=每月还款额-利息
loan.setCapitalBalance(balance = balance.subtract(loan.getCapital()));// 当期剩余本金
loan.setRepaymentDate(date.plusMonths(i - 1));// 还款日
result.put(i, loan);
if (i != loanPeriods) {
noneLastCapitalTotal = noneLastCapitalTotal.add(loan.getCapital());
}
}
// 修正最后一期的数值
Loan lastLoan = result.get(loanPeriods);
lastLoan.setCapital(param.getLoanAmount().subtract(noneLastCapitalTotal));// 最后一期使用倒推法先计算本金,本金=贷款金额-前期合计本金。
lastLoan.setInterest(monthAmount.subtract(lastLoan.getCapital()));// 计算最后一期利息,当期利息=每月还款额-本金。
lastLoan.setCapitalBalance(BigDecimal.ZERO);// 最后一期剩余本金为零,剩余的全部加在最后一期本金计算利息
return result;
}
/**
* 等额本金计算公式
*
* @param param - 贷款信息
*/
public static Map<Integer, Loan> calcEqCapital(Param param) {
Map<Integer, Loan> result = new TreeMap<>(Comparator.comparingInt(o -> o));
// 总贷款期限
int loanPeriods = param.getLoanPeriods();
// 贷款月利率
BigDecimal monthRate = param.getMonthRate();
// 每月还款本金
BigDecimal monthCapital = calMonthCapital(param);
// 剩余本金
BigDecimal balance = param.getLoanAmount();
Loan loan;
LocalDate date = ObjectsUtil.isNull(param.getStartRepaymentDate()) ? LocalDate.now() : param.getStartRepaymentDate();// 还款日
for (int i = 1; i <= loanPeriods; i++) {
loan = new Loan().setPeriod(i).setCapital(monthCapital);// 本金
loan.setInterest(calMonthInterest(balance, monthRate));// 利息=剩余本金*月利率
loan.setAmount(loan.getCapital().add(loan.getInterest()));// 每月还款额=本金+利息
loan.setCapitalBalance(balance = balance.subtract(monthCapital));// 当期剩余本金
loan.setRepaymentDate(date.plusMonths(i - 1));// 还款日
result.put(i, loan);
}
// 修正最后一期的数值
Loan lastLoan = result.get(loanPeriods);
lastLoan.setCapital(param.getLoanAmount().subtract(monthCapital.multiply(BigDecimal.valueOf(loanPeriods - 1))));// 最后一期使用倒推法先计算本金,本金=贷款金额-前期合计本金。
lastLoan.setInterest(calMonthInterest(lastLoan.getCapital(), monthRate));// 计算最后一期利息,利息=剩余本金*月利率。
lastLoan.setAmount(lastLoan.getCapital().add(lastLoan.getInterest()));// 计算最后一期还款额,每月还款额=本金+利息。
lastLoan.setCapitalBalance(BigDecimal.ZERO);// 最后一期剩余本金为零,剩余的全部加在最后一期本金计算利息
return result;
}
/**
* 计算等额本息还款月供
*
* @param param 贷款信息包含贷款金额、年利率、期限
* @return 等额本息还款月供
*/
private static BigDecimal calMonthRepayment(Param param) {
BigDecimal totalRate = param.getMonthRate().add(BigDecimal.ONE);
return fromMetadata(param.getLoanAmount().multiply((param.getMonthRate().multiply(totalRate.pow(param.getLoanPeriods()))).divide(totalRate.pow(param.getLoanPeriods()).subtract(BigDecimal.ONE), RoundingMode.HALF_EVEN)), 2);
}
/**
* 计算等额本金每月还款本金
*
* @param param 贷款信息包含贷款金额、年利率
* @return 等额本金每月还款本金
*/
private static BigDecimal calMonthCapital(Param param) {
return fromMetadata(param.getLoanAmount().divide(new BigDecimal(param.getLoanPeriods()), 2, RoundingMode.HALF_EVEN), 2);
}
/**
* 计算利息
*
* @param restCapital 剩余本金
* @param monthRate 月利率
* @return 当期利息
*/
private static BigDecimal calMonthInterest(BigDecimal restCapital, BigDecimal monthRate) {
return fromMetadata(restCapital.multiply(monthRate), 2);
}
/**
* 银行家算法截取指定位数
*
* @param bigDecimal 待格式化数据
* @param digit 截取位数
*/
static BigDecimal fromMetadata(BigDecimal bigDecimal, int digit) {
return bigDecimal.setScale(digit, RoundingMode.HALF_EVEN);
}
}
辅助类
/**
* 贷款数据计算参数
*/
@Getter
@Setter
public class Param {
private final static BigDecimal monthTimes = new BigDecimal("12");
/**
* 贷款金额
*/
private BigDecimal loanAmount;
/**
* 贷款年利率
*/
private BigDecimal yearRate;
/**
* 贷款期限(月)
*/
private int loanPeriods;
/**
* 首期还款日
*/
private LocalDate startRepaymentDate = LocalDate.now();
public BigDecimal getMonthRate() {
return LoanCalculator.fromMetadata(yearRate.divide(monthTimes, 8, RoundingMode.HALF_EVEN), 8);
}
}
/**
* 贷款信息
*/
@ToString
@Getter
@Setter
@Accessors(chain = true)
public class Loan implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 期数
*/
private Integer period;
/**
* 还款日
*/
private LocalDate repaymentDate;
/**
* 月还款金额
*/
private BigDecimal amount;
/**
* 月还款本金
*/
private BigDecimal capital;
/**
* 月还款利息
*/
private BigDecimal interest;
/**
* 剩余应还本金
*/
private BigDecimal capitalBalance;
}