weixin_45772328
weixin_45772328
采纳率0%
2021-02-25 14:45

求问大佬我这个债券摊销算法出现了死循环,是哪个地方出了问题呢

10
已结题
#输入参数
#调用需要使用的库
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)
  • 点赞
  • 收藏
  • 复制链接分享

4条回答

  • qq_22475211 深白色的风 1月前
    sol = solve(C_n - C, r)

    第200行的求解上出问题

    点赞 评论 复制链接分享
  • weixin_45772328 weixin_45772328 2月前

    个人觉得错误可能在

    函数realInterest_rateCal()这里

    点赞 评论 复制链接分享
  • weixin_45772328 weixin_45772328 2月前

    后面还有个生成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)

    点赞 评论 复制链接分享
  • soar3033 soar3033 2月前

    在while issue_date<encash_date-relativedelta(months=x*(12//ir_pay_freq)):下面加个print("1")看看运行后是不是一直在打印1,有可能卡在这了

    点赞 评论 复制链接分享

为你推荐