根据网上的网贷计算公式实现了一个Python计算器,能够计算房贷的还款计划,支持等额本金和等额本息两种方式。

开始以为套用一下公式就可以了,做完才知道远没有看起来那么简单。

根据房贷计算公式:

等额本息计算方式

每月还款额=贷款本金×[月利率×(1+月利率)^还款月数]÷[(1+月利率)^还款月数-1]

总支付利息:总利息=还款月数×每月月供额-贷款本金

每月应还利息=贷款本金×月利率×〔(1+月利率)^还款月数-(1+月利率)^(还款月序号-1)〕÷〔(1+月利率)^还款月数-1〕

每月应还本金=贷款本金×月利率×(1+月利率)^(还款月序号-1)÷〔(1+月利率)^还款月数-1〕

总利息=还款月数×每月月供额-贷款本金

等额本金计算方式

每月月供额=(贷款本金÷还款月数)+(贷款本金-已归还本金累计额)×月利率

每月应还本金=贷款本金÷还款月数

每月应还利息=剩余本金×月利率=(贷款本金-已归还本金累计额)×月利率。

每月月供递减额=每月应还本金×月利率=贷款本金÷还款月数×月利率

总利息=还款月数×(总贷款额×月利率-月利率×(总贷款额÷还款月数)*(还款月数-1)÷2+总贷款额÷还款月数) [1]

==========================================================================================

第一版实现代码如下:

#  coding=utf-8
# 房贷计算器,计算等额本金和等额本息得计算器,可以导出Excel

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow,QTableWidgetItem,QMessageBox
from w7 import Ui_MainWindow

import xlwt
import xlrd

def outExlTo(tList):

wb = xlwt.Workbook(encoding="utf-8")
    ws = wb.add_sheet("confirmt")
    listTit=['期数(月)','应还金额','应还本金','应还利息']
    t1Con=0
    for t1 in listTit:
        ws.write(0,t1Con,t1)
        t1Con=t1Con+1
    rownum=1
    columnum=0
    for row in tList:
        for column in row:
            
            outT=float(column)
            #设置每个位置的文本值
            ws.write(rownum,columnum,outT)
            #print(rownum,columnum,item)
            columnum=columnum+1
        columnum=0
        rownum=rownum+1

wb.save('outPut.xls')  
    
def isFloat(str):
    try:
        float(str)
    except ValueError:
        return False
    else:
        return True

def isInt(str):
    try:
        int(str)
    except ValueError:
        return False
    else:
        return True
#等额本金计算
def calF1(loan,monthRate,period):
    # 每月应还本金,初始化列表有period*12个元素
    monthPrincipalPayment = [loan/(period*12)]*period*12
    #print(monthPrincipalPayment)
    # 每月应还本息
    monthInterestPayment = [(loan - loan*n/(period*12))*monthRate+loan/(period*12) for n in range(0,period*12)]
    print(monthInterestPayment)
    # 还款期数
    month = [n for n in range(1,period*12+1)]
    rowSet=[]
    for x in month:
        print(x)
        col=[]
        col.append(x)
        col.append(round(monthInterestPayment[x-1],2))
        col.append(round(monthPrincipalPayment[x-1],2))
        f_intD=round(round(monthInterestPayment[x-1],2)-round(monthPrincipalPayment[x-1],2),2) #计算应还利息
        col.append(f_intD)
        rowSet.append(col)
    print("等额本金计算:")
    #print(rowSet)
    return rowSet

