IEOR E4706: Real Options

Summary

实物期权的期权性带来的价值。

区分 non-financial uncertainty 和 financial uncertainty 并对应使用真实概率和风险中性概率。

资产定价为何不应使用 deterministic discounting factor 而应使用 stochastic discounting factor。定价 pricing kernel。Remind me of kernel methods and monte carlo...

\[p = E[mx]\]

一些二叉树,包括汇率的二叉树;状态价格的回顾。

Example 1: Luenberger's Simplico Gold Mine

1
import numpy as np

Gold Price Lattice Given

1
2
3
4
5
6
7
8
9
10
# Gold Price Lattice

T=10
goldPriceLattice = np.ones((T+1, T+1)) * np.nan
goldPriceLattice[0,0] = 400

for t in range (1, T+1):
goldPriceLattice[t, 0] = goldPriceLattice[t-1, 0] * 0.9
goldPriceLattice[t, 1: t+1] = goldPriceLattice[t-1, :t] * 1.2
goldPriceLattice.round(1)
array([[ 400. ,    nan,    nan,    nan,    nan,    nan,    nan,    nan,
           nan,    nan,    nan],
       [ 360. ,  480. ,    nan,    nan,    nan,    nan,    nan,    nan,
           nan,    nan,    nan],
       [ 324. ,  432. ,  576. ,    nan,    nan,    nan,    nan,    nan,
           nan,    nan,    nan],
       [ 291.6,  388.8,  518.4,  691.2,    nan,    nan,    nan,    nan,
           nan,    nan,    nan],
       [ 262.4,  349.9,  466.6,  622.1,  829.4,    nan,    nan,    nan,
           nan,    nan,    nan],
       [ 236.2,  314.9,  419.9,  559.9,  746.5,  995.3,    nan,    nan,
           nan,    nan,    nan],
       [ 212.6,  283.4,  377.9,  503.9,  671.8,  895.8, 1194.4,    nan,
           nan,    nan,    nan],
       [ 191.3,  255.1,  340.1,  453.5,  604.7,  806.2, 1075. , 1433.3,
           nan,    nan,    nan],
       [ 172.2,  229.6,  306.1,  408.1,  544.2,  725.6,  967.5, 1289.9,
        1719.9,    nan,    nan],
       [ 155. ,  206.6,  275.5,  367.3,  489.8,  653. ,  870.7, 1161. ,
        1547.9, 2063.9,    nan],
       [ 139.5,  186. ,  247.9,  330.6,  440.8,  587.7,  783.6, 1044.9,
        1393.1, 1857.5, 2476.7]])

Real Option Lattice (baseline)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
C=200
amount = 10000
r = 0.1
q = 2/3

realOptionLattice = np.ones_like(goldPriceLattice)*np.nan

# t=10, the lease expires and is worthless
realOptionLattice[-1] = 0

for t in range(T-1, -1, -1):
realOptionLattice[t, :t+1] = np.clip(goldPriceLattice[t, :t+1] - C, 0, None) * amount / (1+r) + \
(realOptionLattice[t+1, 1:t+2] * q + realOptionLattice[t+1, :t+1] * (1-q) ) / (1+r)

