可以很方便地看出节省利息在每个月还款额中的比重。
这种情况下提前还款导致后续每个月产生的利息少了,但是月供没变,相当于后续每个月额外多还了本金。但是在各类提前还款计算器的计算中,月供并不是和之前相同的,经过反复的计算后和网上的贷款计算器结果最终一致,发现各类提前还款计算器隐含了下列约束:
- 提前还款相当于用剩余本金新做一个贷款。
- “月供”不是真的不变。而是通过缩短年限方式,使得新贷款首月月供尽可能和当前月供相当。
- 如果是等额本金模式,新贷款首月月供中,偿还本金并未增多,需要略低于上月偿还本金,等额本息模式则无此约束。
想想这个逻辑也有道理,如果真的“月供不变”,那么等额本金模式下提前还款后,后续每个月偿还的本金都会比新做贷款的偿还的本金多,相当于后续每个月都在提前还款,后续每个月月供本金就不能称为“等额”了。
我们下面先写个求解首月月供的函数,以及通过缩短年限逼近上月月供总额和月供本金的函数。而后计算“月供不变,年限缩短”模式下节省的具体利息。
def getFirstPaid(months, principal, rate, capitalAveraged):
month_rate = rate / 12
monthly_capital = principal / months
monthly_payment = principal * month_rate * (1 month_rate) ** months / ((1 month_rate) ** months - 1)
interests1 = principal * month_rate
if capitalAveraged:
return monthly_capital interests1, monthly_capital
else:
return monthly_payment, monthly_payment - interests1
def getLeftMonths(leftMonthsMax, capitalPaidMax, paidMax, leftPrincipal, rate, capitalAveraged):
lastPaid, lastCapitalPaid, lastMonths = 0, 0, 0
for i in range(leftMonthsMax, 1, -1):
paid, capitalPaid = getFirstPaid(i, leftPrincipal, rate, capitalAveraged)
if paid > paidMax or (capitalAveraged and capitalPaid > capitalPaidMax):
return lastMonths, lastPaid, lastCapitalPaid
else:
lastPaid, lastCapitalPaid, lastMonths = paid, capitalPaid, i
def extraPaidWithFixedPaid(months, principal, rate,
capitalAveraged, extraPaidList: list):
capitals, interests, total_payment = normalPaid(
months, principal, rate, capitalAveraged)
extraPaidList.sort(key=lambda x: x[0])
originCapital, originInterests, originTotal = capitals.copy(), interests.copy(), total_payment.copy()
left_principal = [0] * months
left_principal[0] = principal
for x in range(0, months):
if x < months - 1:
left_principal[x 1] = left_principal[x] - capitals[x]
def normalPaidOffset(left_months, principal, rate,
capitalAveraged, offset, left_months2):
month_rate = rate / 12
monthly_capital = left_principal[offset] / left_months
monthly_payment = left_principal[offset] * month_rate * (1 month_rate) ** left_months / ((1 month_rate) ** left_months - 1)
for i in range(0, left_months):
interests[offset i] = left_principal[offset i] * month_rate
if capitalAveraged:
capitals[offset i] = monthly_capital
total_payment[offset i] = monthly_capital interests[offset i]
else:
total_payment[offset i] = monthly_payment
capitals[offset i] = total_payment[offset i] - interests[offset i]
if i == 0:
print("次月还款 %.2f" % total_payment[offset i])
if offset i 1 < months:
left_principal[offset i 1] = left_principal[offset i] - capitals[offset i]
for i in range(left_months, left_months2):
interests[offset i] = 0
capitals[offset i] = 0
total_payment[offset i] = 0
return
realMonth = months
for x, y in extraPaidList:
capitalParam = capitals[x]
capitals[x] = capitals[x] y
left_principal[x 1] = left_principal[x] - capitals[x]
total_payment[x] = capitals[x] interests[x]
maxMonth, maxPaid, maxPaidCapital = getLeftMonths(months - x - 1, capitalParam, total_payment[x - 1], left_principal[x 1], rate, capitalAveraged)
normalPaidOffset(maxMonth, left_principal[x 1], rate, capitalAveraged, x 1, months - x - 1)
realMonth = x 1 maxMonth
print("当月需还 %.2f 剩余本金 %.2f 下月需还:%.2f 原本剩余账期:%d,当前剩余账期:%d, 账期缩短:%d" %(total_payment[x], left_principal[x 1],total_payment[x 1], months - x - 1,maxMonth, months - x - 1 - maxMonth))
printStatistics(originCapital, originInterests, originTotal, months)
print("")
printStatistics(capitals, interests, total_payment, realMonth)
print("节省利息 %.2f" % (np.sum(originInterests) - np.sum(interests)))
return capitals, interests, total_payment, originTotal, originInterests
a, b, c, d, e = extraPaidWithFixedPaid(12 * 20, 875000, 0.049, True, [(13, 100000)])
drawDiagramExtraPaid(12 * 20, a, b, d, e)
drawTableExtraPaid(12 * 20, a, b, c, d, e)[10:20]