无套利与二叉树期权定价
一份看涨期权在到期时可能支付 20,也可能支付 0。今天该付多少才能买到它?如果能用股票和债券拼出完全相同的到期支付,期权价格就必须等于这个组合的成本。二叉树把未来每一步限制为两种状态,让这笔账可以逐节点算清。
金融市场与数学建模已介绍期权权利、到期支付和扣除权利金后的盈亏。下面的数值均为假设,金额采用同一种货币单位,每份期权对应一股不派息的股票。
到期支付、今天的价格与套利
行权价为 、到期股价为 时,看涨和看跌期权的到期支付分别为:
支付取决于到期状态; 和 则是今天购买权利的价格。欧式期权只能在到期时行权,美式期权还允许提前行权。
套利指无需净投入、未来各状态都不亏损,且至少一个具有正概率的状态能获利的交易策略。假定两种股价状态都有正概率,且相关交易可以执行。MIT 开放课程的期权习题课讲义用相同支付必须具有相同成本的原则推导复制定价与看涨看跌平价。否则,买入便宜的一边、卖出昂贵的一边,就能留下确定的差额。
这里允许按同一无风险利率借贷、买卖任意比例的股票和期权,并允许卖空;不计手续费、税收、买卖价差和保证金约束。这些条件保证下面的套利交易可以成立。
一期模型:买半股,再借一笔钱
设今天股价 ,一期后只可能为 或 。一期无风险增长因子为 ,即投入 1,到期得到 1.05;这是一期 5% 的利率。欧式看涨期权行权价 。
用 股股票和今天价值为 的无风险债券复制它。债券到期价值为 ; 表示借款。两种状态都要满足:
两式相减后,,所以 。再代回下跌状态:
也就是买半股、借入 38.095238,净成本为 11.904762。借款到期须偿还 40:
两种结果都复制成功,因此价格由组合成本确定。计算不需要先猜上涨有多大概率。
若这份欧式看涨期权报价为 14,卖出一份并建立复制组合,今天剩下 。把差额存入无风险账户,到期得到 2.2;复制组合支付恰好抵消卖出期权的负债,两种状态都获利。若期权价格低于复制成本,则反向买入期权、卖空复制组合,并存入差额。金额显示到六位小数,计算使用未舍入值。
风险中性概率从哪里来
对一般的一期树,令 、,其中 。股票与债券本身无套利要求:
若 ,卖空股票并把所得存入账户,偿付股票后各状态不亏,下跌时获利;若 ,借钱买股票也能产生套利。严格不等式让两种状态都有正的定价权重。
任意到期支付 的复制组合满足:
把它们代入 ,可整理成:
称为风险中性上涨概率。它满足 :按这组权重取平均,股票增长率等于无风险增长率。这个例子中,、,所以:
这里的 62.5% 是复制方程给出的定价权重,不是对真实上涨频率的预测。真实概率 用于预测收益和风险;直接拿 对期权支付求平均,再按无风险利率贴现,一般不能得到复制价格。风险中性定价也不要求投资者真的对风险无所谓。
看涨看跌平价:再核对一组价格
对同一不派息股票、相同行权价和到期日的欧式期权,比较两种持仓:看涨期权加一张到期支付 的债券,看跌期权加一股股票。到期时两边都支付 ,因此:
在一期例子中,看跌期权的上涨支付为 0,下跌支付为 20:
价差也一致:。多期时把 换为到期行权价的现值;连续复利、年利率 和期限 下为 。派息股票需要计入分红,美式期权可能在不同时间行权,所以不能直接套用这条欧式等式。
多期树:从到期向今天倒推
Cox、Ross 和 Rubinstein 的 1979 年论文 Option Pricing: A Simplified Approach发表于 Journal of Financial Economics 7(3),229–263 页;可读原文第三节把复制推广到多期,第六节处理分红与看跌期权。
在固定 的树中,先涨后跌和先跌后涨到达同一股价,因为 。这叫可重组树。第 步、累计上涨 次的股价为:
先在最后一层填入到期支付,再一层层向前算。欧式看跌期权满足:
这个过程称为向后递推。每个节点都复制下一步的两种期权价值;走到下一节点后,重新调整股票与债券数量。新组合由旧组合的价值支付,无须追加资金,这就是自融资策略。一次买入后不再调整,通常只能复制一期期权。
美式看跌期权:每个节点都比较是否行权
美式期权在每个可行权节点要比较继续持有价值 和立即行权价值 :
最后一层仍是到期支付。有限树只在树的日期检查行权,用这些日期近似美式期权的连续行权机会。同一棵树上,美式价格至少等于欧式价格,因为持有人可以选择一直等到到期。
另取一个两步例子:、 年、连续复利年利率 、年化波动率参数 。每步为半年,按 CRR 的取法:
这里 ,得到 、、、。本例的 5% 是连续复利年利率,与前例的一期简单利率不同。到期股价与看跌支付为:
半年时的下跌节点,股价为 86.812345。继续持有值为:
立即行权却能得到 ,因此美式持有人应在这个节点行权。上涨节点的支付和继续持有值均为 0。回到今天:
今天立即行权值为 0,所以美式持有人仍应等待。提前行权权利带来的价差约为 1.074211。正利率下,提早收到行权款的利息价值可能超过继续保留期权的价值;这正是看跌期权即使没有分红也可能提前行权的原因。
假设与 Black–Scholes–Merton 极限
以上树使用固定的利率和上涨、下跌因子;CRR 取法用固定波动率参数决定每步的两个因子。还假定股票不派息、交易没有成本、借贷利率相同、可以卖空和持有分数股,并能在每个节点准确再平衡。改变利率、波动率、分红、融资或交易约束,都可能改变价格和最优行权位置。
是模型输入,不是无套利原则推导出的股价预测。选定它们后,仍须检查 。CRR 的 决定对数价格每步的跳动幅度;有限步数下,它不必精确等于一年终端对数收益的标准差。
原论文第五节给出 Black–Scholes 极限。固定 、常数 与 ,让 ,每次都按上面的公式重新计算 ,并采用风险中性概率。对不派息股票的欧式看涨、看跌期权,在这些无摩擦条件下,树价格收敛到 Black–Scholes–Merton 价格。风险中性权重下,各步跳动相互独立,对数跳动幅度随 缩小;中心极限定理于是给出正态的终端对数收益,其极限均值为 ,方差为 。终端股价因此具有 BSM 所用的对数正态极限分布。仅让均值和方差一致,还不足以确定这个分布极限。
只增加层数却保持原来的 不变,会改变整个期限的风险,不能据此得到这个极限。增加步数也不会修复模型遗漏的跳跃、变动波动率或交易成本。美式看跌树逼近的是相应模型中的最优停止价值,不能用欧式 BSM 公式代替提前行权判断。
用 Python 在同一棵树上比较两种看跌期权
把下列代码保存为 binomial_example.py,运行 python3 binomial_example.py。它只使用 Python 标准库。数值输入先转换为浮点数,且须有限;股价、行权价、期限和波动率参数须为正,步数须为正整数,树须满足无套利不等式。增长因子和所有股价节点都须能用有限的正浮点数表示,算出的概率须严格介于 0 和 1 之间,期权价值也须保持有限。输入无效或树超出这个数值范围时,代码抛出 ValueError;Python 数学函数的溢出也会转换为 ValueError。本例不处理零波动率的退化树。
j 表示累计上涨次数,所以 values[j] 是下跌子节点,values[j+1] 是上涨子节点。两种期权共用股价、行权价、利率、波动率、期限和步数;american=True 只增加逐节点的行权比较。两步运行打印美式节点记录,随后比较更细的树。
from math import exp, isfinite, log, sqrt
def price_put(s0, strike, years, rate, sigma, steps, american=False, trace=False):
try:
s0, strike, years, rate, sigma = map(float, (s0, strike, years, rate, sigma))
except (TypeError, ValueError, OverflowError) as error:
raise ValueError("numeric inputs must be representable as floats") from error
inputs = (s0, strike, years, rate, sigma)
if not all(isfinite(x) for x in inputs):
raise ValueError("inputs must be finite")
if min(s0, strike, years, sigma) <= 0:
raise ValueError("s0, strike, years, sigma must be positive")
if type(steps) is not int or steps < 1:
raise ValueError("steps must be a positive integer")
log_s0 = log(s0)
try:
dt = years / steps
move = sigma * sqrt(dt)
u, d = exp(move), exp(-move)
growth = exp(rate * dt)
lowest = exp(log_s0 - steps * move)
highest = exp(log_s0 + steps * move)
except OverflowError as error:
raise ValueError("tree exceeds floating-point range") from error
if not all(isfinite(x) and x > 0 for x in (u, d, growth, lowest, highest)):
raise ValueError("tree exceeds floating-point range")
if not d < growth < u:
raise ValueError("tree must satisfy d < exp(rate * dt) < u")
q = (growth - d) / (u - d)
if not 0 < q < 1:
raise ValueError("risk-neutral probability is outside floating-point range")
def stock_at(i, j):
return s0 if i == 0 else exp(log_s0 + (2*j - i) * move)
values = [max(strike - stock_at(steps, j), 0.0)
for j in range(steps + 1)]
if trace:
print(f"u={u:.6f}; d={d:.6f}; R={growth:.6f}; q={q:.6f}")
print("terminal_puts=" + ", ".join(f"{v:.6f}" for v in values))
for i in range(steps - 1, -1, -1):
previous = []
for j in range(i + 1):
stock = stock_at(i, j)
continuation = ((1-q) * values[j] + q * values[j+1]) / growth
exercise = max(strike - stock, 0.0)
value = max(exercise, continuation) if american else continuation
if not isfinite(value):
raise ValueError("option value exceeds floating-point range")
previous.append(value)
if trace:
decision = "exercise" if american and exercise > continuation else "hold"
print(f"i={i}; j={j}; S={stock:.6f}; "
f"continue={continuation:.6f}; exercise={exercise:.6f}; "
f"value={value:.6f}; decision={decision}")
values = previous
return values[0]
parameters = (100.0, 100.0, 1.0, 0.05, 0.20)
eu = price_put(*parameters, 2)
am = price_put(*parameters, 2, american=True, trace=True)
print(f"n=2; European={eu:.6f}; American={am:.6f}; premium={am-eu:.6f}")
for n in (50, 200, 800):
eu = price_put(*parameters, n)
am = price_put(*parameters, n, american=True)
print(f"n={n}; European={eu:.6f}; American={am:.6f}; premium={am-eu:.6f}")
u=1.151910; d=0.868123; R=1.025315; q=0.553908
terminal_puts=24.636168, 0.000000, 0.000000
i=1; j=0; S=86.812345; continue=10.718647; exercise=13.187655; value=13.187655; decision=exercise
i=1; j=1; S=115.190991; continue=0.000000; exercise=0.000000; value=0.000000; decision=hold
i=0; j=0; S=100.000000; continue=5.737654; exercise=0.000000; value=5.737654; decision=hold
n=2; European=4.663444; American=5.737654; premium=1.074211
n=50; European=5.533634; American=6.073728; premium=0.540094
n=200; European=5.563534; American=6.086383; premium=0.522849
n=800; European=5.571027; American=6.089402; premium=0.518375
两步树便于手算,但价格仍是粗略近似;更细树的这些结果显示,提前行权价差也随离散方式变化。需要估计持有股票的真实收益与风险时,另见收益率、分散投资与组合风险;其他量化主题见量化金融概览。