Appendix A: 周辺の話題

 QuantLibライブラリーを使うにあたって、これまでのChapterで、基本的な事項について説明して来ましたが、読みやすさを重視して、技術的な詳細について延々と述べる事を避けてきました。Douglas AdamsがHitchhiker三部作(銀河ヒッチハイクガイド)の、4番目の本の中で、次のように指摘しています。(注:3部作における4番目の本は変ですが、間違いではありません。そもそも5冊の本からなるのに3部作と呼ばれています。)

 「必要以上に詳細な説明は問題です。それは行動を前に進めません。詳細な説明は本を分厚くしますが、あなたが行きたい所にはたどり着かせないでしょう。」

 このAppendixは、そういった詳細な事項について、簡単な参考説明を提供しています。しかし、それらを網羅的かつ体系的に説明しようとするものではありません。もし、そのような参考文献が必要なら、QuantLibライブラリーの中にあるReference Manualを参照下さい。

基本的なデータ型

 QuantLibライブラリーのインターフェースは、(intfloatdoubleのような)既に組み込まれているデータ型を使っていません。その代わりに、typedefを使って、TimeRateInteger、やSizeといったQuantLibライブラリー特有のデータ型を定義しています。これらのデータ型は、すべて基本的なデータ型に対応付けされています。(以前、型のレンジチェックの機能まで付いたデータ型を定義しようか議論しましたが、それはあきらめました。)さらに、すべての浮動小数点型はRealと定義され、それはdoubleに対応しています。こうすれば、Realに対応するデータ型を定義し直すことで(訳注:例えばdoublelong doubleに定義し直して)、整合的にすべてのReal型変数の浮動小数点の精度を同時変更できます。

 原理的には、これにより、浮動小数点の数値精度をユーザーが選択できます。しかし、それが理由で、test-suiteの中で、Real型を、floatlong doubleで定義し直した場合に、変な計算結果が出るケースが発生してしまいました。typedefを使うメリットは、プログラムコードの意味がより明瞭になる事です。また、私のように、かつて物理学者で次元解析に馴染みがあれば、例えば、exp(r) とか r+s*t といった式が Rate r, Spread s, Time t という型宣言の後に来ると、直ぐに変だと気づく事ができます。(訳注: r が金利、s がスプレッド、t が時間を示しているのが、データ型からすぐ判れば、exp(r) は、exp(r*t) あるいは exp(-r*t) の間違いで、r+s*t(r+s)*t の間違いだとすぐ気づく事ができます)

 もちろん、これらのデータ型は、double と同義語であり、コンパイラーは全く同じものとして扱います。もし、これらのデータ型がより強い型としての機能を持つなら、もっとよかったかも知れません。例えば、あるメソッドがPriceを渡された場合とVolatilityを渡された場合に、(いずれもdoubleであるにも拘わらず)異なった型の入力データとして取り扱い、関数のオーバーロードが出来るとか。

 それを可能にする手段として、BOOST_STRONG_TYPEDEFを使う方法があり、これはBoostライブラリーが提供している膨大な機能のひとつです。これは、例えば、次のようにして使います。

    BOOST_STRONG_TYPEDEF(double, Time)
    BOOST_STRONG_TYPEDEF(double, Rate)

 こうすると、対象とするデータ型と同じように使える、新しいクラス型が創出できます。この方法だと、先ほどのような関数のオーバーロードが可能になります。一方で、この方法の欠点は、すべての型変換が明示的では無い事です。そうすると、古いバージョンとの互換性に問題が発生し、プログラミング上も面倒くさいことになります。

(注:例えば、単純な構文 Time t = 2.0; でもコンパイルエラーになる可能性があります。また、Timeを引数として取る関数f()についてf(1.5)では無くf(Time(1.5))という風に書く必要があります。)

 また、(BOOST_STRONG_TYPEDEFのような)マクロによって定義されたクラス型を使うと、すべての演算子をオーバーロードします。そうすると、Time(時間)にRate(金利)を足すような事も出来てしまいます(そう、これもまた次元解析的には問題です)。もし、データ型の体系が、RateとSpreadの足し算やRateとTimeの掛け算は許しても、RateとTimeの足し算に対してはコンパイルエラーを出してくれると助かります。

 このような事をランタイムでの計算時間に影響を与えず、かつ汎用的に行えるような枠組みは、最初に Barton and Nackman, 1995 によって示されました。このアイデアの発展形は Boost.Units ライブラリで実装されています。より簡単なバージョンは、私が物理学の世界で働いていた時代に書いたコードがあります(ここでは、説明しませんが、なかなかいい出来です)。しかし、そこまでの機能は、QuantLibの中では必要ないでしょう。QuantLibでは、長さ、体積、質量、時間などの物理量をすべて取り扱う必要は無いからです。

 将来取り入れる可能性がある理想的な妥協点としては、(Boostのstrong typedefのような)Wrapperクラスを実装する方法があります。そのクラスは明示的に、どの演算子がどの型に許されているか定義します。いつもの事ですが、このアイデアを示したのは我々が最初ではありません。次のバージョンのC++に加えるべき機能として、opaque typedefという名前で、(Brown, 2013)により提案が為されています。それが実現すれば、この種の型を定義するのは、より簡単になるでしょう。

 最後の注意点として、これらの型の中で、(型名から変数の内容が)一意に決まらない型タイプがあります。価格のvolatilityと金利のvolatilityは、異なる次元の値と考える必要があります。従って、両者は異なる型として取り扱うべきです。端的に言えば、Volatility型はテンプレート型にすべきでしょう。

Date Calculations: 日数計算方法

 日数計算の機能は、クオンツファイナンスにおける基本的な道具のひとつです。当然ながら、QuantLibライブラリーも、いくつかの日数計算の機能を提供しています。以下のセクションで、これらについて簡単に解説します。

日数と期間を扱うクラス

 Dateクラスのインスタンスは、個別の日付(例えば2014年11月15日)を表現するオブジェクトです。このクラスは、いくつかのメソッドを用意しており、該当する日付に関する情報(たとえば営業日に該当するかどうか、ある月や年の何日目になるのか、等)を取りだす事ができます。その他、個別の日付で許容されている最小と最大の日付(今の所、それぞれ1901年1月1日と2099年12月31日に設定されています)、その年が閏年かどうか、その日をExcelで使っている日付のSerialナンバーに換算した値、その日が月末に該当するかどうか、といった情報も取りだす事ができます。提供されているメソッドとそのインターフェースの全リストは、QuantLibライブラリーのReference Manualに掲載されています。但し、「時間」の情報(その日の何時何分か)は提供されていません。(今、それを加えるか検討中です。)

 Dateクラスでは、C++の機能を活用して、算術演算子をオーバーロード関数で定義しており、日付に関する代数計算を自然に行う事ができます。例えば、++d という表現は、特定の日 d を一日前に進める操作になり、d+2d を 2日分先に進める操作を表し、d2–d1 はふたつの日付 d1d2 の間の日数を表し、d–3*Weeks は d の3週間前の日付を表します(WeeksTimeUnitのenumerationで提供されているカレンダー単位のひとつで、他の enumメンバーにはDaysMonthsYearsがあります)。また d1<d2 という比較演算子は、d1d2 の前にあるなら trueを返します。Dateクラスが提供しているこういったオーバーロードされた演算子の機能は、カレンダー日付を前提にしており、休日や営業日調整の慣行は考慮されていません。

 Periodクラスは、2日や3週間や5年といった期間の長さをオブジェクトモデル化したもので、TimeUnit(日、週、月、年といった期間の単位)と、その長さを表す整数を組合せて、データを保存しています。このクラスが提供している、オーバーロードされた算術演算子と比較演算子は限られています。理由は、カレンダーは完全な算術計算に馴染まない傾向がある為、2つのPeriodインスタンスについてどちらが長いか短いか、簡単に比較できないからです。例えば、11か月は1年より短いのは明らかですが、60日が2か月より短いか長いかは、どの2か月を取るかで異なります。仮に、比較がうまく出来ない場合は、例外処理に飛ぶようになっています。

 比較内容が明らかな場合であっても、いくつかの驚くような事を克服する必要があります。例えば次のような比較ですが

    Period(7,Days) == Period(1,Weeks)

 は、true を返します。当然、正しい結果と思いますよね? 覚えておいて下さい。

カレンダークラス

 Calendarクラスは、休日と営業日の情報を取扱います。このクラスには、数多くの派生クラスが存在しており、それらは具体的な市場毎の休日の情報を定義しています。ベースクラスは、いくつかのメソッドを提供しており、特定の日が休日か営業日かを判定するシンプルなメソッドや、仮にその日が休日だった場合に、最も近い営業日を特定する少し複雑なメソッド(最も近いという操作は、BusinessDayConvention の enumeration に従って決定されます)、さらに、与えられた日数あるいは営業日数をもとに、カレンダーをその期間だけ前に進めるメソッドなどです。

 Calendarクラスの記述の仕方によって、その動作がどのように異なるのかを見てみるのは興味深い事です。ひとつの方法は、Calendarインスタンスに、その特定の市場の休日情報をすべてリストで持たせる事です。しかし、この方法だと休日の変更があった場合などメンテナンスの問題が発生します。

 従って、我々は、休日決定のルール(例えば、11月の第4木曜日とか、各年の12月25日とかいう決まり)をプログラムコード化する方法を取るべきと考えました。そうすると、polymorphism(多相性)を持たせたTemplate Methodパターンを使うのが有力な方法です。この方法では、派生クラスで、ベースクラスのisBusinessDay()メソッドをオーバーライドして、様々な国のカレンダーに対応します。これでもOKですが、欠点としてCalendarインスタンスを、shared_ptrsを使って、他のオブジェクト等に渡したり、データを保持したりする必要があります。このクラスは概念的にもシンプルで、しかも頻繁に使われるオブジェクトなのでユーザーが簡単にインスタンス化し、利用できるようにした方が良く、そうすると(shared_ptrsによる)メモリーのdynamic allocationの仕組みは余計な冗長さになるかも知れません。

 最終的に我々が取った方法は、下記Listing A.1に示すような形です。これはpimple idiom (訳注:Pointer to Implementation idiomの略)のバリエーションで、StrategyパターンやBridgeパターンの名残でもあります。最近の若い人たちは、type erasure(型消去のテクニック)とも呼んでいるようです。

Listing A.1:Calendarクラスの概要
  class Calendar {
    protected:
      class Impl {
        public:
          virtual ~Impl() {}
          virtual bool isBusinessDay(const Date&) const = 0;
      };
      boost::shared_ptr<Impl> impl_;
    public:
      bool isBusinessDay(const Date& d) const {
          return impl_->isBusinessDay(d);
      }
      bool isHoliday(const Date& d) const {
          return !isBusinessDay(d);
      }
      Date adjust(const Date& d,
                  BusinessDayConvention c = Following) const {
          // uses isBusinessDay() plus some logic
      }
      Date advance(const Date& d,
                   const Period& period,
                   BusinessDayConvention c = Following,
                   bool endOfMonth = false) const {
          // uses isBusinessDay() and possibly adjust()
      }
      // more methods
  };

 手短に説明すると、Calendarクラスは polymorphic な内部クラス Impl を宣言し、営業日決定(or 休日決定)ルールの実装をその派生クラスに委任し、自らはその pointer を保持するものです。isBusinessDay()メソッドは、それ自体は仮想関数ではありませんが、(関数内でImplのポインターを使って)動作を、Impl派生クラスのメソッドに委託しています。その他のメソッドも、Template Methodパターンをある程度取り入れ、仮想関数としてでは無く、isBusinessDay()メソッドを直接あるいは間接的に使って実装されています。
(注:同じテクニックは次のセクションのDayCounterクラスや、Chapter VIで説明したParameterクラスでも使われています。)

 派生Calendarクラスは、Calendar::Impl の派生クラスを内部クラスとして定義し、具体的なカレンダーの動作を提供します。そのコンストラクターは、Implインスタンスへの shared pointer を生成し、それをベースクラスの impl_ に保持します。その結果、生成された Calendarインスタンスは、それを使いたいどんなオブジェクトであっても、安全にコピーできます。このインスタンスを分割しても、polymorphicな Implクラスへのポインターのおかげで、正しい動作を維持する事ができます。最後に、同じ派生Calendarインスタンスは、同じ Implインスタンスをシェアできる点を注意しておきます。これは Flyweightパターンの実装とみる事ができ、ひとつの単純なクラスの為に、合計で2.5種類のデザインパターンを使ったことになります。

 Calendarクラスの実装内容の説明については十分したので、その動作の説明に移ります。前のセクションで説明した、“驚くべき事”についてです。Period(1,Weeks)Period(7,Days) と同じ(==演算子が true を返す) と述べた所を覚えていますか?実は、Calendarクラスの advance()メソッドだけは、7Daysは7business Days(7営業日)として計算しています。従って、仮に2つの期間 p1p2 が同じ (すなわちp1==p2trueを返す) だったとしても、calendar.advance(p1)の結果は calendar.advance(p2) と違ってくる可能性があります。とんでも無い事をやってしまいました。

 この問題に対する良い解決策を持っている訳ではありません。過去のバージョンとの互換性を考えれば、今のadvance()メソッドでのDaysの使い方はそのままにしなければなりません。そうすると、(7カレンダー日だけ日を前に進めたい場合でも)calendar.advance(7, Days)として、それを7カレンダー日前に進めたと解釈する事はできません。ひとつの逃げ道は、今の状況はそのまま残して、enumerationの中にBusinessDaysCalendarDaysを追加する方法です。(今後開発されるプログラムコードで、これを使い分ければ、)意味の曖昧さが取り除かれ、次第にDaysを使われなくなるでしょう。あるいは、(advance()メソッドも含めてQuantLib全体で)7daysは1weekとは違うという事に統一してしまう方法です。しかし私自身はあまり乗り気ではありません。

 もし、過去のバージョンとの互換性をあきらめるなら(待ち遠しいQuantLib 2.0のバージョンで)、もっと別の解決策があります。ひとつは、Daysを常にカレンダー日数として使い、BusinessDaysは営業日数を示すものとして(TimeUnitの)enumerationに加える方法です。別の方法は、(私自身は、考えれば考えるほどこちらの方法がいいと思いますが)、Daysを常にカレンダー日数として使い、CalendarクラスにadvanceBusinessDays()メソッドを追加するか、advance()メソッドをオーバーロードしてadvance(n, BusinessDays)を加える方法です(ここでBusinessDaysは別のクラスのインスタンスとする)。しかし、これは例えば3business daysは、もはや期間でなくなる(年数換算の計算が出来ない)ことになります。

 既に申し上げた通り、明解な解決策はありません。もし読者の方が別の解決法をお持ちなら、お聞かせ下さい。

