使用Python对QuantLib债券的每日定价

2024-10-01 11:21:38 发布

您现在位置:Python中文网/ 问答频道 /正文

我想在python中使用QuantLib,主要是在投资组合环境中对利率工具(衍生工具)进行定价。主要要求是将日收益率曲线传递给系统,以便在连续几天进行定价(我们暂时忽略系统性能问题)。我的问题是,我是否正确地构建了下面的示例来实现这一点?我的理解是,我需要至少一个曲线对象,每天与必要的链接等。我已经利用熊猫来尝试这一点。如能提供指导,将不胜感激。在

import QuantLib as ql
import math
import pandas as pd
import datetime as dt


# MARKET PARAMETRES
calendar = ql.SouthAfrica()
bussiness_convention = ql.Unadjusted
day_count = ql.Actual365Fixed()
interpolation = ql.Linear()
compounding = ql.Compounded
compoundingFrequency = ql.Quarterly


def perdelta(start, end, delta):
    date_list=[]
    curr = start
    while curr < end:
        date_list.append(curr)
        curr += delta
    return date_list


def to_datetime(d):
    return dt.datetime(d.year(),d.month(), d.dayOfMonth())

def format_rate(r):
    return '{0:.4f}'.format(r.rate()*100.00)


#QuantLib must have dates in its date objects
dicPeriod={'DAY':ql.Days,'WEEK':ql.Weeks,'MONTH':ql.Months,'YEAR':ql.Years}


issueDate = ql.Date(19,8,2014)
maturityDate = ql.Date(19,8,2016)

#Bond Schedule
schedule = ql.Schedule (issueDate, maturityDate,
                     ql.Period(ql.Quarterly),ql.TARGET(),ql.Following, ql.Following,
                    ql.DateGeneration.Forward,False)


fixing_days = 0 
face_amount = 100.0


def price_floater(myqlvalDate,jindex,jibarTermStructure,discount_curve):

    bond = ql.FloatingRateBond(settlementDays = 0,
                            faceAmount = 100,
                            schedule = schedule,
                            index = jindex,
                            paymentDayCounter = ql.Actual365Fixed(),
                            spreads=[0.02])   

    bondengine = ql.DiscountingBondEngine(ql.YieldTermStructureHandle(discount_curve))
    bond.setPricingEngine(bondengine)
    ql.Settings.instance().evaluationDate = myqlvalDate
    return [bond.NPV() ,bond.cleanPrice()]


start_date=dt.datetime(2014,8,19)
end_date=dt.datetime(2015,8,19)
all_dates=perdelta(start_date,end_date,dt.timedelta(days=1))
dtes=[];fixings=[]
for d in all_dates:
    if calendar.isBusinessDay(ql.QuantLib.Date(d.day,d.month,d.year)):
        dtes.append(ql.QuantLib.Date(d.day,d.month,d.year))
        fixings.append(0.1)


df_ad=pd.DataFrame(all_dates,columns=['valDate'])
df_ad['qlvalDate']=df_ad.valDate.map(lambda x:ql.DateParser.parseISO(x.strftime('%Y-%m-%d')))
df_ad['jibarTermStructure'] = df_ad.qlvalDate.map(lambda x:ql.RelinkableYieldTermStructureHandle())
df_ad['discountStructure'] = df_ad.qlvalDate.map(lambda x:ql.RelinkableYieldTermStructureHandle())
df_ad['jindex'] = df_ad.jibarTermStructure.map(lambda x: ql.Jibar(ql.Period(3,ql.Months),x))
df_ad.jindex.map(lambda x:x.addFixings(dtes, fixings))
df_ad['flatCurve'] = df_ad.apply(lambda r: ql.FlatForward(r['qlvalDate'],0.1,ql.Actual365Fixed(),compounding,compoundingFrequency),axis=1)
df_ad.apply(lambda x:x['jibarTermStructure'].linkTo(x['flatCurve']),axis=1)
df_ad.apply(lambda x:x['discountStructure'].linkTo(x['flatCurve']),axis=1)
df_ad['discount_curve']= df_ad.apply(lambda x:ql.ZeroSpreadedTermStructure(x['discountStructure'],ql.QuoteHandle(ql.SimpleQuote(math.log(1+0.02)))),axis=1)
df_ad['all_in_price']=df_ad.apply(lambda r:price_floater(r['qlvalDate'],r['jindex'],r['jibarTermStructure'],r['discount_curve'])[0],axis=1)
df_ad['clean_price']=df_ad.apply(lambda r:price_floater(r['qlvalDate'],r['jindex'],r['jibarTermStructure'],r['discount_curve'])[1],axis=1)
df_plt=df_ad[['valDate','all_in_price','clean_price']]
df_plt=df_plt.set_index('valDate')


from matplotlib import ticker

def func(x, pos): 
    s = str(x)
    ind = s.index('.')
    return s[:ind] + '.' + s[ind+1:]  

