2. "Implementing QuantLib"の和訳
Chapter-VI The Monte Carlo Framework
6.3 Putting it all together : すべての部品を使って組み立てる (つづき)
6.3.3 Monte Carlo Symulation : モンテカルロシミュレーション
さらに、もう1ステップ、複雑な階段を上がりましょう。次の Listing 6.15に、McSimulationクラステンプレートの実装を示します。このクラスの仕事は、単純作業しか頭にない MonteCarloModelをゴールに向けて追い立てる事です。ゴールとは、推定誤差の要求レベルに達するか、あるいはサンプル数の最大値に達するということです。このクラスは PricingEngineを構築するスタート地点として使う事ができます (そのようにデザインされていました)。
Listing 6.15: Sketch of the McSimulation class template.
template <template <class> class MC, class RNG,
class S = Statistics>
class McSimulation {
public:
typedef typename MonteCarloModel<MC,RNG,S>::path_generator_type
path_generator_type;
typedef typename MonteCarloModel<MC,RNG,S>::path_pricer_type
path_pricer_type;
typedef typename MonteCarloModel<MC,RNG,S>::stats_type
stats_type;
typedef typename MonteCarloModel<MC,RNG,S>::result_type
result_type;
virtual ~McSimulation() {}
result_type value(Real tolerance,
Size maxSamples = QL_MAX_INTEGER,
Size minSamples = 1023) const;
result_type valueWithSamples(Size samples) const;
void calculate(Real requiredTolerance,
Size requiredSamples,
Size maxSamples) const;
const stats_type& sampleAccumulator(void) const;
protected:
McSimulation(bool antitheticVariate,
bool controlVariate);
virtual shared_ptr<path_pricer_type> pathPricer() const = 0;
virtual shared_ptr<path_generator_type> pathGenerator() const= 0;
virtual TimeGrid timeGrid() const = 0;
virtual shared_ptr<path_pricer_type> controlPathPricer() const;
virtual shared_ptr<path_generator_type>
controlPathGenerator() const;
virtual result_type controlVariateValue() const;
virtual shared_ptr<PricingEngine> controlPricingEngine() const;
mutable shared_ptr<MonteCarloModel<MC,RNG,S> > mcModel_;
bool antitheticVariate_, controlVariate_;
};
template <template <class> class MC, class RNG, class S>
typename McSimulation<MC,RNG,S>::result_type
McSimulation<MC,RNG,S>::value(Real tolerance,
Size maxSamples,
Size minSamples) const {
...
Real error = mcModel_->sampleAccumulator().errorEstimate();
while (error > tolerance) {
QL_REQUIRE(sampleNumber < maxSamples, ...);
Real order = (error*error)/(tolerance*tolerance);
Size nextBatch =
std::max<Size>(sampleNumber*order*0.8-sampleNumber,
minSamples));
nextBatch = std::min(nextBatch, maxSamples-sampleNumber);
sampleNumber += nextBatch;
mcModel_->addSamples(nextBatch);
error = mcModel_->sampleAccumulator().errorEstimate();
}
return mcModel_->sampleAccumulator().mean();
}
template <template <class> class MC, class RNG, class S>
void McSimulation<MC,RNG,S>::calculate(Real requiredTolerance,
Size requiredSamples,
Size maxSamples) const {
if (!controlVariate_) {
mcModel_ = shared_ptr<MonteCarloModel<MC,RNG,S> >(
new MonteCarloModel<MC,RNG,S>(
pathGenerator(), pathPricer(),
S(), antitheticVariate_));
} else {
... // same as above, but passing the control-variate args, too.
}
if (requiredTolerance != Null<Real>())
this->value(requiredTolerance, maxSamples);
else
this->valueWithSamples(requiredSamples);
}
McSimulationクラスは、Template Methodパターンの方法を取っています。このクラスは、ユーザーがいくつかの動作を実装する必要がありますが、そのかわり MonteCarloModelのインスタンスの生成とシミュレーションを走らせる汎用的な枠組みを提供しています。派生クラスは、少なくとも、使用する PathPricerのインスタンスを返す pathPricer( )メソッド、PathGeneratorインスタンスを返す pathGenerator( )メソッド、及びシミュレーションの離散時間の各 gridを返す timeGrid( )メソッド、を実装しなければなりません。Control Variate法で使われるオブジェクトを返すその他のメソッドは、実装してもしなくても構いません。McSimulationクラスは、Control Variateに関するデフォールトの実装として Null値を返すものを提供しており、派生クラスで Control Variate法を使わないクラスでは、忘れてしまって結構です。
McSimulationクラスはこれらのメソッドを使って、PricingEngineに必要な殆どの動作を提供しています。コンストラクターは、2種類の bool変数を引数として取り、それぞれ antithetic法あるいは control variate法を使うかどうかの flagを設定します。後者の flagは派生クラスで必要なメソッドを実装した場合にのみ意味があります。
value( )メソッドは、MonteCarloModelに対し、推定誤差が要求レベル以下になるまでサンプルを加えていきます。(誤差の推定が必要になりますが、準乱数を使った場合は、それは行われません。) このメソッドは、その時点でのサンプル数 n と誤差 \(\varepsilon \) を見に行き、誤差の許容範囲 \( \tau \) を基に、必要なサンプル数 N を推定します \( (N= n \times \varepsilon^2 / \tau^2 \) : 推定誤差は標本数の平方根の逆数に比例する \( \varepsilon \propto 1/ \sqrt n\) ところから逆算)。誤差の許容範囲に達していなければ、追加のサンプル数(N - n)を加えます。そこで再度、誤差を計算し、必要なら同じプロセスを繰り返します。
valueWithSamples( )メソッドは、引数で取ったサンプル数を使い、必要なシミュレーションの数と比較して、足りない分だけ MonteCarloModelにサンプルを加えます。実装内容はスペースの関係でここには示していませんが、仕組みは単純です。
最後に calculate( )メソッドが、シミュレーションを最後まで走らせます。このメソッドは、引数として誤差の許容範囲か、最大シミュレーション数のいずれかを取ります (どちらかの引数はNullである事。但し両方がNullにはならない)。まず MonteCarloModelインスタンスを生成しますが、その際 Control Variateフラッグに従って MonteCarloModelのコンストラクターに Control Variate法に必要な引数を渡さなかったり、渡したりします。次に、シミュレーションの終了方法に従って、value( ) あるいは valueWithSamples( )メソッドを呼び出します。
次のセクションでは、Pricing Engineを構築する為に、どのようにして McSimulationクラスを使うのか、具体例をあげて説明します。しかし、それの進む前に、今のデザインの中で、いくつか改善すべき点ついて指摘させて下さい。
まず最初に、今実装されているロジックでは、シミュレーションの終了条件を2通りから選択できます (すなわち、シミュレーションの正確性の要求レベル、あるいは事前に決めたサンプル数)。しかし、もちろん他の基準を加える事も可能でした。例えば、シミュレーションを、事前に決めた時間まで行う、あるいはサンプルを複数のグループに分けて、各グループのシミュレーション終了後、都度計算結果をみて、収束が要求レベルに達していればそこで止める、などです。このことは、ベースクラスで具体的な実装がなされている value( ) や valueWithSamples( )(テンプレート)メソッドを、Strategy Patternを使ったオブジェクトに置き換えて、calculate( )メソッドの中で、Strategyの中からどれを呼び出すかスイッチできるようにしてもよかったかも知れません。そうすると、今のcalculate( )メソッドが少し不細工な点も改善できるでしょう。今の実装では、Control Variateフラッグに従い (MonteCarloModelのインスタンス生成の為に)多くの、しかもその大半は Null値の引数をそれに渡すことになっています (まるで、大昔、C++で関数のオーバーロードが出来なかった時代のように)。それが、単に Strategyオブジェクトに必要な引数を渡し、それが必要な戻り値を calculate( )に返すだけになります。
最後に、controlPricingEngine( )メソッドの存在は、若干問題を引き起こしそうです。McSimulationの実装では、それを使いません。すなわち、厳密には、そのメソッドは Template Methodパターンの一部ではなく、ここに置かれるべきでは無かったという事です。但し、2つの別のクラス (両方ともMcSimulationクラスから派生していますが、それ以外は無関係)で、そのメソッドを宣言し、controlVariateValue( )の実装の中で、実際に使っています。従って、今ここに残しておくのは、純粋にデザインの見地からではなく、重複を避ける為です。
< Aside : シンクロナイズドウォーキング >
MonteCarloModelクラスと McSimulationクラスのいずれの Templateとも、Control Variate法の為に、それ用の PathGeneratorクラスを定義できるようになっています。しかし、この仕組みは慎重に使うべきであるという点に、注意して下さい。この分散逓減法のテクニックがきちんと働く為には、2つめの PathGeneratorインスタンスで生成された制御変量用の Pathが、通常の PathGeneratorで生成されたPathと相関していなければなりません。これは、この2種類の PathGeneratorが基本的に全く同じ乱数生成装置を使わなければならないという事です。すなわち、同じ種類、同じ次元、そして同じSeed値です。
残念ながら、この制約によって、いくつかの拡張性を不可能にしています。例えば、もし Heston Modelのような Stochastic Volatility Model (確率変動ボラティリティモデル)を使っていた場合、Black-Scholesモデルの解析解を制御変量として使いたい誘惑にかられるでしょう。しかし、そのようにうまくはいきません。離散時間のステップ数を n 個取った場合、Heston Modelでは 2n 個の乱数を必要とします (株価の為に n個、Volatilityの為に n個)。一方、Black-Scholes過程では n個で十分です。すなわち、2種類のPathGeneratorインスタンスを同期させる事は不可能という事です。
実際には、別の PathGeneratorの為のユースケースがあります。複数のパラメータを持ち、一般には解析解が無い確率過程で、パラメータのいくつかを Nullとすれば、オプション価値の解析解が求まるような場合です。もし運が良ければ、メインの PathGeneratorのインスタンスを生成して完全に Calibrate済みのモデルを使う一方、別のインスタンスを使って、Null値のパラメータのプロセスで制御変量用の Pathを生成できます。
<ライセンス表示>
QuantLibのソースコードを使う場合は、ライセンス表示とDisclaimerの表示が義務付けられているので、添付します。 ライセンス