【手把手教你】固定收益和衍生品分析利器QuantLib入门

2023-10-30 21:50

本文主要是介绍【手把手教你】固定收益和衍生品分析利器QuantLib入门,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

引言

QuantLib是一个专门用于利率、债券与衍生品等金融工具定价分析的库,可以说是固定收益和金融衍生品分析的一个利器。QuantLib本身是使用C++写的,通过SWING技术封装后可以在Python调用。直接使用pip安装可能会报错,建议下载安装包的whl文件,然后再用pip进行安装。

https://www.lfd.uci.edu/~gohlke/pythonlibs/#quantlib (建议收藏,涵盖了Python大部分第三方包),找到与自己电脑和Python相对应的版本下载,如我的电脑是64位,Python3.7,选择第一个下载,将下载的文件放在当前工作目录,然后进入cmd模式(cmd模式下的地址即为当前工作目录),输入:

“pip install QuantLib_Python‑1.11‑cp37‑cp37m‑win_amd64.whl”即可,如果import QuantLib没有报错说明安装成功。

在公众号后台回复“quantlib”可以获取quantilib的使用手册(英文版)

如何查看系统环境、包的版本号和当前工作路径呢?

#先pip安装watermark
#查看系统环境和module的版本号
%load_ext watermark
%watermark
%watermark -p pandas,numpy,QuantLib,matplotlib

输出结果:

#查看当前工作目录
import os
os.getcwd()
#输出结果:
'C:\\Users\\zjy'引入QuantLib包
#引入QuantLib
import QuantLib as ql
QuantLib基础模块:Dates

日期模块Dates包含了Date,Period,Calendar,DayCounter,Schedule,DateGeneration等,是QuantLib的基础模块,包含了时间、日期、日历等的定义、生成和逻辑运算等,是数学建模和量化分析的重要基础。

Date:日期

Date是日期格式,日期范围是1901-01-01至2199-12-31。有三种写法:(1)Date(n),与Excel类似,其中n的范围是367-109574之间的数字(含),其他超出该范围的数字都会报错;(2)Date(day,month,year),即日,月,年的输入格式,其中日和年必须是数字,而月可以是其他格式,如ql.June(相当于输入6)。(3)Date(日期,格式),如:Date('20-09-2020', '%d-%m-%Y')

应用实例:定义日期“2020年11月11日”

d1=ql.Date(11,11,2020)
d2=ql.Date('2020-11-11','%Y-%m-%d')
d3=ql.Date('20201111','%Y%m%d')
d4=ql.Date(44146)
#四种写法等价
print(d1==d2==d3==d4)
#输出结果:True


日期定义与运算

today=ql.Date(11,11,2020)
print('初始日期:', today)
print('ISO格式:', today.ISO())
#返回一周七天对应的数字:注意周日(Sunday)是1,周六(Saturday)是7
print('一周中第几天:', today.weekday())
print('该月的第几天:', today.dayOfMonth())
print('本年的第几天:', today.dayOfYear())
print('月份:', today.month())
print('年份:', today.year())
#相当于第一种写法的逆运算
print('日期数字:', today.serialNumber())

输出结果:
初始日期: November 11th, 2020
ISO格式: 2020-11-11
一周中第几天: 4
该月的第几天: 11
本年的第几天: 316
月份: 11
年份: 2020
日期数字: 44146

#逻辑判断
print(today == ql.Date(12, 11, 2020))
print(today > ql.Date(11, 10, 2020))
print(today < ql.Date(1, 12, 2020))
print(today != ql.Date(11, 9, 2020))

输出结果:False、True、True、True

获取Date内置日期和运算函数

print('当前日期:', ql.Date.todaysDate())
print('系统支持最小日期 :', ql.Date.minDate())
print('系统支持最大日期 :', ql.Date.maxDate())
#判断是否闰年
print('是闰年吗? :', ql.Date.isLeap(2020))
print('该月的最后一天:', ql.Date.endOfMonth(ql.Date(11, ql.November, 2020)))
print('是该月的最后一天吗? :', ql.Date.isEndOfMonth(ql.Date(30, 11, 2020)))
print('该日期的下个星期一 :', ql.Date.nextWeekday(ql.Date(11, 11, 2020), ql.Monday))
print('该月的第2个星期五 :', ql.Date.nthWeekday(2, ql.Friday, 11, 2020))

输出结果:
当前日期: November 11th, 2020
系统支持最小日期 : January 1st, 1901
系统支持最大日期 : December 31st, 2199
是闰年吗?: True
该月的最后一天: November 30th, 2020
是该月的最后一天吗?: True
该日期的下个星期一 : November 16th, 2020
该月的第2个星期五 : November 13th, 2020

