Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
transitionmatrix.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2017 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
19#include "toplevelfixture.hpp"
20
21#include <boost/make_shared.hpp>
22#include <boost/test/unit_test.hpp>
23
26
27#include <ql/math/comparison.hpp>
28
29using namespace QuantLib;
30using namespace QuantExt;
31
32using boost::unit_test_framework::test_suite;
33
34BOOST_FIXTURE_TEST_SUITE(QuantExtTestSuite, qle::test::TopLevelFixture)
35
36BOOST_AUTO_TEST_SUITE(TransitionMatrix)
37
38namespace {
39template <class I> Real normEucl(I begin, I end) {
40 Real sum = 0.0;
41 for (I it = begin; it != end; ++it)
42 sum += (*it) * (*it);
43 return std::sqrt(sum);
44}
45
46template <class I> Real normMax(I begin, I end) {
47 Real m = 0.0;
48 for (I it = begin; it != end; ++it)
49 m = std::max(std::abs(*it), m);
50 return m;
51}
52
53template <class I> Real normMad(I begin, I end) {
54 Real sum = 0.0;
55 for (I it = begin; it != end; ++it)
56 sum += std::abs(*it);
57 return sum / static_cast<Real>(end - begin);
58}
59} // namespace
60
61BOOST_AUTO_TEST_CASE(testGenerator) {
62
64 BOOST_CHECK(true);
65 return;
66 }
67
68 BOOST_TEST_MESSAGE("Testing transition matrix generator computation...");
69
70 // cf. Alexander Kreinin and Marina Sidelnikova, "Regularization Algorithms for Transition Matrices"
71 // table 1 (Moody's average rating transition matrix of all corporates, 1980-1999)
72
73 // clang-format off
74 Real transDataRaw[] = {
75 // Aaa Aa A Baa Ba B C Default
76 0.8588, 0.0976, 0.0048, 0.0000, 0.0003, 0.0000, 0.0000, 0.0000, // Aaa
77 0.0092, 0.8487, 0.0964, 0.0036, 0.0015, 0.0002, 0.0000, 0.0004, // Aa
78 0.0008, 0.0224, 0.8624, 0.0609, 0.0077, 0.0021, 0.0000, 0.0002, // A
79 0.0008, 0.0037, 0.0602, 0.7916, 0.0648, 0.0130, 0.0011, 0.0019, // Baa
80 0.0003, 0.0008, 0.0046, 0.0402, 0.7676, 0.0788, 0.0047, 0.0140, // Ba
81 0.0001, 0.0004, 0.0016, 0.0053, 0.0586, 0.7607, 0.0274, 0.0660, // B
82 0.0000, 0.0000, 0.0000, 0.0100, 0.0279, 0.0538, 0.5674, 0.2535, // C
83 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 1.0000 // Default
84 };
85 // clang-format on
86 std::vector<Real> transData(transDataRaw, transDataRaw + 8 * 8);
87
88 Matrix trans(8, 8, 0.0);
89 for (Size i = 0; i < 8; ++i) {
90 for (Size j = 0; j < 8; ++j) {
91 trans[i][j] = transData[i * 8 + j];
92 }
93 }
94 BOOST_TEST_MESSAGE("Original Transition Matrix =\n" << trans);
95
96 // sanitise matrix
97
98 Matrix transSan(trans);
100 BOOST_TEST_MESSAGE("Sanitised Transition Matrix =\n" << transSan);
101 for (Size i = 0; i < transSan.rows(); ++i) {
102 Real sum = 0.0;
103 for (Size j = 0; j < transSan.columns(); ++j) {
104 if (i != j)
105 BOOST_CHECK_CLOSE(trans[i][j], transSan[i][j], 1E-8);
106 sum += transSan[i][j];
107 }
108 BOOST_CHECK_CLOSE(sum, 1.0, 1E-8);
109 }
110 BOOST_CHECK_NO_THROW(checkTransitionMatrix(transSan));
111
112 // compute generator
113
114 Matrix ltr = QuantExt::Logm(transSan);
115 BOOST_TEST_MESSAGE("Log Transition Matrix=\n" << ltr);
116
117 Matrix gen = generator(transSan);
118 BOOST_TEST_MESSAGE("Generator =\n" << gen);
119 BOOST_CHECK_NO_THROW(checkGeneratorMatrix(gen));
120
121 // compute approxmiate 1y transition matrix
122
123 Matrix approx1y = QuantExt::Expm(gen);
124 BOOST_TEST_MESSAGE("Approximate Transition Matrix =\n" << approx1y);
125 checkTransitionMatrix(approx1y);
126
127 // check results from table 5
128
129 Real rowDist[] = {6.769E-4, 0.032E-4, 1.021E-4, 0.0, 0.0, 0.0, 6.475E-4, 0.0};
130 Matrix diff1 = gen - ltr;
131
132 for (Size i = 0; i < 7; ++i) {
133 Real dist = normEucl(diff1.row_begin(i), diff1.row_end(i));
134 BOOST_TEST_MESSAGE("row " << i << " reference result " << rowDist[i] << " actual result " << dist);
135 if (QuantLib::close_enough(rowDist[i], 0.0)) {
136 BOOST_CHECK(dist < 100 * QL_EPSILON);
137 } else
138 // 2% rel. diff. to value in paper
139 BOOST_CHECK_CLOSE(dist, rowDist[i], 2.0);
140 }
141
142 // check results from table 7
143
144 Matrix diff2 = approx1y - transSan;
145 // 1% rel. diff. to value in paper
146 BOOST_CHECK_CLOSE(normMax(diff2.begin(), diff2.end()), 4.599E-4, 1.0);
147 BOOST_CHECK_CLOSE(normMad(diff2.begin(), diff2.end()), 0.382E-4, 1.0);
148
149} // testGenerator
150
151BOOST_AUTO_TEST_SUITE_END()
152
153BOOST_AUTO_TEST_SUITE_END()
matrix functions
QuantLib::Matrix Logm(const QuantLib::Matrix &m)
QuantLib::Matrix Expm(const QuantLib::Matrix &m)
bool supports_Logm()
bool supports_Expm()
void checkTransitionMatrix(const Matrix &t)
check if the matrix is a transition matrix, i.e. row sums are 1 and entries are non-negative
void checkGeneratorMatrix(const Matrix &g)
check if the matrix is a generator matirx, i.e. row sums are 0 and non-diagonal elements are non-nega...
Real sum(const Cash &c, const Cash &d)
Definition: bondbasket.cpp:107
Matrix generator(const Matrix &t, const Real horizon)
void sanitiseTransitionMatrix(Matrix &m)
BOOST_AUTO_TEST_CASE(testGenerator)
Fixture that can be used at top level.
utility functions for transition matrices and generators