#等额本息计算
def calF2(loan,monthRate,period):
    # 还款期数
    month = [n for n in range(1,period*12+1)]
    # 首月应还利息
    firstMonthInterest = loan*monthRate
    print(firstMonthInterest)
    # 每月应还本息
    monthPayment = (loan*monthRate*(1+monthRate)**(period*12))/((1+monthRate)**(period*12)-1)
    loanPI = [loan*(1+monthRate)-monthPayment]
    
    # 每期应还利息
    loanInterest = [loan*monthRate]
    
    for n in range(1, period*12):
        loanPI.append((loanPI[n-1]*(1+monthRate)-monthPayment))
        loanInterest.append(round(loanPI[n-1]*monthRate,2))
    
    # 每期应还本金
    loanPrincipal = [monthPayment-loanInterest[n] for n in range(0,len(loanInterest))]
    
    
    print(loanPrincipal)
    print(loanInterest)
    print(monthPayment)
    rowSet=[]
    for x in month:
        print(x)
        col=[]
        col.append(x)
        col.append(round(monthPayment,2))
        col.append(round(loanPrincipal[x-1],2))
        f_intD=round(round(monthPayment,2)-round(loanPrincipal[x-1],2),2) #计算应还利息
        col.append(f_intD)
        rowSet.append(col)
    print("等额本息计算:")
    #print(rowSet)
    return rowSet
#输出项检查
def checkInput(self):
    loan = self.textEdit.toPlainText() # 贷款金额,万元
    if isFloat(loan):
        #print('贷款金额是数字!');
        pass
    else:
        print('贷款金额不是数字!');
        QMessageBox.critical(self, '警告', '贷款金额不是数字!',QMessageBox.Yes )
        return None,None,None
    annualRate = self.textEdit_2.toPlainText() # 贷款年利率
    if isFloat(annualRate):
        #print('贷款年利率是数字!');
        pass
    else:
        print('贷款年利率不是数字!');
        QMessageBox.critical(self, '警告', '贷款年利率不是数字!',QMessageBox.Yes )
        return None,None,None
    
    period = self.textEdit_3.toPlainText() # 贷款期限30年
    if isInt(period):
        #print('贷款期限是整数!');
        pass
    else:
        print('贷款期限不是数字!');
        #show_message(self,'贷款期限不是数字!')
        QMessageBox.critical(self, '警告', '贷款期限不是整数!',QMessageBox.Yes )  
        return None,None,None
    return loan,annualRate,period
class MainWindow(QMainWindow,Ui_MainWindow):
    outPutList=[]
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        #loadUi('test.ui', self)
        self.setupUi(self)
        self.pushButton.clicked.connect(self.say)
        self.pushButton_2.clicked.connect(self.closew)
        self.pushButton_3.clicked.connect(self.outPExl)
    def show_message(self,errMsgstr):
        QMessageBox.critical(self,  errMsgstr)   
    def say(self):      
        try:  
            loan,annualRate,period=checkInput(self)
            if(loan is None):
                print("输出检查出问题了!")
                return
            print(loan)
            print(annualRate)
            print(period)
            f_load=float(loan)*10000
            f_monthRate = float(annualRate)/1200 # 贷款月利率
            int_period=int(period)
            retList=[]
            sFlag=self.comboBox.currentText()   # 获得当前内容
            if sFlag=='等额本金':
                
                retList=calF1(f_load,f_monthRate,int_period)
            else:
                retList=calF2(f_load,f_monthRate,int_period)
            self.tableWidget.setRowCount(len(retList))
            self.tableWidget.setColumnCount(4)
            self.tableWidget.setHorizontalHeaderLabels(['期数(月)','应还金额','应还本金','应还利息'])
            self.tableWidget.verticalHeader().hide()  #隐藏默认的行编号
           
            rownum=0
            columnum=0
            for row in retList:
                for column in row:
                    
                    item=QTableWidgetItem(str(column))
                    #设置每个位置的文本值
                    self.tableWidget.setItem(rownum,columnum,item)
                    #print(rownum,columnum,item)
                    columnum=columnum+1
                columnum=0
                rownum=rownum+1
            self.outPutList=retList
        except Exception as e:
            print(e)
    def closew(self):  
        try:                         
            self.close()
            sys.exit(app.exec())
        except Exception as e:
            print(e)
    #导出Excel        
    def outPExl(self):  
        try:  
            #print(self.outPutList)
            outExlTo(self.outPutList)                      
            print('导出成Excel文件!')
            QMessageBox.critical(self, '成功', '导出文件结果为outPut.xls!',QMessageBox.Yes )
        except Exception as e:
            print(e)