ax=df_plt.plot()
ax.yaxis.set_major_formatter(ticker.FuncFormatter(func))

多亏了Luigi Ballabio,我修改了上面的示例,将设计原则纳入QuantLib中,以避免不必要的调用。 现在静态数据是真正静态的,只有市场数据会变化(我希望如此)。 我现在更好地理解了活动对象如何监听链接变量中的更改。在

静态数据如下:

  • 键合机
  • 债券
  • 结构句柄
  • 历史吉巴尔指数

市场数据将是唯一变化的组成部分

  • 日掉期曲线
  • 掉期曲线上的市场利差

修改后的示例如下:

^{pr2}$

Tags: lambdaimportdfdatetimedatedtpricead
1条回答
网友
1楼 · 发布于 2024-10-01 11:21:38

你的解决方案是可行的,但是每天建立一个联系有点违背了图书馆的原则。您可以只创建一次bond和JIBAR索引,只需更改评估日期和相应的曲线;bond将检测更改并重新计算。在

在一般情况下,这将类似于:

# here are the parts that stay the same...

jibarTermStructure = ql.RelinkableYieldTermStructureHandle()
jindex = ql.Jibar(ql.Period(3,ql.Months), jibarTermStructure)
jindex.addFixings(dtes, fixings)
discountStructure = ql.RelinkableYieldTermStructureHandle()

bond = ql.FloatingRateBond(settlementDays = 0,
                          faceAmount = 100,
                          schedule = schedule,
                          index = jindex,
                          paymentDayCounter = ql.Actual365Fixed(),
                          spreads=[0.02])   

bondengine = ql.DiscountingBondEngine(discountStructure)
bond.setPricingEngine(bondengine)

# ...here is the pricing function...

def price_floater(myqlvalDate,jibar_curve,discount_curve):
    ql.Settings.instance().evaluationDate = myqlvalDate
    jibarTermStructure.linkTo(jibar_curve)
    discountStructure.linkTo(discount_curve)
    return [bond.NPV() ,bond.cleanPrice()]

# ...and here are the remaining varying parts:

df_ad=pd.DataFrame(all_dates,columns=['valDate'])
df_ad['qlvalDate']=df_ad.valDate.map(lambda x:ql.DateParser.parseISO(x.strftime('%Y-%m-%d')))
df_ad['flatCurve'] = df_ad.apply(lambda r: ql.FlatForward(r['qlvalDate'],0.1,ql.Actual365Fixed(),compounding,compoundingFrequency),axis=1)
df_ad['discount_curve']= df_ad.apply(lambda x:ql.ZeroSpreadedTermStructure(jibarTermStructure,ql.QuoteHandle(ql.SimpleQuote(math.log(1+0.02)))),axis=1)
df_ad['all_in_price']=df_ad.apply(lambda r:price_floater(r['qlvalDate'],r['flatCurve'],r['discount_curve'])[0],axis=1)
df_ad['clean_price']=df_ad.apply(lambda r:price_floater(r['qlvalDate'],r['flatCurve'],r['discount_curve'])[0],axis=1)
df_plt=df_ad[['valDate','all_in_price','clean_price']]
df_plt=df_plt.set_index('valDate')

现在,即使在最一般的情况下,上面的内容也可以优化:您在每个日期调用price_floater两次,所以您要做两倍的工作。我不熟悉pandas,但我想你可以打一个电话,用一个单独的赋值设置df_ad['all_in_price']和{}。在

此外,根据您的用例,可能还有进一步简化代码的方法。贴现曲线可能会实例化一次,而价差在定价过程中会发生变化:

^{pr2}$

在不同的部分,你会有一系列的信用利差和一系列的贴现曲线。在

如果曲线都是平的,那么可以利用另一个功能来实现同样的操作:如果用天数和日历(而不是日期)初始化曲线,则其参考日期将随评估日期移动(如果天数为0,则为评估日期;如果为1,则为下一个工作日,依此类推)。在

# only once:
risk_free = ql.SimpleQuote()
jibar_curve = ql.FlatForward(0,calendar,ql.QuoteHandle(risk_free),ql.Actual365Fixed(),compounding,compoundingFrequency)
jibarTermStructure.linkTo(jibar_curve)

# pricing:
def price_floater(myqlvalDate,risk_free_rate,credit_spread):
    ql.Settings.instance().evaluationDate = myqlvalDate
    risk_free.linkTo(risk_free_rate)
    spread.setValue(credit_spread)
    return [bond.NPV() ,bond.cleanPrice()]

在变化部分,您将用一个简单的速率数组替换jibar曲线数组。在

上面的结果应该与代码相同,但是实例化的对象要少得多,因此可能会节省内存并提高性能。在

最后一个警告:如果pandas的map并行计算结果,我的代码和你的代码都不起作用;你将尝试同时将全局评估日期设置为多个值,这不会很好。在

相关问题 更多 >