还在头疼每月房贷还款?这个房贷计算机让你一目了然
本文分享自華為云社區(qū)《房貸計(jì)算器-從原理、計(jì)算到提前還款和可視化》,作者: 蜉蝣與海 。
前言
最近各地樓市震蕩不斷,2022年12月份以來(lái)不少銀行紛紛降息,隨后更是引發(fā)了一波提前還款的大潮。不少地區(qū)樓市相關(guān)的微信小程序也自帶了貸款計(jì)算器、提前還款計(jì)算器等工具,通過(guò)這些工具人們可以很容易的了解每期還款本金、等額本金/本息的利息差異、提前還款節(jié)省利息的問(wèn)題。
了解這些計(jì)算工具的相關(guān)原理,可以做到心中有數(shù),臨危不慌。
注:
如何計(jì)算利息
背景:等額本金和等額本息的共同點(diǎn)
了解過(guò)貸款的小伙伴都知道,貸款有等額本金和等額本息這兩種方式,前者每月還款的本金相同,利息逐月遞減;后者每月還款額相同,剛開(kāi)始還款時(shí)利息還的多,后面本金還的逐漸增多。參考網(wǎng)上討論利息計(jì)算的諸多文章,兩個(gè)模型理論上,都有下列共同特點(diǎn):
利息按月利率計(jì)算,一月一期
按期還款情況下當(dāng)月應(yīng)還利息只由未還完的本金決定
每月還款額除了未還本金產(chǎn)生的全部利息外,剩下的金額應(yīng)該全部用于償還本金
像最近部分銀行提出的先息后本(先還利息若干年,最后一次性償還本金)則不符合這個(gè)條件。
還款額的計(jì)算
前陣子,院長(zhǎng)有位朋友在惠州買了套120平米的房,總價(jià)125萬(wàn)左右,大約貸了87.5萬(wàn)。 辦房貸的時(shí)候,他聽(tīng)從銷售的建議,選了等額本息的還款方式。每個(gè)月固定還5726.39元。這個(gè)還款額度在他的承受范圍之內(nèi),因此就選了。 那假如選擇等額本金呢?第一個(gè)月要還的金額為7218.75元,此后每個(gè)月少還14.89元,直至20年后還完。知乎文章為什么買房貸款,最好選擇等額本金?中提到了一個(gè)例子:
通過(guò)描述可知,貸款87.5萬(wàn),貸20年,等額本息每月還款5726.39元,等額本金首月還款7218.75元。假設(shè)文中的貸款未使用公積金,計(jì)算時(shí)利率為固定利率,根據(jù)網(wǎng)上的貸款計(jì)算器可知此時(shí)的貸款年利率為4.9%。
以這個(gè)例子為例,簡(jiǎn)單說(shuō)明等額本金和等額本息的計(jì)算方法:
首先貸20年,按月分期,貸款為 20 × 12 = 240期。 年利率4.9%,月利率為 0.049 ÷ 12 = 0.004983 即0.4083%。
等額本金?情況下:
- 每月應(yīng)還本金=總本金 ÷ 期數(shù)
- 每月應(yīng)還利息=剩余本金×月利率
- 每月還款額=每月應(yīng)還本金 + 每月應(yīng)還利息
在這個(gè)例子中:
- 每月應(yīng)還本金為875000÷240=3645.83元
- 首月應(yīng)還利息為875000×0.4083%=3572.92元
- 首月應(yīng)還:3645.83 + 3572.92 = 7218.75元。
- 第2月剩余本金為875000 - 3645.83 = 871354.17元。
- 第2月應(yīng)還利息為871354.17×0.4083%=3558.03元。
- 第2月應(yīng)還:3645.83 + 3558.03 = 7203.86元。
將這段邏輯抽象為代碼有:
import matplotlib.pyplot as plt import numpy as np def averageCapital(months, principal, rate):month_rate = rate / 12monthly_capital = principal / monthsinterests = [0] * monthscapitals = [0] * monthsleft_principal = [0] * monthsleft_principal[0] = principaltotal_payment = [0] * monthsfor i in range(0, months):interests[i] = left_principal[i] * month_ratecapitals[i] = monthly_capitaltotal_payment[i] = monthly_capital + interests[i]if i + 1 < months:left_principal[i + 1] = left_principal[i] - monthly_capitalreturn capitals, interests, total_payment為了便于查看再封裝一個(gè)打印成表格的函數(shù):
import pandas as pd def drawTable(months, fn, *args, **kwargs):capitals, interests, total_payment = fn(months, *args, **kwargs)paid_capital = [0] * monthspaid_interests = [0] * monthspaid_capital[0] = capitals[0]paid_interests[0] = interests[0]for x in range(1, months):paid_capital[x] = paid_capital[x - 1] + capitals[x]paid_interests[x] = paid_interests[x - 1] + interests[x]origin = pd.DataFrame([total_payment, capitals, interests, paid_capital, paid_interests])return pd.DataFrame(origin.values.T, columns=['還款額','還款本金','還款利息','已還本金','已還利息'], index=np.arange(1, months + 1))我們運(yùn)行一下知乎上的例子,看看頭幾年還款的本金、利息等:
pd.options.display.float_format = '{:.2f}'.format drawTable(12 * 20, averageCapital, 875000, 0.049)[0:10]可以看到和文中描述一致,使用微信房小團(tuán)小程序,也可以打印出一致的結(jié)果。
等額本息?的計(jì)算方法有些復(fù)雜,參考用Python深度解讀房貸利率文中的解法,設(shè)A為本金,第i個(gè)月月末所欠銀行本金為Ai,每月所還貸款總額為X,月利率為β, 則有:
由于最后一期時(shí)剩余本金為0,可反解得:
這里m為總期數(shù)(在剛剛的例子中,m=240)。而后就可以使用與等額本金計(jì)算中類似的邏輯,從第一期所還利息開(kāi)始,反推每期的利息與本金。具體代碼如下:
def averageCapitalPlusInterest(months, principal, rate):month_rate = rate / 12monthly_payment = principal * month_rate * (1 + month_rate) ** months / ((1 + month_rate) ** months - 1)interests = [0] * monthscapitals = [0] * monthsleft_principal = [0] * monthsleft_principal[0] = principaltotal_payment = [0] * monthsfor i in range(0, months):total_payment[i] = monthly_paymentinterests[i] = left_principal[i] * month_ratecapitals[i] = total_payment[i] - interests[i]if i + 1 < months:left_principal[i + 1] = left_principal[i] - capitals[i]return capitals, interests, total_payment我們運(yùn)行一下知乎上的例子,看看等額本息模式下第8年附近,到底還了多少利息和本金:
drawTable(12 * 20, averageCapitalPlusInterest, 875000, 0.049)[90:100]可以看到第96期(第8年年終)時(shí),本金還了25萬(wàn),但利息已經(jīng)還了近30萬(wàn)了,和之前文中例子的數(shù)據(jù)是可以對(duì)得上的。
還款可視化
剛剛我們已經(jīng)將還款的各項(xiàng)數(shù)據(jù)以表格的形式打印。此外我們還可以借助python的能力,打印還款的柱狀圖。
import numpy as np def printStatistics(capitals, interests, total_payment, months):print("總本金:" + str(np.sum(capitals)))print("總利息:" + str(np.sum(interests)))print("總利息/總本金" + str(np.sum(interests)/np.sum(capitals)))print("首月還款 %.2f 末月還款: %.2f" % (total_payment[0], total_payment[months - 1])) def drawDiagram(months, fn, *args, **kwargs):capitals, interests, total_payment = fn(months, *args, **kwargs)printStatistics(capitals, interests, total_payment, months)month_array = np.arange(1, months + 1, 1)height = interestsplt.bar(month_array, capitals, width=0.2, align='center', color='red')plt.bar(month_array, interests, width=0.2, align='center', color='blue', bottom=capitals)plt.show()再跑一下知乎的例子,繪制等額本金和等額本息的還款柱狀圖:
drawDiagram(12 * 20, averageCapital, 875000, 0.049)如圖,藍(lán)色是所還利息,紅色是所還本金。可以看出本金每月不變,利息逐月遞減的特征。
等額本息情況下:
drawDiagram(12 * 20, averageCapitalPlusInterest, 875000, 0.049)也能看出所繪圖形和等額本息的含義基本一致。
另外部分城市可以公積金貸款,以杭州為例,目前杭州公積金充足情況下可貸50w-60w,這里考慮一下公積金的情況:
def averageCapitalWithPublicFund(months, principal1, rate1, principal2, rate2):a, b, c = averageCapital(months, principal1, rate1)a1, b1, c1 = averageCapital(months, principal2, rate2)return np.sum([a,a1],axis=0).tolist(), np.sum([b,b1],axis=0).tolist(), np.sum([c,c1],axis=0).tolist() drawTable(12 * 20, averageCapitalWithPublicFund, 700000, 0.041, 300000, 0.031)[0:10]這里算了下商貸70w(利率4.1%),公積金貸30w(利率3.1%)下組合貸款的情況,和微信小程序房小團(tuán)的計(jì)算是一致的。
提前還款相關(guān)原理
再來(lái)討論下提前還款。如果知乎文中買房的那位,在貸款1年后提前還款10w會(huì)怎樣呢?了解一點(diǎn)背景知識(shí)的朋友,都知曉提前還款分兩種情況:
- 年限不變,月供減少
- 年限縮短,月供不變
現(xiàn)在分情況討論,并給出計(jì)算函數(shù)。
注:notebook中所有計(jì)算結(jié)果均在微信房小團(tuán)小程序上得到互相驗(yàn)證。
年限不變,月供減少
這種情況下,相當(dāng)于在提前還款月之后重新做了一次貸款。我們首先對(duì)剛剛的計(jì)算函數(shù)進(jìn)行一定的簡(jiǎn)化,抽象一下公共的部分。
def normalPaid(months, principal, rate, capitalAveraged):month_rate = rate / 12monthly_capital = principal / monthsmonthly_payment = principal * month_rate * (1 + month_rate) ** months / ((1 + month_rate) ** months - 1)interests = [0] * monthscapitals = [0] * monthsleft_principal = [0] * monthsleft_principal[0] = principaltotal_payment = [0] * monthsfor i in range(0, months):interests[i] = left_principal[i] * month_rateif capitalAveraged:capitals[i] = monthly_capitaltotal_payment[i] = monthly_capital + interests[i]else:total_payment[i] = monthly_paymentcapitals[i] = total_payment[i] - interests[i]if i + 1 < months:left_principal[i + 1] = left_principal[i] - capitals[i]return capitals, interests, total_payment drawTable(12 * 20, normalPaid, 875000, 0.049, False)[10:14] drawTable(12 * 20, normalPaid, 875000, 0.049, True)[10:14]可以看到抽象出公共結(jié)構(gòu)后,前后的計(jì)算結(jié)果并沒(méi)有發(fā)生變化。
考慮年限不變提前還款的情況,這里將每次提前還款的時(shí)間和金額組成python的元組,若干個(gè)(賬期,還款金額)元組組成一個(gè)list輸入函數(shù)。函數(shù)首先計(jì)算正常情況下的還款信息,而后根據(jù)提前還款信息,修改提前還款日的剩余本金,并從各個(gè)提前還款日重新計(jì)算剩余還款。
def extraPaidWithFixedPeriod(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] * monthsleft_principal[0] = principalfor 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):month_rate = rate / 12monthly_capital = left_principal[offset] / left_monthsmonthly_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_rateif capitalAveraged:capitals[offset + i] = monthly_capitaltotal_payment[offset + i] = monthly_capital + interests[offset + i]else:total_payment[offset + i] = monthly_paymentcapitals[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]returnfor x,y in extraPaidList:capitals[x] = capitals[x] + yleft_principal[x + 1] = left_principal[x] - capitals[x]total_payment[x] = capitals[x] + interests[x]print("當(dāng)月需還 %.f 剩余本金 %.f" %(total_payment[x], left_principal[x + 1]))normalPaidOffset(months - x - 1, left_principal[x + 1], rate, capitalAveraged, x + 1)printStatistics(originCapital, originInterests, originTotal, months)print("")printStatistics(capitals, interests, total_payment, months)print("節(jié)省利息 %.2f" % (np.sum(originInterests) - np.sum(interests)))return capitals, interests, total_payment, originTotal, originInterests再定義幾個(gè)函數(shù)對(duì)提前還款節(jié)省的利息進(jìn)行可視化。
def drawDiagramExtraPaid(months, capitals, interests, originalTotal, originalInterests, showOriginTotal=True):month_array = np.arange(1, months + 1, 1)capital_with_origin_interest = [0] * monthsheight = interestsfor x in range(1, months):capital_with_origin_interest[x] = capitals[x] + originalInterests[x]l1 = plt.bar(month_array, originalTotal if showOriginTotal else capital_with_origin_interest, width=0.2, align='center', color='yellow')l2 = plt.bar(month_array, capitals, width=0.2, align='center', color='red')l3 = plt.bar(month_array, interests, width=0.2, align='center', color='blue', bottom=capitals)# plt.legend(handles = [l1, l2,l3], labels = ['每月少還' if showOriginTotal else '節(jié)省利息', '本金','利息'], loc = 'best',fontsize=20)plt.ylim(0, (capitals[0]+interests[0])*1.1)plt.show() def drawTableExtraPaid(months, capitals, interests, total_payment, originalTotal, originalInterests):paid_capital = [0] * monthspaid_interests = [0] * monthssaved_money = [0] * monthspaid_capital[0] = capitals[0]paid_interests[0] = interests[0]for x in range(1, months):paid_capital[x] = paid_capital[x - 1] + capitals[x]paid_interests[x] = paid_interests[x - 1] + interests[x]saved_money[x] = saved_money[x - 1] + (originalInterests[x] - interests[x] )origin = pd.DataFrame([total_payment, capitals, interests, paid_capital, paid_interests,saved_money])return pd.DataFrame(origin.values.T, columns=['還款額','還款本金','還款利息','已還本金','已還利息','累計(jì)節(jié)省'], index=np.arange(1, months + 1))通過(guò)參數(shù)showOriginTotal的取值,可以分別繪制每月少還的錢與當(dāng)月節(jié)省利息的情況。下面分別繪制了等額本金和等額本息情況下,87.5萬(wàn)貸20年,在第一年還10萬(wàn)后還款和利息的變化情況。
a, b, c, d, e = extraPaidWithFixedPeriod(12 * 20, 875000, 0.049, True, [(13,100000)]) drawDiagramExtraPaid(12 * 20, a, b, d, e) drawDiagramExtraPaid(12 * 20, a, b, d, e, False) drawTableExtraPaid(12 * 20, a, b, c, d, e)[10:20]?
a, b, c, d, e = extraPaidWithFixedPeriod(12 * 20, 875000, 0.049, False, [(13,100000)]) drawDiagramExtraPaid(12 * 20, a, b, d, e) drawDiagramExtraPaid(12 * 20, a, b, d, e, False) drawTableExtraPaid(12 * 20, a, b, c, d, e)[10:20]可以很方便地看出節(jié)省利息在每個(gè)月還款額中的比重。
月供不變,年限縮短
這種情況下提前還款導(dǎo)致后續(xù)每個(gè)月產(chǎn)生的利息少了,但是月供沒(méi)變,相當(dāng)于后續(xù)每個(gè)月額外多還了本金。但是在各類提前還款計(jì)算器的計(jì)算中,月供并不是和之前相同的,經(jīng)過(guò)反復(fù)的計(jì)算后和網(wǎng)上的貸款計(jì)算器結(jié)果最終一致,發(fā)現(xiàn)各類提前還款計(jì)算器隱含了下列約束:
- 提前還款相當(dāng)于用剩余本金新做一個(gè)貸款。
- “月供”不是真的不變。而是通過(guò)縮短年限方式,使得新貸款首月月供盡可能和當(dāng)前月供相當(dāng)。
- 如果是等額本金模式,新貸款首月月供中,償還本金并未增多,需要略低于上月償還本金,等額本息模式則無(wú)此約束。
想想這個(gè)邏輯也有道理,如果真的“月供不變”,那么等額本金模式下提前還款后,后續(xù)每個(gè)月償還的本金都會(huì)比新做貸款的償還的本金多,相當(dāng)于后續(xù)每個(gè)月都在提前還款,后續(xù)每個(gè)月月供本金就不能稱為“等額”了。
我們下面先寫個(gè)求解首月月供的函數(shù),以及通過(guò)縮短年限逼近上月月供總額和月供本金的函數(shù)。而后計(jì)算“月供不變,年限縮短”模式下節(jié)省的具體利息。
可以看出,雖然縮短年限的本質(zhì)也是重新做一次貸款,但確實(shí)可以節(jié)省很多利息。
小結(jié)
本文初稿寫于華為云AI-Gallery貸款計(jì)算器-從原理、公式到提前還款和可視化,通過(guò)頁(yè)面進(jìn)入CodeLab可以直接在界面上調(diào)整參數(shù)進(jìn)行房貸利息、提前還款等相關(guān)計(jì)算,計(jì)算過(guò)程原理直觀,配合可視化方便理解,歡迎開(kāi)發(fā)者前往體驗(yàn)。
整篇文章帶大家了解了不同房貸貸款方式的差異,以及對(duì)房貸利息計(jì)算、提前還款的原理做了較為細(xì)致的剖析和數(shù)據(jù)可視化。后續(xù)在面對(duì)貸款利息計(jì)算的問(wèn)題時(shí),可以直面原理、心中有數(shù)、臨危不慌。
參考資料
[1]用Python深度解讀房貸利率
[2]為什么買房貸款,最好選擇等額本金?
[3]杭州房小團(tuán)微信小程序-貸款計(jì)算
[4]杭州房小團(tuán)微信小程序-提前還款
點(diǎn)擊關(guān)注,第一時(shí)間了解華為云新鮮技術(shù)~
總結(jié)
以上是生活随笔為你收集整理的还在头疼每月房贷还款?这个房贷计算机让你一目了然的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 水稻秸秆当饲料综合利用雷州观摩会 国稻种
- 下一篇: 欧姆龙温控器参数笔记(一)