app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec())

========================================================================

窗体部分代码使用Qt5 Designer拖拽实现,就不贴代码了。

实现效果如下:

实现完成后,发现一个巨大的坑,包括各大网站/银行公布的计算器都有这个问题。总账和分账不平衡。

期数(月) 应还金额 应还本金 应还利息
1 17213.29 16213.29 1000
2 17213.29 16294.36 918.93
3 17213.29 16375.83 837.46
4 17213.29 16457.71 755.58
5 17213.29 16540 673.29
6 17213.29 16622.7 590.59
7 17213.29 16705.81 507.48
8 17213.29 16789.34 423.95
9 17213.29 16873.28 340.01
10 17213.29 16957.65 255.64
11 17213.29 17042.44 170.85
12 17213.29 17127.65 85.64
总计 206559.48 200000.06 6559.42

如上表所示:分期还的本金之和竟然不等于20万元。这样的账目是无法提交银行或者有对账要求的财务公司的。(有的网上的计算器,甚至应还本金+应还利息不等于应还金额。)

所以,必须要对算法改造。理论上的算法没有考虑元角分单位的不连续性,导致分期本金计算出现了累计误差。

思考后,决定对分期账目进行末期平衡修正,这样会导致最后一笔还款金额不再是等额本息。但是账务是可以平衡了。

调整后账目如下图:

期数(月) 应还金额 应还本金 应还利息
1 17213.29 16213.29 1000
2 17213.29 16294.36 918.93
3 17213.29 16375.83 837.46
4 17213.29 16457.71 755.58
5 17213.29 16540 673.29
6 17213.29 16622.7 590.59
7 17213.29 16705.81 507.48
8 17213.29 16789.34 423.95
9 17213.29 16873.28 340.01
10 17213.29 16957.65 255.64
11 17213.29 17042.44 170.85
12 17213.23 17127.59 85.64
总计 206559.42 200000 6559.42

=================================================================================

增加的末期调整函数如下:

#调整末期本金账务平衡关系
def cBalance(rowSet,loan):
    rownum=0
    columnum=0  
    setLen=len(rowSet)
    sumBen=0 #累计本金
    for row in rowSet:
        sumBen=sumBen+row[2]
        rownum=rownum+1
        if(rownum>setLen-2):
            break
    print("末期调整:"+str(sumBen))
    print(loan-sumBen)
    endBen=round(loan-sumBen,2)
    endInv=rowSet[setLen-1][3]
    endSum=round(endBen+endInv,2) #末期本息=末期本金+末期利息
    print(endSum)
    rowSet[setLen-1][1]=endSum
    rowSet[setLen-1][2]=endBen

=================================================================================

计算器下载链接:https://download.csdn.net/download/rishengcsdn/19122239?spm=1001.2014.3001.5503

只支持win10操作系统运行。

