写个期权定价的东西难吗?你如果随便搜几下,网上就能找到上千篇的期权价格计算的代码实现文章,功能都一样,看似可以满足需求。如果真的用起来(就是动手了,实操了,不是小打小闹!),你会发现…

1. 函数输入参数列表冗长?

2. 各种标志位(option_type="看涨"、"看跌")?

3. 代码里面一堆if...else...?

4. 手里一堆持仓,怎么办?

5. 怎么做压力测试?

6. 情景分析?

7. 有各种期权结构,但都写在一个文件里。有好几个做开发的人,任务分不出去?

……

因为没人教,又找不到人问,所以吃了很多苦,绕了很多弯路。

但也让我理解得更透彻,更深刻。

一、前人留下的版本

1. 一个文件。4000+行。一半以上代码冗余。

2. 标志位Type,str输入类型,可输入"Price"、“Delta”、“Gamma”、“Vega”、“Theta”、“ALL”。根据不同的Type,返回不同结果。麻烦的是,如果写的“ALL”,返回数据类型将是一个list。

3. 基本是一个函数一种结构,也有其实没用到,但不得不有的全局变量,读取交易日历。

4. ……

之后,演变路径基本是这样的。

二、OOP版本。v1。

定义了VanillaCallOption类、VanillaPutOption类、BinaryCallOption类、BinaryPutOption类。但是,缺点

1. 期权合约要素作为了构造函数的输入参数(非常不好,Python不允许函数重载overload)。

2. 期权合约要素有很多是共有属性,可以复用。

三、OOP版本。v2。

对上个版本的改进。 用上继承、运算符重载。

1. 定义了 BasicOption类,作为基本期权类,封装了“我认为”的公共属性,也声明(没办法,Python里就写成了定义)了一堆“我认为”的公共函数(如计算价格 calc_price,计算delta calc_delta等)

这样,每种结构的期权,都去extends基类,并override函数,把价格和风险指标的实现方式改成自己特有的公式,就可以了。

如果我有10个人,每人分出去1种结构,比起一个文件全部写完,我们的开发速度可以是10倍。当然,我想多了,在这里也只有我一个人了……

import numpy as np
from scipy.stats import norm
from greeks import Greeks  # 需要读者自己创建相关类,具体定义在后面class BasicOption:def __init__(self, s, k, t, v, r, d):self.s = s  # 标的物价格self.k = k  # 行权价self.t = t  # 剩余期限时间self.v = v  # 波动率self.r = r  # 利率self.d = d  # 分红self.greeks = None  # 希腊值self.price = 0  # 价值# 检查期权是否到期self.is_expired = self.t <= 1e-7@staticmethoddef blsd(x, u, v, t):return (np.log(x) + u * t) / (v * (t ** 0.5))@staticmethoddef blsd_1(s, k, t, v, r, d):return BasicOption.blsd(s / k, r - d + 0.5 * v ** 2, v, t)@staticmethoddef blsd_2(s, k, t, v, r, d):return BasicOption.blsd(s / k, r - d - 0.5 * v ** 2, v, t)@staticmethoddef nd_1(s, k, t, v, r, d):return norm.cdf(BasicOption.blsd_1(s, k, t, v, r, d))def calc_price(self):"""计算价格,返回数字:return:"""return 0def calc_delta(self):"""计算delta,返回数字:return:"""return 0def calc_gamma(self):return 0def calc_vega(self):return 0def calc_theta(self):return 0def calc_rho(self):return 0def calc_greeks(self):"""计算greeks,返回greeks:return:"""delta = self.calc_delta()gamma = self.calc_gamma()vega = self.calc_vega()theta = self.calc_theta()self.greeks = Greeks(delta, gamma, vega, theta, rho=0)return self.greeksdef calc_price_and_greeks(self):self.calc_price()self.calc_greeks()

2. 定义了Greeks类,作为期权风险指标的类,还重载了运算符。可以见前文《pyhton:运算符重载(期权Greeks相加)》。看起来很炫酷。