Day Count Conventions: 日数計算方法

 DayCounterクラスは、2つの日付の間の期間を計算するツールを提供しており、その期間は、“日数”あるいは“小数点付きの年数”で表されます。Actual360Thirty360 といった派生クラスが存在しており、これら派生クラスは前のセクションで説明したCalendarクラスと同じ様なpolymorphic(多相的)な動作を実装しています。
(訳注:様々な日数計算方法については、Wiki参照

 そのインターフェースは、残念ながら少し粗い作りです。例えば、yearFraction()メソッドは、単に2つの日付を取るのでは無く、次のようになっています。

    Time yearFraction(const Date&,
                      const Date&,
                      const Date& refPeriodStart = Date(),
                      const Date& refPeriodEnd = Date()) const;

 2つの任意引数(refPeriodStart と refPeriodEnd)は、ある特別の日数計算方法(ISMA Actual/Actualがそれです)の為だけに必要です。この日数計算方法では、2つの日付の他に2つのreference日付けを指定する必要があります。(訳注:ISMA Actual/Actualは、主に米国債の経過利息計算に使われているが、分母にあたる実日数は、クーポン期間の実日数で、年2回払いであれば、182日であったり、183日であったりする) 

 ところが、共通のインターフェースを持たせる為に、(ISMA Actual/Actual以外の)派生クラスでも、yearFraction()メソッドの宣言に、この2つの追加の引数を加えなければなりませんでした (ほとんどのクラスで、問題なくそれを無視していますが)。このDayCounterが起こした問題は、これだけに留まりません。それらを次のセクションで見てみます。

Schedules: クーポンスケジュール

 次のListing A.2に示すScheduleクラスは、クーポン日付のスケジュールを生成する為に使われます。

Listing A.2:Scheduleクラスのインターフェース
    class Schedule {
      public:
        Schedule(const Date& effectiveDate,
                 const Date& terminationDate,
                 const Period& tenor,
                 const Calendar& calendar,
                 BusinessDayConvention convention,
                 BusinessDayConvention terminationDateConvention,
                 DateGeneration::Rule rule,
                 bool endOfMonth,
                 const Date& firstDate = Date(),
                 const Date& nextToLastDate = Date());
        Schedule(const std::vector<Date>&,
                 const Calendar& calendar = NullCalendar(),
                 BusinessDayConvention convention = Unadjusted);

        Size size() const;
        bool empty() const;
        const Date& operator[](Size i) const;
        const Date& at(Size i) const;
        const_iterator begin() const;
        const_iterator end() const;

        const Calendar& calendar() const;
        const Period& tenor() const;
        bool isRegular(Size i) const;
        Date previousDate(const Date& refDate) const;
        Date nextDate(const Date& refDate) const;
        ... // other inspectors and utilities
    };

 様々な市場慣行やISDAの決まり事を取り込む為、このクラスは相当数のパラメータを取る必要があります。それがいかに多いかは、コンストラクターの引数のリストを見れば一目瞭然です。(これらの引数の説明をしませんがお許し下さい。意味については皆さんお判りでしょう。)この数はおそらく多すぎるので、QuantLibではNamed Parameter Idiom(既にChapter IV キャッシュフロー配列の生成 の所で説明しました)を使い、より使いやすいFactoryクラスを提供しています。それを使えば、次のようなコードの記述で、Scheduleクラスのインスタンスが生成できます。

    Schedule s = MakeSchedule().from(startDate).to(endDate)
                 .withFrequency(Semiannual)
                 .withCalendar(TARGET())
                 .withNextToLastDate(stubDate)
                 .backwards();

 このクラスは他にも、メンバー変数のデータを読み取るインスペクター関数や、日付の配列に対するインターフェースとしてsize()operator[]begin()end()といったメソッドを提供しています。

 Scheduleクラスは、もう一つ別のコンストラクターがあり、引数として事前に設定された日付の配列を取ります。但し、これについては未完成です。このコンストラクターを使って生成されたインスタンスは、インスペクター関数に対応するデータが揃っておらず、今の所、そういった場合は例外処理に飛びます。その例として、tenor()isRegular()といったメソッドがあり、それについて少し説明する必要があります。

 まずisRegular(i)メソッドですが、このメソッドが指し示すのはi番目のクーポン日付では無く、i番目のクーポン期間、すなわちi とi+1番目のクーポン日の間の期間を指します(訳注:その期間がregularならtrue、そうでなければfalseを返す)。そう言いましたが、では”regular”はどういう意味なのでしょうか?クーポンスケジュールが、“クーポン期間”をベースに生成された場合、大半のクーポン期間は引数で指定された期間と同じになります(それがregularの意味です)。しかし、最初と最後のクーポン期間は、最初のクーポン日あるいは最後からひとつ手前のクーポン日を明示的に指定した場合、指定されたクーポン期間より長かったり短かったりします。例えば、First Short Coupon日を特に設定したい場合などは、その日を明示的に指定します。

 もし、Scheduleが、事前に設定されたクーポン日の配列でインスタンス化された場合、上記のような指定されたregularなクーポン期間情報を持たない為、isRegular(i)メソッドの問いかけに答えられません。より問題なのは、それにより、このScheduleインスタンスを使って、実際の債券クーポンのスケジュールを生成する事が出来なくなる事です。もし、このScheduleインスタンスを、固定クーポン債を生成するコンストラクターに渡した場合、例外処理に飛んでしまいます。

 では、なぜ債券のコンストラクターは、クーポンスケジュールを生成するのに、この isRegular(i)の情報が必要なのでしょうか?理由は、もしその債券のクーポンの日数計算方法が ISMA actual/actual だった場合、reference期間の情報が必要になりますが、その reference期間の情報を計算する為には、クーポン期間の情報も必要になるからです。

 幸いなことに、この問題を解決するのは難しくありません。ひとつは、日数計算方法をチェックし、必要な場合にだけreference期間を計算する事です。この場合でも、日数計算方法がISMA actual/actualの場合は例外処理に飛びますが、その他の日数計算方法ならすべてうまく行きます。別の方法は、(クーポン日配列を渡されて生成された)Scheduleインスタンスに、クーポン期間とregularityの情報を何とかして導出して加える事です。そうすれば、対応するメソッドはすべてうまくいきます。しかし、この方法に意味があるかどうか、確信がありません。

金融に関連する概念を対象とするクラス

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

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 II で説明した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ライブラリーに加えられるでしょう。

金利

 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つの日付 \(d_1\) と d_2 の間の期間として与えられる)において、所与の金利と複利の回数に従って、元本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を使った構文のままにし、若干の問題を甘受することにしました。

Index: インデックスクラス

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

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

Listing A.6: 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.7にInterestRateIndexクラスを示します。

Listing A.7: 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)に登録するメソッドを用意する方法です。しかし、それを強制する事はできませんし、エラーが起こりにくいものの、よりめんどくさくなります。

 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()メソッドは仮想関数として宣言されていませんでした。おそらく、見逃していたのだと思いますが、将来リリースするバージョンで修正しなければなりません。

ExerciseクラスとPayoffクラス

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

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

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

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

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

*    *    *

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

