2.Implementing QuantLibの和訳

Chapter-II. Financial Instruments and Pricing Engines

2.2 Pricing Engines   価格計算アルゴリズムのオブジェクトモデル

2.2.1 Example: Plain Vanilla Option   具体例: シンプルなオプション

ここで、ひとつ例を示す必要があるでしょう。一言だけ注意をしておくと、QuantLibの中にプレインバニラ- すなわち単純な株のコール・プット オプションで、行使タイミングがヨーロピアンやアメリカンやバーミューダタイプ - のオプションクラスは、深いクラス階層の奥底に存在します。ベースクラスとしてInstrumentクラスがあり、そこからまずOptionクラスが派生し、さらに対象資産が一個のオプションを定義するOneAssetOptionクラスが派生し、さらに一つ二つのクラスを経由して最終的にVanillaOptionクラスに行きつきます。 

この様に、多くの層を経由させた理由は当然あります。OneAssetOptionクラスの定義は他のオプション、例えばアジアンオプションでも使えますし、Optionクラスは、様々なタイプのバスケットオプションに使えます。残念ながら、そのせいで、単純なオプションの価格計算のプログラムコードが、多層な階層に分散されており、解りやすく説明する為の実例としては使いにくいものになっています。そこで、ここでの説明だけの為に、実装内容はQuantLibのLibraryと同じものですが、Instrumentクラスから直接派生させた仮想のVanillaOptionクラスを作り、中間にある派生クラスの実装内容もその中に取り込んでしまいました。 

Listing 2.7にこの仮想VanillaOptionクラスの実装内容を示します。まずInstrumentベースクラスのインターフェースで実装が必要なメソッドを宣言し、次にこのクラスで追加された計算結果のデータ(名前にある通り、オプションのGreeks(感応度))を読みだせるメソッド群を宣言しています。前の章で指摘した通り、それらのデータはmutableで宣言されており、論理的にconst宣言されたcalculate( )メソッドによっても、値を変更出来ます。 

Listing 2.7: Interface of the VanillaOption class and its inner classes. 

    class VanillaOption : public Instrument {
      public:
        // accessory classes
        class arguments;
        class results;
        class engine;
        // constructor
        VanillaOption(const boost::shared_ptr<Payoff>&,
                      const boost::shared_ptr<Exercise>&);
        // implementation of instrument method
        bool isExpired() const;
        void setupArguments(Arguments*) const;
        void fetchResults(const Results*) const;
        // accessors for option-specific results
        Real delta() const;
        Real gamma() const;
        Real theta() const;
        // ...more greeks
      protected:
        void setupExpired() const;
        // option data
        boost::shared_ptr<Payoff> payoff_;
        boost::shared_ptr<Exercise> exercise_;
        // specific results
        mutable Real delta_;
        mutable Real gamma_;
        mutable Real theta_;
        // ...more
    };		
    
    class VanillaOption::arguments
        : public PricingEngine::arguments {
      public:
        // constructor
        arguments();
        void validate() const;
        boost::shared_ptr<Payoff> payoff;
        boost::shared_ptr<Exercise> exercise;
    };

    class Greeks : public virtual PricingEngine::results {
      public:
        Greeks();
        Real delta, gamma;
        Real theta;
        Real vega;
        Real rho, dividendRho;
    };

    class VanillaOption::results : public Instrument::results,
                                   public Greeks {
      public:
        void reset();
    };

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

VanillaOptionクラスは、自分自身に必要なメンバー変数やメソッドの他に、いくつかのアクセサリークラスを宣言しています。すなわち、このクラス特有の(内部クラスとなる)argumentsと results構造体、および基本となるPricingEngineクラスです。これらのクラスはすべて、Optionクラスとの関係性を明確にするために、VanillaOptionクラスの内部クラスとして定義されています。それらのインターフェースについても、上記Listingの中で示されています。 