class Greeks:def __init__(self, delta=0.0, gamma=0.0, vega=0.0, theta=0.0, rho=0.0):""":param delta: 标准delta:param gamma: 标准gamma:param vega: :param theta::param rho:"""self.delta = deltaself.gamma = gammaself.vega = vegaself.theta = thetaself.rho = rhodef __add__(self, other):""":param other: 只能是greeks:return:"""self.delta += other.deltaself.gamma += other.gammaself.vega += other.vegaself.theta += other.thetaself.rho += other.rhoreturn selfdef __sub__(self, other):""":param other: :return: """self.delta -= other.deltaself.gamma -= other.gammaself.vega -= other.vegaself.theta -= other.thetaself.rho -= other.rhoreturn selfdef __mul__(self, other):""":param other: 只能是标量:return:"""self.delta *= otherself.gamma *= otherself.vega *= otherself.theta *= otherself.rho *= otherreturn selfdef __truediv__(self, other):""":param other: 只能是标量:return:"""self.delta /= otherself.gamma /= otherself.vega /= otherself.theta /= otherself.rho /= otherreturn selfdef __str__(self):s = """delta: {delta}gamma: {gamma}vega:  {vega}theta: {theta}rho:   {rho}""".format(delta=self.delta,gamma=self.gamma,vega=self.vega,theta=self.theta,rho=self.rho)return sif __name__ == '__main__':delta = 0.5gamma = 0.1vega = 0.3theta = 0.5rho = 0.2greeks_1 = Greeks(delta, gamma, vega, theta, rho)greeks_2 = Greeks(delta, gamma, vega, theta, rho)print(greeks_1 - greeks_2)

3. 那么,欧式看涨期权的解析解实现就应该这么写。

"""
标准欧式看涨
Vanilla Call Function Based On Black-Scholes Model
"""import numpy as np
from scipy.stats import norm
from basic_option import BasicOption
from greeks import Greeksclass VanillaCallOption(BasicOption):def __init__(self, s, k, t, v, r, d):BasicOption.__init__(self, s, k, t, v, r, d)def calc_price(self):if self.is_expired:price = np.maximum(self.s - self.k, 0)else:price = self.s * np.exp(-self.d * self.t) * norm.cdf(self.blsd(self.s / self.k, self.r - self.d + 0.5 * self.v * self.v, self.v, self.t)) - self.k * np.exp(-self.r * self.t) * norm.cdf(self.blsd(self.s / self.k, self.r - self.d - 0.5 * self.v * self.v, self.v, self.t))self.price = pricereturn self.pricedef calc_delta(self):if self.is_expired:delta = 0else:delta = np.exp(-self.d * self.t) * norm.cdf(self.blsd(self.s / self.k, self.r - self.d + 0.5 * self.v * self.v, self.v, self.t))return deltadef calc_gamma(self):if self.is_expired:gamma = 0else:gamma = np.exp(-self.d * self.t) * norm.pdf(self.blsd(self.s / self.k, self.r - self.d + 0.5 * self.v * self.v, self.v, self.t)) / (self.s * self.v * (self.t ** 0.5))return gammadef calc_vega(self):if self.is_expired:vega = 0else:vega = self.s * np.exp(-self.d * self.t) * norm.pdf(self.blsd(self.s / self.k, self.r - self.d + 0.5 * self.v * self.v, self.v, self.t)) * (self.t ** 0.5)return vegadef calc_theta(self):if self.is_expired:theta = 0else:theta = self.d * self.s * np.exp(-self.d * self.t) * norm.cdf(self.blsd(self.s / self.k, self.r - self.d + 0.5 * self.v * self.v, self.v, self.t)) - np.exp(-self.d * self.t) * self.s * norm.pdf(self.blsd(self.s / self.k, self.r - self.d + 0.5 * self.v * self.v, self.v, self.t)) * self.v / (2 * (self.t ** 0.5)) - self.r * self.k * np.exp(-self.r * self.t) * norm.cdf(self.blsd(self.s / self.k, self.r - self.d - 0.5 * self.v * self.v, self.v, self.t))return thetadef calc_rho(self):return 0if __name__ == '__main__':s = 1k = 1r = 0.05d = 0.05t = 1v = 0.2option = VanillaCallOption(s, k, t, v, r, d)price = option.calc_price()print('price: {price}'.format(price=price))greeks = option.calc_greeks()print('delta: {delta}'.format(delta=greeks.delta))print('gamma: {gamma}'.format(gamma=greeks.gamma))print('vega: {vega}'.format(vega=greeks.vega))print('theta: {theta}'.format(theta=greeks.theta))

