2. "Implementing QuantLib"の和訳

Appendix A   Odds and Ends 周辺の話題

A.3   Finance-related Class : 金融に関連する概念を対象とするクラス 

QuantLibライブラリーが対象としている領域からすれば、金融に関する概念をオブジェクトモデル化したクラスがいくつかあるのは当然です。その中のいくつかについて、このセクションで説明します。 

A.3.1   Market Quotes 市場データ

市場でクォートされている金融商品の価格をオブジェクトモデル化するには、少なくとも2通りの方法があります。ひとつは、市場価格を、Time-stamp(価格を取得した時価)と対応させた連続した価格の配列とし、配列の最後に直近の値に対応するようにしたものです。もうひとつは、現時点の価格をモデル化し、その値が価格変動に合わせ動的に動くようにオブジェクトモデル化したものです。 

いずれにモデル化の方法も有用であり、QuantLibライブラリーでは両方のオブジェクトモデルを実装しています。最初のモデルはTimeSeriesクラスに対応していますが、ここでは詳細な説明を控えます。このクラスは基本的に、日付と価格を対応づけて、そこから指定された日付に対応する価格を取りだすメソッドや、価格の配列を操作するIteratorが備わっています。しかし、このクラスはQuantLibライブラリーの中で、他のクラスで使われた形跡はありませんでした。 

もうひとつのモデルは、Quoteクラスです。その概要を下記 Listing A-3 に示します。 

Listing A-3 : Quoteクラスのインターフェース 

   class Quote : public virtual Observable {
      public:
        virtual ~Quote() {}
        virtual Real value() const = 0;
        virtual bool isValid() const = 0;
    }; 

ご覧の通り、インターフェースはスリムです。このクラスは Observableクラスから派生しており、値(市場価格)が変更になった場合は、この市場価格に依存している(registerされている) 他のオブジェクト(Observer)すべてに変更通知を送ります(訳注:Observableクラスで定義されている notifyObserver( )が呼び出される)。このクラスは isValid( )メソッドを宣言しており、保持する市場価格が有効範囲にあるかどうかを返します(価格が保持されていない、とか、価格が有効期限を過ぎているといったチェックをするのではありません)。また value()メソッドは、現時点の市場価格を返します。 

この2つのメソッドで、Quoteクラスが必要とされる機能が十分提供されています。市場価格に依存しているすべてのオブジェクトは(例えばChapter 2で説明したBootstrap Helperクラスなどは)、QuoteオブジェクトのHandle(訳注:メモリー管理を自動で行うポインターのポインター)を保持し、かつ自らをObserverとしてQuoteクラスにregister(登録)します。その仕組みにより、Observerオブジェクトは、いつでも最新の市場データにアクセスできます。 

QuantLibライブラリーでは、Quoteの派生クラスを数多く定義しており、ベースクラスのインターフェースを、そこで実装しています。その内のいくつかは、市場価格として他の Quoteオブジェクトから取得した価格を、加工してから返すものもあります。例えば、ImpliedStdDevQuoteクラスは、特定のオプション価格から Implied Volatility を計算し、それを返すようになっています。他には、他の(市場データ)オブジェクトに付随させて使うものがあります。ForwardValueQuoteオブジェクトは、金利の TermStructureオブジェクトを使って、先日付の金利インデックスの Fixing Rate を計算して返します。また、LastFixingQuoteオブジェクトは、Fixing Rateの時系列配列から最新の値を返します (訳注:このケースでは時々刻々変化する動的な市場価格というより、一日一回更新されるFixing Rateの情報から直近の情報を取りだしている)。 

 現時点では、外部データを情報源とする Quoteオブジェクトとして実装されているのはひとつしかありません。それが SimpleQuoteクラスであり、その実装内容を下記Listing A-4 に示します。 

Listing A-4 :SimpleQuoteクラスの実装内容 

    class SimpleQuote : public Quote {
      public:
        SimpleQuote(Real value = Null<Real>())
        : value_(value) {}
       Real value() const {
            QL_REQUIRE(isValid(), "invalid SimpleQuote");
            return value_;
        }
        bool isValid() const {
            return value_!=Null<Real>();
        }
        Real setValue(Real value) {
            Real diff = value-value_;
            if (diff != 0.0) {
                value_ = value;
                notifyObservers();
            }
            return diff;
        }
      private:
        Real value_;
    }; 

このクラスは、具体的な市場データからのデータフィードを実装していないので、極めてシンプルです(訳注:実際に外部データからのデータフィードを使う場合、データ供給元が提供するAPIを使うことになります。その際、様々なチェックや、エラー処理の機能を備える必要があります)。 

