1. QuantLibを使ってみる
1.2 Exampleを試す
1.2.3 MulticurveBootstrapping : Multi-Curveの構築
1.2.3.1 はじめに
前のセクションの繰り返しになりますが、QuantLib を端的に説明すると、
・Financial Instrument というキャッシュフローの情報を格納しているオブジェクトに
・Pricing Engine という価格計算のアルゴリズムを格納しているオブジェクトを組み合わせ、
・様々な金融商品の様々な価格計算方法を提供しているライブラリです。
前のセクションの“EquityOption” で、この2つのオブジェクトを、部品から組み立てて、最後に Financial Instrumentsオブジェクトが持つ NPV( ) メソッドを呼び出して、価格計算を行いました。この Example も、やろうとしている事は基本的に同じです。
各種部品の中で、最も重要なものはイールドカーブです。具体的には YieldTermStructure クラスのオブジェクトになります。イールドカーブは、変動金利キャッシュフローの構築に必要不可欠であり、変動金利キャッシュフローを持つ"Instrument"オブジェクトの部品になります。また、現在価値に割引く為の Discount Factor を提供するものであり、すべての "PricingEngine" に必要不可欠な部品でもあります。前者のイールドカーブを Forecasting Curve または Index Curve と呼び、後者のイールドカーブを Discounting Curve と呼びます。
イールドカーブの構築方法については、基礎編のイールドカーブの構築方法(2):Bootstrapping + Interpolation法で、伝統的なSingle Curve構築のアルゴリズムを紹介しました。また、上級編の”イールドカーブ→Multi-Curve対応→Multi-Curvesの構築”の 所で、Forecasting CurveとDiscounting Curveを別にした場合の構築方法を解説しています。Bootstrapping + Interpolation法のアルゴリズムに馴染みが無い方は、まずそちらの方をご覧下さい。
この Example では、この2種類のイールドカーブを構築します。一方を Instruments オブジェクト(ここでは"VanillaSwap"オブジェクト)を生成する部品として使い、もう一方を Pricing Engineオブジェクト(ここでは"DiscountingSwapEngine"オブジェクト)の部品に使っています。ソースコードは、全体で 900行以上ありますが、その 6割近くを、この 2本のイールドカーブ構築に使っています。イールドカーブ自体も様々な部品(オブジェクト)から出来あがっており、その辺りも含めてコードを解析していきたいと思います。
1.2.3.2 ソースコードのコンパイルと実行
“EquityOption”の時と同様、まずExampleのソースコードをビルドし、実行可能なバイナリーファイルを生成します。QuantLibのインストール方法を解説したセクションですでにビルドしていれば、再ビルドは必要ありません。もし、ビルドが済んでいない場合は、次の手順で、Debugモードでビルドし、試しに実行してみて下さい。
- QuantLib のファイルFolder の Exampleディレクトリから、MulticurveBootstrapping のディレクトリを探し、そこにあるプロジェクトファイル MulticurveBootstrapping.vcxproj をダブルクリックし開ける。Visual Studio が起動され、このプロジェクトファイルが開かれます。
- ソリューションエクスプローラーに表示されているプロジェクトのディレクトリを展開し、SourceFiles のフォルダーにある MulticurveBootstrapping.cpp をクリックする。約 940 行あるソースコードが開かれます。
- きちんと動作するか、まず確認してみましょう。メニューバーからビルド→ソリューションのビルドを選択。ビルドが開始されます。既にビルド済みであれば、このステップは必要ありません。(当初、複数のプロジェクトをまとめているQuantLib.slnをビルドした場合、個別のプロジェクトにおけるmain()関数を走らせる必要があります。その場合は、ソリューションエクスプローラーwindowから、該当のプロジェクト(ここではMulticurveBootstrapping.)をクリックした上で、メニューバーから “プロジェクト” → “スタートアッププロジェクトに設定” を選択して下さい。)
- エラー無くビルドが正常終了すれば、試しにプログラムを走らせてみます。メニューバーのデバック→デバックの開始を選択。下記の Console 画面が出力されれば成功です。(プログラムが終了すると同時に Console 画面も消えてしまうようであれば、ツール→オプション→デバック→全般とし、「デバックの停止時に自動的にコンソールを閉じる」 のチェックボックスをはずします。)
- この Console 画面の出力を見れば判る通り、このプロジェクトでは、2種類の Swap を、OISカーブ と Indexカーブを Discounting Curve として使って現在価値計算を行っています。出力画面にあるeonia discがOISカーブによるDiscountingの例で、euribor discがIndexカーブによるDiscountingの例です。また、5年物 Swap の市場レートを 0.76% と 0.9% の2通りで計算しています。
1.2.3.3 ソースコードの解析
< 全体像 >
まず、ソースコードの全体像を俯瞰してみます。(参考のために、ソースコード(MulticurveBootstrapping.cpp ファイル)を .html ファイルに変換したものを、用意したので、それを見ながら読み進めて下さい。)
ここでは Instrument として、「金利スワップ」オブジェクトを2種類生成しています。また、Pricing Engine は、金利スワップ用の価格計算エンジンを生成しています。それぞれ、様々な部品から出来ており、その部品の組み立てから始まりますが、ソースコードの大半はイールドカーブ構築 ("YieldTermStructure"オブジェクトの生成) に使われています。
イールドカーブは2種類生成しています。1つは、EONIA インデックス (Euro Over-Night Index Average) をベースにした Euro の OISカーブです。もう1つは、Euro の6カ月EURIBOR をベースにしたIndexカーブです。前者は Discounting Curve として、PricingEngine の部品になります。後者は Forecasting Curveとして、Instrumentの部品となりますが、この例では同時に、これを Discounting Curve としてPricing Engineに渡し、価格計算にも使っています。
イールドカーブ構築が終わると、具体的な"Instrument"オブジェクトを組み立てます。この Example で使われている商品は、固定金利と6カ月Euribor を交換する Euro 建ての"VanillaSwap"オブジェクトです。ここでは期間5年のシンプルなスワップを、スポットスタートと1年後のフォワードスタートの2種類作成しています。
商品が生成されると、"PricingEngine" を生成し、その商品に設定します。ここで作成されている "PricingEngine" は、"DiscountingSwapEngine"で、既に作成された Discounting Curve が、その主要部品になります。
以上で価格計算の準備が完了し、あとは、幾通りかのシナリオで、2種類のスワップの価格計算を行い、それを Console 画面に出力するようにしています。
< ステップ毎のコード解析 >
ここでは、プリプロセッサや includeファイルについての説明は省き、main( ) 関数の中身についてだけ解析していきます。
① 例外処理
例外処理については、既に "EquityOption" の Example でも説明しました。この Example でも同様に、main() 開始直後から try{…} で囲んでおり、何らかのエラーが発生すれば、最後の例外処理に飛ぶようにしています。QuantLib では、Error クラスという、エラーメッセージをオブジェクト化したクラスがありますが、この Example でも使われていません。
② boost::timer( )の起動
71行目で boostライブラリの timer を起動して、main( ) 関数の処理時間を計測しています。上記の Console 出力画面の最後に、1分27秒かかったことが示されています。イールドカーブ構築を合計で10数回行っていますが、そこで使われているアルゴリズムでSolverを使っており、相応の時間がかかったと推察されます。
③ 日付およびカレンダーの情報設定
まず、78行目でCalendarクラスのインスタンスとしてTARGETカレンダーを設定しています。TARGETについては、EquityOption の所(ソースコードの解析) で説明済なので、そちらをご覧下さい。
80行目で、本日 = '2012年12月11日' とし、これを 'Evaluation Date' として時価評価の基準日に設定しています。次の行の "Settings"クラスのインスタンスの生成についても EquityOption の所で説明済です。84~87行目で、スワップ取引や EURIBOR の資金取引の一般的な'決済日'を指定しています。市場慣行は本日から2営業日後なので、そのように設定しています。
90~94行目は、'本日' と '決済日'の日付を Console 画面に出力するものです。
④ 部品の生成
この例での主要部品は、"YieldTermStructure"オブジェクトのみです。しかし、その部品の構築に、ソースコードの6割を使っており、むしろ部品の方が本命のようなテストプログラムです。この大きな部品も、当然ながら細かな部品を組み立てて作るので、部品の部品の作り方から、手順を詳しく見ていきます。
その中で、Equity Optionの例の中でも簡単に解説した、Observerパターンについて、もう少し詳しく解説したいと思います。QuantLibでは、"PricingEngine"のように、絶えず変化する市場データに依存するオブジェクトが多数あります。Observerパターンは、そういった関係を制御する為のデザインパターンで、QuantLib の至る所で使われています。
また、イールドカーブを構築するアルゴリズムの中で、非常に重要な役割を果たす、BootstrapHelper クラス(及びその派生クラスである RateHelper クラス)についても、解説します。このオブジェクトは"YieldTermStructure"オブジェクトを構築する為の、最も重要な”部品の部品”になります。
④-1 イールドカーブ構築の概要
いよいよイールドカーブの構築です。2種類のイールドカーブの内、まずOISカーブからです。96~316 行目までの 220行を使って、EONIAインデックスをベースにした OISスワップのイールドカーブを生成しています。ここにあるイールドカーブ (YieldTermStructure クラスのインスタンス)の生成方法の概略を説明すると
- まず、カーブの原データとなるベンチマーク商品の市場データをカプセル化した "SimpleQuote"オブジェクトを生成(96~140行目)
- この "SimpleQuote"を使って、各ベンチマーク商品に対応する "RateHelper"オブジェクトを生成(142~260行目)。"RateHelper"は、BootstrapHelperクラスの派生クラスで、イールドカーブ構築アルゴリズムの屋台骨を提供しますが、それについては後で詳しく説明します。
- 用意された"RateHelper"インスタンスを一本の配列に格納。 277行目で 'eoniaInstruments' という名前の配列を生成していますが、これの事です。その配列に各 RateHelper(BootstrapHelper)インスタンスを代入しています。
- 最後に、その配列と、'日付'や'日数計算方法'などの部品を組み合わせて、具体的なイールドカーブのインスタンスを生成 (310行目)。ここでは、PiecewiseYieldCurve<Discount、Cubic> というテンプレートクラスを使って、具体的なイールドカーブを構築しています。テンプレート引数の Discount は Interpolation の対象 (ここではDiscount Factor) の属性を指定し、Cubic は Interpolation の方法 (ここではCubic Spline) を指定しています。Bootstrapping + Interpolation のアルゴリズムは、このテンプレートクラスに実装されており、ユーザーはテンプレート引数を指定する事と、BootstrapHelper インスタンスの配列を与えるだけで、Bootstrapping + Interpolation 済みのイールドカーブが出来上がります。
この順番に従って、もう少し詳しくみていきます。
④-2 "SimpleQuote"インスタンスの生成
まず、ベンチマーク商品の市場価格・レートを保持する "SimpleQuote"の生成です。SimpleQuoteクラスは、EquityOptionの例でも説明しましたが、Quoteの派生クラスで、QuoteはObservableの派生クラスです。"Quote"クラスは、市場データを保持する機能に特化したオブジェクトです。"Observable"については、この後、詳しく説明します。
104~140 行目にある通り、OISカーブの原データとなるベンチマーク商品として、以下の商品が選択されています。
−O/N、T/N、S/N (それぞれ、Over-Night、Tomorrow-Next、Spot-Nextの略)のインターバンク預金金利
−期間1週間から30年までの OISスワップ金利 (Overnight Index と交換される固定金利レート)
−ECB の準備預金残高メンテナンス期間に対応する先日付スタートの Dated-OISスワップ
Multi-Curve 対応の場合、対応する金利インデックスと同質の対象資産をベンチマーク商品として選択する必要があります。OISインデックスにふさわしい短期金利が何になるかは、通貨によって異なります。通貨によっては、何を選択するか、今でも市場参加者間で議論されています。ここでは、O/N,T/N,/S/N の(無担保の)インターバンク金利が使われていますが、実務の世界では、何を使うかいまだに流動的です。
コード例にある通り、各データを SimpleQuoteインスタンスの Shared pointer に格納しています。Shared Pointer は、ここではBoostライブラリが提供しているものを使っています。その機能は、あるインスタンスに対するポインタを、複数のオブジェクトで共有する場合に、メモリー領域の生成・消去を自動的に行ってくれます。1つの市場データを、様々な商品やイールドカーブが使う事を想定しています。
SimpleQuote は、Quote の派生クラスで、Quote は Observable の派生クラスです。デザインパターンのひとつである、Observerパターンを使って設計されています。Observerパターンについては、EquityOption の例で簡単に説明しましたが、ここでもう一度詳しく説明したいと思います。
(Observer パターンについて)
QuantLibが取り入れているデザインパターンの中では、Observer パターンが最も頻繁に使われています。
金融商品の価格計算は、当然ながら市場データを必要とします。実務では、それを Reuter や Bloomberg、あるいは証券取引所などの、市場データの提供会社から電子データとして取得します。市場データは、絶えず変動しているので、それによってデリバティブズ商品の価格も変動します。QuantLibの中心機能は、金融商品オブジェクトに、価格エンジンオブジェクトを組み合わせて、価格計算を行うものです。いずれのオブジェクトも市場データに依存するので、市場データを監視するオブジェクト (Observer) になります。すなわち、Instrument も PricingEngine も Observerクラスの派生クラスです。Observer は、監視される側 (Observable)となる市場データオブジェクトのポインタをメンバー変数として保持しています。
一方、市場データを取り込む Quoteクラスは、監視される側 (Observableクラス) になります。この Observableは、自分(市場データ)に依存している (すなわち価格の動きを監視している) すべての Observerを、登録(register) しています。具体的には、自分を監視している Observer インスタンスのポインタ(複数ある事が想定されるので、その配列)をメンバー変数として保持しています。
そして、Quote側(Observable側)でデータの更新があった場合、Observableは、登録しているすべてのObserverに対し、その事を通知します(notify)。通知を受け取ったObserver側は、データ変更のフラッグを立て、価格の再計算に備えます(直ちに価格の再計算をする訳ではありません)。
この関係を図示すると、次の様になります。
これが、Observer と Observable の関係のすべての仕組みではありませんが、Observerパターンの最も基本的な役割です。
このExampleでは、SimpleQuoteからRateHelper,YieldTermStructureと部品を組み立てて行き、最終的にInstrument と PricingEngineを構築して行きます。その中で、Observer と Observable の関係が、多重的に繋がって行きます。上の図のように、SimpleQuoteとInstrumentが直接ObservableとObserverの関係になる訳ではありません。
市場データをカプセル化したSimpleQuoteオブジェクトは、まず RateHelperオブジェクトの中に取り込まれます。そこで、RateHelper が Observer で SimpleQuote が Observable になります。次に、RateHelperは、YieldTermStructureクラスのオブジェクトの部品になります。今度は、RateHelper が Observable の役割になり、YieldTermStructureオブジェクトが Observer となります。最終的にYieldTermStructureオブジェクトが、Instrument と PricingEngine の部品としてそれぞれのオブジェクトに取り込まれますが、その結果 YieldTermStructure が Observable となり、Instrument と PricingEngine が Observer となります。図示すると下記のような関係です。
Instrument と PricingEngine も Observable の役割を持っており、この商品や価格エンジンのデータに依存する他の Observer オブジェクトから監視される側になる事も想定されています。
④-3 "RateHelper"オブジェクトの生成
142~260 行目までで、"SimpleQuote"を使って、"RateHelper"オブジェクトを組み立てています。RateHelper は、BootstrapHelper
< BootstrapHelperクラス について >
RateHelperのベースクラスであるBootstrapHelperクラスの役割は2つあります。
ひとつ目は、ベンチマーク商品の市場データの提供と、その商品のキャッシュフロー日を使ってカーブの Pillar (特定の時点)を特定する事です。イールドカーブを、Bootstrapping法を使って構築するには、まず原データとして「ベンチマーク商品の市場データ」が必要になります。LIBOR-Indexカーブであれば、インターバンク預金、FRA、預金先物や Swap金利などが該当します。それぞれの商品毎に作られる "BootstrapHelper"は、商品の属性を引数としたコンストラクターによって生成され、そこから Pillar となる日付が導出できます。また、その商品の市場データを、上記の "SimpleQuote"に対する "Handle" として取り込みます。
二つめの役割は、与えられたイールドカーブを使って、ベンチマーク商品の市場価格を計算する事です。この市場価格計算機能を使って、Bootstrapping の際に、Solver が使う目的関数(最小値問題となる関数)を提供します。どういう事かと言うと、まず Discount Factor の推定値を使って生成途上のイールドカーブを作ります。そして、そのカーブを使って、ベンチマーク商品の価格を一旦計算してみます。当然、その計算値は市場実勢値と異なりますが、市場実勢値に近づくように推定値を動かして、誤差が許容範囲内になるまで再計算を繰り返します。最終的に、誤差が許容範囲内になった時点の Discount Factor 推定値が、Bootstrap 済みのイールドカーブになります。BootstrapHelper は、このプロセスの中で、データソースとなるベンチマーク商品の価格計算を行う機能を提供しています。この機能は、価格エンジンがセットされた商品オブジェクトと同じに見えます。しかし、Instrumentクラスや PricingEngineクラスを使わずに、その2つのオブジェクトの機能をひとつにパッケージ化したクラスになります。
BootstrapHelper は Instrument と PricingEngine を合体させたようなものなので、商品の種類ごとに派生クラスが用意されています。従って、そのコンストラクターの引数は、ベンチマーク商品のキャッシュフローが特定できるような属性になります。その一例として、154行目で、O/N預金の RateHelperインスタンス(BootstrapHelperの派生クラスのインスタンス)を生成している部分を抜き出しました。
ext::shared_ptr <RateHelper> dON(new DepositRateHelper(
Handle<Quote>(dONRate),
1 * Days, 0,
calendar, Following,
false, depositDayCounter));
ここでは、RateHelperクラスの派生クラスである、DepositRateHelperインスタンスを生成しています。コンストラクターに渡している引数は、以下の通りです。
- Deposit の期間 : ここでは1日
- スタート日までの期間 : ここでは0日。すなわち本日スタート
- calendar : 休日を指定する具体的なカレンダーインスタンス。ここではTARGET
- Following : エンド日が休日の場合の取扱い。ここでは、翌営業日に指定。
- false : エンド日が月末の場合の取扱い。ここでは、エンド日が月末かどうか関係ない。
- depositDayCounter : 日数計算方法のインスタンス。152行目で生成したもの。ここでは実日数/360。
以上の情報があれば、インターバンク預金のキャッシュフロー日付が特定できます。PricingEngine に関する情報はありませんが、シンプルなイールドカーブを使った現在価値計算のアルゴリズムなので、クラス内で実装済みです (impliedQuote( ) という名前のメソッドで、個別商品ごとに RateHelper派生クラスで実装されています。)。イールドカーブは、これから作ろうとするものなので、この段階では特定できていませんが、後で YieldTermStructureオブジェクトを構築する時に、それが設定されます。
ベンチマーク商品として、OISスワップと、ECB-Dated OISスワップも使われているので、それぞれに対応する、OISRateHelper と DatedOISRateHelperクラスも使われています(172行目以降)。これらのコンストラクターの引数を見ると、スワップのスタート日、期間、SimpleQuoteインスタンスへの Handle、および OISインデックスのインスタンスだけです。これだけでは、スワップのキャッシュフローを特定する情報として不十分です。OISRateHelperクラスの定義をしているソースコード(”oisratehelper.hpp”) を見れば解りますが、足りない情報は、すべて市場慣行を使ってデフォールトで設定されています。価格計算についても、DepositRateHelperと同じく、イールドカーブを使って現在価値を計算するアルゴリズムが、内包されています(impliedQuote( )メソッドで実装されている)。
④-4 BootstrapHelper インスタンスの配列の生成
すべてのベンチマーク商品の "BootstrapHelper"インスタンスを用意したら、それを一本の配列にまとめます。277行目で BootstrapHelper ポインタの配列変数を生成し、278行目から、各RateHelperインスタンスをこの配列に push_back() しています。
その少し手前の、271と274行目で、"DayCounter"オブジェクト(termStructureDayCounter)と、Solverを使う際の許容誤差値(tolerance)を設定しています。この2つも "YieldTermStructure"の部品になります。
④-5 YieldTermStructureオブジェクトの生成
以上で、OISカーブの部品の用意が出来ました。いよいよイールドカーブの構築です。310行目をご覧下さい。
ext::shared_ptr<YieldTermStructure> eoniaTermStructure(
new PiecewiseYieldCurve<Discount, Cubic> (
todaysDate,
eoniaInstruments,
termStructureDayCounter,
tolerance) );
これで "PiecewiseYieldCurve<Discount, Cubic>" クラスのインスタンスが生成されました。コンストラクターが起動すると、BootstrappingとInterpolationのアルゴリズムも作動し、Bootstrapping後のイールドカーブが完成しています。引数には、これまでに生成された部品が使われているのが分ります。部品さえ用意出来ていれば、イールドカーブの構築はこれだけです。
PiecewiseYieldCurve<Discount, Cubic> はテンプレートクラスで、テンプレート引数に、このオブジェクトが使う他のクラスを指定できるようになっています。ここでテンプレート引数として指定されている ‘Discount’ は、Interpolation の対象が Discount Curve である事を示し、‘Cubic’ は Interpolation法が Cubic Spline法である事を示しています。Bootstrapping + Interpolation のアルゴリズムの複雑さや、様々なInterpolationの方法がある事を考えると、これだけの設定で本当に出来るのかという疑問が湧きます。
そこで、PiecewiseYieldCurve<Discount, Cubic>クラスについて、もう少し詳しくみていきます。
<PiecewiseYieldCurve<...>テンプレートクラス >
このクラスの宣言は、piecewiseyieldcurve.hppにありますが、次の様になっています。
template <class Traits, class Interpolator, template <class> class Bootstrap = IterativeBootstrap>
class PiecewiseYieldCurve
: public Traits::template curve<Interpolator>::type,
public LazyObject { ...
};
これは、テンプレート引数を3種類持つテンプレートクラスになります。3つ目のテンプレート引数‘Bootstrap’は、IterativeBootstrapクラスでデフォールト設定されています。従って、この Exampleコードでは、3番目のテンプレート引数は、省略されていました。
この3種類のテンプレート引数で、①Interpolation の対象となるイールドカーブの種類、②Interpolation の方法、③Bootstrap の方法、を指定した事になります。この例では、①Discount Curve を、②Cubic Spline法による Interpolation を行いながら、③Solver による再計算を使ったBootstrappingアルゴリズムで、構築している事になります。
まだ疑問が残ります。Cubic Spline による Interpolation法は、複数存在します。テンプレート引数で Cubic と指定していますが、どの Cubic Spline法か不明です。これも、Cubic クラスのデフォールト設定により、内容が特定されています。ここでは、その詳細の説明は省略しますが、数々の Cubic Spline法の定義内容は、ソースファイルのcubicspline.hpp に書かれているので、そこで確認して下さい。また、Cubic Spline法も含めた、様々な Interpolation法については、上級編のInterpolationの方法を参考にして下さい。要は、様々な Bootstrapping + Interpolation法のアルゴリズムと選択肢がある中、QuantLib がデフォールトで用意したイールドカーブ構築方法を使って、YieldTermStructure オブジェクトを作ったという事です。QuantLib のイールドカーブ構築ライブラリを使いこなすには、これらの選択肢を理解する必要がありますが、Reference Manualやソースコードを参考にして下さい。また Luigi Ballabio氏の文献 “Implementing QuantLib” の Chapter-III で金利などのTerm Structureクラスについて詳しい説明があるので、そちらも参考にして下さい。その和訳もこのサイトに上げています。
ここで、再び、Example のコードに戻ります。
321 と 323行目で、YieldTermStructureオブジェクトへの "Handle" を2種類用意しています。ひとつは Discounting Curve用で、もうひとつは Forecasting Curve用です。"Handle"は、このYieldTermStructureオブジェクトをFinancial InstrumentとPricing Engineに取り付ける際のソケットのような役割を果たします。部品を取り替える際に、メモリー領域の管理を自動的に行ってくれます。
④-6 6カ月EURIBOR の Index Curve の構築
328~608行目までは、6カ月EURIBORのIndex Curveの構築です。見ての通り、OISカーブの構築手順とほぼ同じです。すなわち
- まず、ベンチマーク商品の市場価格・市場レートを SimpleQuoteインスタンスで取り込む。
- 次に、その SimpleQuoteインスタンスを使って、ベンチマーク商品の種類ごとに、RateHelperインスタンスを作成する。
- その RateHelperインスタンスをひとまとめにした配列を作成する。
- 最後に、部品を組み合わせて、YieldTermStructureオブジェクトを生成する。
ここでも、PiecewiseYieldCurve <Discount, Cubic> のテンプレートクラスを使って、QuantLib がデフォールトで用意したアルゴリズムで、具体的なイールドカーブを構築しています。
一点、注意しておきたい事があります。この Example では、Forecasting Curve の構築を、かつての市場慣行であったSingle Curve構築と同じアルゴリズムを使っています。すなわち、Bootstrapping + Interpolation法のアルゴリズムの中で、Forecasting Curve と Discounting Curve を同じものとして使っている事です。これでも使えない事は無いですが、この方法だと、金利スワップをOISカーブでDiscountingした場合とIndexカーブでDiscountingした場合で、僅かながら価格差が発生し、アービトラージが可能となります。アービトラージフリーとなるような、Multi-Curve構築方法は、上級編 − イールドカーブ − Multi-Curveの構築方法を参照下さい。
以上で、2種類のイールドカーブ構築が出来ました。
⑤ Instrumentオブジェクトの生成
いよいよ、時価評価の対象となる金融商品(Instrumentオブジェクト)を作ります。ここでは、シンプルな期間5年の金利スワップを、スポットスタートと、1年後のフォワードスタートの2種類生成しています。
615~644行目までで、まず金利スワップの部品を作成しています。すべての金融商品は、キャッシュフローの集合と見做せますが、これらの部品は、そのキャッシュフローを特定するのに必要な、次の様な情報になります。
- みなし元本額
- 固定金利キャッシュフローの、レート、支払い回数、
- 変動金利キャッシュフローの金利インデックス、支払い回数、
- それぞれのキャッシュフローの日数計算方法(DayCounterクラス)、クーポン日と休日が重なった場合の取扱い方法(BusinessDayConventionクラス)
- それぞれのキャッシュフロー日付(Scheduleクラス)
- 変動金利のスプレッド
- スワップの期間
などが、作成されています。休日を特定するCalendarオブジェクトは、Exampleの最初の方で準備済です。
さて、このリストの中に、イールドカーブがありません。あれほど、重要な部品と言っていたのに、なぜでしょうか?
627行目で"変動金利インデックス"のオブジェクト("Euribor6M"クラスのインスタンス)を生成していますが、ここで先ほど作成したForecasting Curveが使われています。すなわち、イールドカーブは金利スワップオブジェクトの直接の部品ではなく、変動金利インデックスオブジェクトの中に組み込まれて、部品の部品になっています。このイールドカーブを使って、スワップの変動金利クーポン期間に対応する6カ月LIBORのフォワード金利を推定します。
以上で、部品の用意が終わりました。645と662行目で、それらの部品をコンストラクターに渡し、スポットスタートのVanillaSwapオブジェクトと1年後のフォワードスタートのVanillaSwapオブジェクトが生成されました。部品さえ揃っていれば、Instrumentオブジェクトの作成は簡単です。
⑥ PricingEngineの生成
668行目から、いよいよ価格計算のプロセスです。673~699行目までは Console 画面出力の準備です。
705行目で PricingEngineオブジェクトを生成しています。部品は、先ほど作った Discounting Curve だけで、それを使って DiscountingSwapEngine を生成しています。
708 と 709行目で、今作った PricingEngine オブジェクトを、2種類の Instrument オブジェクトに設定しています。
712 と 713行目で、Forecasting Curve と Discounting Curve に、それぞれ6カ月EURIBOR のインデックスカーブと、EONIAベースの OISスワップカーブをリンクさせています。それぞれ 321 と 325行目で RelinkableHandle<YieldTermStructure> クラスのインスタンスとして生成済みですが、Handle につながっているイールドカーブオブジェクトを、linkTo( ) メソッドで、簡単に取替える事ができます。Observer と Observable の関係性の管理や、ポインタのメモリー管理などを、自動的に行ってくれるので、そういった操作を一切気にする必要はありません。
⑦ 現在価値の計算
以上で準備完了です。715行目で、スポットスタートの金利スワップの価格計算を行っていいます。さらに Swapクラスに備わっている fairSpread( ) fairRate( ) メソッドを使って、今の市場実勢をベースにした場合に、この Swapインスタンスの市場価格が Parになるような、変動金利スプレッドと、固定金利レートをそれぞれ計算しています。
次に、Discounting Curveを、OISカーブから6カ月EURIBOR のインデックスカーブに入れ替えて価格計算を行っています。736 と 737行目で、Forecasting Curve と Discounting Curve を取り替えています。そして739~741行目で先ほどと同じ計算をしています。
計算結果は、上記の Console画面の出力をご覧下さい。現在価値にしてわずか 36ユーロの差(価格では0.0036%の差)しかありません。イールドカーブがフラットな場合は、Multi-Curve での時価評価と Single Curve での時価評価には、あまり差は出ません。カーブが完全にフラットな場合は、同じになります。しかし、イールドカーブの傾斜が立って、かつOISスプレッドが拡大すると、この差は顕著になってきます。
761 行目からは、1年後スタートの期間 5年の金利スワップの時価評価を行っています。上のスポットスタートの金利スワップの価格計算と、全く同じ手順です。計算結果は、上記の Console画面の出力の通りです。スポットスタートと比べると、現在価値は大きく動いていますが、Multi-Curve による時価評価と Single Curve による時価評価の差は、やはりほとんどありません。理由は、先ほどと同じです。
次に、Par Curveのベンチマーク商品の内、期間5年の EURIBORスワップレートを 0.76% から 0.9%まで動かした場合の時価評価を行っています。これも、スポットスタートと1年後のフォワードスタートの2種類の商品で、先ほどと同じように Discounting Curveを2通り使って行っています。
817行目で、スワップレートを新たに 0.9% に設定しています。この時点で、背後で、Observable である SimpleQuoteインスタンスから、それを監視している Observerに対して、データ変更の通知が一斉に飛びます。この例では、SimpeQuote→RateHelper→YieldTermStructure→IborIndex→Instrumentという順番で、通知が伝達されていきます。通知がされた段階では、イールドカーブの再構築や、価格の再計算は行われません。836行目で Instrumentオブジェクトから NPV( ) が呼び出された時に、一斉に再計算されます。この仕組み(データ変更があっても、直ぐに再計算せず、実際の価格計算メソッドが呼び出された時に初めて、再計算をスタートする仕組み)は、LazyObjectクラスを使って取り込まれています。これまで作った市場データに関する部品や、商品オブジェクトはすべて LazyObjectの派生クラスになります。
計算結果は、上記の Console画面の出力をご覧下さい。現在価値は、スポットスタートのスワップでは大きく動いていますが、1年後のフォワードスタートの方は、殆ど変化がありません。理由は、ご自分で考えてみて下さい。ヒントは、5年のスワップ金利しか動かしていない事です。
以上で、このExampleの説明を終わります。イールドカーブの構築は、様々な方法があり、アルゴリズムは非常に複雑です。このExampleを見ただけでは、その過程を詳しく理解するのは難しいですが、ユーザーとして使うだけならば、この例のように部品を用意しておけば、それを組み立てて、QuantLibが用意したデフォールトの方法で、簡単に構築できます。Term Structureについて、より詳しく理解されたい型は、Luigi Ballabio氏の"Implementing QuantLib"の Chapter-III をご覧下さい。
<ライセンス表示>
QuantLibのソースコードを使う場合は、ライセンス表示とDisclaimerの表示が義務付けられているので、添付します。 ライセンス