输出结果

price: 0.07577082146427272
delta: 0.5135001229824934
gamma: 1.8879647164532514
vega: 0.3775929432906503
theta: -0.033970753255851395

但是,缺点

1. 我发现,“行权价”k这个属性,不应该放在BasicOption里面,因为有些期权不止这个属性。比如垂直价差(vertical spread)。需要高、低行权价。尽管用了继承,但这样写下去,好像会多出一堆无关紧要的成员变量。

2. 运算符重载,算了,不要搞这些花里胡哨的了。

3. 同一种结构的期权,其价格的计算可能可以有不同的实现方式,如解析解、偏微分方程(扩散方程)的数值解法(如有限差分)、蒙特卡洛模拟,各种方法的价格应该一致。

这样写下去,如果我要求 看涨期权分别用 解析解、有限差分、蒙特卡洛分别计算 其价格,那我要新建一个 VanillaCallOptionFde 类、一个 VanillaCallOptionMc 类。它们分别继承 BasicOptionFde类 和 BasicOptionMc类,而后面这两个类,需要都继承BasicOption类。

其中,BasicOptionFde类 属于有限差分的公共基类,目前所接触到的期权价格计算有一定的规律。设置边界条件、从后往前迭代计算。BasicOptionMc类 属于蒙特卡洛的公共基类,具有生成标的物随机价格曲线的功能。

但是,我的需求仅仅是成员函数的实现方式的不同,我用有限差分求解欧式看涨期权的时候,有限差分计算类本身,不需要有 行权价、剩余期限等期权条款作为成员变量,也不需要有 标的物价格、波动率等市场环境作为成员变量。这是一种负担。

4. 计算公式里面,变量就变量,别用成员变量,加self.好难看啊!你拿到标准的、原生态的公式,还要全部替换成 self.xxx累不累?

于是,我在去掉了构造函数必填的参数列表,参考了“策略模式”[1]、面向对象“多态”,也参考了quantlib[2]这种成熟库的写法,有了目前正在写的第三个版本。

四、OOP版本。v3。

参考[1]和[2]就能写出来。等后面又有情怀,慢慢开源吧…

Ref.

[1] 策略模式

[2] quantlib官网

