Pythonでモンテカルロ法、コンパウンド・オプションの価格計算方法

本記事の目的と対象

本記事の目的は、コンパウンド・オプションの価格を、Pythonを用いたモンテカルロ法により計算する方法を提供することである。

対象者は以下の条件を満たす読者である。
  • Pythonを使う環境が整っている人
  • コンパウンド・オプションとは何かを理解している人
  • Pythonを用いてコンパウンド・オプションのモンテカルロ・シミュレーションを実施したい人
また、以下の記事の内容を前提とする。

なお、本記事の内容は下記書籍の内容を参考にしているため、合わせて参照してほしい。

目次

コンパウンド・オプションのモンテカルロ法評価のアルゴリズム

オプションのオプションたるコンパウンド・オプションをモンテカルロ法により評価する際のアルゴリズムは、以下のようになる。
  1. 乱数\( \epsilon_1\)を発生させ、第一権利行使日\( T_1\)における株価\( S_{T_1}\)を、以下の式で求める。 \begin{eqnarray*} S_{T_1}=S_0e^{(r-q-\frac{1}{2}\sigma^2)T_1+\sigma \epsilon_1\sqrt{T_1}} \end{eqnarray*}
  2. 第一権利行使日\( T_1\)における原オプション価格\( C_{T_1}\)を、モンテカルロ法により求める。 \begin{eqnarray*} C_{T_1}\approx\frac{1}{N}e^{-r(T_2-T_1)}\sum_{j=1}^{N}Max(S_{T_2,j}-K,0) \end{eqnarray*}
  3. 原オプション価格\( C_{T_1}\)と第一権利行使価格\( K_1\)を比較し、\( C_{T_1}>K_1\)であれば権利行使とする。

  4. 権利行使された場合、株価\( S_{T_1}\)を初期株価として、乱数\( \epsilon_2\)を発生させ、第二権利行使日\( T_2\)における株価\( S_{T_2}\)を、以下の式で求める。 \begin{eqnarray*} S_{T_2}=S_{T_1}e^{(r-q-\frac{1}{2}\sigma^2)(T_2-T_1)+\sigma \epsilon_2\sqrt{T_2-T_1}} \end{eqnarray*}
  5. \( S_{T_2}\)と第二権利行使価格\( K_2\)の差と\( 0\)の大きな方を、コンパウンド・オプションの第二権利行使日\( T_2\)におけるペイオフとする。
    またここで、すでに支払った第一権利行使価格の\( T_2\)における価値\( K_1e^{r(T_2-T_1)}\)をペイオフから引く。

  6. シミュレーションにより発生させたコンパウンド・オプションのペイオフを全て足し合わせ、乱数\( \epsilon_1\)の発生個数×乱数\( \epsilon_2\)の発生個数で割り、現在価値に割り引く。

コンパウンド・オプションのモンテカルロ法評価のPythonコード例

まず必要なモジュールと関数をインポートする。

正規乱数を使うためのモジュールrandom、配列を扱うためのモジュールnumpyと、scipyからいくつかの基本的な関数を読み込む。
import random
import numpy as np
from scipy import exp,log,sqrt

次に株価のモンテカルロ・シミュレーションに必要な、幾何ブラウン運動の関数を定義する。
def gBM(S,sigma,mu,t,z):
    gBM= S*exp((mu- sigma**2/2)*t + sigma * sqrt(t) * z)
    return gBM

次に原オプションをモンテカルロ法により評価するための関数を定義する。

ここでは評価時点の株価\( S_t\)と権利行使価格\( K\)と満期までの期間\( T\)、そしてシミュレーション回数\( n\)を与えるとモンテカルロ法によるコールオプション価格を返す関数として定義する。
def BSMC_Call(S_t,K,T,n):
    S_T=np.zeros(n)             #権利行使日の株価を格納する配列
    Call_T=np.zeros(n)          #各株価に対応するオプション価格の配列
    Sum_Call_T=0                #Σmax(S_T-K,0)を計算するための変数
    for j in range(n):  
        S_T[j]=gBM(S_t,sigma,r-q,T,random.gauss(0,1))
        Call_T[j]= max(S_T[j]- K, 0)
        Sum_Call_T=Sum_Call_T+Call_T[j]
    Expected_Call_Value=Sum_Call_T / n
    Call_Value=exp(-r*T)*Expected_Call_Value #現在価値に割り引く
    return Call_Value

最後にコンパウンド・オプションのモンテカルロ法による評価を関数として定義する。

評価時点の株価\( S_0\)、第一・第二権利行使価格\( K_1,~K_2\)、第一・第二権利行使日\( T_1,~T_2\)を指定する。

また、第一権利行使日における株価のシミュレーション回数であるSampleT_1と、その時点において原オプションの価格を求めるためのシミュレーション回数SapmleOP、そして第二権利行使日における株価のシミュレーション回数SampleT_2を指定する。

これらの関数としてモンテカルロ法によるコール・オン・コール・コンパウンド・オプション価格を返す関数として定義する。
def CoC_MC(S0,K1,K2,T1,T2,SampleT1,SampleOP,SampleT2):
    S_T1=np.zeros(SampleT1)
    S_T2=np.zeros(SampleT1*SampleT2)
    Call_T1=np.zeros(SampleT1)
    Call_T2=np.zeros(SampleT1*SampleT2)
    Sum_Call=0
    for n in range(SampleT1):      
        S_T1[n]=gBM(S0,sigma,r-q,T1,random.gauss(0,1))
        Call_T1[n]=BSMC_Call(S_T1[n],K2,T2-T1,SampleOP)
        if Call_T1[n]>K1:
            for m in range(SampleT2):
                S_T2[n*SampleT1+m]=gBM(S_T1[n],sigma,r-q,T2-T1,random.gauss(0,1))
                Call_T2[n*SampleT1+m]=max(S_T2[n*SampleT1+m]-K2,0)
                Sum_Call=Sum_Call+Call_T2[n*SampleT1+m]-exp(r*(T2-T1))*K1
    Expected_CoC_Value=Sum_Call / SampleT1/SampleT2
    CoC_Value=exp(-r*T2)*Expected_CoC_Value 
    return CoC_Value

計算結果

以下のインプットのもと、コンパウンド・オプションの価格を計算する。
S0=100
sigma=30/100
r=5/100
q=0/100
T1=1
T2=2
K1=20
K2=100
SampleT1=100
SampleT2=100
SampleOP=100

結果は以下の通り。
>>> CoC_MC(S0,K1,K2,T1,T2,SampleT1,SampleOP,SampleT2)
8.4606646672982588
>>> CoC_AN_MVN(S0,T1,T2,K1,K2)
9.1383287801281021
誤差にして約7%の乖離が見られるが、これはモンテカルロ法による計算に一定のブレがあるためである。

解析解との乖離はモンテカルロ法の試行回数を増やせば小さくなっていく。

まとめ

本記事ではコンパウンド・オプションの価格をモンテカルロ・シミュレーションにより求める方法を提示した。

オプションのオプションであるコンパウンド・オプションをモンテカルロ法で評価しようとした場合、第一権利行使価格の株価、原オプションの価格、第二権利行使価格の株価という3つの資産価格をモンテカルロ・シミュレーションしなくてはならないため、アルゴリズムが複雑になり計算に時間がかかる。

参考文献


スポンサードリンク