0 准备工作
首先,确认环境中有numpy、scipy.stats和tkinter三个功能包。前两个功能包可用于Python的数学计算,比如使用numpy来生成随机数用于Monte Carlo模拟,以及使用scipy.stats包来计算正态分布概率累积函数,这两个功能包可以使用pip安装。而第三个功能包tkinter是Python自带的一个GUI系统,理论上已经整合在了你的Python环境中。所谓的GUI(Graphical User Interface),可以理解为是一个照顾使用者感情,善于跟使用者打交道的系统。我们必须假定使用我们程序的人,比如你在商学院的某个同学,喜欢一看就能明白的界面。他或许并不需要学会使用Console,就能比你赚更多的钱。
需要注意的一点是,虽然scipy已经集成了scipy.stats,但是引用需要单独操作。
其次,要对期权的基本定价方法有一定的了解。一个欧式看涨期权,其到期日的支付为max(S - K, 0),看跌期权为max(K - S, 0). 其中,S为到期日标的资产的价格,K为行权价格。对于一个非常简单的欧式期权,只要掌握几个基本参数,就可以对其进行定价。比如使用著名的Black-Scholes模型,或使用二叉树模型(后被证明其极限情况就是BS公式),或使用Monte Carlo方法(似乎是无懈可击但比较烧钱的方法)。这篇笔记将使用BS公式和Monte Carlo方法来对简单的期权进行定价,所以要对这两个方法使用的公式有所了解。
Black-Scholes的看涨期权和看跌期权的定价法分别如下:
其中F为标的资产的远期价格,下式中的q为连续红利率:
两个小d的公式则是:
Monte Carlo方法则更为简单粗暴,易于理解。在拙作《使用Python描绘Markowitz有效边界》中曾提到过Monte Carlo方法。在使用Monte Carlo计算期权价格时,假设标的资产每天的收益率是个均值为0,标准差为给定值的random walk. 则我们可以模拟给定时间以后标的资产可能的价格,针对每一个价格给出期权的获利水平,将其折现并取平均值。模拟股价的公式如下:
其中,z是一个服从标准正态分布N(0, 1)的随机变量。
到现在为止,我们已经对欧式期权定价的理论基础有所了解。打开你的Python代码编辑器,你便完成了所有的准备工作。
1 编写一个期权类
“万物皆对象”,既然我们需要针对一个欧式期权进行计算,不妨将期权编写成一个类,在将某个期权实例化为对象时,将其各种属性赋予这个对象。
编写类时,要先给类起一个名字,比如Option.
# -*- coding: utf-8 -*-
from tkinter import *
import numpy as np
import scipy.stats as sps
class Option:
然后想象一下,一个欧式期权应该具有哪些属性,从而编写一个初始化函数。我们的欧式期权,应该具有以下几个属性。
看涨或看跌(c or p)
标的资产现价(S0)
期权执行价格(K)
期权到期时间(t)
适用的无风险利率(rf)
适用的波动率(sigma)
股利信息(本例中使用连续股利率dv)
也就是说,为了构造一个欧式期权对象(或为了计算期权的价格),我们至少要知道上面的七个信息。换言之,程序的使用者要输入这七个信息,才能构造一个期权对象。因此,我们的初始化函数要从输入端读取七个变量。
class Option:
def __init__(self, cp, s0, k, t, r, sigma, dv=0):
self.cp = 'Call' if (cp == 'C' or cp == 'c') else 'Put'
self.cp_sign = 1.0 if self.cp == 'Call' else -1.0
self.s0 = s0 * 1.0
self.k = k * 1.0
self.t = t * 1.0
self.sigma = sigma * 1.0
self.r = r * 1.0
self.dv = dv * 1.0
初始化函数的名字是有规定的,叫做"__init__",应该是英语initialize的简写。它的参数有8个,第一个叫做self,它代表了对象本身。在编写一个类时,所有的方法(函数)都要有self这个参数,并默认放在第一个位置。在初始化函数中,我们用self.xxx的方式为类的实例规定它们的属性。如定义self.dv = dv * 1.0,即赋予了实例一个名为dv的属性,并赋值为从输入端读取的dv.
参数的读取区中,dv=0表示dv不是一个强制参数,如果我们不输入dv,则它的值为默认的0.
输入期权的看涨和看跌属性时,我们可以自定义一些规则,我在