文章

怎样计算亚式期权的 theta?

theta 可能是最难计算的一个期权参数,时间变了,一切都变了。

怎样计算亚式期权的 theta?

采用 Turnbull-Wakeman1 或 Levy2 近似公式计算亚式期权的价格时,都需要一个特定的参数——当前平均价格,也就是观察起始日到估值日这一段时间窗口内价格的平均值。举个例子,观察起始日是 2026-AUG-1,估值日是 2026-AUG-5,当前平均价格就是 2026-AUG-1、2026-AUG-2、2026-AUG-3 和 2026-AUG-4 这四天价格的平均值。

当前平均价格表面上看是一个常量,其实它是一个和时间有关的变量,这使得计算亚式期权的 theta 时需要小心处理这个参数的值。

以 Turnbull-Wakeman 近似公式为例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import numpy as np
from financepy.utils.date import Date
from financepy.utils.global_types import OptionTypes
from financepy.utils.math import normcdf


def TurnbullWakeman(
        t0,
        t_exp,
        tau,
        s,
        k,
        r,
        q,
        v,
        accrued_average,
        opt_type_int
):
    '''
    :param t0: start averaging date to value date
    :param t_exp: value date to expiry date
    :param tau: start averaging date to expiry date
    :param s: spot price
    :param k: strike price
    :param r: risk-free rate
    :param q: dividend yield
    :param v: volatility
    :param accrued_average: accrued average price
    :param opt_type_int: Call(1), Put(2)
    :return: Asian option price
    '''
    multiplier = 1.0

    if t0 < 0:  # we are in the averaging period
        # we adjust the strike to account for the accrued coupon
        k = (k * tau + accrued_average * t0) / t_exp
        # the number of options is rescaled also
        multiplier = t_exp / tau
        # there is no pre-averaging time
        t0 = 0.0

    # need to handle this
    b = r - q
    sigma2 = v ** 2
    a1 = b + sigma2
    a2 = 2 * b + sigma2
    s0 = s

    dt = t_exp - t0

    if b == 0:
        m1 = 1.0
        m2 = 2.0 * np.exp(sigma2 * t_exp) - 2.0 * np.exp(sigma2 * t0) * (
                1.0 + sigma2 * dt
        )
        m2 = m2 / sigma2 / sigma2 / dt / dt
    else:
        m1 = s0 * (np.exp(b * t_exp) - np.exp(b * t0)) / (b * dt)
        m2 = np.exp(a2 * t_exp) / a1 / a2 / dt / dt + (
                np.exp(a2 * t0) / b / dt / dt
        ) * (1.0 / a2 - np.exp(b * dt) / a1)
        m2 = 2.0 * m2 * s0 * s0

    f0 = m1
    sigma2 = (1.0 / t_exp) * np.log(m2 / m1 / m1)
    sigma = np.sqrt(sigma2)

    d1 = (np.log(f0 / k) + sigma2 * t_exp / 2) / sigma / np.sqrt(t_exp)
    d2 = d1 - sigma * np.sqrt(t_exp)

    if opt_type_int == OptionTypes.EUROPEAN_CALL.value:
        call = np.exp(-r * t_exp) * (f0 * normcdf(d1) - k * normcdf(d2))
        v = call
    elif opt_type_int == OptionTypes.EUROPEAN_PUT.value:
        put = np.exp(-r * t_exp) * (k * normcdf(-d2) - f0 * normcdf(-d1))
        v = put
    else:
        return None

    v = v * multiplier

    return v

用差分法计算 theta 时,如果假定“当前平均价格”是常量,将得到一个错误的结果。正确的做法是根据时间差分的数值更新当前平均价格,用更新的参数计算期权价格。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
from financepy.utils.date import Date

s = 100
k = 100
r = 0.05
q = 0.03
v = 0.2
opt_type_int = OptionTypes.EUROPEAN_PUT.value

start_averaging_date = Date(1, 8, 2026)
value_date = Date(5, 8, 2026)
expiry_date = Date(31, 8, 2026)

days_peer_year = 365

t0 = (start_averaging_date - value_date) / days_peer_year
t_exp = (expiry_date - value_date) / days_peer_year
tau = (expiry_date - start_averaging_date) / days_peer_year

accrued_average = (104 + 103 + 102 + 101) / 4.0
# accrued_average = (96 + 97 + 98 + 99) / 4.0

call = TurnbullWakeman(t0, t_exp, tau, s, k, r, q, v, accrued_average, opt_type_int)

