最初に紹介するExampleとして、“EquityOption”を選びました。Engine」という価格計算のアルゴリズムを格納しているオブジェクトを組み合わせた、金融商品の価格計算ライブラリです(リスク量の計算など、その周辺の機能も含まれます)。その仕組みが最も解りやすい例がこのテストプログラムかと思います。この2つの中心となるオブジェクトは、それぞれ必要な部品を組み立てて作ります。その部品自体もさらに細かなパーツを使って組み立てます。いった様子がこのExampleのソースコードを見ればよく解ると思います。ソースコードをビルドし、実行可能なバイナリーファイルを生成します。QuantLibのインストール方法のセクションで、既にビルドしていれば、再ビルドは必要ありません。ビルドが済んでいない場合は、次の手順で、Debugモードでビルドし、試しに実行してみて下さい。QuantLibのフォルダー下にある"Examples"フォルダーから、"EquityOption"のディレクトリを探し、そこにある"EquityOption.vcxproj"をダブルクリックして下さい。起動され、このプロジェクトファイルが開かれます。Studioの画面中にある"ソリューションエクスプローラー"に表示される"プロジェクトファイル"のディレクトリを展開し、"SourceFiles"のフォルダーにある"EquityOption.cpp"をクリックします。約400行あるソースコードが開かれます。動作するか、まず確認してみましょう。メニューバーからビルド→ソリューションの“ビルド”を選択。ビルドが開始されます。ビルド済みであれば、このステップは必要ありません。エラー無くビルドが正常終了すれば、試しにプログラムを走らせてみます。メニューバーのデバック→デバックの開始を選択。下記のConsole画面が出力されれば成功です。チェックボックスをはずします。)このConsole画面を見れば判る通り、このExampleでは、シンプルな株式オプションを、3種類の行使タイプに分けて、それぞれの価格を、17種類の価格計算エンジンを使って算出しています。>まず、ソースコードの全体像を把握しておきましょう。)メソッドを呼び出して価格計算を行います。このExampleのソースコードも、その流れに沿って書かれています。InstrumentオブジェクトもPricingEngineオブジェクトも、それぞれ主要な部品を組み立てて生成されます。特定する為の様々な情報です。あれば、オプション行使日("Exercise"オブジェクト)とPayoff関数("Payoff"オブジェクト)の情報です。Factorを計算する"TermStructure"オブジェクト(リスクフリー金利のイールドカーブ)です。このExampleでは、具体的な商品として、シンプルな株式のプットオプションを、Instrumentの派生クラスである"VanillaOption"クラスを使って、3種類生成しています。この商品の主要部品である"Exercise"オブジェクトとして、ヨーロピアン、バーミューダン、アメリカンの3種類の部品を用意し、それらを使って3種類のVanillaOptionインスタンスを生成しています。"PricingEngine"を17種類用意しています。これらを組みあわせて、様々な商品に対し、様々な価格計算アルゴリズムを使って、価格計算を行っています。これを念頭に、"EquityOption.cpp"のソースコードの中身を見ていきましょう。最初の例なので、できるだけ丁寧にコードの説明をしたいと思います。プログラミングコードの全体像を見てみましょう。EquityOption.cppは約400行ありますが、最初から132行目までは、"Instrument"と"PricingEngine"の主要部品を生成しています。それらの部品を使って、135行目から137行目で、"Instrument"の派生クラスである"VanillaOption"のインスタンスを3種類生成しています。americanExercise);部品の作成に、相当のステップ数を使いましたが、部品さえできてしまえば、Instrumentオブジェクトの生成は、たったこれだけです。141行目以降は、様々な"PricingEngine"を生成して、それを使って3種類の"VanillaOption"の価格計算を行っています。具体的には、VanillaOptionインスタンスにPricingEngineインスタンスを設定し、NPV()メソッドを呼び出して価格計算を行い、結果をConsole画面に出力しています。>全体像を把握した上で、ステップ毎にソースコードを見ていきましょう。まで、try{...}で囲んでいます。すると、{...}内で何らかのエラーが発生すれば、例外処理に飛ぶようになっています。ここでは標準テンプレートライブラリーが提供しているエラーメッセージが出力されるようになっています(413と416行目)。QuantLibにも、Errorクラスという、エラーメッセージをオブジェクト化したクラスがありますが、このExampleでは使われていません。Boost::timerの起動59行目でboostライブラリのtimerが起動されています。returnで終了するまでにかかる時間を計測しています。直前でストップウォッチを止め、経過時間を出力します。Console出力画面の最後の行を見ると、すべての計算に33秒かかっています(計算時間はPCの性能や計算時の環境に依存します)。Engineを使った、アメリカンオプションの価格計算にかかったものです。Carlo法を使えば、無理やり価格計算できなくもありません。計算時間は相当かかるので、他に方法が無い場合に限って使うべきでしょう。グローバル変数の設定64から66行目で、今日の日付と、決済日をそれぞれ1998年5月15日と、5月17日に設定しています。古い日付で、おそらくQuantLibの開発プロジェクトが始まった頃に作られたExampleだと推察します。(時価評価基準日)に、’本日’(=1989年5月15日)のデータを設定しています。Settingsクラスは、プロジェクト全体で使われるグローバル変数を格納するオブジェクトです。このクラスは、Singletonクラスの派生クラスで、デザインパターンのひとつSingletonパターンの技法を使って設計されています。このクラスの解説は飛ばします。QuantLib”に詳しいので、そちらをご覧下さい。部品の生成さて、ここから、価格計算に必要な、2つのメインのオブジェクト"Instrument"と"PricingEngine"を生成していきますが、その前に、まずその部品を生成する所から始めます。前後しますが、63行目でCalendarインスタンスをTARGET()を使って生成しています。"Calendar"オブジェクトは、キャッシュフロー日が営業日である事を確認する為に必須であり、InstrumentクラスやPricingEngineクラスの部品となります。Bankが管轄している即時グロス決済の資金決済システムの事です。EU加盟国は多数あり、それぞれの国の休日にあわせて決済システムを止めてしまうと休日だらけになってしいます。そこで、逆にEU加盟国の営業日の最小公倍数的なカレンダーを作って、どこかの国が営業日なら資金決済システムを開けておくようにしています。このTARGETシステムをベースにしたCalendarを"TARGET"と呼んでいます。このExampleでは便宜的にTARGETインスタンスを使っていますが、実際には商品の取引市場や、取引当事者の所在国などを基準に決められます。QuantLibでは40か国程度のCalendarクラスが定義されています。国をまたぐ取引では、複数の国のCalendarが適用になる事が多いので、その為のクラスも用意されています。オプションの条件69から90行目は、オプションの価格計算に必要な入力項目を設定しています。Black-Scholes-Mertonのオプション価格公式で使われている入力項目とほぼ同じです。これらの項目は、オプションという“商品の属性”として取引当事者間で決めたものと、価格計算の際に使われる“市場データ"や"パラメータ”が混在しています。行使価格(strike)やオプション期日(maturity)は商品の属性で、"Instrument(商品オブジェクト)"の部品になります。それ以外は市場データで、"PricingEngine(価格エンジンオブジェクト)"の部品になります。この段階では部品を生成する為の原材料を準備しているだけです。78行目以降は、これらの情報をConsole画面に出力するためのコードです。100行目から112行目までは、オプションの主要部品である、"Exercise"オブジェクトを3種類生成しています。EuropeanExercise、AmericanExercise、BermudanExerciseはそれぞれ"Exercise"クラスの派生クラスで、名前の通り、「行使日」をオブジェクトモデル化したものです。ここでは、Boostライブラリが提供するスマートポインタのひとつshared_ptrを使って、それぞれのインスタンスへのポインターを生成しています。スマートポインタとは、メモリー上にあるポインターが指し示す変数領域の生成・消去を自動的に行ってくれるポインターの事です。市場データ114から127行目は、市場データを格納する部品を生成しています。’Volatility’です。対象資産の市場価格ですが、70行目で作成済のunderlying変数に格納されています。これは部品を生成する為の原材料にすぎません。114行目で、それを使って"SimpleQuote"オブジェクトへのスマートポインタを生成し、それを"Handle"オブジェクトに格納しています。"SimpleQuote"が部品で、それを保持する"Handle"は、部品を"PricingEngine"に繫ぐソケットのような役割りです。"Quote"オブジェクト、"Handle"オブジェクト>QuantLibでは基本的に、市場データを、"Quote"オブジェクトに格納して使います。"Quote"オブジェクトの派生クラスです。Engineにデータを渡しています。ここで、コードの解析から少しはずれて、なぜこのような設計方法を取っているかについて、簡単に解説します。QuantLibは、巨大な価格計算モデルライブラリですが、それを単独で使うのでは無く、何等かのポジション管理システムやリスク管理システムの中に組み込んで使われる事を想定しています。そのようなシステムは通常、外部の市場データシステム(ロイターやBloombergなど)と接続され、そこから、常に更新される市場データを受け取っています。市場データが動けば金融商品の価格も変わるので、動いた都度、InstrumentオブジェクトあるいはPricingEngineオブジェクトに知らせ、必要があれば価格を再計算する仕組みが必要です。Engineに手入力して再計算するようでは、とても間に合いません。Engineが常にモニターし、必要に応じて価格の再計算を行える仕組みを備えています。それを実現する手段として、コード中にある"SimpleQuote"クラスを使っています。このクラスはObservableクラスの派生クラスで、それに対し、"Instrument"や"PricingEngine"オブジェクトはObserverクラスの派生クラスになっています。"Observable"は、市場データを格納し、データの更新が行われた都度、それを"Observer"へ通知する機能を備えています。"Observer"は、名前の通り"Observable"を常に監視(Observe)しており、データ更新の通知を受取ると、必要に応じて再計算などの動作を起動します。Engineに渡しています。その理由は、複数の’情報源’に対応する為です。Exchange)にも上場されています。SIMEXは大証がクローズした後でもオープンしており、大証クローズ後は、SIMEXのデータを使って価格計算を行いたいと考えるのは自然な事です。Handleクラスは、実質的にはポインターに対するポインターになりますが、機能としては、部品を価格エンジンに取り付けるソケットの役割りを果たします。Handleクラスの保持しているSimpleQuoteオブジェクトを、必要に応じて簡単に、別のSimpleQuoteオブジェクトに付け替える事が出来ます。「簡単に」の意味は、ポインターを付け替える際に必要なメモリーの生成・消去の管理を、自動的に行ってくれるという意味です。要するに、常に動いている市場価格への対応と、複数の情報源への対応のために、このような仕組みが取られています。QuantLib”を参考にして下さい。話が大分それましたので、元に戻します。この段階では部品ではなく原料です。先ほどと同様、これを使って、"YieldTermStructure"オブジェクトを生成します。このオブジェクトもObservableの派生クラスであり、先ほど説明した仕組みを取り入れています。そのポインターを"Handle"オブジェクトに格納して、それをPricingEngineに渡します。回りくどい仕組みを取った理由は先ほどのSimpleQuoteと同じです。’Volatility'も既に、変数volatilityに格納されています。先ほどと同様に、これを使って"BlackVolTermStructure"オブジェクトを生成しています。これもObservableの派生クラスです。そのポインタを"Handle"に格納しています。これで、市場データの部品が揃いました。Process"オブジェクトの部品になります。Engineの部品になります。Payoffさて、128と129行目ですが、"VanillaOption"のもうひとつの重要部品である"Payoff"オブジェクトを生成しています。ここでは、最もシンプルなPayoff関数を提供する"PlainVanillaPayoff"クラスのインスタンスを生成しています。いずれの情報も、すでに'type'と、'strike'という名前のローカル変数に格納されており、これを使って生成されています。Process(確率過程)オブジェクト130から132行目は、確率過程を記述した"BlackScholesMertonProcess"オブジェクトを生成しています。このクラスは、"StochasticProcess"クラスの派生クラスです。“基礎編”のオプションの所で、オプションの価格は期待値であり、対象証券の価格の確率過程がわかれば、どんなオプションであっても、かならず価格が求まると述べました。Engineにとって、必須の部品になります。参考のために、QuantLibのリファレンスマニュアルから抜粋したこのクラスの派生関係のチャートを添付します。ここで使われている確率過程クラス以外にも、様々なクラスが用意されている事が分かります。Engineの直接の部品になります。そのコンストラクターに、先ほど作った市場データの部品を渡して、具体的なインスタンスを生成しています。Engine以外は、すべてここで生成されたBlackScholesMertonProcessを使っています。このクラスは、具体的には、リスクフリー金利から配当利回りを引いた割合相当のドリフト項を持ち、一定のVolatilityで幾何ブラウン運動をする、株式の確率過程をオブジェクトモデル化したものです。以上で必要な部品の準備は終わりました。これらの部品を使って"Instrument"と"PricingEngine"を組み立てます。オブジェクトの生成QuantLibの中で価格計算の対象となっている商品はすべて、"Instrument"ベースクラスの派生クラスです。ここでは、そのひとつ、"VanillaOption"クラスを使って具体的な商品を生成しています。"VanillaOption"オブジェクトの主要部品は、すでに作成されたPayoffとExerciseオブジェクトです。Option"を生成しています。引数として、部品となるPayoffとExerciseのインスタンスを渡しているのがわかります。部品の構築には相当ステップ数をかけていますが、部品が揃ってしまえば、商品オブジェクト生成は簡単です。QuantLibを使いこなそうと思えば、どういった金融商品が、どのような部品で出来ているのか、さらにその部品が、さらにどのようなパーツから出来ているか、を理解する必要があります。他のExampleの説明も参考にしながら、その辺りの感覚をつかんで頂ければと思います。"PricingEngine"オブジェクトの生成と価格計算さて、商品オブジェクトが出来上がったので、次は"PricingEngine"の生成です。あとは、それを商品オブジェクトに設定すれば、価格計算が出来ます。Engineを生成し、それぞれのエンジンを使った計算結果をConsole画面に出力しています。Engineは大きく分けて、解析解を使ったものと、数値解を使ったものに分けられます。Carloシミュレーション法を使ったものに分けられます。Engineで、計算方法は大きく異なりますが、やっている事の本質は、(i)"StochasticProcess"から導出される確率分布をもとに、(ii)"Payoff"関数の期待値を計算し、(iii)その現在価値を求める事です。解析解による価格計算142から150行目は、かの有名なBlack-Scholes-Mertonが導出したヨーロピアンオプションの価格公式を使って価格計算を行っています。この価格式は、Black-Scholes-Merton過程を記述した確率偏微分方程式を解析的に解いていって導出された解析解です。)メソッドの呼び出しの中で同時に行っています。ここで使われているのは"AnalyticEuropeanEngine"です。AnalyticEuropeanEngine(bsmProcess)));部品の準備には、ずいぶん手間がかかりましたが、オブジェクトの組み立てと設定は、たったこれだけです。Engineのコンストラクターに、部品としてStochasticProcessオブジェクトしか渡されていません。Factorを計算する部品、すなわちリスクフリー金利のイールドカーブは必要ないのでしょうか?当然必要です。Processオブジェクトだけを引数で取るタイプと、それに加えてTermStructure(イールドカーブ)オブジェクトも引数として取り込むものと、2種類用意されています。Factorを計算する目的にも使っています。Processオブジェクトを生成する際、その部品の中にリスクフリー金利のTermStructureインスタンスが含まれていた事を思い出して下さい。Factorを計算する為のイールドカーブが異なる場合に使います。これで価格計算の準備はすべて終了です。あとはInstrumentオブジェクトが持つNPV()メソッドを呼び出せば、価格計算が実行され結果が返ってきます。147行目にあるeuropeanOption.NPV()という記述がそれです。Engineの生成と計算結果の出力です。Modelとは、Black-Scholesモデルでは定数として取り扱われていた拡散項の係数(すなわちvolatility)を、それ自体も確率変動するものと仮定した確率過程のモデルです。Volatilityモデルと呼んでいます。このモデルは、実際の市場で観測される分布の歪みを取り込む為に仮定されたものです。Processオブジェクトをまず作ります。そのオブジェクトの部品は既に作られているので、それらを使ってコンストラクターを呼び出すだけです。Engineを組み立てています。ここでは、モデルの詳細や計算アルゴリズムには立ち入りませんが、計算結果はConsole画面に出力された通り、Black-Scholesモデルによる解析解とほぼ一致しています。ModelのパラメータをCalibrationした結果です。Engineの生成と計算結果の出力です。Diffusionの項を加えたものです。Diffusionとは、確率変数が連続的に変化するだけではなく、ときおりジャンプを起こすももと想定した確率過程です。ここでもモデルの詳細には、立ち入りませんが、計算結果はBlack-Scholesモデルのそれとほぼ一致しています。(これも、一致するようにCalibrationした結果です。アメリカンオプション価格を解析的近似解で求める方法187から206行目までは、アメリカンオプションの解析的近似解を導出した2つの価格モデルを使ったものです。Engineのアルゴリズムの詳細には立ち入りませんが、通常、アメリカンオプションの価格計算は2項・3項Treeを使った数値解か、有限差分法を使った数値解を使うのが、一般的です。解析的に価格が求まれば、計算時間は大幅に短縮できます。Engineで計算した値と、若干ずれが発生しています。実務で使うのであれば、よりポピュラーな数値解の方法を使った方がいいと思います。数値積分によるオプションの価格計算209から217行目は数値積分法を使った数値解による価格計算を行っています。Engineを生成し、NPV()を呼び出して価格計算を行っています。価格モデルのアルゴリズムの詳細には立ち入りませんが、計算結果は、Black-Scholesモデルの解析解とほぼ一致しています。いう期待値計算の式を、まさにその通りに計算しており、最も理解しやすいオプション価格計算方法かと思います。Method)を使った価格計算です。有限差分法は、2項Treeやモンテカルロシミュレーションと比べると、アルゴリズムがかなり複雑で、プログラムコードを理解するには、数値解析や行列演算に関する一定レベルの知識が必要です。計算の収束速度や、Greeksの計算、複雑なデリバティブズへの対応力などは、他の数値計算と比べると一段秀でていると思います。Engineを3種類用意し、価格計算を行っています。有限差分法によるオプション価格の計算アルゴリズムについては、それだけで一冊の本ができるくらいで、とてもここでは説明しきれません。参考までに、下記文献を紹介しておきます。Engineを使ってオプション価格を計算しています。金融工学の書籍等で一番みかけるのは、2番目のCox-Ross-Rubinsteinの2項Treeモデルではないかと思います。これらのモデルの違いについては、下記インタネットサイトに簡潔な解説があります。各モデルの計算結果をみればわかる通り、いずれもBlack-Scholesの解析解にほぼ一致しています。Black-Scholesの価格公式はヨーロピアンオプションについてしか使えませんが、ここでテストされている2項Treeモデルはすべて、バーミューダオプションやアメリカンオプションにも対応できます。2項モデルは、一般的に数値解を求める手法の中では計算時間が早い方で、かつエキゾチックオプションへの対応範囲も広いのが特徴です。実務で使うにはTreeのメッシュを相当細かくする必要があり、それに伴い、計算時間も長くなります。Engineを生成しています。(Crude)とあるように、最もシンプルなモンテカルロシミュレーション法を使ったものです。具体的には、コンピューターのアルゴリズムを使ってPsuedo-Random(疑似乱数)を発生させ、それを使って対象証券価格の拡散過程をシミュレーションし、最後に期待値計算をします。簡単に言いましたが、そのステップは何段階にも分かれており、相当複雑です。Exampleのコードからは、裏で走っているアルゴリズムが分からないので、それも含め少し詳しく説明します。mcSeed=42と設定しています。Stepが1の意味は、確率過程の中途を飛ばし、現時点から、一気にオプション期日の株価をシミュレーションするものです。ヨーロピアンオプションであれば、途中の価格過程の情報は必要ないからです。mcSeedは、乱数生成アルゴリズムに渡すシード数です。352行目で、MCEuropeanEngine<、PseudoRandam&gtというモンテカルロシミュレーション価格エンジンを生成しています。Engineを生成しています。.withXXX(...)という形で必要な部品を追加していますが、この手法はFactoryパターンと呼ば出るデザインパターンの手法です。Engineを、商品にセットしています。360行目で、この商品のNPV()メソッドを呼び出して価格計算を行っています。このNPV()を呼び出した瞬間に、以下のような、長いアルゴリズムが起動します。正規乱数に、ドリフトと標準偏差を作用させ、オプション期日における対象資産価格のサンプルを必要数作るオプション行使日のPayofff関数を使って、各サンプルにおけるオプション行使価値を計算それを合算して、Path数で割る。Factorをかけ、現在価値に換算するコンピューターのアルゴリズムにより生成された乱数は、疑似乱数と呼ばれるくらいなので、完璧な乱数ではありません。何をもって完璧な乱数と言うのかは、数学者の方にお任せします。詳しく分析すると、分布にどうしても何等かの偏りが出てしまうようです。分布の偏り以外にも、コンピューターのアルゴリズムからくる制約があります。表現できる数字の数が、ビット数に依存します。アルゴリズムを使って乱数を生成するという性格上、同じシード数からスタートすると、同じ乱数列が生成されます。質の悪いアルゴリズムだと、その数列の途中に、すでに発生した乱数と同じ数が登場した場合、そこから循環して同じ乱数の列を生成してしまいます。実際に使われているアルゴリズムでは、この循環をうまく避けるよう工夫されています。Twisterと呼ばれるアルゴリズムが使われています。このアルゴリズムは、日本人の数学者の方が開発されました。Sequence(低食い違い数列、あるいは超一様分布数列と訳されているようです)を使ったものです。Sobol列は、高次のモーメントが、純粋な乱数(数学者の方に怒られそうな表現ですみません)と一致するように、アルゴリズムを使って生成された乱数列です。ばらつき度合いの質が、疑似乱数より高い点と、数列を生成する為の計算時間が疑似乱数より短いという利点を持っています。一方で、生成される数列の数が、一定単位まとまらないとモーメントが純粋の乱数とマッチしないので、シミュレーションに使う乱数の数を、事前に決めておく必要があります。サンプル数を最初に指定しているのは、その為です。NPV()を走らせた後に起動するアルゴリズムは、一様乱数の生成の部分がSobol列になっている以外は、MC(Crude)と同じです。Schwarts)は、モンテカルロシミュレーション法を使って、アメリカンオプションの価格計算を行っています。Engineの計算アルゴリズムは、Longstaff-Schwartzにより紹介された、最小2乗モンテカルロ法です。そのアルゴリズムの解説は省略しますが、計算時間が相当かかるので、他に方法が無い場合にのみ使うべきでしょう。Engineオブジェクトを使ったオプション価格計算法を紹介しました。繰り返しますが、QuantLibはデリバティブズの価格計算の為のライブラリーです。Engineオブジェクトを生成それらを結合させて、NPV()というメソッドを呼び出し、価格計算を実行し、結果を返すというものです。この2つのオブジェクトを構成する様々な部品もまた、オブジェクト化されクラスライブラリーとして用意されています。Engineを組み立てる事ができます。それらの部品の組み立て方をマスターすれば、QuantLibを使いこなせるようになったと言えるでしょう。解ったように言っていますが、膨大なライブラリなので、私自身も使い方をすべてマスターしている訳ではなく、勉強中です。<ライセンス表示>QuantLibのソースコードを使う場合は、ライセンス表示とDisclaimerの表示が義務付けられているので、添付します。