最近面试的时候,面试官问我怎么用蒙卡模拟无股息的美式期权定价。我瞬间石化。。。不怕死地申请了期权建模的实习。。。主要参考课件
主要内容二叉树定价推导+Python
Longstaff-Schwartz定价推导+Python
一、引言
本讲的大前提:股票无股息。
1、美式期权主要问题是:在
之间找到合适的停止点
,从而美式期在t时刻价值
其中,
是收益,又称为内在价值。等式不难理解:给定t时刻的期权价格为
,找到未来合适的停止点
,然后把那一刻的期权价值折现到t时刻。
注意:
是随机值(random variable)。对于股票过程(例如GBM)的每个不同路径,
可以不同。解决之道:每时每刻基于标的股票的当前价值、评估此刻执行期权是不是最优的。
可以证明,V(t,s)是下面方程的弱解(weak solution) :
又是解PDE。。。我之前写过欧式期权的PDE,可参考丹尼尔:Code and Finance 4 期权定价之有限差分法
美式期权实际上有3中解法,①PDE(以后有空再整理);②二叉树(Binomial tree);③Longstaff-Schwarts(※※※)。本文主要讲解后两种~
二、二叉树求解美式期权定价
1、基本逻辑前提假设风险中性的世界中,衍生品的现值=未来预期收益的折现值,折现率为无风险收益率
。
股价在
的时间内,上升概率为
,下降概率为
;
即为无风险概率。
上升幅度为
,下降幅度为
;
当前时间是t=0,当前标的资产股价为
,当前衍生品价格为
;
时间后,股价要么上升到
,对应的衍生品价值为
;要么下降到
,对应的衍生品价值为
。
如何求解p? ,那么
。
如何求解u和d?前提条件:
Grisanov's theoremu和d选择的必要条件:股价收益率的波动率在现实世界和风险中性世界一致(股价收益率在两个世界可以不同)。
综上:
2、实例+代码例子:当前股价为100,put行权价为100,时间为1年,无风险利率为0.1,年化波动率为0.2。求出当前时间的美式看跌期权价格。
假设我把1年的时间切分成100份,那么在最后一次,一共有101个分支价格,这就是S_T是101个数据点;然后分别和K进行比较,得到每个点的call/put价格。接着,将第100个时间点向第99个时间点折现,注意:
的期权折现值
。
接着,比较
提前行权的期权价值
和折现值的大小;哪个大那么就选择哪个。代码
import numpy as np
S0 = 100
K = 100
T = 1
r = 0.1
sig = 0.2
payoff = "put"
def getAmeOption(S0, K, T, r, sig, N, payoff):
# 将时间T拆分为N份,每份是dT。
dT = float(T) / N
#计算u,d,p,q。
u = np.exp(sig * np.sqrt(dT))
d = 1.0 / u
a = np.exp(r * dT)
p = (a - d)/(u - d)
q = 1.0 - p
# V承接N+1个价格
V = np.zeros(N+1)
# 注意S_T的生成顺序,是从u=0,d=N开始.
S_T = np.array( [(S0 * u**j * d**(N - j)) for j in range(N + 1)] ) # price S_T at time T
if payoff =="call":
V[:] = np.maximum(S_T-K, 0.0)
elif payoff =="put":
V[:] = np.maximum(K-S_T, 0.0)
for i in range(N-1, -1, -1):
# 这一步至关重要:V在第k轮迭代中,只关注0~N-k的位置,
# 每个位置=(下一个位置上升的期权*p+下一个位置下降的期权*q)*折现因子
V[:-1] = np.exp(-r*dT) * (p * V[1:] + q * V[:-1])
# 股价也进行新一轮的迭代,同样只有0~N-k是需要关注的,剩下的位置无关紧要。
S_T = S_T * u
# 比较此刻行权和下一轮预期收益的折现
if payoff=="call":
V = np.maximum( V, S_T-K )
elif payoff=="put":
V = np.maximum( V, K-S_T )
return V[0]
结果
getAmeOption(S0, K, T, r, sig, 10000, 'put')
getAmeOption(S0, K, T, r, sig, 10000, 'call')
put价格为4.81661591469,call价格为13.269467766434364。
三、Longstaff-Schwartz这个方法又称为是最小二乘法。
1、步骤详解——通过例子详细解答假设:我模拟了10条(paths=10)路径,以及将时间分成了4段(N=4,0-1-2-3),
,每段时间的折现因子
,
。求出0时刻的call&put价格。
第一步:求出10条路径的股价发展过程首先,第0个时间点的10条路径是[100, ... 100](10个100);
第1个时间点的10条路径计算方式是把第0条的10个100作为输入值,
=[95.84, ..., 121.39](10个随机数);
第2个时间点的10条路径是把第1个时间点的10条路径的股价作为S0输入,结果为[93.16, ... , 135.93];
第3个时间点的10条路径是把第2个时间点的10个路径的股价作为S0输入,结果为[103.58, ... , 145.24]。
因此股价的矩阵为
代码
from random import gauss
from math import exp, sqrt
def calculate_S_T(S, v, r, T):
"""模拟epsilon,计算S_T"""
return S * exp((r - 0.5 * v ** 2) * T + v * sqrt(T) * gauss(0.0, 1.0))
def getRoundStockPrice(S, v, r, N, T, rounds, paths):
firstRound = [S] * paths
nextRound = firstRound.copy()
for i in range(rounds):
nextRound1 = nextRound[-paths:]
for s in nextRound1:
nextS = calculate_S_T(s, v, r, T)
nextRound.append(nextS)
nextRound = np.array(nextRound).reshape((-1, paths)).T
return nextRound
S = 100
v = 0.2
r = 0.1
N = 3
T = 1 / N
paths = 10
S_Matrix = getRoundStockPrice(S, v, r, N, T, N, paths)第二步:求出每个时期行权的收益,称为exercise value(EV)call的EV=max(S-K, 0),put的EV=max(K-S, 0),第三步t=3的时候,必须要行权,exercise value(EV)=holding value(HV)。
t=2的时候,计算第2期的收益现值HV,就是t=3的EV折现(这是因为第三期就到期了,第三期的EV=HV),即为下两张表的第2列2callHV和2putHV。
然后,将t=2的EV>0的部分圈出来(※※※※※),对于call而言,即为2.3.6.7.10,对于put而言,即为1.4.5.8.9;
然后对相应行进行least-squares回归,回归方程
;其中S是第2期的股价,HV是第二期的持有价值HV;即为y是2callHV(2putHV),自变量是S2和S2^2;得到回归系数之后,再代入第二期的股价S,求出E(HV);注意,put和call分开回归,不是一起回归。
然后计算出E(HV),即为2callE(HV)和2putE(HV),比较E(HV)和EV大小;如果E(HV)>EV,那么绝不提前行权;如果E(HV)≤EV,那么第二期立刻行权。最后,得到了第二期的期权情况,即为最后一列。t=1的时候,第2列1callHV和1putHV,分别是上面两张表的最后一列的折现值;第二列来自H矩阵在t=1的值;同样,圈出EV>0的情况,call是2、3、6、7、10行,put是1、4、5、8、9行,分别和S1&S1^2回归,然后计算得到1callE(HV)和1putE(HV);比较E(HV)和EV的大小,if E(HV)>EV,绝不提前行权;else 提前行权;最终得到1call和1put。最后一步,将1call和1put分别计算均值,然后折现到t=0时期,即为
和二叉树差的有点远。。应该是我分的区间太小了。下面拆成10000*10000的网格点的时候,明显结果更加稳健。
2、代码详解
可以看到,上面的计算,程序基本大同小异,那么使用python一次性解决。
import scipy.stats as ss
import numpy as np
def LSM(S0, K, T, r, sig, payoff, N=10000, paths=10000, order=2):
dt = T/(N-1) # time interval
df = np.exp(r * dt) # discount factor per time time interval
X0 = np.zeros((paths,1))
increments = ss.norm.rvs(loc=(r - sig**2/2)*dt, scale=np.sqrt(dt)*sig, size=(paths,N-1))
X = np.concatenate((X0,increments), axis=1).cumsum(1)
S = S0 * np.exp(X)
if payoff == "put":
H = np.maximum(K - S, 0) # intrinsic values for put option
if payoff == "call":
H = np.maximum(S - K, 0) # intrinsic values for call option
V = np.zeros_like(H) # value matrix
V[:,-1] = H[:,-1]
# Valuation by LS Method
for t in range(N-2, 0, -1):
good_paths = H[:,t] > 0
# polynomial regression:将EV>0的部分挑出来回归
rg = np.polyfit( S[good_paths, t], V[good_paths, t+1] * df, 2)
# 估计E(HV)
C = np.polyval( rg, S[good_paths,t] )
# 如果E(HV)
exercise = np.zeros( len(good_paths), dtype=bool)
exercise[good_paths] = H[good_paths,t] > C
V[exercise,t] = H[exercise,t]
V[exercise,t+1:] = 0
discount_path = (V[:,t] == 0)
V[discount_path,t] = V[discount_path,t+1] * df
V0 = np.mean(V[:,1]) * df #
return V0
结果
call = LSM(100, 100, 1, 0.1, 0.2, 'call', N=10000, paths=10000, order=2)
put = LSM(100, 100, 1, 0.1, 0.2, 'put', N=10000, paths=10000, order=2)
得到call=13.297700328385172,put=4.755646113796444。
基本和二叉树的结果一致。
整理了一天,终于完成这篇小作业~对于二叉树定价,我这里也是第一次试验,结果还挺不错;以前以为二叉树是不是太简单的,现在来看,结果和复杂的Longstaff-Schwartz结果一致;而且运行速度也更占优势。
以上是美式无股息期权定价推导+Python代码,希望能够给你带来帮助~
参考
如果觉得《美式期权定价python_蒙特卡洛模拟和美式期权定价》对你有帮助,请点赞、收藏,并留下你的观点哦!