これらのアクセサリークラスについて、2点コメントします。一点目は、この例を紹介した際に私が述べたことに反し、(クラス内クラスとして、このクラス特有の計算結果を保持するために定義されたはずの)VanillaOption::resultsクラスの中で、メンバー変数を全く宣言していません。これは、実装内容の詳細な仕組みを際立たせる為に、わざとそうしました。プログラム開発者は、いくつかの商品に共通するような計算結果を保持する構造体を定義したいと考えるかもせれません。そのような構造体は、派生クラスを作ることにより再利用が可能になるからです。Greeks構造体の例がまさにそれです。(訳注:様々なオプションで共通のリスク感応度の値を、Greeksという名前の構造体で定義しておけば、オプション毎にresults構造体のメンバー変数を定義せず、Greeks構造体を再利用すれば簡単で済む。) 従って、このクラス(VanillaOption::results)は、(Greeks構造体と)Instrument::resultsクラスから派生させて、最終的な構造が構築されています。その際に、かの悪名高い菱形継承を避ける為、(Greeks::resultsもInstruments::resultsも)Pricing Engine::resultsクラスからの仮想継承にしなければなりません。(継承のダイアモンドについては、例えば下記[1]参照)(訳注:VanillaOption::resultsは、Instrument::resultsとGreeks::resultsの2つのベースクラスから派生しているが、この2つのベースクラスは、いずれもPricingEngine::resultsから派生している。この場合、仮想継承にしておかないと、派生クラスからベースクラスのメソッドを呼び出す時、2つのルートが発生して曖昧さが発生する。) 

二点目は、このVanillaOptionクラス特有のPricing Engineクラスは、テンプレートクラスであるGenericEngineから直接継承し、適切なargumentsクラスとresultsクラスをつかってインスタンスを生成するだけで十分であったという事です。以下に説明するとおり、派生クラスは、ただ単にcalculate( )メソッドを実装するだけで十分です。 

さあ、いよいよVanillaOptionクラスの実装内容(Listing 2.8)の説明に移りたいと思います。 

Listing 2.8: Implementation of the VanillaOption class. 

    VanillaOption::VanillaOption(
        const boost::shared_ptr<StrikedTypePayoff>& payoff,
        const boost::shared_ptr<Exercise>& exercise)
    : payoff_(payoff), exercise_(exercise) {}

    bool VanillaOption::isExpired() const {
        Date today = Settings::instance().evaluationDate();
        return exercise_->lastDate() < today;
    }

    void VanillaOption::setupExpired() const {
        Instrument::setupExpired();
        delta_ = gamma_ = theta_ = ... = 0.0;
    }

    void VanillaOption::setupArguments(
                       PricingEngine::arguments* args) const {
        VanillaOption::arguments* arguments =
            dynamic_cast<VanillaOption::arguments*>(args);
        QL_REQUIRE(arguments != 0, "wrong argument type");
        arguments->exercise = exercise_;
        arguments->payoff = payoff_;
    }

    void VanillaOption::fetchResults(
                      const PricingEngine::results* r) const {
        Instrument::fetchResults(r);
        const VanillaOption::results* results =
            dynamic_cast<const VanillaOption::results*>(r);
        QL_ENSURE(results != 0, "wrong result type");
        delta_ = results->delta;
        ... // other Greeks
    }	

コンストラクターは、具体的なオプションの条件を決める複数のオブジェクトを引数として取ります。これらについては後々の章またはAppendix Aで説明します。今の段階で簡単に説明しておくと、(引数のひとつ)payoffオブジェクトはストライク(行使価格)とオプションタイプ(コールかプットか)の情報を持ち、exerciseオブジェクトは行使日と行使タイプ(ヨーロピアン、アメリカン、バーミューダン)の情報を持ちます。引数で渡されたこれらオブジェクトのインスタンスは、メンバー変数に保持されます。ここでは、市場データの情報は渡されていない点に注意して下さい。市場データは別の所で渡されています。 

オプション期日到来に関するメソッドは極めて単純です。isExpired( )メソッドは、最終オプション行使日が過ぎていないかどうかチェックし、setupExpired( )メソッドは、ベースクラスで実装されたメソッドを呼び出すと同時に、いくつかのメンバー変数(Greeks)を0にセットします。 

