knitr::opts_chunk$set( collapse = TRUE, comment = "#>" )
This vignette provides a brief introduction to the syslosseval
package, a collection of
functions and data to perform the analysis in the paper Systemic Loss Evaluation by Thomas
Breuer, Martin Summer and Branko Urosevic.
We give first a brief description of how to install the package and download the source code. In the next section we describe the data, while in the last section we show how to use the functions of the package to analyze the data.
To use the package, you first have to install it from GitHub. Here are the necessary steps:
To install a R package, start by installing the devtools package. The best way to do this is from CRAN, by typing:
install.packages("devtools", repo = "https://cran.r-project.org/")
Install the package of interest from GitHub using the following code, where
you need to remember to list both the author and the name of the package
(in GitHub jargon, the package is the repo, which is short for repository).
In this example, we are installing the syslosseval
package created by Martin-Summer-1090.
library(devtools) install_github("Martin-Summer-1090/syslosseval", repos = "http://cran.at.r-project.org")
library(syslosseval)
Now you are ready to use the functions and the data made available by the syslosseveal
package.
To download the source code, you have to go to https://github.com/Martin-Summer-1090/syslosseval and
press the green button which is marked "Code". This will download a zip archive of the package source
to your local machine. In the source code you can study the code of the individual functions. The
source code directory also contains the all raw data as a syslosseval_raw_data.tar.gz
archive.
This archive also contains the scripts which construct the data files from these raw data.
You have to unpack the syslosseveal_raw_data.tar.gz
to see both the raw data files, the ancilliary information
files as well as the R-scripts which generate the datafiles provided by the package from the raw data.
Here are
instructions how to unpack a tar.gz
archive on windows and mac https://www.uubyte.com/extract-tar-gz-bz2-on-windows-mac.html, here is an instruction how to unpack in linux
https://linuxize.com/post/how-to-extract-unzip-tar-gz-file/
When you install the package there will be in total seven datasets available to you. These datasets are:
|Dataset number | Dataset name | Data Description |
|:--------------|:------------ | :----------------|
| 1 |eba_exposures_2016
| Exposure data from the EBA 2016 stress test |
| 2 |eba_exposures_2020
| Exposure data from the EBA 2020 transparency exercises |
| 3 |eba_impairments_2016
| Impairment data from the EBA 2016 stress test |
| 4 |eba_impairments_2020
| Imputed impairments data based on IMF methods |
| 5 |sovereign_bond_indices
| Daily values of sovereign bond indices from 2009-2019 |
| 6 |average_daily_volume_sovereign
| Average daily volumes of sovereign bonds from 2009 -2019 |
| 7 |example_multiple_equilibria
| A toy example, where multiple equilibria occur |
A detailed description of how the data are compiled is given in the paper in appendix B. Alternatively you can
look at the scripts make_balance_sheets_2016.R
, make_balance_sheets_2020.R
, make_price_volume_data.R
and
make_2020_impairment_scenarios.R
, which are contained in the syslosseval_raw_data.tar.gz
in the data-raw
folder
of the project source code.
To understand the structure of the exposure data, let us look at an example with the 2016 data.
exposure_data_2016 <- eba_exposures_2016 head(exposure_data_2016, 3)
You can see 11 variables: LEI_code
, Country_code
, Bank_name
, Period
, Country
, Exposure
, Loan_Amount
,
Bond_Amount
, Total_Amount
, Unit
, Currency
and their first tree records. LEI_code
is the legal identifyer
of the banks. The variable Bank_name
gives the corresponding name of the bank, while Country_code
gives the
ISO-code of the country in which the banks are domiciled.
The list of legal identifiers, country codes and bank names in the 2016 dataset are:
library(magrittr) lei_count_name <- dplyr::select(exposure_data_2016, LEI_code, Country_code, Bank_name) %>% unique() knitr::kable(lei_count_name)
You can filter the data with respect to these values to look at individual banks for example. The Period
variable
specifies for which observation time the data are recorded. In the 2016 dataset this
is 201512
meaning 31.12.2015.
The variable Country
specifies the countries to which a particular exposure is held. The country is described
either by an ISO-code or by a verb like Total
when the exposure refers to an aggregate exposure. The list of
countries in the 2016 dataset is:
exp_count_list_2016 <- dplyr::select(exposure_data_2016, Country) %>% unique() %>% dplyr::arrange(Country) knitr::kable(exp_count_list_2016)
The Exposure
variable gives the asset class or the exposure category. The entire list of exposure
categories for the 2016 data follows the IRB scheme. STA exposures are aggregated with the IRB exposures
according to a mapping described in appemndix B of the paper.
exposure_list_2016 <- dplyr::select(exposure_data_2016, Exposure) %>% unique() knitr::kable(exposure_list_2016)
The variables Loan_Amount
and Bond_Amount
split the total exposure recorded in Total_Amount
according
to whether the exposure is to be regarded as a loan or as a bond. The data allow us to construct such a split
only for the exposure category Central banks and central governments
.
The exposure data for the period 2020 work exactly according to same scheme but cover more banks than the 2016 stress test exercise.
To understand the structure of impairments let us look at the impairment data of 2016 as an example again.
impairment_data_2016 <- eba_impairments_2016 head(impairment_data_2016, 3)
Here the variables are similar as in the case of exposure data. In contrast to the exposure
data the Period
variable in the impairment data can take three values 201612
, 201712
and 201812
because
in the EBA stress test there impairments are projected one, two and three years ahead from the observation
period, which is in this case 201512
. The Scenario
variable specified whether the impairments in a given
period are projections fro the baseline scenario or the adverse scenario. Finally the value of the impairment rate
is recorded in the variable Impairment_rate
.
For the 2020 data we have constructed an "artificial" impairment dataset. The stress test 2020 was suspended due to the pandemic. What we do is to take results from an IMF-paper by Hardy and Schmieder (Rules of thumb for bank solvency stress tests, IMF WP 13 232) and take impairment rates that they find typical for advanced countries in baseline and stress scenario, assuming that the first year after the pandemic is "severe", the second year is "extreme" and the third year is "moderate".
The average daily volume data are collected from various internet sources and compiled into as small dataframe. To show the structure of this dataframe we display the data for the year 2015 as an example. We have these data from 2009-2019:
adv <- average_daily_volume_sovereign %>% dplyr::filter(Year == "2015") knitr::kable(adv)
From the table we can see that we have not collected the data for all the exposure countries recorded in the
EBA dataset. We were not able to compile all of these data from public sources. What we do instead is to consider
only the individual countries DE
, ES
, FR
, GB
, IT
, JP
, US
, and we put all the rest
into a general position Rest of the world
. The details of how this residual positions is estimated we refer
the paper or to the script make_price_volume_data.R
in the tar.gz archive in the data-raw folder of the source
code.
The data on sovereign bond indices have the variables Country
, recorded as an ISO code, Date
specifying
particular days of recorded index values and Value
, the particular value of the index. These data are again
recorded for DE
, ES
, FR
, GB
, IT
, JP
, US
and Rest of the world
. To give an impression we display
the first ten records of the file:
bond_data <- sovereign_bond_indices %>% head(10) knitr::kable(bond_data)
Finally the package contains data used in an example discussed in Table 5 in the appendix of the paper. The example is there to demonstrate that in general we can get multiple equilibria. The example data can be used to reproduce the claims made in the paper.
In this section we give a brief example, how to work with the functions provided in the syslosseval
package.
Throughout the description we uses the case of the 2016 stress test data as an example. The analysis for the
2020 transparency exercise works completely in the same way.
make_stress_data()
functionA typical analysis begins with compiling data for a stress test. The function
make_stress_data()
does this job for us. The function takes as arguments the exposure dataset of the
given year, the impairment dataset of the given year a parameter specifiying the stress horizon (1,2,3) for one,
two or three years ahead and the base year, which specifies the year when the expsosures are recorded.
From this input the function produces a new table with all the variables contained in the exposure and impairemt data. It matches the impairment data from the stress scenario at the given horizon with the exposure data and computes loan losses from this input.
Assume we want to look at the EBA data from 2016 and analyze the losses from the EBA-stress scenario
one year ahead using the function make_stress_data()
from our package.
stress_data <- make_stress_data(eba_exposures_2016, eba_impairments_2016, 1, 2015) head(stress_data, 5)
Here we have specified the appropriate exposure and impairment dataset, the horizon is 1 (year ahead) and the
base year is 2015 (December 2015). We can see that the function produces a dataframe containing 13 variables
LEI_code
, Country_code
, Bank_name
, Period
, Country
, Exposure
, Loan_Amount
,
Bond_Amount
, Total_Amount
, Loan_Losses
, Unit
, Currency
and Impairment_rate
. The dataframe (tibble)
shown in our example displays the values of the first five records for the first seven variables.
If we would like to use the impairment rates projected for the two year hrizon by EBA we would specify the third
argument as 2 instead of 1. This would use the impairment rates projected for 2017 and apply these rates to
compute the variable Loan_Losses
. This dataframe can then be the basis for further experiments.
Note that we always get the stress values and not the baseline scenarios. We have implemnted the function this way because we only work with the stress scenarios in the context of our paper. We might in a future version implement the option to also choose the baseline scenario. This would need another parameter in the function.
Finally note that you can always look up the necessary information via the standard help-functionality of the R system by typing
?make_stress_data
This will display the function documentation in the Help pane in the standard help format provided by R.
make_state_variables()
functionThe next important function you will need for conducting an analysis similar as in the paper is the
make_state_variables()
function. This function takes the output of make_stress_data
as input and
creates the following objects from the paper
| State variable | Name in R | Description | |:---------------|:----------|:------------| | $S^0$ | S_0 | Number of banks times number of marketable security classes matrix at $t=0$| | $L^0$ | L_0 | Number of banks times number of loan exposure classes matrix at $t=0$| | $e^0$ | e_0 | Number of banks times 1 vector of core tier 1 equity at $t=0$ | | $S^1$ | S_1 | Number of banks times number of marketable security classes matrix at $t=1$| | $L^1$ | L_1 | Number of banks times number of loan exposure classes matrix at $t=1$| | $e^1$ | e_1 | Number of banks times 1 vector of core tier 1 equity at $t=1$ | | $\theta$ | theta | Number of banks times 1 vector of sold shares of marketable securities with all components 0 |
When you call make_state_variables()
these objects will be written to a list. They can then be referred to
using the typical list operations of R.
For example, if you want to extract the core tier 1 equity vector at the observation period, you would type
As an example, assume we want to build the state variables from the stress data
compiled by make_stress_data()
and we want to inspect $e^0$. We would then type for example
states <- make_state_variables(stress_data) equity <- states$e_0 # write to table for display in this vigniette knitr::kable(equity)
A final note on the compiled stress data is in order here. EBA feeds its data based on the FINREP/COREP regulatory reporting framework. This is a risk-oriented regulatory framework which is very special to the risk analysis as conducted by the European competent authorities. Applying these data to a different context, like to our specific research question, requires some care.
For example, our approach uses the standard concept of a bank balance sheet. This already creates difficulties because it requires to aggregate two reporting schemes using slightly different asset classifications - IRB and STA - into one framework. In the EBA stress test this is no issue because in their methodology loss projections are applied to the different classifications separately. The losses are then simply aggregated and compared to the equity figures.
Another issue which arises is that EBA distinguishes credit risk, market risk and operational risk as separate categories. While most assets of a bank are subject to some form of credit risk, this is not true for all assets. Thus if we naively add up the value of assets in the EBA datafiles we will not arrive at the value of total assets published in the annual reports and balance sheets of banks.
Interestingly the EBA figures can be smaller (this is the case for most banks) but also larger (for some). When the figures are smaller this means that under the existing financial reporting scheme some banks can exclude many positions on their balance sheet from credit risk reporting because they are not considered relevant for credit risk. A complicated and opaque system of adjustment factors, on the other hand, forces banks to adjust the values fo certain positions for risk considerations by applying some multipliers determined in the regulation amplifying certain positions. This can end up in a situation where the credit risky positions have an aggregate value from the EBA data which is are larger than total assets reported in the balance sheet.
Since we work with the concept of a balance-sheet and the concept of raw (unweighted) leverage of banks we have to correct for this. Our approach in the paper is as follows. We check whether the value of all exposures reported by a bank to EBA is smaller (this is the majority of cases) or larger (minority). If the value is smaller we add to the exposures a Residual value making up for the difference between the total EBA exposure and the reported value of total assets. If the value is bigger, the residual is 0 and the total assets of the bank in our data will be larger than the total assets reported in the balance sheet. This is of course a heuristic, which does not go into the details of where these gaps actually come from.
The residual position is computed based on these assumptions when the make_stress_data()
function is called and
is called Residual. The residual position is added as an additional category of exposure to the $L^0$ and
to the $L^1$ matrix.
The individual matrices are created by seperate functions nested in the make_state_variables
function and
are called make_e0()
, make_L0()
etc. These functions just extract the data from the long format of
the output of make_stress_variables()
and transforms them into matrices (with bank names as row names and
asset classes as column names). The loan asset classes are the EBA IRB
exposure categories plus the residual position, the sovereign bond exposure classes are the
bond exposure values recorded in the EBA data where the countries DE, ES, GB, FR, IT, JP, US, Rest of the world
are treated as separate marketable asset classes.
Definition 1 in our paper defines the concept of a fire sale equilibrium. To compute a fire sale equilibrium we need to compose three seperate functions:
The bank behavior function (Definition 1, 1 in the paper) is implemented in bank_behavior_function()
and
the price impact function (equation (9) in the paper) is implemented in price_impact_function()
.
Both of these functions are called by the third function fixed_point_computation_function()
which computes
a fixed point (a securities discount factor fulfilling the equilibrium condition) by implementing the procedure
characterized in Theorem 2. The Theorem specifies an iteration which converges to a fixed point for a given set
of accuracy, which is set in our implementation as a default to $10^{-9}$ (this
precision can be adjusted as needed). Since in general we can not hope that a fixed point is unique (see example
in the appendix of our paper) the iteration is started first from below (starting at minimum impact 0 and
one iteration starting from the maximum impact $\delta_{max}$). According to the Tarski fixed point theorem, the
set of fixed points is a complete lattice. There might be fixed points other than the potentially different fixed
points found by our function through iteration from below or above. Since the fixed points can be ordered
it is sufficient for us to find the lowest and the highest fixed point, because this allows us to gauge
fire sale losses. Other fixed points will just yoield intermediate values and need not be especially considered
for a loss potential estimate. For actual data it will often be the case that the lower and upper fixed point
coincide. In this case the fixed point is unique.
Now let us show how our functions can be used to compute fire sale equilibria. We have computed the state variables
from the 2016 data using the 1 year ahead EBA loss projections from the stress test. We have compiled the
stress data with make_stress_data()
before and we have used the output of this compilation to compute
the list fo matrices "states" using make_state_variables()
.
With these data and a few additional parameters we can call the fixed point function. The additional parameters are
Calling the function with these parameters will give us the price impacts for the different sovereign bond holdings which may arise by our behavior induced deleveraging. The output of the function is a dataframe reporting the lower fixed point, the number of iterations for the approximation from below, the upper fixed point and the number of iterations for approximation from above, the maximum impact, the fixed point(s) of course and an indicator which immediately checks and displays whether the fixed points coincide or not (whether the fixed point is unique)
fix <- fixed_point_computation_function(mat = states, lb = 33, data_idx = sovereign_bond_indices, data_adv = average_daily_volume_sovereign, base_year = 2015, constant = 1.5, accuracy = 10^(-9)) # write to table for display in this vigniette knitr::kable(fix)
Here we see the output of the computation written to a tibble. The first column shows the security class, identifying the different sovereign bonds by the country ISO-code and by the label "Rest of the world". Then we see the lower fixed point, which was computed in 10 iterations, starting from $\delta = 0$. Then we see the upper fixed point computed in 9 iterations strating from $\delta = \delta_{\max}$. We see the maximum impact, the impact that would have resulted if all banks had sold their entire sovereign bond portfolio. Finally we see that all components in the upper and lowe fixed points are identical, thus the fixed point in this case is also unique.
Finally we show how to use three auxiliary functions which help to display the output of the computations in terms of the system state according to Table 1 (state of the system at $t=0$), Table 2 (state of the system at $t=1$) and Table 3 (state of the system at $t = (1+\tau)$ in the paper.
These functions are called make_table_initial_state()
, make_table_stress_state()
and make_table_final_state()
. The table displays Bank names the total asset value ${\bf{a}^t}$, the value of
equity $\bf{e}^t$ and leverage $\bf{lambda}^t$.
With the 2016 data, if we want to display the state of the system at $t=0$ we would call
init <- make_table_initial_state(states) # write to table for display in this vigniette knitr::kable(init)
The system state at $t=1$ can be displayed with
stress_state <- make_table_stress_state(states) # write to table for display in this vigniette knitr::kable(stress_state)
Finally to display the table after fire sales (when the fixed point is unique) or the lower and upper tables (when there are multiple fixed points) we need the following function call. Note that this time the function needs to get the output of the fixed point computation as an input and we need to give the leverage threshold as an argument once again.
final <- make_table_final_state(states, fix, 33) # write to table for display in this vigniette knitr::kable(stress_state)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.