(realOptionLattice / 1e6).round(1)
array([[24.1,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
       [17.9, 27.8,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
       [12.9, 20.7, 31.2,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
       [ 8.8, 15. , 23.3, 34.2,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
       [ 5.6, 10.4, 16.7, 25.2, 36.5,  nan,  nan,  nan,  nan,  nan,  nan],
       [ 3.2,  6.7, 11.5, 17.9, 26.4, 37.7,  nan,  nan,  nan,  nan,  nan],
       [ 1.4,  4. ,  7.4, 12. , 18.1, 26.2, 37.1,  nan,  nan,  nan,  nan],
       [ 0.4,  2. ,  4.3,  7.4, 11.5, 17. , 24.3, 34.1,  nan,  nan,  nan],
       [ 0. ,  0.7,  2.1,  3.9,  6.4,  9.7, 14.1, 20. , 27.8,  nan,  nan],
       [ 0. ,  0.1,  0.7,  1.5,  2.6,  4.1,  6.1,  8.7, 12.3, 16.9,  nan],
       [ 0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ]])

The value of Enhancement

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# By purchasing the new equipment at the cost of $4m, yearly extraction becomes 12500 and extraction cost increases to $240
C_=240
amount_ = 12500

r = 0.1
q = 2/3

realOptionLattice_ = np.ones_like(goldPriceLattice)*np.nan

# t=10, the lease expires and is worthless
realOptionLattice_[-1] = 0

for t in range(T-1, -1, -1):
realOptionLattice_[t, :t+1] = np.clip(goldPriceLattice[t, :t+1] - C_, 0, None) * amount_ / (1+r) + \
(realOptionLattice_[t+1, 1:t+2] * q + realOptionLattice_[t+1, :t+1] * (1-q) ) / (1+r)

(realOptionLattice_ / 1e6).round(1)

# 27 - 4 = 23 < 24.1? But the enhancement is still of value because of optionality
array([[27. ,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
       [19.5, 31.8,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
       [13.5, 23.3, 36.4,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
       [ 8.6, 16.3, 26.6, 40.4,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
       [ 4.9, 10.8, 18.7, 29.3, 43.5,  nan,  nan,  nan,  nan,  nan,  nan],
       [ 2.3,  6.5, 12.5, 20.4, 31. , 45.2,  nan,  nan,  nan,  nan,  nan],
       [ 0.8,  3.4,  7.7, 13.4, 21. , 31.2, 44.8,  nan,  nan,  nan,  nan],
       [ 0.1,  1.3,  4.1,  8. , 13.2, 20. , 29.2, 41.4,  nan,  nan,  nan],
       [ 0. ,  0.2,  1.8,  4.1,  7.2, 11.3, 16.8, 24.1, 33.9,  nan,  nan],
       [ 0. ,  0. ,  0.4,  1.4,  2.8,  4.7,  7.2, 10.5, 14.9, 20.7,  nan],
       [ 0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ]])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
C=200
amount = 10000

r = 0.1
q = 2/3

realOptionLattice = np.ones_like(goldPriceLattice)*np.nan

# t=10, the lease expires and is worthless
realOptionLattice[-1] = 0

for t in range(T-1, -1, -1):
val = np.clip(goldPriceLattice[t, :t+1] - C, 0, None) * amount / (1+r) + \
(realOptionLattice[t+1, 1:t+2] * q + realOptionLattice[t+1, :t+1] * (1-q) ) / (1+r)
val_enhanced = realOptionLattice_[t, :t+1]

realOptionLattice[t, :t+1] = np.maximum(val, val_enhanced-4e6)

(realOptionLattice / 1e6).round(1)

# The value of the enhancement is 24.6 - 24.1
array([[24.6,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
       [18. , 28.6,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
       [12.9, 20.9, 32.6,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
       [ 8.8, 15. , 23.5, 36.4,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
       [ 5.6, 10.4, 16.7, 25.6, 39.5,  nan,  nan,  nan,  nan,  nan,  nan],
       [ 3.2,  6.7, 11.5, 17.9, 27. , 41.2,  nan,  nan,  nan,  nan,  nan],
       [ 1.4,  4. ,  7.4, 12. , 18.1, 27.2, 40.8,  nan,  nan,  nan,  nan],
       [ 0.4,  2. ,  4.3,  7.4, 11.5, 17. , 25.2, 37.4,  nan,  nan,  nan],
       [ 0. ,  0.7,  2.1,  3.9,  6.4,  9.7, 14.1, 20.1, 29.9,  nan,  nan],
       [ 0. ,  0.1,  0.7,  1.5,  2.6,  4.1,  6.1,  8.7, 12.3, 16.9,  nan],
       [ 0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ]])
1
2
# 100 marks where enhancement took place
((realOptionLattice / 1e6).round(1) == (realOptionLattice_ / 1e6 -4).round(1))* 100
array([[  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
       [  0,   0,   0, 100,   0,   0,   0,   0,   0,   0,   0],
       [  0,   0,   0,   0, 100,   0,   0,   0,   0,   0,   0],
       [  0,   0,   0,   0, 100, 100,   0,   0,   0,   0,   0],
       [  0,   0,   0,   0,   0, 100, 100,   0,   0,   0,   0],
       [  0,   0,   0,   0,   0,   0, 100, 100,   0,   0,   0],
       [  0,   0,   0,   0,   0,   0,   0, 100, 100,   0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0]])

Exercise 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
C_=240
amount_ = 14000

r = 0.1
q = 2/3

realOptionLattice_ = np.ones_like(goldPriceLattice)*np.nan
realOptionLattice_[-1] = 0

for t in range(T-1, -1, -1):
realOptionLattice_[t, :t+1] = np.clip(goldPriceLattice[t, :t+1] - C_, 0, None) * amount_ / (1+r) + \
(realOptionLattice_[t+1, 1:t+2] * q + realOptionLattice_[t+1, :t+1] * (1-q) ) / (1+r)

(realOptionLattice_ / 1e6).round(1)
array([[30.3,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
       [21.9, 35.6,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
       [15.1, 26.1, 40.7,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
       [ 9.7, 18.3, 29.8, 45.2,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
       [ 5.5, 12.1, 21. , 32.9, 48.7,  nan,  nan,  nan,  nan,  nan,  nan],
       [ 2.6,  7.3, 14. , 22.9, 34.8, 50.6,  nan,  nan,  nan,  nan,  nan],
       [ 0.9,  3.8,  8.6, 15. , 23.6, 35. , 50.2,  nan,  nan,  nan,  nan],
       [ 0.2,  1.5,  4.6,  9. , 14.7, 22.4, 32.7, 46.4,  nan,  nan,  nan],
       [ 0. ,  0.3,  2. ,  4.6,  8. , 12.6, 18.8, 27. , 37.9,  nan,  nan],
       [ 0. ,  0. ,  0.5,  1.6,  3.2,  5.3,  8. , 11.7, 16.6, 23.2,  nan],
       [ 0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ]])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
C=200
amount = 10000

r = 0.1
q = 2/3

realOptionLattice = np.ones_like(goldPriceLattice)*np.nan

# t=10, the lease expires and is worthless
realOptionLattice[-1] = 0

for t in range(T-1, -1, -1):
# enhancement only available until the beginning of 5th year
val = np.clip(goldPriceLattice[t, :t+1] - C, 0, None) * amount / (1+r) + \
(realOptionLattice[t+1, 1:t+2] * q + realOptionLattice[t+1, :t+1] * (1-q) ) / (1+r)
if t > 4:
val_enhanced = realOptionLattice_[t, :t+1]
realOptionLattice[t, :t+1] = np.maximum(val, val_enhanced-4e6)
else:
realOptionLattice[t, :t+1] = val

(realOptionLattice / 1e6).round(1)

# the value of the enhancement now worth 25.9 - 24.1 = 1.8m
array([[25.9,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
       [18.8, 30.4,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
       [13.1, 22.1, 34.9,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
       [ 8.8, 15.4, 25.3, 39.2,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
       [ 5.6, 10.4, 17.4, 28.2, 43.3,  nan,  nan,  nan,  nan,  nan,  nan],
       [ 3.2,  6.7, 11.5, 18.9, 30.8, 46.6,  nan,  nan,  nan,  nan,  nan],
       [ 1.4,  4. ,  7.4, 12. , 19.6, 31. , 46.2,  nan,  nan,  nan,  nan],
       [ 0.4,  2. ,  4.3,  7.4, 11.5, 18.4, 28.7, 42.4,  nan,  nan,  nan],
       [ 0. ,  0.7,  2.1,  3.9,  6.4,  9.7, 14.8, 23. , 33.9,  nan,  nan],
       [ 0. ,  0.1,  0.7,  1.5,  2.6,  4.1,  6.1,  8.7, 12.6, 19.2,  nan],
       [ 0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ]])

Example 2

汇率的二叉树

推导汇率二叉树的方法在于假设外币 euro 为我们所投资的一个付息的资产。

numeraire security 是美元,投资的证券是欧元,其价值 \(X_i\) 个美元,息率 \(r_f\)

\[ X_i = E_i^Q \left[\frac{X_{i+1} + r_f X_i}{1+r_d} \right] \]

\[ X_i = E_i^Q \left[\frac{X_{i+1}}{1+r_d-r_f} \right] \]

\[1 € = X_i \$ \]

\[X_i = €/\$ \]

注意 Notes 此处表述有误。

含息债券价格二叉树的风险中性测度需要去息,

\[ q = \frac{(1+r_d - r_f) -d}{u-d} \]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Exchange Rate Lattice

T = 5
exchangeRateLattice = np.ones((T+1, T+1)) * np.nan

exchangeRateLattice[0, 0] = 1.2
u=1.05
d=1/u
rd = 0.05 / 12
rf = 0.1 / 12
qu = (1 + rd - rf -d) / (u-d)
qd = 1-qu

for t in range(1, T+1):
exchangeRateLattice[t, 0] = exchangeRateLattice[t-1, 0] * d
exchangeRateLattice[t, 1:t+1] = exchangeRateLattice[t-1, :t] * u
exchangeRateLattice.round(2)
array([[1.2 ,  nan,  nan,  nan,  nan,  nan],
       [1.14, 1.26,  nan,  nan,  nan,  nan],
       [1.09, 1.2 , 1.32,  nan,  nan,  nan],
       [1.04, 1.14, 1.26, 1.39,  nan,  nan],
       [0.99, 1.09, 1.2 , 1.32, 1.46,  nan],
       [0.94, 1.04, 1.14, 1.26, 1.39, 1.53]])
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
# random payments in euro from t = 1 to 5, with expectation mu

# derive state prices by forward equations

shortRateLattice = np.ones((T+1, T+1))*np.nan
for t in range(T+1):
shortRateLattice[t, :t+1] = rd


# from TermStructureLattice's Note, but modified the risk neutral probability
def state_prices(r: np.array, qu, qd):
T = r.shape[0] - 1
Pe = np.zeros((T+1, T+1))

Pe[0,0] = 1

for k in range(T):
for s in range(1, k+1):
Pe[k+1, s] = Pe[k, s-1]/((1+r[k, s-1])) *qu + Pe[k,s] / ((1+r[k,s])) *qd
Pe[k+1, 0] = Pe[k, 0] / ((1+r[k,0])) * qd
Pe[k+1, k+1] = Pe[k,k] / ((1+r[k,k])) * qu

return Pe

statePriceLattice = state_prices(shortRateLattice, qu, qd)
statePriceLattice.round(3)


array([[1.   , 0.   , 0.   , 0.   , 0.   , 0.   ],
       [0.553, 0.443, 0.   , 0.   , 0.   , 0.   ],
       [0.305, 0.49 , 0.196, 0.   , 0.   , 0.   ],
       [0.169, 0.406, 0.326, 0.087, 0.   , 0.   ],
       [0.093, 0.299, 0.36 , 0.193, 0.039, 0.   ],
       [0.052, 0.207, 0.332, 0.266, 0.107, 0.017]])
1
np.nansum(statePriceLattice * exchangeRateLattice, axis=1)[1:]
array([1.19004149, 1.18016563, 1.17037173, 1.1606591 , 1.15102707])

讲义