Period:周期

Period可以生成日期频数,如多少日(周、月、年)等。主要有三种写法:(1)ql.Period(n, units),其中units可以是日:ql.Days,周:ql.Weeks,月:ql.Months,年:ql.Years。(2)ql.Period(periodString),其中periodString可以是日:'1D',周:'1W',月:'1M'和年:'1Y'。(3)ql.Period(frequency),如每年,ql.Annual。

#一年的等价写法
ql.Period('1Y')==ql.Period(1,ql.Years)==ql.Period(ql.Annual)
#输出结果:True

Period主要是用于Date的运算。

today=ql.Date(11,11,2020)
print(f'{today}三天后是{today+3}')
print(f'{today}前一天是{today-1}')
print(f'{today}下一周是{today+ql.Period(1,ql.Weeks)}')
print(f'{today}下一个月是{today+ql.Period(1,ql.Months)}')
print(f'{today}下一年是{today+ql.Period(1,ql.Years)}')

输出结果:
November 11th, 2020三天后是November 14th, 2020
November 11th, 2020前一天是November 10th, 2020
November 11th, 2020下一周是November 18th, 2020
November 11th, 2020下一个月是December 11th, 2020
November 11th, 2020下一年是November 11th, 2021

Calendar:日历

Date对象没有考虑假期因素,而实际应用中,证券交易需要考虑指定交易所或者国家的假期,Calendar对主要的交易所给出了交易日历,包括:Argentina : [‘Merval’];Brazil : [‘Exchange’,‘Settlement’],Canada : [‘Settlement’, ‘TSX’]、China : [‘IB’, ‘SSE’];CzechRepublic : [‘PSE’],France : [‘Exchange’, ‘Settlement’];Germany : [‘Eurex’, ‘FrankfurtStockExchange’, ‘Settlement’, ‘Xetra’],HongKong : [‘HKEx’],Iceland : [‘ICEX’],India : [‘NSE’],Indonesia : [‘BEJ’, ‘JSX’],Israel : [‘Settlement’, ‘TASE’],Italy : [‘Exchange’, ‘Settlement’],Mexico : [‘BMV’],Russia : [‘MOEX’, ‘Settlement’],SaudiArabia : [‘Tadawul’],Singapore :[‘SGX’],Slovakia : [‘BSSE’],SouthKorea : [‘KRX’, ‘Settlement’],Taiwan : [‘TSEC’],Ukraine : [‘USE’],UnitedKingdom : [‘Exchange’, ‘Metals’, ‘Settlement’];UnitedStates :[‘FederalReserve’,‘GovernmentBond’,‘LiborImpact’, ‘NERC’, ‘NYSE’, ‘Settlement’]

calendar1 = ql.UnitedKingdom(ql.UnitedKingdom.Exchange)
calendar2 = ql.UnitedStates(ql.UnitedStates.NYSE)
calendar3 = ql.China(ql.China.SSE)
day1=ql.Date(1,1,2020)
day2=ql.Date(31,12,2020)
uk_bday=calendar1.businessDaysBetween(day1,day2)
us_bday=calendar2.businessDaysBetween(day1,day2)
ch_bday=calendar3.businessDaysBetween(day1,day2)
print(day1,'至',day2,'之间有',uk_bday,'个英国交易日')
print(day1,'至',day2,'之间有',us_bday,'个美国交易日')
print(day1,'至',day2,'之间有',ch_bday,'个中国交易日')

输出结果:
January 1st, 2020 至 December 31st, 2020 之间有 253 个英国交易日
January 1st, 2020 至 December 31st, 2020 之间有 252 个美国交易日
January 1st, 2020 至 December 31st, 2020 之间有 260 个中国交易日

判断日期类型

cal1 = ql.China()
cal2 = ql.UnitedStates()
mydate = ql.Date(1, 10, 2020)
#判断交易日
print(mydate,'在中国是交易日吗? :', cal1.isBusinessDay(mydate))
print(mydate,'在美国是交易日吗? :', cal2.isBusinessDay(mydate))

输出结果:
October 1st, 2020 在中国是交易日吗?: True
October 1st, 2020 在美国是交易日吗?: True

cal = ql.China()
day1 = ql.Date(11, 11, 2020)
day2 = ql.Date(14, 11, 2020)
print('Is Business Day : ', cal.isBusinessDay(day1))
print('Is Business Day : ', cal.isBusinessDay(day2))
#添加或者移除节假日设定
cal.addHoliday(day1)
cal.removeHoliday(day2)
print('Is Business Day : ', cal.isBusinessDay(day1))
print('Is Business Day : ', cal.isBusinessDay(day2))

