2. "Implementing QuantLib"の和訳

Chapter-VII The Tree Framework: Tree を使った価格モデルのフレームワーク

7.3   Tree をベースにした価格エンジン

7.3.1   具体例: コールオプション付き固定金利債

具体例として、コールオプション付きの固定金利債で使われる、Treeベースの Pricing Engine の実装方法について概観します。簡潔にする為、CallableBondクラス自体の説明は飛ばします。(注:正確に言えば、クラス名は CallableFixedRateBond とすべきでしたが、その名前もすぐに古臭くなるでしょうから、ここでは短いクラス名を使う事をお許し下さい。) その代わり、PricingEngine の内部クラスである argumentsクラスと resultsクラスについて説明します。この内部クラスは PricingEngine とのインターフェースの役割を担っており、その実装内容を、それに対応する engineクラスと供に、下記 Listing 7.17 で示します。もし CallableBondクラスの実装内容全体に興味があれば、QuantLibライブラリーの中の experimental のフォルダーの中を探してみてください。 

Listing 7.17: CallableBondクラスの内部クラスのインターフェース 

  class CallableBond::arguments : public PricingEngine::arguments {
      public:
        std::vector<Date> couponDates;
        std::vector<Real> couponAmounts;
        Date redemptionDate;
        Real redemptionAmount;
        std::vector<Callability::Type> callabilityTypes;
        std::vector<Date> callabilityDates;
        std::vector<Real> callabilityPrices;
        void validate() const;
    };

    class CallableBond::results : public Instrument::results {
      public:
        Real settlementValue;
    };

    class CallableBond::engine
        : public GenericEngine<CallableBond::arguments,
                               CallableBond::results> {}; 

さて、エンジンの部分に入っていきましょう。この商品の(離散的な)価格過程を記述する為に、DiscretizedAssetクラスを使う必要があります。Callable Bond用にそれを実装しているDiscretizedCallableBondクラスの実装内容を下記Listing 7.18に示します。 

Listing 7.18: DiscretizedCallableBondクラスの実装 

    class DiscretizedCallableBond : public DiscretizedAsset {
      public:
        DiscretizedCallableBond(const CallableBond::arguments& args,
                                const Date& referenceDate,
                                const DayCounter& dayCounter)
        : arguments_(args) {
            redemptionTime_ = dayCounter.yearFraction(referenceDate, args.redemptionDate);

            couponTimes_.resize(args.couponDates.size());
            for (Size i=0; i<couponTimes_.size(); ++i)
                couponTimes_[i] =
                    dayCounter.yearFraction(referenceDate, args.couponDates[i]);
            // same for callability times
        }
        std ::vector<Time> mandatoryTimes() const {
            std ::vector<Time> times;

            Time t = redemptionTime_;
            if (t >= 0.0)
                times.push_back(t);
            // also add non-negative coupon times and callability times

            return times;
        }
        void reset(Size size) {
            values_ = Array(size, arguments_.redemptionAmount);
            adjustValues();
        }
      protected:
        void preAdjustValuesImpl();
        void postAdjustValuesImpl();
      private:
        CallableBond ::arguments arguments_;
        Time redemptionTime_;
        std ::vector<Time> couponTimes_;
        std ::vector<Time> callabilityTimes_;
        void applyCallability(Size i);
        void addCoupon(Size i);
    };
    void DiscretizedCallableBond :: preAdjustValuesImpl() {
        for (Size i=0; i<callabilityTimes_.size(); i++) {
            Time t = callabilityTimes_[i];
            if (t >= 0.0 && isOnTime(t)) {
                applyCallability(i);
            }
        }
    }
    void DiscretizedCallableBond :: postAdjustValuesImpl() {
        for (Size i=0; i<couponTimes_.size(); i++) {
            Time t = couponTimes_[i];
            if (t >= 0.0 && isOnTime(t)) {
                addCoupon(i);
            }
        }
    }
    void DiscretizedCallableBond :: applyCallability(Size i) {
        switch (arguments_.callabilityTypes[i]) {
            case Callability :: Call:
                for (Size j=0; j<values_.size(); j++) {
                    values_[j] =std :: min(arguments_.callabilityPrices[i],values_[j]);
                }
            break;
            case Callability :: Put :
                for (Size j=0; j<values_.size(); j++) {
                    values_[j] =std::max(arguments_.callabilityPrices[i],values_[j]);
                }
            break;
            default:
                QL_FAIL("unknown callability type");
        }
    }
    void DiscretizedCallableBond :: addCoupon(Size i) {
        values_ += arguments_.couponAmounts[i];
	} 

