本記事の目的と対象
本記事の目的は、コンパウンド・オプションの価格を解析的に表現し、Pythonを用いてコーディングする方法について述べることである。対象者は以下の条件を満たす読者である。
- 基本的なオプションについて理解している人
- Pythonを使う環境が整っている人
- Pythonを使ってコンパウンド・オプションの価格を計算したい人
なお、本記事の内容は下記書籍の内容を参考にしているため、合わせて参照してほしい。
目次
コンパウンド・オプションとはなにか
コンパウンド・オプションとは、オプションのオプション、すなわちオプションを買うもしくは売る権利である。オプションにはコールとプットがあるため、コンパウンド・オプションは、以下の4種類が存在する。
- コールのコール
- コールのプット
- プットのコール
- プットのプット
オプションがヨーロピアンの場合には通常、権利行使価格と権利行使日が設定されるので、オプションのオプションたるコンパウンド・オプションには、二つの権利行使価格と権利行使日がある。
コンパウンド・オプションの保有者は、第一権利行使日\(T_1 \)において、第一権利行使価格\( K_1\)にて、原オプションを購入もしくは売却する権利を有する。
このとき、第一権利行使日\( T_1\)における原オプション価値が、第一権利行使価格\( K_1\)を上回るとき、コンパウンド・オプションは行使される。
さらにこの原オプションは、第二権利行使日\(T_2 \)において、第二権利行使価格\( K_2\)で、原資産を購入もしくは売却する権利である。
以下では、コールオプションを買う権利、つまりコールオプションのコールオプションの価格を解析的に表現し、それを用いてPythonで計算する。
コンパウンド・オプションの解析解
コール・オプションに対するヨーロピアン・コール・オプションの価格は、 \begin{eqnarray*} S_0e^{-qT_2}M(a_1,b_1;\sqrt{T_1/T_2})-K_2e^{-rT_2}M(a_2,b_2;\sqrt{T_1/T_2})-K_1e^{-rT_1}N(a_2) \end{eqnarray*}
で表される[1]。
ただし、\( M(a,b;\rho)\)は相関係数\( \rho\)の二次元正規分布の累積分布関数であり、第一変数が\( a\)以下かつ、第二変数が\( b\)以下のときの値である。
また、
\begin{eqnarray*}
a_1&=&\frac{\ln(S_{0}/S^*)+(r-q+\sigma^2/2)T_1}{\sigma\sqrt{T_1}}\\
a_2&=&a_1-\sigma\sqrt{T_1}\\
b_1&=&\frac{\ln(S_{0}/K_2)+(r-q+\sigma^2/2)T_2}{\sigma\sqrt{T_2}}\\
b_2&=&b_1-\sigma\sqrt{T_2}
\end{eqnarray*}
であり、変数\(S^* \)は\( C_{T_{1}}(S_{T_1},K_2,T_{2}-T_{1})=K_1\)を満たす\( S_{T_1}\)、つまり原オプションが時点\(T_1 \)でAt-The-Moneyとなるような原資産価格である。ただし、\( M(a,b;\rho)\)は相関係数\( \rho\)の二次元正規分布の累積分布関数であり、第一変数が\( a\)以下かつ、第二変数が\( b\)以下のときの値である。
また、
Pythonでのコード例
まず必要なモジュールをインポートする。
scipy.stats.norm
は一次元正規分布の累積密度関数を、scipy.stats.mvn
は二次元の正規分布の累積密度関数を使用するために必要である。また、
scipy
に組み込まれたいくつかの関数と、方程式を解くためのサブモジュールscipy.optimize
をインポートする。さらに、数値の組を扱うための配列を呼び出すため、
numpy
をインポートする。from scipy.stats import norm, mvn from scipy import exp,log,sqrt,optimize,pi,sign import numpy as np
基本的なブラック・ショールズ式の関数を定義する。
これは変数\( S^*\)を計算するために必要である。
def BS_Call(S, sigma ,r,q,T,K): d1 = (log(S / K) + (r -q+ sigma**2 / 2) * T) / (sigma * sqrt(T)) d2 = d1 - sigma * sqrt(T) BS_Call = S * exp(-q * T) * norm.cdf(x=d1, loc=0, scale=1) \ -K * exp(-r * T) * norm.cdf(x=d2, loc=0, scale=1) return BS_Call
変数\( S^*\)を計算するために\( C_{T_{1}}(S_{T_1},K_2,T_{2}-T_{1})=K_1\)を解く必要がある。
左辺を移行し、\( h(S_{sol})=C_{T_{1}}(S_{sol},K_2,T_{2}-T_{1})-K_1=0\)として方程式を解くため、関数を定義する。
def h(S_sol): return BS_Call(S_sol, sigma ,r,q,T2-T1,K2)-K1
二次元正規分布の累積分布関数を定義する。
第一変数が\( a\)、第二変数が\( b\)、相関係数が\( c\)であるときの、
scipy.stats.mvn
モジュールを用いた二次元正規分布の累積分布関数\( M_{MVN}(a,b;c)\)は、以下のようにコーディングすればよい。def M_MVN(a,b,c): mean = np.array([0,0]) Sigma = np.array([[1,c],[c,1]]) lower=np.array([-10000,-10000]) upper=np.array([a,b]) p,i=mvn.mvnun(lower,upper,mean,Sigma) return p
以上を踏まえ、コール・オン・コール・コンパウンド・オプションの価格は、以下のようなプログラムで計算可能である。
def CoC_AN_MVN(S0,T1,T2,K1,K2): S_star=float(optimize.fsolve(h,100)) #配列形式を数値に変換 a1 = (log(S0 / S_star) + (r-q + sigma**2 / 2) * T1) / (sigma * sqrt(T1)) a2 = a1 - sigma * sqrt(T1) b1 = (log(S0 / K2) + (r-q + sigma**2 / 2) * T2) / (sigma * sqrt(T2)) b2 = b1 - sigma * sqrt(T2) CoC_AN = S0 * exp(-q * T2)*M_MVN(a1,b1,sqrt(T1/T2))\ - K2 * exp(-r * T2) *M_MVN(a2,b2,sqrt(T1/T2))\ -K1* exp(-r * T1) *norm.cdf(x=a2, loc=0, scale=1) return CoC_AN
計算結果
以下のインプットで計算する。S0=100 sigma=30/100 r=5/100 q=0/100 T1=1 T2=2 K1=20 K2=100 rho=0.3 SampleT1=100 SampleT2=100 SampleOP=100
計算結果は以下の通り。
>>> CoC_AN_MVN(S0,T1,T2,K1,K2) 9.1383287801281021
検算(『フィナンシャルエンジニアリング』の結果と照合)
『フィナンシャルエンジニアリング』[1]に付属しているソフトウェアDerivaGemを用いると、様々な金融商品を評価することが出来る。コンパウンド・オプションの価格も計算可能である。
本節では前節での計算結果が、DerivaGemの計算結果と一致することを確かめる。
DevivaGemをCD-ROMからインストールすると、3つのExcelマクロファイルが利用できる。そのうち
DG300 functions.xls
を開く。
このブックのうち、Exotic Optionsシートにコンパウンド・オプションの計算フォームが載っている。
ここに前節と同様のデータをインプットすると、
9.138265559という計算結果が得られる。
前節の結果
9.1383287
と小数第3位まで一致している。小数第4位以降で差異が生じているのは、正規分布の累積分布関数の定義が異なっているためであると考えられる。
まとめ
本記事では、コンパウンド・オプションの解析解をPythonで実装する際のコード例を提供し、インプットデータを与え実際に価格の計算を行った。その結果は、著名な金融工学の書籍である『フィナンシャルエンジニアリング』に付属の計算ソフトの結果に一致した。
0 件のコメント :
コメントを投稿