Skip to content

SRISK

Introduction

A conditional capital shortfall measure of systemic risk by Brownlees and Engle (2017).

Capital shortfall

Capital shortfall is a firm's required capital reserve minus the firm's equity. Specifically, capital shortfall of a firm i on day t is

$$ \begin{equation} CS_{it} = kA_{it} - W_{it} = k(D_{it}+W_{it}) - W_{it} \end{equation} $$ where,

  • W_{it} is the market value of equity
  • D_{it} is the book value of debt
  • A_{it} = W_{it} + D_{it} is the value of quasi assets
  • k is the prudential capital fraction, set to 8%
Note

A positive capital shortfall CS means the firm is in distress, i.e., the capital reserve required is larger than the firm's equity value.

Systemic event and SRISK

A systemic event is a market decline below a threshold C over a time horizon h.

If the multiperiod arithmetic market return between t+1 to t+h is R_{mt+1:t+h}, then the systemic event is \{R_{mt+1:t+h}<C\}.

Note

h=1 month and C=-10\% are chosen in Brownlees and Engle (2017).

SRISK is the expected capital shortfall conditional on a systemic event.

\begin{equation} SRISK_{it} = E_t(CS_{it+h} | R_{mt+1:t+h} < C) \end{equation}

The total amount of systemic risk in the financial system is measured as the sum of all firm-level SRISK of the N institutions in the system with positive SRISK measures.

\begin{equation} SRISK_{t} = \sum_{i=1}^{N} SRISK_{it} \end{equation}

Institutions with negative SRISK are ignored

In a crisis it is unlikely that surplus capital will be easily mobilized through mergers or loans to support failing firms.

Computation of SRISK

First, we expand CS_{it+h},

\begin{align} SRISK_{it} &= E_t(CS_{it+h} | R_{mt+1:t+h} < C) \\ &= k E_t(D_{it+h} | R_{mt+1:t+h} < C) + (1-k) E_t(W_{it+h} | R_{mt+1:t+h} < C) \end{align}

Assumption

If debt cannot be renegotiated in case of systemic event, E_t(D_{it+h} | R_{mt+1:t+h} < C)=D_{it}

So we have,

\begin{align} SRISK_{it} &= k D_{it} + (1-k) W_{it} (1 - LRMES_{it}) \\ &= W_{it} [k LVG_{it} + (1-k) LRMES_{it} - 1] \end{align}

where,

  • LRMES_{it} is Long-Run MES, defined as LRMES_{it}=-E_i[R_{it+1:t+h} | R_{mt+1:t+h} < C].
  • LVG_{it} is quasi leverage ratio (D_{it}+W_{it})/W_{it}.

Estimating LRMES

The key step in computing SRISK is estimating the LRMES. Please refer to Long-Run MES for details.

API

estimate(firm_returns, market_returns, W, D, k=0.08, lrmes_h=22, lrmes_S=10000, lrmes_C=-0.1, lrmes_random_seed=42, aggregate_srisk=False)

SRISK of firm(s) or market at a given time

Parameters:

Name Type Description Default
firm_returns np.ndarray

(n_days,) array of firm log returns. Can also be (n_day,n_firms) array of multiple firms' log returns.

required
market_returns np.ndarray

(n_days,) array of market log returns

required
W float | np.ndarray

market value of equity. It can be either a single float value for a firm or a (n_firms,) array for multiple firms.

required
D float | np.ndarray

book value of debt. It can be either a single float value for a firm or a (n_firms,) array for multiple firms.

required
k float

prudential capital factor. Defaults to 8%.

0.08
lrmes_h int

parameter used to estimate LRMES. Prediction horizon. Defaults to 22.

22
lrmes_S int

parameter used to estimate LRMES. The number of simulations. Defaults to 10_000.

10000
lrmes_C float

parameter used to estimate LRMES. The markdown decline that defines a systemic event. Defaults to -0.1.

-0.1
lrmes_random_seed int

random seed in estimating LRMES. Defaults to 42.

42
aggregate_srisk bool

whether to compute the aggregate SRISK. Defaults to False.

False

Returns:

Type Description
np.ndarray | float

np.ndarray | float: If aggregate_srisk=False, (n_firms,) array of firm-level SRISK measures. Otherwise, a single float value for aggregate SRISK.

Examples:

>>> from frds.measures.srisk import estimate as srisk
>>> import yfinance as yf
>>> import numpy as np
>>> df = yf.download(tickers="SPY JPM GS", start="2017-01-01", end="2022-12-31",
...         progress=False, rounding=True)
>>> df = df[['Adj Close']]
>>> df.columns = df.columns.droplevel(0)
>>> df
                GS     JPM     SPY
Date
2017-01-03  214.04   72.67  202.09
2017-01-04  215.43   72.80  203.29
2017-01-05  213.82   72.13  203.13
2017-01-06  216.99   72.14  203.85
2017-01-09  215.21   72.19  203.18
...            ...     ...     ...
2022-12-23  343.05  129.30  381.45
2022-12-27  339.54  129.76  379.95
2022-12-28  338.45  130.46  375.23
2022-12-29  340.99  131.21  381.98
2022-12-30  340.94  132.08  380.98
[1510 rows x 3 columns]
>>> mkt_returns = np.log(df.SPY.pct_change()+1).dropna().to_numpy()
>>> firm_returns = np.array([np.log(df.JPM.pct_change()+1).dropna().to_numpy(),
...                          np.log(df.GS.pct_change()+1).dropna().to_numpy()]).T
>>> srisk(firm_returns, mkt_returns,
...         W=np.array([100,80]), D=np.array([900,250])) # (1)
array([  5.79157017, -64.68651904])
>>> srisk(firm_returns, mkt_returns,
...         W=np.array([100,80]), D=np.array([900,250]),
...         aggregate_srisk=True) # (2)
5.7915701669051245
  1. Hypothetical market value of equity and book value of debt.

  2. Only positive SRISK estimates are summed.