ここでは単に、新しい市場価格を、適切なメソッドを呼び出してマニュアルで更新するようになっています。直近の市場価格は(もしそれが無ければ Null(Real>( )が)、メンバー変数 value_ に格納されます。ベースクラスのインターフェースである value( )メソッドが実装されており、格納された値を返すようになっています。また isValid( )メソッドは、その値が null値か否かをチェックします。新しい市場価格を取りこむメソッドは setValue( )で、その引数で新しい市場価格が供給されると、それが直近の価格と異なっている場合は、(新しい価格を value_ にコピーした上で) Observerオブジェクトに通知し、直近価格との価格差を返します。(注:価格差を返すようにしたのは、C や C++ の設計慣行からすれば(通常は更新前の値を返す)、おかしいかもしれません。) 

最後にいくつかの注意点を述べて、このセクションを終わりにしたいと思います。

まずひとつ目は、Quoteクラスが対象とする市場価格のデータ型は Real(訳注:実体はdouble) のみです。これまでの所、それで問題になった事はありませんが、もし他の型も使えるようにする為に Quoteクラスを Templateクラスとして定義し直そうとしても、もはや手遅れです。従って、この点についての変更が行われる事は無いでしょう。 

ふたつ目は、当初の目論見は、このQuoteクラスを、実際の市場価格データフィードとのアダプターとして使い、(データ供給元が異なる場合に)異なるAPI(Application Program Interface)の呼び出しに応じた実装をして、全体として統一的に市場価格をオブジェクト化しようというものでした。しかし、これまでの所、(QuantLibライブラリーのユーザーの中で)誰もそのような実装を行っていないようです。それに一番近い使い方は、Excelに取り込んだ市場価格のデータフィードを使ってSimpleQuoteインスタンスの値を設定するくらいです。 

最後の注意点は少し長いですが、SimpleQuoteクラスのインターフェースは将来、より先進的な使い方をする為に修正される可能性があります。複数の市場価格を一纏めにして、新しい値をセットする場合(例えば、イールドカーブを Bootstrapping する為に使われる金利の Quoteの配列など)、それぞれの金利 Quoteが更新される都度ではなく、その Quoteすべてが更新されてから、notifyObserver( )を一括して発信する方が効率的です。なぜなら、ObservableからObserverへ価格更新の通知と変更のプロセスは、結構時間がかかる為です。この変更は(setValue( )メソッドに追加で silent というパラメータを渡し、それがtrueの場合は、notifyObserver( )を呼び出さないようにする変更は) 既にLibraryの開発者によって実装済で、いずれQuantLibライブラリーに加えられるでしょう。 

A.3.2   Interest Rate : 金利

InterestRateクラス(下記 Listing A-5参照)は一般的な金利計算のロジックをカプセル化したものです。このオブジェクトのインスタンスは、4つの引数、すなわち ①利率(Rate), ②日数計算方法(Day Counter), ③複利の方法 (Compounding method), ④複利の回数(Compounding Frequency)、を使って生成されます(但し、金利の複利の回数にかかわらず、利率(Rate)の表示は、年率換算後の値を使います)。これにより、使用される「金利」の、具体的内容が定まります。例えば、5%、Actual / 365、Continuously Compounded(連続複利)とか、2.5%、Actual /360、 Semiannually compounded(年2回複利)、とかです。但し、複利の回数については必ずしも常に必要とされる情報ではありません。これについては後で説明します。  

Listing A-5:InterestRateクラスの概要 

    enum Compounding { Simple,              // 1+rT
	                  Compounded,          // (1+r)^T
                               Continuous,          // e^{rT}
                               SimpleThenCompounded
    };

    class InterestRate {
      public:
        InterestRate(Rate r,
                     const DayCounter&,
                     Compounding,
                     Frequency);
        // inspectors
        Rate rate() const;
        const DayCounter& dayCounter();
        Compounding compounding() const;
        Frequency frequency() const;
        // automatic conversion
        operator Rate() const;
        // implied discount factor and compounding after a given time
        // (or between two given dates)
        DiscountFactor discountFactor(Time t) const;
        DiscountFactor discountFactor(const Date& d1, const Date& d2) const;
        Real compoundFactor(Time t) const;
        Real compoundFactor(const Date& d1, const Date& d2) const;
        // other calculations
        static InterestRate impliedRate(Real compound,
                                        const DayCounter&,
                                        Compounding,
                                        Frequency,
                                        Time t);
        ... // same with dates
        InterestRate equivalentRate(Compounding,
                                    Frequency,
                                    Time t) const;
        ... // same with dates
    }; 

このクラスは、いくつかの自明なインスペクターの他に、数種類のメソッドを提供しています。まず Rate( )オペレーターですが、変数の型を(InterestRate型から) Rate型すなわち“double”に変換します。後で考えてみると、これは少しリスクのあるやり方でした。型変換された後の値は、「日数計算方法」や「複利の回数」など、金利に関する重要な情報を失ってしまうからです。そうすると、例えば連続複利利回りの情報が欲しかったにも拘らず、単純複利の情報がそのまま使われてしまうような事が起こりうるかもしれません。この型変換のオペレーターは、(QuantLibの開発段階で)InterestRateクラスを導入した時、backward compatibility(古いバージョンのプログラムを使用した際に問題を起こさせない)の為に加えられたものでした。将来、どこかでこのオペレーターを取り除こうと考えていますが、ユーザーにとって、その方がより安全と思われる時にやりたいと考えています。 

(注:安全性については、中心となっているソースコードの開発者にとって、意見が分かれるものです。ある者は、ベビーシッターのように、何なから何まで面倒をみないといけないと考えているし、ある者は、少しの遺産を与えて子を世に送り出す親のように、ユーザーがある程度自分で面倒を見るべきと考えています。) 

その他のメソッドはすべて、(金利に関する)基本的な計算を提供しています。まず compoundFactor( )メソッドですが、与えられた期間 t (あるいは2つの日付 d1 と d2 の間の期間として与えられる)において、所与の金利と複利の回数に従って、元本 1 に対する倍率を返します。 discountFactor( )メソッドは、所与の期間、金利、複利の回数に従って、割引率(compoundFactorの逆数)を返します。impliedRate( ) メソッドは、所与の Compound Factor、日数計算方法、複利の回数、期間、から年率ベースの利回りを返します。 equivalentRate( ) メソッドは、別の複利回数で計算した場合の利回りを返します。 

InterestRateクラスのコンストラクターと同様に、上記のメソッドのいくつかは複利の回数(Frequency)を引数として取ります。しかし、場合によってはこの情報は不要です。そういう時のために、Frequencyを定義している enum には、NoFrequencyの項目も含めています。 

この方法は Bugの原因になりやすいかもしれません。複利回数の情報(Frequency)は、複利の方法の情報(Compounding)と一体となって持たせて、それと関係のない方法(例えば、単利や連続複利)では、そういった情報を一切持たせないのが理想かもしれません。もし C++ が以下のような Syntax(構文)を持つなら、うまくいくのですが、残念ながらそうなっていません。 

    enum Compounding { Simple,
                               Compounded(Frequency),
                               Continuous,
                               SimpleThenCompounded(Frequency)
    }; 

このSyntaxは、関数言語における代数データ型や、Scalaにおける case classesのようなものですが C++ では選択肢にありません。(注:両方とも、c++ の switch構文をより強力にした、オブジェクトにおける型を適合させる機能を持つ。興味のある方はそちらの方を一読下さい)  

C++ で同じような機能を持たせるためには、Strategyパターンを使い、Compoundingクラスの階層を作れば出来なくはありません。しかし、それはやりすぎのような気がしますので、enumを使った構文のままにし、若干の問題を甘受することにしました。 

A.3.3   Index

Indexクラスは、InstrumentやTermStructureといったクラスと同様、非常に広い概念をカバーします。このクラスは、例えば、金利インデックス、インフレーションインデックス、株価インデックス等々、皆さんご存知の概念をカバーしています。 

言わずもがなですが、モデル化された概念は非常に多くのものをカバーしている為、共通となるインターフェースは極めて限られたものになります。次の Listing A-5 に示す通り、このベースクラスが提供しているメソッド群はすべて、インデックスの決定方法に関連するものです。 

Listing A-5: Indexクラスのインターフェース 

    class Index : public Observable {
      public:
        virtual ~Index() {}
        virtual std::string name() const = 0;
        virtual Calendar fixingCalendar() const = 0;
        virtual bool isValidFixingDate(const Date& fixingDate) const = 0;
        virtual Real fixing(const Date& fixingDate,
                            bool forecastTodaysFixing = false) const = 0;
        virtual void addFixing(const Date& fixingDate,
                               Real fixing,
                               bool forceOverwrite = false);
        void clearFixings();
    }; 

isValidFixingDate( )メソッドは、引数として取った日付が“インデックス決定日”に該当するか否か(あるいは将来そうなるのか)を示します。また、fixingCalendar( )は、インデックス決定日を決めるカレンダーを返し、fixing( )メソッドは、過去あるいは将来のインデックス決定日におけるインデックスの値を返します。その他のメソッドは、過去に決定したインデックス値に関するものです。name( )メソッドは各インデックスを特定するインデックス名を返します。このメソッドは通常、保存されている様々なIndexデータから該当のIndexデータを取りだす為に使われます。addFixing( )メソッドは、値が決定されたインデックス値を保存します(設定する値が複数でも、オーバーロード関数で対応しています)。またclearFixing( )メソッドは、保存されているインデックスのデータを消去します。 

インデックスのデータを保存・消去すると言ってますが、Indexクラスのメンバー変数の中にデータを格納する変数が見当たりませんね? 実は、過去のインデックス値はIndexインスタンス毎に持つのでは無く、(その外に保存して)幅広くシェアして使いたいという要請に対応しました。仮に、6カ月Euriborインデックスのインスタンスが、ある日付のインデックス決定値を格納しようとする場合、その値は、addFixing( )を呼び出したインスタンスからだけではなく、同じIndexのすべてのインスタンスからも見えるようにしたいと考えました。 

(注:ここで同じIndexのインスタンスとは、同じクラスのIndexでは無く、全く同一のIndexのインスタンスを意味します。例えばUSDLibor(3*Months)とUSDLibor(6*Months)は全く同じインデックスとは見做しません。しかし、2つの異なるUSDLibor(3*Months)インスタンスは、同一のインデックスインスタンスと見做します。)  

その方法は、Singletonクラスの派生クラスとしてIndexManagerクラスを定義し、カーテンの後ろに隠しながら、それを使う事にしました。少し問題含みと思われますか? 確かにそうですが、それは、すべてのSingletonクラスがそうだからです。代替策としては、インデックス値を格納する為に、各派生クラスにstaticなクラスをメンバー変数として持たせる方法があります(しかし、それは、Singletonと同様にデータの同一性原則に反します)。いずれにしても、この件についてはQuantLibライブラリーの次の大きな改修時に再検討しなければなりません。(注:これまでの修正実績からすれば、次の大きなバージョンアップは2025年頃かも知れません。いいえ、冗談です。でもそうなるかも) 

今の所、Indexベースクラスは Observerからは派生していません。しかし、いくつかの派生クラスではそうなっています(別に驚くにはあたりません。将来の Fixing(インデックス決定値)を予想する場合は、殆ど常に何等かの市場価格の Quoteを参照するからです)。この点については、最初から決めた設計方法では無く、プログラムコードの開発を進めていくにつれて、自然とそうなって行ったもので、将来のバージョンで変更するかも知れません。しかし、Indexベースクラスを仮に Observerクラスから派生させたとしても、派生クラスにおいて発生する若干の重複を避ける事はできません。その理由については、もう少し詳しく説明する必要があるでしょう。 

既に述べた通り、インデックスのfixing値が更新されるケースは2通り考えられます。ひとつは、将来の fixing値を予想する際に、データソースとなる Observable を参照する場合です。この場合、Indexインスタンスは、自らをその Observable に登録すれば済む話です(これは、各派生クラスレベルで為されます。それぞれの派生クラスは、依存するデータソースが異なるからです)。もうひとつは、新しい fixing値が決まって利用できる状態になった場合で、これは取り扱いが少し厄介になります。新しい fixing値は、個別の Indexインスタンスから addFixing( )メソッドを呼び出す事によって、(IndexManagerに)格納されます。そうすると、(addFixing( )を呼び出したインスタンスにとっては) 外部からデータ変更の通知は必要なさそうです。また、新しい fixing値は、(そのIndexインスタンスに登録されている)Observerインスタンスに通知するだけでよさそうです。しかし、そうではありません。既に述べた通り、fixing値は共有されています。もし本日の3Month Euriborの fixing値が決まり、それを(IndexManagerに)格納したら、そのデータは、同じ3カ月Euriborのすべてのインスタンスからも使える様になります。従って、そのすべてのインスタンスは、fixing値が決まった事を知る必要があります。さらに、Instruments や TermStructureのインスタンスは、そういった個別の Indexインスタンスに登録されているでしょうから、新しい Fixing値が決まれば、それを知る必要があります。即ち、各Indexインスタンスは fixing値が新しく決まった事を、それぞれに登録されているすべてのObserverに通知しなければなりません。 

これの解決策は、同じ種類の Indexインスタンスは、Shared Object(具体的にはすべての fixing値を格納している IndexManagerクラスの Singleton) を経由して、お互いに、連絡を取り合えるようにする事です。IndexManagerクラスは、各 fixing値(の時系列データ)に、特定のインデックス名を示す名札(Tag)を付けます。そして、その Tag に対応する ObservableValueクラスのインスタンスを生成して、Tagと同一名の、fixing値が加えられた場合、それを通知する仕組みを提供しています。(このクラスについては、後程この Appendixの中で説明しますので、ここでは詳細な説明は必要ないでしょう) 

これで、すべての部品が揃いました。Indexインスタンスは、生成された後、IndexManagerインスタンスに対し、name( )メソッドで返されるインデックスTag に対応する Shared Observableを要求します。その後、例えば 6month Euribor のインスタンスから addFixing( )メソッドが呼び出された場合、新しいFixing値がIndexManagerに格納され、かつ Shared Observableからすべての同じIndexインスタンス(6month Euriborインスタンス)に対して、通知が発せられます。それで、同じインデックスインスタンス全体がうまくいくという訳です。 

しかし、C++は、この歯車にモンキーレンチを入れて、邪魔しにきます。上記の通りであれば、Indexのコンストラクターから次のようなメソッドを呼び出すだけで済ませようという誘惑にかられます。 

    registerWith(IndexManager::instance().notifier(name())); 

しかし、この方法はうまく機能しません。理由は、ベースクラスのコンストラクターにより生成された仮想関数 name( )メソッドは polymorphicではないからです。

(注:C++の問題の多い部分にあまり詳しくない読者の為に説明すると、ベースクラスのコンストラクターが呼び出された場合、その段階では未だ派生クラスのメンバー変数は生成されていません。個別の派生クラスに特有の動作が(訳注:ここでは派生クラスで実装されるname()メソッド)、未だ生成されていない派生クラスのメンバー変数(個別のIndex名)に依存するので、C++は派生クラスでの動作をあきらめ、ベースクラスで実装されている仮想関数を使おうとします) 

この理由から、数段落前に指摘したプログラムコードの重複が発生します。すなわち、上記のメソッドの呼び出しは、派生クラスのコンストラクター毎に記述する必要があるのです。Indexベースクラスそのものは、実際の所(コンパイラーがデフォールトで提供しているもの以外は)コンストラクターを持っていません。 

Indexクラスの具体的な派生クラスでの実装例として、次のListing A-6にInterestRateIndexクラスを示します。 

Listing A-6 InterestRateIndexクラスの概要 

    class InterestRateIndex : public Index, public Observer {
      public:
        InterestRateIndex(const std::string& familyName,
                          const Period& tenor,
                          Natural settlementDays,
                          const Currency& currency,
                          const Calendar& fixingCalendar,
                          const DayCounter& dayCounter);
        : familyName_(familyName), tenor_(tenor), ... {
            registerWith(Settings::instance().evaluationDate());
            registerWith(IndexManager::instance().notifier(name()));
        }

        std::string name() const;
        Calendar fixingCalendar() const;
        bool isValidFixingDate(const Date& fixingDate) const {
            return fixingCalendar().isBusinessDay(fixingDate);
        }
        Rate fixing(const Date& fixingDate, bool forecastTodaysFixing = false) const;
        void update() { notifyObservers(); }

        std::string familyName() const;
        Period tenor() const;
        ... // other inspectors

        Date fixingDate(const Date& valueDate) const;
        virtual Date valueDate(const Date& fixingDate) const;
        virtual Date maturityDate(const Date& valueDate) const = 0;
      protected:
        virtual Rate forecastFixing(const Date& fixingDate) const = 0;
        std::string familyName_;
        Period tenor_;
        Natural fixingDays_;
        Calendar fixingCalendar_;
        Currency currency_;
        DayCounter dayCounter_;
    };
    std::string InterestRateIndex::name() const {
        std::ostringstream out;
        out << familyName_;
        if (tenor_ == 1*Days) {
            if (fixingDays_==0) out << "ON";
            else if (fixingDays_==1) out << "TN";
            else if (fixingDays_==2) out << "SN";
            else out << io::short_period(tenor_);
        } else {
            out << io::short_period(tenor_);
        }
        out << " " << dayCounter_.name();
        return out.str();
    }

    Rate InterestRateIndex::fixing(
                           const Date& d,
                           bool forecastTodaysFixing) const {
        QL_REQUIRE(isValidFixingDate(d), ...);
        Date today = Settings::instance().evaluationDate();
        if (d < today) {
            Rate pastFixing = IndexManager::instance().getHistory(name())[d];
            QL_REQUIRE(pastFixing != Null<Real>(), ...);
            return pastFixing;
        }
        if (d == today && !forecastTodaysFixing) {
            Rate pastFixing = ...;
            if (pastFixing != Null<Real>())
                return pastFixing;
        }
        return forecastFixing(d);
    }

    Date InterestRateIndex::valueDate(const Date& d) const {
        QL_REQUIRE(isValidFixingDate(d) ...);
        return fixingCalendar().advance(d, fixingDays_, Days);
    } 

予想された通り、このクラスは、Indexベースクラスから継承した動作以外に、かなりの数の個別の動作を定義しています。そもそも、このクラスは Observerクラスからも派生しています(Indexベースクラスはそうではありません)。InterestRateIndexクラスのコンストラクターは、金利インデックスを特定するのに必要なデータを、引数として取ります。すなわち、①“Euribor”といったファミリー名、②同じファミリーの中の個別の期間(3か月とか6か月)、③Fixing日から決済日までの期間、④対象通貨、⑤Fixing日を決めるカレンダー、⑥(経過利息計算に必要な)日数計算方法などです。 

引数で渡されたデータは、もちろん対応するメンバー変数に格納されます。そして、自分自身のインスタンスを、2つの Observableインスタンスに登録します。一つは、グローバル変数である EvaluationDateインスタンスです。これが必要な理由は、このIndexインスタンスが、本日の Fixing値を聞かれた時に起動される、“日付に関する特別な動作”に必要だからです。これについてはすぐ後で説明します。ふたつ目の Observableは、IndexManagerの中に格納されているインスタンスで(訳注:先ほど説明された ObservableValue のこと)、新しい Fixing値が決まった際、そこから各Observerに通知が発せられます。この InterestRateIndex派生クラスの階層で、もう Observable となる IndexManager内のインスタンスを特定する事が可能です。InterestRateIndexクラスは金利インデックスを特定する情報をすべて持っており、このクラスで実装された name( )メソッドを呼び出す事が出来ます。それは同時に、InterestRateIndexクラスからさらに派生するクラスで、name( )メソッドをオーバーライドしてはいけない事を意味します。さらに派生するクラスでオーバーライドされたメソッドは、継承元のコンストラクターが呼び出された段階では呼び出す事は出来ない為(理由は少し前に説明しました)、その派生クラスが間違ったIndexManagerインスタンスに登録されてしまう事になります。残念ながら、C++ ではこれを防ぐ方法がありません (C++では、Javaの”final”やC#の“sealed”に相当する宣言文が無いからです)。代替策として、InterestRateIndexクラスから派生したクラスでも、必ずIndexManager(内の同一InterestRateIndexを格納するObservable)に登録するメソッドを用意する方法です。しかし、それを強制する事はできませんし、Errorが起こりにくいものの、よりめんどくさくなります。 

InterestRateIndexクラスで定義されている他のメソッドは、それぞれ独自の目的を持っています。いくつかは、ベースクラスである Index と Observer で宣言されたインターフェースを実装しています。最もシンプルなのは update( )メソッドで、Observableからデータ更新の通知を受け取ると、それを(自分が登録している)他のObserverに転送するだけです。 fixingCalendar( )メソッドは、メンバー変数に保持されているCalendarインスタンスを返すだけで、isValidFixingDate( )メソッドは、引数として渡された日付が Fixing用の Calendarで営業日になっているかどうかチェックします。 

name( )メソッドは、少し複雑です。このメソッドは、インデックスのファミリー名、期間、日数計算方法の情報を繋ぎ合わせて、“Euribor 6M Act/360”とか、”USD Libor 3M Act/360”といった個別の金利インデックスの名前を作り、それを返します。Overnight、Tomorrow-Next、Spot-Nextといった特殊な期間については、それぞれに対応する略称を使って、そのインデックス名を作ります。 

fixing( )メソッドが、このクラスのメインの動作を担います。まず、引数として渡された日付けが、Fixing日に該当するか否かチェックし、該当しない場合は例外処理に移ります。次に、その日付けが本日と一致するかどうかチェックし、仮にそれが過去の日付の場合は、Fixing値を Singletonである IndexManagerインスタンスから探し、もしデータがそこに無ければ例外処理に移ります(過去の Fixing値を予想する術は無いからです)。もし日付が本日の日付の場合、最初に IndexManager に Fixing値を探しに行き、値があればそれを返しますが、そこに無い場合はまだ Fixing値が決まっていないという事になります。その場合、(あるいは日付が将来の日付の場合)、Indexインスタンスは、Fixingの予想値を計算します。その計算は forecastFixing( )メソッドを呼び出して行われますが、このメソッドは InterestRateIndexクラスの中では純粋仮想関数として宣言されているので、そこから継承する派生クラスで実装される必要があります。この fixing( )メソッドが動作するロジックの為に、先ほど述べたように、このインスタンスを EvaluationDateインスタンスに登録する必要があったのです。fixing( )の中での動作は、引数で渡された日が本日かどうかのチェックを含んでおり、日付が移った場合は、それを通知してもらう必要があるのです。 

最後に、InterestRateIndexクラスは Indexクラスから継承していないメソッドも幾つか定義しています。そのほとんどがインスペクター関数で、メンバー変数に保持されているインデックスのファミリー名や期間を返します。それ以外のメソッドは、主に日数計算を行っています。valueDate( )メソッドは、Fixing日に対応する金利期間のスタート日を返します(例えば、LIBOR金利が適用になるロンドンインターバンク市場の預金は、大半の通貨において、Fixing日の2営業日後がそれに該当します)。maturityDate( )メソッドは、引数に Value Date(たった今説明したもの)を取り、金利期間の最終日(預金の最終日)を返します。fixingDate( )メソッドは valueDate( )メソッドの逆関数となる動作で、Value Dateを引数で取り、Fixing日を返します。これらのメソッドの内のいくつかは、仮想関数であり、派生クラスでオーバーライド出来ます。例えば、valueDate( )メソッドのデフォールトの動作は、メンバー変数に持つFixing Days(Fixing日からValue Dateまでの日数)の情報と適用になるカレンダーを基に、その営業日数分だけカレンダーを前に進めた日を返します。しかし、LIBORインデックスでは、まず Londonのカレンダーを使ってそれを行った後、インデックスの対象通貨を基に、その通貨に対応するカレンダーを使って再調整をする必要があるので、通貨によっては、その部分を派生クラスでオーバーライドする必要があります。ところが、何らかの理由で、fixingDate( )メソッドは仮想関数として宣言されていませんでした。おそらく、見逃していたのだと思いますが、将来リリースするバージョンで修正しなければなりません。 

< Aside:How much generalization? どの程度まで汎用化する必要があるのか? >

InterestRateIndexクラスのいくつかのメソッドは、明らかに、LIBORインデックスを想定して作られています。なぜなら、QuantLibライブラリーで最初に作られたIndexクラスがLIBORインデックスだったからです。その為、このクラスの汎用性が若干犠牲になっています。例えば、5年と10年のスワップ金利の金利差を金利インデックスとして取り扱おうとした場合、それをベースクラスのインターフェースや、tenor( )のようなメソッドにフィットさせるのは大変な作業になるでしょう。しかし、一方で、そのような例を具体的に実装して試してみる事なく、Indexクラスのインターフェースをより汎用的に書き換えるのは、賢明ではありません。2つの金利インデックスの“スプレッド”は(単なる金利差であり、インデックスでは無いと考えられるので)、Indexクラスの汎用化の対象とすべきではないでしょう。 

 

A.3.4  ExerciseクラスとPayoffクラス

このセクションの最後に、特定の Instrumentsクラスの定義の中で使われているその商品用の特別なクラスについて説明します。 

最初に、下記 Listing A-7に示すExerciseクラスからです。 

Listing A-7: Exerciseクラス、およびその派生クラスのインターフェース  

   class Exercise {
	public:
	  enum Type {American, Bermudan, European};
	  explicit Exercise(Type type);
	  virtual ~Exercise();
	  Type type() const;
	  Date date(Size index) const;
	  const std::vector<Date>& dates() const;
	  Date lastDate() const;
	protected:
	  std::vector<Date> dates_;
	  Type type_;
   };

   class EarlyExercise : public Exercise {
	public:
	  EarlyExercise(Type type, bool payoffAtExpiry = false);
	  bool payoffAtExpiry() const;
   };

   class AmericanExercise : public EarlyExercise {
	public:
	  AmericanExercise(const Date& earliestDate,
			const Date& latestDate,
			bool payoffAtExpiry = false);
   };

   class BermudanExercise : public EarlyExercise {
	public:
	  BermudanExercise(const std::vector<Date>& dates, bool payoffAtExpiry = false);
   };

   class EuropeanExercise : public Exercise {
	public:
	  EuropeanExercise(const Date& date);
   }; 

皆さん予想されていたでしょうが、ベースクラスは、ひとつあるいは複数の行使日の情報を取り出すメソッドを宣言しています。見ての通り、そのメソッドは複数あります。dates( )メソッドは、行使日の配列を返します。また date( )メソッドは、引数で指定された配列インデックスに該当する行使日を(行使日の配列から抜き出して)返します。便利なメソッドである lastDate( )は、ご想像の通り、最終行使日を返します。従って、若干の機能の重複があります。(注:カプセル化が好きな人にとっては、行使日の配列を返すメソッドより、特定の配列インデックスを指定して、それに該当する行使日を返すメソッドの方を好まれるでしょう。配列を返すメソッドは、カプセル化されたデータの中から必要とされる以上のものを外に出すことになるからです。)  

また、type( )メソッドが定義されていますが、それをみて、ちょっと頭をかかえてしまいました。このメソッドは、クラス内で宣言された、Typeという名前の enumリスト(具体的には、European、Bermudan、Americanという行使タイプ)から、どれかをピックアップするものです。それ自体はおかしくありません。しかし、その次にあるクラス群の宣言は、このメソッドの趣旨に反するものです。すなわち、このクラスから派生させて、AmericanExercise、BermudanExercise、EuropeanExerciseクラスを宣言してしましました。Exerciseベースクラスで仮想デストラクターを宣言しているのを見ると、新しい行使タイプを定義したい場合は、継承を使う事を想定していたのでしょう。しかし一方で、ベースクラスで行使Typeをenumで宣言したのは、その想定に反しています。なぜなら、新しい行使タイプを派生クラスで追加した場合、ベースクラスの enumリストも追加しなければならないからです。継承を想定していたもうひとつの証拠は、QuantLibの中のあちこちで使われているイデオムを見ると、Exerciseベースクラスのインスタンスを生成し、そのスマートポインターを使いまわしている事です。一方で、継承の想定に反しているのは、ベースクラスでは、デストラクター以外は仮想関数を宣言しておらず、派生クラスで追加された動作(メソッド)は、すべてベースクラスのメンバー変数を使っている事です。要するに、このコードを書いた当時、我々は(現在の私よりも)もっと頭の中が混乱していたのでしょう。 

もしこれを今書き直すとしたら、おそらく enumリストを残して、Exerciseベースクラス自体を具体的なクラス(訳注:仮想関数を持たず、それ自体のインスタンスを生成して使えるクラス) にしたでしょう。その場合、派生クラスを作ったとしても、それはベースクラスのインスタンスを安全に切り出して使いまわしができるようなオブジェクトにするか、あるいは Exerciseインスタンスを直接返すような関数オブジェクトにするかでしょう。こうする事で、オブジェクト指向を信奉するプログラマーを不機嫌にさせるかも知れませんが、QuantLibのあちこちで、行使タイプをチェックする必要がある場所があり、そこで型変換や Visitorパターンを使う事と比べても、enumリストを使うのは現実的な対応方法でしょう。派生クラスで、特別な動作を実装していないのを見ても、これでいいのではないかと思います。 

このSectionを書いていて、“オプション行使”は、Chapter-IVで解説した Eventクラスに含めても良かったのではないかと思えてきました。しかし、Exerciseクラスがオブジェクトモデル化している概念は、必ずしもそれとマッチしません。ヨーロピアンタイプの Exerciseであれば、それを Eventクラスのインスタンスとしてもいいでしょう。バーミューダタイプの Exerciseであれば、Eventの配列とすればいいでしょう。しかし、アメリカンタイプでは、行使日の“範囲”の概念をモデル化しなければなりません。そうすると、Exerciseクラスで用意されているインターフェースの意味も変わってきます。なぜなら、dates( )メソッドは、行使可能日の配列を返すのではなく、単に行使可能期間の最初の日と最後の日を返すだけです。よく起こる事ですが、一見簡単そうにみえても、注意深く見てみるとモデル化が難しいケースがあるものです。 

*********************************************************************************** 

次に、Payoffクラスを見てみます。下記 Listing A-8に、そのインターフェースといくつかの派生クラスを示します。 

Listing A-8:Interface of the Payoff class and a few derived classes. 

   class Payoff : std::unary_function<Real,Real> {
	public:
	  virtual ~Payoff() {}
	  virtual std::string name() const = 0;
	  virtual std::string description() const = 0;
	  virtual Real operator()(Real price) const = 0;
	  virtual void accept(AcyclicVisitor&);
   };
   class TypePayoff : public Payoff {
	public:
	  Option::Type optionType() const;
	protected:
	  TypePayoff(Option::Type type);
   };
   class FloatingTypePayoff : public TypePayoff {
	public:
	  FloatingTypePayoff(Option::Type type);
	  Real operator()(Real price) const;
	  // more Payoff interface
   };
   class StrikedTypePayoff : public TypePayoff {
	public:
	  Real strike() const;
	  // more Payoff interface
	protected:
	  StrikedTypePayoff(Option::Type type,Real strike);
   };
   class PlainVanillaPayoff : public StrikedTypePayoff {
	public:
	  PlainVanillaPayoff(Option::Type type, Real strike);
	  Real operator()(Real price) const;
	  // more Payoff interface
   }; 

このクラスのインターフェースの内、operator( )は、引数として対象資産価格を受取り、それに対応する行使価値 (payoff) を返します。また accept( )は、Visitorパターン用のメソッドです。その他にインスペクター関数が2種類あり (name( )とdescription( )メソッド)、いずれもレポート用に名前を返しますが、片方は余分でした。 

開発時にこのクラスをモデル化するにあたって、我々はこのオブジェクトの使用方法について十分把握できていませんでした。その結果、残念なことに、出来上がったインターフェースは、あまり応用が利きません。最も大きな問題は、operator( )メソッドが、引数としてひとつの対象資産価格しか取れないという点です。(注:この点は、対象資産がひとつであっても、複数の Fixing値でPayoffが決まる場合でも問題となります。) 

もうひとつの大きな問題は、“継承”に頼りすぎている事です。例えば、Payoffの派生クラスとして TypePayoff を作り、そこで行使タイプ (CallまたはPutを指定していますが、それだけだと制約的かも知れません) と、それに対応するインスペクター関数を加えています。さらにそこから StrikedTypePayoffクラスを派生させ、行使価格の情報を加えて、そこからさらに PlainVanillaPayoffクラスを派生させ、最終的にそれがシンプルな Call または Put の Payoff の具体的クラスになります。ここまでで、ベースクラスから3段階もの階層になっており、シンプルな Payoff用のオブジェクトモデルとしては、階層化のやりすぎでしょう。今からQuantLibを使って、教科書的なオプションのプログラムコードを学ぼうとしている人にとっては、非常に解りにくくなっています。 

もうひとつ、我々がやってしまった失策は、OptionクラスにPayoffインスタンスへのポインターをメンバー変数として持たせた事です。当然、そのポインター(が示すインスタンス)にはPayoffの情報が含まれているものと想定しての事です。その結果、FloatingTypePayoffクラスのようなもの(上記Listingに記述されています)が出来上がってしまいました。このクラスは、Floating Look-Back Optionの実装で使われ、オプションのタイプ(CallかPutか)の情報を保持します。しかし、このオプションの行使価格は、オプション満期時に決まるので、その時点までPayoff(行使価値)を計算できず、従って、payoff()インターフェースを実装する事が出来ません。その結果、我々はこのクラスのoperator( )メソッドが呼び出された場合、例外処理に飛ぶようにしました。この場合、あえてpayoff値を計算させず、ルックバックオプションというタイプ名を返す事もできたでしょう。それは即ち、ベースのオプションクラスが、この時点でのpayoff値を期待していない場合だけ可能です。次に、プログラムコードを見直す時に、(改良すべきことを)覚えておかなければなりません。 

 

 

<ライセンス表示>

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

目次

Page Top に戻る

// // //