2.Implementing QuantLib の和訳
Chapter-III Term Structures
3.4 Other Term Structures : その他の期間構造クラス
このChapterではこれまでの所、金利のTerm Structure(期間構造)にフォーカスして説明してきました。QuantLibでは、当然ながら、その他の期間構造も取り扱っています。このセクションでは、それらについても簡単に説明したいと思いますが、主に、金利の期間構造と異なっている点、および、それらに特有の機能についてフォーカスしたいと思います。
3.3.1 Default Probability Term Structure : デフォールト確率の期間構造
デフォールト確率のTerm Structureは、金利の期間構造と最も類似性があります。このTerm Structureは、デフォールト確率、サバイバル確率(訳注:対象クレジットがデフォールトせずに存在し続ける確率)、デフォールト強度、ハザード発生強度(訳注:指数分布 \( \lambda e^{-\lambda t} \) における強度 λ )など、いくつかの表現方法があり、そのいずれからも他の表現方法が導出できます。この点で金利のTerm Structureにおけるゼロ金利と割引率の関係とよく似ています。
しかし、金利のTerm Structureとは異なり(そこでは、すべての(各期間のPillarにおける対応レートの導出)メソッドがdiscountImpl( )一本をベースに実装されていた)、なにか特定のメソッド一本を使って、他のデフォールト確率のTerm Structureの値(各Pillarにおける対応するデフォールト確率やデフォールト強度など) を導出するような仕組みにしていません。そのかわり、下記のListing 3.13にある通り、survivalProbabilityImpl( ) とdefaultDensityImpl( )という2種類の抽象メソッドが(純粋仮想関数として)宣言されています。どちらのメソッドをベースにもう一方のメソッドを実装するかは、派生クラスに任されています。ベースクラスでは、survivalProbability( ) とdefaultProbability( )を、それぞれのImpl( )メソッドを使って実装しています(下記例では、レンジのチェックやExtrapolationなどの部分を省略しているので、実際のコードはもっと複雑ですが)。defaultProbability( )は極めて単純にsurvivalProbability( )を使って計算されており、hazardRate( )も同様に、単純にサバイバル確率とデフォールト強度から計算されています。
Listing 3.13: Sketch of the DefaultProbabilityTermStructure class.
class DefaultProbabilityTermStructure : public TermStructure {
public:
// ...constructors...
Probability survivalProbability(Time t) const {
return survivalProbabilityImpl(t);
}
Probability defaultProbability(Time t) const {
return 1.0 - survivalProbability(t);
}
Probability defaultProbability(Time t1, Time t2) const {
Probability p1 = defaultProbability(t1),
p2 = defaultProbability(t2);
return p2 - p1;
}
Real defaultDensity(Time t) const {
return defaultDensityImpl(t);
}
Rate hazardRate(Time t) const {
Probability S = survivalProbability(t);
return S == 0.0 ? 0.0 : defaultDensity(t)/S;
}
// ...other methods...
protected:
virtual Probability survivalProbabilityImpl(Time) const = 0;
virtual Real defaultDensityImpl(Time) const = 0;
private:
// ...data members...
};
Listing 3.14はアダプタークラスの概要を示しています。すなわち、金利のTerm Structureの章で説明した通り、新たなデフォールト確率のTerm Structureを、サバイバル確率、デフォールト強度、あるいはハザード発生強度のいずれかひとつの指標を使って(それぞれの派生クラスを)作ります。(但しデフォールト確率はサバイバル確率と表裏一体なので、あえてこのためのアダプターを作る必要は無いと考えました)
Listing 3.14: Adapter classes for default-probability term structures.
class SurvivalProbabilityStructure
: public DefaultProbabilityTermStructure {
public:
// ...constructors...
protected:
Real defaultDensityImpl(Time t) const {
// returns the derivative of the survival probability at t
}
};
class DefaultDensityStructure
: public DefaultProbabilityTermStructure {
public:
// ...constructors...
protected:
Probability survivalProbabilityImpl(Time t) const {
// 1 minus the integral of the default density from 0 to t
}
};
class HazardRateStructure
: public DefaultProbabilityTermStructure {
public:
// ...constructors...
protected:
virtual Real hazardRateImpl(Time) const = 0;
Probability survivalProbabilityImpl(Time t) const {
// exp(-I); I is the integral of the hazard rate from 0 to t
}
Real defaultDensityImpl(Time t) const {
return hazardRateImpl(t)*survivalProbabilityImpl(t);
}
};
最初の派生クラスは、SurvivalProbabilityStructureで、その中でdefaultDensityImpl( )メソッドを定義しており、このメソッドは純粋仮想関数であるsurvivalProbabilityImpl( ) を使って実装されています。(訳注:上記コードでは、実装内容が書かれていませんが、Survival probabilityの微分を計算するとだけ記されています)。 このsurvivalProbabilityImpl( ) は、純粋仮想関数のままで置かれているので、さらに派生するクラスで内容を実装しなければなりません。2番目の派生クラスは、DefaultDensityStructureクラスで、正反対の方法でメソッドを定義しています。(訳注:survivalProbabilityImpl( )の内容を、defaultDensityImpl( )を使って実装している)3番目のクラスは、HazardRateStructureで、サバイバル確率とデフォールト強度を、新たに宣言された抽象メソッドであるhazardRateImpl( )を使って実装されています。
残念ながら、いくつかのアダプターメソッドは、計算したい値を導出する為に、数値積分を使う必要があります。QuantLibが提供している実装方法は、数値積分を効率よく行う為、数学的にもプログラム的にもちょっとしたトリック(ガウス求積法とBoost Libraryの中のboost::bind)を使っています。しかし、皆さん自身で派生クラスを実装する際、積分の解析解が求まるのであれば、それを使ってこのアダプターを書き換えて下さい。
金利のTermStructureクラスと同じように、QuantLibではアダプターのインターフェースを実装するために、いくつかのテンプレートクラスを用意しています。すなわち、Node毎の離散的なデータをInterpolatorや、汎用的なpiecewise default-probabilityのテンプレート、さらに原データを選択する為のTraitsクラスです。既存のInterpolation Traitsと組み合わせて、これらのテンプレートは次のようなインスタンスの生成ができます。
PiecewiseDefaultCurve<DefaultDensity,Linear>
実装方法は、すでに金利のPiecewiseYieldCurveクラスの所で説明したものと殆ど同じなので、あえて繰り返しません。但し、一点だけ注意して頂きたい事は、デフォールト確率のTerm Structureは、それだけではBootstrappingは出来ないという事です。そのためには、ディスカウントカーブも必要になります。(イールドカーブについて、Multi-Curve対応をする場合も、同様です。) その場合は、商品の価格エンジンで使われるものと同じイールドカーブを使って下さい。そうでないと、例えばCDSを取引した瞬間に、そのCDSはAt the Moneyでなくなる(MtMをすると即座に損益が発生する)ことになります。
< Cinderella method:シンデレラ法 >
DefaultPrbabilityTermStructureクラスの実装の際、読者の方は、前の章(Chapter 3.2)で議論したのと同じ、“対称性の破れ”に気付かれたと思います。しかしながら、ここでは若干の違いがあります。金利のTerm Structureにおいてはディスカウントファクターが、(3種類のTerm Structureの表現方法の中で)特別な地位をもっていました。一方、デフォールト確率のTerm Structureにおいては、Harzard Rate(訳注:デフォールト強度。指数分布 \( \lambda e^{-\lambda t}\) における強度λのこと) は、不当な扱いを受けている義理の妹(シンデレラ)のような役割を負わされています。すなわち他のクラスにあるサバイバル確率やデフォールト密度を出力する抽象メソッドと同じような、hazardRateImpl( )メソッドは定義されていません。昔のバージョンのソースコードを見ていただけると分ると思いますが、対称性の為だけに、かつてはそのメソッドを定義していましたが、説明もせずに取り除いてしまいました。その頃は、その方が良いと考えていました。
その結果、HazardRateStructureの派生クラスのアダプターメソッドは、hazardRateImpl( )が使えないので、ハザード発生強度を計算する為に、他のメソッドをぐるぐる巡らないといけません。すなわち、デフォールト確率の実装を使ってデフォールト強度とサバイバル確率を計算(かつその過程で数値解析法による積分)しなければなりません。残念ながら、おとぎ話の中のまま母でさえ、バグ発生のリスクを考えると、すでに取り除いてしまったhazardRateImpl( )を復活できなくなりました。
3.3.2 Inflation Term Structure : インフレ率の期間構造
インフレ率の期間構造(Term Structure)は、これまで説明してきたTerm Structureとはいくつかの点で特徴が異なります。当然ながら、そのような特徴の大部分は、クラスの構造を複雑にしています。
最も顕著な差異は、インフレ率のTerm Structureは、2つの異なった種類のTerm Structureを(従って2つの異なったインターフェースを)持っている事です。QuantLibはInflationTermStructureという一種類のベースクラスを用意し、そこでいくつかのインスペクター関数と共通の動作を定義しています。しかしながら、実際のインフレ率を返すインターフェースは、2種類の派生クラスの中で宣言されており、Listing 3.15のような継承関係を形作っています。その2種類の派生クラスは、それぞれゼロクーポンレート(のように表現したインフレ率)と、前年比として表示されるインフレ率をオブジェクトモデル化したものです。この2つのインフレ率の表現方法は、お互いに他の表現方法に換算するのが難しく、これまで使った複合的なアダプターパターンを使えませんでした。
Listing 3.15: Sketch of the InflationTermStructure class and its children.
class InflationTermStructure : public TermStructure {
public:
// ...constructors...
virtual Date baseDate() const = 0;
virtual Rate baseRate() const;
virtual Period observationLag() const;
Handle<YieldTermStructure> nominalTermStructure() const;
void setSeasonality(const shared_ptr<Seasonality>&);
protected:
Handle<YieldTermStructure> nominalTermStructure_;
Period observationLag_;
// ...other data members...
};
class ZeroInflationTermStructure
: public InflationTermStructure {
public:
// ...constructors...
Rate zeroRate(const Date &d,
const Period& instObsLag = Period(-1,Days),
bool forceLinearInterpolation = false,
bool extrapolate = false) const;
protected:
virtual Rate zeroRateImpl(Time t) const = 0;
};
class YoYInflationTermStructure
: public InflationTermStructure {
public:
// ... constructors ...
Rate yoyRate(const Date &d,
const Period& instObsLag = Period(-1,Days),
bool forceLinearInterpolation = false,
bool extrapolate = false) const;
protected:
virtual Rate yoyRateImpl(Time time) const = 0;
};
このようなプログラムの記述方法は、有利な点と不利な点がありますが、もしかしたら不利な点の方が多いかもしれません。まず、このプログラムだと、一組の重複するサブクラスを生成することになり、これだと明らかにBugの原因になりやすい形です。(注:もっとひどくなるかもしれません。今までのところ、「年率」以外に特定の期間毎のインフレ率を考慮した事はありません。望むらくは、年率インフレ率が一般的な方法になってほしいです。)その一方で、サブクラスの構造を少し単純にできます。それぞれのインフレ率のTerm Structureは、原データの数値が一種類しかなく、アダプタークラスが必要ありません。
その他の相違点は、インフレ率の決め方の特殊性に由来するものです。インフレ率の発表は、観測月から少し遅れて行われる為、インフレ率Term StructureはBase DateとReference Dateという2種類の日付を持つ必要があります。Base Dateとは、直近のインフレ率のFixingの日に対応するものです。もし特定の日のインフレ率のデータが必要になった場合、かつその日が本日からみると過去の日であっても、Base Dateより後の日であった場合、その数字はあくまで「予想」なります。(注:本Chapterの最初の方で、先日付は必ずしも、Reference Dateを基準にそこからスタートするとは限らないと述べた事を覚えておられるでしょうか?本例がそこで述べた例外的なケースです)。また、インフレ率のFixingは季節調整が入るため、インフレ率Term StructureはSeasonalityという多相的なクラスのインスタンスを保持するための仕組みを提供しています(Seasonalityクラスについては話を簡潔にするためここでは説明しません)。そのようなインスタンスが与えられると、Strategicパターンのシンプルなデザインによって、インフレ率の値に季節調整を加える仕組みが導入できます。
他の種類のTerm Structureと異なり、インフレ率クラスのインターフェースは、日付ではなく、年数(Time)を引数で取る仕組みを持ち合わせていません。インフレ率のFixing Dateまでの年数を計算するには日数計算のロジックに依存することになります。年数を受け取ってもそれを正確な日付に変換するロジックを構築する事が困難な為、その機能を持たせないようにしました。
その他にも、インフレ率Term Structureが他のTerm Structureと異なる点として、Discount Curveを保持する事があります。そうです、これを読んでいる人と同じように、私自身も(その必要性を認識した時)顔をしかめました。しかも、そのタイミングはVersion1.0のLibraryのリリース直前でした。むしろ私の方が読者の方よりよっぽどしかめっ面な顔をしていたと思います。しかし、最終的には、Inflation Term Structureだけ、そうする事にしました。このデザインはデフォールト確率Term Structureの中では取り入れていません。こうする事によって少なくとも、インフレ率のTerm StructureをBootstrappingする際に、それが使用するDiscounting Curveを使って行えるという利点があります。似たようなカーブについて、すべて同じような構造にしなければならない訳ではありませんので。このまま数年使ってみた後、Version2.0のリリースの際には、より最適なデザインを選択したいと思います。終わり良ければすべて良しですから。少なくとも、私自身は、コードの見直しをもっと頻繁に行う事を肝に銘じました。
その他の仕組み(InterpolationやPiecewise Bootstrappingなど)は他の種類のカーブの所で説明したのとほぼ同様です。但し、Zero CouponとYear on Yearベースで表示されるインフレ率が別々のサブクラスの継承関係を持つことから、2つほど相違点があります。一点目は、一種類のUnderlying Quantityをベースにすべての…Impl( )インターフェースを組みこんだAdapterクラスが存在していない事です。(例えばZeroStrucutreはDiscount Curveを、HazardStructureではDefault ProbabilityがそれぞれUnderlying QuantityとしてAdapterクラスによるすべてのインターフェースが構築されている) もう一点は、Piecewise CurveはUnderlying Quantityを選択するためのTraitsを使っていない事です。(例えば PiecewiseYieldCurve<Discount, Loglinear>のようなデザインです) 何を選択するかについては、ユーザー自身で適当なクラスを選択し PiecewiseZeroInflation<Linear>といったようなデザインを自分自身で導入して下さい。
3.3.3 Volatility Term Structure : ボラティリティの期間構造
ボラティリティのTerm Structureは、きわめて多様な構造を持つ為、そのすべてに共通な特徴を探すのは、プログラマーの能力の限界を試されます。実際の所、このTerm Structureのベースクラスの定義については(下記Listing 3.16のVolatilityTermStructureの定義を参照)、役に立つような抽象化といえるかどうか自信がありません。
Listing 3.16: Interface of the VolatilityTermStructure class.
class VolatilityTermStructure : public TermStructure {
public:
VolatilityTermStructure(BusinessDayConvention bdc, const DayCounter& dc = DayCounter());
VolatilityTermStructure(const Date& referenceDate,
const Calendar& cal,
BusinessDayConvention bdc,
const DayCounter& dc = DayCounter());
VolatilityTermStructure(Natural settlementDays,
const Calendar& cal,
BusinessDayConvention bdc,
const DayCounter& dc = DayCounter());
virtual BusinessDayConvention businessDayConvention() const {
return bdc_;
}
Date optionDateFromTenor(const Period&) const {
return calendar().advance(referenceDate(), p, businessDayConvention());
}
virtual Rate minStrike() const = 0;
virtual Rate maxStrike() const = 0;
protected:
void checkStrike(Rate strike, bool extrapolate) const;
private:
BusinessDayConvention bdc_;
};
このクラスは、ベースクラスであるTermStructureに2つの機能を追加しています。一つ目は、optionDateFromTenor( )メソッドで、オプション行使日までの期間から、具体的な行使日を返します。その計算には、ベースクラスの持つcalendar( )のインターフェースと、このクラスが持つBusinessDayConvention のインスタンス(この情報はコンストラクターに渡され、必要なInspectorメソッドで整合性チェックがなされます)を使います。(訳注:Business Day Conventionすなわち営業日に関する取引慣行とは、1カ月とか1年後の期間の応答日が、たまたま休日であった場合、翌営業日と前営業日のどちらにするか決める取引慣行。Following, Preceding, Modified Followingなどの慣行がある) この行使期間からオプション期日を計算する機能は、ユーティリティークラスにカプセル化し他のクラスでも使えるようにする、という方法もありました。(何年か前に、日数計算方法を、そのようにカプセル化するアイデアをMailing Listの読者から頂いたのですが、名前を思い出せない事をご容赦下さい)
二つ目の追加は、2種類の純粋仮想関数で、ボラティリティのTerm Structureの範囲を決めるための、最低行使価格と最大行使価格の情報を返すものと、一種類のProtectedメソッドで、与えられたストライクがその最大値と最小値の範囲内に入っているかどうかチェックするものです。残念ながら、これらの機能は、Local VolatilityのTerm Structureでは意味を成しません。しかし、これらの機能を外してしまうと、さらに派生クラスを作って(おそらくImpliedVolStructureクラスといった名前になるでしょうが)それらの機能を盛り込む必要があるので、このままにしておいても問題ないと思います。
3.3.4 Equity Volatility Structures : 株式ボラティリティの期間構造
EquityオプションやFXオプションで使われるBlack Volatility(訳注:株式オプションのBlack Scholesモデルや金利オプションのBlack Modelで使われている確率変数の対数正規分布を前提にしたボラティリティ)は、下記Listing3.17のように、BlackVolTermStructureクラスでオブジェクトモデル化しています。
Listing 3.17: Partial interface of the BlackVolTermStructure class.
class BlackVolTermStructure : public VolatilityTermStructure {
public:
Volatility blackVol(const Date& maturity,
Real strike,
bool extrapolate = false) const;
Volatility blackVol(Time maturity,
Real strike,
bool extrapolate = false) const;
Real blackVariance(const Date& maturity,
Real strike,
bool extrapolate = false) const;
Real blackVariance(Time maturity,
Real strike,
bool extrapolate = false) const;
Volatility blackForwardVol(const Date& date1,
const Date& date2,
Real strike,
bool extrapolate = false) const;
Real blackForwardVariance(const Date& date1,
const Date& date2,
Real strike,
bool extrapolate = false) const;
// same two methods as above, taking two times
protected:
virtual Real blackVarianceImpl(Time t, Real strike) const = 0;
virtual Volatility blackVolImpl(Time t, Real strike) const = 0;
};
このクラスではいくつかのコンストラクター(これらは引数をベースクラスのコンストラクターに渡すだけの役割であり、C++ 11に依拠して開発すればコンストラクターの継承機能により(派生クラスでの)定義が不要にできたでしょう)の他に、次のような機能を持つメソッドがオーバーロードされて定義されています。
- 与えられたオプション行使日(あるいは行使期間)に対応するボラティリティを返すblackVol( )というメソッド。
- 同じく、オプション行使日(あるいは行使期間)に対応する分散値(Black Volatilityの2乗にTimeをかける)を返すblackVariance( )メソッド
- 将来の2つの日付の間(期間)に対応するボラティリティあるいは分散を返すblackForwardVol( ) と blackForwardVariance( )メソッド
これらのメソッドは、Template Methodパターンを使い、Protectedメソッドの純粋仮想関数であるblackVolImpl( ) およびblackVarianceImpl( )を呼び出すことによって、実装されます。publicインターフェースは、レンジのチェック機能を提供しており、さらにフォワードボラティリティやフォワード分散の計算では、引数で渡される2つの日付に対応するフォワードの値をスポットの値から計算するロジックを提供しています。
本書を書いている時点で、BlackVolTermStructureクラスは、privateで宣言されたstatic-constのメンバー変数dTを持っていますが、盲腸や退化した尾骨のように、実際には使用されていません。読者がこれを読まれている時点までにはそれ(私の盲腸ではなく、そのデータメンバー)を取り除いておきたいと考えています。
これまで同様、blackVolImpl( )あるいはblackVarianceImpl( )メソッドのいずれかを実装する為のアダプタークラスが用意されています。それらは下記のListing3.18を見てください。(いずれかの簡単な例としてLibraryの中にあるConstantBlackVolクラスや、誤解されやすい名前ですがImpliedVolTermStructureクラスを見てみてください。)残念ながらBlackVolatilityTermStructureという名前は、ベースクラスの名前と非常に混同しやすいものにしてしまいました。将来、Libraryをバージョンアップする際、適切なクラス名に変更したいと考えていますので、良いアイデアがあれば教えてください。
Listing 3.18: Adapters for the BlackVolTermStructure class.
class BlackVolatilityTermStructure
: public BlackVolTermStructure {
... // constructors, not shown
protected:
Real blackVarianceImpl(Time maturity, Real strike) const {
Volatility vol = blackVolImpl(t, strike);
return vol*vol*t;
}
};
class BlackVarianceTermStructure
: public BlackVolTermStructure {
... // constructors, not shown
protected:
Volatility blackVolImpl(Time t, Real strike) const {
Time nonZeroMaturity = (t==0.0 ? 0.00001 : t);
Real var = blackVarianceImpl(nonZeroMaturity, strike);
return std::sqrt(var/nonZeroMaturity);
}
};
< InterpolationとExtrapolation >
提供されているVolatility Term Structureクラスの内、BlackVarianceSurfaceクラスというものがありますが、市場で価格が提示されているBlack Volatility行列(Strike軸と行使期間軸の行列)のInterpolationを行います。あまりに多くのTerm Structureクラスの例を説明してきたために、読者の方はもう十分だとお思いでしょうから、詳しい説明は行いませんが、2点ほど面白い機能があるので、そこだけ説明します。
一点目は、Interpolation方法(Interpolatorオブジェクト)をTerm Structure(のインスタンス)を作成後に変更できるという事です。その為のメソッドは以下のようなものです。
template <class Interpolator>
void setInterpolation(const Interpolator& i = Interpolator()) {
varianceSurface_ =
i.interpolate(times_.begin(), times_.end(),
strikes_.begin(), strikes_.end(),
variances_);
notifyObservers();
}
この機能は、他のInterpolationを行ったカーブでは不可能です。例えば本章の中で例を示したPiecewiseYieldCurve
BlackVarianceSurfaceの2点目の特徴は、もしあるオプションのストライクがTerm Structureのレンジの外であった場合に使われるExtrapolationの方法をカスタマイズできるという点です。やり方は、Interpolation法を使って補外するか、またはカーブの端からVolatilityをフラットにして補外するか、選択できます。なおかつ、カーブの両端でそれぞれ別の補外方法も選択できます。
こうなると、この機能を何等かのベースクラスにオブジェクトモデル化して他のTerm Structureでも使えれば便利かもしれません。Extrapolation方法の選択の仕組みを、より一般的な方法で実装するには、次のようにすると良いでしょう。カーブの端Xmax(あるいはXmin)までのInterpolationの方法が関数 \(f( )\) として定義されているとすると、\( x > Xmax となる x\) が引数として与えられた場合、Interpolationの方法をそのままExtrapolationで使うなら \(f(x)\) を返すようにし、Flat Extrapolationにするなら \(f(Xmax)\) を返せばいいでしょう。
このような仕組みを取り込むためには、Extrapolatorクラスという様なものを定義するのも、ひとつの方法であったかと思います。しかし、この方法では、カーブの両端で異なったExtrapolationの方法を使う場合はうまくいきません。Extrapolatorクラスをベースクラスとして設計する際、金利のTerm Structureの場合は、時間軸をベースにしたカーブでカーブの片方の端(手前)は0と決まっていますが、Volatility Surfaceの場合は、時間軸の他にストライク価格に沿ったカーブを持っており、こういった違いを知る余地がないからです。Extrapolatorを作ったとしても、カーブの両端の情報について区別する事が出来ない為、カーブの両端でどのような処理をするかというインターフェースを設計することが出来ないという事です。従って、上記のような実装方法を必要な場所でいちいち実装する選択肢しか無かったという訳です。
プログラムの再利用の仕組みを無理に作るのであれば、多相性のあるExtrapolationクラスを作り、インターフェースの異なる派生クラスを作るという方法がありますが、その場合は、与えられたTermStructureクラスに、必要な数だけExtrapolationインスタンスを保持しておく必要があります。
3.3.5 Interest Rate Volatility Structures : 金利ボラティリティの期間構造
Term Structureの解説の最後のトピックは金利ボラティリティです。このクラスは3種類の階層構造を持っており、そのいずれもクセがあります。
まず最初に紹介する階層構造は、Cap / FloorのボラティリティTerm Structureをオブジェクトモデル化したものです。ベースクラスはCapFloorTermVolatilityStructureで、下記Listing3.19に定義を示します。
Listing 3.19: Interface of the CapFloorTermVolatilityStructure class.
class CapFloorTermVolatilityStructure
: public VolatilityTermStructure {
public:
... // constructors, not shown
Volatility volatility(const Period& length, Rate strike,
bool extrapolate = false) const;
Volatility volatility(const Date& end, Rate strike,
bool extrapolate = false) const;
Volatility volatility(Time t, Rate strike,
bool extrapolate = false) const;
protected:
virtual Volatility volatilityImpl(Time length,
Rate strike) const = 0;
};
この定義の仕方は、これまでみてきたデザインパターンをそのまま使っています。一点だけ大きな違いは、ボラティリティは今日をスタートとしてみた行使日に対応するものでは無く、CapletsやFloorletsのシリーズ(すなわちCAP/FLOOR)の満期日に対応するものです。
この差異により同じ単語が違う意味を持つことになります。このクラスで定義されている volatility( t , strike ) メソッドは、他のボラティリティクラスの中の同じ名前のメソッドと、インターフェースは同じでも異なる意味を持ちます。その結果、インターフェースに2点ほど小さな差異をもたらしています。一つめはvolatility( )メソッドに、引数としてCAP/FLOORの期間に相当する(自然数のデータ型である)Periodを取る、オーバーロードメソッドが追加されています。2つめは、ここでモデル化されたvolatilityは特定の確率変数のT時点における分布の幅を意味するものではないので、variance( )メソッドは省かれています。(訳注:CAPのVolatilityは、複数のCapletsのVolatilityの平均のような意味なので、特定のCapletの確率分布を表すパラメータでは無い)
2種類目の階層構造は、一本のCapletあるいはFloorletのボラティリティをモデル化したものです。(もちろん、CAPボラティリティのTerm StructureからCapletボラティリティを導出する事は可能ですが、ここではその方法について説明しません。その導出方法について興味があれば。QuantLibの中のOptionletStripperクラスとその派生クラスの中身を見てください。) そのベースクラスはOptionletVolatilityStructureクラスで、下記Listing3.20に示しています。
Listing 3.20: Interface of the OptionletVolatilityStructure class.
class OptionletVolatilityStructure
: public VolatilityTermStructure {
public:
... // constructors, not shown
Volatility volatility(const Period& optionTenor,
Rate strike,
bool extrapolate = false) const;
Volatility volatility(const Date& optionDate,
Rate strike,
bool extrapolate = false) const;
Volatility volatility(Time optionTime,
Rate strike,
bool extrapolate = false) const;
Real blackVariance(const Period& optionTenor,
Rate strike,
bool extrapolate = false) const;
// same overloads as for volatility
shared_ptr smileSection(
const Period& optionTenor,
bool extrapolate = false) const;
shared_ptr smileSection(
const Date& optionDate,
bool extrapolate = false) const;
shared_ptr smileSection(
Time optionTime,
bool extrapolate = false) const;
protected:
virtual shared_ptr smileSectionImpl(
const Date& optionDate) const;
virtual shared_ptr smileSectionImpl(
Time optionTime) const = 0;
virtual Volatility volatilityImpl(const Date& d,
Rate strike) const {
return volatilityImpl(timeFromReference(d), strike);
}
virtual Volatility volatilityImpl(Time optionTime,
Rate strike) const = 0;
};
ここでは、関数名の意味が本来の意味に戻っており、すなわちvolatility( )は行使日に対応するVolatilityを返すようになっています。クラスの構造も、本来の形に戻っていますが、特筆すべき追加があります。このクラスは通常のvolatility( )メソッドに加え、smileSection( )メソッドを持っています。このメソッドは、行使日(あるいはこれに相当する期間)を引数として取り、その日におけるVolatility Smileの形状をモデル化したSmileSectionオブジェクトを返します。そのベースクラスとなるSmileSectionクラスの定義は下記Listing3.21のとおりです。このクラスのインターフェースはボラティリティクラスのインターフェースに添って定義されており、特に説明はいらないと思います。一点だけ注意して欲しいのは、オプション期間は固定されており、引数として各メソッドに渡される必要はありません。
Listing 3.21: Partial interface of the SmileSection class.
class SmileSection : public virtual Observable,
public virtual Observer {
public:
SmileSection(Time exerciseTime,
const DayCounter& dc = DayCounter());
virtual ~SmileSection() {}
virtual Real minStrike() const = 0;
virtual Real maxStrike() const = 0;
Real variance(Rate strike) const;
Volatility volatility(Rate strike) const;
virtual Real atmLevel() const = 0;
protected:
virtual Real varianceImpl(Rate strike) const;
virtual Volatility volatilityImpl(Rate strike) const = 0;
};
見てのとおりシンプルな構造で、Smile Sectionクラスを作ったことにより、ボラティリティのTerm Structureを、(2次元の)Volatility Surfaceとして保持するのではなく、各Caplet/Floorletの満期日毎のVolatility Smile Curveの配列という形で保持するという構成にしています。これにより、Volatility Smileを直接モデル化し、Cap/FloorやSwaptionのVolatility Term Structureにも再利用できるようになっています。
しかし一方で、完全にこの新しいインターフェースの方法に転換するほど勇気はありませんでした。二通りのVolatility Term Structureの保持方法(Volatility Surfaceとして保持する方法とVolatility Smileの集合として保持する方法)がベースクラスのCaplet-Volatilityクラスに依然として残っており、その結果いくつかの不都合な点が発生しています。派生クラスにおいてvolatilityImpl( )メソッドはsmileSectionImpl( )を使って下記のように実装できますが、これは最初にスマイルカーブをモデル化した場合にしかうまく行きません。
Volatility volatilityImpl(Time t, Real strike) const {
return smileSectionImpl(t).volatility(strike);
}
逆に、もし先にVolatility Surfaceをモデル化した場合、この方法は新しいクラスを実装する上で非常に面倒くさい事になります。 すなわち、volatilityImpl( )を使ってsmileSectionImpl( )を実装するのは簡単ではありません。そのメソッドはSmileSectionインスタンスを返す必要がありますが、そのインスタンスが実装するvolatility( )メソッドからvolatilityImpl( )を呼び出せる必要があります。しかし、そうするとSmile SectionとVolatility Surfaceが持つLifetime(訳注:おそらく各インスタンスのメモリーの割当を解放するタイミングの事)をリンクさせることになり、かえって様々な問題を引き起こしそうです。おそらく、SmileSectionクラスを定義するために、Volatility Surfaceクラスと同じコードを使う事になると思われます。そうするとvolatilityImpl( )も一緒に削除する事になるでしょう。
残念ながら、smileSectionImpl( )メソッドを雑に実装すると、そのメソッドを呼び出すたびに新しいオブジェクトをメモリー上に生成する事になり、パフォーマンスに悪影響を及ぼします。より高度な実装方法は、そのオブジェクトをキャッシュ(一時保存)する必要があるでしょうが、コードが複雑になるでしょう。SmileSectionクラスは興味深いアイデアと思いますが、その価値以上に問題を起こすかもしれません。SmileSectionの使用目的を狭めて、Volatility Smileを直接モデル化するクラスの実装で使うに留めた方がいいかもしれません。
OptionletVolatilityStructureクラスについての、最後の注意点です。このクラスは、これまで見てきたクラスと違って、‘日付’を引数で取るvolatilityImpl( )メソッドのオーバーロード関数を宣言しています。このメソッドはデフォールトの実装として、引数の日付を‘期間’に変換し、それを使って別のオーバーロード関数を呼び出しています。従って、新たな派生クラスのプログラムを書く際は、その関数をOverride(上書き)する必要はありません。むしろ、派生クラスがその引数で取った日付を自らのメンバー変数に代入しておけば、正確性が向上します。
最後に、3種類目の継承関係は、SwaptionのVolatility Term Structureをモデル化しています。そのベースクラスはSwaptionVolatilityStructureクラスで、下記Listing3.22にその定義を示しています。
Listing 3.22: Interface of the SwaptionVolatilityStructure class.
class SwaptionVolatilityStructure
: public VolatilityTermStructure {
public:
... // constructors, not shown
Volatility volatility(const Period& optionTenor,
const Period& swapTenor,
Rate strike,
bool extrapolate = false) const;
// various overloads, also for blackVariance and smileSection
virtual const Period& maxSwapTenor() const = 0;
Time maxSwapLength() const;
protected:
virtual Volatility volatilityImpl(Time optionTime,
Time swapLength,
Rate strike) const = 0;
void checkSwapTenor(Time swapLength,
bool extrapolate) const;
};
このクラスも、CapletVolatilityのクラスで説明したのと同じ、新しい仕組み(SmileSectionクラスとオーバーロードされたvolatilityImpl( )メソッド)と、さらにひつつの新しい仕組みを持っています。対象スワップの満期日という新しい次元を追加し、それをサポートする為の(対応する引数のレンジのチェックなどを行う)いくつかのメソッドを追加しています。
............................................................................................
以上がTerm Structureクラスの説明のすべてです。Libraryの中で、多くのプログラム例がありますので、その内容を理解するためにこの章を利用して下さい。
<ライセンス表示>
QuantLibのソースコードを使う場合は、ライセンス表示とDisclaimerの表示が義務付けられているので、添付します。 ライセンス