输出结果:
Is Business Day : True
Is Business Day : False
Is Business Day : False
Is Business Day : True

不同国家日历日和交易日的区别

date = ql.Date(11, 11, 2020)
us_calendar = ql.UnitedStates()
ch_calendar = ql.China()
raw_date = date + ql.Period(30, ql.Days)
us_date = us_calendar.advance(date, ql.Period(30, ql.Days))
ch_date = ch_calendar.advance(date, ql.Period(30, ql.Days))
print(date,"后推30个日历日:     ", raw_date)
print(date,"后推美国30个交易日: ", us_date)
print(date,"后推中国30个交易日: ", ch_date)

输出结果:
November 11th, 2020 后推30个日历日:December 11th, 2020
November 11th, 2020 后推美国30个交易日:December 24th, 2020
November 11th, 2020 后推中国30个交易日:December 22nd, 2020

DayCounter:天数计算

DayCounter可以统计某两个日期之间的天数,是固定收益类产品估值和分析的重要基础,常用的计数函数包括:

  • Actual360 : Actual / 360,一年按360天

  • Actual365Fixed : Actual / 365(Fixed),一年按365天

  • Standard:标准;Canadian:加拿大;NoLeap:即所有年份都是365天

  • ActualActual : 按每年实际天数

  • ISMA \ Bond\ ISDA\ Historical\ Actual365\ AFB\ Euro

  • Business252 : Business / 252,证券交易日

  • Thirty360 : 30 / 360,按月30天,年360天

  • SimpleDayCounter:简单的日计数

常用的函数有两个:

dayCount(d1,d2):计算 d1,d2 之间的天数

yearFraction(d1, d2):将 d1,d2 之间的天数年化

d1 = ql.Date(1,10,2020)
d2 = ql.Date(11,11,2020)
dc=ql.Business252()
dd=dc.dayCount(d1,d2)
yf=dc.yearFraction(startDate,endDate)
print(f'日历间隔天数:{d2-d1}')
print(f'Business252计算规则天数:{dd}')
print(f'Business252计算规则天数年化:{yf:.4f}')
日历间隔天数:41
Business252计算规则天数:27
Business252计算规则天数年化:0.1071
dayCounters = {'SimpleDayCounter': ql.SimpleDayCounter(),'Thirty360': ql.Thirty360(),'Actual360': ql.Actual360(),'Actual365Fixed': ql.Actual365Fixed(),'Actual365Fixed(Canadian)': ql.Actual365Fixed(ql.Actual365Fixed.Canadian),'Actual365NoLeap': ql.Actual365NoLeap(),'ActualActual': ql.ActualActual(),'Business252': ql.Business252()}
for name,dc in dayCounters.items():dd=dc.dayCount(startDate,endDate)print(name,'计算规则天数',dd)       

输出结果:
SimpleDayCounter 计算规则天数 40
Thirty360 计算规则天数 40
Actual360 计算规则天数 41
Actual365Fixed 计算规则天数 41
Actual365Fixed(Canadian) 计算规则天数 41
Actual365NoLeap 计算规则天数 41
ActualActual 计算规则天数 41
Business252 计算规则天数 27

Schedule:时间表

Schedule(effectiveDate, terminationDate, tenor, calendar, convention, terminationDateConvention, rule, endOfMonth, firstDate=Date(), nextToLastDate=Date()),各变量分别代表:

  • effectiveDate, terminationDate : 日历列表的起始和终止日期, 比如债券的定价和到期日期

  • tenor : Period对象, 两个日期的间隔, 如债券发行频率(1年或6个月)或利率掉期利率(3个月)。

  • calendar : 一个日历表,用于生成要遵循的日期的特定日历。

  • convention : 整数型,如何调整非工作天(最后一天除外),值范围是quantlib-python的一些保留变量。

  • terminationDateConvention : 整数型,如果最后的日期是非工作日,如何调整它,值范围是quantlib-python的一些保留变量。

  • Rule : 日期生成的一个成员,用于为日期生成规则。

  • endOfMonth : 如果开始日期在月底,是否需要在月底安排其他日期(最后日期除外)。

  • firstDate : nextToLastDate(可选):Date,为生成的方法规则提供的开始和结束日期(不常用)。

Schedule对象的行为和list类似,是一种存储Date对象的序列容器。使用len(sch):返回 Schedule 对象sch内日期的个数,[i]:返回第 i 个日期,Schedule对象是可迭代的。

