#输入参数
#调用需要使用的库
import pandas as pd
from datetime import datetime
from dateutil.relativedelta import relativedelta#这个库功能要比date库更强大一点
from dateutil import rrule
from sympy import *
# #二分法使用的精度
# eps=1e-6
#债券要素输入,
bondCode="293813.IB"#输入后缀,通过后缀检索交易场所
bondType="付息债" #枚举值“付息债”“贴现债”“到期还本付息债”“零息债”
paperIr=0.0495
ir_pay_freq=2
issue_date=datetime(2019,2,1) #在“,”之后输入年份,月份,日期
encash_date=datetime(2021,2,1)
dayCounter="A/A_B_coup" #枚举值A/365,A/365F,A/A_B_coup,A/A_B_day,A/360
netPrice=102.01
buyDate=datetime(2019,2,18) #买入日
M=100
C= 100000000 #计息本金
#生成理论付息日list
def couponDate():
couponDate=[]
x=0
while issue_date<encash_date-relativedelta(months=x*(12//ir_pay_freq)):
couponDate.append(encash_date-relativedelta(months=x*(12//ir_pay_freq)))
x+=1
couponDate.reverse()#此时的couponDate为日期从小到大自到期日向前推算生成的理论付息日期列表,不包括issue_date
return couponDate
couponDate=couponDate()
def payment_date(c_date):
temp_year_number=encash_date.year-c_date.year
m=len(couponDate)-1
if c_date<=couponDate[0]:
nextpayment_date=couponDate[0]
lastpayment_date=issue_date
elif c_date>=couponDate[m-1]:
nextpayment_date=couponDate[m]
lastpayment_date=couponDate[m-1]
elif c_date in couponDate[1:m-1]:#待确认
nextpayment_date=couponDate[couponDate.index(c_date)+1]
lastpayment_date=c_date
else:
if c_date>couponDate[m-(ir_pay_freq*(temp_year_number-1)+(encash_date.month+12-c_date.month)//(12//ir_pay_freq))]:
nextpayment_date=couponDate[m-(ir_pay_freq*(temp_year_number-1)+(encash_date.month+12-c_date.month)//(12//ir_pay_freq))+1]
lastpayment_date=couponDate[m-(ir_pay_freq*(temp_year_number-1)+(encash_date.month+12-c_date.month)//(12//ir_pay_freq))]
else:
nextpayment_date=couponDate[m-(ir_pay_freq*(temp_year_number-1)+(encash_date.month+12-c_date.month)//(12//ir_pay_freq))]
lastpayment_date=couponDate[m-(ir_pay_freq*(temp_year_number-1)+(encash_date.month+12-c_date.month)//(12//ir_pay_freq))-1]
return lastpayment_date,nextpayment_date
# lastpayment_date=payment_date(c_date)[0]
# nextpayment_date=payment_date(c_date)[1]
# print("生成的上一付息日{}下一付息日是{}".format(lastpayment_date,nextpayment_date))
def termCal(c_date):
years_total=rrule.rrule(rrule.YEARLY,dtstart=issue_date,until=encash_date).count()-1
years_left=rrule.rrule(rrule.YEARLY,dtstart=c_date,until=encash_date).count()-1#调用了dateutil库rrule方法
#起息日至结算日的整年数years_accrued
years_accrued=rrule.rrule(rrule.YEARLY,dtstart=issue_date,until=c_date).count()-1
#当前付息周期周期的天数days_between、theory_days:因为此处尚未考虑付息日准则
days_between=(payment_date(c_date)[1]-payment_date(c_date)[0]).days
theroy_days=days_between
#已计息天数accrued_days,注意:交易所买入当日利息归卖出方所有,银行间买入当日利息归买入方所有
check_date=list(rrule.rrule(rrule.DAILY,dtstart=payment_date(c_date)[0],until=c_date))
if dayCounter=="A/365F" :#闰年2月29日不计息
for datex in check_date:
if (datex.month==2 and datex.day==29):
accrued_days=(c_date-payment_date(c_date)[0]).days
break
else:
accrued_days=(c_date-payment_date(c_date)[0]).days+1
else:
accrued_days=(c_date-payment_date(c_date)[0]).days+1
#当前付息周期剩余天数days_left
days_left=(payment_date(c_date)[1]-c_date).days
# 剩余年限encash_years
if bondType!="付息债":
if dayCounter==("A/365" or "A/365F"):
Y=365
elif dayCounter=="A/360":
Y=360
else: #dayCounter=="A/A":待确认
Y=(c_date.replace(year=c_date.year+1)-c_date).days
encash_years=years_left+(encash_date.replace(year=encash_date.year-years_left)-c_date).days/Y
else:
encash_years=1
# 剩余完整付息周期的个数periods_left \剩余付息次数paytimes_left
paytimes_left=0
for m in couponDate:
if c_date<m:
paytimes_left+=1
else:
pass
if c_date in couponDate:#当计算日在某个理论付息日当天
periods_left=paytimes_left
else:
periods_left=paytimes_left-1
#本付息周期所在计息年度的天数TY(从起息日起计算的结算日所属的整年度,即债券本身的完整计息年度)
if dayCounter=="A/A_B_day":#
temp_year1=c_date.year-issue_date.year
if c_date<issue_date.replace(year=issue_date.year+temp_year1):
TY=(issue_date.replace(year=issue_date.year+temp_year1)-issue_date.replace(year=issue_date.year+temp_year1-1)).days
else:
TY=(issue_date.replace(year=issue_date.year+temp_year1+1)-issue_date.replace(year=issue_date.year+temp_year1)).days
elif dayCounter=="A/A_B_coup":
TY=" 无用的参数"
elif dayCounter in ["A/365" , "A/365F"]:
TY=365
else:#A/360
TY=360
return years_total,years_left,years_accrued,days_between,theroy_days,accrued_days,days_left,encash_years,periods_left,paytimes_left,TY
# years_total=termCal(c_date)[0]
# years_left=termCal(c_date)[1]
# years_accrued=termCal(c_date)[2]
# days_between=termCal(c_date)[3]
# theroy_days=termCal(c_date)[4]
# accrued_days=termCal(c_date)[5]
# days_left=termCal(c_date)[6]
# encash_years=termCal(c_date)[7]
# periods_left=termCal(c_date)[8]
# paytimes_left=termCal(c_date)[9]
# TY=termCal(c_date)[10]
# print("起息日至到期日的整年数为{}".format(years_total))
# print("结算日至到期日的整年数为{}".format(years_left))
# print("当前付息周期的天数为{} 上一理论付息日与下一理论付息日之间的天数为{}".format(days_between,theroy_days))
# print("已计息天数为{}".format(accrued_days))
# print("当前付息周期的剩余天数{}".format(days_left))
# print("剩余年限为{:.4f}".format(encash_years))
# print("剩余的完整付息周期的个数为{} 剩余付息次数为{}".format(periods_left,paytimes_left))
# print("本付息周期所在计息年度的天数为{}".format(TY))
dateList=list(rrule.rrule(rrule.DAILY,dtstart=buyDate,until=encash_date))#生成买入日至持有至到期期间的所有日期list
if dayCounter=="A/365F":#A/365F计息基础下闰年2月29日不计息,自然也不摊销,所以不在日期列表中存储该日。
for date in dateList:
if date.month==2 and date.day==29:
dateList.remove(date)
else:
pass
else:
pass
if bondType=="贴现债":
lastpayment_date=issue_date
nextpayment_date=encash_date
print("生成的上一付息日{}下一付息日是{}".format(lastpayment_date,nextpayment_date))
else:
pass
if bondType==("零息债")or bondType=="到期一次还本付息债":
ir_pay_freq=1
else:
pass
amortionBefore=netPrice*C/100
n=len(dateList)-1
def accrued_ir_Cal(c_date):#单日应计利息的生成过程
if bondType==("贴现债" or "零息债"):
accrued_ir=(100-M)/((encash_date-issue_date).days)*termCal(c_date)[5]
elif bondType=="到期一次还本付息债":
accrued_ir=termCal(c_date)[5]/termCal(c_date)[4]*M*paperIr+paperIr*M*termCal(c_date)[2]
else: #bond_type="付息债"
if dayCounter=="A/A_B_coup":#按平均值分配
accrued_ir=M*paperIr/ir_pay_freq*termCal(c_date)[5]/termCal(c_date)[3]
else:#其他计息基础A/365,A/365F,A/A_B_day,A/360
accrued_ir=M*paperIr*termCal(c_date)[5]/TY#未考虑中间还本的情况
return accrued_ir
# accrued_ir=accrued_ir_Cal(c_date)
def accrued_interest_List():#生成与dateList对应的债券百元应计利息的list
accrued_interest_List=[]
for i in range(n):
c_date=dateList[i]
accrued_interest_List.append(accrued_ir_Cal(c_date))
return accrued_interest_List
accrued_interest_List=accrued_interest_List()
def accrued_Holdinterest_List():#生成与dateList对应的持仓债券应计利息总额的list
accrued_Holdinterest_List=[]
for i in range(n):
accrued_Holdinterest_List.append(accrued_interest_List[i]*C/100)
return accrued_Holdinterest_List
accrued_Holdinterest_List=accrued_Holdinterest_List()
def accrued_Amountinterest_List():#生成与dateList对应的持仓债券应计利息发生额的list
accrued_Amountinterest_List=[]
c_date=buyDate-relativedelta(days=1)
buyMoment_accrued_interest=accrued_ir_Cal(c_date)*C/100
if bondCode[-2:]=="IB":#银行间债券买入日当日计息
accrued_Amountinterest_List.append(accrued_Holdinterest_List[0]-buyMoment_accrued_interest)
else:#交易所债券买入当日不计息
accrued_Amountinterest_List.append(0)
for i in range(1,len(dateList)-1):
accrued_Amountinterest_List.append(accrued_Holdinterest_List[i]-accrued_Holdinterest_List[i-1])
return accrued_Amountinterest_List
accrued_Amountinterest_List=accrued_Amountinterest_List()
def realInterest_rateCal():#计算以当前成本购买,持有至到期的实际利率
r=Symbol('r')
C_n=amortionBefore*((1+r)**n)
for i in range(n):
C_n-=accrued_Amountinterest_List[i]*((1+r)**(n-i-1))#计算实际利率的公式,读者可自行推导
sol=solve(C_n-C,r)
return sol
r=realInterest_rateCal()
def listCal():
realInterest_income_List=[]
amortionBefore_List=[]
amortionAfter_List=[]
interestAdjustments_List=[]
amortionBefore_List.append(amortionBefore)
if bondCode[-2:] in ["SH","SZ"]: #交易所债券买入日当日不计提摊销 ,利息收入归卖出方所有
realInterest_income_List.append(0)
interestAdjustments_List.append(0)
amortionAfter_List.append(amortionBefore)
#此处定义了买入日的歌科目余额
for i in range(1,n):
realInterest_income_List.append(amortionBefore_List[-1]*r)
interestAdjustments_List.append(realInterest_income_List[-1]-accrued_Amountinterest_List[i])
amortionAfter_List.append(amortionBefore_List[-1]+ interestAdjustments_List[-1])
amortionBefore_List.append(amortionAfter_List.append[-1])
else:#银行间债券当日即计提摊销,利息收入归买入方所有
for i in range(n):
realInterest_income_List.append(amortionBefore_List[-1]*r)
interestAdjustments_List.append(realInterest_income_List[-1]-accrued_Amountinterest_List[i])
amortionAfter_List.append(amortionBefore_List[-1]+ interestAdjustments_List[-1])
amortionBefore_List.append(amortionAfter_List.append[-1])
return realInterest_income_List,amortionBefore_List,amortionAfter_List,interestAdjustments_List
realInterest_income_List=listCal()[0]
amortionBefore_List=listCal()[1]
amortionAfter_List=listCal()[2]
interestAdjustments_List=listCal()[3]
print(r)
求问大佬我这个债券摊销算法出现了死循环,是哪个地方出了问题呢
- 写回答
- 好问题 0 提建议
- 追加酬金
- 关注问题
- 邀请回答
-
3条回答 默认 最新
- weixin_45772328 2021-02-25 16:43关注
后面还有个生成excel的代码,这里补充下:
dic1={'日期':dateList,'摊销前成本':amortionBefore_List,'实际利息收入':amortionAfter_List,'百元应计利息':accrued_interest_List,'应计利息':accrued_Holdinterest_List,'应计利息发生额':accrued_Amountinterest_List,'利息调整发生额':interestAdjustments_List,'摊销后成本':interestAdjustments_List}
df=pd.DataFrame(dict1)
df.to_excel('bondAmortion.xlsx',index=False)解决 无用评论 打赏 举报
悬赏问题
- ¥30 这是哪个作者做的宝宝起名网站
- ¥60 版本过低apk如何修改可以兼容新的安卓系统
- ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
- ¥50 有数据,怎么建立模型求影响全要素生产率的因素
- ¥50 有数据,怎么用matlab求全要素生产率
- ¥15 TI的insta-spin例程
- ¥15 完成下列问题完成下列问题
- ¥15 C#算法问题, 不知道怎么处理这个数据的转换
- ¥15 YoloV5 第三方库的版本对照问题
- ¥15 请完成下列相关问题!