Python 房贷计算器小工具相关推荐

  1. python分数计算器_python算法——方程计算器小工具

    python算法--方程计算器小工具 工具介绍 方程计算器小工具使用python开发,可实现三元以内一次.二次等方程的计算,包含基本计算器的功能.可用于老师.学生.家长等快速验证方程的求解,检查学生作 ...

  2. 太强了,Python 开发桌面小工具,让代码替我们干重复的工作~

    作者 | Cherish 来源 | 杰哥的IT之旅 决定写这篇文章的初衷是来源于一位小伙伴的问题,关于"如何根据数据源用 Python 自动生成透视表",这个问题背后有个非常好的解 ...

  3. python对工作效率的提升_使用了这个几个Python内置小工具,可以让你的工作效率提升一倍...

    使用了这个几个Python内置小工具,可以让你的工作效率提升一倍 我们将会详情4个Python解释器自身提供的小工具. 这些小工具在笔者的日常工作中经常使用到, 减少了各种时间的白费, 然而,却很容易 ...

  4. 太强了!Python 开发桌面小工具,让代码替我们干重复的工作!

    作者:Cherish 来源:https://www.jianshu.com/p/91128d442198 决定写这篇文章的初衷是来源于一位小伙伴的问题,关于"如何根据数据源用 Python ...

  5. Python 开发桌面小工具,让代码替我们干重复的工作!

    作者:Cherish 来源:https://www.jianshu.com/p/91128d442198 本文为读者投稿 决定写这篇文章的初衷是来源于一位小伙伴的问题,关于"如何根据数据源用 ...

  6. python运维小工具_Python实现跨平台运维小神器

    (本文已不再同步更新,最新代码请移步github) 这阵子一直在学python,碰巧最近想把线上服务器环境做一些规范化/统一化,于是便萌生了用python写一个小工具的冲动.就功能方面来说,基本上是在 ...

  7. 【python小项目】用python写一个小工具——番茄钟

    用python写一个小工具--番茄钟 最近听到朋友说在用番茄钟,有点兴趣也想下载一个来用用,后面仔细一想这玩意做起来也不难,索性自己顺手写一个算了,在这里也分享给大家了 一.功能简述 番茄钟即番茄工作 ...

  8. 太强了~Python 开发桌面小工具,让代码替我们干重复的工作

    决定写这篇文章的初衷是来源于一位小伙伴的问题,关于"如何根据数据源用 Python 自动生成透视表",这个问题背后有个非常好的解决思路,让代码替我们做重复的工作,从而减轻工作量,减 ...

  9. python粘贴板小工具---复制内容格式化(clipboard)

    目标: 处理粘贴板中的内容,比如进行合并行.转换大小写.首字母大写等. 假设场景: 在阅读pdf文件的时候,遇见了需要复制的内容,一般复制下来都会有各种问题.比如换行错位. 那有没有办法把复制出来的内 ...

  10. 推荐一个Python的开源小工具大合集!

    今天推荐一个python的开源项目 python写的各种小工具,涉及的知识比较多,包括pyqt5.简单的爬虫.文本匹配.计算器.二维码制作.端口扫描器等等. 开源地址 https://github.c ...

最新文章

  1. MyBatis复习笔记5:MyBatis代码生成器
  2. 超简单的网页选项卡---jQuery
  3. vue checkbox 默认选中
  4. 淘宝自营“护肤品”精准引流加粉分享
  5. python手机版下载3.7.2-qpython手机版下载
  6. python 内置模块:collections
  7. 经典常用算法/常用算法思维---附伪代码以及实现
  8. Windows 7的使用技巧或功能经典20条
  9. 【案例】护士发错药怎么处理?
  10. 8635 气球(组合数)
  11. OpenStack本地存储选项的现在与未来
  12. 递增的整数序列链表的插入_leetcode673_go_最长递增子序列的个数
  13. BNU29140 Taikotaiko(概率)
  14. linux ip被占用顶掉,记一次 Linux服务器被***后的排查思路
  15. ZLMediaKit流媒体服务器
  16. 超声波传感器(CHx01) 学习笔记 Ⅲ - I2C读写操作
  17. 圆通快递拒收后,如何快速查询退回件单号的物流情况
  18. 同城聚合平台v59.2.0 本地同城 同城信息 同城商家
  19. Greedy Gift Givers
  20. oracle ora 3136,oracle中ORA-3136,ORA-609

热门文章

  1. Java调用webservice服务接口步骤详解
  2. Android木马病毒com.schemedroid的分析报告
  3. 第26次ccf认证第二题:寻宝!大冒险!
  4. Linux Block Driver - 1
  5. Alexa交叉编译(avs-device-sdk)
  6. 群晖docker安装Transmission下载器
  7. 傅里叶变换与Matlab
  8. 联想服务器r525维修,扩展性强易管理 联想R525 G2服务器拆解
  9. .NET环境下基于RBAC的访问控制
  10. 用QtCreator创建控制台应用程序