对期权价格计算的实现方式的思考相关推荐

  1. .NetCore中三种注入方式的思考

    .NetCore中三种注入方式的思考 原文:.NetCore中三种注入方式的思考 该篇内容由个人博客点击跳转同步更新!转载请注明出处! .NetCore彻底诠释了"万物皆可注入"这 ...

  2. 《面向对象的思考过程(原书第4版)》一 第2章 如何以面向对象的方式进行思考...

    本节书摘来自华章出版社<面向对象的思考过程(原书第4版)>一书中的第2章,[美] 马特·魏斯费尔德(Matt Weisfeld) 著黄博文 译更多章节内容可以访问云栖社区"华章计 ...

  3. 阮一峰老师博客爬取与博客文章存储持久化方式的思考

    阮一峰老师博客爬取与博客文章存储持久化方式的思考 前言 博客文章存储持久化思考 文本形式存储 html形式存储 pdf形式存储 博客爬取思路 爬取思路一 爬取思路二 个人选择 pdf存储 结尾 前言 ...

  4. 传统登录实现方式问题思考

    传统登录实现方式在应付分布式.微服务场景时存在的问题: 1. 每个微服务都要进行登录校验,十分麻烦,我们需要的是单点登录 2. 会话保持问题 3. 认证方式单一,无法适应各种认证场景(扫码,指纹... ...

  5. 谷牛期权长期投资策略的实践与思考

    把谷牛期权作为策略构建的主要品种,已经有半年多的时间,从期初的顺利,到中期的掉坑,再到后续的爬坑,也算是经历了一个小周期,在这里把投资中的体会和想法做个分享. 小周期经历了三个阶段,如下图: 第一阶段 ...

  6. 【数据结构与算法】数组动态分配方式的思考

    概述 之前编写的顺序表: 在我写的第一个顺序表里面,内部维护的数组长度是定长的,为100,不存在扩容和缩容的问题.然而如果顺序表储存了较少元素,会造成空间的较多冗余:如果顺序表内部数组空间满,会发生上 ...

  7. Delphi更高效率的编程方式的思考【一】

    我想还是有必要花点时间来整理一下思路的,就是说有必要写一些什么吧. 博客园有一点不好的地方是:没有自己的客户端,我不喜欢安装那些乱七八糟的程序. 虽然博客园提供了一个居于微软的软件,但是我不喜欢使用它 ...

  8. 换一种方式去思考--microsoft for win server03

    微软的东西被很多人鄙视. 原因是蔽塞,但大部分是无奈. 域环境下实现的强大功能是其他系统所不能比拟的. 沿用这样的战略思路.只要小盖开心,可以让全球的MS系统计算机组建成微软计算机群的大军. 这也是 ...

  9. 惊艳面试官-Java中关于随机数生成8种方式的思考

    Java中生成随机数常用的有下面这8种写法:简而言之,名称带安全的未必安全,名字简洁的未必简单. Math.random() Random ThreadLocalRandom SecureRandom ...

最新文章

  1. 堪比当年的LSTM,Transformer引燃机器学习圈:它是万能的
  2. python计算特征的统计值并文本输出
  3. js脚本 处理js注入
  4. 微信开发:微信js_sdk 分享,前端部分(二)
  5. preg_grep用法
  6. linux下源码安装cmake
  7. CSPNOIP2020总结
  8. 不出现php version网页_php冷知识 - 从命令行参数列表中获取选项
  9. python replace函数后面的数字的含义
  10. Memcached 教程 | 菜鸟教程
  11. JavaScript 框架这一年:React、Angular 们正在互相渗透
  12. compile函数 java_正则表达式--关于Java中Pattern.compile函数的相关解释
  13. ios开发网络学习AFN框架的使用一:get和post请求
  14. matlab中 晶闸管整流桥导通角_逆变角如何设置,matlab仿真模型作业
  15. viper4android蓝牙耳机,蝰蛇音效app下载-蝰蛇音效官方版(ViPER4Android FX)下载v2.7.1.0 安卓版-单机手游网...
  16. BLP模型(Bell-La Padula模型)
  17. Java实现两个csv文件的对比_比较 csv 文件中数据差异
  18. 关于广州“开四停四“违法逻辑实现
  19. java下雪_下雪屏保java,基础
  20. mailgun_用Mailgun邮寄出去!

热门文章

  1. php实现挖掘百度相关词,百度相关搜索关键词采集即长尾关键词挖掘脚本
  2. JavaScript模块打包器rollup
  3. Fragment overlap problem
  4. STM8L101时钟管理
  5. reactos终于被成功编译通过
  6. 数据治理周周谈(三):数据质量管理
  7. 用html语言编写彩虹雨流动代码,前端H5 canvas 爱心和彩虹雨
  8. STM32F030C8T6单片机PWM呼吸灯寄存器配置
  9. 自考英语二之从阅读开始--2020-11-15
  10. 小度智能音箱维修点_小度在家是哪家公司的_小度在家智能音箱怎么样