プログラムコードが重たくなるのを避ける為、コンストラクターは、argumentsクラスのインスタンスを取り込み、それをメンバー変数に保存するだけです (訳注:価格計算に必要なデータリストを、それぞれ取りこむのでは無く、それをひとまとめにしたクラスを使ってコードを簡略化)。これによって、少なくとも3か所で、計算に必要なデータのリストをいちいちプログラムコードで書き上げる必要が無くなります。その3か所とは、メンバー変数の宣言、コンストラクター、およびユーザーが DiscretizedAssetクラスのインスタンスを生成する際のプログラムコードです。argumentsクラス以外に、Reference Date と Day Counter も、コンストラクターに引数として渡されます。これらのデータは、債券のキャッシュフロー発生日などを、現時点からの年数に換算するプログラムで使われます。(この年数に換算するプログラムコードはやや冗長で、おそらく何らかの抽象化が可能である事を示唆しています。しかし“time converter”のような名前のオブジェクトを作ったとしても、ややあいまいです。もしいいアイデアがあれば教え下さい。前からずっと気になっていた事ですから) 

次に来るのが、DiscretizedAssetクラスで宣言されたインターフェースの実装です。mandatoryTimes( )メソッドは、債券の償還日、利払い日、コールオプション日を集めて、その中からすでに過去となった日を取り除いた配列を返します。reset( )メソッドは (Lattice上の離散的な) 債券価格の配列のサイズを調整し、各要素に償還額を設定し、さらに必要な調整を行います。この調整が、このクラスの動作の中で興味深い部分です。 

このクラスは特定の商品に特化している為、他のクラスと合体して何かをすることは、ほぼありません。従って、この具体例では、調整作業が preAdjustValuesImpl( )かあるいは、postAdjustValuesImpl( )で行われるかは、大きな問題ではありません。(訳注:オプションと対象資産の関係のように、Nodeでの価値の決定が、対象資産のキャッシュフローイベントに影響を受ける場合、その調整を、オプション行使の前に行うか、後に行うかは、慎重に判断しなければなりません。ところが、このクラスでは、両者が一体化しているので、そういった調整のタイミングを考える必要が無いということ) しかし、説明をわかりやすくする為に、コールオプション行使の判断と、クーポン支払いを分けて、それぞれ pre- と post-adjustmentとしてマネージします。 

preAdjustValuesImpl( )は、コール日付を順番にループして行き、それぞれの日が Lattice上の時点と同じかどうかチェックし、同じ場合は applyCallability( )メソッドを呼び出します。postAdjustValuesImpl( )も、同じ動作を行いますが、チェックするのは利払い日であり、呼ぶ出されるメソッドは addCoupon( )になります。 

applyCallability( )メソッドは、オプションタイプのフラッグを引数として取り、そのフラッグをチェックし (コールオプションとプットオプションの両方に対応可能です)、各 Node において、オプション行使するかしないかの判断をした後、債券の価値をセットします。その判断の論理は単純明快です。各 Node において、行使しなかった場合の債券価値と、行使した場合のプレミアムの推定値が与えられ、(コールオプションを持つ)発行体はこの2つの価値の低い方を選び、(プットオプションを持つ)債券保有者は高い方を選びます。addCoupon( )メソッドは、ずっとシンプルで、各 Nodeの債券価値にクーポン額を加えるだけです。 

