2.Implementing QuantLibの和訳
Chapter-II Financial Instruments and Pricing Engines
2.1 The Instrument Class (続き)
2.1.3 Example: Interest-Rate Swap 事例:金利スワップ
このセクションの説明の最後に、これまで説明してきた(Instrumentクラスの)機能をもとに、個別の金融商品をどのように実装するか説明します。
例として‘金利スワップ’を使いたいと思います。当然ご存知の通り、この商品は、定期的にキャッシュフローを交換する契約です。この商品の価値(Net Present Value ‘NPV’)は、受取キャッシュフローと支払いキャッシュフローの現在価値を計算し、合算したものです。
当然ながら、金利スワップはInstrumentクラスの派生クラスとして実装されています。その概要をListing 2.4に示します。(この実装内容は、若干古いバージョンのものですが、簡単に説明できるので、例として使います)
Listing 2.4: Partial interface of the Swap class.
class Swap : public Instrument {
public:
Swap(const vector<shared_ptr<CashFlow> >& firstLeg,
const vector<shared_ptr<CashFlow> >& secondLeg,
const Handle<YieldTermStructure>& termStructure);
bool isExpired() const;
Real firstLegBPS() const;
Real secondLegBPS() const;
protected:
// methods
void setupExpired() const;
void performCalculations() const;
// data members
vector<shared_ptr<CashFlow> > firstLeg_, secondLeg_;
Handle<YieldTermStructure> termStructure_;
mutable Real firstLegBPS_, secondLegBPS_;
};
このクラスは、メンバー変数として、価格計算に必要な情報のオブジェクトを持っています。すなわち、2つのキャッシュフロー(Cashflow
Legs)と、それを現在価値に割引くための金利期間構造(Yield Term Structure)です。それに加え、計算結果を格納するための2つの変数を持ちます。さらに、(ベースクラスである)Instrumentクラスのインターフェースを、具体的に実装する為のメソッドと、それ以外にも金利スワップに特有の値を返すメソッドを宣言しています。(訳注:isExpired( )、setupExpired( )、performCalculations( )、はベースクラスで仮想関数として宣言されている。また firstLegBPS( ) 、secondLegBPS( )、はSwapに特有の値を返すメソッド)
Swapクラスとそれに関連するクラスの関係図を、下記Figure 2.1に載せておきます。
Figure 2.1: Class diagram of the Swap class.
このクラスをInstrumentベースクラスへ適合させる為、3つの手順を踏んでいますが、3番目の手順は派生クラスによっては任意です。それに関係するメソッドを下記Listingに示します。
Listing 2.4: Partial implementation of the Swap class. (続き)
Swap::Swap(const vector<shared_ptr<CashFlow> >& firstLeg,
const vector<shared_ptr<CashFlow> >& secondLeg,
const Handle<YieldTermStructure>& termStructure)
: firstLeg_(firstLeg), secondLeg_(secondLeg),
termStructure_(termStructure) {
registerWith(termStructure_);
vector<shared_ptr<CashFlow> >::iterator i;
for (i = firstLeg_.begin(); i!= firstLeg_.end(); ++i)
registerWith(*i);
for (i = secondLeg_.begin(); i!= secondLeg_.end(); ++i)
registerWith(*i);
}
bool Swap::isExpired() const {
Date settlement = termStructure_->referenceDate();
vector<shared_ptr<CashFlow> >::const_iterator i;
for (i = firstLeg_.begin(); i!= firstLeg_.end(); ++i)
if (!(*i)->hasOccurred(settlement))
return false;
for (i = secondLeg_.begin(); i!= secondLeg_.end(); ++i)
if (!(*i)->hasOccurred(settlement))
return false;
return true;
}
void Swap::setupExpired() const {
Instrument::setupExpired();
firstLegBPS_= secondLegBPS_ = 0.0;
}
void Swap::performCalculations() const {
NPV_ = - Cashflows::npv(firstLeg_,**termStructure_)
+ Cashflows::npv(secondLeg_,**termStructure_);
errorEstimate_ = Null<Real>();
firstLegBPS_ = - Cashflows::bps(firstLeg_, **termStructure_);
secondLegBPS_ = Cashflows::bps(secondLeg_, **termStructure_);
}
最初のステップは、コンストラクターによって実行されており、2種類のCash Flowsオブジェクトと、それらを現在価値に割引く為に必要な、金利のTerm Structureオブジェクトを引数で取り、それぞれメンバー変数へ代入します。さらにコンストラクターによって生成されたSwapオブジェクト自身を、Cash FlowsオブジェクトとTerm Structureオブジェクトに対し、Observerとして登録する事も含まれています。前のセクションでも説明した通り、この登録により各Cash Flowオブジェクトは、その内容に変更があった場合は、(Observerである)Swapオブジェクトに通知し、価格の再計算を促します。
2番目のステップは、必要なインターフェース(各種メソッド)を実装する事です。 isExpired( )メソッドのロジックは極めて簡単で、メンバー変数に保持しているキャッシュフローについて、それぞれの支払日をチェックします。ひとつでも、支払日が到来していないキャッシュフローが存在していれば、not expiredとして情報を返します。もし未到来のキャッシュフローが全く無い場合は、その商品はexpiredとなります。その場合、setupExpired( )メソッドが呼び出されます。このメソッドはベースクラスであるInstrumentクラスの持つインターフェースですが、ここでは、そのベースクラスのメソッドを呼び出すのに加え、各Cash Flows Legの現在価値を0に設定する動作をします。
最後のステップは、performCalculations( )の実装です。この計算は、外部のCashflowsクラスが持つ2つの関数を呼び出して行われます。(読者の方がもし何か騙されていると感じたならば、ここでの具体例のポイントは、計算手順をどのように実装するかではなく、それをひとつのクラスの中にまとめる方法を解説するのが目的であるという点を考慮して下さい。おかしいと思われる点については、後の章で、Cashflowsクラスとそれに関連する複数の関数の説明を行いますので、そこで確認して下さい。) ひとつめの呼び出される関数は、npv( )で、上記で概略を説明したアルゴリズムを、そのまま実装したもので、将来キャッシュフローの現在価値を順番に足しあげています。変数NPV_には、2つのキャッシュフローの現在価値の差額(ネット金額)を代入します。ふたつ目の外部関数は、bps( )で、両方のキャッシュフロー全体のbasis-point sensitivity(金利感応度)を計算しています。キャッシュフローLeg毎に関数が呼び出され、計算結果はそれぞれ別のメンバー変数に格納されます。計算結果についは(数値解析による価格計算を行っていないので)推定誤差がゼロであり、errorEstimate_変数は Null<Real>( )-特別な浮動小数点の値で、無効な数字を表示するために使用される-に設定されます。(NaN (Not a Number)を使う方が良かったかもしれませんが、それを感知する方法を異なる環境へ移植するのが困難です。Boost::optionalを使う方法については調べてみる必要があるかも知れません)
最後のステップは、この派生クラスで追加された、計算結果を保持するメンバー変数の値を出力する手順です。ここでは、その変数に対応するメソッド(firstLegBPS( )と secondLegBPS( ) )が実装されており、その保持された変数を出力する際、まずlazilyに(訳注:入力データの変動があった場合にのみ実行されるという意味)計算を実行するようにしています。
Swapクラスの実装は以上です。Instrumentクラスに被せてプログラムコードを書くことにより、Swapクラスでは親クラスのプログラムコードの恩恵を受けています。すなわち、入力データの変更通知があったかどうかによって、価格の再計算を行い、その値を一時保存するという動作を、Swapクラスでプログラムコードを書かなくても、行わせることができるようになっています。
< Handleと共有ポインター >
Swapクラスのコンストラクターが、引数として、Discount CurveをHandle(ハンドル:前の章で説明した、ポインターに対するSmart(賢い)ポインター)で受取り、キャッシュフローを単純な共有ポインターで受け取っている事について疑問に思われるかもしれません。その理由は、Discount Curveについては、(価格計算において)別のカーブを使ってみる可能性があるのに対し(従って、それが可能なHandleを使う)、キャッシュフローについては、Swapの定義(Swapクラスの個別のインスタンス)の重要な一部であり、それを変更する事は想定されない為です。
2.1.4 今後の開発予定
読者の方は、上の具体例とInstrumentクラス全般について、私が定義した方法に欠点があることに気付かれたかもしれません。汎用化したにもかかわらず、我々が実装したSwapクラスは、2つのキャッシュフローが異なった通貨の場合、対応できません。Instrumentクラスについても、もしユーザーが通貨の異なる2種類のInstrumentの価値を合算しようとする場合にも同様の問題が発生します。その場合、ユーザーの方はマニュアルで、自分で一方の通貨の価格をもう一方の商品の通貨の価格に換算してから行う必要がありあす。
このような問題は、プログラムの実装におけるひとつの弱点から発生しています。すなわち、価格計算の結果をReal型(単純な浮動小数点の型)で返すようにしてしまった事です。その結果、計算結果について、実務の世界では当たり前の、通貨の情報が失われてしまいました。
この弱点については、計算結果を(Realではなく)Moneyクラスで返すようにすれば解消するかもしれません。このクラスのインスタンスは通貨の情報を持たせることができ、さらに、ユーザーのセッティング次第では、自動的に共通の通貨(例えば、会計で使う通貨)に換算する動作を加える事も可能です。
しかし、これは非常に大きな変更になり、プログラムコードの相当大きな部分に、複雑な形で影響を与えることになります。従って、この弱点への対応には、相当真剣に取り組まなければなりません。
もうひとつの(より微妙な)弱点は、Swapクラスは2種類の抽象化されたComponents(訳注:インスタンスの持つ基本情報のオブジェクト)を明確に分離できていない事です。即ち、スワップ契約を特定する情報(Cash Flows)と、それを時価評価するための市場データ(Term Structure)を明確に区分できていない事です。
その問題の解決方法は、Instrumentのインスタンスの中に、前者の情報(タームシートに書かれているスワップ契約の内容そのもの)のみを保持させ、市場データについては別の所で持たせるようにする事です。(概念的にその方がより明確である事に加え、外部の関数が、その金融商品の情報を別のフォーマットに変換するような場合-例えば、FpMLフォーマットに変換したり、あるいは戻したりするような場合-に、非常に有効になるでしょう。 訳注:FpML:Financial Products Markup Language: XMLをベースにしたデリバティブズ取引などの情報交換の為の言語)
その方法については、次のセクションで説明します。
<ライセンス表示>
QuantLibのソースコードを使う場合は、ライセンス表示とDisclaimerの表示が義務付けられているので、添付します。 ライセンス