Title: | Risk Measures for (Financial) Networks |
---|---|
Description: | Implements some risk measures for (financial) networks, such as DebtRank, Impact Susceptibility, Impact Diffusion and Impact Fluidity. |
Authors: | Carlos Cinelli <[email protected]>, Thiago Cristiano Silva <[email protected]> |
Maintainer: | Carlos Cinelli <[email protected]> |
License: | GPL-3 |
Version: | 0.1.4 |
Built: | 2025-01-06 03:01:21 UTC |
Source: | https://github.com/carloscinelli/networkriskmeasures |
The communicability of an adjacency matrix M is defined as exp(M) where
M[i,j] can be interpreted as the weighted sums of paths from i to j.
Recall that exp(M) can be cast into a Taylor series expansion with an
infinite number additive terms.
The function permits the evaluation of exp(M) using the expm
package
or using a simpler mathematical approximation.
In the second case, the function truncates the infinite series by
simply calculating the summation terms up to a pre-defined number of factors.
communicability_matrix(x, terms = Inf, sparse = TRUE)
communicability_matrix(x, terms = Inf, sparse = TRUE)
x |
|
terms |
truncates the communicability matrix evaluation up to a pre-defined number of terms.
If |
sparse |
should the function use sparse matrices when computing the communicability?
However, if |
The function returns the communicability matrix.
Estrada, E. Hatano, N. (2008). Communicability in complex networks. Physical Review E, 77:036111.
# Creating example data ## Assets Matrix (bilateral exposures) assets_matrix <- matrix(c(0, 10, 3, 1, 0, 2, 0, 3, 0), ncol = 3) rownames(assets_matrix) <- colnames(assets_matrix) <- letters[1:3] ## Capital Buffer buffer <- c(a = 2, b = 5, c = 2) # Computing vulnerability v <- vulnerability_matrix(assets_matrix, buffer, binary = TRUE) # Computing communicability of the vulnerability matrix communicability_matrix(v)
# Creating example data ## Assets Matrix (bilateral exposures) assets_matrix <- matrix(c(0, 10, 3, 1, 0, 2, 0, 3, 0), ncol = 3) rownames(assets_matrix) <- colnames(assets_matrix) <- letters[1:3] ## Capital Buffer buffer <- c(a = 2, b = 5, c = 2) # Computing vulnerability v <- vulnerability_matrix(assets_matrix, buffer, binary = TRUE) # Computing communicability of the vulnerability matrix communicability_matrix(v)
Given a matrix of exposures, a vector of buffers and weights (optional) the functions simulates contagion for all the shock vectors provided. You may choose from the implemented propagation contagion method or create you own propagation method. Details on how to create your own method will be provided in a future version.
contagion(exposures, buffer, shock = "all", weights = NULL, method = c("debtrank", "threshold"), ..., exposure_type = c("assets", "liabilities", "impact", "vulnerability"), keep.history = FALSE, abs.tol = .Machine$double.eps ^ 0.2, max.it = min(1000, nrow(v)*10), verbose = TRUE)
contagion(exposures, buffer, shock = "all", weights = NULL, method = c("debtrank", "threshold"), ..., exposure_type = c("assets", "liabilities", "impact", "vulnerability"), keep.history = FALSE, abs.tol = .Machine$double.eps ^ 0.2, max.it = min(1000, nrow(v)*10), verbose = TRUE)
exposures |
an adjacency |
buffer |
a numeric vector with the capital buffer for each vertex.
Values should be in the same row/column order as the network of bilateral exposures. The
buffer is not needed if |
shock |
a list with the shock vectors. If |
weights |
default is |
method |
the contagion propagation method. Currently, you should use either "debtrank" for the DebtRank propagation method or "threshold" for the traditional default
cascades. The DebtRank version implemented is the one proposed in Bardoscia et al (2015).
If you want to use the old "single-hit" DebtRank of Battiston et al (2012), simply provide the argument |
... |
other arguments to be passed to the contagion propagation method. |
exposure_type |
character vector indicating the type of the bilateral exposures. It can be
an |
keep.history |
keep all the history of stress levels? This can use a lot of memory, so
the default is |
abs.tol |
the desired accuracy. |
max.it |
the maximum number of iterations. |
verbose |
gives verbose output. Default is |
The function returns an object of class "contagion"
with the results of the simulation.
Bardoscia M, Battiston S, Caccioli F, Caldarelli G (2015) DebtRank: A Microscopic Foundation for Shock Propagation. PLoS ONE 10(6): e0130406. doi: 10.1371/journal.pone.0130406
Battiston, S., Puliga, M., Kaushik, R., Tasca, P., and Caldarelli, G. (2012). DebtRank: Too central to fail? Financial networks, the FED and systemic risk. Scientific reports, 2:541.
# Loads simulated banking data data("sim_data") head(sim_data) # seed for reproducibility set.seed(15) # minimum density estimation # verbose = F to prevent printing md_mat <- matrix_estimation(sim_data$assets, sim_data$liabilities, method = "md", verbose = FALSE) # rownames and colnames for the matrix rownames(md_mat) <- colnames(md_mat) <- sim_data$bank # DebtRank simulation contdr <- contagion(exposures = md_mat, buffer = sim_data$buffer, weights = sim_data$weights, shock = "all", method = "debtrank", verbose = FALSE) summary(contdr) plot(contdr) # Traditional default cascades simulation contthr <- contagion(exposures = md_mat, buffer = sim_data$buffer, weights = sim_data$weights, shock = "all", method = "threshold", verbose = FALSE) summary(contthr) # simulating shock scenarios 1% to 25% shock in all vertices s <- seq(0.01, 0.25, by = 0.01) shocks <- lapply(s, function(x) rep(x, nrow(md_mat))) names(shocks) <- paste(s*100, "pct shock") cont <- contagion(exposures = md_mat, buffer = sim_data$buffer, shock = shocks, weights = sim_data$weights, method = "debtrank", verbose = FALSE) summary(cont) plot(cont)
# Loads simulated banking data data("sim_data") head(sim_data) # seed for reproducibility set.seed(15) # minimum density estimation # verbose = F to prevent printing md_mat <- matrix_estimation(sim_data$assets, sim_data$liabilities, method = "md", verbose = FALSE) # rownames and colnames for the matrix rownames(md_mat) <- colnames(md_mat) <- sim_data$bank # DebtRank simulation contdr <- contagion(exposures = md_mat, buffer = sim_data$buffer, weights = sim_data$weights, shock = "all", method = "debtrank", verbose = FALSE) summary(contdr) plot(contdr) # Traditional default cascades simulation contthr <- contagion(exposures = md_mat, buffer = sim_data$buffer, weights = sim_data$weights, shock = "all", method = "threshold", verbose = FALSE) summary(contthr) # simulating shock scenarios 1% to 25% shock in all vertices s <- seq(0.01, 0.25, by = 0.01) shocks <- lapply(s, function(x) rep(x, nrow(md_mat))) names(shocks) <- paste(s*100, "pct shock") cont <- contagion(exposures = md_mat, buffer = sim_data$buffer, shock = shocks, weights = sim_data$weights, method = "debtrank", verbose = FALSE) summary(cont) plot(cont)
The criticality of a vertex measures its impact
on its neighbors if it defaults. It is basically the rowSums
of the impact_matrix
.
criticality( exposures, buffer, binary = FALSE, exposure_type = c("assets", "liabilities", "impact", "vulnerability") )
criticality( exposures, buffer, binary = FALSE, exposure_type = c("assets", "liabilities", "impact", "vulnerability") )
exposures |
an adjacency |
buffer |
a numeric vector with the capital buffer for each vertex.
Values should be in the same row/column order as the network of bilateral exposures. The
buffer is not needed if |
binary |
if |
exposure_type |
character vector indicating the type of the bilateral exposures. It can be
an |
The function returns a (named) vector with the criticality for each vertex.
# Creating example data ## Assets Matrix (bilateral exposures) assets_matrix <- matrix(c(0, 10, 3, 1, 0, 2, 0, 3, 0), ncol = 3) rownames(assets_matrix) <- colnames(assets_matrix) <- letters[1:3] ## Capital Buffer buffer <- c(a = 2, b = 5, c = 2) # Criticality criticality(assets_matrix, buffer)
# Creating example data ## Assets Matrix (bilateral exposures) assets_matrix <- matrix(c(0, 10, 3, 1, 0, 2, 0, 3, 0), ncol = 3) rownames(assets_matrix) <- colnames(assets_matrix) <- letters[1:3] ## Capital Buffer buffer <- c(a = 2, b = 5, c = 2) # Criticality criticality(assets_matrix, buffer)
The impact_susceptibility
measures the
feasible contagion paths that can reach a vertex in relation to its
direct contagion paths. When the impact susceptibility is greater than 1,
it means that the vertex is vulnerable to other vertices beyond its direct
neighbors (remotely vulnerable).
The impact_fluidity
is simply the average of the impact susceptibility in
the network.
The impact_diffusion
tries to capture the influence
exercised by a node on the propagation of impacts in the network. The
impact diffusion of a vertex is measured by the change it causes on the
impact susceptibility of other vertices when its power to
propagate contagion is removed from the network.
All these measures are based on the communicability of the
vulnerability matrix (see vulnerability_matrix
and
communicability_matrix
).
impact_susceptibility( exposures, buffer, weights = NULL, terms = Inf, sparse = TRUE, binary = TRUE, exposure_type = c("assets", "liabilities", "impact", "vulnerability") ) impact_fluidity( exposures, buffer, weights = NULL, terms = Inf, sparse = TRUE, binary = TRUE, exposure_type = c("assets", "liabilities", "impact", "vulnerability") ) impact_diffusion( exposures, buffer, weights = NULL, terms = Inf, sparse = TRUE, binary = TRUE, exposure_type = c("assets", "liabilities", "impact", "vulnerability") )
impact_susceptibility( exposures, buffer, weights = NULL, terms = Inf, sparse = TRUE, binary = TRUE, exposure_type = c("assets", "liabilities", "impact", "vulnerability") ) impact_fluidity( exposures, buffer, weights = NULL, terms = Inf, sparse = TRUE, binary = TRUE, exposure_type = c("assets", "liabilities", "impact", "vulnerability") ) impact_diffusion( exposures, buffer, weights = NULL, terms = Inf, sparse = TRUE, binary = TRUE, exposure_type = c("assets", "liabilities", "impact", "vulnerability") )
exposures |
an adjacency |
buffer |
a numeric vector with the capital buffer for each vertex.
Values should be in the same row/column order as the network of bilateral exposures. The
buffer is not needed if |
weights |
default is |
terms |
truncates the communicability matrix evaluation up to a pre-defined number of terms.
If |
sparse |
should the function use sparse matrices when computing the communicability?
However, if |
binary |
if |
exposure_type |
character vector indicating the type of the bilateral exposures. It can be
an |
The impact_susceptibility
function returns a vector with the (weighted) impact susceptibility
The impact_fluidity
function returns a vector with the (weighted) impact fluidity of the network.
The impact_diffusion
function returns a data.frame
with
the vertex name and the (weighted) start, intermediate and total impact diffusion.
Silva, T.C.; Souza, S.R.S.; Tabak, B.M. (2015) Monitoring vulnerability and impact diffusion in financial networks. Working Paper 392, Central Bank of Brazil.
Silva, T.C.; Souza, S.R.S.; Tabak, B.M. (2015) Network structure analysis of the Brazilian interbank market . Working Paper 391, Central Bank of Brazil.
# Creating example data ## Assets Matrix (bilateral exposures) assets_matrix <- matrix(c(0, 10, 3, 1, 0, 2, 0, 3, 0), ncol = 3) rownames(assets_matrix) <- colnames(assets_matrix) <- letters[1:3] ## Capital Buffer buffer <- c(a = 2, b = 5, c = 2) # Measures impact_susceptibility(assets_matrix, buffer) impact_fluidity(assets_matrix, buffer) impact_diffusion(assets_matrix, buffer)
# Creating example data ## Assets Matrix (bilateral exposures) assets_matrix <- matrix(c(0, 10, 3, 1, 0, 2, 0, 3, 0), ncol = 3) rownames(assets_matrix) <- colnames(assets_matrix) <- letters[1:3] ## Capital Buffer buffer <- c(a = 2, b = 5, c = 2) # Measures impact_susceptibility(assets_matrix, buffer) impact_fluidity(assets_matrix, buffer) impact_diffusion(assets_matrix, buffer)
Methods for estimating matrix entries from the marginals (row and column sums).
There are currently two methods implemented: Maximum Entropy (Upper 2004) and Minimum Density (Anand et al. 2015).
You may use the matrix_estimation()
function, setting the desired method
.
Or you may use directly the max_ent()
function for maximum entropy estimation
or the min_dens()
function for minimum density estimation.
matrix_estimation( rowsums, colsums, method = c("me", "md"), ..., max.it = 1e+05, abs.tol = 0.001, verbose = TRUE ) max_ent(rowsums, colsums, max.it = 1e+05, abs.tol = 0.001, verbose = TRUE) min_dens( rowsums, colsums, c = 1, lambda = 1, k = 100, alpha = 1/sum(rowsums), delta = 1/sum(rowsums), theta = 1, remove.prob = 0.01, max.it = 1e+05, abs.tol = 0.001, verbose = TRUE )
matrix_estimation( rowsums, colsums, method = c("me", "md"), ..., max.it = 1e+05, abs.tol = 0.001, verbose = TRUE ) max_ent(rowsums, colsums, max.it = 1e+05, abs.tol = 0.001, verbose = TRUE) min_dens( rowsums, colsums, c = 1, lambda = 1, k = 100, alpha = 1/sum(rowsums), delta = 1/sum(rowsums), theta = 1, remove.prob = 0.01, max.it = 1e+05, abs.tol = 0.001, verbose = TRUE )
rowsums |
a numeric vector with the row sums. |
colsums |
a numeric vector with the column sums. |
method |
the matrix estimation method. Choose |
... |
further arguments passed to or from other methods. |
max.it |
the maximum number of iterations. |
abs.tol |
the desired accuracy. |
verbose |
gives verbose output. Default is |
c |
the 'cost' an extra link for the minimum density estimation. See Anand et al. (2015). |
lambda |
you should use |
k |
you should use |
alpha |
weights for the row sums deviations. See Anand et al. (2015). |
delta |
weights for the column sums deviations. See Anand et al. (2015). |
theta |
scaling parameter. Emphasizes the weight placed on finding solutions with similar characteristics to the prior matrix. See Anand et al. (2015). |
remove.prob |
probability to randomly remove a link during the algorithm. See Anand et al. (2015). |
The functions return the estimated matrix.
Upper, C. and A. Worm (2004). Estimating bilateral exposures in the German interbank market: Is there a danger of contagion? European Economic Review 48, 827-849.
Anand, K., Craig, B. and G. von Peter (2015). Filling in the blanks: network structure and interbank contagion. Quantitative Finance 15:4, 625-636.
# Example from Anand, Craig and Von Peter (2015, p.628) # Liabilities L <- c(a = 4, b = 5, c = 5, d = 0, e = 0, f = 2, g = 4) # Assets A <- c(a = 7, b = 5, c = 3, d = 1, e = 3, f = 0, g = 1) # Maximum Entropy ME <- matrix_estimation(A, L, method = "me") ME <- round(ME, 2) # Minimum Density set.seed(192) MD <- matrix_estimation(A, L, method = "md")
# Example from Anand, Craig and Von Peter (2015, p.628) # Liabilities L <- c(a = 4, b = 5, c = 5, d = 0, e = 0, f = 2, g = 4) # Assets A <- c(a = 7, b = 5, c = 3, d = 1, e = 3, f = 0, g = 1) # Maximum Entropy ME <- matrix_estimation(A, L, method = "me") ME <- round(ME, 2) # Minimum Density set.seed(192) MD <- matrix_estimation(A, L, method = "md")
The function computes an impact or vulnerability matrix given a network of bilateral exposures and a vector of capital buffers.
risk_matrix( exposures, buffer, binary = FALSE, exposure_type = c("assets", "liabilities", "impact", "vulnerability"), returns = c("impact", "vulnerability") ) vulnerability_matrix( exposures, buffer, binary = FALSE, exposure_type = c("assets", "liabilities", "impact", "vulnerability") ) impact_matrix( exposures, buffer, binary = FALSE, exposure_type = c("assets", "liabilities", "impact", "vulnerability") )
risk_matrix( exposures, buffer, binary = FALSE, exposure_type = c("assets", "liabilities", "impact", "vulnerability"), returns = c("impact", "vulnerability") ) vulnerability_matrix( exposures, buffer, binary = FALSE, exposure_type = c("assets", "liabilities", "impact", "vulnerability") ) impact_matrix( exposures, buffer, binary = FALSE, exposure_type = c("assets", "liabilities", "impact", "vulnerability") )
exposures |
an adjacency |
buffer |
a numeric vector with the capital buffer for each vertex.
Values should be in the same row/column order as the network of bilateral exposures. The
buffer is not needed if |
binary |
if |
exposure_type |
character vector indicating the type of the bilateral exposures. It can be
an |
returns |
will the function return the impact or the vulnerability matrix?
The default is |
The impact matrix represents how much a vertex impacts the capital buffer of another vertex when it defaults.
The vulnerability matrix is just the transpose of the impact matrix. It represents how much a vertex is impacted by the default of another vertex.
The function returns a (binary) impact or vulnerability matrix.
The term V[i,j] of the impact matrix represents the impact of i's default in j's capital buffer.
The term V[i,j] of the vulnerability matrix represents how much i's capital buffer is impacted by j's default.
If binary = TRUE
the values less than 1 are truncated to zero.
# Creating example data ## Assets Matrix (bilateral exposures) assets_matrix <- matrix(c(0, 10, 3, 1, 0, 2, 0, 3, 0), ncol = 3) rownames(assets_matrix) <- colnames(assets_matrix) <- letters[1:3] ## Capital Buffer buffer <- c(a = 2, b = 5, c = 2) # Vulnerability matrices vulnerability_matrix(assets_matrix, buffer, binary = FALSE) vulnerability_matrix(assets_matrix, buffer, binary = TRUE)
# Creating example data ## Assets Matrix (bilateral exposures) assets_matrix <- matrix(c(0, 10, 3, 1, 0, 2, 0, 3, 0), ncol = 3) rownames(assets_matrix) <- colnames(assets_matrix) <- letters[1:3] ## Capital Buffer buffer <- c(a = 2, b = 5, c = 2) # Vulnerability matrices vulnerability_matrix(assets_matrix, buffer, binary = FALSE) vulnerability_matrix(assets_matrix, buffer, binary = TRUE)
A simulated dataset with interbank assets, liabilities, capital buffer and weights for 125 "banks". The code to generate the data is on the examples.
A data frame with 125 rows and 5 variables
# Simulated data for ilustration purposes # Setting Seed set.seed(1100) # Heavy tailed assets assets <- rlnorm(125, 0, 2) assets[assets < 4] <- runif(length(assets[assets < 4])) # Heavy tailed liabilities liabilities <- rlnorm(125, 0, 2) liabilities[liabilities < 4] <- runif(length(liabilities[liabilities < 4])) # Making sure assets = liabilities assets <- sum(liabilities) * (assets/sum(assets)) # Buffer as a function of assets buffer <- pmax(0.01, runif(length(liabilities))*liabilities + abs(rnorm(125, 4, 2.6))) # Weights as a function of assets, buffer and liabilities weights <- (assets + liabilities + buffer + 1) + rlnorm(125, 0, 1) # creating data.frame sim_data <- data.frame(bank = paste0("b", 1:125), assets = assets, liabilities = liabilities, buffer = buffer, weights = weights)
# Simulated data for ilustration purposes # Setting Seed set.seed(1100) # Heavy tailed assets assets <- rlnorm(125, 0, 2) assets[assets < 4] <- runif(length(assets[assets < 4])) # Heavy tailed liabilities liabilities <- rlnorm(125, 0, 2) liabilities[liabilities < 4] <- runif(length(liabilities[liabilities < 4])) # Making sure assets = liabilities assets <- sum(liabilities) * (assets/sum(assets)) # Buffer as a function of assets buffer <- pmax(0.01, runif(length(liabilities))*liabilities + abs(rnorm(125, 4, 2.6))) # Weights as a function of assets, buffer and liabilities weights <- (assets + liabilities + buffer + 1) + rlnorm(125, 0, 1) # creating data.frame sim_data <- data.frame(bank = paste0("b", 1:125), assets = assets, liabilities = liabilities, buffer = buffer, weights = weights)