effectiveDate = ql.Date(1,1,2018)
terminationDate = ql.Date(15,6,2020)
frequency = ql.Period('6M')
#默认使用当前系统日期
calendar = ql.TARGET()
convention = ql.ModifiedFollowing
terminationDateConvention = ql.ModifiedFollowing
#Forward是以初始日期向后推算,Backward是以结束日期向前推算
rule = ql.DateGeneration.Forward
endOfMonth = False
mysch = ql.Schedule(effectiveDate, terminationDate, frequency, calendar, convention, terminationDateConvention, rule, endOfMonth)
for i,d in enumerate(mysch):print(i+1,d)

输出结果:
1 January 2nd, 2018
2 July 2nd, 2018
3 January 2nd, 2019
4 July 1st, 2019
5 January 2nd, 2020
6 June 15th, 2020

schedule常用的函数:

  • until(d):从日期列表中截取前半部分,并保证最后一个日期是d。

  • isRegular(i):判断第 i 个区间是否完整。如果一个Schedule对象有 n 个日期,该对象就有 n-1个区间,那么第 i 个区间的长度和事先规定的时间间隔一致,则判断该区间是完整的(Regular)。

mys=mysch.until(ql.Date(15, ql.June, 2019))
for i in range(len(mys)-1):print(mys[i],'至',mys[i+1],'该区间完整吗?',mys.isRegular(i+1))

输出结果:
January 2nd, 2018 至 July 2nd, 2018 该区间完整吗?True
July 2nd, 2018 至 January 2nd, 2019 该区间完整吗?True
January 2nd, 2019 至 June 15th, 2019 该区间完整吗?False

DateGeneration

许多产品的估值依赖于对未来现金流的分析,因此准确地列出未来现金流的日期是至关重要的。在给定开始和结束日期后,可以采用“反向方法”或“正向方法”生成日期列表。

effectiveDate = ql.Date(5,1,2020)
terminationDate = ql.Date(20,4,2020)
frequency = ql.Period('1M')
#默认使用当前系统日期
calendar = ql.TARGET()
convention = ql.ModifiedFollowing
terminationDateConvention = ql.ModifiedFollowing
#Forward是以初始日期向后推算,Backward是以结束日期向前推算endOfMonth = Falserules = {'Backward': ql.DateGeneration.Backward,'Forward': ql.DateGeneration.Forward,'Zero': ql.DateGeneration.Zero,'ThirdWednesDay': ql.DateGeneration.ThirdWednesday,'Twentieth': ql.DateGeneration.Twentieth,'TwentiethIMM': ql.DateGeneration.TwentiethIMM,'CDS': ql.DateGeneration.CDS}for name, rule in rules.items():schedule = ql.Schedule(effectiveDate, terminationDate, frequency, calendar, convention, terminationDateConvention, rule, endOfMonth)print(name, [dt for dt in schedule])

输出结果:

InterestRate:利率类

InterestRate类可用于存储具有复利类型、日计数和复利频率的利率。下面我们将展示如何使用实际日计数惯例(Actual/Actual)创建8.0%复利年利率。

annual_rate = 0.08
day_count = ql.ActualActual()
compound_type = ql.Compounded
frequency = ql.Annual
interest_rate = ql.InterestRate(annual_rate,day_count,compound_type,frequency)

假设你以上述描述的利率投资一美元,利息对象中的复合因子法给出你的投资在任何时期后的价值。下面演示由复合因子返回的2年的值与预期的复利公式是一致的。

t = 2.0
print (interest_rate.compoundFactor(t))
print((1.0+annual_rate)**2) 

输出结果:1.1664  1.1664

discountFactor方法返回复合因子方法的倒数。在计算未来现金流的现值时,折现系数是非常实用的。

print (f'{interest_rate.discountFactor(t):.4f}')
print (f'{1.0/interest_rate.compoundFactor(t):.4f}')

输出结果:0.8573  0.8573

一个给定的利率可以转换为其他的复利类型和复利频率使用相等的中心方法。

compound_type= ql.Compounded
t=2.0
new_frequency = ql.Monthly
new_interest_rate =interest_rate.equivalentRate(compound_type, new_frequency,t)
new_annual_rate = new_interest_rate.rate()
print (f'{new_annual_rate:.4f}')

输出结果:0.0772

两个利率对象(interest_rate和new_interest_rate)的折现因子相同,如下所示。

print (f'{interest_rate.discountFactor(t):.4f}')
print (f'{new_interest_rate.discountFactor(t):.4f}')

输出结果:0.8573    0.8573

结语

