本项目是一个使用Python语言编写的开源金融衍生品定价引擎库,包含多种定价方法,涵盖了国内场外衍生品市场主流的权益类衍生品结构。定价方法方面,本定价库支持解析解、Monte Carlo模拟法、PDE有限差分法、数值积分法以及树方法;波动率模型方面,本定价库支持常数波动率模型、局部波动率模型、Heston随机波动率模型。
基于国内场外衍生品市场发展的现状,市场对自动赎回结构(Autocallable),尤其是各种类型的雪球、凤凰结构的定价有更大的需求。本定价库立足于国内市场需求,支持多种雪球变种和凤凰结构的定价,具体如下:
产品分类 | 产品子类 | 模型 |
---|---|---|
1 香草及其组合 | 欧式,价差,跨式,宽跨式,折价,领口,风险逆转,蝶式,鹰式 | Black Scholes Merton解析解,蒙特卡洛模拟,PDE,积分法,二叉树 |
美式期权 | BAW(1987)近似解、Bjerksund Stensland(2002)近似解、LSMC(最小二乘蒙特卡洛)、PDE、积分法、二叉树 | |
2 亚式期权 | 亚式期权-几何平均/算术平均替代收盘价 | Kemma Vorst(1990)几何平均收盘价亚式解析解,Turnbull Wakeman (1991)算术平均收盘价亚式近似估计,算术平均收盘价二叉树,蒙特卡洛模拟 |
亚式期权-几何平均/算术平均替代执行价,增强亚式 | 蒙特卡洛模拟 | |
3 二元及其组合 | 单边二元:欧式二元,美式二元,一触即付 | Reiner Rubinstein(1991b)解析解,蒙特卡洛模拟,PDE,二叉树,积分法 |
双边二元:(欧式)二元凸式/二元凹式;(美式)双接触/双不接触 | Hui(1996)美式双接触/双不接触近似解,蒙特卡洛模拟,PDE | |
4 障碍期权 | 单边障碍:敲出期权(单鲨),敲入期权 | Reiner Rubinstein(1991a)单边障碍解析解,蒙特卡洛模拟,PDE,二叉树,积分法 |
双边障碍,双鲨 | Ikeda Kunitomo(1992)双边障碍级数解,Haug(1998)双边障碍近似解,蒙特卡洛模拟,PDE | |
安全气囊 | 解析解(障碍与二元组合),蒙特卡洛模拟,PDE | |
5 自动赎回结构 | 无敲入的自动赎回:二元小雪球 | 蒙特卡洛模拟,PDE,积分法 |
定期派息票据:FCN、DCN、凤凰 | 蒙特卡洛模拟,PDE,积分法 | |
雪球结构:平敲、降敲、早利、蝶变、双降、限损、OTM、降落伞、看涨雪球 | 蒙特卡洛模拟,PDE,积分法 | |
雪球变种:巴黎雪球 | 蒙特卡洛模拟 | |
6 累计期权 | 标准累购,标准累沽,区间累计 | 蒙特卡洛模拟 |
注:二元期权和障碍期权的解析解,都可以选择Broadie Glasserman Kou(1995)离散观察修正,既支持连续观察,也支持离散观察。
具体的产品及其参数的介绍,在"场外百科全书"微信小程序中,已经整理成了结构百科,可以方便地查阅。
我们会持续关注市场上的最新动态,迅速地在PriceLib中更新流行产品的定价模型。如果您有特别的产品定价需求,也可以与我们联系,我们通常可以在两至三天更新至PriceLib中。您可以在Gitee提交Issue,或通过下方电话、邮件联系我们,也欢迎大家扫描下方二维码关注"凌瓴科技"微信公众号。
如果您只想调用PriceLib实现定价,在您的终端中使用命令pip install pricelib
即可将PriceLib安装到Python解释器的Lib/site-packages
中。
如果您是开发者并且想要在本地安装这个Python库,您需要在 https://gitee.com/lltech/pricelib 下载项目的源码zip压缩包,然后在项目的根目录下,用终端运行pip install -e .
命令,这个目录应该包含setup.py
文件。这个命令将以"editable"模式安装这个库,这意味着您对源代码的任何修改都会立即反映到您的环境中,无需重新安装。
如果您还想安装绘图依赖项和开发依赖项(在setup.py
文件的extras_require
中定义,绘图依赖项包括matplotlib
和plotly
,开发依赖项包括静态分析工具flake8
、pylint
和测试工具pytest
),您应该在终端中使用pip install -e .[dev,plot]
命令, 这将安装PriceLib库、绘图依赖项和开发依赖项。
如果您只想安装PriceLib库和开发依赖项,则您应该使用pip install -e .[dev]
命令。
- 如果您使用
pip install pricelib
安装,用jupyter notebook运行代码,当您想要查看类和函数的说明文档时,有两种方式(以StandardSnowball
为例):
- 方法1:在jupyter notebook的一个空白代码块中,输入
?StandardSnowball
,然后运行。- 方法2:将光标移动到
StandardSnowball
上面,按Shift + Tab
,弹出文档,右上角的^
和+
会展开更多文档。- 如果您使用
pip install -e .
本地安装,用Pycharm运行代码,当您想要查看类和函数的说明文档时,有两种方式:
- 方法1:将鼠标悬停在
StandardSnowball
上面,Pycharm会自动弹出文档。- 方法2:可以按住
Ctrl
然后鼠标左键点击StandardSnowball
,即可自动跳转,查看源码和注释。- 教程篇幅有限,您可以在
examples
文件夹浏览详细的示例代码。如果您想了解产品类详情,可以在pricelib/products
文件夹中查看对应产品的说明,每个产品的说明都详细介绍了所有输入参数,以及支持的定价引擎。
只需两行代码,创建产品结构
,然后调用price
方法,即可完成定价。 以美式期权为例:
from pricelib import *
# 1. 创建产品结构,输入s、r、q、vol参数,此时会直接使用默认的定价引擎和BSM模型
option = VanillaOption(strike=100, maturity=1, callput=CallPut.Call,
exercise_type=ExerciseType.American,
s=100, r=0.02, q=0.01, vol=0.2)
# 2. 完成定价
print(option.price())
在上面的例子中,美式期权会自动使用默认的BAW近似解定价引擎进行定价。
同一个产品对象可以切换不同的定价引擎,将上面的美式期权改为PDE有限差分法进行定价:
# 创建PDE有限差分法定价引擎,输入s、r、q、vol参数,此时会直接使用默认的BSM模型
fdm_engine = FdmVanillaEngine(s=100, r=0.02, q=0.01, vol=0.2)
# 为产品对象切换定价引擎
option.set_pricing_engine(fdm_engine)
print(option.price())
调用产品对象的delta, gamma, vega, theta, rho
方法,即可计算常见的5种期权希腊值:
print({"delta": option.delta(), "gamma": option.gamma(), "vega": option.vega(),
"theta": option.theta(), "rho": option.rho()})
如果您想一次性计算期权估值和5种希腊值,可以调用产品对象的pv_and_greeks
方法:
result = option.pv_and_greeks()
# result == {'pv': pv, 'delta': delta, 'gamma': gamma, 'vega': vega, 'theta': theta, 'rho': rho}
再比如较为复杂的雪球结构,"敲出票息10%的一年期锁三103-80平敲雪球",一样只需两行代码即可完成定价:
from pricelib import *
option = StandardSnowball(maturity=1, lock_term=3, s0=100, barrier_out=103,
barrier_in=80, coupon_out=0.10, s=100, r=0.02,
q=0.01, vol=0.16)
print(option.price())
这里的经典雪球会使用默认的PDE有限差分定价引擎。输入参数s0=100, barrier_out=103, barrier_in=80
的价格是百分比形式,您也可以输入价格的绝对值,例如s0=5535.40, barrier_out=5701.46, barrier_in=4428.32
,只要所有价格参数保持一致即可。
常见问题QA
- Q: 我使用上述简易定价接口设置了r和vol,没有定价成功,为什么呢?
- A: 目前实例化产品的时候,您需要输入s、r、q、vol全部4个参数,才能自动创建默认的定价引擎。如果不需要分红率,您可以输入q=0。参数s指的是估值时的标的价格,与期初价格s0不同,因此需要指定s参数。若您想获得期初估值,可以将s与s0设为相同的值。
- Q: 雪球的定价结果为什么与examples中的demo有微小差异呢?
- A: 日期设置在后文有详细介绍,雪球敲出观察日缺省时,会按照起始日和交易日历自动生成敲出观察日序列,估值日和起始日会默认使用今天的日期,这会对定价结果产生微小的影响。您需要用
set_evaluation_date(datetime.date(2022, 1, 5))
设置估值日、在实例化StandardSnowball
时加上start_date=datetime.date(2022, 1, 5)
设置起始日,计算结果才能完全一致。
对于雪球结构的PDE定价引擎,您可以获取有限差分的price、delta和gamma网格,进行绘图:
from pricelib.common.utilities import pde_plot
# 这里的option是上面的StandardSnowball对象,其默认定价引擎是FdmSnowBallEngine
pde_engine = option.engine
# 绘制delta和gamma曲面图
delta_matrix, s_vec = pde_engine.delta_matrix(status=StatusType.NoTouch)
fig1 = pde_plot.draw_greeks_surface("delta", delta_matrix,
spot_range=(65, 115), show_plot=True)
gamma_matrix, s_vec = pde_engine.gamma_matrix(status=StatusType.NoTouch)
fig2 = pde_plot.draw_greeks_surface("gamma", gamma_matrix, show_plot=True)
# 绘制t=0时的delta和gamma曲线
fig3 = pde_plot.draw_greeks_curve(delta_matrix, gamma_matrix, t=0,
spot_range=(65, 115), show_plot=True)
除了使用默认的定价引擎之外,您也可以自行为产品结构配置所需的随机过程和定价引擎。
本定价库搭建了一个统一的框架:定价引擎、随机过程和波动率模型使用策略模式:
本框架不仅易于使用,而且方便扩展新的产品和定价方法。
以香草期权为例,首先创建标的价格spot、无风险利率r、分红率q、波动率vol等估值参数对象。
from pricelib import *
# 1. 市场数据,Observable被观察者
riskfree = SimpleQuote(value=0.02, name="无风险利率")
dividend = SimpleQuote(value=0.05, name="中证1000贴水率")
volatility = BlackConstVol(0.16, name="中证1000波动率")
spot_price = SimpleQuote(value=100, name="中证1000指数")
# 常数波动率
process = GeneralizedBSMProcess(spot=spot_price, interest=riskfree,
div=dividend, vol=volatility)
其中r、q、vol可以设为常数,也可以设为期限结构、局部波动率。
import pandas as pd
# 从csv文件中读取数据
div = pd.read_csv("./tests/div.csv")
loc_vol_df = pd.read_csv("./tests/loc_vol.csv", index_col=0)
expirations = loc_vol_df.index.values
strikes = loc_vol_df.columns.values.astype(float)
volval = loc_vol_df.values
# q期限结构
dividend = RateTermStructure.from_array(div['maturity'].values, div['q'].values)
# 局部波动率
volatility = LocalVolSurface(expirations=expirations, strikes=strikes,
volval=volval)
然后,实例化一个随机过程,可以选择广义BSM过程或Heston过程,其属性包括上述spot、r、q、vol等估值参数。
随机过程是Observer观察者,在估值参数改变时会收到通知。
根据不同的r和q值,广义BSM过程可以变为:
# 2. 随机过程
# BSM动态过程:常数波动率 or 局部波动率
process = GeneralizedBSMProcess(spot=spot_price, interest=riskfree,
div=dividend, vol=volatility)
# Heston动态过程
process = HestonProcess(spot=spot_price, interest=riskfree, div=dividend,
v0=0.025, var_theta=0.02, var_kappa=4.01,
var_vol=0.1, var_rho=-0.3)
接着,实例化一个定价引擎,定价引擎可以选择解析解、蒙特卡洛模拟、PDE有限差分法、数值积分法、树方法,
其属性包含一个随机过程,以及定价方法参数,例如:
# 3. 定价引擎
an_engine = AnalyticVanillaEuEngine(process)
mc_engine = MCVanillaEngine(process, n_path=100000, seed=0,
rands_method=RandsMethod.LowDiscrepancy,
antithetic_variate=True, ld_method=LdMethod.Sobol)
quad_engine = QuadVanillaEngine(process, quad_method=QuadMethod.Simpson,
n_points=801, n_max=4)
bitree_engine = BiTreeVanillaEngine(process, tree_branches=500)
pde_engine = FdmVanillaEngine(process, s_step=400, n_smax=4, fdm_theta=0.5)
最后,实例化一个期权产品对象,其属性包括一个定价引擎,以及产品参数,以香草期权为例,产品参数为看涨看跌、行权方式、行权价和到期时间。
# 4. 定义产品:香草欧式期权
option = VanillaOption(maturity=1, strike=100, callput=Callput.Call,
exercise_type=ExerciseType.European)
# 使用Monte Carlo模拟定价
option.set_pricing_engine(mc_engine)
price_mc = option.price()
# 使用有限差分法定价
option.set_pricing_engine(pde_engine)
price_pde = option.price()
调用期权产品的price()方法,即可实现定价。各个衍生品结构的不同定价方法的使用示例,详见 https://gitee.com/lltech/pricelib/examples 中的demo。
定价引擎支持的波动率模型具体如下:
定价引擎 | 波动率模型 |
---|---|
1 解析解 | 常数波动率 |
2 Monte Carlo模拟 | 常数波动率、局部波动率、Heston随机波动率 |
3 PDE有限差分法 | 常数波动率、局部波动率 |
4 积分法 | 常数波动率 |
5 树方法 | 常数波动率 |
在上面的例子中,产品的到期时间直接输入了maturity = 1
年:
option = VanillaOption(maturity=1, strike=100, callput=Callput.Call,
exercise_type=ExerciseType.European)
但是,有时候我们需要输入起止日期,这时候可以使用以下方式:
import datetime
start_date= datetime.date(2022, 1, 5)
end_date = datetime.date(2023, 1, 5)
option = VanillaOption(start_date=start_date, end_date=end_date, strike=100,
callput=Callput.Call, exercise_type=ExerciseType.European)
需要注意的是,采用设置起止日期的方法时,一般需要指定估值日期,否则估值日期默认为今天的日期:
set_evaluation_date(datetime.date(2022, 1, 5))
print(option.price())
在仅输入maturity = 1
年时,实际上将起始日期设置为了估值日期,结束日期设置为了估值日期延后1年。
如果您需要更精细地处理交易日历和年化系数,可以在产品对象中设置相关参数:
option = VanillaOption(strike=100, callput=Callput.Call,
exercise_type=ExerciseType.European,
start_date=start_date, end_date=end_date,
trade_calendar=CN_CALENDAR,
annual_days=annual_days, t_step_per_year=t_step_per_year)
set_evaluation_date(datetime.date(2022, 1, 5))
其中:
对于一些结构复杂的衍生品,例如雪球、凤凰等自动赎回结构,涉及到敲出观察日。
正如前面举过的例子,您可以不输入敲出观察日,仅输入存续期maturity = 1
年和锁定期lock_term = 3
个月,
此时产品对象会自动生成一个根据交易日历调整后的敲出观察日期序列:
option = StandardSnowball(maturity=1, lock_term=3, s0=100, barrier_out=103,
barrier_in=80, coupon_out=0.10, coupon_div=0.05)
这是一个"敲出票息10%,红利票息5%的1年期锁三103-80平敲雪球",由于敲出观察日缺省,在定价过程中,程序会自动生成如下的敲出观察日序列(假设估值日期设为2022年1月5日):
20220406_20220505_20220606_20220705_20220805_20220905_20221010_20221107_20221205_20230105
可以注意到自动生成的日期序列是每个月的工作日,遇到周六周日和法定节假日会向后顺延(如果顺延后月份变化,则会向前调整)。
另一种方式是直接输入敲出观察日序列,同时到期时间改为输入起止日期:
from datetime import date
start_date= date(2022, 1, 5)
end_date = date(2023, 1, 5)
obs_dates = [date(2022, 4, 6), date(2022, 5, 5), date(2022, 6, 6), date(2022, 7, 5),
date(2022, 8, 5), date(2022, 9, 5),date(2022, 10, 10), date(2022, 11, 7),
date(2022, 12, 5), date(2023, 1, 5)]
option = StandardSnowball(start_date=start_date, end_date=end_date,
obs_dates=obs_dates, lock_term=3, s0=100,
barrier_out=103, barrier_in=80,
coupon_out=0.10, coupon_div=0.05)
set_evaluation_date(date(2022, 1, 5))
print(option.price())
这里使用的是默认的CN_CALENDAR(中国内地交易日历),年化系数为365和243,如果需要更改,可以在产品对象中设置相关参数。
如果您在运行过定价库的代码之后,移动了定价库的位置,或者更改了定价库内文件夹的名称,这时候可能会出现报错。
这个问题产生的原因是定价库中使用了numba的JIT(Just in Time)即时编译技术进行加速,
并且开启了cache=True
,这样会将编译好的中间代码缓存到本地,以提高下次运行的速度。如果您移动了定价库的位置,缓存的中间代码没有更新,于是就会引发报错。
遇到这种问题时,只需要在定价库目录搜索所有的__pycache__
文件夹,删除其中的所有文件,然后重新运行代码即可。
当前库中暂未把资金交易作为重点,例如,在发行雪球时,在各个时点与对手方进行现金和利息的收付,后续我们会视情况增加。此外,我们目前聚焦于权益类衍生品,利率、汇率、商品等其他品种会在后续逐步扩展。
由于Python语言是出名的胶水语言,和C/C++、Java等工业界常用的开发语言可以很好的融合,当然也可以使用Flask直接封装成SaaS服务。总之,基于PriceLib定价库,可以开发多种场外业务系统,包括但不限于:
PriceLib作为一个核心定价引擎库,没有界面,不太直观,因此在PriceLib的基础上,我们短时间快速开发了pyRisk这个小巧的工具作为展示定价引擎的窗口。pyRisk具有以下功能:
pyRisk的定位是一个小巧的风险管理的辅助工具,主要目的只是为了展示应用PriceLib的经典场景,您可以通过以下网址免费下载使用pyRisk:
https://api.galatech.com.cn/pyRisk/pyRisk_1.0.0_win10_x64.zip
如果对于系统有较高性能和产品定制化特性要求,欢迎与我们联系,试用凌瓴科技采用C++开发的智能风险管理平台iRisk。
├─examples 使用示例
├─pricelib 金融衍生品定价库
│ ├─common 公共类和函数
│ │ ├─product_base 产品基类
│ │ ├─pricing_engine_base 定价引擎基类
│ │ ├─processes 随机过程
│ │ ├─term_structures 期限结构
│ │ ├─volmodels 波动率模型
│ │ ├─time 日期处理
│ │ └─utilities 工具函数
│ ├─products 产品
│ │ ├─vanilla 香草期权
│ │ ├─asian 亚式期权
│ │ ├─digital 二元期权
│ │ ├─barrier 障碍期权
│ │ ├─autocallable 自动赎回
│ │ └─accurals 累计期权
│ └─pricing_engines 定价引擎
│ ├─analytic_engines 解析解
│ ├─fdm_engines PDE有限差分法
│ ├─integral_engines 数值积分法
│ ├─mc_engines 蒙特卡洛模拟
│ └─tree_engines 树方法
├─tests 测试
├─setup.py 安装配置文件
├─.flake8 静态代码检查配置文件
├─.pylintrc 静态代码检查配置文件
├─LICENSE 许可证文件
├─NOTICE 项目许可
└─README.md 说明文档
支持的Python解释器: Python 3.8及以上版本。
必须安装以下计算依赖项:
可选安装以下绘图依赖项:
具体详见setup.py文件。
本项目使用flake8
和pylint
进行静态代码检查,使用pytest
进行单元测试
flake8 pricelib
,pylint pricelib
pytest
pricelib is an open-source financial derivatives pricing library written in Python.
This file is part of pricelib.
Copyright (C) 2024 Galaxy Technologies
Licensed under the Apache License, Version 2.0 (the "License"); you may not use pricelib except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
在长期的默默地准备中,来自上海财经大学的多位实习生做出了重要的贡献。在这里我们要大声地感谢他们:夏鸿翔、张鹏任、张峻尉、刘振伟、裴子涵,谢谢你们!也让我们感谢上海财经大学培养了这么多优秀的学生,为金融强国贡献了力量!
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。