2. "Implementing QuantLib"の和訳
Chapter-VII The Tree Framework: Tree を使った価格モデルのフレームワーク
7.1 The Lattice and Discretized Asset classes: 格子クラス および 離散化資産クラス
7.1.1 具体例: Discretized Bond (離散モデルで表現された債券)
プレインバニラの債券 (ゼロクーポン債、固定金利債、変動金利債) は、単純な商品という点で具体例としてはうってつけです。同時に、その特性は十分広範囲にわたるので、(プログラムコードの)問題点を指摘する材料にもなります。
とは言っても、ゼロクーポン債では単純すぎるでしょうか? この債券は、最も単純な資産であり (すなわち、ペイオフの無い、たった一本のキャッシュフロー)、それ自体は興味を引かない商品かも知れません。しかし、後ほど説明する例にある通り、Lattice上の割引率を推定する方法を提供しているので、Helperクラスとして使うと非常に役に立つでしょう。
その実装内容を下記 Listing 7.2 に示します。 TimeGridに強制的に加えるべき時間の配列変数は、定義されていません (なぜなら、ユーザー自身が設定する時間で初期化されるだけで十分だからです)。従って、mandatoryTimes( )メソッドは何も加えません (なぜなら満期まで何のキャッシュフローも発生しないからです)。さらに reset( )メソッドは資産価格の配列に1通貨単位 (訳注:ゼロクーポン債の償還価格を1と見做している) を代入して初期化しているだけです。従って、仮に時間 T でこのクラスのインスタンスを初期化し、そこから時間 t までLatticeを遡るとすると、資産価格の配列に書き込まれる値は、時間Tからtまでの間の各離散時間に対応する割引率に他なりません。
Listing 7.2: DiscretizedDiscountBondクラス
class DiscretizedDiscountBond : public DiscretizedAsset {
public:
DiscretizedDiscountBond() {}
void reset(Size size) {
values_ = Array(size, 1.0);
}
std::vector<Time> mandatoryTimes() const {
return std::vector<Time>();
}
};
*********************************************************************************
固定金利債の例を見てみると、プログラムコードの内容はより興味深いものです。しかし、QuantLibライブラリーでは、今の所、DiscretizedFixedRateBond といったクラスは提供されていません。以下の Listing 7.3 に示すプログラムコードは、実際に実装するとすれば、どのようになるか簡単な例として示すものです。しかし、実際に Library に取り込むにはもっと修正が必要でしょう。
Listing 7.3: DiscretizedFixedRateBondクラスの実装例
class DiscretizedFixedRateBond : public DiscretizedAsset {
public:
DiscretizedFixedRateBond(const vector<Time>& paymentTimes,
const vector<Real>& coupons, Real redemption)
: paymentTimes_(paymentTimes), coupons_(coupons), redemption_(redemption) {}
vector<Time> mandatoryTimes() const {
return paymentTimes_;
}
void reset(Size size) {
values_ = Array(size, redemption_);
adjustValues();
}
private:
void postAdjustValuesImpl() {
for (Size i=0; i<paymentTimes_.size(); i++) {
Time t = paymentTimes_[i];
if (t >= 0.0 && isOnTime(t)) {
addCoupon(i);
}
}
}
void addCoupon(Size i) {
values_ += coupons_[i];
}
vector<Time> paymentTimes_;
vector<Real> coupons_;
Real redemption_;
};
コンストラクターは、引数として、①クーポンスケジュールとなる支払日の配列、②クーポン額の配列、及び③償還額 を取り、それぞれ該当のメンバー変数に保存します。引数の型が、Instrumentクラスにおける同じ種類の商品が保持している型とは異なっている点に注意して下さい (例えば、Instrumentクラスではクーポン額の配列ではなくCashFlowクラスのインスタンスを保持するように設計されている)。これは、実際に PricingEngine がこのクラスのインスタンスを生成しようとする場合、相応の型変換の操作が必要になることを示唆しています。
クーポンの支払いスケジュールがあるという事は、ゼロクーポン債とは違って、このクラスは任意のスケジュールを使って勝手にインスタンス化できないという事です。実際に、このクラスは mandatoryTimes( )メソッドを備えており、クーポン支払いのスケジュールを返すようになっています。Lattice上の離散時間を遡る際、かならずその時点で止まる必要があり、資産価格の初期値設定は債券の償還日に行わなければならず、その償還日は時間軸の最終日になります。その設定を行う際、reset( )メソッドはその期日における資産価格の配列を償還額で設定し、さらにadjustValues( )メソッドを呼び出して、その値に償還日のクーポン額を加えます。
adjustValues( )メソッドがこの動作を行えるようにする為に、このクラスは仮想関数である postAdjustValuesImpl( )メソッドをオーバーライドしています。(なぜpre-ではなく post- になるのか疑問に思われるかもしれません。あと数ページ我慢して下さい。もう少し内容の説明をすればすべて明らかになります。) このメソッドは、ループを使って、それぞれのクーポン支払日が、Lattice の時間軸と一致する時点を順番に探します (そのチェックは、やや不適当なメソッド名の isOnTime( ) で行います)。一致した時点で、その時点に対応するクーポン額を、その時点の資産価格(の配列)に加えます。読みやすくするため、実際の作業は addCoupon( )メソッドに委託されています。加えられたクーポン額は、Lattice上を遡っていくと、自動的に Discounting されていきます。
************************************************************************************
最後に、変動金利債を見てみましょう。下記 Listing 7.4 にその実装内容を示します。これもまた、例示用に単純化したものです。
Listing 7.4: DiscretizedFloatingRateBondクラスの実装内容
class DiscretizedFloatingRateBond : public DiscretizedAsset {
public:
DiscretizedFloatingRateBond(const vector<Time>& paymentTimes,
const vector<Time>& fixingTimes,
Real notional)
: paymentTimes_(paymentTimes), fixingTimes_(fixingTimes),
notional_(notional) {}
vector<Time> mandatoryTimes() const {
vector<Time> times = paymentTimes_;
std::copy(fixingTimes_.begin(), fixingTimes_.end(),
back_inserter(times));
return times;
}
void reset(Size size) {
values_ = Array(size, notional_);
adjustValues();
}
private:
void preAdjustValuesImpl() {
for (Size i=0; i<fixingTimes_.size(); i++) {
Time t = fixingTimes_[i];
if (t >= 0.0 && isOnTime(t)) {
addCoupon(i);
}
}
}
void addCoupon(Size i) {
DiscretizedDiscountBond bond;
bond.initialize(method(), paymentTimes_[i]);
bond.rollback(time_);
for (Size j=0; j<values_.size(); j++) {
values_[j] += notional_ * (1.0 - bond.values()[j]);
}
}
vector<Time> paymentTimes_;
vector<Time> fixingTimes_;
Real notional_;
};
コンストラクターは、引数として、①クーポン支払い日の配列と ②金利Fixing日の配列、および③債券の元本額を取り、それぞれメンバー変数に保存します。クーポン額については、Lattice上で別途推定値を計算する為、引数には含まれません。実装を簡単にするため、i 番目のクーポンの経過期間はクーポンのレート Fixing日と支払日の間の期間と同じであると仮定しています。
mandatoryTimes( )メソッドは支払日と Fixing日の両方をまとめたものを返します。計算のプロセスの中で両方とも必要になるからです。reset( )メソッドは DiscretizedFixedRateBondクラスのそれと同じ様な動作をします。即ち、資産価格の配列を、償還額でセットし、その後に adjustValues( )を呼び出します。
資産価格の修正は、オーバーライドされた preAdjustValuesImpl( )で実行されます。(そうです。pre-バージョンです。もう少し辛抱して下さい) このメソッドはループを使って変動金利の Fixing日それぞれについて、(Lattice上の)現時点と同じかどうかチェックし、もしそうなら addCoupon( )メソッドを呼び出します。
ここで、読者の方は、故エタ・ジェームスのヒット曲のフレーズのように、“その結婚式、止めて!”と叫ぶのは、ごもっともです。当然ながら、クーポン額が足されるべき日は (Fixing日ではなく) クーポン支払日であるべきですよね? はい、わかっています。しかし、問題は Lattice 上で時間を遡らなければなりません。そうすると、クーポン支払日に該当するLattice上の時点においては、クーポン額を足したくても、必要な情報が無いのです。クーポン額を計算するには、 Lattice 上でまた到達していない時点(fixing日)における金利の値が必要なのです。従って、我々はLatticeを変動金利の Fixing日まで遡らざるを得ず、そこで初めてクーポン額が計算でき、そこで資産価値に修正を加えます。その場合、実際の支払日より、クーポン期間分手前の時点になるので、Payment Dateから、その日までの割引率を使って、金額を修正しておく必要があります。
まさにこの動作を、addCoupon( )メソッドが行っている訳です。まず最初に、このメソッドはクーポン支払い日(T)が満期の割引債オブジェクト(訳注: DiscretizedDiscountBondクラス。本セクションの例の最初に登場したオブジェクト) をインスタンス化します。そして、その価格を Lattice 上の現時点、すなわち Fixing日 t まで遡って計算します。その価格は j ノードにおける期間 t から T の間の割引率 \(D_j\) に相当します。この価格から、その時点における変動金利 \(r_j\) を推定でき (方程式 \(1+r_j(T-t)=1/D_j\) を満たすので、そこから \(r_j\) を計算できます)、さらにクーポン額も計算できます。しかし、若干の代数を使う事によって、もっと簡単に計算できます。クーポン額 \(C_j\) は N を元本額とすると \(N\times r_j(T-t)\) の式で求まります。上記の金利と割引率の関係式から \(r_j(T-t)=1/D_j-1\) の等式が成り立ち、それを代入すると、クーポン額は \(C=N(1/D_j-1)\) で求まります。ここで、Latticeを遡って Fixing日まで来てしまっている事を思い出して下さい。クーポン額を加えるにあたって、クーポン支払い日から Fixing日までの期間に相当する額を割引かなければなりません。すなわち、上記の式 \(C=N(1/D_j-1)\) の右辺にさらに \(D_j\) をかける必要があり、そうすると、j-Node における資産価格に加える修正額は、\(C_j=N(1/D_j-1)\times D_j=N(1-D_j)\) となります。最後の式が addCoupon( )メソッドの実装の中で使われている計算式です。
読者の方はおそらく気づかれたと思いますが、上記で説明した内容はクーポンの経過期間が Fixing日と支払日の期間と一致しているという前提に立っています。仮にそうでないケースでは、もはや計算方法を簡潔化する事はできず、実装内容をかなり変更する必要があります。そうすると、例えば、変動金利とクーポン額の推定に使う割引債のインスタンスを、経過期間の最後の日に生成し、さらに割引率を計算するためにもうひとつの割引債インスタンスを支払日に生成する事になるでしょう。当然ながら、正確さは向上するでしょうが、パフォーマンスは悪化します。なぜなら addCoupon( )メソッドは、2つの資産価格で Lattice を遡らなければならないからです。ユーザーの方は、いずれかの方法を自身の優先順位に従って実装すれば良いでしょう。
<ライセンス表示>
QuantLibのソースコードを使う場合は、ライセンス表示とDisclaimerの表示が義務付けられているので、添付します。 ライセンス