| 1 | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| 2 | |
| 3 | /* |
| 4 | Copyright (C) 2014 Jose Aparicio |
| 5 | |
| 6 | This file is part of QuantLib, a free-software/open-source library |
| 7 | for financial quantitative analysts and developers - http://quantlib.org/ |
| 8 | |
| 9 | QuantLib is free software: you can redistribute it and/or modify it |
| 10 | under the terms of the QuantLib license. You should have received a |
| 11 | copy of the license along with this program; if not, please email |
| 12 | <quantlib-dev@lists.sf.net>. The license is also available online at |
| 13 | <http://quantlib.org/license.shtml>. |
| 14 | |
| 15 | This program is distributed in the hope that it will be useful, but WITHOUT |
| 16 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 17 | FOR A PARTICULAR PURPOSE. See the license for more details. |
| 18 | */ |
| 19 | |
| 20 | #ifndef quantlib_tcopula_policy_hpp |
| 21 | #define quantlib_tcopula_policy_hpp |
| 22 | |
| 23 | #include <ql/errors.hpp> |
| 24 | #include <ql/experimental/math/convolvedstudentt.hpp> |
| 25 | #include <ql/functional.hpp> |
| 26 | #include <boost/math/distributions/students_t.hpp> |
| 27 | #include <vector> |
| 28 | |
| 29 | namespace QuantLib { |
| 30 | |
| 31 | /*! \brief Student-T Latent Model's copula policy. |
| 32 | |
| 33 | Describes the copula of a set of normalized Student-T independent random |
| 34 | factors to be fed into the latent variable model. |
| 35 | The latent model requires the independent variables to be of unit variance |
| 36 | so the policy expects the factors coefficients to be as usual and the T |
| 37 | variables to be normalized, the normalization is performed by the policy. |
| 38 | To normalize the random variables they are divided by the square root of |
| 39 | the variance of each T (\f$ \frac{\nu}{\nu-2}\f$) |
| 40 | */ |
| 41 | class TCopulaPolicy { |
| 42 | public: |
| 43 | /*! Stores the parameters defining the factors random variable |
| 44 | T-distributions. As it is now the latent models are restricted to |
| 45 | having the same distribution for all idiosyncratic factors, so only |
| 46 | one parameter is needed for them. |
| 47 | */ |
| 48 | typedef |
| 49 | struct { |
| 50 | std::vector<Integer> tOrders; |
| 51 | } initTraits; |
| 52 | |
| 53 | /*! Delayed initialization of the distribution parameters and caches. |
| 54 | To be called by the latent model. */ |
| 55 | /* \todo |
| 56 | Explore other constructors, with different vector dimensions, defining |
| 57 | simpler combinations (only one correlation, only one variable) might |
| 58 | simplify memory. |
| 59 | */ |
| 60 | explicit TCopulaPolicy( |
| 61 | const std::vector<std::vector<Real> >& factorWeights = |
| 62 | std::vector<std::vector<Real> >(), |
| 63 | const initTraits& vals = initTraits()); |
| 64 | |
| 65 | //! Number of independent random factors. |
| 66 | Size numFactors() const { |
| 67 | return latentVarsInverters_.size() + varianceFactors_.size() - 1; |
| 68 | } |
| 69 | |
| 70 | //! returns a copy of the initialization arguments |
| 71 | //... better to have a cache? |
| 72 | initTraits getInitTraits() const { |
| 73 | initTraits data; |
| 74 | data.tOrders.resize(new_size: distributions_.size()); |
| 75 | for (Size i=0; i<distributions_.size(); ++i) { |
| 76 | data.tOrders[i] = static_cast<Integer>( |
| 77 | distributions_[i].degrees_of_freedom()); |
| 78 | } |
| 79 | return data; |
| 80 | } |
| 81 | const std::vector<Real>& varianceFactors() const { |
| 82 | return varianceFactors_; |
| 83 | } |
| 84 | /*! Cumulative probability of a given latent variable. |
| 85 | The iVariable parameter is the index of the requested variable. |
| 86 | */ |
| 87 | Probability cumulativeY(Real val, Size iVariable) const { |
| 88 | #if defined(QL_EXTRA_SAFETY_CHECKS) |
| 89 | QL_REQUIRE(iVariable < latentVarsCumul_.size(), |
| 90 | "Latent variable index out of bounds." ); |
| 91 | #endif |
| 92 | return latentVarsCumul_[iVariable](val); |
| 93 | } |
| 94 | //! Cumulative probability of the idiosyncratic factors (all the same) |
| 95 | Probability cumulativeZ(Real z) const { |
| 96 | return boost::math::cdf(dist: distributions_.back(), x: z / |
| 97 | varianceFactors_.back()); |
| 98 | } |
| 99 | /*! Probability density of a given realization of values of the systemic |
| 100 | factors (remember they are independent). |
| 101 | Intended to be used in numerical integration of an arbitrary function |
| 102 | depending on those values. |
| 103 | */ |
| 104 | Probability density(const std::vector<Real>& m) const { |
| 105 | #if defined(QL_EXTRA_SAFETY_CHECKS) |
| 106 | QL_REQUIRE(m.size() == distributions_.size()-1, |
| 107 | "Incompatible sample and latent model sizes" ); |
| 108 | #endif |
| 109 | Real prodDensities = 1.; |
| 110 | for(Size i=0; i<m.size(); i++) |
| 111 | prodDensities *= boost::math::pdf(dist: distributions_[i], |
| 112 | x: m[i] /varianceFactors_[i]) /varianceFactors_[i]; |
| 113 | // accumulate lambda |
| 114 | return prodDensities; |
| 115 | } |
| 116 | /*! Returns the inverse of the cumulative distribution of the (modelled) |
| 117 | latent variable (as indexed by iVariable). Involves the convolution |
| 118 | of the factors' distributions. |
| 119 | */ |
| 120 | Real inverseCumulativeY(Probability p, Size iVariable) const { |
| 121 | #if defined(QL_EXTRA_SAFETY_CHECKS) |
| 122 | QL_REQUIRE(iVariable < latentVarsCumul_.size(), |
| 123 | "Latent variable index out of bounds." ); |
| 124 | #endif |
| 125 | return latentVarsInverters_[iVariable](p); |
| 126 | } |
| 127 | /*! Returns the inverse of the cumulative distribution of the |
| 128 | idiosincratic factor. The LM here is limited to all idiosincratic |
| 129 | factors following the same distribution. |
| 130 | */ |
| 131 | Real inverseCumulativeZ(Probability p) const { |
| 132 | return boost::math::quantile(dist: distributions_.back(), p) |
| 133 | * varianceFactors_.back(); |
| 134 | } |
| 135 | /*! Returns the inverse of the cumulative distribution of the |
| 136 | systemic factor iFactor. |
| 137 | */ |
| 138 | Real inverseCumulativeDensity(Probability p, Size iFactor) const { |
| 139 | #if defined(QL_EXTRA_SAFETY_CHECKS) |
| 140 | QL_REQUIRE(iFactor < distributions_.size()-1, |
| 141 | "Random factor variable index out of bounds." ); |
| 142 | #endif |
| 143 | return boost::math::quantile(dist: distributions_[iFactor], p) |
| 144 | * varianceFactors_[iFactor]; |
| 145 | } |
| 146 | //to use this (by default) version, the generator must be a uniform one. |
| 147 | std::vector<Real> allFactorCumulInverter(const std::vector<Real>& probs) const; |
| 148 | private: |
| 149 | mutable std::vector<boost::math::students_t_distribution<Real> > distributions_; |
| 150 | mutable std::vector<Real> varianceFactors_; |
| 151 | mutable std::vector<CumulativeBehrensFisher> latentVarsCumul_; |
| 152 | mutable std::vector<InverseCumulativeBehrensFisher> latentVarsInverters_; |
| 153 | }; |
| 154 | |
| 155 | } |
| 156 | |
| 157 | #endif |
| 158 | |