Note

yfinance.download may lead to different data at each run, so the results can vary. However, given a fixed input dataset, the function will yield the same estimate conditional on the same random seed.

Source code in src/frds/measures/srisk.py
def estimate(
    firm_returns: np.ndarray,
    market_returns: np.ndarray,
    W: float | np.ndarray,
    D: float | np.ndarray,
    k=0.08,
    lrmes_h=22,
    lrmes_S=10000,
    lrmes_C=-0.1,
    lrmes_random_seed=42,
    aggregate_srisk=False,
) -> np.ndarray | float:
    """SRISK of firm(s) or market at a given time

    Args:
        firm_returns (np.ndarray): (n_days,) array of firm log returns. Can also be (n_day,n_firms) array of multiple firms' log returns.
        market_returns (np.ndarray): (n_days,) array of market log returns
        W (float | np.ndarray): market value of equity. It can be either a single float value for a firm or a (n_firms,) array for multiple firms.
        D (float | np.ndarray): book value of debt. It can be either a single float value for a firm or a (n_firms,) array for multiple firms.
        k (float, optional): prudential capital factor. Defaults to 8%.
        lrmes_h (int, optional): parameter used to estimate `LRMES`. Prediction horizon. Defaults to 22.
        lrmes_S (int, optional): parameter used to estimate `LRMES`. The number of simulations. Defaults to 10_000.
        lrmes_C (float, optional): parameter used to estimate `LRMES`. The markdown decline that defines a systemic event. Defaults to -0.1.
        lrmes_random_seed (int, optional): random seed in estimating `LRMES`. Defaults to 42.
        aggregate_srisk (bool, optional): whether to compute the aggregate SRISK. Defaults to False.

    Returns:
        np.ndarray | float: If `aggregate_srisk=False`, (n_firms,) array of firm-level SRISK measures. Otherwise, a single float value for aggregate SRISK.

    Examples:
        >>> from frds.measures.srisk import estimate as srisk
        >>> import yfinance as yf
        >>> import numpy as np
        >>> df = yf.download(tickers="SPY JPM GS", start="2017-01-01", end="2022-12-31",
        ...         progress=False, rounding=True)
        >>> df = df[['Adj Close']]
        >>> df.columns = df.columns.droplevel(0)
        >>> df
                        GS     JPM     SPY
        Date
        2017-01-03  214.04   72.67  202.09
        2017-01-04  215.43   72.80  203.29
        2017-01-05  213.82   72.13  203.13
        2017-01-06  216.99   72.14  203.85
        2017-01-09  215.21   72.19  203.18
        ...            ...     ...     ...
        2022-12-23  343.05  129.30  381.45
        2022-12-27  339.54  129.76  379.95
        2022-12-28  338.45  130.46  375.23
        2022-12-29  340.99  131.21  381.98
        2022-12-30  340.94  132.08  380.98
        [1510 rows x 3 columns]
        >>> mkt_returns = np.log(df.SPY.pct_change()+1).dropna().to_numpy()
        >>> firm_returns = np.array([np.log(df.JPM.pct_change()+1).dropna().to_numpy(),
        ...                          np.log(df.GS.pct_change()+1).dropna().to_numpy()]).T
        >>> srisk(firm_returns, mkt_returns,
        ...         W=np.array([100,80]), D=np.array([900,250])) # (1)
        array([  5.79157017, -64.68651904])
        >>> srisk(firm_returns, mkt_returns,
        ...         W=np.array([100,80]), D=np.array([900,250]),
        ...         aggregate_srisk=True) # (2)
        5.7915701669051245

        1. Hypothetical market value of equity and book value of debt.

        2. Only positive SRISK estimates are summed.

        !!! note
            `yfinance.download` may lead to different data at each run, so the results can vary.
            However, given a fixed input dataset, the function will yield the same estimate conditional on the same random seed.

    """
    if len(firm_returns.shape) == 1:
        # Single firm
        n_firms = 1
        assert firm_returns.shape == market_returns.shape
        assert isinstance(D, float) and isinstance(W, float)
    else:
        # Multple firms
        n_days, n_firms = firm_returns.shape
        assert n_firms > 1
        assert market_returns.shape[0] == n_days
        assert isinstance(D, np.ndarray) and isinstance(W, np.ndarray)
        assert D.shape == W.shape
        assert D.shape == np.zeros((n_firms,)).shape

    # Firm-level LRMES
    LRMES = lrmes(
        firm_returns,
        market_returns,
        h=lrmes_h,
        S=lrmes_S,
        C=lrmes_C,
        random_seed=lrmes_random_seed,
    )  # (n_firms,) array of LRMES estimates

    LVG = (D + W) / W

    SRISK = W * (k * LVG + (1 - k) * LRMES - 1)
    if not aggregate_srisk:
        return SRISK
    else:
        return np.sum(SRISK.clip(min=0.0))

References

See Also

Systemic risk measures:


Bug report | Sponsor me