setupArguments( ) とfetchResults( )メソッドの実装内容はもう少し複雑です。setupArguments( )メソッドは、まず引数として受け取った汎用arguments(ここではPricingEngine::arguments*)オブジェクトのポインターをVanillaOptionクラスのargumentsクラスの型タイプへダウンキャスト(型変換)します。もし、型が合っていない場合は、そこで例外処理に飛び、型が合っている場合は動作が継続します。次に。VanillaOptionオブジェクトのメンバー変数は、逐語的に(型変換後の)argumentsオブジェクトのメンバー変数にコピーされていきます。しかしながら、いくつかのPricing Engineでは、同じような計算(例えば、日付を時間に換算する計算)が必要なくなる場合があるでしょう。そういった場合は、setupArguments( )のメソッドの中で一回実装するだけで済みます。 

fetchResults( )メソッドは、setupArguments( )メソッドと対を成すものです。このメソッドも、引数として受け取ったresultsオブジェクトのポインターをダウンキャスト(型変換)する事から始まります。型の適合性の確認を行った後、resultsオブジェクトの内容を自分のメンバー変数にコピーしていきます。 

シンプルではありますが、上記のような実装で、実際に金融商品を稼働することができます。稼働するとは、Pricing Engineを備えれば、必要な価格計算ができるという事です。そのようなPricing Engineの例を、Black-Scholes-Mertonによるヨーロピアンオプションの解析解モデルで実装したプログラムコードをListing 2.9に示します。 

Listing 2.9: Sketch of an engine for the VanillaOption class. 

    class AnalyticEuropeanEngine
        : public VanillaOption::engine {
      public:
        AnalyticEuropeanEngine(
          const shared_ptr<GeneralizedBlackScholesProcess>&
                                                        process)
        : process_(process) {
            registerWith(process);
        }
        void calculate() const {
          QL_REQUIRE(
              arguments_.exercise->type() == Exercise::European,
              "not an European option");
          shared_ptr<PlainVanillaPayoff> payoff =
             dynamic_pointer_cast<PlainVanillaPayoff>(
                                              arguments_.payoff);
          QL_REQUIRE(process, "Black-Scholes process needed");
          ... // other requirements
            
          Real spot = process_->stateVariable()->value();
          ... // other needed quantities

          BlackCalculator black(payoff, forwardPrice,
                                stdDev, discount);

          results_.value = black.value();
          results_.delta = black.delta(spot);
          ... // other greeks
        }
      private:
        shared_ptr<GeneralizedBlackScholesProcess> process_;
    };  

コンストラクターは、Black Scholesモデルの確率過程を記述するオブジェクトを引数として取ります。このオブジェクトは、市場データ(対象資産とその現在価値、リスクフリー金利、配当利回り、ボラティリティ)の情報を保持しており、それらをメンバー変数にコピーします。ここでも又、実際の計算過程は、他のクラス(BlackCalculatorクラス)のインターフェースの奥に隠れてしまいましたが、プログラムコードを見れば、計算に必要なデータのやり取りの様子はわかると思います。 

calculate( )メソッドは、まずいくつかの事前データチェックからスタートします。これには少し驚かれるかもしれません。なぜなら、これらのデータはcalculate( )がスタートする時点ですでにチェック済のはずだからです。しかし、どんなPricing Engineでも、計算をスタートする前に、さらなるデータチェックをする場面があるかもしれません。上記のプログラムでは、オプションがヨーロピアンか否か、payoffが単純なCallまたはPutなのか、というチェックを行い、後者の手続きはさらに、必要とするクラスにcast downするプロセスまでも含みます。(boost::dynamic_pointer_castは、shared pointerの為のdynamic_castと同じです。) 

このメソッドのプログラムコードの真中あたりに、Pricing Engineが引数から必要な情報を取り出している部分があります。ここでは、対象資産の現在価格やその他の価格計算に必要な情報(対象資産のオプション期日での先渡し価格、満期時におけるDiscount Factor)などです。(QuantLibのソースコードにすべて記述されているので、確認して下さい) 

最後に、計算アルゴリズムが実行され、計算結果がresults構造体の該当箇所にそれぞれコピーされます。これで、calculate( )メソッドと事例の紹介を終わります。 

 

[1] B. Stroustrup, The C++ Programming Language, 4th edition. Addison-Wesley, 2013. 

 

<ライセンス表示>

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

目次

Page Top に戻る

// // //