| 1 | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| 2 | |
| 3 | /* |
| 4 | Copyright (C) 2008 Roland Lichters |
| 5 | Copyright (C) 2009 Jose Aparicio |
| 6 | |
| 7 | This file is part of QuantLib, a free-software/open-source library |
| 8 | for financial quantitative analysts and developers - http://quantlib.org/ |
| 9 | |
| 10 | QuantLib is free software: you can redistribute it and/or modify it |
| 11 | under the terms of the QuantLib license. You should have received a |
| 12 | copy of the license along with this program; if not, please email |
| 13 | <quantlib-dev@lists.sf.net>. The license is also available online at |
| 14 | <http://quantlib.org/license.shtml>. |
| 15 | |
| 16 | This program is distributed in the hope that it will be useful, but WITHOUT |
| 17 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 18 | FOR A PARTICULAR PURPOSE. See the license for more details. |
| 19 | */ |
| 20 | |
| 21 | #include <ql/experimental/credit/randomdefaultmodel.hpp> |
| 22 | #include <ql/math/solvers1d/bisection.hpp> |
| 23 | #include <ql/math/solvers1d/brent.hpp> |
| 24 | #include <utility> |
| 25 | |
| 26 | using namespace std; |
| 27 | |
| 28 | namespace QuantLib { |
| 29 | |
| 30 | namespace { |
| 31 | |
| 32 | // Utility for the numerical solver |
| 33 | class Root { |
| 34 | public: |
| 35 | Root(Handle<DefaultProbabilityTermStructure> dts, Real pd) |
| 36 | : dts_(std::move(dts)), pd_(pd) {} |
| 37 | Real operator()(Real t) const { |
| 38 | QL_REQUIRE(t >= 0.0, "GaussianRandomDefaultModel: internal error, t < 0 (" |
| 39 | << t << ") during root searching." ); |
| 40 | return dts_->defaultProbability(t, extrapolate: true) - pd_; |
| 41 | } |
| 42 | private: |
| 43 | const Handle<DefaultProbabilityTermStructure> dts_; |
| 44 | Real pd_; |
| 45 | }; |
| 46 | |
| 47 | } |
| 48 | |
| 49 | GaussianRandomDefaultModel::GaussianRandomDefaultModel( |
| 50 | const ext::shared_ptr<Pool>& pool, |
| 51 | const std::vector<DefaultProbKey>& defaultKeys, |
| 52 | const Handle<OneFactorCopula>& copula, |
| 53 | Real accuracy, |
| 54 | long seed) |
| 55 | : RandomDefaultModel(pool, defaultKeys), copula_(copula), accuracy_(accuracy), seed_(seed), |
| 56 | rsg_(PseudoRandom::make_sequence_generator(dimension: pool->size() + 1, seed)) { |
| 57 | registerWith(h: copula); |
| 58 | } |
| 59 | |
| 60 | void GaussianRandomDefaultModel::reset() { |
| 61 | Size dim = pool_->size() + 1; |
| 62 | rsg_ = PseudoRandom::make_sequence_generator(dimension: dim, seed: seed_); |
| 63 | } |
| 64 | |
| 65 | void GaussianRandomDefaultModel::nextSequence(Real tmax) { |
| 66 | const std::vector<Real>& values = rsg_.nextSequence().value; |
| 67 | Real a = sqrt(x: copula_->correlation()); |
| 68 | for (Size j = 0; j < pool_->size(); j++) { |
| 69 | const string name = pool_->names()[j]; |
| 70 | const Handle<DefaultProbabilityTermStructure>& |
| 71 | dts = pool_->get(name).defaultProbability(key: defaultKeys_[j]); |
| 72 | |
| 73 | Real y = a * values[0] + sqrt(x: 1-a*a) * values[j+1]; |
| 74 | Real p = CumulativeNormalDistribution()(y); |
| 75 | |
| 76 | if (dts->defaultProbability(t: tmax) < p) |
| 77 | pool_->setTime(name, time: tmax + 1); |
| 78 | else { |
| 79 | // we know there is a zero of f(t) = dts->defaultProbability(t) - p in [0, tmax] |
| 80 | try { |
| 81 | // try bracketing the root and find it with Brent |
| 82 | Brent brent; |
| 83 | brent.setLowerBound(0.0); |
| 84 | brent.setUpperBound(tmax); |
| 85 | pool_->setTime(name, time: brent.solve(f: Root(dts, p), accuracy: accuracy_, guess: tmax / 2.0, step: 1.0)); |
| 86 | } catch (...) { |
| 87 | // if Brent fails, use Bisection, this is guaranteed to find the root |
| 88 | pool_->setTime( |
| 89 | name, time: Bisection().solve(f: Root(dts, p), accuracy: accuracy_, guess: tmax / 2.0, xMin: 0.0, xMax: tmax)); |
| 90 | } |
| 91 | } |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | } |
| 96 | |
| 97 | |