Listing A.9: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()メソッドが、引数として1つの対象資産価格しか取れないという点です。(注:この点は、対象資産が1つであっても、複数の 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ライブラリーは、C++の標準ライブラリーが提供する算術計算ツール以外に、いくつかのツールを用意する必要があります。ここでは、それらについて概略を説明します。

Interpolation:補間関数

 Interpolationに関する関数群は、QuantLibの中では珍しく使用するのがやや危険なクラスです。

 ベースクラスである Interpolationクラスの概要を下記 Listing A-9 に示します。このクラスは、x と y の2つのデータ配列を使って、x 軸上の任意の点に対応する y の値を線形補間して導出します(訳注:イールドカーブで言えばxが期間、yがその期間に対応するイールド)。このクラスは、その補間された値を返す operator()メソッドや、いくつかの便利な関数を用意しています。少し前に説明したCalendarクラスと同様、このクラスも pimpl idiom(Pointer to Implementation)を使ってpolymorphicな動作を実現しています。すなわち、まず内部クラスとして Implクラスを宣言し、その Implの派生クラスが個別の Interpolationアルゴリズムを実装します。そして InterpolationベースクラスのメソッドからImpl派生クラスのメソッドを呼び出す形でそれを使います。もうひとつの内部クラスであるtemplateImplクラスは、一般的な動作を実装すると供に、Interpolationの対象となるデータを格納します。

Listing A.10:Interpolationクラスの概要
    class Interpolation : public Extrapolator {
      protected:
        class Impl {
          public:
            virtual ~Impl() {}
            virtual void update() = 0;
            virtual Real xMin() const = 0;
            virtual Real xMax() const = 0;
            virtual Real value(Real) const = 0;
            virtual Real primitive(Real) const = 0;
            virtual Real derivative(Real) const = 0;
        };
        template <class I1, class I2>
        class templateImpl : public Impl {
          public:
            templateImpl(const I1& xBegin, const I1& xEnd,
                         const I2& yBegin);
            Real xMin() const;
            Real xMax() const;
          protected:
            Size locate(Real x) const;
            I1 xBegin_, xEnd_;
            I2 yBegin_;
        };
        boost::shared_ptr<Impl> impl_;
      public:
        typedef Real argument_type;
        typedef Real result_type;
        bool empty() const { return !impl_; }
        Real operator()(Real x, bool extrapolate = false) const {
            checkRange(x,extrapolate);
            return impl_->value(x);
        }
        Real primitive(Real x, bool extrapolate = false) const;
        Real derivative(Real x, bool extrapolate = false) const;
        Real xMin() const;
        Real xMax() const;
        void update();
      protected:
        void checkRange(Real x, bool extrapolate) const;
    };

 コードを見ての通り、templateImplクラスは(対象データとなる) x と y の値をコピーしていません。その代わり、この2種類の変数列に対する Iterator(ポインター)を保持して、データ値を見に行けるようにしています。これが、Interpolationクラスを使うのが安全でない理由です。(危険を回避するには)まず、Interpolationインスタンスの生存期間 が、対象となるデータ変数列のそれを越えないようにしなければなりません。すなわち、メモリーから対象データを消去した場合、(Interpolationインスタンスが持つ)そこへのポインターを残してはいけないという事です。それと同時に、Interpolationインスタンス自体を保持するオブジェクトをコピーする際は、細心の注意を払わなければなりません。

 前者の注意点は、それほど大きな問題ではありません。Interpolationインスタンスが単独で使われる事は殆どありません。通常は、他のクラスのメンバー変数として、Interpolationの対象データと一緒に格納されます。その結果、Interpolationインスタンスと対象データは(それを保持するオブジェクトの生成・消去の際に)同時に生成され、同時に消去されるので、生存期間の問題は、保持するオブジェクトが(自動的に)管理します。

 後者の注意点も、さほど大きな問題ではありませんが、前者の問題は、通常、自動的に管理されるのに対し、後者の問題はユーザーが注意する必要があります。今述べた通り、Interpolationインスタンスは、通常、他のオブジェクトのメンバー変数として対象データと一緒に保持されます。そのオブジェクト用にコンパイラーが生成するコピーコンストラクターは、保持しているメンバー変数の新しいコピーも生成します。対象データについてはそれでかまいません。しかし Interpolationインスタンスの新しいコピーは、引き続きコピー元の対象データへのポインターを持つことになります(なぜなら Interpolationインスタンスのコピーは、元の対象データへのIteratorのコピーだからです)。この動作は、当然正しくありません。

 これを避ける為には、ホストのクラス(コンテナーとしてInterpolationインスタンスと対象データをメンバー変数に持つクラス) の開発者は、ユーザー定義のコピーコンストラクターを作る必要があり、そこではメンバー変数をコピーするだけでなく、新たな Interpolationインスタンスを生成して、それがコピーされた対象データへのポインターを保持するようにしなければなりません。しかし、それは単純な操作ではありません。Interpolationインスタンスを保持するコンテナーオブジェクトは、その正確なデータ型の情報を持っていない(それは Implクラスの中に隠されている)ので、新しい Interpolationインスタンスを再生成できないのです。

 この問題の解決方法のひとつは、Interpolationクラスに、仮想関数として、自分と同じ型のインスタンスを生成する clone()メソッドのようなものを持たせるか、コピーされた際に、対象データへのポインターをコピーされた方の対象データへのポインターに変更する rebind()のようなメソッドを持たせる事です。しかし、あえてそうする必要はありませんでした。なぜなら、Interpolation Traitsが既に用意されて、殆どの場合これで対応出来るからです。

 何それ?と言われますよね。実は、Chapter IIIで、TermStructureクラスの Interpolationの概要を説明した際に出てきた LinearLogLinearクラスの事です。そのひとつの例を、次の Listing A.11に示します。

Listing A.11: LinearInterpolation クラスと、その traitsクラスの概要
    template <class I1, class I2>
    class LinearInterpolationImpl
        : public Interpolation::templateImpl<I1,I2> {
      public:
        LinearInterpolationImpl(const I1& xBegin, const I1& xEnd,
                                const I2& yBegin)
        : Interpolation::templateImpl<I1,I2>(xBegin, xEnd, yBegin),
          primitiveConst_(xEnd-xBegin), s_(xEnd-xBegin) {}
        void update();
        Real value(Real x) const {
            Size i = this->locate(x);
            return this->yBegin_[i] + (x-this->xBegin_[i])*s_[i];
        }
        Real primitive(Real x) const;
        Real derivative(Real x) const;
      private:
        std::vector<Real> primitiveConst_, s_;
    };

    class LinearInterpolation : public Interpolation {
      public:
        template <class I1, class I2>
        LinearInterpolation(const I1& xBegin, const I1& xEnd,
                            const I2& yBegin) {
            impl_ = shared_ptr<Interpolation::Impl>(
                new LinearInterpolationImpl<I1,I2>(xBegin, xEnd,
                                                   yBegin));
            impl_->update();
        }
    };

    class Linear {
      public:
        template <class I1, class I2>
        Interpolation interpolate(const I1& xBegin, const I1& xEnd,
                                  const I2& yBegin) const {
            return LinearInterpolation(xBegin, xEnd, yBegin);
        }
        static const bool global = false;
        static const Size requiredPoints = 2;
    };

 LinearInterpolationクラスの構造は単純です。このクラスは template引数を持つコンストラクターを定義し(但しこのクラスそのものは Templateではありません)、そこで自分用の Implクラス(LinearInterpolationImplクラス)のインスタンスを生成します。LinearInterpolationImplクラスは、templateImplクラスから継承されており、実際のInterpolationアルゴリズムの実行という重たい作業を行います(その時、ベースクラスで定義されているlocate()クラスの助けを借りています)。

 Linear traitsクラス(上記コード中の Linearクラス)は、2つの静的変数を定義しています。すなわち、Interpolationに最低限必要なデータ数が2である事(requiredPoints=2)、及びある点における値を変化させた時の影響が局所的である事という事(global=false)です。また interpolate( )メソッドを定義しており、引数で取った Iterator x と yを使って、この traitsに対応する型の Interpolationインスタンスを生成します。このメソッドは、すべての traitsで、同じインターフェース(引数のセット)を使って実装されており(仮にSpline関数のようにInterpolationにより多くのパラメータを必要とする場合は、それらの情報はtraitsのコンストラクターに渡されそこで保持されます)、先ほどのコピーコンストラクターで発生する問題の解決策も提供しています。Chapter IIIで解説した InterpolatedZeroCurveクラスのコードを見て頂くと、そこではtraitsクラスのインスタンス (そこでinterpolator_と呼ばれる変数です)の他に、Interpolationインスタンスと対象データも一緒に保持しているのが判ると思います。Interpolationインスタンスを保持するクラスについて、すべて同じ様な設計にすれば、(ユーザーが定義する)コピーコンストラクターの中で、traitsに対応する型の Interpolationインスタンスを(対象データと同時に)生成出来ます。

 残念ながら、Interpolationインスタンスを保持するクラスで、このようなコピーコンストラクターをユーザーが定義する事を強制する術がありません。従って、開発者はそれを覚えておかなければなりません。Interpolationクラスをコピー不可にするか、コピーを防ぐ何等かのidiom(訳注:プログラム言語に備わる構文上のルール)が無い限り、これを防ぐ方法はありません。C++11では、コピー不可かつ movable に出来るので、対応可能です。

 最後の注意点です。Interpolationインスタンスは、対象データに対するIteratorを保持していますが、それだけではそのデータが更新された際のアップデートの仕組みとしては不十分です。対象データの更新があった場合、update( )メソッドが呼び出されInterpolationも更新できるようにするべきですが、それは(今の設計の仕組みでは)Interpolationインスタンスと対象データを保持しているクラスの役割です(但し、対象データの方は、おそらく何等かのデータソースに対しObserverとして登録されているでしょう)。しかしObserverとしての登録は、データを直接読み込んでいるInterpolationオブジェクトにも当てはまります。クラスによって実装の違いはありますが、LinearInterpolationのように、いくつかの事前計算を行い、そのデータを自分で格納しているクラスもあります。LinearInterpolationクラスの実装では、ポイント間の傾きと各ポイントの原データを、(事前計算して)格納する様になっています。対象データがどの程度頻繁に更新されるかによりますが、この事前計算の仕組みは、パフォーマンス上、最適かも知れないし、あるいは、全くその逆の可能性もあります。

 (注:このクラスにあるprimitive()derivative()メソッドは、若干Implementation Leak(訳注:本来ならベースクラスで一般化したメソッドとして定義、実装されるべきものが、そこで漏れた為、派生クラスにおいてメソッドを定義・実装している)と言えるかも知れません。このメソッドはInterpolateされたイールドカーブにおいて、ゼロ金利とフォワード金利の間の換算をする時に使われます。(訳注:従って、LinearInterpolationに限らずInterpolationクラス一般で使えるメソッドとした方が良かったかも知れない))

One-dimensional Solvers: 1次元ソルバー

 ソルバーは、Chapter IIIで説明した Bootstrappingの計算ルーティンや、Chapter IVで説明した利回りの計算などの様に、計算値を特定のターゲット値に収束させる計算アルゴリズムの中で使われます。すなわち、ある関数 \(f(x)\) が与えられ、\(f(x) = \xi\) が所定の誤差内に留まるような \(x\) を求めるアルゴリズムです。

 既存のソルバーは、\(f(x) = 0\) となる様な \(x\) を求めるアルゴリズムです。もちろん、これによって(訳注:ターゲット値を \(\xi\) ではなく 0 にする事によって)汎用性を失うものではありません。但しその場合は、ユーザーが追加のヘルパー関数 \(g(x) \equiv f(x)-\xi\) を定義する必要があります。QuantLibライブラリーには、いくつかの(1次元)ソルバーが用意されていますが、すべて Numerical Recipes in C (Press et al, 1992)から取ってきて、それを書き換えて実装したものです。

 (注: いくつかの多次元 Optimizer も同じ本から取ってきています。著作権の問題と同時に、C++の文法に適合させる必要もあり、若干書き換えています(例えば、配列のインデックスは 0 からスタートする)。)

 下記の Listing A.12 は、複数のソルバーの土台として使われている Solver1D クラステンプレートのインターフェースを示しています。

Listing A.12 :Solver1Dクラスのインターフェースと、いくつかの派生クラス
    template <class Impl>
    class Solver1D : public CuriouslyRecurringTemplate<Impl> {
      public:
        template <class F>
        Real solve(const F& f,
                   Real accuracy,
                   Real guess,
                   Real step) const;
        template <class F>
        Real solve(const F& f,
                   Real accuracy,
                   Real guess,
                   Real xMin,
                   Real xMax) const;
        void setMaxEvaluations(Size evaluations);
        void setLowerBound(Real lowerBound);
        void setUpperBound(Real upperBound);
    };

    class Brent : public Solver1D<Brent> {
      public:
        template <class F>
        Real solveImpl(const F& f,
                       Real xAccuracy) const;
    };

    class Newton : public Solver1D<Newton> {
      public:
        template <class F>
        Real solveImpl(const F& f,
                       Real xAccuracy) const;
    };

 このクラスは、すべてのソルバーに共通して使える、複数の同一名のメソッドを提供しています。オーバーロードされた solve()メソッドの内、ひとつは解を含むレンジの上限と下限を探します。もう一つの solve()メソッドは、引数として渡された上限と下限の間に解が存在するかどうかチェックします。いずれのメソッドも、実際の計算アルゴリズムは、派生クラスで実装されているsolveImpl()メソッドに委託されています。その他のメソッドは、求める解の範囲に関する‘条件’を課したり、関数の(最大)計算回数を設定したりします。

 実際の計算アルゴリズムを solveImpl()に委託するやり方は、Curiously Recurring Template Pattern(“CRTP”)を使って実装されています。この Patternについては Chapter VIIで既に説明しています。ソルバーのプログラムを書いていた当時、テンプレートクラスをしきりに使おうとしていました。(その頃expression templates (Veldhuizen, 2000)を実装しようとした事は、既に述べましたっけ?)従って、このPatternを選択したのは、当時の流行に影響されたと思われるかも知れません。しかし、dynamic polymorphismを使おうとすれば、それ以外に選択肢はありませんでした。われわれは、ソルバーを、関数ポインターや関数オブジェクトと一緒に機能させたいと考えていました。またその頃は、boost::functionは存在していませんでした。その結果、templateメソッドを使うに至った訳です。templateメソッドは仮想関数とする事が出来ないので、CRTPが唯一、共通のメソッドをベースクラスに置き、そこから派生クラスで定義・実装されたメソッドを呼び出す方法でした。

 このセクションを終わるにあたっていくつかの注意点を述べます。一点目は、CRTPを使っている為、ユーザーがソルバーを使う関数をプログラムする場合は、テンプレート関数にする必要があります。これは少し変に感じるかも知れません。実際には、我々がプログラムしたほとんどのケースで、そんな事を気にせずに、ソルバーを最初に明示的に選択して対応していました。ユーザーの方が同じ事を行っても非難するつもりはありません。二点目は、ほとんどのソルバーは、引数として渡された関数fを呼び出して f(x) を計算するだけなので、それがどんな関数であってもソルバーは動作します。しかし Newtonクラスや NewtonSafeクラスでは、f.derivative(x) を定義する必要があります(訳注:ニュートン法は微分係数を使って、収束速度を速めてるアルゴリズムなので、微分不可能な関数では対応できない)。これもまた、動的な polymorphismを使っているのに、変に感じるかも知れません。三点目は、Solver1Dのインターフェースを見ても、引数として渡される誤差の許容値 \(\epsilon\) が、\(x\) の許容誤差(推定値 \(\tilde{x}\) と真の解 \(x\) との許容誤差)を意味するのか \(f(x)\) の許容誤差( \(f(\tilde{x})\) の計算結果が 0 からどれだけ離れてもよいか)を意味するのか明確ではありません。これについては、既存のソルバーはすべて、\(x\) の許容誤差として使っています。

Optimizers: 多次元関数の最小値問題

 多次元の Optimizerは1次元のソルバーより、ずっと複雑です。Optimizerの役割を掻い摘んで言うと、Cost Function(コスト関数または目的関数)\(f(\mathbf{x})\) が最小値を返すような変数ベクトル \(\tilde{\mathbf{x}}\) を探し出す事ですが、その説明だけでは不十分です。それについては Cost Functionの実装内容を見ていく際に、もう少し詳しく説明します。

 Optimizerのオブジェクトモデル化においては、(Solverの時とは異なり)テンプレートを使いませんでした。すべての Optimizerクラスは、次の Listing A.13に示す OptimizationMethodベースクラスから派生しています。

Listing A.13:OptimizationMethodクラスのインターフェース
    class OptimizationMethod {
      public:
        virtual ~OptimizationMethod() {}
        virtual EndCriteria::Type minimize(
                        Problem& P,
                        const EndCriteria& endCriteria) = 0;
    };

 このクラスが提供しているメソッドは、デストラクター以外は minimize()メソッドのみです。このメソッドは引数として Problemクラスのインスタンスを取ります。そのインスタンスが、最小値を求める“関数インスタンス”とその“制約条件インスタンス”への参照を持ち、そこで実際の計算アルゴリズムが実行されます。計算が終了すると、多数の計算結果を返す Problemインスタンスが出来あがります。Problemインスタンスは計算結果として、最適解となるベクトル \(\tilde{\mathbf{x}}\) の他に、計算がどのように終了したか、その理由も返さなければなりません(最小値を探す計算がうまく収束したのか、それとも計算反復回数の最大値に到達したのでその時点での推定値を返すのか)。さらに、導出された最適解 \(\tilde{\mathbf{x}}\) を使った関数値そのものを返す事が出来れば、もっと良かったかもしれません(既に計算されているはずですから)。

 現時点での実装は、このメソッドは計算が終了した理由を返すのみで、実際の計算結果は Problemインスタンスの中に保存されたままです(メソッドの戻り値として返さない)。これが、メソッドの引数である Problemインタンスが、non-constな“参照”として渡されている理由です。別の実装方法として、Problemインスタンスはそのままにして、計算結果をすべて“構造体”に放り込んで、それを返す事も出来きましたが、その方がより面倒くさくなると思います。一方で(引数の Problemインスタンスだけでなく)minimize()メソッド自体も non-constにした理由は良く分かりません。おそらく、見逃してしまったのでしょう(これについては、後程振り返ります)。

 次に、下記 Listing A.14 に示す Problemクラスの説明に進みます。

Listing A.14:Problemクラスのインターフェース
    class Problem {
      public:
        Problem(CostFunction& costFunction,
                Constraint& constraint,
                const Array& initialValue = Array());

        Real value(const Array& x);
        Disposable<Array> values(const Array& x);
        void gradient(Array& grad_f, const Array& x);
        // ... other calculations ...

        Constraint& constraint() const;
        // ... other inspectors ...

        const Array& currentValue();
        Real functionValue() const;
        void setCurrentValue(const Array& currentValue);
        Integer functionEvaluation() const;
        // ... other results ...
    };

 既に述べたように、このクラスは、最小値を求める Cost Function(目的関数)や、制約条件や、適当な推定値、といった、最小値問題に必要な引数を取り纏めたクラスです。また、対象となる Cost Functionを呼び出しながら、同時に試行回数のカウントを行うメソッドも提供しています(それについては、Cost Functionの説明をする時に詳しく解説します)。さらに、いくつかのインスペクター関数や計算結果を取りだすメソッドも提供しています。(Optimizerの中で使われる、それらの値を設定するメソッドもあります)。

 このクラスの問題点は、そういった計算で使われる要素を、non-constな参照として取り込み、保存している事です。それらを constとして取り扱うかどうかについては、後ほど説明します。それらが“参照”であること自体が問題です。なぜなら、これらのインスタンスの生成消去を少なくとも Problemインスタンスの生成消滅に合わせる責任を、ユーザー側の方で負わなければならないからです。

 代替策としてOptimizerが、計算結果を構造体で返すようにする方法を取っても、若干問題は残ります(訳注:先ほどの説明に合った通り、今の実装方法は計算結果を Problemインスタンスに書き込んでいる)。もしそうするなら、Problemクラスを取り除いて、計算で使う要素を、直接 Optimizerクラスの minimize()メソッドに渡せばいいでしょう。こうすれば、先ほどの non-constの”参照”をメモリーに保持しない為、メモリー領域の生成消去の問題は回避できます。しかし、その場合は、各 Optimizer が Cost Functionの試行回数のカウントを行う必要があり、プログラムコードを重複させる原因になります。

 また、1次元ソルバーとは異なり、Cost Functionは minimize()メソッドのテンプレート引数になっていません。各 Cost Functionは次の Listing A.15にある CostFunctionベースクラスから継承する必要があります。

Listing A.15: CostFunctionクラスのインターフェース
    class CostFunction {
      public:
        virtual ~CostFunction() {}

        virtual Real value(const Array& x) const = 0;
        virtual Array values(const Array& x) const = 0;

        virtual void gradient(Array& grad, const Array& x) const;
        virtual Real valueAndGradient(Array& grad,
                                      const Array& x) const;
        virtual void jacobian(Matrix &jac, const Array &x) const;
        virtual Array valuesAndJacobian(Matrix &jac,
                                        const Array &x) const;
    };

 当然ですが、このクラスのインターフェースで value()メソッドを宣言しており、このメソッドは引数として与えられた変数の配列を使って、関数の計算結果を返します(読者の方は、ここで operator()メソッドを宣言していない事に驚いたかも知れません)。一方で、values()メソッドも宣言されており、これは配列を返します。Optimizerが、複数の Quoteデータを使って Calibrateするのに使われる事を思い出せば、それ程驚く事ではないでしょう。value()メソッドが誤差値の合計(すなわち変数毎の誤差の2乗平均、あるいは同様の数値)を返すのに対し、values()メソッドは、(入力された変数である)Quote毎の誤差値の配列を返します。この数値は、Optimizerの収束速度を向上させるようなアルゴリズムの中で使われます。

 その他のメソッド群は、各変数に対する微分を返し、その値は特別な計算アルゴリズムの中で使われます。その内の一つ gradient()メソッドは各変数に対する微分を計算し、その値を第一引数として渡された配列 (上記コードではArray& grad) に保持します。jacobian()メソッドは、同じ操作を values()メソッドの為に行い、計算値を行列 に保持します。さらに valueAndGradient()valuesAndJacobian()メソッドは、計算誤差の値と変数に対する微分の計算の両方を同時に行います。この2つのメソッドはデフォールトとして有限差分による微分を計算しています。当然ながら相応の計算時間がかかります。仮に解析的な方法で、これらのメソッドをオーバーライドできないのであれば、差分を使った方法を使う価値があるかどうか確信が持てません。

 ひとつ注意点があります。CostFunctionのインターフェースをチェックして判った事ですが、すべてのメソッドが const として宣言されています。従って、このクラスのインスタンスを Problemクラスのコンストラクターに non-const な参照として渡す事は、間抜けなやり方でした。それを const に変更するのはコンストラクターの役割を広げるだけなので、過去のバージョンとの互換性を壊す事なく変更可能でしょう。

 最後に、Constraintクラスを下記Listing A.16に示します。

Listing A.16: Constraintクラスのインターフェース
    class Constraint {
      protected:
        class Impl;
      public:
        bool test(const Array& p) const;
        Array upperBound(const Array& params) const;
        Array lowerBound(const Array& params) const;
        Real update(Array& p,
                    const Array& direction,
                    Real beta);
        Constraint(const shared_ptr<Impl>& impl =
                                             shared_ptr<Impl>());
    };

 このクラスは Cost Functionの定義域に適用される“制約条件”のベースクラスです。QuantLibライブラリーは、ここでは示していませんが、いくつかの定義済みのクラスと、それらをひとつに纏めた CompositeConstraintクラスを定義しています。

 このクラスの中心となるメソッドは test( )で、変数の配列を引数として取り、それらの値が制約条件を満たしているかどうかをチェックします。すなわち、配列の値が変数の領域内であれば、有効とします。このクラスは、upperBound()lowerBound()メソッドを定義しており、理屈では変数の上限と下限を設定することになっていますが、実際にはそれを常に正しく設定できません。例えば領域が円であった場合、ある点のx座標とy座標が、それぞれ上限の内側にあったとしても、その点そのものは円領域の外という事が起こり得ます。

 さらに注意点を2つ。ひとつ目は、Constraintクラスは update()メソッドを定義していますが、constとして宣言されていません。もしこのメソッドの目的が制約条件自体を変更するなら、そうする意味があったでしょうが、そうでないなら問題です。実際には、このメソッドは変数の配列とその方向の配列を引数として取り、変数が制約条件を満たすよう、与えられた方向に値を修正していきます。(訳注:すなわち制約条件自体を変更するのではなく、引数である入力変数を変更している)従って、このメソッドは const にすべきでしたし、メソッド名も別のものにすべきでした。たとえ子供がと駄々をこねるように、開発者が“I can’t”と言ったとしても。実際には、なんとか修正出来たでしょう。

 ふたつ目は、このクラスは pimpl idiom(Pointer to Implementation)を使っています(これについては既に説明しています)。デフォールトのコンストラクターは任意の引数として Implインスタンスに対するポインターを取っています。もし今このクラスを再設計するとしたら、まず引数を取らないデフォールトのコンストラクターを作成します。その上で別途 Implへのポインターを取るコンストラクターを作成し、それを protectedとして宣言して派生クスでしか使えないようにしたでしょう。

 最後に、これらのクラスがconstを正しく使ったかどうかについて私の考えを短く説明します。端的に言えば、良くありません。いくつかのメソッドは修正可能ですが、いくつかは無理です。例えば、minimize()メソッドを変えると、過去のバージョンとの互換性が失われるでしょう(このメソッドは純粋仮想関数として宣言されておりconstかどうかという事はメソッドの属性に含まれています)。また、いくつか他のOptimizerクラスもminimize()メソッドを通して別のメソッドを呼び出し、メソッド間のデータのやり取りを、メンバー変数を使って行っているので、同じく互換性の問題が起こります。

 (注:いくつかのOptimizerクラスではメンバー変数をmutableで宣言しているものがあります。おそらく、かつてはメソッドがconstとして宣言されていた為でしょう。このセクションを書いている時点では、詳しく調べていませんが)

 Version1.0 がリリースされる前に、コードを見直す努力をもう少ししていれば、この問題を避けられたかもしれません。若いプログラマーの方は、これを反面教師として学んで下さい。

Statistics: 統計値

 Statisticsクラス群は、Monte Carloシミュレーションで生成されたサンプルを集計する目的で作られました(Chapter VIでの説明を思い出して下さい)。このクラスが提供するすべての機能は(Decoratorパターンを使って)一連の decoratorで実装されており、各 decorator が、階層化されてメソッドを追加するようになっています。階層化が多重になっている理由は、私の推察では、decoratorの基本階層になるベースクラスが2つ用意されている為であり、その内のいずれかを選択できるようにする為かと思われます。階層化された設計方法により、ユーザーは、基本階層の共通インターフェースに基づいて、機能を追加していく事が出来ます。

 ユーザーが選択できる基本階層のひとつは IncrementalStatistics クラスで、そのインターフェースを下記 Listing A.17 に示します。このクラスがどの様なインターフェースを提供しているかは簡単に推察がつくと思いますが、例えば、サンプル数を返したり、加重されたサンプルの場合はその加重合計であったり、その他様々な統計値を返したりします。

Listing A.17:IncrementalStatisticsクラスのインターフェース
    class IncrementalStatistics {
      public:
        typedef Real value_type;
        IncrementalStatistics();

        Size samples() const;
        Real weightSum() const;
        Real mean() const;
        Real variance() const;
        Real standardDeviation() const;
        Real errorEstimate() const;
        // skewness, kurtosis, min, max...

        void add(Real value, Real weight = 1.0);
        template <class DataIterator>
        void addSequence(DataIterator begin, DataIterator end);
    };

 サンプルを追加する場合、1つずつ追加する事もできますし、iteratorで指定された配列として追加する事もできます。このクラスの特徴は、引数として渡されたデータを保持せず、そのまま統計値を計算する事です。これは、メモリーを節約するのが目的でした。(Libraryのプロジェクトが始まった)2000年頃は PCの RAMのサイズは 128MB から 256MBであった為、メモリー確保は今よりもずっと大きな関心事でした。このクラスで実際の計算を行うプログラムコードは、かつては独自に開発されたものでしたが、今では Boost の accumulator libraryを使っています。

 ふたつ目の基本階層は、GeneralStatisticsクラスで、IncrementalStatisticsクラスと同じ名前を持つインターフェースに加え、いくつかの独自のメソッドが加わっています。このクラスは、渡されたデータを一旦保持し、その値を返す事が出来るので、追加されたメソッドはそうした機能を提供するものです。例えば、データのパーセンタイルを返す事や、データの並び替えが可能です。このクラスは、expectationValue()というテンプレートメソッドを提供しており、ユーザーが独自に開発した計算アルゴリズムを使う事が出来ます。興味のある方は、この Sectionの最後にある“Aside”の欄をご覧下さい。

Listing A.18:GeneralStatisticsクラス
    class GeneralStatistics {
      public:
        // ... same as IncrementalStatistics ...

        const std::vector<std::pair<Real,Real> >& data() const;

        template <class Func, class Predicate>
        std::pair<Real,Size>
        expectationValue(const Func& f,
                         const Predicate& inRange) const;

        Real percentile(Real y) const;
        // ... other inspectors ...

        void sort() const;
        void reserve(Size n) const;
    };

 次に、外側(訳注:DecoratorパターンにおけるDecorateする側)の階層について説明します。最初のクラスは、Expected Shortfall や Value at Riskといった、リスク量計算における統計値を計算できるクラスです。(訳注:Value at Riskは、2σ、99%信頼区間、99%パーセンタイル、といった、ある一定の“閾値における”損失額。Expected Shortfallは“閾値を超える”損失について、その発生確率をかけた条件付き期待値。)これらの値を計算する際の問題点は、サンプルデータのすべてを必要とすることです。IncrementalStatisticsクラスの場合、サンプルデータを保持していないので、正確な計算はあきらめて、近似値を計算する事になります。その方法のひとつは、標本集団がガウス分布と同じモーメントを持つと仮定して、サンプルの平均と分散を使い、解析的に計算する方法です。下記の GenericGaussianStatisticsクラスは、まさにそれを行っているクラスです。

Listing A.19:GenericGaussianStatisticsクラスのインターフェース
    template<class S>
    class GenericGaussianStatistics : public S {
      public:
        typedef typename S::value_type value_type;
        GenericGaussianStatistics() {}
        GenericGaussianStatistics(const S& s) : S(s) {}

        Real gaussianDownsideVariance() const;
        Real gaussianDownsideDeviation() const;
        Real gaussianRegret(Real target) const;
        Real gaussianPercentile(Real percentile) const;
        Real gaussianValueAtRisk(Real percentile) const;
        Real gaussianExpectedShortfall(Real percentile) const;
        // ... other measures ...
    };

    typedef GenericGaussianStatistics<GeneralStatistics>
                                           GaussianStatistics;

 すでに述べた通り、またコードを見ても判りますが、このクラスはベースクラスに対する Decorator として実装されています。Decorateされる対象(ベースクラス)をテンプレート引数として取り、そこから派生するクラスを定義する事によって、ベースクラスのすべてのメソッドが使えた上で、さらに新しいメソッドが追加できます。QuantLibでは、テンプレート引数を特定して具体化されたクラス型として、GaussianStatisticsクラスを提供していますが、テンプレート引数として GeneralStatisticsクラスを指定しています。おかしいですね。テンプレート引数として IncrementalStatisticsクラスの方が指定されるべきですが、Libraryの中に見当たりませんでした。これには理由があります。少しお待ちください。

 ベースクラスがサンプルデータをすべて保持しているのであれば、(分布から解析的に計算されるリスク量では無く)データから実際のリスク量を計算する Decoratorクラスを作る事ができるはずです。それが、下記 Listing A.20 にある GenericRiskStatisticsクラスになります。ここではガウス分布に従った統計量計算アルゴリズムの実装内容については説明しません(各自Libraryのコードで確認して下さい)。

Listing A.20: GenericRiskStatisticsクラスのインターフェース
    template <class S>
    class GenericRiskStatistics : public S {
      public:
        typedef typename S::value_type value_type;

        Real downsideVariance() const;
        Real downsideDeviation() const;
        Real regret(Real target) const;
        Real valueAtRisk(Real percentile) const;
        Real expectedShortfall(Real percentile) const;
        // ... other measures ...
    };

    typedef GenericRiskStatistics<GaussianStatistics>
                                               RiskStatistics;

 コードを見れば判る通り、Decoratorの階層が重ねられています(訳注:コードの最後にある typedef を使ったテンプレートクラスの具体化の部分)。すなわち、ライブラリーでは、GenericRiskStatistics のテンプレート引数に GaussianStatisticsクラスを指定して、RiskStatisticsという具体的クラスを定義しています(訳注:それ自体が Decoratorである GaussianStatisticsクラスを、さらに Decorateするクラスを定義している)。従って、このクラスは、ガウス分布を前提にして解析的に導出した統計値に加え、実際のデータを使って導出した統計値も算出できます。これが、GaussianStatistics の Decorate対象として GeneralStatisticsクラスが選択された理由のひとつでした。

 以上に加えて、さらに別の Decorator を重ねる事も可能です。QuantLibライプラリーの中でも、追加の Decorator がいくつか提供されていますが、ここではプログラムコードは示しません。そのひとつに、SequenceStatisticsクラスがあります。このクラスは、標本集団がひとつでは無く、(標本集団の)配列として存在している場合に使われます。このクラスは、内部的にはスカラーの Statisticsインスタンスの配列を使っており、各標本集団間の相関や共分散の計算アルゴリズムが追加されています。このクラスは、例えば、LIBOR Market Modelのような、異なる LIBOR期間のキャッシュフローについて、それぞれに標本集団が必要な場合に使われます。その他には、ConvergenceStatisticsクラスや DiscrepancyStatisticsクラスがあり、これらは、標本集団の配列に関する情報を計算して提供しています。このクラスは Libraryの他の所では使われていませんが、少なくとも単体テストはやりました。

Linear Algebra: 線形代数(ベクトルと行列)

 下記に Arrayクラスと Matrixクラスの概要を示しますが、これらについて説明すべき事は、それ程ありません。

Listing A.21:ArrayクラスとMatrixクラスの概要
    class Array {
      public:
        explicit Array(Size size = 0);
        // ... other constructors ...
        Array(const Array&);
        Array(const Disposable<Array>&);
        Array& operator=(const Array&);
        Array& operator=(const Disposable<Array>&);

        const Array& operator+=(const Array&);
        const Array& operator+=(Real);
        // ... other operators ...

        Real operator[](Size) const;
        Real& operator[](Size);

        void swap(Array&);
        // ... iterators and other utilities ...
      private:
        boost::scoped_array<Real> data_;
        Size n_;
    };

    Disposable<Array> operator+(const Array&, const Array&);
    Disposable<Array> operator+(const Array&, Real);
    // ... other operators and functions ...

    class Matrix {
      public:
        Matrix(Size rows, Size columns);
        // ... other constructors, assignment operators etc. ...

        const_row_iterator operator[](Size) const;
        row_iterator operator[](Size);
        Real& operator()(Size i, Size j) const;

        // ... iterators and other utilities ...
    };

    Disposable<Matrix> operator+(const Matrix&, const Matrix&);
    // ... other operators and functions ...

 このクラスが提供しているインターフェースは、皆さんの予想通りのものです。すなわち、コンストラクターと代入演算子(=)、配列要素へのアクセス演算子(注:Arrayクラスは a[i] という記法を使い、Matrixクラスでは、ユーザーにより便利なように、m[i][j]m(i,j) という2種類の記法を用意しています)、さらに配列や行列の要素毎に行われる算術演算子とユーティリティーメソッドです。
(注:a * b という算術演算子が、ドット積ではなく、要素毎の積を返すように設計されている点は、どうも気になります。でも、それは私だけのようで、他のプログラマー達は気にならないようです)(訳注:ソースコードを見ると、* 演算子は、スカラー積と内積の両方で使えるようオーバーロードされており、内積については、別途 DotProduct( ) というメソッドが Arrayクラスにだけ用意されている。)
 このクラスには、要素サイズを変更するメソッドや、通常のコンテナクラスが持っているようなメソッドはありません。理由は、これらのクラスは数学的演算の為に使われるクラスであり、コンテナクラスとして使われる事を想定していないからです。データの保管は、単純な scoped_ptr を使って行っており(訳注:上記コードでは、boost::scoped_arrayを使っている)このクラスがメモリーの生成消去管理を行っています。

 Arrayクラスでは、Abs()Log()といったメソッドも提供しています。C++ 開発者コミュニティーでの良き開発者でいるために、これらのメソッドを std の namespace にある同じ名前のメソッドをオーバーロードしないようにしています。なぜなら、C++ の標準でそれが禁止されているからです。より複雑な計算機能(例えば、行列の平方根や、様々な行列の分解方法など)は、別のモジュールで提供されています。

 端的にまとめると、このクラスはベクトルや行列の基本的な機能を忠実に実装しているだけです。一点だけ、意味が明解でない内容の部分は、Disposableクラステンプレートを使っている所ですが、この Appendixの別のセクションで詳しく説明します。ここでは、このクラスを使用したのは、C++11の標準が発表される前に、move semanticsと同様の効果を試しただけ、と述べておきます。すなわち、抽象化によるメモリー負荷を軽減したかったという事です。演算子のオーバーロードは大変便利なものです。c=a+b という記述の方が、add(a,b,c) という記述よりもはるかに解りやすいですが、デメリット無しでという訳にはいきません。例えば、

    Array operator+(const Array& a, const Array& b);

 という関数宣言では、この演算子は新しいArrayインスタンスを生成し、それを返すようにしなければなりません。すなわち、その為にメモリーを確保し、そのメモリーをコピーする動作が必要になります。そうすると、演算の回数が増えるほど、それにかかる計算負荷は大きくなっていきます。

 ライブラリーの最初のバージョンでは、その問題に対し、expression templates(式テンプレート)を使って対応しようとしていました。(以下にそれを簡単に解説しますが、詳細は巻末(Veldhuizen, 2000)を参照して下さい。)その考え方は、演算子は Arrayのインスタンスを返すのではなく、式中の各項を示す参照を持つ parse treeのようなものを返します(訳注:parse treeとは、式の構文を解析して演算の順序を示すフローチャートに変換されたもの)。従って、例えば、2*a+b といった算術表現は、実際にその計算をするのではなく、計算に必要な情報を持ったアルゴリズムの構造を生成するようにします。式テンプレートに配列が代入された時にだけ、算術式が解析され、その時点でコンパイラーが内容を解析し、計算ループをひとつ生成し、計算を実行して結果を代入された配列にコピーします。

 このプログラム技術は今でも有効(コンパイラーの技術が進歩した今では、より重要)ですが、我々は開発開始から数年後に、この方法を止める事にしました。理由は、コードを読むのが難しく、メンテナンスが大変な事です。コードの複雑さについては、今の実装コードと、かつての下記のようなコードを比べてみて下さい。

   VectorialExpression<
       BinaryVectorialExpression<
           Array::const_iterator, Array::const_iterator, Add> >
   operator+(const Array& v1, const Array& v2);

 それと、すべてのコンパイラーがこの処理を実行できず、expression templatesとより単純な実装コードの両方をメンテナンスせざるを得なかったからです。従って、C++ の開発者コミュニティーで move semanticsの話が開始され、その実装方法のアイデアが登場した際に、それをヒントにして Disposableクラスを使う事にしました。

 既に述べた通り、コンパイラーの性能が近年大幅に向上し、すべてのコンパイラーが expression-templateの実装に対応するようになり、その技術も向上しました。しかし、仮に今プログラムコードを設計するとしたら、問題は ArrayMatrixクラスをそもそも設計する必要があったかどうかです。最低限の機能は、std::valarray を使って自分で設計し実装できると思います。しかし、boostライブラリーが提供している uBLAS といった既存のライブラリーを使うのがいいでしょう。これらは、数値計算のコードの専門家によって書かれたものであり、すでに QuantLibライブラリーの中で、いくつか使われています。

Global Settings: ライブラリー全体の変数と定数の設定

 下記 Listing A.22に示すSettingsクラスは、Singleton(後で説明します)の一種でQuantLibライブラリー全体に渡る情報を格納するオブジェクトです。

Listing A.22:Settingsクラスの概要
    class Settings : public Singleton<Settings> {
      private:
        class DateProxy : public ObservableValue<Date> {
            DateProxy();
            operator Date() const;
            ...
        };
        ... // more implementation details
      public:
        DateProxy& evaluationDate();
        const DateProxy& evaluationDate() const;
        boost::optional<bool>& includeTodaysCashFlows();
        boost::optional<bool> includeTodaysCashFlows() const;
        ...
    };

 このクラスのメンバー変数は、ほとんどが(何等かの状態を示す)フラッグで、それぞれの意味については 正式なDocumentationをご覧下さい。仮にその意味が判らなくても、ライブラリーの使用は問題ありません。ユーザーがきちんと管理しなければならない情報は Evaluation Date(評価日)だけです。そのデフォールト設定は“本日”で、それは(通常)金融商品の時価評価基準日であり、かつ各種インデックスの Fixing値を決める日でもあります。

 この情報をどう取り扱うかは難問でした。金融商品の価格計算は、Evaluation Dateに依存しており、日付が変更になれば当然、その通知を受ける必要があります。その機能は、必要な情報を、proxyクラス(訳注:特定のクラスのインスタンスをメンバー変数として取り込み、別の機能を加えた上で、あたかもその特定クラスの代理として使われるクラス)の中に包み込んで、間接的に返す事によって実現されています。このクラスにある、その機能を担うメソッド群を見れば、それが判ります。この proxyクラス(訳注:上記コードにある DateProxyクラス)は ObservableValueクラステンプレート(次のListing A.23参照)から派生しています。このクラスは、黙示的に Observableクラスに転換され、さらに代入演算子をオーバーロードして、データ変更時に(登録されているObserver群に)変更通知を送る機能を与えられています。それと同時に、この proxyクラスは、内包されたデータ(訳注:ここでは Dateクラスのインスタンス)に自動的に変換する機能も持っています。

Listing A.23:ObservableValueクラスの概要
    template <class T>
    class ObservableValue {
      public:
        // initialization and assignment
        ObservableValue(const T& t)
        : value(t), observable_(new Observable) {}
        ObservableValue<T>& operator=(const T& t)  {
          value_ = t;
          observable_->notifyObservers();
          return *this;
        }
        // implicit conversions
        operator T() const { return value_; }
        operator boost::shared_ptr<Observable>() const {
          return observable_;
        }
      private:
        T value_;
        boost::shared_ptr<Observable> observable_;
    };

 このクラスを使えば、今説明した機能を、自然な構文で使えます。
 まず、Observerインスタンスを Observable(訳注:ここでは evaluationDate()が返す DateProxyインスタンス) に登録するには、次の様な構文で出来ます。

    registerWith(Settings::instance().evaluationDate());

 また、次のような構文で、evaluationDate()の返し値を、Dateインスタンスのように使えます。

    Date d2 = calendar.adjust(Settings::instance().evaluationDate()); 

 この構文は、(evaluationDate()が返す DateProxyインスタンスを)自動的に Date型に変えています(訳注:オーバーロードされた代入演算子”=”が、その機能を提供している)。
 さらに3点目として、下の構文のように evaluationDate( )が返すDateProxyインスタンスに、特定の日付を代入できます。(訳注:これも代入演算子“=”をオーバーロードして機能させている。)

   Settings::instance().evaluationDate() = d;

 さらに、この代入動作の直後、日付の更新があった事を、すべてのObserverに通知します。

*    *    *

 もちろん、誰もが気づいている問題は、グローバル変数としての Evaluation Date(時価評価基準日)しか持っていないという点です。そこから来る明らかな弱点は、異なる Evaluation Dateを使って商品の時価評価を、並行して行えないという事です(少なくとも、QuantLibライブラリーの Configuration(環境設定)をそのまま使うとすればですが)。

 という事は、環境設定を操作すれば、何とかなりそうですが、問題は簡単ではありません。まず、(ユーザーが若干手を加えれば) Settingsインスタンスを スレッド毎に持たせるようにコンパイル時のフラッグを設定する事は可能ですが、後で説明する通り、これですべての問題が解決される訳ではありません。また別の問題として、シングルスレッドでプログラムを走らせていても、この Global変数はあまり好ましくない現象を起こす可能性があります。仮に、あるひとつの商品だけを、異なる Evaluation Dateを使って時価評価した場合、Evaluation Dateを元の日に戻した時点で、その日付変更によって、他のすべての商品の時価の再計算を起動してしまうことになります。

 この状況を解決するには、Global Settingsクラスを何等かの contextクラス(訳注:どのスレッドで、そのプロセスが選択されたのかを示す指標を管理するクラス)に取り換えるべきとの方向性を示唆しています(この問題について、我々の間で話をした時、何人かの鋭い人からも同じアイデアが出ました)。しかし、ある特定の価格計算において、どうやって contextを選択すればいいのでしょうか?

 Instrumentsクラスの中にsetContext()メソッドを加えるアイデアは、使えそうに見えるかも知れません。そうする事によって、計算の際に、Instrumentsは価格エンジンに対しcontextを伝え、その価格エンジンからさらに価格計算に使われるTermStructureインスタンスなどにそれを伝達する方法です。しかし、このロジックを実装するのは簡単ではありません。

 まず、Instrumentsインスタンスも価格エンジンインスタンスも、価格計算に必要な TermStructureクラスが何か常に判っている訳ではありません。例えば、Swapは、いくつかのクーポンキャッシュフローを持っていますが、その一部は価格計算において Forecastingカーブの情報を参照している可能性があります。その情報を得る為には、関連するすべてのクラスに、必要な機能を付け加える必要があります。そもそも Couponオブジェクトに contextオブジェクトを設定するのは、いいアイデアとは思いません。

 次に、そしてより重要な点ですが、価格エンジンに contextを設定する動作は、データが変更される動作 (mutating operation) を内包しています。Instrumentsに、その context設定の役割をまかせると、(Instrumentsから)constメソッドであるはずの NPV()メソッドを呼び出している最中に、価格エンジン中のcontext情報が変更されてしまう可能性があります。この状況は、簡単に race condition(同じ動作で異なった計算結果を出す状況)を発生させます。例えば、一見害のなさそうな計算として、2つの商品が、異なる Evaluation Dateを基準に、同じ Discountカーブを使って時価評価しようとすると、どうなるでしょう。並列処理プログラミングの経験がほとんどないユーザーが、夢にも思わなかった事が起こり得ます。例えば、同時に走っている2つのスレッドから、同じ(TermStructureインスタンスへの)ハンドルにリンクされてしまう事があり得ます。仮にそうなっても、データ変更の動作が constメソッドの中に隠されているので、ユーザーはそれに気づかない可能性があります。
 (注:“ちょっと待って。NPV()を呼び出している間に、他にもデータを変更するような動作があるのでは?”と言われるかも知れません。よく気づかれました。このセクションの最後にある Asideの欄を見てください。)

 そうすると、どうも価格計算を始める前に contextを設定する必要がありそうです。であれば、Instrumentsにそれをまかせる方法は排除されます(なぜなら、(価格エンジンにcontextを設定する場合と)同様に、ある商品に contextを設定した場合、その商品が使う TermStructureインスタンスについて、同じインスタンスを既に使っていた別の商品により設定された context を無効にしてしまう可能性があるからです)。おそらく、価格計算に使う複数の TermStructureインスタンスに、明示的に contextを設定しなければならないでしょう。その方向性を取った場合のメリットは、知らずに同じオブジェクトに対し同じ contextを設定しようとしてrace conditionに陥るリスクを無くせる事です。逆にデメリットは、設定がより複雑になる事と、異なる contextを同時に設定する場合は、TermStructureインスタンスの複製を作る必要があるという事でしょう。例えば異なるEvaluation Dateでの価格計算を並行処理する場合、OISディスカウンティングカーブを2つ用意しないといけなくなります。かつ、その場合、スレッド事の Singletonを管理しないといけなくなるでしょう。

 最後に、contextが渡されても保存されないような場合について簡単に触れましょう。その場合、メソッドの呼び出し方法が次のような形になるでしょう。

    termStructure->discount(t, context);

 これだと、cashingを完全に壊し、すべての関係者に不愉快な思いをさせそうです。もしこのような物を望むなら、Haskellでプログラムを書いたでしょう。

 まとめると、このセクションンを暗い気持ちで終わるのは嫌ですが、すべてがうまく行く方法はありません。Global変数の設定は制約になりますが、私自身解決策を持ち合わせていません。さらに悪いことに、取り得る変更方法は、複雑さを増大させます。QuantLibライブラリーを初めて使う人に、Black-Scholesの公式を探しているのなら、TermStructure や Quotes や Instrumentsや Engineといったクラスだけでなく、contextも必要ですと説明する事になるでしょう。このセクションの説明で少しは役に立ったでしょうか?

Utilities: ユーティリティークラス

 QuantLibでは、金融に関する概念以外の概念も、いくつかのクラスや関数によってオブジェクトモデル化されています。これらは、ライブラリー全体の土台を築くのに使われるボルトやナットのような役割をしています。その内のいくつかについて、このセクションで説明します。

Smart Pointers and Handles: スマートポインターとハンドル

 実行時 polymorphism (多相性)を使っている事により、(大半とは言いませんが)多くのオブジェクトが Heapメモリー上に領域を割り当てられています。この事は、メモリー管理の問題を発生させます。他のプログラム言語では、その役割は備え付けの Garbage Collection ( Heap上の不要なメモリー領域を管理し消去する機能)が行っていますが、C++ では開発者自身で管理する必要があります。

 メモリー管理は、殆どが過去の問題で、現時点で多くの問題が残っている訳ではありません。この問題への対応はやっかいなので(特に、例外処理が発生した場合)、マニュアル管理ではなく、できるだけ自動的にできるような方法を探してきました。

 C++ 開発者にとって、この問題と戦う為の最善の武器は、スマートポインターです。これは、C++ に備え付けのポインターと同じ機能を持つと同時に、ポインターが指し示すオブジェクト用に、メモリーの生成や維持管理を行い、また不要になれば破棄するという動作まで行っています。このような機能を持つクラスの実装例はたくさんありますが、我々は Boostライブラリーが提供しているスマートポインターを選択しました (特に shared_ptr で、今では ANSI/ISO の C++ 標準ライブラリーにも含まれています)。このポインターの詳しい説明は Boostライブラリーの文書を見て下さい。QuantLibは、このスマートポインターを使う事により、メモリー管理が完全に自動化されているという点だけ述べておきます。様々なオブジェクトがあちこちで、動的にメモリー配分されていますが、QuantLibの何万行ものコードの中で、ひとつも delete を使っていません。

 “ポインターへのポインター”も、スマートポインターと同じ様な機能(訳注:メモリーの領域取得と破棄を自動的に行う機能)をするクラスを使って用意しています(“ポインターへのポインター”について再確認したい方は、このセクションの最後のAsideをご覧下さい)。我々は、単にスマートポインターへのスマートポインターを使うような選択をしませんでした。仮にそうすると、次のようなコードになりますが、Emacs (訳注:UNIX環境のプログラマーに人気のテキストエディタで、キーボード操作だけで素早くプログラムを書ける)を使ったとしても、長たらしいコードを書くのは面倒です。

    boost::shared_ptr<boost::shared_ptr<YieldTermStructure> >

 また、この構文の中の内側のポインターは、動的に配分される必要がありますが、あまり気持ちよくありません。さらに、(訳注:Observableへの監視が、さらに間接的になるので) Observerパターンを実装するのが難しくなります。

 そこで、QuantLibでは、Handleというクラステンプレートを用意しています。その実装内容を下記 Listing A.24 に示しますが、このクラスの機能は、スマートポインター(shared_ptr<Type>)をメンバー変数に持つ“Link”という介在役の内部クラスを利用しています。Handleクラスは、それを使って、Link型インスタンスへのポインター(shared_ptr<Link<Type>>)をメンバー変数として保持し、かつそれを使い易くする為のメソッド群を用意しています。ある Handleインスタンスのコピーは、すべて同じ Linkインスタンスを共有するので、そのリンク先のオブジェクトに変更があった場合でも、それがすべての Handleインスタンスに自動的に反映されます。

Listing A.24:Handleクラステンプレートの概要
    template <class Type>
    class Handle {
      protected:
        class Link : public Observable, public Observer {
          public:
            explicit Link(const shared_ptr<Type>& h =
                                         shared_ptr<Type>());
            void linkTo(const shared_ptr<Type>&);
            bool empty() const;
            void update() { notifyObservers(); }
          private:
            shared_ptr<Type> h_;
        };
        boost::shared_ptr<Link<Type> > link_;
      public:
        explicit Handle(const shared_ptr<Type>& h =
                                         shared_ptr<Type>());
        const shared_ptr<Type>& operator->() const;
        const shared_ptr<Type>& operator*() const;
        bool empty() const;
        operator boost::shared_ptr<Observable>() const;
    };

    template <class Type>
    class RelinkableHandle : public Handle<Type> {
      public:
        explicit RelinkableHandle(const shared_ptr<Type>& h =
                                         shared_ptr<Type>());
        void linkTo(const boost::shared_ptr<Type>&);
    };

 さらに Handleクラスは、shared_ptr<Link<Type>>型の変数によって、他のクラスのObservableとなる機能も与えられています。即ち、Linkクラスは Observer と Observable から派生しており、自分が監視している Observableから(データ更新の)通知を受けた場合、それを、自分を監視している Observerに転送する機能を持ちます。また、自分がポイントしているオブジェクトが入れ替わった場合にも、同様に Observerに通知します。Handleクラスは、この機能を、Linkへのポインターを返す shared_ptr<Observable>オペレーターの自動型変換を定義する事によって実現しています。従って、次のような宣言は構文上許されており、期待通りの動作をします。

    registerWith(h);

 登録された Observerは、Linkインスタンスと(間接的に)それが指し示すオブジェクトから通知を受ける事になります。

 Handleを別のオブジェクトに再リンクする(すなわち、ある Handleインスタンスのコピーも同時にすべて、リンクし直した後の別のオブジェクトを指し示す)方法が、Handleクラスそのものではなく、派生クラスである RelinkableHandleクラスに与えられている点に気づかれたでしょうか。このようにした理由は、再リンクの機能を使う事ができるHandleインスタンスと、その機能を使えないHandleインスタンスを、区別して間違えないようにする為です。Handleクラスの典型的な使用例では、Handleインスタンスが生成された後、様々な金融商品オブジェクトや価格エンジン、その他のオブジェクトに渡されて、それらのオブジェクト毎に、その Handleインスタンスのコピーが保存され、使用されます。ここで重要な点は、Handleのコピーを保持しているオブジェクトに Handleが指し示しているポインターの再リンクを、どのような理由があっても、許してはならないという事です (あるいは、そのオブジェクトのインスペクター関数を使って、オブジェクトの外から Handleインスタンスにアクセスできるようにしている場合には、その外からのアクセス元にも同様)。仮にそうしてしまうと、他のオブジェクトにひどい影響を与える可能性があります。(注:そういった影響は、思ったよりも頻繁に起こり得ます。実際に、それでひどい目にあいました)

 再リンクは、オリジナルのHandleインスタンス(Master Handleとでも呼びましょうか)からのみ、変更が許されるべきなのです。

 人間にその管理を任せると間違いを起こしやすいので、我々は、それをコンパイラーに強制させるようにしました。しかし、linkTo()メソッドを const にした上で、インスペクター関数から、const な Handle を返すような方法では、機能しません。なぜなら クライアントコード(ユーザーーが独自に開発したコード)で Handleインスタンスの non-const なコピーが、簡単に作れてしまうからです。そこで、我々は、linkTo()メソッドを Handleクラスのインターフェースから取り除き、派生クラスに移しました (訳注:再リンクの機能を、RelinkableHandleクラスという特別な派生クラスにのみ許可している)。C++ にある型変換の仕組みを使えば、これでうまくいきます。まず、マスターHandle の役割を持つインスタンスを、RelinkableHandleクラスを使って生成します。そのインスタンスを、同じポイント先のデータを必要とする他のオブジェクトに渡します。その際、派生クラスからベースクラスへの自動型変換が働き、そのオブジェクトは完全に機能する Handleインスタンスのコピーを受け取ります。一方で、Handleインスタンスのコピーがインスペクター関数から返された場合、それを RelinkableHandle へ Down-Castする方法はありません。(訳注:その結果、Link先のポインターの変更は RelinkableHandleのマスターインスタンスでしか行えないことになる)

Error Reporting: 例外処理

 QuantLibライブラリーの中には、入力値の条件チェックを行うべき場所が至る所にあります。このチェックの方法については、

    if (i >= v.size())
        throw Error("index out of range");

 という風にやるのでは無く、次の構文のようにチェックの意図がより明確に解るような方法を取りたいと考えました。

    require(i < v.size(), "index out of range");

 この構文であれば、条件が満たされない事をチェックするのではなく条件が満たされる事を要求するような表現になります。さらにそれ自体の意味が自明な requireensureassertという単語を使って、チェックの意図(入力値のチェックなのか、出力値のチェックなのか、プログラムのエラーチェックなのか)が解るようなプログラムコードが書けます。

 我々は、そのような syntax(構文)を、マクロを使って提供しています。読者の方が“そんなの止めてくれ”と言っているのが聞こえます。その通り、マクロは良くないと思われているし、実際にこれによって若干の問題も発生しています。それについては下記で説明します。しかしこの場合は、関数を使う方がより大きなデメリットをもたらします。なぜなら関数は、すべての引数を評価するからです。次のような、やや複雑な例外処理のメッセージを生成する場合が多々あります。

    require(i < v.size(),
        "index " + to_string(i) + " out of range");

 この場合、もし require()が関数であるなら、(2つめの引数である)メッセージの部分は、条件が満たされようが無かろうが、必ず生成されます。そうすると、場合によっては許容できないほどのパフォーマンス悪化をもたらします。もし同じ構文をマクロで作ると、マクロの部分は実際には次のような構文で置き換えられます。

    if ( !( i < v.size() ) )
        throw Error ("index " + to_string(i) + " out of range");

 こうすれば、例外処理のメッセージは、条件が満たされなかった場合のみ生成されます。

 次の Listing A.25 は、そういったマクロのひとつ QL_REQUIRE の現バージョンを示しています。その他のマクロも同じ様に定義されています。

Listing A.25:QL_REQUIREマクロの定義
    #define QL_REQUIRE(condition,message) \
    if (!(condition)) { \
        std::ostringstream _ql_msg_stream; \
        _ql_msg_stream << message; \
        throw QuantLib::Error(__FILE__,__LINE__, \
                              BOOST_CURRENT_FUNCTION, \
                              _ql_msg_stream.str()); \
    } else 

 この定義の方法を使えば、エラー時に、さらにいくつかの警告メッセージが出されます。ひとつ目は、メッセージを生成するのに標準ライブラリーの ostringstreamを使っているので、次のような構文が使えます。

    QL_REQUIRE(i < v.size(),
              "index " << i << " out of range");

 (この方法がどのように動作するか理解する為に、マクロの本体を一部修正して動作確認してみて下さい。) ふたつ目は、例外処理に飛ぶ場合、Errorインスタンスに、今走っている関数の名前と例外処理に飛んだコードの line とファイル名が渡されます。コンパイル時のフラッグ設定によりますが、この情報もエラーメッセージの中に含める事が可能で、プログラマーの問題発見の参考になります。QuantLibライブラリーでのデフォールトの設定は、ユーザーにとっての便益があまり無いので、この情報をメッセージに含めないようになっています。最後に、マクロ構文の最後になぜ else が付いているか疑問に思うでしょう。マクロに共通する落とし穴で、lexical scope の欠如と呼ばれています。else以下は、次のように何らかのプログラムコードを追加する必要があります。

    if (someCondition())
        QL_REQUIRE(i < v.size(), "index out of bounds");
    else
        doSomethingElse();

 マクロの中に else が含まれていなかったとすると、上記のコードは意図した通りに動作しません。else をマクロの外でプログラムコードの中に入れた時、マクロ中の if 文に対する else と組み合わされ、次のようなコードが書かれたと解釈されてしまいます。

    if (someCondition()) {
        if (!(i < v.size()))
            throw Error("index out of bounds");
        else
            doSomethingElse();
    }

 これは、意図した動作ではありません。

 最後の注意点として、これらのマクロの不利益な点についても説明しなければなりません。現状のマクロの記述では、例外処理に飛ぶと、引数に含まれているメッセージしか返す事が出来ません。その他のチェックすべきデータのインスペクター関数が全く定義されていません。例えば、インデックスが“範囲外”とのメッセージの中にそのインデックスを含める事が出来ますが、例外処理のプロセスの中にそのインデックスを integer として返すメソッドがありません。従って、その情報はユーザーが Displayで見る事は出来ますが、例外処理後にエラーをリカバリーするプログラムコードの中で使う事が出来ません。だれかがメッセージからその情報を切り取る以外は。しかし、そのメリットはそのコードを書く手間に見合いません。従って、今の所、解決策はありません。もし読者の中で良いアイデアをお持ちなら、そのコードを送って下さい。

Disposable Objects (move semantics の代用)

 Disposableクラステンプレートは、C++98 準拠のコードで、move semantics を実現する為の試みでした。(訳注:move semanticsについては ”右辺値参照・ムーブセマンティクス”) そのアイデアを提唱した人の功績を称える為、そのアイデアを取ってきた原典を紹介します。それはAndrei Alexandrescu氏の論文で(巻末(Alexandrescu, 2003)参照)、そこでは、関数が値を返す際に一時的に生成されるオブジェクトのコピーを省略する方法を提示しました。

 基本的なアイデアは、彼の文献が発表された当時、すでに広まりつつあったものですが、最終的に C++11で正式に取り入れられました。すなわち、一時的に生成されたオブジェクト(右辺値)を、他のオブジェクト(左辺値)にコピーする場合、場合によっては、その一時オブジェクトを代入先と交換する方が、プロセス効率が上がる場合があります。例えば、一時的に生成された配列変数を動かしたい時(要素をすべて別の配列変数にすべて移す場合)どうしますか? 新しい配列変数を作って、一時的な配列変数が示すPointee(配列の中身)の先頭アドレスをコピーする方が、配列変数の要素をすべてコピーするより早いですよね。新しい C++では、rvalue reference(右辺値参照)の概念を使って move semanticsをサポートしています。(巻末(Hinnant et al, 2006)
 コンパイラーは、一時的に生成されるオブジェクトの生成消去のタイミングが分っています。それを利用すれば(C++11準拠の)std::moveを使って、一時的に生成されたオブジェクトを、別のオブジェクトに移す事ができます。ところが、我々の実装方法は、次の Listing A.26 にあるように、そのようなコンパイラーのサポートを使わずに実現しようとするものです。その結果どうなったか、すぐ後に説明します。

Listing A.26 :Disposable クラステンプレートの実装
    template <class T>
    class Disposable : public T {
      public:
        Disposable(T& t) {
            this->swap(t);
        }
        Disposable(const Disposable<T>& t) : T() {
            this->swap(const_cast<Disposable<T>&>(t));
        }
        Disposable<T>& operator=(const Disposable<T>& t) {
            this->swap(const_cast<Disposable<T>&>(t));
            return *this;
        }
    };

 このクラスそのものに、あまり注目すべき所はありません。このクラスの動作は、swap()メソッドを実装する際に使われるテンプレート引数に依存します。このメソッドを使って、引数のインスタンスの中に含まれるデータを(望むべくは効率よく)コピーではなく、スワップします。コンストラクターと代入演算子はすべて、この swap()メソッドを使って、インスタンスの中のデータを、コピーせずに新しいオブジェクトに移動させます。 

 Disposableインスタンスを別の Disposableインスタンスを使って構築する場合は、const参照を使って、構築元のオブジェクトを受取ります。なぜなら引数のオブジェクトを、一時変数のままにしておく必要があるからです(実際に Disposableオブジェクトの殆どは、一時変数です)。従って、実際に swap()メソッドを起動した時、Disposable の中身を使う必要があるので、そこで const_cast を使う必要があります。(訳注:const_cast により引数の Disposable の constを外し、内容を変更可能にする為) Disposable を Disposable以外のオブジェクトから作る場合は、反対にそれを non-const な参照として取ります。これは、破壊的な型変換を起こしたり、使えると思ったオブジェクトが空っぽだった状況に陥るのを防ぐ為です。この事はしかし、デメリットもあります。それについては、少し後で触れます。

 次の Listing A.27 は Disposableを(テンプレート引数で特定された)クラスに改造する方法を示しています。ここでは Arrayクラスを例に使っています。

Listing A.27 : Arrayクラスを使った、Disposableクラステンプレートの使用法
    Array::Array(const Disposable<Array>& from)
    : data_((Real*)(0)), n_(0) {
        swap(const_cast<Disposable<Array>&>(from));
    }

    Array& Array::operator=(const Disposable<Array>& from) {
        swap(const_cast<Disposable<Array>&>(from));
        return *this;
    }

    void Array::swap(Array& from) {
        data_.swap(from.data_);
        std::swap(n_,from.n_);
    }

 ご覧の通り、Disposableを引数で取る、コンストラクターおよび代入演算子を追加で定義する必要があります。( C++11では、これらは、rvalue reference(右辺値参照)を取る moveコンストラクターと move代入演算子になります。)それと同時に、コンストラクターと代入演算子の中で使われる swap()メソッドも実装しなければなりません。ここでもコンストラクターは、一時変数を拘束する為に、引数を const参照として取り、後で const_cast して const を外しています。しかし、今思えば、引数をコピーで取るような、もうひとつの効率的なswap()メソッドを追加してもよかったかも知れません。

 最後に、関数からの返される値としてDisposable を使う方法についてです。以下のコードをご覧下さい。

    Disposable<Array> ones(Size n) {
        Array result(n, 1.0);
        return result;
    }

    Array a = ones(10);

 ones()メソッドが Array型を返すと、それが Disposable<Array>に型変換され、それをさらに Array型の変数 a に代入すると、その内容が a とスワップされます。ここで、先ほど Disposableコンストラクターが安全に non-const の参照を取る場合のデメリットについて述べて事を思い出して下さい。デメリットとは、一時変数を bind 出来ないという事です。従って、上のコードの ones() を以下のように簡略化する事はできません。 (訳注:上のコードでは、関数内で一旦Array型のローカル変数result(左辺値)を生成して、それを返すようにしているが、下記コードでは、Arrayのコンストラクターの返す値(Array型のインスタンスで右辺値) を関数の返値としている)

    Disposable<Array> ones(Size n) {
        return Array(n, 1.0);
    }

 なぜなら、このコードはコンパイルできません。その結果、より冗長な構文を強いられ、かつそのArray配列に変数名を割り当てなければなりません。
 (注:実際には強いられている訳ではありません。例えば 上のコードで、Arrayを返すのではなく、Disposable<Array> (Array(n,10)) を返すようにすれば、ローカル変数をわざわざ生成する必要はありません。しかし、これではローカル変数を生成するよりも、さらに判りにくいでしょう。)

 現時点では (C++11以降に準拠すれば)、もちろん右辺値参照と moveコンストラクターを使って、以上のような事(訳注:わざわざ Disposableクラステンプレートを作り、さらに各クラスでコンストラクターと代入演算子をオーバーロードする作業)を忘れ去る事ができます。さらに正直に申し上げると、Disposable はコンパイラーの邪魔をして、意図したメリットよりもデメリットの方が多いのではと疑っています。実は、最新の C++ に準拠すれば、上記のようなシンプルなコードで、かつ抽象化のペナルティーを避ける方法があるのをご存知ですか? それは、次の様なものです。

    Array ones(Size n) {
        return Array(n, 1.0);
    }

    Array a = ones(10);

 C++17では、Arrayを返す際や、代入する際に行われていた(一次変数への)コピーの動作が、実は省略されています(すなわち、コンパイラーによって、代入先の変数のメモリー領域を、代入元の領域にあらかじめ確保するようなコードが生成されている)。最新のコンパイラーは、この動作が C++ 準拠と決まる前に、既にこういった省力化を行っていました。コンパイラーが自動的に行ってくれるこの動作は、RVO(Return Value Optimization)と呼ばれています。しかし乍ら、Disposable を使うと、コンパイラーが行うこの自動的な動作を妨げ、返って処理速度を遅くしてしまう可能性があります。

Designe Patterns: デザインパターン

 QuantLibライブラリーの中では、様々なデザインパターンが使われています。デザインパターンの詳細については、有名な Gang of Four(“4人組”) の本(巻末 (Gamma et al, 1995)参照)を参考にして下さい。ではなぜ、ここで私自身がデザインパターンについて書く必要があるのでしょうか? かつてG.K.Chestertonが記していた次の言葉があります。

Poets have been mysteriously silent on the subject of cheese(詩人は、“チーズ”については謎のごとく題材にしないものだ)

 そして、有名な4人組も、デザインパターンを実装する際に直面する現実的な問題については、何も述べていません(但し、それは彼らの問題では無い点注意して下さい)。デザインパターンの応用例はほとんど無限にありますが、彼らは4人しかいないのです。

 そこで、Appendixの最後のセクションを使って、QuantLibライブラリーの要件を満たす為、我々が使ったデザインパターンの実装方法を説明したいと思います。

The Observer Pattern: オブザーバーパターン

 QuantLibライブラリーでは、至る所で Observerパターンが使われています。既に、Chapter IIとIIIで、Instruments(金融商品)や TermStructure(期間構造)クラスが、このパターンを使って市場データの変動を常にウォッチし、必要な時に価格の再計算を行うのを見てきました。

 我々の実装方法は(概要を次の Listing A.28 に示します)4人組の本で説明された方法とほぼ同じです。しかし、先ほど述べた通り、この本では教えてくれていない、いくつかの疑問点や問題点があります。

Listing A.28: ObserverObservableクラスの概要
    class Observable {
        friend class Observer;
      public:
        void notifyObservers() {
            for (iterator i=observers_.begin();
                            i!=observers_.end(); ++i) {
                try {
                    (*i)->update();
                } catch (std::exception& e) {
                    // store information for later
                }
            }
        }
      private:
        void registerObserver(Observer* o) {
            observers_.insert(o);
        }
        void unregisterObserver(Observer*);
        list<Observer*> observers_;
    };

    class Observer {
      public:
        virtual ~Observer() {
            for (iterator i=observables_.begin();
                          i!=observables_.end(); ++i)
                (*i)->unregisterObserver(this);
        }
        void registerWith(const shared_ptr<Observable>& o) {
            o->registerObserver(this);
            observables_.insert(o);
        }
        void unregisterWith(const shared_ptr<Observable>&);
        virtual void update() = 0;
      private:
        list<shared_ptr<Observable> > observables_;
    };

 例えば、(ObservableからObserverに送るデータ更新の)“通知”の中に、どのような情報を含めたらいいのでしょうか? 我々は、それを最小限の情報に留めました。即ち、Observerオブジェクトが知るべき情報は、データの更新があったという事だけにしました。もっと多くの情報を渡す事も可能でした(例えば、update( )メソッドの引数に“通知”を発信しているObservableインスタンスを持たせるとか)。そうすれば、Observer側は何を再計算すればいいのか分るので、CPUクロックの数サイクル分、計算時間を節約できたかも知れません。しかし、それによるメリットは、その結果生じる複雑さのデメリットを上回るとは思いません。

 別の疑問点として、Observerの update()メソッドの最中に例外処理が発生した場合は、どうなるのでしょう? そのような事態は、ある Observable から“通知”を発信している最中、即ち Observable が登録している複数の Observerインスタンスに“通知”を順次発信している際に、その中のひとつの Observerインスタンスで起こり得ます。もし、そこで例外処理ルーチンに飛んでしまうと、Iteratorを使ったループ処理が中断し、残りの Observerインスタンスは“通知”を受け取れません。それは良くないですね。我々が取った方法は、何等かの例外をキャッチした場合でもループ処理は継続して行い、ループ終了後に例外処理ルーチンに飛ぶようにしました。この方法だと、発生した例外に関する情報が失われる難点がありますが、通知が中断する事と比べれば問題は小さいと考えました。

 3番目の疑問点に移ります。それはコピーされた際の動作に関するものです。Observerあるいは Observableインスタンスがコピーされた際に、どのように動作するのが正解なのか、あまり明確ではありません。現状は、我々が妥当だと考える方法で実装しています。まず Observableがコピーされた場合、そこに登録されている Observerインスタンスのリストはコピーされないようにしています。一方で、Observerがコピーされた場合、コピー元が登録されていた Observableに、そのコピーも登録されるようにしています。別の方法も選択可能でしたが、実際の所、正しい選択肢は、コピーそのものを禁止する事だったかもしれません。

 しかし乍ら、コピーを禁止せず上記の対応をした結果、2つの大きな問題が発生しています。ひとつ目は、Observer と Observableインスタンスの生成・消去を適切に管理しなければなりません。即ち、消去済みの Observerに“通知”が発信されないようにしなければなりません。それを実現する為、まず Observer に Observableインスタンスへの shared pointerを持たせるようにしました。それにより Observableを消去しようとしても、そこに登録されているすべての Observerにおいて Observableの shared pointerを消去するまでは、それが出来ないようにしました。一方で Observerインスタンスを消去する場合は、それが登録されているすべての Observableで、登録をはずすようにしています。そうすれば、Observableが Observerの生のポインターのリストを保持しても安全です。

 ところが、この shared pointer を使った対応については、(そのままでは)シングルスレッドの場合でしかきちんと動作しません。我々は QuantLibの bindings を C# や Java に exportしようとしていますが、そこでは残念ながら、別のスレッドで常に Garbage Collection のコードが走って、不要になったHeapメモリー上のオブジェクトを消去しています。その為、Observableからの“通知”が、半分消去された(訳注:文脈から、同じ変数を指す複数の shared pointer の中の一部のポインターだけを消去した状態と想定される)Observerインスタンスに送られると、不規則なクラッシュが起こります。この問題が発見された後、この Bug は boost::shared_ptrに備わっている裏技を使って修正されましたが(Klausさん、ありがとう)、その裏技については十分な文書が無く、将来これが std::shared_ptr に移行した後も有効かどうか、よく分かりません。また、この裏技により動作スピードが遅くなっています。その為、デフォールトの設定ではこの裏技を使わないようにしており、コンパイル時のスイッチ操作で使用可能に出来る様にしています。従って、もし C# や Java で QuantLibを使う場合にだけ、使用可能にして下さい。

 ふたつ目の大きな問題は(この問題は今でも続いています)、まるで Jerry Lee Lewis の歌“Whole Lotta Shakin’ Goin’ On”のように、大量の“通知”が飛び回る事です (Whole lotta notifin’ goin’ on)。ひとつのデータ更新が、何十あるいは何百もの通知発信を起動することがあります。ほとんどの update( )メソッドが、単にフラッグをセットしたり、通知を転送するだけであったとしても、数が膨らめば、時間もそれだけかかります。

 QuantLibライブラリーを実際に、CVA/XVA のような、計算時間が相当かかるようなアプリケーションの中で使っている人の中には、”通知”が自動的に発信される仕組みを止めて、再計算を指示された場合にだけ明示的に行うようにする事で回避しているケースもあります(Peterさん、そうですね?)。“通知”にかかる時間を減少させる為に、通知連鎖の途中にある転送するだけのオブジェクトは飛ばしてしまう方法はうまくいきません。理由は、“通知”の連絡網の至る所で使われている Handleクラスの為です。Handleオブジェクトは re-link が可能で、オブジェクトが生成された後でも連絡網のネットワークが変更されてしまいます。

 端的に言えば、この問題は未解決です。もし読者の方が良い解決方法をお持ちなら、連絡先はご存知ですよね。

The Singleton Pattern: シングルトンパターン

 かの“4人組”の人たちは、彼らの本の最初の部分を creational パターン(訳注:オブジェクトの生成に関するデザインパターン。Singleton Patternの他、Abstract Factory Pattern、 Builder Pattern、Factory Method Pattern、Prototype Patternが該当。詳細は Wiki ”Creational Pattern”参照)に費やしました。論理的にはその順番になるのでしょうが、この選択は残念な副作用を生み出す事になりました。この本を読んだ熱心なプログラマー達が、それに倣って、コードのあちこちに Abstract Factory や Singletonクラスのオブジェクトをまき散らしているのを、頻繁に見かけます。言わずもがなですが、その結果、プログラムコードの明瞭さの点で意図せざる事が起こりました。

 読者の方は、QuantLibライブラリーの中で Singletonクラステンプレートがあるのを見て、同じ理由によるものと疑われたかも知れません。(意地悪く言いますけど、それは見当違いです。)幸いにも、そうで無いという根拠は、バージョンコントロールの記録で示す事が出来ます。このクラスは、QuantLibライブラリーの中では、Observerパターン(behavioral pattern)や Compositeパターン(structural pattern)よりも後に加えられました。

 QuantLibライブラリーにおける Singleton のデフォールトの実装内容は下記 Listing A.29で示す通りです。

Listing A.29: Singletonクラステンプレートの実装内容
    template <class T>
    class Singleton : private noncopyable {
      public:
        static T& instance();
      protected:
        Singleton();
    };

    #if defined(QL_ENABLE_SESSIONS)
    // the definition must be provided by the user
    Integer sessionId();
    #endif

    template <class T>
    T& Singleton<T>::instance() {
        static map<Integer, shared_ptr<T> > instances_;
        #if defined(QL_ENABLE_SESSIONS)
        Integer id = sessionId();
        #else
        Integer id = 0;
        #endif
        shared_ptr<T>& instance = instances_[id];
        if (!instance)
            instance = shared_ptr<T>(new T);
        return *instance;
    }

 このクラスは、Curiously Recurring Template Pattern をベースに構築しています。このパターンについては、既に Chapter VII で解説しています。あるクラスのインスタンスが Singleton(インスタンスの存在を1つしか許さない)である為には、そのクラス CSingleton<C>クラステンプレートから派生させる必要があります。Singleton は privateで宣言されたコンストラクターを提供し、その派生クラスは Singleton<C>をフレンドクラスとして宣言することによって、その privateなコンストラクターを使用できます。この仕組みについては、Settingsクラスの実装例をご覧下さい。

 Scott Meyers(巻末 (Meyers, 2005)参照)が提言している通り、Singletonのインスタンス(訳注:原文ではinstances と複数になっている)は、instance()メソッドの中で static変数として宣言されている map<Integer, shared_ptr<C> > オブジェクトに保持されます(Singletonなのに、なぜ複数のインスタンスを想定しているかの説明は後ほど)。こうする事によって、いわゆる static initialization order fiasco(静的オブジェクトの初期化順序の失敗。staticオブジェクト(変数)が、それが初期化される前に他のオブジェクトで使用される時に発生する) の問題を回避し、さらに C++11 の環境では、そのオブジェクトの初期化は thread-safeになる事が保証されています。(後で見る通り、これで問題がすべて終わりという訳ではありませんが)

 さて、読者の方はいくつかの疑問を持たれたかと思います。例えば、Singletonであるはずのインスタンスを、なぜインスタンスの map< > とするのか?(訳注:たった一つのインスタンスしか必要としないのに、なぜ複数のインスタンスを想定している map< > を使ってインデックスと対応付けているのか?) 実は、1つのインスタンスしか持たないと、それによる制約が発生するからです。例えば、ユーザーが、同時に異なる評価日を使って時価計算を行いたいと考えるかもしれません(訳注:時価評価日”EvaluationDate”クラスは、Singletonとして宣言されている)。そういった場合への対応として、我々はスレッド毎にひとつの Singletonを持たせる事を許容しました。その選択は、コンパイル時のフラッグにより設定できるようになっており、instance()メソッドの中の #if 文による分岐を使って sessionID()関数から、スレッドIDを取ってくるようにし、それを map<Integer, shared_ptr<C> >におけるインデックス用整数として使っています。コンパイル時フラッグで、スレッド毎の Singletonインスタンスの許容を選択した場合、ユーザー自身で sessionID()関数を実装しなければなりません。その関数は、おそらく次のようなかたちになるでしょう。

    Integer sessionId() {
        return /* some unique thread id from your system API */ ;
    }

 この関数の中で、ユーザーはOSが提供している関数(あるいはスレッドを管理用の Libraryが提供する関数)を使ってスレッドを特定し、そのスレッドID を整数に変換して返すようにします。その結果、instance()メソッドは各スレッドに対応する Singletonインスタンスを返します。もしユーザーが、この機能をオフにすると、スレッドID は常に 0 となり、instance()メソッドは常に同じ Singletonインスタンスを返します。その場合は、(問題含みなので)ユーザーはマルチスレッドを使いたいとは思わないでしょう。仮に使うとしても、よく注意する必要があります。理由は、前のGlobal Settingの説明をご覧下さい。

 他にも、読者の方は、私が上記のListingを“デフォールトの実装内容”と述べたのはなぜかと思われるかも知れません。いい所に気づかれました。ここでは詳しく説明しませんが、実は他にも Singletonクラスの実装方法が複数あり、これもコンパイル時のフラッグ設定で選択可能です。

 ひとつは、(Singletonインスタンスを保持する)map< > オブジェクトを、instance()メソッドの中で宣言される static変数とするのでは無く、Singletonクラスの staticメンバー変数として宣言する方法です(QL_MANAGED フラッグを 1 に設定すればこうなります)。これは、map<>instance()の中で宣言した場合 .NETフレームワークの中で managed C++ としてコンパイルされると、うまく機能しない事が判明したからです (少なくとも古いバージョンの Visual Studioのコンパイラーではそうです)。

 もうひとつの方法は、ユーザーがマルチスレッド環境の中でグローバルな Singletonインスタンスを1つだけ使いたい場合に使います。その場合、Singletonインスタンスの初期化は thread safe(マルチスレッド下でも安全にデータ共有できる状態)でなければなりません (ここでは Singletonインスタンスそのものの話をしており、それを保持している map<Integer, T> の事ではありません。それは new T で生成されます)。従って、この方法を選択した場合は、(thread safeを実現する為に) lock や mutex クラスなどが必要になります。デフォールトのシングルスレッドの設定の中に、これらの部品に関する記述を含めるべきではありません。従って、そのコードは、別のコンパイル時フラッグ設定の後ろにあります。興味のある方は、QuantLibライブラリーのコードを見て下さい。

 読者の方の最後の疑問点は、はたして Singletonクラスがそもそも必要だったのかどうか という事でしょう。それは厳しい質問です。私見については、前に説明した Global Settingsの所を再度参照して下さい。今の所、Winston Churchillによる民主主義制度の評価と同じ様に、悪い選択肢の中で最もましなものではないかと思っています。

The Visitor Pattern: ビジターパターン

 QuantLibでの Visitor Pattern の実装例を、次の Listing A-29 で示しますが、これは4人組の本による本来の形ではなく、Acyclic Visitor Pattern (巻末(Martin, 1997)参照)を取り入れています。そこではまず、インターフェース用の degenerateな(訳注:すなわちメソッドを持たず何の動作もしない)AcyclicVisitorクラスを定義します。それと同時に(具体的な動作を担う)Visitorクラステンプレートを定義して、その中でテンプレート引数用の動作を提供する visit() を純粋仮想関数として宣言しています (訳注:従ってvisit()の具体的な動作内容は派生クラスで実装される)。

Listing A.30: AcyclicVisitorクラスとVisitorクラステンプレートの定義
    class AcyclicVisitor {
      public:
        virtual ~AcyclicVisitor() {}
    };

    template <class T>
    class Visitor {
      public:
        virtual ~Visitor() {}
        virtual void visit(T&) = 0;
    };

 このパターンを機能させるには、Visitorクラスの訪問を受ける側のクラス階層(訳注:Acceptorクラス、下記コードでは Event、CashFlow、Couponクラスが該当)も必要になります。下記 Listing A.31 に、その一例を示します。

Listing A.31: Visitorから訪問を受ける側のクラスにおけるaccept()メソッドの実装内容
    void Event::accept(AcyclicVisitor& v) {
        Visitor<Event>* v1 = dynamic_cast<Visitor<Event>*>(&v);
        if (v1 != 0)
            v1->visit(*this);
        else
            QL_FAIL("not an event visitor");
    }

    void CashFlow::accept(AcyclicVisitor& v) {
        Visitor<CashFlow>* v1 =
            dynamic_cast<Visitor<CashFlow>*>(&v);
        if (v1 != 0)
            v1->visit(*this);
        else
            Event::accept(v);
    }

    void Coupon::accept(AcyclicVisitor& v) {
        Visitor<Coupon>* v1 = dynamic_cast<Visitor<Coupon>*>(&v);
        if (v1 != 0)
            v1->visit(*this);
        else
            CashFlow::accept(v);
    }

 訪問を受ける側(Acceptor) のクラス階層における各クラスは(少なくとも、Visitorクラスに何等かの動作を委託しているクラスは)、AcyclicVisitorの参照を引数として取る accept()メソッドを定義する必要があります。それぞれの accept()メソッドは、引数として渡された AcyclicVisitorインスタンスを、dynamic_castを使って、自分自身のクラスに対応する(訳注:Visitor<C> のテンプレート引数 C に、自分自身の型を指定して具体化されている) Visitorインスタンスに型変換します。その型変換がうまくいけば、その Visitorインスタンスの visit()メソッドを呼び出します。型変換がうまくいかない場合は、代替策を探さなければなりません。仮にその Acceptorクラスが派生クラスであれば、そのベースクラスで実装されている accept() を呼び出し、そこで型変換を試みます。もしベースクラスでもうまく行かなかった場合は、例外処理に進みます(あるいは何もせずにメソッドを終了するという選択肢もありますが、失敗に気が付かないで終わるのは避けました)。(訳注:上記コード例では階層の一番下にある Couponクラスの accept()内で適合する Visitor を探し、それが存在しない場合親クラスである CashFlow クラスの accept( )を呼び出して適合する Visitorを探し、さらにそれもうまくいかなければベースクラスである Eventクラスの accept()を呼び出す。それでもダメな場合は例外処理に移る)

 Visitorパターンは、Chapter IV のキャッシュフロー分析の所で説明した BPSCalculatorクラスで取り入れられています。このクラスは AcyclicVisitorクラスから派生しており、従って様々なクラスの accept( )メソッドの引数として使う事が出来ます。また同時に、テンプレート引数が特定され具体化された Visitor<C> からも派生させています(そのクラスでは visit()が実装されているはずです)。その Visitorインスタンスは、(それを必要とする)何等かのオブジェクトの accept()メソッドに渡され、そこで(型変換が)うまくいけば visit()メソッドが呼び出され、だめなら例外処理に飛びます。

 Visitorパターンの有用性については、Chapter IVで既に説明したので、そこを参照して下さい。ここでは、なぜAcyclicVisitorパターンを選択したかについて、2点ほど説明したいと思います。

 端的に言えば、4人組が提唱した元々の Visitorパターンの方が、若干プログラムのスピードは速いかも知れませんが、より取扱いが厄介です。特に、訪問を受ける側 (Acceptor側)の階層に新たなクラスを追加した場合、それに対応する visit()メソッドを、既存の Visitorクラスの階層すべてについて追加する必要があります(そうしないとコードをコンパイルできません)。AcyclicVisitorパターンを使えば、その必要はありません。ユーザー定義の Acceptor派生クラスに対応する Visitorクラスが定義されていなくても、accept()メソッドにおいて型変換が失敗して例外処理に飛ぶだけです。(注: 実際の所、ユーザーは accept()そのものを定義する必要さえありません。ベースクラスのそれを継承すればいいのです。しかしその場合は、Visitor側で、その Acceptor用の visit()メソッドを実装しても意味がありません)

 注意しておきますが、この事は(人生において多くの事がそうであるように)便利ではあるものの、良い事ではありません。新しい Acceptorをユーザー定義した場合でも、ユーザーは常に対応する Visitorクラスの階層を見直して、既存の visit()の実装を流用する事が問題ないかチェックし、もしそうでないなら新たに visit()を実装しなければなりません。しかし、コンパイラーがコンパイルできないデメリットと比べれば、visit()を全ての Acceptorクラス用の Visitor階層に実装しないで済むメリットの方が大きいと思います。BPSCalculatorクラスで言えば、様々な階層の CashFlowクラスに対応する BPS計算アルゴリズムの実装が、2種類程度の visit()メソッドの実装で十分まかなえます。