起因
最近一个搞投融资的好哥们儿问我分期付款利率的计算,说实在的我也不懂,答应他过几天帮他看看。
数学理论
忙了几天后,今天闲下来了,在网上搜了搜相关的知识。
主要是这七个公式:
- 【式1】最简单,是下面所有式子推导的起点。它的含义是初始资金P在利率i下进行n期利滚利迭代后的资金额F。
- 【式2】由式1反推
- 【式3】是指在每期固定还款额度情况下,执行n期后的资金总额。具体就是式1从1到n的和,按等比数列求和公式可得。
- 【式4】由式3反推。
- 【式5】则是将式3带入式2得到。
- 【式6】是式5反推。这是计算等额本息分期付款额度的关键公式。
- 【式7】是计算每期还款额度中多少属于偿还上期剩余本金的利息,这样每期还款额A减去利息后剩余的额度就用来偿还本金。
在等额本息偿还方式下,随着偿还期数递增,本金逐渐减少,每期的需偿还的利息比重也逐渐减少。到了还款后期每次基本上是在偿还本金,从这点儿看等额本息的还款方式确实不太适合提前偿还。
python 实现
最近几天由于项目发展需要又开始拾起来python了,这个小小的计算程序用python简直不要太简单了!
# 复利终值计算
def rF(P,n):
ret = P * pow(1 + r, n)
return ret
# 复利现值
def rP(F,n):
ret = F * pow(1 + r, -n)
return ret
# 等额多次支付终值计算
def mrF(A,n):
ret = A * (pow((1 + r), n) - 1) / r
return ret
# 等额多次支付现值计算
def mrP(A,n):
ret = A * (pow((1 + r), n) - 1) / pow((1 + r), n) / r
return ret
# 资金回笼计算
def mrA(P,n):
ret = P * pow((1 + r), n) * r / (pow((1 + r), n) - 1)
return ret
#
def mrAr(P,n,r):
ret = P * pow((1 + r), n) * r / (pow((1 + r), n) - 1)
return ret
# 月还款利息成分
def mrR(P,n,i):
A = P * pow((1 + r), n) * r / (pow((1 + r), n) - 1)
return (P * r - A) * pow(1 + r, i - 1) + A
先把几个公式写出来,利率i用全局变量r来代替。接下来用python列表操作得出每期的还款额,利息,本金,
# 计算等额本息----开始
# 本金
P = 1000000
# 分期多少月
n = 36
# 利率 1分=0.01
r = 0.01
# A:每月还钱数;R:其中包含的利息; p:其中包含的本金。A=R+p
A = [mrA(P,n)]*n
R = [mrR(P,n,i) for i in range(1,n+1)]
p = [mrp(P,n,i) for i in range(1,n+1)]
连个循环都不用写,真是不习惯!
分阶段的等额本息
这哥们儿接着提出了个要求:还是等额本息,但在借款期间的利息计算是变动的,比如借三年,第一年24%,第二年8%,第三年4%,这样三年平均起来的利息还接近12%,但第一年就将大部分利息收回来了,从而提高了资金收益率。我听了心里不由得道了声--我嚓~,你一个月收到3万,里面有多少利息其实并不是算法或其他什么规定的,这只是一个在利率迭代过程给出的等效值啊等效值啊等效值啊!如果你想,你把这三万全定位利息也不错啊!哈哈
后来这哥们儿又给出解释:像从事他们这一行的,遇见的客户也很少能规规矩矩的按时执行还款合约的,这样即使半截终止了合同,前面收回了利息,等等,但我估计还应该是后面再追回了本金才不亏,否则或者客户到后期感觉这钱白用着很好,干脆用满整个期间,咋办
好吧,虽然觉得还是有点儿问题,咱只要给他搞出个算法就好了。
由于是分段计息,又要求整个过程是等额还款,这样不太好法直接确定各个阶段的资金比例,也就没办法套用上面的公式,如果非要先推一个解析式的话,估计只能是个分段函数。
但考虑到这个终值F和还款额A的函数关系一定是收敛的,而且A的区间比较好确定,整个算法递推上几十步对计算机来说简直小菜一碟,所以插值逼近的算法正适合这个问题。
算法描述:
1、确定A的上下区间MaxA、MinA
2、取A当前值curA=区间中点
3、递推还款期间本金偿还数量
3.1、设置还款期数=1,本金curP=P
3.2、计算本期利息curR=curP*r
还款额扣除利息后偿还本金curP -= curR
还款期数+1
3.3、若未到最后还款期执行步骤3.2,否则向下继续
4、如果curP==0(实际上是curP绝对值小于ε(ε=0.01)),说明还款额度适中,算法结束,输出结果。
如果curP>0,则说明还款额度不够,继续向上插值。修改插值区间低点MinA=curA,设置 = (MinA+MaxA)/2,执行步骤3
如果curP<0,则说明还款额度超出,继续向下插值。修改插值区间高点MaxA=curA,设置 = (MinA+MaxA)/2,执行步骤3
python代码:
计算--阶段利息---等额本息----开始
RPerYear = [0.24,0.08,0.04]
RPerMonth = []
for RYear in RPerYear:
slicRPerYear = [RYear / 12.0] * 12
RPerMonth += slicRPerYear
#printFloat(RPerMonth,1)
# 本金
P = 1000000
# 迭代计算每月还款额度
MinA = RPerMonth[0] * P
MaxA = P
count = len(RPerMonth)
# 初值
curA = (MaxA + MinA) / 2.0
while 1:
curP = P
for i in range(count):
curR = curP * RPerMonth[i]
curP -= curA - curR
#print("curP=%.2f,curA=%.2f"%(curP,curA))
#print("MinA=%.2f,MaxA=%.2f"%(MinA,MaxA))
# 迭代精度0。01=1分
if abs(curP) < 0.01:
break;
else:
if curP > 0:
MinA = curA
curA = (MinA + MaxA) / 2.0
else:
MaxA = curA
curA = (MinA + MaxA) / 2.0
print(RPerYear)
print("等额本息还款额度=%.2f"%(curA))
运行结果:
[0.24, 0.08, 0.04]
等额本息还款额度=35473.49
接下来计算平均利息以及每期还款情况。从前面的公式分析可知,由利息计算F或P比较容易,但已知A、F或P反推利息则相当于解高次方程,最好也采用插值算法。具体算法就不多啰嗦了,直接上代码:
# 迭代计算等价利息
MaxR = max(RPerMonth)
MinR = min(RPerMonth)
curR = (MaxR + MinR) / 2.0
while 1:
newA = mrAr(P,len(RPerMonth),curR)
if abs(newA - curA) < 0.001:
break
else:
if newA > curA: #高了
MaxR = curR
curR = (MinR + MaxR) / 2.0
else:
MinR = curR
curR = (MinR + MaxR) / 2.0
print("等价利息=%.4f" % (curR))
# 推算每个月还款额度中的利息和本金
count = len(RPerMonth)
A = [curA]*count
R = []
p = []
curP = P
for i in range(count):
step_R = curP * RPerMonth[i]
step_p = A[i] - step_R
curP -= step_p
R.append(step_R)
p.append(step_p)
# all done!!
#printVFloat((A,RPerMonth,R,p))
printVFloatWithR((A,R,p),RPerMonth)
运行结果:
等价利息=0.0139
| 序 号 | 1 | 2 | 3 | 4 |
| 1 |35473.49 | 0.0200 |20000.00 |15473.49 |
| 2 |35473.49 | 0.0200 |19690.53 |15782.96 |
| 3 |35473.49 | 0.0200 |19374.87 |16098.61 |
| 4 |35473.49 | 0.0200 |19052.90 |16420.59 |
......
| 36 |35473.49 | 0.0033 | 117.85 |35355.63 |
| 总 计 |1277045.49| 0.3600 |277045.49|1000000.01|
总结
从这个实际应用来看,利用python提供的元组、列表以及针对性的批量操作算法进行数据处理简直太好用了,无愧于它在大数据领域的名声!