print("npv:", call)

ds = 1
call_up = TurnbullWakeman(t0, t_exp, tau, s + ds, k, r, q, v, accrued_average, opt_type_int)
call_dn = TurnbullWakeman(t0, t_exp, tau, s - ds, k, r, q, v, accrued_average, opt_type_int)

delta = (call_up - call_dn) / (2 * ds)
print("delta:", delta)

gamma = (call_up - 2 * call + call_dn) / (ds ** 2)
print("gamma:", gamma)

dt = 1e-3
call_next = TurnbullWakeman(t0 - dt, t_exp - dt, tau, s, k, r, q, v, accrued_average, opt_type_int)

theta_one_day = (call_next - call) / dt * 1 / days_peer_year
print("wrong theta_one_day:", theta_one_day)

accrued_average_next = (-accrued_average * t0 + s * dt) / (-t0 + dt)
call_next = TurnbullWakeman(t0 - dt, t_exp - dt, tau, s, k, r, q, v, accrued_average_next, opt_type_int)

theta_one_day = (call_next - call) / dt * 1 / days_peer_year
print("reasonable theta_one_day:", theta_one_day)
1
2
3
4
5
npv: 0.8751257730086413
delta: -0.37710904369368664
gamma: 0.10943927075583715
wrong theta_one_day: -0.09505525169875748
reasonable theta_one_day: -0.05819583706393781

由更新当前平均价格的逻辑可以得知,当参数 t0 恰好等于 0 的时候,accrued_average 应该等于 s 才是合理的。

下面验证一下上述更新当前平均价格的逻辑合理的。

首先,模拟出 2026-AUG-6 可能的价格,再计算出这一天亚式期权的价格。通常情况下,用 theta、delta 和 gamma 构造的线性近似可以解释大部分期权价格的变化。下面以期权价格的变化量为因变量,资产价格的变化量为自变量,建立一个二次多项式回归模型。得到的截距和回归参数分别对应 theta、delta 和二分之一 gamma。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
value_date_next = Date(6, 8, 2026)
t0 = (start_averaging_date - value_date_next) / days_peer_year
t_exp = (expiry_date - value_date_next) / days_peer_year
tau = (expiry_date - start_averaging_date) / days_peer_year

dt = 1 / days_peer_year

np.random.seed(42)
s_next = s * np.exp(
    (r - q - 0.5 * v ** 2) * dt
    + v * np.sqrt(dt) * np.random.normal(size=200)
)

accrued_average_next = (accrued_average * 4 + s) / 5
call_next = np.array(
    [TurnbullWakeman(t0, t_exp, tau, price, k, r, q, v, accrued_average_next, opt_type_int) for
     price in s_next]
)

delta_s = s_next - s
delta_s2 = delta_s ** 2
delta_call = call_next - call

import statsmodels.api as sm

X = sm.add_constant(np.column_stack((delta_s, delta_s2)))
model = sm.OLS(delta_call, X).fit()

print(model.summary())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
                            OLS Regression Results                            
==============================================================================
Dep. Variable:                      y   R-squared:                       1.000
Model:                            OLS   Adj. R-squared:                  1.000
Method:                 Least Squares   F-statistic:                 1.589e+06
Date:                Sat, 26 Sep 2026   Prob (F-statistic):               0.00
Time:                        18:07:50   Log-Likelihood:                 892.02
No. Observations:                 200   AIC:                            -1778.
Df Residuals:                     197   BIC:                            -1768.
Df Model:                           2                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const         -0.0566      0.000   -231.298      0.000      -0.057      -0.056
x1            -0.3627      0.000  -1766.535      0.000      -0.363      -0.362
x2             0.0513      0.000    343.595      0.000       0.051       0.052
==============================================================================
Omnibus:                      184.249   Durbin-Watson:                   1.959
Prob(Omnibus):                  0.000   Jarque-Bera (JB):             6122.068
Skew:                          -3.247   Prob(JB):                         0.00
Kurtosis:                      29.315   Cond. No.                         2.34
==============================================================================

可以看出来,x1 和 x2 的系数和差分得到的 delta 和二分之一 gamma 基本一致,截距和一日 theta 基本一致,和错误的 theta 相差甚远。


  1. A Quick Algorithm for Pricing European Average Options ↩︎

  2. Pricing European Average Rate Currency Options ↩︎

本文由作者按照 CC BY 4.0 进行授权