2. "Implementing QuantLib"の和訳
Chapter VIII The Finite-Difference Framework (有限差分法のフレームワーク)
8.2 The New Framework (新しいフレームワーク)
8.2.3 Example: Black-Scholes Operator (具体例)
QuantLibライブラリーの中で、機能が完備した Operator(差分演算子)クラスの殆どは、FdmLinearOpクラスや他の基本的な Operatorクラスから直接派生していません。それらは、FdmLinearOpCompositeクラスから派生し (下記 Listing 8.24 に示します)、ひとつ、あるいは複数の基本的な Operatorインスタンスをメンバー変数に持ち、これらを使って独自の動作を実装しています。
Listing 8.24:FdmLinearOpCompositeクラスのインターフェース
class FdmLinearOpComposite : public FdmLinearOp {
public:
virtual Size size() const = 0;
virtual void setTime(Time t1, Time t2) = 0;
virtual Array apply_mixed(const Array& r) const = 0;
virtual Array apply_direction(Size direction, const Array& r) const = 0;
virtual Array solve_splitting(Size direction, const Array& r,
Real s) const = 0;
virtual Array preconditioner(const Array& r, Real s) const = 0;
};
見ての通り、FdmLinearOpCompositeクラスは、ベースクラスのインターフェースに、さらにいくつかのメソッドを追加していますが、それらは(純粋仮想関数として宣言されているので)派生クラスで実装しなければなりません。その内、size( )メソッドは、最も単純で、単にOperatorの次元数を返すだけです。その他のメソッドについては、もう少し注意して見る必要があります。
setTime( )メソッドは、前に Time-dependent Operatorsのセクションで説明したのと同じ考えを取り入れています。このメソッドが呼び出されると、時間に依存する Operatorを修正、あるいは再構築して、指定された時点に対応させます。しかし、このメソッドは、2点、機能アップが計られています。ひとつは、古いフレームワークでのこの仕組み(メソッド)は、Operatorの基本的な構成要素として TridiagonalOperatorベースクラスに含めていましたが、新しいフレームワークでは、基本的な Operatorはよりシンプルにして、setTime( )メソッドはより高いレベルのインターフェース(具体的な動作を行う派生クラス)に加えられました。もう一つは、通常 Operatorは特定の時間上で作用していきますが、このインターフェースでは、時間ステップの、スタートとエンドの時間を渡す事ができます。その結果、Operatorは、特定の時間のグリッド値だけでなく、2時点のグリッド値の平均や数値積分を使って、より柔軟な離散化グリッドを提供できるようになっています。
残りのメソッドは、ADI(Alternating Direction Implicit)スキームをサポートする為に使われます。このスキームの詳細についてはここでは立ち入りませんが(その概要は下記文献[1]を参照)、基本的な考え方は、演算子 \(A\ を\ A=A_m+A_0+A_1+...A_{N-1}\) のように分解します。ここで \(A_m\) は交差偏微分を表し、各 \(A_i\) は i-方向への微分(差分)を表現しています。そして、これらの演算子を別々に使います。その際、このOperatorオブジェクトが各差分演算子を生成してADIスキームをそれに作用させるのでは無く、各項の差分演算子に対応するメソッドをインターフェースで宣言しています。従って apply_mixed( )メソッドは \(A_m\) に対応する項に作用し、apply_direction( )メソッドは引数で指定された次元軸の項に、すなわち \(A_i\) の内のいずれかに作用されます。solve_splitting( ) と preconditioner( )メソッドも、ADIスキームの中で使われます。
最初の具体例として、次の Listing 8.25 に示す FdmBlackScholesOpクラスを見てください。
Listing 8.25:FdmBlackScholesOpクラスの概要
class FdmBlackScholesOp : public FdmLinearOpComposite {
public:
FdmBlackScholesOp(
const shared_ptr<FdmMesher> & mesher,
const shared_ptr<GeneralizedBlackScholesProcess>&,
Real strike,
bool localVol = false,
Real illegalLocalVolOverwrite = -Null<Real>(),
Size direction = 0);
Size size() const { return 1; }
void setTime(Time t1, Time t2) {
if (localVol_) {
/* `more calculations` */
} else {
Rate r = rTS_->forwardRate(t1, t2, Continuous);
Rate q = qTS_->forwardRate(t1, t2, Continuous);
Real v = volTS_->blackForwardVariance(
t1, t2, strike_)/(t2-t1);
mapT_ = /* `put them together` */;
}
}
Array apply(const Array& r) const {
return mapT_.apply(r);
}
Array apply_mixed(const Array& r) const {
return Array(r.size(), 0.0);
}
Array apply_direction(Size direction, const Array& r) const {
return (direction == direction_) ? mapT_.apply(r)
: Array(r.size(), 0.0);
}
Array solve_splitting(Size direction, const Array& r, Real s) const;
Array preconditioner(const Array& r, Real s) const;
private:
TripleBandLinearOp mapT_;
/* ... other data members ... */
};
少し前に述べた通り、このクラスは FdmLinearOpCompositeクラスから派生し、必要なインターフェースを実装しています。コンストラクターは、いくつかのパラメータを引数として取り、後で使えるように、メンバー変数に格納します。引数の中で、(差分計算の)方向を示す値を取り、これを使って、この Operatorがどの次元軸の方向に差分計算を行うか判断します。これによって、この Operatorは別の Operatorの中の構成部品として使う事が可能です(訳注:この Operator 自体は1次元用だが、多次元用の Operatorの部品として使える)。
実際の Operator(差分演算子)は、setTime( )メソッドの中で構築され、mapT_ という(TripleBandOperator型の)メンバー変数に保存されます。既に述べた通り、時間ステップのスタート時とエンド時の時間 t1 と t2 の両方を持つ事により、プログラムがより正確な時間の離散化ができるようになっています。すなわち、金利と分散の Term Structure から、(瞬間フォワード金利や瞬間分散ではなく) その2時点間の正確なフォワード金利と、分散の値を取りだす事ができます。Local Volatilityを使いたいかどうかによって、拡散項の計算方法が異なります 。しかし、いずれの場合でも、下記のような Black-Scholes微分演算子を生成し、それを mapT_ に保存します。
\[ L_{BS} (t)=-\left( r-q-\frac{σ^2}{2}\right) \frac{\partial}{\partial x}-\frac{σ^2}{2} \frac{\partial^2}{\partial x^2}+r \](setTime()メソッドの中で)完成形となる Operatorが構築されたら、他のメソッドの実装は、極めて直截的です。apply( )メソッドは、mapT_(オペレーター)を、引数で受取った配列に作用させ、その結果 (演算子を作用させた後の配列) を返すだけです。また、FdmBlackScholesOpクラスは 1次元(1変数)用の差分演算子であり、交差微分の項が存在しないので、(交差差分を計算する)apply_mixed( )メソッドはnull値の配列を返すだけです。最後に、apply_direction( )メソッドは、引数で指定された次元軸がコンストラクターで特定された次元軸と同じ場合にだけmapT_を、引数で取った配列に作用させますが、それ以外の場合はnull値の配列を返します(なぜなら、このOperatorは違う次元軸に作用するためのcomponentを持ってないからです)。その他のメソッドの実装内容も同じ様な方法で動作します。
二つ目の具体例として、下記 Listing 8.26 に Fdm2dBlackScholesOpクラスの概要を示しますが、このクラスは、2つの1次元Operatorに相関係数を組み合わせて、2次元(2変数)用のBlack-Scholes Operatorを構築します。
Listing 8.26 : Fdm2dBlackScholesOpクラスの概要
class Fdm2dBlackScholesOp : public FdmLinearOpComposite {
public:
Fdm2dBlackScholesOp(
const shared_ptr<FdmMesher>& mesher,
const shared_ptr<GeneralizedBlackScholesProcess>& p1,
const shared_ptr<GeneralizedBlackScholesProcess>& p2,
Real correlation,
Time maturity,
bool localVol = false,
Real illegalLocalVolOverwrite = -Null<Real>());
Size size() const { return 2; }
void setTime(Time t1, Time t2) {
opX_.setTime(t1, t2);
opY_.setTime(t1, t2);
corrMapT_ = /* ... other calculations ... */
}
Array apply(const Array& x) const {
return opX_.apply(x) + opY_.apply(x) + apply_mixed(x);
}
Array apply_mixed(const Array& x) const {
return corrMapT_.apply(x) + currentForwardRate_*x;
}
Array apply_direction(Size direction,const Array& x) const {
if (direction == 0)
return opX_.apply(x);
else if (direction == 1)
return opY_.apply(x);
else
QL_FAIL("direction is too large");
}
Array solve_splitting(Size direction, const Array& x, Real s) const;
Array preconditioner(const Array& r, Real s) const;
private:
FdmBlackScholesOp opX_, opY_;
NinePointLinearOp corrMapT_;
/* ... other data members ... */
};
このクラスも同様に、コンストラクターは引数をメンバー変数に保持するだけで、実際に Operatorインスタンスを構築するのは、setTime( )メソッドになります。その際、相関係数が適用になる Operatorである corrMapT_ は、このメソッドの中で構築されますが、2つの1次元 Operator、opX_ とopY_ は、それぞれの1次元Operatorの setTime( )メソッドを呼び出して構築します。
その他のメソッドは、これも前と同様に、単純です。各Operator(差分演算子)は線形性を持つので、apply( )メソッドは、項別の差分演算子を、引数で渡された配列に順番に作用させ、その計算結果を合計して返します。apply_mixed( )は交差微分演算子である corrMapT_ を使い、それに定数項を加えます。また apply_direction( )メソッドは、指定された方向の1次元差分演算子を使います。
QuantLibライブラリーはその他の Operatorも用意しているので、興味があれば、チェックしてみて下さい。その殆どが、同じ構成である事が判ります (訳注:すなわち複数の方向へのOperatorの線形結合の形を取っている)。例えば、FdmHestonOpクラスは、各変数用に Helper となる Operatorを実装し、それをとりまとめてひとつの Operatorを構築しています。また、FdmHestonHullWhiteOpクラスは、3次元の差分演算子の例として、内容を吟味してみて下さい。
[1] C. S. L. de Graaf. Finite Difference Methods in Derivatives Pricing under Stochastic Volatility Models. Master’s thesis, Mathematisch Instituut, Universiteit Leiden, 2012.
<ライセンス表示>
QuantLibのソースコードを使う場合は、ライセンス表示とDisclaimerの表示が義務付けられているので、添付します。 ライセンス