すでに気が付かれたと思いますが、このクラスは、コール行使日と利払い日が同じ日に発生すると仮定しています。仮に、行使日が利払い日より数日でも早ければ、このクラスは役に立ちません (その場合は、利払日にクーポン額が資産価格に足されて、その後で行使条件がチェックされます。[訳注:オプションが行使される場合、本来そのクーポン額は受け取れないはずですが、受取れるという前提でオプション行使の判断がなされてしまう])。もちろん、こういった事は十分起こり得る事なので(訳注: こういった条件のCallable Bondが存在するので)、きちんと考慮しておくべきです。今の所、QuantLibライブラリーの中では、この問題を避けて通っており、コールオプション行使日を (無理やり)調整して利払い日と同じ日に合わせています。別の少しましな方法は、まず、オプション行使により支払いの影響を受けるクーポン支払日を特定し、その場合、クーポン額を別の所に一旦コピーしておき、コール行使日まで遡及してコール行使の判断を行った後に、元のDiscretizedAssetの価格に戻すという操作を行えばいいでしょう。 

最後に、TreeCallableBondEngineクラスの実装内容を下記 Listing 7.19 に示します。コンストラクターは、引数として、①Lattice を提供する ShortRateModelインスタンス、②Latticeに設定すべきTime Stepの数、および③基準日と④日数計算方法(これは任意)を取り、それぞれメンバー変数に保存します。 

Listing 7.19: TreeCallableBondEngineクラスの実装内容の概要 

   class TreeCallableBondEngine : public CallableBond ::engine {
      public:
        TreeCallableBondEngine(
                       const Handle<ShortRateModel>& model,
                       const Size timeSteps,
                       const Date& referenceDate = Date(),
                       const DayCounter& dayCounter = DayCounter());
        : model_(model), timeSteps_(timeSteps),
          referenceDate_(referenceDate), dayCounter_(dayCounter) {
            registerWith(model_);
        }
        void calculate() const {
            Date referenceDate;
            DayCounter dayCounter;

            // try to extract the reference date and the day counter
            // from the model, use the stored ones otherwise.

            DiscretizedCallableBond bond(arguments_,
                                         referenceDate,
                                         dayCounter);

            std::vector<Time> times = bond.mandatoryTimes();
            TimeGrid grid(times.begin(), times.end(), timeSteps_);
            boost::shared_ptr<Lattice> lattice = model_->tree(grid);

            Time redemptionTime =
                dayCounter.yearFraction(referenceDate,
                                        arguments_.redemptionDate);
            bond.initialize(lattice, redemptionTime);
            bond.rollback(0.0);
            results_.value = bond.presentValue();
        }
    }; 

このクラスの動作は calculate( )メソッドがすべてです。このメソッドが呼び出される前に、PricingEngine で使う arguments データはすべて、Instrumentインスタンスによって設定済み、という前提です。価格計算には、他にも Reference Date (価格計算基準日)と Day Counter (日数計算方法のオブジェクト) が必要です。すべての ShortRateModel がその情報を提供できるとは限らないので、プログラムでは (ここではその退屈なプログラムコードの部分は省略していますが)、ShortRateModel を、それが提供できる特別なクラスに型変換して、取りだそうとします。仮に、それが出来なかった場合は、コンストラクターに任意変数として渡された価格計算基準日と日数計算方法を使います。 

その後、実際の計算が開始します。Pricing Engineは、まず DiscretizedCallableBondインスタンスを生成し、それを使って mandatoryTimes( )を呼び出し TimeGrid を構築します。その TimeGrid は ShortRateModelインスタンスに渡され、そのインスタンスは短期金利の拡散過程に対応した Latticeインスタンスを生成して返します。最後に残った作業は、Lattice上の債券償還時 (上記コードではそれを再計算していますが、mandatoryTimes の配列の中からその最大値を取りだして使う事も出来ます。) における離散的な債券価格の配列を初期化して、そこから現時点まで遡及計算をし、現在価値を算出して results_ に格納します。 

 

 

<ライセンス表示>

QuantLibのソースコードを使う場合は、ライセンス表示とDisclaimerの表示が義務付けられているので、添付します。   ライセンス

目次

Page Top に戻る

// // //