QuantLib主要用于固定收益和衍生品的量化分析,内容包罗万象,涵盖的领域也比较广。本文主要介绍了QuantLib的基础模块——Dates日期类和InterestRate利率类。这两个类是后续利率、债券、金融衍生品估值和定价分析的重要基础。后续推文将以专题的形式介绍Quantlib在固定收益分析中的应用案例,敬请期待。

看完记得点赞和在看哦~

参考资料:

1. Luigi Ballabio and Goutham Balaraman,2017,《QuantLib Python Cookbook》.

2. QuantLib官方网上英文教程:https://quantlib-python-docs.readthedocs.io/en/latest/dates.html

关于Python金融量化

专注于分享Python在金融量化领域的应用。加入知识星球,可以免费获取量化投资视频资料、量化金融相关PDF资料、公众号文章Python完整源码、量化投资前沿分析框架,与博主直接交流、结识圈内朋友等。

这篇关于【手把手教你】固定收益和衍生品分析利器QuantLib入门的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/310802

相关文章

Redis主从/哨兵机制原理分析

《Redis主从/哨兵机制原理分析》本文介绍了Redis的主从复制和哨兵机制,主从复制实现了数据的热备份和负载均衡,而哨兵机制可以监控Redis集群,实现自动故障转移,哨兵机制通过监控、下线、选举和故... 目录一、主从复制1.1 什么是主从复制1.2 主从复制的作用1.3 主从复制原理1.3.1 全量复制

Redis主从复制的原理分析

《Redis主从复制的原理分析》Redis主从复制通过将数据镜像到多个从节点,实现高可用性和扩展性,主从复制包括初次全量同步和增量同步两个阶段,为优化复制性能,可以采用AOF持久化、调整复制超时时间、... 目录Redis主从复制的原理主从复制概述配置主从复制数据同步过程复制一致性与延迟故障转移机制监控与维

Python在固定文件夹批量创建固定后缀的文件(方法详解)

《Python在固定文件夹批量创建固定后缀的文件(方法详解)》文章讲述了如何使用Python批量创建后缀为.md的文件夹,生成100个,代码中需要修改的路径、前缀和后缀名,并提供了注意事项和代码示例,... 目录1. python需求的任务2. Python代码的实现3. 代码修改的位置4. 运行结果5.

Redis连接失败:客户端IP不在白名单中的问题分析与解决方案

《Redis连接失败:客户端IP不在白名单中的问题分析与解决方案》在现代分布式系统中,Redis作为一种高性能的内存数据库,被广泛应用于缓存、消息队列、会话存储等场景,然而,在实际使用过程中,我们可能... 目录一、问题背景二、错误分析1. 错误信息解读2. 根本原因三、解决方案1. 将客户端IP添加到Re

Python中构建终端应用界面利器Blessed模块的使用

《Python中构建终端应用界面利器Blessed模块的使用》Blessed库作为一个轻量级且功能强大的解决方案,开始在开发者中赢得口碑,今天,我们就一起来探索一下它是如何让终端UI开发变得轻松而高... 目录一、安装与配置:简单、快速、无障碍二、基本功能:从彩色文本到动态交互1. 显示基本内容2. 创建链

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

锐捷和腾达哪个好? 两个品牌路由器对比分析

《锐捷和腾达哪个好?两个品牌路由器对比分析》在选择路由器时,Tenda和锐捷都是备受关注的品牌,各自有独特的产品特点和市场定位,选择哪个品牌的路由器更合适,实际上取决于你的具体需求和使用场景,我们从... 在选购路由器时,锐捷和腾达都是市场上备受关注的品牌,但它们的定位和特点却有所不同。锐捷更偏向企业级和专

手把手教你idea中创建一个javaweb(webapp)项目详细图文教程

《手把手教你idea中创建一个javaweb(webapp)项目详细图文教程》:本文主要介绍如何使用IntelliJIDEA创建一个Maven项目,并配置Tomcat服务器进行运行,过程包括创建... 1.启动idea2.创建项目模板点击项目-新建项目-选择maven,显示如下页面输入项目名称,选择

Spring中Bean有关NullPointerException异常的原因分析

《Spring中Bean有关NullPointerException异常的原因分析》在Spring中使用@Autowired注解注入的bean不能在静态上下文中访问,否则会导致NullPointerE... 目录Spring中Bean有关NullPointerException异常的原因问题描述解决方案总结

python中的与时间相关的模块应用场景分析

《python中的与时间相关的模块应用场景分析》本文介绍了Python中与时间相关的几个重要模块:`time`、`datetime`、`calendar`、`timeit`、`pytz`和`dateu... 目录1. time 模块2. datetime 模块3. calendar 模块4. timeit