Depositor Runs = f(Fundamentals, Liquidity Fragility)
Public / common shock (helps coordination in panic) = Mark-to-Market (MTM) losses on securities
Larger MTM losses → worse perceived solvency / collateral value → higher run probability especially in an acute panic window
Liquidity fragility / mismatch (amplifies run incentives) = Uninsured deposit leverage
Higher uninsured deposits (relative to liquid resources/equity) → greater rollover risk and weaker ability to meet withdrawals → higher run probability
Key interaction (panic mechanism)
MTM × Uninsured leverage: Runs rise with (i) public bad news about solvency (MTM) and (ii) rollover fragility (uninsured leverage), and the effect of MTM is strongest when uninsured leverage is high.
Identification Strategy:
Baseline — Who Borrowed? \(\text{BTFP}_i = \alpha + \beta_1 \cdot \text{MTM Loss}_i + \beta_2 \cdot \text{UninsuredLev}_i + \gamma \mathbf{X}_i + \varepsilon_i\) Expect \(\beta_1 > 0\) (worse fundamentals → borrow), \(\beta_2 > 0\) (fragile banks borrowed).
Interaction Model \(\text{BTFP}_i = \alpha + \beta_1 \cdot \text{MTM}_i + \beta_2 \cdot \text{UninsuredLev}_i + \beta_3 (\text{MTM}_i \times \text{UninsuredLev}_i) + \gamma \mathbf{X}_i + \varepsilon_i\) \(\beta_3 > 0\) → threshold varies with fragility (Goldstein-Pauzner).
Solvent (AdjEquity > 0) vs Insolvent (AdjEquity < 0) splits.
Intensive Margin — Borrowing amount conditional on borrowing.
OMO Losses Only — Exploits collateral eligibility channel.
Par Benefit — Treatment intensity from BTFP design.
Temporal Evolution — Falsification: crisis drivers should fade post-crisis.
Pre-BTFP Discount Window — Before par-value subsidy existed.
Table Map:
| Paper Table | Analysis | Sample | Method |
|---|---|---|---|
| Table 1 | Extensive Margin — Baseline + Interaction (Acute) | BTFP: All, Solvent, Insolvent | 6 models, LPM |
| Table 1-DW | Extensive Margin — DW (Acute) | DW: All, Solvent, Insolvent | 6 models, LPM |
| Table 1-FHLB | Extensive Margin — FHLB (Acute) | FHLB: All, Solvent, Insolvent | 6 models, LPM |
| Table 2 | Robustness: Acute vs Arb | BTFP All | 4 models, LPM |
| Table 3 | Robustness: OMO Losses Only (Acute) | BTFP All | 2 models, LPM |
| Table 4 | Robustness: Intensive Margin (Acute) | BTFP Borrowers | 2 models, LPM |
| Table 5 | Robustness: Par Benefit (Acute) | BTFP All | 2 models, LPM |
| Table 6 | Temporal Evolution (BTFP, DW, FHLB) | All | 2 per period, LPM |
| Table 7 | Pre-BTFP DW (March 10 & March 10–13) | DW All | 4+ models, LPM |
Sections:
Controls: ln_assets,
cash_ratio, loan_to_deposit,
book_equity_ratio, wholesale,
roa
(Note: book_equity_ratio omitted from to avoid
collinearity.)
rm(list = ls())
# Core
library(data.table)
library(dplyr)
library(tidyr)
library(stringr)
library(lubridate)
library(purrr)
library(tibble)
# Econometrics
library(fixest)
library(marginaleffects)
library(nnet)
library(broom)
# Tables
library(modelsummary)
library(knitr)
library(kableExtra)
# Statistics
library(DescTools)
# Visualization
library(ggplot2)
library(gridExtra)
library(scales)
library(patchwork)
# I/O
library(readr)
library(readxl)
cat("All packages loaded.\n")
## All packages loaded.
# ==============================================================================
# CORE HELPERS
# ==============================================================================
winsorize <- function(x, probs = c(0.025, 0.975)) {
if (all(is.na(x))) return(x)
q <- quantile(x, probs = probs, na.rm = TRUE, names = FALSE)
pmax(pmin(x, q[2]), q[1])
}
standardize_z <- function(x) {
if (all(is.na(x))) return(x)
(x - mean(x, na.rm = TRUE)) / sd(x, na.rm = TRUE)
}
safe_div <- function(num, denom, default = NA_real_) {
ifelse(is.na(denom) | denom == 0, default, num / denom)
}
create_size_category_3 <- function(assets_thousands) {
assets_millions <- assets_thousands / 1000
case_when(
assets_millions >= 100000 ~ "Large (>$100B)",
assets_millions >= 1000 ~ "Medium ($1B-$100B)",
TRUE ~ "Small (<$1B)"
)
}
size_levels_3 <- c("Small (<$1B)", "Medium ($1B-$100B)", "Large (>$100B)")
format_pval <- function(p) {
case_when(is.na(p) ~ "", p < 0.01 ~ "***", p < 0.05 ~ "**", p < 0.10 ~ "*", TRUE ~ "")
}
# ==============================================================================
# SUMMARY STATISTICS
# ==============================================================================
summary_stats_function <- function(df, column_list, group_by = NULL) {
summary_stat_df <- df %>% select(all_of(column_list)) %>% data.table()
calc_stats <- function(data) {
data %>%
summarise(across(everything(), list(
N = ~ sum(!is.na(.)),
Mean = ~ round(mean(., na.rm = TRUE), 3),
SD = ~ round(sd(., na.rm = TRUE), 3),
P10 = ~ round(quantile(., 0.10, na.rm = TRUE), 3),
P25 = ~ round(quantile(., 0.25, na.rm = TRUE), 3),
P50 = ~ round(quantile(., 0.50, na.rm = TRUE), 3),
P75 = ~ round(quantile(., 0.75, na.rm = TRUE), 3),
P90 = ~ round(quantile(., 0.90, na.rm = TRUE), 3)
), .names = "{col}__{fn}")) %>%
pivot_longer(cols = everything(),
names_to = c("Variable", ".value"), names_sep = "__")
}
if (!is.null(group_by)) {
summary_stat_df %>% group_by(across(all_of(group_by))) %>% calc_stats()
} else {
calc_stats(summary_stat_df)
}
}
# ==============================================================================
# FILE I/O (OneDrive resilient)
# ==============================================================================
safe_writeLines <- function(text, con, max_retries = 5, wait_sec = 2) {
for (i in seq_len(max_retries)) {
result <- tryCatch({ writeLines(text, con); TRUE },
error = function(e) {
if (i < max_retries) { Sys.sleep(wait_sec) }
FALSE
})
if (isTRUE(result)) return(invisible(NULL))
}
warning("Failed to write ", con, " after ", max_retries, " attempts.")
}
save_table <- function(tbl, filename, caption_text = "") {
latex_file <- file.path(TABLE_PATH, paste0(filename, ".tex"))
latex_content <- knitr::kable(tbl, format = "latex", caption = caption_text, booktabs = TRUE)
safe_writeLines(as.character(latex_content), latex_file)
message("Saved: ", filename, ".tex")
}
save_figure <- function(plot_obj, filename, width = 12, height = 8) {
ggsave(file.path(FIG_PATH, paste0(filename, ".pdf")), plot = plot_obj,
width = width, height = height, device = "pdf")
message("Saved: ", filename, ".pdf")
}
# ==============================================================================
# PATHS — EDIT THIS BLOCK ONLY
# ==============================================================================
BASE_PATH <- "C:/Users/mohua/OneDrive - Louisiana State University/Finance_PhD/DW_Stigma_paper/Liquidity_project_2025"
DATA_PROC <- file.path(BASE_PATH, "01_data/processed")
OUTPUT_PATH <- file.path(BASE_PATH, "03_documentation/analysis_all_specification")
TABLE_PATH <- file.path(OUTPUT_PATH, "tables")
FIG_PATH <- file.path(OUTPUT_PATH, "figures")
for (path in c(TABLE_PATH, FIG_PATH)) {
if (!dir.exists(path)) dir.create(path, recursive = TRUE)
}
# ==============================================================================
# KEY DATES AND PERIODS
# ==============================================================================
BASELINE_MAIN <- "2022Q4"
BASELINE_ARB <- "2023Q3"
BASELINE_WIND <- "2023Q4"
DATE_MAR01 <- as.Date("2023-03-01")
DATE_MAR09 <- as.Date("2023-03-09")
DATE_MAR10 <- as.Date("2023-03-10")
DATE_MAR12 <- as.Date("2023-03-12")
DATE_MAR13 <- as.Date("2023-03-13")
DATE_MAR14 <- as.Date("2023-03-14")
ACUTE_START <- as.Date("2023-03-13")
ACUTE_END <- as.Date("2023-05-01")
POST_ACUTE_END <- as.Date("2023-10-31")
ARB_START <- as.Date("2023-11-01")
ARB_END <- as.Date("2024-01-24")
WIND_START <- as.Date("2024-01-25")
WIND_END <- as.Date("2024-03-11")
DW_DATA_END <- as.Date("2023-12-31")
OVERALL_START <- as.Date("2023-03-01")
OVERALL_END <- as.Date("2024-03-11")
call_q <- read_csv(file.path(DATA_PROC, "final_call_gsib.csv"), show_col_types = FALSE) %>%
mutate(idrssd = as.character(idrssd))
btfp_loans_raw <- read_csv(file.path(DATA_PROC, "btfp_loan_bank_only.csv"), show_col_types = FALSE) %>%
mutate(rssd_id = as.character(rssd_id), btfp_loan_date = mdy(btfp_loan_date))
dw_loans_raw <- read_csv(file.path(DATA_PROC, "dw_loan_bank_2023.csv"), show_col_types = FALSE) %>%
mutate(rssd_id = as.character(rssd_id), dw_loan_date = ymd(dw_loan_date))
cat("=== DATA LOADED ===\n")
## === DATA LOADED ===
cat("Call Report:", nrow(call_q), "obs |", n_distinct(call_q$idrssd), "banks\n")
## Call Report: 75989 obs | 5074 banks
cat("BTFP Loans:", nrow(btfp_loans_raw), "| DW Loans:", nrow(dw_loans_raw), "\n")
## BTFP Loans: 6734 | DW Loans: 10008
excluded_banks <- call_q %>%
filter(period == BASELINE_MAIN, failed_bank == 1 | gsib == 1) %>%
pull(idrssd)
cat("Excluded banks (failed + G-SIBs):", length(excluded_banks), "\n")
## Excluded banks (failed + G-SIBs): 41
btfp_loans <- btfp_loans_raw %>% filter(!rssd_id %in% excluded_banks)
dw_loans <- dw_loans_raw %>% filter(!rssd_id %in% excluded_banks)
# ==============================================================================
# Creates period-specific borrower indicators from loan-level data
# ==============================================================================
create_borrower_indicator <- function(loans_df, date_col, id_col, amount_col,
start_date, end_date, prefix) {
loans_df %>%
filter(!!sym(date_col) >= start_date, !!sym(date_col) <= end_date) %>%
group_by(!!sym(id_col)) %>%
summarise(
"{prefix}" := 1L,
"{prefix}_amt" := sum(!!sym(amount_col), na.rm = TRUE),
"{prefix}_first" := min(!!sym(date_col)),
.groups = "drop"
) %>%
rename(idrssd = !!sym(id_col))
}
# --- BTFP indicators ---
btfp_mar10 <- create_borrower_indicator(btfp_loans, "btfp_loan_date", "rssd_id", "btfp_loan_amount", DATE_MAR10, DATE_MAR10, "btfp_mar10")
## Warning: There was 1 warning in `summarise()`.
## ℹ In argument: `btfp_mar10_first = min(btfp_loan_date)`.
## Caused by warning in `min.default()`:
## ! no non-missing arguments to min; returning Inf
btfp_mar10_13 <- create_borrower_indicator(btfp_loans, "btfp_loan_date", "rssd_id", "btfp_loan_amount", DATE_MAR10, DATE_MAR13, "btfp_mar10_13")
btfp_acute <- create_borrower_indicator(btfp_loans, "btfp_loan_date", "rssd_id", "btfp_loan_amount", ACUTE_START, ACUTE_END, "btfp_acute")
btfp_post <- create_borrower_indicator(btfp_loans, "btfp_loan_date", "rssd_id", "btfp_loan_amount", ACUTE_END + 1, POST_ACUTE_END, "btfp_post")
btfp_arb <- create_borrower_indicator(btfp_loans, "btfp_loan_date", "rssd_id", "btfp_loan_amount", ARB_START, ARB_END, "btfp_arb")
btfp_wind <- create_borrower_indicator(btfp_loans, "btfp_loan_date", "rssd_id", "btfp_loan_amount", WIND_START, WIND_END, "btfp_wind")
btfp_overall <- create_borrower_indicator(btfp_loans, "btfp_loan_date", "rssd_id", "btfp_loan_amount", OVERALL_START, OVERALL_END, "btfp_overall")
# --- DW indicators ---
dw_prebtfp <- create_borrower_indicator(dw_loans, "dw_loan_date", "rssd_id", "dw_loan_amount", DATE_MAR01, DATE_MAR12, "dw_prebtfp")
dw_mar10 <- create_borrower_indicator(dw_loans, "dw_loan_date", "rssd_id", "dw_loan_amount", DATE_MAR10, DATE_MAR10, "dw_mar10")
dw_mar10_13 <- create_borrower_indicator(dw_loans, "dw_loan_date", "rssd_id", "dw_loan_amount", DATE_MAR10, DATE_MAR13, "dw_mar10_13")
dw_acute <- create_borrower_indicator(dw_loans, "dw_loan_date", "rssd_id", "dw_loan_amount", ACUTE_START, min(ACUTE_END, DW_DATA_END), "dw_acute")
dw_post <- create_borrower_indicator(dw_loans, "dw_loan_date", "rssd_id", "dw_loan_amount", ACUTE_END + 1, min(POST_ACUTE_END, DW_DATA_END), "dw_post")
dw_overall <- create_borrower_indicator(dw_loans, "dw_loan_date", "rssd_id", "dw_loan_amount", OVERALL_START, DW_DATA_END, "dw_overall")
cat("\n=== BORROWER COUNTS ===\n")
##
## === BORROWER COUNTS ===
cat("BTFP: Mar10=", nrow(btfp_mar10), " Acute=", nrow(btfp_acute),
" Post=", nrow(btfp_post), " Arb=", nrow(btfp_arb), " Wind=", nrow(btfp_wind), "\n")
## BTFP: Mar10= 0 Acute= 485 Post= 811 Arb= 797 Wind= 237
cat("DW: Pre=", nrow(dw_prebtfp), " Acute=", nrow(dw_acute), " Post=", nrow(dw_post), "\n")
## DW: Pre= 106 Acute= 417 Post= 954
# ==============================================================================
# UNIFIED VARIABLE CONSTRUCTION
# Builds BOTH the AE and MTM variables in a single pass
# ==============================================================================
construct_analysis_vars <- function(baseline_data) {
baseline_data %>%
mutate(
# =======================================================================
# RAW VARIABLES (for descriptive tables — original scale)
# =======================================================================
mtm_total_raw = mtm_loss_to_total_asset,
mtm_btfp_raw = mtm_loss_omo_eligible_to_total_asset,
mtm_other_raw = mtm_loss_non_omo_eligible_to_total_asset,
uninsured_lev_raw = uninsured_deposit_to_total_asset,
uninsured_share_raw = uninsured_to_deposit,
ln_assets_raw = log(total_asset),
cash_ratio_raw = cash_to_total_asset,
securities_ratio_raw = security_to_total_asset,
loan_ratio_raw = total_loan_to_total_asset,
book_equity_ratio_raw = book_equity_to_total_asset,
tier1_ratio_raw = tier1cap_to_total_asset,
roa_raw = roa,
fhlb_ratio_raw = fhlb_to_total_asset,
loan_to_deposit_raw = loan_to_deposit,
wholesale_raw = safe_div(
fed_fund_purchase + repo + replace_na(other_borrowed_less_than_1yr, 0),
total_liability, 0) * 100,
# Deposit outflows (forward-looking)
uninsured_outflow_raw = change_uninsured_fwd_q,
insured_outflow_raw = change_insured_deposit_fwd_q,
total_outflow_raw = change_total_deposit_fwd_q,
# =======================================================================
# ADJUSTED EQUITY (the primary solvency measure)
# AE = Book Equity - MTM Losses, as % of assets
# This is theta in Goldstein-Pauzner: the bank's TRUE economic position
# =======================================================================
adjusted_equity_raw = book_equity_to_total_asset - mtm_loss_to_total_asset,
# =======================================================================
# JIANG ET AL. INSOLVENCY MEASURES
# =======================================================================
mv_assets = total_asset * (1 - mtm_loss_to_total_asset / 100),
idcr_1 = safe_div(
mv_assets - 0.5 * uninsured_deposit - insured_deposit,
insured_deposit, NA_real_),
idcr_2 = safe_div(
mv_assets - uninsured_deposit - insured_deposit,
insured_deposit, NA_real_),
mtm_insolvent = as.integer(adjusted_equity_raw < 0),
mtm_solvent = as.integer(adjusted_equity_raw >= 0),
insolvent_idcr_s50 = as.integer(idcr_1 < 0),
insolvent_idcr_s100 = as.integer(idcr_2 < 0),
# =======================================================================
# WINSORIZED VARIABLES (2.5% / 97.5%)
# =======================================================================
mtm_total_w = winsorize(mtm_total_raw),
mtm_btfp_w = winsorize(mtm_btfp_raw),
mtm_other_w = winsorize(mtm_other_raw),
uninsured_lev_w = winsorize(uninsured_lev_raw),
adjusted_equity_w = winsorize(adjusted_equity_raw),
ln_assets_w = winsorize(ln_assets_raw),
cash_ratio_w = winsorize(cash_ratio_raw),
book_equity_ratio_w = winsorize(book_equity_ratio_raw),
roa_w = winsorize(roa_raw),
loan_to_deposit_w = winsorize(loan_to_deposit_raw),
wholesale_w = winsorize(wholesale_raw),
uninsured_outflow_w = winsorize(uninsured_outflow_raw),
insured_outflow_w = winsorize(insured_outflow_raw),
total_outflow_w = winsorize(total_outflow_raw),
# =======================================================================
# Z-SCORE STANDARDIZED (for regression — interpret as 1-SD change)
# =======================================================================
# --- Primary: Adjusted Equity framework ---
adjusted_equity = standardize_z(adjusted_equity_w),
uninsured_lev = standardize_z(uninsured_lev_w),
ae_x_uninsured = adjusted_equity * uninsured_lev,
# --- Robustness: MTM framework ---
mtm_total = standardize_z(mtm_total_w),
mtm_btfp = standardize_z(mtm_btfp_w),
mtm_other = standardize_z(mtm_other_w),
mtm_x_uninsured = mtm_total * uninsured_lev,
mtm_omo_x_uninsured = mtm_btfp * uninsured_lev,
mtm_nonomo_x_uninsured = mtm_other * uninsured_lev,
# --- Controls ---
ln_assets = standardize_z(ln_assets_w),
cash_ratio = standardize_z(cash_ratio_w),
book_equity_ratio = standardize_z(book_equity_ratio_w),
roa = standardize_z(roa_w),
loan_to_deposit = standardize_z(loan_to_deposit_w),
wholesale = standardize_z(wholesale_w),
# --- Deposit outflows (standardized) ---
uninsured_outflow = standardize_z(uninsured_outflow_w),
insured_outflow = standardize_z(insured_outflow_w),
total_outflow = standardize_z(total_outflow_w),
# --- Par Benefit and Collateral Capacity ---
par_benefit_raw = safe_div(mtm_btfp_raw, mtm_btfp_raw + 100 * safe_div(
omo_eligible, total_asset * 1000, 0), NA_real_),
collateral_capacity_raw = safe_div(omo_eligible, total_asset * 1000, 0) * 100,
# Size category
size_cat = factor(create_size_category_3(total_asset), levels = size_levels_3),
# Clustering variables
state = if ("state" %in% names(.)) state else NA_character_,
fed_district = if ("fed_district" %in% names(.)) fed_district else NA_character_
) %>%
# --- Par Benefit: winsorize and standardize (needs prior columns) ---
mutate(
par_benefit_w = winsorize(par_benefit_raw),
par_benefit = standardize_z(par_benefit_w),
par_x_uninsured = par_benefit * uninsured_lev
)
}
# ==============================================================================
# QUARTILE DUMMIES AND RISK CATEGORIES
# ==============================================================================
add_run_risk_dummies <- function(data) {
# Median splits for 2x2 risk categories
medians <- data %>%
summarise(median_mtm = median(mtm_total_w, na.rm = TRUE),
median_uninsured = median(uninsured_lev_w, na.rm = TRUE))
# Quartile breakpoints
quartiles <- data %>%
summarise(
adj_eq_q1 = quantile(adjusted_equity_w, 0.25, na.rm = TRUE),
adj_eq_q2 = quantile(adjusted_equity_w, 0.50, na.rm = TRUE),
adj_eq_q3 = quantile(adjusted_equity_w, 0.75, na.rm = TRUE),
unins_lev_q1 = quantile(uninsured_lev_w, 0.25, na.rm = TRUE),
unins_lev_q2 = quantile(uninsured_lev_w, 0.50, na.rm = TRUE),
unins_lev_q3 = quantile(uninsured_lev_w, 0.75, na.rm = TRUE),
mtm_q1 = quantile(mtm_total_w, 0.25, na.rm = TRUE),
mtm_q2 = quantile(mtm_total_w, 0.50, na.rm = TRUE),
mtm_q3 = quantile(mtm_total_w, 0.75, na.rm = TRUE)
)
data %>%
mutate(
# --- 2x2 Median-split Risk Dummies (MTM-based, reference = Risk 1) ---
run_risk_1 = replace_na(as.integer(mtm_total_w < medians$median_mtm & uninsured_lev_w < medians$median_uninsured), 0L),
run_risk_2 = replace_na(as.integer(mtm_total_w < medians$median_mtm & uninsured_lev_w >= medians$median_uninsured), 0L),
run_risk_3 = replace_na(as.integer(mtm_total_w >= medians$median_mtm & uninsured_lev_w < medians$median_uninsured), 0L),
run_risk_4 = replace_na(as.integer(mtm_total_w >= medians$median_mtm & uninsured_lev_w >= medians$median_uninsured), 0L),
# --- Adjusted Equity Quartiles (ref = Q4 = strongest solvency) ---
adj_equity_q1 = replace_na(as.integer(adjusted_equity_w <= quartiles$adj_eq_q1), 0L),
adj_equity_q2 = replace_na(as.integer(adjusted_equity_w > quartiles$adj_eq_q1 & adjusted_equity_w <= quartiles$adj_eq_q2), 0L),
adj_equity_q3 = replace_na(as.integer(adjusted_equity_w > quartiles$adj_eq_q2 & adjusted_equity_w <= quartiles$adj_eq_q3), 0L),
# --- Uninsured Leverage Quartiles (ref = Q1 = lowest fragility) ---
unins_lev_q2 = replace_na(as.integer(uninsured_lev_w > quartiles$unins_lev_q1 & uninsured_lev_w <= quartiles$unins_lev_q2), 0L),
unins_lev_q3 = replace_na(as.integer(uninsured_lev_w > quartiles$unins_lev_q2 & uninsured_lev_w <= quartiles$unins_lev_q3), 0L),
unins_lev_q4 = replace_na(as.integer(uninsured_lev_w > quartiles$unins_lev_q3), 0L),
# Uninsured Leverage tercile (for threshold gradient figure)
unins_lev_tercile = cut(uninsured_lev_w,
breaks = quantile(uninsured_lev_w, c(0, 1/3, 2/3, 1), na.rm = TRUE),
labels = c("Low", "Med", "High"), include.lowest = TRUE),
# --- MTM Quartiles (ref = Q1 = lowest loss) ---
mtm_q2 = replace_na(as.integer(mtm_total_w > quartiles$mtm_q1 & mtm_total_w <= quartiles$mtm_q2), 0L),
mtm_q3 = replace_na(as.integer(mtm_total_w > quartiles$mtm_q2 & mtm_total_w <= quartiles$mtm_q3), 0L),
mtm_q4 = replace_na(as.integer(mtm_total_w > quartiles$mtm_q3), 0L),
# =======================================================================
# 16-CELL AE x UNINSURED LEVERAGE (ref = aeq4_ulq1: safest cell)
# =======================================================================
aeq1_ulq1 = replace_na(as.integer(adjusted_equity_w <= quartiles$adj_eq_q1 & uninsured_lev_w <= quartiles$unins_lev_q1), 0L),
aeq1_ulq2 = replace_na(as.integer(adjusted_equity_w <= quartiles$adj_eq_q1 & uninsured_lev_w > quartiles$unins_lev_q1 & uninsured_lev_w <= quartiles$unins_lev_q2), 0L),
aeq1_ulq3 = replace_na(as.integer(adjusted_equity_w <= quartiles$adj_eq_q1 & uninsured_lev_w > quartiles$unins_lev_q2 & uninsured_lev_w <= quartiles$unins_lev_q3), 0L),
aeq1_ulq4 = replace_na(as.integer(adjusted_equity_w <= quartiles$adj_eq_q1 & uninsured_lev_w > quartiles$unins_lev_q3), 0L),
aeq2_ulq1 = replace_na(as.integer(adjusted_equity_w > quartiles$adj_eq_q1 & adjusted_equity_w <= quartiles$adj_eq_q2 & uninsured_lev_w <= quartiles$unins_lev_q1), 0L),
aeq2_ulq2 = replace_na(as.integer(adjusted_equity_w > quartiles$adj_eq_q1 & adjusted_equity_w <= quartiles$adj_eq_q2 & uninsured_lev_w > quartiles$unins_lev_q1 & uninsured_lev_w <= quartiles$unins_lev_q2), 0L),
aeq2_ulq3 = replace_na(as.integer(adjusted_equity_w > quartiles$adj_eq_q1 & adjusted_equity_w <= quartiles$adj_eq_q2 & uninsured_lev_w > quartiles$unins_lev_q2 & uninsured_lev_w <= quartiles$unins_lev_q3), 0L),
aeq2_ulq4 = replace_na(as.integer(adjusted_equity_w > quartiles$adj_eq_q1 & adjusted_equity_w <= quartiles$adj_eq_q2 & uninsured_lev_w > quartiles$unins_lev_q3), 0L),
aeq3_ulq1 = replace_na(as.integer(adjusted_equity_w > quartiles$adj_eq_q2 & adjusted_equity_w <= quartiles$adj_eq_q3 & uninsured_lev_w <= quartiles$unins_lev_q1), 0L),
aeq3_ulq2 = replace_na(as.integer(adjusted_equity_w > quartiles$adj_eq_q2 & adjusted_equity_w <= quartiles$adj_eq_q3 & uninsured_lev_w > quartiles$unins_lev_q1 & uninsured_lev_w <= quartiles$unins_lev_q2), 0L),
aeq3_ulq3 = replace_na(as.integer(adjusted_equity_w > quartiles$adj_eq_q2 & adjusted_equity_w <= quartiles$adj_eq_q3 & uninsured_lev_w > quartiles$unins_lev_q2 & uninsured_lev_w <= quartiles$unins_lev_q3), 0L),
aeq3_ulq4 = replace_na(as.integer(adjusted_equity_w > quartiles$adj_eq_q2 & adjusted_equity_w <= quartiles$adj_eq_q3 & uninsured_lev_w > quartiles$unins_lev_q3), 0L),
aeq4_ulq1 = replace_na(as.integer(adjusted_equity_w > quartiles$adj_eq_q3 & uninsured_lev_w <= quartiles$unins_lev_q1), 0L), # REFERENCE
aeq4_ulq2 = replace_na(as.integer(adjusted_equity_w > quartiles$adj_eq_q3 & uninsured_lev_w > quartiles$unins_lev_q1 & uninsured_lev_w <= quartiles$unins_lev_q2), 0L),
aeq4_ulq3 = replace_na(as.integer(adjusted_equity_w > quartiles$adj_eq_q3 & uninsured_lev_w > quartiles$unins_lev_q2 & uninsured_lev_w <= quartiles$unins_lev_q3), 0L),
aeq4_ulq4 = replace_na(as.integer(adjusted_equity_w > quartiles$adj_eq_q3 & uninsured_lev_w > quartiles$unins_lev_q3), 0L),
# =======================================================================
# 16-CELL MTM x UNINSURED LEVERAGE (ref = mtmq1_ulq1)
# =======================================================================
mtmq1_ulq1 = replace_na(as.integer(mtm_total_w <= quartiles$mtm_q1 & uninsured_lev_w <= quartiles$unins_lev_q1), 0L), # REFERENCE
mtmq1_ulq2 = replace_na(as.integer(mtm_total_w <= quartiles$mtm_q1 & uninsured_lev_w > quartiles$unins_lev_q1 & uninsured_lev_w <= quartiles$unins_lev_q2), 0L),
mtmq1_ulq3 = replace_na(as.integer(mtm_total_w <= quartiles$mtm_q1 & uninsured_lev_w > quartiles$unins_lev_q2 & uninsured_lev_w <= quartiles$unins_lev_q3), 0L),
mtmq1_ulq4 = replace_na(as.integer(mtm_total_w <= quartiles$mtm_q1 & uninsured_lev_w > quartiles$unins_lev_q3), 0L),
mtmq2_ulq1 = replace_na(as.integer(mtm_total_w > quartiles$mtm_q1 & mtm_total_w <= quartiles$mtm_q2 & uninsured_lev_w <= quartiles$unins_lev_q1), 0L),
mtmq2_ulq2 = replace_na(as.integer(mtm_total_w > quartiles$mtm_q1 & mtm_total_w <= quartiles$mtm_q2 & uninsured_lev_w > quartiles$unins_lev_q1 & uninsured_lev_w <= quartiles$unins_lev_q2), 0L),
mtmq2_ulq3 = replace_na(as.integer(mtm_total_w > quartiles$mtm_q1 & mtm_total_w <= quartiles$mtm_q2 & uninsured_lev_w > quartiles$unins_lev_q2 & uninsured_lev_w <= quartiles$unins_lev_q3), 0L),
mtmq2_ulq4 = replace_na(as.integer(mtm_total_w > quartiles$mtm_q1 & mtm_total_w <= quartiles$mtm_q2 & uninsured_lev_w > quartiles$unins_lev_q3), 0L),
mtmq3_ulq1 = replace_na(as.integer(mtm_total_w > quartiles$mtm_q2 & mtm_total_w <= quartiles$mtm_q3 & uninsured_lev_w <= quartiles$unins_lev_q1), 0L),
mtmq3_ulq2 = replace_na(as.integer(mtm_total_w > quartiles$mtm_q2 & mtm_total_w <= quartiles$mtm_q3 & uninsured_lev_w > quartiles$unins_lev_q1 & uninsured_lev_w <= quartiles$unins_lev_q2), 0L),
mtmq3_ulq3 = replace_na(as.integer(mtm_total_w > quartiles$mtm_q2 & mtm_total_w <= quartiles$mtm_q3 & uninsured_lev_w > quartiles$unins_lev_q2 & uninsured_lev_w <= quartiles$unins_lev_q3), 0L),
mtmq3_ulq4 = replace_na(as.integer(mtm_total_w > quartiles$mtm_q2 & mtm_total_w <= quartiles$mtm_q3 & uninsured_lev_w > quartiles$unins_lev_q3), 0L),
mtmq4_ulq1 = replace_na(as.integer(mtm_total_w > quartiles$mtm_q3 & uninsured_lev_w <= quartiles$unins_lev_q1), 0L),
mtmq4_ulq2 = replace_na(as.integer(mtm_total_w > quartiles$mtm_q3 & uninsured_lev_w > quartiles$unins_lev_q1 & uninsured_lev_w <= quartiles$unins_lev_q2), 0L),
mtmq4_ulq3 = replace_na(as.integer(mtm_total_w > quartiles$mtm_q3 & uninsured_lev_w > quartiles$unins_lev_q2 & uninsured_lev_w <= quartiles$unins_lev_q3), 0L),
mtmq4_ulq4 = replace_na(as.integer(mtm_total_w > quartiles$mtm_q3 & uninsured_lev_w > quartiles$unins_lev_q3), 0L),
# Store cutoff values for reporting
median_mtm_used = medians$median_mtm,
median_uninsured_used = medians$median_uninsured
)
}
# ==============================================================================
# BASELINE DATASETS (one per Call Report quarter)
# ==============================================================================
df_2022q4 <- call_q %>%
filter(period == BASELINE_MAIN, !idrssd %in% excluded_banks,
!is.na(omo_eligible) & omo_eligible > 0) %>%
construct_analysis_vars() %>% add_run_risk_dummies()
df_2023q3 <- call_q %>%
filter(period == BASELINE_ARB, !idrssd %in% excluded_banks,
!is.na(omo_eligible) & omo_eligible > 0) %>%
construct_analysis_vars() %>% add_run_risk_dummies()
df_2023q4 <- call_q %>%
filter(period == BASELINE_WIND, !idrssd %in% excluded_banks,
!is.na(omo_eligible) & omo_eligible > 0) %>%
construct_analysis_vars() %>% add_run_risk_dummies()
cat("=== BASELINE DATASETS ===\n")
## === BASELINE DATASETS ===
cat("2022Q4:", nrow(df_2022q4), "| 2023Q3:", nrow(df_2023q3), "| 2023Q4:", nrow(df_2023q4), "\n")
## 2022Q4: 4292 | 2023Q3: 4214 | 2023Q4: 4197
cat("\n=== INSOLVENCY COUNTS (2022Q4) ===\n")
##
## === INSOLVENCY COUNTS (2022Q4) ===
cat("MTM Insolvent (AE < 0):", sum(df_2022q4$mtm_insolvent, na.rm = TRUE), "\n")
## MTM Insolvent (AE < 0): 825
cat("IDCR 50% Insolvent:", sum(df_2022q4$insolvent_idcr_s50, na.rm = TRUE), "\n")
## IDCR 50% Insolvent: 16
cat("IDCR 100% Insolvent:", sum(df_2022q4$insolvent_idcr_s100, na.rm = TRUE), "\n")
## IDCR 100% Insolvent: 364
# ==============================================================================
# JOIN BORROWER INDICATORS TO BASELINES
# ==============================================================================
join_all_borrowers <- function(df_base, btfp_df, dw_df, btfp_var, dw_var) {
df_base %>%
left_join(btfp_df %>% select(idrssd, starts_with(btfp_var)), by = "idrssd") %>%
left_join(dw_df %>% select(idrssd, starts_with(dw_var)), by = "idrssd") %>%
mutate(
"{btfp_var}" := replace_na(!!sym(btfp_var), 0L),
"{dw_var}" := replace_na(!!sym(dw_var), 0L),
fhlb_user = as.integer(abnormal_fhlb_borrowing_10pct == 1),
user_group = factor(case_when(
!!sym(btfp_var) == 1 & !!sym(dw_var) == 1 ~ "Both",
!!sym(btfp_var) == 1 ~ "BTFP_Only",
!!sym(dw_var) == 1 ~ "DW_Only",
TRUE ~ "Neither"
), levels = c("Neither", "BTFP_Only", "DW_Only", "Both")),
any_fed = as.integer(!!sym(btfp_var) == 1 | !!sym(dw_var) == 1),
non_user = as.integer(!!sym(btfp_var) == 0 & !!sym(dw_var) == 0 & fhlb_user == 0)
)
}
# --- Acute Period ---
df_acute <- join_all_borrowers(df_2022q4, btfp_acute, dw_acute, "btfp_acute", "dw_acute") %>%
mutate(
btfp_pct = ifelse(btfp_acute == 1 & btfp_acute_amt > 0,
100 * btfp_acute_amt / (total_asset * 1000), NA_real_),
dw_pct = ifelse(dw_acute == 1 & dw_acute_amt > 0,
100 * dw_acute_amt / (total_asset * 1000), NA_real_),
log_btfp_amt = ifelse(btfp_acute == 1 & btfp_acute_amt > 0, log(btfp_acute_amt), NA_real_),
log_dw_amt = ifelse(dw_acute == 1 & dw_acute_amt > 0, log(dw_acute_amt), NA_real_)
)
# --- Pre-BTFP (Mar 1 - Mar 12) ---
btfp_prebtfp <- create_borrower_indicator(btfp_loans, "btfp_loan_date", "rssd_id", "btfp_loan_amount",
DATE_MAR01, DATE_MAR12, "btfp_prebtfp")
## Warning: There was 1 warning in `summarise()`.
## ℹ In argument: `btfp_prebtfp_first = min(btfp_loan_date)`.
## Caused by warning in `min.default()`:
## ! no non-missing arguments to min; returning Inf
df_prebtfp <- join_all_borrowers(df_2022q4, btfp_prebtfp, dw_prebtfp, "btfp_prebtfp", "dw_prebtfp")
# --- Mar 10 only ---
df_mar10 <- join_all_borrowers(df_2022q4, btfp_mar10, dw_mar10, "btfp_mar10", "dw_mar10")
# --- Mar 10-13 ---
df_mar10_13 <- join_all_borrowers(df_2022q4, btfp_mar10_13, dw_mar10_13, "btfp_mar10_13", "dw_mar10_13")
# --- Post-Acute Period ---
df_post <- df_2022q4 %>%
left_join(btfp_post %>% select(idrssd, btfp_post, btfp_post_amt), by = "idrssd") %>%
left_join(dw_post %>% select(idrssd, dw_post, dw_post_amt), by = "idrssd") %>%
mutate(btfp_post = replace_na(btfp_post, 0L), dw_post = replace_na(dw_post, 0L),
fhlb_user = as.integer(abnormal_fhlb_borrowing_10pct == 1),
any_fed = as.integer(btfp_post == 1 | dw_post == 1),
non_user = as.integer(btfp_post == 0 & dw_post == 0 & fhlb_user == 0),
user_group = factor(case_when(
btfp_post == 1 & dw_post == 1 ~ "Both", btfp_post == 1 ~ "BTFP_Only",
dw_post == 1 ~ "DW_Only", TRUE ~ "Neither"),
levels = c("Neither", "BTFP_Only", "DW_Only", "Both")))
# --- Arbitrage Period (2023Q3 baseline) ---
df_arb <- df_2023q3 %>%
left_join(btfp_arb %>% select(idrssd, btfp_arb, btfp_arb_amt), by = "idrssd") %>%
mutate(btfp_arb = replace_na(btfp_arb, 0L),
fhlb_user = as.integer(abnormal_fhlb_borrowing_10pct == 1),
any_fed = btfp_arb,
non_user = as.integer(btfp_arb == 0 & fhlb_user == 0),
user_group = factor(ifelse(btfp_arb == 1, "BTFP_Only", "Neither"),
levels = c("Neither", "BTFP_Only", "DW_Only", "Both")))
# --- Wind-down Period (2023Q4 baseline) ---
df_wind <- df_2023q4 %>%
left_join(btfp_wind %>% select(idrssd, btfp_wind, btfp_wind_amt), by = "idrssd") %>%
mutate(btfp_wind = replace_na(btfp_wind, 0L),
fhlb_user = as.integer(abnormal_fhlb_borrowing_10pct == 1),
any_fed = btfp_wind,
non_user = as.integer(btfp_wind == 0 & fhlb_user == 0),
user_group = factor(ifelse(btfp_wind == 1, "BTFP_Only", "Neither"),
levels = c("Neither", "BTFP_Only", "DW_Only", "Both")))
cat("\n=== USER GROUP COUNTS (ACUTE) ===\n")
##
## === USER GROUP COUNTS (ACUTE) ===
print(table(df_acute$user_group))
##
## Neither BTFP_Only DW_Only Both
## 3531 368 299 94
cat("Pure Non-Users:", sum(df_acute$non_user), "\n")
## Pure Non-Users: 3285
# ==============================================================================
# MODEL INFRASTRUCTURE
# CONTROLS_MTM — used with raw MTM (includes book_equity_ratio)
# ==============================================================================
# ==============================================================================
# MODEL INFRASTRUCTURE
# ==============================================================================
CONTROLS_MTM <- "ln_assets + cash_ratio + loan_to_deposit + book_equity_ratio + wholesale + roa"
# ==============================================================================
# FIXEST DICTIONARY (labels for etable output)
# ==============================================================================
setFixest_dict(c(
# Primary: Explanatory
mtm_total = "MTM Loss (z)",
uninsured_lev = "Uninsured Leverage (z)",
# (Duplicate mtm_total removed)
mtm_btfp = "MTM Loss OMO (z)",
mtm_other = "MTM Loss Non-OMO (z)",
mtm_x_uninsured = "MTM $\\times$ Uninsured",
mtm_omo_x_uninsured = "MTM OMO $\\times$ Uninsured",
mtm_nonomo_x_uninsured = "MTM Non-OMO $\\times$ Uninsured",
# Par Benefit
par_benefit = "Par Benefit (z)",
par_x_uninsured = "Par Benefit $\\times$ Uninsured",
# AE quartile dummies
adj_equity_q1 = "Adj. Equity Q1 (Lowest)", adj_equity_q2 = "Adj. Equity Q2", adj_equity_q3 = "Adj. Equity Q3",
# UL quartile dummies
unins_lev_q2 = "Unins. Lev. Q2", unins_lev_q3 = "Unins. Lev. Q3", unins_lev_q4 = "Unins. Lev. Q4 (Highest)",
# MTM quartile dummies
mtm_q2 = "MTM Q2", mtm_q3 = "MTM Q3", mtm_q4 = "MTM Q4 (Highest Loss)",
# Controls
ln_assets = "Log(Assets)", cash_ratio = "Cash Ratio", loan_to_deposit = "Loan-to-Deposit",
book_equity_ratio = "Book Equity Ratio", wholesale = "Wholesale Funding", roa = "ROA"
))
# ==============================================================================
# EXPLANATORY VARIABLE STRINGS
# ==============================================================================
# --- Primary: Adjusted Equity ---
EXPL_MTM_BASE <- "mtm_total + uninsured_lev"
EXPL_MTM_INTER <- "mtm_total + uninsured_lev + mtm_x_uninsured"
# --- Mechanism: OMO Decomposition ---
EXPL_OMO_BASE <- "mtm_btfp + uninsured_lev"
EXPL_OMO_INTER <- "mtm_btfp + uninsured_lev + mtm_omo_x_uninsured"
EXPL_OMO_DECOMP <- "mtm_btfp + mtm_other + uninsured_lev"
EXPL_OMO_HORSE <- "mtm_btfp + mtm_other + uninsured_lev + mtm_omo_x_uninsured + mtm_nonomo_x_uninsured"
# --- Par Benefit ---
EXPL_PAR_BASE <- "par_benefit + uninsured_lev"
EXPL_PAR_INTER <- "par_benefit + uninsured_lev + par_x_uninsured"
# ==============================================================================
# COEFFICIENT DISPLAY ORDER
# ==============================================================================
COEF_ORDER <- c(
"Constant",
"MTM Loss",
"Uninsured Leverage",
"MTM.*Uninsured",
"MTM Loss OMO", "MTM Loss Non-OMO","Par Benefit",
"MTM OMO.*Uninsured", "MTM Non-OMO.*Uninsured", "Par Benefit.*Uninsured"
)
# ==============================================================================
# GENERIC MODEL RUNNER
# ==============================================================================
run_one <- function(data, dv, explanatory, family_type = "lpm", controls = CONTROLS_MTM) {
ff <- as.formula(paste(dv, "~", explanatory, "+", controls))
if (family_type == "lpm") {
feols(ff, data = data, vcov = "hetero")
} else {
feglm(ff, data = data, family = binomial("logit"), vcov = "hetero")
}
}
# ==============================================================================
# BATCH MODEL RUNNER: Base + Interaction (Refactored)
# ==============================================================================
run_base_inter <- function(data, dv, framework = "mtm", family_type = "lpm") {
# 1. Define the mapping of frameworks to their formula strings
framework_map <- list(
mtm = list(base = EXPL_MTM_BASE, inter = EXPL_MTM_INTER),
omo = list(base = EXPL_OMO_BASE, inter = EXPL_OMO_INTER),
par = list(base = EXPL_PAR_BASE, inter = EXPL_PAR_INTER)
)
# 2. Check to ensure valid framework input
if (!framework %in% names(framework_map)) {
stop("Error: Framework '", framework, "' not found. Options: ",
paste(names(framework_map), collapse = ", "))
}
# 3. Retrieve the formulas
forms <- framework_map[[framework]]
# 4. Run the models
list(
base = run_one(data, dv, forms$base, family_type, CONTROLS_MTM),
inter = run_one(data, dv, forms$inter, family_type, CONTROLS_MTM)
)
}
# ==============================================================================
# ETABLE SAVER (produces .tex files)
# ==============================================================================
save_etable <- function(models, filename, title_text, notes_text,
fitstat_use = ~ n + r2, extra_lines = NULL) {
# Export to LaTeX (Force SE below coefficient)
etable(models,
title = title_text,
notes = notes_text,
fitstat = fitstat_use,
order = COEF_ORDER,
extralines = extra_lines,
se.below = TRUE, # <--- Enforces SE below coefficient
tex = TRUE,
file = file.path(TABLE_PATH, paste0(filename, ".tex")),
replace = TRUE,
style.tex = style.tex("aer")
)
message("Saved: ", filename, ".tex")
}
# ==============================================================================
# VISUALIZATION THEME
# ==============================================================================
theme_paper <- theme_minimal(base_size = 12) +
theme(
plot.title = element_text(face = "bold", size = 13, hjust = 0),
plot.subtitle = element_text(size = 10, color = "grey40", hjust = 0),
legend.position = "bottom",
panel.grid.minor = element_blank(),
strip.text = element_text(face = "bold", size = 11)
)
pal_user <- c("DW" = "#D62828", "BTFP" = "#003049", "FHLB" = "#F77F00",
"Pure Non-Borrower" = "grey70", "Both" = "#7209B7")
# ==============================================================================
# PURE COMPARISON SAMPLES: facility borrowers vs pure non-borrowers
# ==============================================================================
# --- Acute Period ---
df_btfp_s <- df_acute %>% filter(btfp_acute == 1 | non_user == 1)
df_dw_s <- df_acute %>% filter(dw_acute == 1 | non_user == 1)
df_fhlb_s <- df_acute %>% filter(fhlb_user == 1 | non_user == 1)
# --- Solvency subsamples (Acute) ---
df_btfp_sol <- df_btfp_s %>% filter(mtm_solvent == 1)
df_btfp_ins <- df_btfp_s %>% filter(mtm_insolvent == 1)
df_dw_sol <- df_dw_s %>% filter(mtm_solvent == 1)
df_dw_ins <- df_dw_s %>% filter(mtm_insolvent == 1)
df_fhlb_sol <- df_fhlb_s %>% filter(mtm_solvent == 1)
df_fhlb_ins <- df_fhlb_s %>% filter(mtm_insolvent == 1)
# --- Pre-BTFP DW ---
df_dw_pre_s <- df_prebtfp %>% filter(dw_prebtfp == 1 | non_user == 1)
df_dw_mar10_s <- df_mar10 %>% filter(dw_mar10 == 1 | non_user == 1)
df_dw_mar10_13_s <- df_mar10_13 %>% filter(dw_mar10_13 == 1 | non_user == 1)
# --- Post-Acute ---
df_btfp_post_s <- df_post %>% filter(btfp_post == 1 | non_user == 1)
df_dw_post_s <- df_post %>% filter(dw_post == 1 | non_user == 1)
df_fhlb_post_s <- df_post %>% filter(fhlb_user == 1 | non_user == 1)
# --- Arbitrage ---
df_btfp_arb_s <- df_arb %>% filter(btfp_arb == 1 | non_user == 1)
df_fhlb_arb_s <- df_arb %>% filter(fhlb_user == 1 | non_user == 1)
# --- Wind-down ---
df_btfp_wind_s <- df_wind %>% filter(btfp_wind == 1 | non_user == 1)
df_fhlb_wind_s <- df_wind %>% filter(fhlb_user == 1 | non_user == 1)
cat("=== REGRESSION SAMPLES (Acute) ===\n")
## === REGRESSION SAMPLES (Acute) ===
cat("BTFP vs Non:", nrow(df_btfp_s), "(BTFP =", sum(df_btfp_s$btfp_acute), ")\n")
## BTFP vs Non: 3747 (BTFP = 462 )
cat("DW vs Non:", nrow(df_dw_s), "(DW =", sum(df_dw_s$dw_acute), ")\n")
## DW vs Non: 3678 (DW = 393 )
cat("FHLB vs Non:", nrow(df_fhlb_s), "(FHLB =", sum(df_fhlb_s$fhlb_user), ")\n")
## FHLB vs Non: 3587 (FHLB = 302 )
cat("Including Non User: BTFP Solvent:", nrow(df_btfp_sol), "| BTFP Insolvent:", nrow(df_btfp_ins), "\n")
## Including Non User: BTFP Solvent: 3003 | BTFP Insolvent: 734
Primary specification. Columns 1–2: All banks. Columns 3–4: Solvent banks (AdjEquity ≥ 0). Columns 5–6: Insolvent banks (AdjEquity < 0). Uses Adjusted Equity framework (LPM).
\[ \Pr(\text{BTFP}_i = 1) = \alpha + \beta_1 \cdot \text{MTM Loss}_i + \beta_2 \cdot \text{UninsLev}_i + \beta_3 (\text{MTM Loss}_i \times \text{UninsLev}_i) + \gamma \mathbf{X}_i + \varepsilon_i \]
Predictions: \(\beta_1 < 0\) (weaker solvency → borrow), \(\beta_2 > 0\) (fragile → borrow), \(\beta_3 < 0\) (interaction: solvency effect stronger among fragile banks).
# ==============================================================================
# TABLE 1: EXTENSIVE MARGIN — BTFP (ACUTE)
# 6 columns: All (base, inter), Solvent (base, inter), Insolvent (base, inter)
# ==============================================================================
models_t1 <- list(
"(1) All" = run_one(df_btfp_s, "btfp_acute", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(2) All" = run_one(df_btfp_s, "btfp_acute", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(3) Solvent" = run_one(df_btfp_sol, "btfp_acute", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(4) Solvent" = run_one(df_btfp_sol, "btfp_acute", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(5) Insolvent" = run_one(df_btfp_ins, "btfp_acute", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(6) Insolvent" = run_one(df_btfp_ins, "btfp_acute", EXPL_MTM_INTER, controls = CONTROLS_MTM)
)
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
t1_extra <- list(
"__N(BTFP=1)" = c(sum(df_btfp_s$btfp_acute), sum(df_btfp_s$btfp_acute),
sum(df_btfp_sol$btfp_acute), sum(df_btfp_sol$btfp_acute),
sum(df_btfp_ins$btfp_acute), sum(df_btfp_ins$btfp_acute)),
"__Subsample" = c("All", "All", "Solvent", "Solvent", "Insolvent", "Insolvent"),
"__Framework" = rep("Adjusted Equity", 6)
)
# --- DISPLAY (HTML) ---
etable(models_t1,
fitstat = ~ n + r2,
order = COEF_ORDER,
extralines = t1_extra,
se.below = TRUE, # <--- Puts standard errors below coefficients
digits = 4, # <--- Increases decimal precision
signif.code = c("***"=0.01, "**"=0.05, "*"=0.10),
title = "Table 1: BTFP Extensive Margin — Acute Period (LPM)")
## (1) All (2) All (3) Solv..
## Dependent Var.: btfp_acute btfp_acute btfp_acute
##
## Constant 0.1293*** 0.1316*** 0.1263***
## (0.0054) (0.0055) (0.0064)
## MTM Loss (z) 0.0198*** 0.0243*** 0.0193***
## (0.0055) (0.0059) (0.0062)
## Uninsured Leverage (z) 0.0154** 0.0199*** 0.0162**
## (0.0063) (0.0068) (0.0068)
## MTM $\times$ Uninsured 0.0181***
## (0.0051)
## Log(Assets) 0.0676*** 0.0674*** 0.0621***
## (0.0074) (0.0074) (0.0080)
## Cash Ratio -0.0255*** -0.0238*** -0.0236***
## (0.0047) (0.0046) (0.0047)
## Loan-to-Deposit -0.0117** -0.0083 -0.0111*
## (0.0055) (0.0055) (0.0058)
## Book Equity Ratio -0.0123*** -0.0109** -0.0088**
## (0.0044) (0.0044) (0.0044)
## Wholesale Funding 0.0258*** 0.0252*** 0.0264***
## (0.0064) (0.0064) (0.0070)
## ROA -0.0028 -0.0048 0.0011
## (0.0047) (0.0047) (0.0049)
## ______________________ __________ __________ __________
## S.E. type Hete.-rob. Hete.-rob. Hete.-rob.
## Observations 3,737 3,737 3,003
## R2 0.08704 0.09017 0.08692
## N(BTFP=1) 462 462 330
## Subsample All All Solvent
## Framework Adjusted Equity Adjusted Equity Adjusted Equity
##
## (4) Solv.. (5) Inso.. (6) Inso..
## Dependent Var.: btfp_acute btfp_acute btfp_acute
##
## Constant 0.1303*** 0.0017 0.0168
## (0.0068) (0.0544) (0.0541)
## MTM Loss (z) 0.0262*** 0.0314 0.0289
## (0.0070) (0.0260) (0.0259)
## Uninsured Leverage (z) 0.0255*** 0.0233 -0.0213
## (0.0080) (0.0177) (0.0262)
## MTM $\times$ Uninsured 0.0185*** 0.0443*
## (0.0059) (0.0226)
## Log(Assets) 0.0610*** 0.1016*** 0.0995***
## (0.0080) (0.0207) (0.0208)
## Cash Ratio -0.0221*** -0.0599*** -0.0588***
## (0.0047) (0.0203) (0.0202)
## Loan-to-Deposit -0.0091 0.0143 0.0176
## (0.0057) (0.0196) (0.0197)
## Book Equity Ratio -0.0074* -0.1332*** -0.1269***
## (0.0045) (0.0404) (0.0403)
## Wholesale Funding 0.0257*** 0.0185 0.0170
## (0.0070) (0.0144) (0.0143)
## ROA -0.0005 -0.0341** -0.0344**
## (0.0049) (0.0146) (0.0145)
## ______________________ __________ __________ __________
## S.E. type Hete.-rob. Hete.-rob. Hete.-rob.
## Observations 3,003 734 734
## R2 0.08991 0.08892 0.09341
## N(BTFP=1) 330 132 132
## Subsample Solvent Insolvent Insolvent
## Framework Adjusted Equity Adjusted Equity Adjusted Equity
## ---
## Signif. codes: 0 '***' 0.01 '**' 0.05 '*' 0.1 ' ' 1
# --- SAVE (LaTeX) ---
save_etable(models_t1, "Table_1_BTFP_Extensive",
title_text = "BTFP Extensive Margin: (Acute Period)",
notes_text = "LPM with robust SEs. Sample: BTFP borrowers vs. pure non-borrowers. Solvent: AdjEquity $\\geq$ 0. Insolvent: AdjEquity $<$ 0. All regressors z-standardized. Controls: log(assets), cash ratio, loan-to-deposit, wholesale, ROA, Book equity . Baseline: 2022Q4.",
extra_lines = t1_extra)
## Saved: Table_1_BTFP_Extensive.tex
# ==============================================================================
# TABLE 1-DW: EXTENSIVE MARGIN — DISCOUNT WINDOW (ACUTE)
# ==============================================================================
models_t1_dw <- list(
"(1) All" = run_one(df_dw_s, "dw_acute", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(2) All" = run_one(df_dw_s, "dw_acute", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(3) Solvent" = run_one(df_dw_sol, "dw_acute", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(4) Solvent" = run_one(df_dw_sol, "dw_acute", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(5) Insolvent" = run_one(df_dw_ins, "dw_acute", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(6) Insolvent" = run_one(df_dw_ins, "dw_acute", EXPL_MTM_INTER, controls = CONTROLS_MTM)
)
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
t1_dw_extra <- list(
"__N(DW=1)" = c(sum(df_dw_s$dw_acute), sum(df_dw_s$dw_acute),
sum(df_dw_sol$dw_acute), sum(df_dw_sol$dw_acute),
sum(df_dw_ins$dw_acute), sum(df_dw_ins$dw_acute)),
"__Subsample" = c("All", "All", "Solvent", "Solvent", "Insolvent", "Insolvent")
)
etable(models_t1_dw, fitstat = ~ n + r2,
order = COEF_ORDER, extralines = t1_dw_extra,
title = "Table 1-DW: DW Extensive Margin — Acute Period (LPM)")
## (1) All (2) All (3) Solvent
## Dependent Var.: dw_acute dw_acute dw_acute
##
## Constant 0.1130*** (0.0051) 0.1151*** (0.0052) 0.1169*** (0.0062)
## MTM Loss (z) 0.0122* (0.0054) 0.0161** (0.0059) 0.0177** (0.0063)
## Uninsured Leverage (z) 0.0089 (0.0060) 0.0131* (0.0064) 0.0074 (0.0066)
## MTM $\times$ Uninsured 0.0160** (0.0049)
## Log(Assets) 0.0843*** (0.0074) 0.0842*** (0.0074) 0.0827*** (0.0080)
## Cash Ratio -0.0040 (0.0051) -0.0025 (0.0051) -0.0020 (0.0053)
## Loan-to-Deposit -0.0041 (0.0054) -0.0014 (0.0055) -0.0054 (0.0058)
## Book Equity Ratio -0.0013 (0.0042) -3.36e-5 (0.0043) -0.0034 (0.0046)
## Wholesale Funding 0.0211*** (0.0062) 0.0205*** (0.0062) 0.0213** (0.0069)
## ROA -0.0004 (0.0046) -0.0022 (0.0046) 0.0030 (0.0049)
## ______________________ __________________ __________________ __________________
## S.E. type Heteroskedas.-rob. Heteroskedas.-rob. Heteroskedas.-rob.
## Observations 3,668 3,668 2,989
## R2 0.09279 0.09557 0.09698
## N(DW=1) 393 393 316
## Subsample All All Solvent
##
## (4) Solvent (5) Insolvent (6) Insolvent
## Dependent Var.: dw_acute dw_acute dw_acute
##
## Constant 0.1205*** (0.0065) 0.0243 (0.0479) 0.0360 (0.0476)
## MTM Loss (z) 0.0236** (0.0073) 0.0166 (0.0230) 0.0152 (0.0228)
## Uninsured Leverage (z) 0.0160* (0.0078) 0.0239 (0.0164) -0.0148 (0.0237)
## MTM $\times$ Uninsured 0.0169** (0.0059) 0.0392. (0.0200)
## Log(Assets) 0.0818*** (0.0080) 0.0945*** (0.0196) 0.0927*** (0.0198)
## Cash Ratio -0.0007 (0.0053) -0.0303. (0.0178) -0.0286 (0.0177)
## Loan-to-Deposit -0.0036 (0.0058) 0.0094 (0.0172) 0.0117 (0.0171)
## Book Equity Ratio -0.0020 (0.0046) -0.0710* (0.0340) -0.0661. (0.0340)
## Wholesale Funding 0.0206** (0.0069) 0.0181 (0.0136) 0.0173 (0.0138)
## ROA 0.0014 (0.0049) -0.0242. (0.0138) -0.0244. (0.0136)
## ______________________ __________________ __________________ __________________
## S.E. type Heteroskedas.-rob. Heteroskedas.-rob. Heteroskedas.-rob.
## Observations 2,989 679 679
## R2 0.09957 0.09145 0.09678
## N(DW=1) 316 77 77
## Subsample Solvent Insolvent Insolvent
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_t1_dw, "Table_1DW_Extensive",
title_text = "DW Extensive Margin: (Acute Period)",
notes_text = "LPM with robust SEs. Sample: DW borrowers vs. pure non-borrowers. Solvent: AdjEquity $\\geq$ 0. Insolvent: AdjEquity $<$ 0. All regressors z-standardized. Controls: log(assets), cash ratio, loan-to-deposit, wholesale, ROA, Book equity . Baseline: 2022Q4.",
extra_lines = t1_dw_extra)
## Saved: Table_1DW_Extensive.tex
# ==============================================================================
# TABLE 1-FHLB: EXTENSIVE MARGIN — FHLB (BENCHMARK)
# ==============================================================================
models_t1_fhlb <- list(
"(1) All" = run_one(df_fhlb_s, "fhlb_user", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(2) All" = run_one(df_fhlb_s, "fhlb_user", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(3) Solvent" = run_one(df_fhlb_sol, "fhlb_user", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(4) Solvent" = run_one(df_fhlb_sol, "fhlb_user", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(5) Insolvent" = run_one(df_fhlb_ins, "fhlb_user", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(6) Insolvent" = run_one(df_fhlb_ins, "fhlb_user", EXPL_MTM_INTER, controls = CONTROLS_MTM)
)
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
t1_fhlb_extra <- list(
"__N(FHLB=1)" = c(sum(df_fhlb_s$fhlb_user), sum(df_fhlb_s$fhlb_user),
sum(df_fhlb_sol$fhlb_user), sum(df_fhlb_sol$fhlb_user),
sum(df_fhlb_ins$fhlb_user), sum(df_fhlb_ins$fhlb_user)),
"__Subsample" = c("All", "All", "Solvent", "Solvent", "Insolvent", "Insolvent")
)
etable(models_t1_fhlb, fitstat = ~ n + r2,
order = COEF_ORDER, extralines = t1_fhlb_extra,
title = "Table 1-FHLB: FHLB Extensive Margin — Acute Period (LPM)")
## (1) All (2) All
## Dependent Var.: fhlb_user fhlb_user
##
## Constant 0.0894*** (0.0048) 0.0887*** (0.0049)
## MTM Loss (z) 0.0047 (0.0053) 0.0034 (0.0055)
## Uninsured Leverage (z) 0.0073 (0.0047) 0.0060 (0.0050)
## MTM $\times$ Uninsured -0.0044 (0.0042)
## Log(Assets) 0.0254*** (0.0063) 0.0254*** (0.0063)
## Cash Ratio -0.0205*** (0.0039) -0.0209*** (0.0039)
## Loan-to-Deposit 0.0248*** (0.0050) 0.0240*** (0.0050)
## Book Equity Ratio 0.0102* (0.0041) 0.0098* (0.0041)
## Wholesale Funding 0.0017 (0.0050) 0.0018 (0.0050)
## ROA -0.0093* (0.0042) -0.0088* (0.0042)
## ______________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 3,577 3,577
## R2 0.03907 0.03932
## N(FHLB=1) 302 302
## Subsample All All
##
## (3) Solvent (4) Solvent (5) Insolvent
## Dependent Var.: fhlb_user fhlb_user fhlb_user
##
## Constant 0.0909*** (0.0057) 0.0896*** (0.0059) 0.0756. (0.0406)
## MTM Loss (z) 0.0029 (0.0061) 0.0007 (0.0066) 0.0298 (0.0184)
## Uninsured Leverage (z) 0.0069 (0.0052) 0.0039 (0.0063) 0.0094 (0.0115)
## MTM $\times$ Uninsured -0.0056 (0.0054)
## Log(Assets) 0.0273*** (0.0070) 0.0275*** (0.0070) 0.0110 (0.0133)
## Cash Ratio -0.0221*** (0.0040) -0.0226*** (0.0040) -0.0094 (0.0149)
## Loan-to-Deposit 0.0222*** (0.0055) 0.0216*** (0.0055) 0.0257* (0.0127)
## Book Equity Ratio 0.0084. (0.0045) 0.0079. (0.0044) 0.0266 (0.0297)
## Wholesale Funding 0.0021 (0.0058) 0.0022 (0.0058) 0.0012 (0.0101)
## ROA -0.0093* (0.0046) -0.0088. (0.0046) -0.0085 (0.0108)
## ______________________ ___________________ ___________________ ________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob. Heterosked.-rob.
## Observations 2,930 2,930 647
## R2 0.04103 0.04136 0.03177
## N(FHLB=1) 257 257 45
## Subsample Solvent Solvent Insolvent
##
## (6) Insolvent
## Dependent Var.: fhlb_user
##
## Constant 0.0762. (0.0408)
## MTM Loss (z) 0.0298 (0.0184)
## Uninsured Leverage (z) 0.0072 (0.0175)
## MTM $\times$ Uninsured 0.0022 (0.0155)
## Log(Assets) 0.0109 (0.0133)
## Cash Ratio -0.0093 (0.0151)
## Loan-to-Deposit 0.0258* (0.0126)
## Book Equity Ratio 0.0268 (0.0298)
## Wholesale Funding 0.0011 (0.0100)
## ROA -0.0085 (0.0107)
## ______________________ ________________
## S.E. type Heterosked.-rob.
## Observations 647
## R2 0.03179
## N(FHLB=1) 45
## Subsample Insolvent
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_t1_fhlb, "Table_1FHLB_Extensive",
title_text = "FHLB Extensive Margin: (Acute, Benchmark)",
notes_text = "LPM with robust SEs. FHLB = abnormal borrowing ($>$90th pctile increase). If FHLB is collateral-based, interaction should be weak.",
extra_lines = t1_fhlb_extra)
## Saved: Table_1FHLB_Extensive.tex
Falsification: If AE and UL predict BTFP borrowing during the acute crisis but not during the arbitrage period (profit-driven), then acute-period coefficients reflect genuine crisis dynamics.
# ==============================================================================
# TABLE 2: ACUTE VS ARB — BTFP (All Banks)
# 4 columns: Acute (base, inter), Arb (base, inter)
# ==============================================================================
models_t2 <- list(
"(1) Acute" = run_one(df_btfp_s, "btfp_acute", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(2) Acute" = run_one(df_btfp_s, "btfp_acute", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(3) Arb" = run_one(df_btfp_arb_s, "btfp_arb", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(4) Arb" = run_one(df_btfp_arb_s, "btfp_arb", EXPL_MTM_INTER, controls = CONTROLS_MTM)
)
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 17 observations removed because of NA values (RHS: 17).
## NOTE: 17 observations removed because of NA values (RHS: 17).
t2_extra <- list(
"__N(BTFP=1)" = c(sum(df_btfp_s$btfp_acute), sum(df_btfp_s$btfp_acute),
sum(df_btfp_arb_s$btfp_arb), sum(df_btfp_arb_s$btfp_arb)),
"__Period" = c("Acute", "Acute", "Arbitrage", "Arbitrage"),
"__Baseline" = c("2022Q4", "2022Q4", "2023Q3", "2023Q3")
)
etable(models_t2, fitstat = ~ n + r2,
order = COEF_ORDER, extralines = t2_extra,
title = "Table 2: BTFP — Acute vs Arbitrage (LPM)")
## (1) Acute (2) Acute
## Dependent Var.: btfp_acute btfp_acute
##
## Constant 0.1293*** (0.0054) 0.1316*** (0.0055)
## MTM Loss (z) 0.0198*** (0.0055) 0.0243*** (0.0059)
## Uninsured Leverage (z) 0.0154* (0.0063) 0.0199** (0.0068)
## MTM $\times$ Uninsured 0.0181*** (0.0051)
## Log(Assets) 0.0676*** (0.0074) 0.0674*** (0.0074)
## Cash Ratio -0.0255*** (0.0047) -0.0238*** (0.0046)
## Loan-to-Deposit -0.0117* (0.0055) -0.0083 (0.0055)
## Book Equity Ratio -0.0123** (0.0044) -0.0109* (0.0044)
## Wholesale Funding 0.0258*** (0.0064) 0.0252*** (0.0064)
## ROA -0.0028 (0.0047) -0.0048 (0.0047)
## ______________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 3,737 3,737
## R2 0.08704 0.09017
## N(BTFP=1) 462 462
## Period Acute Acute
## Baseline 2022Q4 2022Q4
##
## (3) Arb (4) Arb
## Dependent Var.: btfp_arb btfp_arb
##
## Constant 0.1898*** (0.0057) 0.1907*** (0.0058)
## MTM Loss (z) 0.0209** (0.0066) 0.0223** (0.0068)
## Uninsured Leverage (z) 0.0120. (0.0064) 0.0135* (0.0066)
## MTM $\times$ Uninsured 0.0082 (0.0051)
## Log(Assets) 0.0535*** (0.0070) 0.0536*** (0.0070)
## Cash Ratio -0.0368*** (0.0057) -0.0360*** (0.0057)
## Loan-to-Deposit -0.0048 (0.0062) -0.0035 (0.0063)
## Book Equity Ratio -0.0072 (0.0056) -0.0065 (0.0056)
## Wholesale Funding 0.1009*** (0.0075) 0.1007*** (0.0075)
## ROA -0.0226*** (0.0057) -0.0236*** (0.0057)
## ______________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 4,038 4,038
## R2 0.14195 0.14242
## N(BTFP=1) 766 766
## Period Arbitrage Arbitrage
## Baseline 2023Q3 2023Q3
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_t2, "Table_2_Acute_vs_Arb",
title_text = "BTFP Robustness: Acute vs.\\ Arbitrage Period",
notes_text = "LPM with robust SEs. Acute: Mar 13--May 1, 2023. Arb: Nov 1, 2023--Jan 24, 2024 (BTFP rate $<$ IORB). If crisis dynamics differ from arbitrage dynamics, AE/UL should be significant only in acute period.",
extra_lines = t2_extra)
## Saved: Table_2_Acute_vs_Arb.tex
Does BTFP selection respond to losses on eligible collateral specifically?
# ==============================================================================
# TABLE 3: OMO LOSSES ONLY — BTFP (All Banks)
# 2 columns: OMO base, OMO + interaction
# ==============================================================================
models_t3 <- list(
"(1) OMO Base" = run_one(df_btfp_s, "btfp_acute", EXPL_OMO_BASE, controls = CONTROLS_MTM),
"(2) OMO Inter" = run_one(df_btfp_s, "btfp_acute", EXPL_OMO_INTER, controls = CONTROLS_MTM)
)
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
t3_extra <- list(
"__N(BTFP=1)" = rep(sum(df_btfp_s$btfp_acute), 2),
"__Specification" = c("OMO Base", "OMO + Interaction")
)
etable(models_t3, fitstat = ~ n + r2,
order = COEF_ORDER, extralines = t3_extra,
title = "Table 3: OMO Losses Only — Acute Period (LPM)")
## (1) OMO Base (2) OMO Inter
## Dependent Var.: btfp_acute btfp_acute
##
## Constant 0.1291*** (0.0054) 0.1290*** (0.0053)
## MTM Loss OMO (z) 0.0155* (0.0062) 0.0157* (0.0062)
## Uninsured Leverage (z) 0.0131* (0.0063) 0.0138* (0.0063)
## MTM OMO $\times$ Uninsured 0.0106* (0.0053)
## Log(Assets) 0.0666*** (0.0075) 0.0658*** (0.0074)
## Cash Ratio -0.0307*** (0.0044) -0.0307*** (0.0044)
## Loan-to-Deposit -0.0087 (0.0061) -0.0086 (0.0061)
## Book Equity Ratio -0.0145*** (0.0044) -0.0144*** (0.0043)
## Wholesale Funding 0.0240*** (0.0064) 0.0237*** (0.0064)
## ROA -0.0035 (0.0047) -0.0039 (0.0047)
## __________________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 3,737 3,737
## R2 0.08613 0.08727
## N(BTFP=1) 462 462
## Specification OMO Base OMO + Interaction
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_t3, "Table_3_OMO_Only",
title_text = "OMO Losses Only: Collateral Channel (Acute Period)",
notes_text = "LPM with robust SEs. MTM Loss OMO = losses on BTFP-eligible securities only. Controls include book equity ratio.",
extra_lines = t3_extra)
## Saved: Table_3_OMO_Only.tex
Conditional on borrowing, did fragile banks borrow more?
# ==============================================================================
# TABLE 4: INTENSIVE MARGIN — BTFP Borrowers Only
# DV: btfp_pct (BTFP amount / total assets %)
# 2 columns: AE base, AE + interaction
# ==============================================================================
df_btfp_int <- df_acute %>% filter(btfp_acute == 1, !is.na(btfp_pct))
cat("=== INTENSIVE MARGIN ===\n")
## === INTENSIVE MARGIN ===
cat("BTFP borrowers with amount data:", nrow(df_btfp_int), "\n")
## BTFP borrowers with amount data: 462
cat("Mean BTFP/TA (%):", round(mean(df_btfp_int$btfp_pct, na.rm = TRUE), 3), "\n")
## Mean BTFP/TA (%): 5.994
models_t4 <- list(
"(1) Base" = run_one(df_btfp_int, "btfp_pct", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(2) +Interaction" = run_one(df_btfp_int, "btfp_pct", EXPL_MTM_INTER, controls = CONTROLS_MTM)
)
t4_extra <- list(
"__N (Borrowers)" = rep(nrow(df_btfp_int), 2),
"__DV" = rep("BTFP/TA (%)", 2)
)
etable(models_t4, fitstat = ~ n + r2,
order = COEF_ORDER, extralines = t4_extra,
title = "Table 4: Intensive Margin — BTFP Borrowing Amount (LPM/OLS)")
## (1) Base (2) +Interaction
## Dependent Var.: btfp_pct btfp_pct
##
## Constant 6.964*** (0.9615) 7.358*** (1.141)
## MTM Loss (z) -1.052 (1.029) -1.185 (1.069)
## Uninsured Leverage (z) -1.809 (1.480) -2.040 (1.560)
## MTM $\times$ Uninsured 1.560 (0.9851)
## Log(Assets) 2.081 (1.442) 2.069 (1.388)
## Cash Ratio 1.914 (1.587) 2.364 (1.654)
## Loan-to-Deposit -3.363. (1.992) -2.823. (1.681)
## Book Equity Ratio 1.042 (0.8556) 1.084 (0.8766)
## Wholesale Funding 0.5755. (0.3354) 0.5103 (0.3443)
## ROA 0.3286 (0.9277) 0.1701 (0.8330)
## ______________________ _________________ ________________
## S.E. type Heteroskeda.-rob. Heterosked.-rob.
## Observations 462 462
## R2 0.12509 0.14654
## N (Borrowers) 462 462
## DV BTFP/TA (%) BTFP/TA (%)
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_t4, "Table_4_Intensive",
title_text = "Intensive Margin: BTFP Borrowing Amount (Acute Period)",
notes_text = "OLS with robust SEs. Sample: BTFP borrowers only. DV = BTFP amount / total assets (\\%).",
extra_lines = t4_extra)
## Saved: Table_4_Intensive.tex
Exploits BTFP design: par valuation benefit varies across banks.
\(\text{ParBenefit}_i = \text{MTM}^{OMO}_i / \text{OMO\_Holdings}_i\)
\(\beta_1 > 0\) → arbitrage motive; \(\beta_3 > 0\) → fragile banks with larger par benefit more likely to borrow.
# ==============================================================================
# TABLE 5: PAR BENEFIT — BTFP (All Banks)
# 2 columns: Par base, Par + interaction
# ==============================================================================
models_t5 <- list(
"(1) Par Base" = run_one(df_btfp_s, "btfp_acute", EXPL_PAR_BASE, controls = CONTROLS_MTM),
"(2) Par Inter" = run_one(df_btfp_s, "btfp_acute", EXPL_PAR_INTER, controls = CONTROLS_MTM)
)
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
t5_extra <- list(
"__N(BTFP=1)" = rep(sum(df_btfp_s$btfp_acute), 2),
"__Specification" = c("Par Benefit Base", "Par + Interaction")
)
etable(models_t5, fitstat = ~ n + r2,
order = COEF_ORDER, extralines = t5_extra,
title = "Table 5: Par Benefit — Acute Period (LPM)")
## (1) Par Base (2) Par Inter
## Dependent Var.: btfp_acute btfp_acute
##
## Constant 0.1292*** (0.0054) 0.1292*** (0.0054)
## Uninsured Leverage (z) 0.0121. (0.0062) 0.0112. (0.0063)
## Par Benefit $\times$ Uninsured -0.0061 (0.0046)
## Par Benefit (z) 0.0013 (0.0040) 0.0008 (0.0042)
## Log(Assets) 0.0697*** (0.0074) 0.0701*** (0.0074)
## Cash Ratio -0.0336*** (0.0043) -0.0341*** (0.0043)
## Loan-to-Deposit -0.0158** (0.0056) -0.0160** (0.0056)
## Book Equity Ratio -0.0154*** (0.0044) -0.0154*** (0.0044)
## Wholesale Funding 0.0249*** (0.0064) 0.0249*** (0.0064)
## ROA -0.0056 (0.0047) -0.0051 (0.0047)
## ______________________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 3,737 3,737
## R2 0.08442 0.08486
## N(BTFP=1) 462 462
## Specification Par Benefit Base Par + Interaction
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_t5, "Table_5_Par_Benefit",
title_text = "Par Benefit as Treatment Intensity (Acute Period)",
notes_text = "LPM with robust SEs. Par Benefit = MTM on OMO-eligible / OMO holdings. Tests whether the par valuation subsidy interacts with funding fragility.",
extra_lines = t5_extra)
## Saved: Table_5_Par_Benefit.tex
Before BTFP existed, DW was the only option. If AE/UL predict DW usage in this narrow window, it confirms run risk drove emergency borrowing before the par-value subsidy was available.
# ==============================================================================
# TABLE 7: PRE-BTFP DW — MARCH 10 & MARCH 10-13
# 4 columns: Mar10 (base, inter), Mar10-13 (base, inter)
# ==============================================================================
models_t7 <- list(
"(1) Mar10" = run_one(df_dw_mar10_s, "dw_mar10", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(2) Mar10" = run_one(df_dw_mar10_s, "dw_mar10", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(3) Mar10-13" = run_one(df_dw_mar10_13_s, "dw_mar10_13", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(4) Mar10-13" = run_one(df_dw_mar10_13_s, "dw_mar10_13", EXPL_MTM_INTER, controls = CONTROLS_MTM)
)
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
t7_extra <- list(
"__N(DW=1)" = c(sum(df_dw_mar10_s$dw_mar10), sum(df_dw_mar10_s$dw_mar10),
sum(df_dw_mar10_13_s$dw_mar10_13), sum(df_dw_mar10_13_s$dw_mar10_13)),
"__Window" = c("Mar 10 only", "Mar 10 only", "Mar 10-13", "Mar 10-13")
)
etable(models_t7, fitstat = ~ n + r2,
order = COEF_ORDER, extralines = t7_extra,
title = "Table 7: Pre-BTFP Discount Window Usage (LPM)")
## (1) Mar10 (2) Mar10 (3) Mar10-13
## Dependent Var.: dw_mar10 dw_mar10 dw_mar10_13
##
## Constant 0.0121*** (0.0017) 0.0122*** (0.0018) 0.0231*** (0.0024)
## MTM Loss (z) -0.0023 (0.0020) -0.0022 (0.0022) -0.0031 (0.0026)
## Uninsured Leverage (z) 0.0009 (0.0020) 0.0011 (0.0021) 0.0067* (0.0030)
## MTM $\times$ Uninsured 0.0007 (0.0018)
## Log(Assets) 0.0133*** (0.0030) 0.0133*** (0.0030) 0.0265*** (0.0041)
## Cash Ratio -0.0041** (0.0013) -0.0041** (0.0013) -0.0058** (0.0020)
## Loan-to-Deposit -0.0019 (0.0017) -0.0018 (0.0018) -0.0045. (0.0026)
## Book Equity Ratio -0.0004 (0.0014) -0.0004 (0.0014) 0.0016 (0.0020)
## Wholesale Funding 0.0071** (0.0025) 0.0071** (0.0025) 0.0085** (0.0030)
## ROA -0.0036** (0.0013) -0.0037** (0.0013) -0.0023 (0.0019)
## ______________________ __________________ __________________ __________________
## S.E. type Heteroskedas.-rob. Heteroskedas.-rob. Heteroskedas.-rob.
## Observations 3,986 3,986 3,988
## R2 0.02394 0.02398 0.04583
## N(DW=1) 47 47 90
## Window Mar 10 only Mar 10 only Mar 10-13
##
## (4) Mar10-13
## Dependent Var.: dw_mar10_13
##
## Constant 0.0236*** (0.0024)
## MTM Loss (z) -0.0021 (0.0029)
## Uninsured Leverage (z) 0.0078* (0.0033)
## MTM $\times$ Uninsured 0.0046. (0.0026)
## Log(Assets) 0.0265*** (0.0041)
## Cash Ratio -0.0054** (0.0020)
## Loan-to-Deposit -0.0037 (0.0027)
## Book Equity Ratio 0.0019 (0.0020)
## Wholesale Funding 0.0083** (0.0031)
## ROA -0.0028 (0.0019)
## ______________________ __________________
## S.E. type Heteroskedas.-rob.
## Observations 3,988
## R2 0.04684
## N(DW=1) 90
## Window Mar 10-13
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_t7, "Table_7_PreBTFP_DW",
title_text = "Pre-BTFP Discount Window: Immediate Crisis",
notes_text = "LPM with robust SEs. March 10 = SVB closure day (before BTFP announcement). March 10--13 includes BTFP announcement on March 12. DW borrowers vs. pure non-users.",
extra_lines = t7_extra)
## Saved: Table_7_PreBTFP_DW.tex
Falsification logic: Crisis drivers should predict borrowing during panic periods but not during arbitrage or wind-down phases.
# ==============================================================================
# TABLE 6: TEMPORAL EVOLUTION
# BTFP: 4 periods x 2 specs = 8 columns
# ==============================================================================
models_t6_btfp <- list(
"(1) Acute" = run_one(df_btfp_s, "btfp_acute", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(2) Acute" = run_one(df_btfp_s, "btfp_acute", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(3) Post" = run_one(df_btfp_post_s, "btfp_post", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(4) Post" = run_one(df_btfp_post_s, "btfp_post", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(5) Arb" = run_one(df_btfp_arb_s, "btfp_arb", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(6) Arb" = run_one(df_btfp_arb_s, "btfp_arb", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(7) Wind" = run_one(df_btfp_wind_s, "btfp_wind", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(8) Wind" = run_one(df_btfp_wind_s, "btfp_wind", EXPL_MTM_INTER, controls = CONTROLS_MTM)
)
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 17 observations removed because of NA values (RHS: 17).
## NOTE: 17 observations removed because of NA values (RHS: 17).
## NOTE: 18 observations removed because of NA values (RHS: 18).
## NOTE: 18 observations removed because of NA values (RHS: 18).
t6_btfp_extra <- list(
"__N(BTFP=1)" = c(sum(df_btfp_s$btfp_acute), sum(df_btfp_s$btfp_acute),
sum(df_btfp_post_s$btfp_post), sum(df_btfp_post_s$btfp_post),
sum(df_btfp_arb_s$btfp_arb), sum(df_btfp_arb_s$btfp_arb),
sum(df_btfp_wind_s$btfp_wind), sum(df_btfp_wind_s$btfp_wind)),
"__Baseline" = c("2022Q4","2022Q4","2022Q4","2022Q4","2023Q3","2023Q3","2023Q4","2023Q4")
)
# --- DISPLAY (HTML) ---
etable(models_t6_btfp,
fitstat = ~ n + r2,
order = COEF_ORDER,
extralines = t6_btfp_extra,
se.below = TRUE, # <--- SE below coefficient
digits = 4, # <--- 4 decimal places
signif.code = c("***"=0.01, "**"=0.05, "*"=0.10), # <--- Standard stars
title = "Table 6A: BTFP Temporal — ")
## (1) Acute (2) Acute (3) Post (4) Post (5) Arb
## Dependent Var.: btfp_acute btfp_acute btfp_post btfp_post btfp_arb
##
## Constant 0.1293*** 0.1316*** 0.2342*** 0.2363*** 0.1898***
## (0.0054) (0.0055) (0.0071) (0.0071) (0.0057)
## MTM Loss (z) 0.0198*** 0.0243*** 0.0217*** 0.0259*** 0.0209***
## (0.0055) (0.0059) (0.0075) (0.0079) (0.0066)
## Uninsured Leverage (z) 0.0154** 0.0199*** 0.0150* 0.0191** 0.0120*
## (0.0063) (0.0068) (0.0080) (0.0085) (0.0064)
## MTM $\times$ Uninsured 0.0181*** 0.0167***
## (0.0051) (0.0062)
## Log(Assets) 0.0676*** 0.0674*** 0.0726*** 0.0724*** 0.0535***
## (0.0074) (0.0074) (0.0093) (0.0093) (0.0070)
## Cash Ratio -0.0255*** -0.0238*** -0.0540*** -0.0520*** -0.0368***
## (0.0047) (0.0046) (0.0066) (0.0066) (0.0057)
## Loan-to-Deposit -0.0117** -0.0083 -0.0228*** -0.0197*** -0.0048
## (0.0055) (0.0055) (0.0074) (0.0075) (0.0062)
## Book Equity Ratio -0.0123*** -0.0109** -0.0213*** -0.0198*** -0.0072
## (0.0044) (0.0044) (0.0063) (0.0063) (0.0056)
## Wholesale Funding 0.0258*** 0.0252*** 0.0165** 0.0158** 0.1009***
## (0.0064) (0.0064) (0.0076) (0.0076) (0.0075)
## ROA -0.0028 -0.0048 -0.0111* -0.0129** -0.0226***
## (0.0047) (0.0047) (0.0064) (0.0065) (0.0057)
## ______________________ __________ __________ __________ __________ __________
## S.E. type Hete.-rob. Hete.-rob. Hete.-rob. Hete.-rob. Hete.-rob.
## Observations 3,737 3,737 3,437 3,437 4,038
## R2 0.08704 0.09017 0.08307 0.08472 0.14195
## N(BTFP=1) 462 462 775 775 766
## Baseline 2022Q4 2022Q4 2022Q4 2022Q4 2023Q3
##
## (6) Arb (7) Wind (8) Wind
## Dependent Var.: btfp_arb btfp_wind btfp_wind
##
## Constant 0.1907*** 0.0569*** 0.0568***
## (0.0058) (0.0035) (0.0036)
## MTM Loss (z) 0.0223*** 0.0025 0.0023
## (0.0068) (0.0041) (0.0042)
## Uninsured Leverage (z) 0.0135** 0.0075* 0.0073*
## (0.0066) (0.0041) (0.0043)
## MTM $\times$ Uninsured 0.0082 -0.0010
## (0.0051) (0.0033)
## Log(Assets) 0.0536*** 4.22e-6 -1.18e-5
## (0.0070) (0.0042) (0.0042)
## Cash Ratio -0.0360*** -0.0103*** -0.0104***
## (0.0057) (0.0038) (0.0038)
## Loan-to-Deposit -0.0035 -0.0098** -0.0100**
## (0.0063) (0.0040) (0.0041)
## Book Equity Ratio -0.0065 -0.0059* -0.0060*
## (0.0056) (0.0035) (0.0035)
## Wholesale Funding 0.1007*** 0.0526*** 0.0526***
## (0.0075) (0.0058) (0.0058)
## ROA -0.0236*** -0.0068* -0.0067*
## (0.0057) (0.0038) (0.0038)
## ______________________ __________ __________ __________
## S.E. type Hete.-rob. Hete.-rob. Hete.-rob.
## Observations 4,038 4,043 4,043
## R2 0.14242 0.06271 0.06273
## N(BTFP=1) 766 229 229
## Baseline 2023Q3 2023Q4 2023Q4
## ---
## Signif. codes: 0 '***' 0.01 '**' 0.05 '*' 0.1 ' ' 1
# --- SAVE (LaTeX) ---
save_etable(models_t6_btfp, "Table_6A_Temporal_BTFP",
title_text = "BTFP Temporal: Across Crisis Periods",
notes_text = "LPM with robust SEs. Acute: Mar 13--May 1. Post: May 2--Oct 31. Arb: Nov 1--Jan 24. Wind: Jan 25--Mar 11.",
extra_lines = t6_btfp_extra)
## Saved: Table_6A_Temporal_BTFP.tex
# ==============================================================================
# TABLE 6-DW: DW TEMPORAL (DW data through Dec 2023 only)
# ==============================================================================
models_t6_dw <- list(
"(1) Pre-BTFP" = run_one(df_dw_pre_s, "dw_prebtfp", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(2) Pre-BTFP" = run_one(df_dw_pre_s, "dw_prebtfp", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(3) Acute" = run_one(df_dw_s, "dw_acute", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(4) Acute" = run_one(df_dw_s, "dw_acute", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(5) Post" = run_one(df_dw_post_s, "dw_post", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(6) Post" = run_one(df_dw_post_s, "dw_post", EXPL_MTM_INTER, controls = CONTROLS_MTM)
)
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
t6_dw_extra <- list(
"__N(DW=1)" = c(sum(df_dw_pre_s$dw_prebtfp), sum(df_dw_pre_s$dw_prebtfp),
sum(df_dw_s$dw_acute), sum(df_dw_s$dw_acute),
sum(df_dw_post_s$dw_post), sum(df_dw_post_s$dw_post)),
"__Period" = c("Pre-BTFP", "Pre-BTFP", "Acute", "Acute", "Post", "Post")
)
# --- DISPLAY (HTML) ---
etable(models_t6_dw,
fitstat = ~ n + r2,
order = COEF_ORDER,
extralines = t6_dw_extra,
se.below = TRUE,
digits = 4,
signif.code = c("***"=0.01, "**"=0.05, "*"=0.10),
title = "Table 6B: DW Temporal — Pre-BTFP, Acute, and Post-Acute")
## (1) Pre-.. (2) Pre-.. (3) Acute (4) Acute (5) Post
## Dependent Var.: dw_prebtfp dw_prebtfp dw_acute dw_acute dw_post
##
## Constant 0.0256*** 0.0259*** 0.1130*** 0.1151*** 0.2544***
## (0.0025) (0.0025) (0.0051) (0.0052) (0.0069)
## MTM Loss (z) 0.0004 0.0009 0.0122** 0.0161*** 0.0071
## (0.0030) (0.0032) (0.0054) (0.0059) (0.0076)
## Uninsured Leverage (z) 0.0003 0.0009 0.0089 0.0131** 0.0151*
## (0.0030) (0.0031) (0.0060) (0.0064) (0.0082)
## MTM $\times$ Uninsured 0.0024 0.0160***
## (0.0024) (0.0049)
## Log(Assets) 0.0236*** 0.0235*** 0.0843*** 0.0842*** 0.1304***
## (0.0041) (0.0041) (0.0074) (0.0074) (0.0092)
## Cash Ratio -0.0048** -0.0045* -0.0040 -0.0025 -0.0158**
## (0.0024) (0.0024) (0.0051) (0.0051) (0.0073)
## Loan-to-Deposit -0.0013 -0.0009 -0.0041 -0.0014 0.0208***
## (0.0027) (0.0028) (0.0054) (0.0055) (0.0076)
## Book Equity Ratio 0.0010 0.0012 -0.0013 -3.36e-5 0.0009
## (0.0024) (0.0024) (0.0042) (0.0043) (0.0065)
## Wholesale Funding 0.0135*** 0.0134*** 0.0211*** 0.0205*** 0.0048
## (0.0035) (0.0036) (0.0062) (0.0062) (0.0075)
## ROA -0.0017 -0.0020 -0.0004 -0.0022 -0.0080
## (0.0021) (0.0021) (0.0046) (0.0046) (0.0067)
## ______________________ __________ __________ __________ __________ __________
## S.E. type Hete.-rob. Hete.-rob. Hete.-rob. Hete.-rob. Hete.-rob.
## Observations 3,988 3,988 3,668 3,668 3,549
## R2 0.03540 0.03566 0.09279 0.09557 0.12653
## N(DW=1) 100 100 393 393 887
## Period Pre-BTFP Pre-BTFP Acute Acute Post
##
## (6) Post
## Dependent Var.: dw_post
##
## Constant 0.2568***
## (0.0070)
## MTM Loss (z) 0.0118
## (0.0080)
## Uninsured Leverage (z) 0.0204**
## (0.0086)
## MTM $\times$ Uninsured 0.0193***
## (0.0063)
## Log(Assets) 0.1301***
## (0.0092)
## Cash Ratio -0.0137*
## (0.0073)
## Loan-to-Deposit 0.0243***
## (0.0076)
## Book Equity Ratio 0.0024
## (0.0065)
## Wholesale Funding 0.0039
## (0.0075)
## ROA -0.0100
## (0.0067)
## ______________________ __________
## S.E. type Hete.-rob.
## Observations 3,549
## R2 0.12864
## N(DW=1) 887
## Period Post
## ---
## Signif. codes: 0 '***' 0.01 '**' 0.05 '*' 0.1 ' ' 1
# --- SAVE (LaTeX) ---
save_etable(models_t6_dw, "Table_6B_Temporal_DW",
title_text = "DW Temporal: Pre-BTFP, Acute, and Post-Acute",
notes_text = "LPM with robust SEs. Pre-BTFP: Mar 1--Mar 12. Acute: Mar 13--May 1. Post: May 2--Oct 31. DW data through Dec 2023.",
extra_lines = t6_dw_extra)
## Saved: Table_6B_Temporal_DW.tex
# ==============================================================================
# TABLE 6-FHLB: FHLB TEMPORAL (BENCHMARK)
# ==============================================================================
models_t6_fhlb <- list(
"(1) Acute" = run_one(df_fhlb_s, "fhlb_user", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(2) Acute" = run_one(df_fhlb_s, "fhlb_user", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(3) Post" = run_one(df_fhlb_post_s, "fhlb_user", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(4) Post" = run_one(df_fhlb_post_s, "fhlb_user", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(5) Arb" = run_one(df_fhlb_arb_s, "fhlb_user", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(6) Arb" = run_one(df_fhlb_arb_s, "fhlb_user", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(7) Wind" = run_one(df_fhlb_wind_s, "fhlb_user", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(8) Wind" = run_one(df_fhlb_wind_s, "fhlb_user", EXPL_MTM_INTER, controls = CONTROLS_MTM)
)
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 17 observations removed because of NA values (RHS: 17).
## NOTE: 17 observations removed because of NA values (RHS: 17).
## NOTE: 18 observations removed because of NA values (RHS: 18).
## NOTE: 18 observations removed because of NA values (RHS: 18).
t6_fhlb_extra <- list(
"__N(FHLB=1)" = c(sum(df_fhlb_s$fhlb_user), sum(df_fhlb_s$fhlb_user),
sum(df_fhlb_post_s$fhlb_user), sum(df_fhlb_post_s$fhlb_user),
sum(df_fhlb_arb_s$fhlb_user), sum(df_fhlb_arb_s$fhlb_user),
sum(df_fhlb_wind_s$fhlb_user), sum(df_fhlb_wind_s$fhlb_user))
)
# --- DISPLAY (HTML) ---
etable(models_t6_fhlb,
fitstat = ~ n + r2,
order = COEF_ORDER,
extralines = t6_fhlb_extra,
se.below = TRUE, # <--- SE below coefficient
digits = 4, # <--- 4 decimal places
signif.code = c("***"=0.01, "**"=0.05, "*"=0.10), # <--- Standard stars
title = "Table 6C: FHLB Temporal — (Benchmark)")
## (1) Acute (2) Acute (3) Post (4) Post (5) Arb
## Dependent Var.: fhlb_user fhlb_user fhlb_user fhlb_user fhlb_user
##
## Constant 0.0894*** 0.0887*** 0.1121*** 0.1114*** 0.0600***
## (0.0048) (0.0049) (0.0060) (0.0060) (0.0042)
## MTM Loss (z) 0.0047 0.0034 0.0042 0.0030 0.0052
## (0.0053) (0.0055) (0.0062) (0.0065) (0.0046)
## Uninsured Leverage (z) 0.0073 0.0060 0.0083 0.0071 0.0008
## (0.0047) (0.0050) (0.0058) (0.0062) (0.0042)
## MTM $\times$ Uninsured -0.0044 -0.0037
## (0.0042) (0.0048)
## Log(Assets) 0.0254*** 0.0254*** 0.0378*** 0.0377*** 0.0047
## (0.0063) (0.0063) (0.0077) (0.0077) (0.0040)
## Cash Ratio -0.0205*** -0.0209*** -0.0247*** -0.0251*** -0.0182***
## (0.0039) (0.0039) (0.0045) (0.0045) (0.0032)
## Loan-to-Deposit 0.0248*** 0.0240*** 0.0292*** 0.0286*** 0.0151***
## (0.0050) (0.0050) (0.0058) (0.0058) (0.0038)
## Book Equity Ratio 0.0102** 0.0098** 0.0129*** 0.0126*** -0.0008
## (0.0041) (0.0041) (0.0047) (0.0047) (0.0035)
## Wholesale Funding 0.0017 0.0018 -0.0003 -0.0001 0.0078
## (0.0050) (0.0050) (0.0058) (0.0059) (0.0051)
## ROA -0.0093** -0.0088** -0.0124** -0.0120** -0.0014
## (0.0042) (0.0042) (0.0050) (0.0050) (0.0037)
## ______________________ __________ __________ __________ __________ __________
## S.E. type Hete.-rob. Hete.-rob. Hete.-rob. Hete.-rob. Hete.-rob.
## Observations 3,577 3,577 2,964 2,964 3,468
## R2 0.03907 0.03932 0.05486 0.05501 0.02140
## N(FHLB=1) 302 302 302 302 196
##
## (6) Arb (7) Wind (8) Wind
## Dependent Var.: fhlb_user fhlb_user fhlb_user
##
## Constant 0.0592*** 0.0371*** 0.0364***
## (0.0041) (0.0030) (0.0030)
## MTM Loss (z) 0.0039 0.0011 1.25e-8
## (0.0046) (0.0035) (0.0035)
## Uninsured Leverage (z) -0.0006 0.0013 0.0001
## (0.0044) (0.0033) (0.0033)
## MTM $\times$ Uninsured -0.0058* -0.0057**
## (0.0034) (0.0024)
## Log(Assets) 0.0046 -0.0070** -0.0070**
## (0.0040) (0.0035) (0.0036)
## Cash Ratio -0.0188*** -0.0112*** -0.0118***
## (0.0032) (0.0029) (0.0029)
## Loan-to-Deposit 0.0141*** 0.0133*** 0.0123***
## (0.0038) (0.0033) (0.0032)
## Book Equity Ratio -0.0014 0.0003 -0.0001
## (0.0034) (0.0029) (0.0029)
## Wholesale Funding 0.0079 0.0110*** 0.0112***
## (0.0051) (0.0041) (0.0041)
## ROA -0.0008 0.0016 0.0021
## (0.0037) (0.0032) (0.0032)
## ______________________ __________ __________ __________
## S.E. type Hete.-rob. Hete.-rob. Hete.-rob.
## Observations 3,468 3,959 3,959
## R2 0.02210 0.01371 0.01466
## N(FHLB=1) 196 145 145
## ---
## Signif. codes: 0 '***' 0.01 '**' 0.05 '*' 0.1 ' ' 1
# --- SAVE (LaTeX) ---
save_etable(models_t6_fhlb, "Table_6C_Temporal_FHLB",
title_text = "FHLB Temporal: (Benchmark)",
notes_text = "LPM with robust SEs. FHLB = abnormal borrowing. If FHLB is collateral-based, interaction should be weak.",
extra_lines = t6_fhlb_extra)
## Saved: Table_6C_Temporal_FHLB.tex
8a. Compare coefficients across facilities 8b. Multinomial logit: Neither / BTFP only / DW only / Both
Prediction: \(\beta_3^{DW} > \beta_3^{BTFP}\) (DW signals more severe run pressure).
# ==============================================================================
# SPEC 6: FACILITY CHOICE
# ==============================================================================
# ------------------------------------------------------------------------------
# 6a: Side-by-side comparison (LPM & Logit)
# ------------------------------------------------------------------------------
# We use EXPL_MTM_INTER to capture the Beta_3 interaction coefficient.
m_spec6_compare <- list(
"BTFP: LPM" = run_one(df_btfp_s, "btfp_acute", EXPL_MTM_INTER, "lpm", controls = CONTROLS_MTM),
"DW: LPM" = run_one(df_dw_s, "dw_acute", EXPL_MTM_INTER, "lpm", controls = CONTROLS_MTM),
"BTFP: Logit" = run_one(df_btfp_s, "btfp_acute", EXPL_MTM_INTER, "logit", controls = CONTROLS_MTM),
"DW: Logit" = run_one(df_dw_s, "dw_acute", EXPL_MTM_INTER, "logit", controls = CONTROLS_MTM)
)
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
# --- DISPLAY 6a (HTML) ---
etable(m_spec6_compare,
fitstat = ~ n + r2 + ll,
drop = "Log|Cash|Loan-to|Wholesale|ROA|Book Equity", # Hide controls for cleaner view
se.below = TRUE,
digits = 4,
signif.code = c("***"=0.01, "**"=0.05, "*"=0.10),
title = "Spec 6a: BTFP vs DW Coefficient Comparison (MTM Framework)")
## BTFP: LPM DW: LPM BTFP: Lo.. DW: Logit
## Dependent Var.: btfp_acute dw_acute btfp_acute dw_acute
##
## Constant 0.1316*** 0.1151*** -2.320*** -2.426***
## (0.0055) (0.0052) (0.0711) (0.0672)
## MTM Loss (z) 0.0243*** 0.0161*** 0.2141*** 0.1944***
## (0.0059) (0.0059) (0.0622) (0.0679)
## Uninsured Leverage (z) 0.0199*** 0.0131** 0.2089*** 0.1603***
## (0.0068) (0.0064) (0.0626) (0.0615)
## MTM $\times$ Uninsured 0.0181*** 0.0160*** 0.0461 0.0832
## (0.0051) (0.0049) (0.0569) (0.0535)
## ______________________ __________ __________ __________ __________
## Family OLS OLS Logit Logit
## S.E. type Hete.-rob. Hete.-rob. Hete.-rob. Hete.-rob.
## Observations 3,737 3,668 3,737 3,668
## R2 0.09017 0.09557 -- --
## Log-Likelihood -973.38 -716.19 -1,214.5 -1,080.9
## ---
## Signif. codes: 0 '***' 0.01 '**' 0.05 '*' 0.1 ' ' 1
# --- SAVE 6a (LaTeX) ---
save_etable(m_spec6_compare, "Spec6a_FacilityChoice_Compare",
title_text = "Spec 6a: BTFP vs. DW Coefficient Comparison (MTM Framework)",
notes_text = "DV: facility-specific. Compare MTM x Uninsured interaction across facilities. Robust SEs. Controls: Log(Assets), Cash, Loan-to-Deposit, Wholesale, ROA, Book Equity.",
fitstat_use = ~ n + r2 + ll)
## Saved: Spec6a_FacilityChoice_Compare.tex
# ------------------------------------------------------------------------------
# 6b: Multinomial Logit (Standardized Z-Scores)
# ------------------------------------------------------------------------------
cat("\n=== SPEC 6b: MULTINOMIAL LOGIT (Standardized) ===\n")
##
## === SPEC 6b: MULTINOMIAL LOGIT (Standardized) ===
# 1. Prepare Data
# Filter for complete cases on the variables we need to avoid errors
df_mlogit <- df_acute %>%
filter(!is.na(mtm_total) & !is.na(uninsured_lev)) %>%
mutate(facility_choice = factor(user_group, levels = c("Neither", "BTFP_Only", "DW_Only", "Both")))
# 2. Fit Model (Define mlogit_fit)
# Using Z-SCORES (mtm_total, uninsured_lev) for consistency with other tables
mlogit_fit <- nnet::multinom(
facility_choice ~ mtm_total + uninsured_lev +
I(mtm_total * uninsured_lev) +
ln_assets + cash_ratio + loan_to_deposit + wholesale + roa + book_equity_ratio,
data = df_mlogit, trace = FALSE
)
# 3. Define Map for Output
coef_map_multinom <- c(
"mtm_total" = "MTM Loss (z)",
"uninsured_lev" = "Uninsured Leverage (z)",
"I(mtm_total * uninsured_lev)" = "MTM $\\times$ Uninsured",
"ln_assets" = "Log(Assets)",
"cash_ratio" = "Cash Ratio",
"loan_to_deposit" = "Loan-to-Deposit",
"wholesale" = "Wholesale Funding",
"roa" = "ROA",
"book_equity_ratio" = "Book Equity Ratio",
"(Intercept)" = "Constant"
)
# 4. Save to LaTeX
modelsummary(
mlogit_fit,
output = file.path(TABLE_PATH, "Spec6b_Multinomial.tex"),
shape = term ~ response,
stars = c("*" = 0.1, "**" = 0.05, "***" = 0.01),
coef_map = coef_map_multinom,
gof_map = c("nobs", "r2.mcfadden"),
title = "Spec 6b: Multinomial Logit — Facility Choice (Ref: Neither)",
notes = list("Standard errors in parentheses. Reference category: Neither. All regressors are z-standardized."),
escape = FALSE
)
## Warning: To compile a LaTeX document with this table, the following commands must be placed in the document preamble:
##
## \usepackage{tabularray}
## \usepackage{float}
## \usepackage{graphicx}
## \usepackage{codehigh}
## \usepackage[normalem]{ulem}
## \UseTblrLibrary{booktabs}
## \UseTblrLibrary{siunitx}
## \newcommand{\tinytableTabularrayUnderline}[1]{\underline{#1}}
## \newcommand{\tinytableTabularrayStrikeout}[1]{\sout{#1}}
## \NewTableCommand{\tinytableDefineColor}[3]{\definecolor{#1}{#2}{#3}}
##
## To disable `siunitx` and prevent `modelsummary` from wrapping numeric entries in `\num{}`, call:
##
## options("modelsummary_format_numeric_latex" = "plain")
## This warning appears once per session.
# 5. Display 6b on Screen (HTML)
modelsummary(
mlogit_fit,
shape = term ~ response,
stars = TRUE,
coef_map = coef_map_multinom,
title = "Spec 6b: Multinomial Logit (Screen Preview)"
)
| (1) | |||
|---|---|---|---|
| BTFP_Only | DW_Only | Both | |
| + p < 0.1, * p < 0.05, ** p < 0.01, *** p < 0.001 | |||
| MTM Loss (z) | 0.233*** | 0.217** | 0.080 |
| (0.069) | (0.075) | (0.152) | |
| Uninsured Leverage (z) | 0.136* | 0.077 | 0.405*** |
| (0.069) | (0.070) | (0.118) | |
| MTM $\times$ Uninsured | 0.013 | 0.061 | 0.207+ |
| (0.061) | (0.061) | (0.110) | |
| Log(Assets) | 0.465*** | 0.716*** | 1.002*** |
| (0.069) | (0.070) | (0.121) | |
| Cash Ratio | -0.504*** | 0.004 | -0.340+ |
| (0.109) | (0.090) | (0.198) | |
| Loan-to-Deposit | -0.008 | 0.105 | -0.013 |
| (0.076) | (0.083) | (0.153) | |
| Wholesale Funding | 0.158** | 0.141* | 0.281*** |
| (0.050) | (0.056) | (0.084) | |
| ROA | 0.022 | 0.085 | -0.106 |
| (0.068) | (0.071) | (0.139) | |
| Book Equity Ratio | -0.356*** | -0.142 | -0.424* |
| (0.089) | (0.087) | (0.193) | |
| Constant | -2.567*** | -2.698*** | -4.423*** |
| (0.075) | (0.074) | (0.181) | |
| Num.Obs. | 4282 | ||
| R2 | 0.100 | ||
| R2 Adj. | 0.100 | ||
| AIC | 5003.5 | ||
| BIC | 5194.4 | ||
| RMSE | 0.27 | ||
# ==============================================================================
# SPECIFICATION: THRESHOLD GRADIENT (Uninsured Leverage Terciles)
# Prediction: Beta_High > Beta_Med > Beta_Low
# ==============================================================================
# 1. Ensure Terciles are defined on the Acute Sample
df_gradient <- df_btfp_s %>%
filter(!is.na(uninsured_lev_w)) %>%
mutate(
unins_tercile = cut(
uninsured_lev_w,
breaks = quantile(uninsured_lev_w, probs = c(0, 1/3, 2/3, 1), na.rm = TRUE),
labels = c("Low", "Medium", "High"),
include.lowest = TRUE
)
)
# 2. Run Regression for Each Tercile
# Model: BTFP ~ MTM_Loss + Controls (Split by Tercile)
models_gradient <- list(
"Low Fragility" = run_one(df_gradient %>% filter(unins_tercile == "Low"),
"btfp_acute", "mtm_total", controls = CONTROLS_MTM),
"Medium Fragility" = run_one(df_gradient %>% filter(unins_tercile == "Medium"),
"btfp_acute", "mtm_total", controls = CONTROLS_MTM),
"High Fragility" = run_one(df_gradient %>% filter(unins_tercile == "High"),
"btfp_acute", "mtm_total", controls = CONTROLS_MTM)
)
## NOTE: 3 observations removed because of NA values (RHS: 3).
## NOTE: 1 observation removed because of NA values (RHS: 1).
## NOTE: 6 observations removed because of NA values (RHS: 6).
# 3. Save/Display Table
etable(models_gradient,
fitstat = ~ n + r2,
se.below = TRUE,
digits = 4,
signif.code = c("***"=0.01, "**"=0.05, "*"=0.10),
title = "Threshold Gradient: Effect of MTM Loss by Uninsured Leverage Tercile")
## Low Frag.. Medium F.. High Fra..
## Dependent Var.: btfp_acute btfp_acute btfp_acute
##
## Constant 0.1050*** 0.1278*** 0.1431***
## (0.0115) (0.0092) (0.0112)
## MTM Loss (z) 0.0075 0.0139 0.0355***
## (0.0070) (0.0104) (0.0127)
## Log(Assets) 0.0623*** 0.0615*** 0.0801***
## (0.0117) (0.0142) (0.0128)
## Cash Ratio -0.0100 -0.0280*** -0.0332***
## (0.0061) (0.0091) (0.0095)
## Loan-to-Deposit -0.0015 -0.0183 -0.0077
## (0.0068) (0.0125) (0.0121)
## Book Equity Ratio -0.0083* -0.0229* -0.0211*
## (0.0047) (0.0122) (0.0118)
## Wholesale Funding 0.0017 0.0581*** 0.0197
## (0.0062) (0.0136) (0.0131)
## ROA -0.0071 0.0083 -0.0089
## (0.0063) (0.0097) (0.0093)
## _________________ __________ __________ __________
## S.E. type Hete.-rob. Hete.-rob. Hete.-rob.
## Observations 1,246 1,248 1,243
## R2 0.06536 0.08395 0.08979
## ---
## Signif. codes: 0 '***' 0.01 '**' 0.05 '*' 0.1 ' ' 1
save_etable(models_gradient, "Table_Threshold_Gradient",
title_text = "Threshold Gradient: MTM Sensitivity by Liquidity Fragility",
notes_text = "LPM with robust SEs. Sample split by terciles of Uninsured Leverage. We expect the coefficient on MTM Loss to increase (become more positive) as fragility increases.")
## Saved: Table_Threshold_Gradient.tex
# 4. Generate Figure (Coefficient Plot)
# Extract MTM coefficients and plot them with 95% CIs
gradient_plot_data <- map_dfr(names(models_gradient), function(name) {
mod <- models_gradient[[name]]
tidy(mod, conf.int = TRUE) %>%
filter(term == "mtm_total") %>%
mutate(Group = factor(name, levels = c("Low Fragility", "Medium Fragility", "High Fragility")))
})
fig_gradient <- ggplot(gradient_plot_data, aes(x = Group, y = estimate)) +
geom_bar(stat = "identity", fill = "#003049", width = 0.6, alpha = 0.9) +
geom_errorbar(aes(ymin = conf.low, ymax = conf.high),
width = 0.2, color = "black", linewidth = 0.8) +
geom_hline(yintercept = 0, linetype = "dashed", color = "grey50") +
geom_text(aes(label = round(estimate, 3)), vjust = -2.5, fontface = "bold") +
scale_y_continuous(limits = c(min(0, min(gradient_plot_data$conf.low)),
max(gradient_plot_data$conf.high) * 1.2)) +
labs(
title = "Figure: Threshold Gradient",
subtitle = "Sensitivity of Borrowing to MTM Losses increases with Funding Fragility",
x = "Uninsured Leverage Tercile",
y = "MTM Loss Coefficient (Beta)"
) +
theme_paper +
theme(panel.grid.major.x = element_blank())
# Display and Save Figure
print(fig_gradient)
save_figure(fig_gradient, "Fig_Threshold_Gradient_MTM")
## Saved: Fig_Threshold_Gradient_MTM.pdf
Subsample: Banks with Adjusted Equity ≥ 0. Among solvent banks, a significant UL coefficient means funding fragility drives borrowing even when the bank is fundamentally sound — the classic panic mechanism.
# ==============================================================================
# SOLVENT SUBSAMPLE — create all period-specific solvent samples
# ==============================================================================
# Acute (already created above)
# df_btfp_sol, df_dw_sol, df_fhlb_sol
# Intensive margin
df_btfp_int_sol <- df_btfp_int %>% filter(mtm_solvent == 1)
# Post-Acute
df_btfp_post_sol <- df_btfp_post_s %>% filter(mtm_solvent == 1)
df_dw_post_sol <- df_dw_post_s %>% filter(mtm_solvent == 1)
# Arbitrage
df_btfp_arb_sol <- df_btfp_arb_s %>% filter(mtm_solvent == 1)
# Wind-down
df_btfp_wind_sol <- df_btfp_wind_s %>% filter(mtm_solvent == 1)
cat("=== SOLVENT SUBSAMPLE COUNTS ===\n")
## === SOLVENT SUBSAMPLE COUNTS ===
cat("BTFP Acute:", nrow(df_btfp_sol), "(BTFP=1:", sum(df_btfp_sol$btfp_acute), ")\n")
## BTFP Acute: 3003 (BTFP=1: 330 )
cat("DW Acute:", nrow(df_dw_sol), "(DW=1:", sum(df_dw_sol$dw_acute), ")\n")
## DW Acute: 2989 (DW=1: 316 )
cat("Intensive:", nrow(df_btfp_int_sol), "\n")
## Intensive: 330
models_t2s <- list(
"(1) Acute" = run_one(df_btfp_sol, "btfp_acute", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(2) Acute" = run_one(df_btfp_sol, "btfp_acute", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(3) Arb" = run_one(df_btfp_arb_sol, "btfp_arb", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(4) Arb" = run_one(df_btfp_arb_sol, "btfp_arb", EXPL_MTM_INTER, controls = CONTROLS_MTM)
)
t2s_extra <- list(
"__N(BTFP=1)" = c(sum(df_btfp_sol$btfp_acute), sum(df_btfp_sol$btfp_acute),
sum(df_btfp_arb_sol$btfp_arb), sum(df_btfp_arb_sol$btfp_arb)),
"__Subsample" = rep("Solvent", 4)
)
# --- DISPLAY (HTML) ---
etable(models_t2s,
fitstat = ~ n + r2,
order = COEF_ORDER,
extralines = t2s_extra,
se.below = TRUE, # <--- SE below coefficient
digits = 4, # <--- 4 decimal places
signif.code = c("***"=0.01, "**"=0.05, "*"=0.10), # <--- Standard stars
title = "Table 2-S: BTFP Acute vs Arb — Solvent Banks (LPM)")
## (1) Acute (2) Acute (3) Arb (4) Arb
## Dependent Var.: btfp_acute btfp_acute btfp_arb btfp_arb
##
## Constant 0.1263*** 0.1303*** 0.1887*** 0.1908***
## (0.0064) (0.0068) (0.0069) (0.0070)
## MTM Loss (z) 0.0193*** 0.0262*** 0.0192*** 0.0225***
## (0.0062) (0.0070) (0.0073) (0.0077)
## Uninsured Leverage (z) 0.0162** 0.0255*** 0.0147** 0.0204***
## (0.0068) (0.0080) (0.0068) (0.0078)
## MTM $\times$ Uninsured 0.0185*** 0.0124**
## (0.0059) (0.0059)
## Log(Assets) 0.0621*** 0.0610*** 0.0520*** 0.0515***
## (0.0080) (0.0080) (0.0076) (0.0076)
## Cash Ratio -0.0236*** -0.0221*** -0.0348*** -0.0338***
## (0.0047) (0.0047) (0.0059) (0.0059)
## Loan-to-Deposit -0.0111* -0.0091 -0.0137** -0.0121*
## (0.0058) (0.0057) (0.0065) (0.0066)
## Book Equity Ratio -0.0088** -0.0074* -0.0063 -0.0050
## (0.0044) (0.0045) (0.0059) (0.0059)
## Wholesale Funding 0.0264*** 0.0257*** 0.1052*** 0.1047***
## (0.0070) (0.0070) (0.0086) (0.0086)
## ROA 0.0011 -0.0005 -0.0221*** -0.0233***
## (0.0049) (0.0049) (0.0060) (0.0060)
## ______________________ __________ __________ __________ __________
## S.E. type Hete.-rob. Hete.-rob. Hete.-rob. Hete.-rob.
## Observations 3,003 3,003 3,256 3,256
## R2 0.08692 0.08991 0.14620 0.14712
## N(BTFP=1) 330 330 556 556
## Subsample Solvent Solvent Solvent Solvent
## ---
## Signif. codes: 0 '***' 0.01 '**' 0.05 '*' 0.1 ' ' 1
# --- SAVE (LaTeX) ---
save_etable(models_t2s, "Table_2S_AcuteArb_Solvent",
title_text = "BTFP Acute vs.\\ Arb: Solvent Banks Only",
notes_text = "LPM with robust SEs. Solvent: AdjEquity $\\geq$ 0.",
extra_lines = t2s_extra)
## Saved: Table_2S_AcuteArb_Solvent.tex
models_t3s <- list(
"(1) OMO Base" = run_one(df_btfp_sol, "btfp_acute", EXPL_OMO_BASE, controls = CONTROLS_MTM),
"(2) OMO Inter" = run_one(df_btfp_sol, "btfp_acute", EXPL_OMO_INTER, controls = CONTROLS_MTM)
)
etable(models_t3s, fitstat = ~ n + r2, order = COEF_ORDER,
title = "Table 3-S: OMO Losses — Solvent Banks (LPM)")
## (1) OMO Base (2) OMO Inter
## Dependent Var.: btfp_acute btfp_acute
##
## Constant 0.1222*** (0.0061) 0.1225*** (0.0061)
## MTM Loss OMO (z) 0.0150* (0.0070) 0.0165* (0.0073)
## Uninsured Leverage (z) 0.0142* (0.0067) 0.0169* (0.0068)
## MTM OMO $\times$ Uninsured 0.0138* (0.0059)
## Log(Assets) 0.0615*** (0.0079) 0.0599*** (0.0079)
## Cash Ratio -0.0269*** (0.0046) -0.0271*** (0.0046)
## Loan-to-Deposit -0.0063 (0.0065) -0.0060 (0.0065)
## Book Equity Ratio -0.0082. (0.0044) -0.0081. (0.0044)
## Wholesale Funding 0.0247*** (0.0070) 0.0239*** (0.0071)
## ROA 0.0003 (0.0049) 3.44e-5 (0.0049)
## __________________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 3,003 3,003
## R2 0.08595 0.08779
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_t3s, "Table_3S_OMO_Solvent",
title_text = "OMO Losses Only: Solvent Banks",
notes_text = "LPM with robust SEs. Solvent banks only.")
## Saved: Table_3S_OMO_Solvent.tex
models_t4s <- list(
"(1) Base" = run_one(df_btfp_int_sol, "btfp_pct", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(2) +Interaction" = run_one(df_btfp_int_sol, "btfp_pct", EXPL_MTM_INTER, controls = CONTROLS_MTM)
)
etable(models_t4s, fitstat = ~ n + r2, order = COEF_ORDER,
title = "Table 4-S: Intensive Margin — Solvent Banks (OLS)")
## (1) Base (2) +Interaction
## Dependent Var.: btfp_pct btfp_pct
##
## Constant 7.110*** (0.9645) 7.451*** (1.124)
## MTM Loss (z) -1.687 (1.545) -1.643 (1.502)
## Uninsured Leverage (z) -2.486 (1.742) -2.180 (1.535)
## MTM $\times$ Uninsured 1.751 (1.194)
## Log(Assets) 2.280 (1.595) 2.167 (1.490)
## Cash Ratio 2.284 (1.808) 2.677 (1.859)
## Loan-to-Deposit -3.775 (2.434) -3.314 (2.145)
## Book Equity Ratio 1.281 (1.181) 1.351 (1.242)
## Wholesale Funding 0.9530* (0.4574) 0.8395. (0.4719)
## ROA 0.6988 (1.035) 0.5524 (0.9404)
## ______________________ _________________ ________________
## S.E. type Heteroskeda.-rob. Heterosked.-rob.
## Observations 330 330
## R2 0.17019 0.18744
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_t4s, "Table_4S_Intensive_Solvent",
title_text = "Intensive Margin: Solvent Borrowers Only",
notes_text = "OLS with robust SEs. BTFP borrowers with AdjEquity $\\geq$ 0.")
## Saved: Table_4S_Intensive_Solvent.tex
models_t5s <- list(
"(1) Par Base" = run_one(df_btfp_sol, "btfp_acute", EXPL_PAR_BASE, controls = CONTROLS_MTM),
"(2) Par Inter" = run_one(df_btfp_sol, "btfp_acute", EXPL_PAR_INTER, controls = CONTROLS_MTM)
)
etable(models_t5s, fitstat = ~ n + r2, order = COEF_ORDER,
title = "Table 5-S: Par Benefit — Solvent Banks (LPM)")
## (1) Par Base (2) Par Inter
## Dependent Var.: btfp_acute btfp_acute
##
## Constant 0.1220*** (0.0061) 0.1220*** (0.0061)
## Uninsured Leverage (z) 0.0133* (0.0066) 0.0122. (0.0067)
## Par Benefit $\times$ Uninsured -0.0057 (0.0048)
## Par Benefit (z) 0.0013 (0.0041) 0.0008 (0.0043)
## Log(Assets) 0.0641*** (0.0080) 0.0646*** (0.0080)
## Cash Ratio -0.0300*** (0.0045) -0.0304*** (0.0045)
## Loan-to-Deposit -0.0132* (0.0059) -0.0133* (0.0059)
## Book Equity Ratio -0.0086. (0.0044) -0.0087. (0.0045)
## Wholesale Funding 0.0257*** (0.0071) 0.0257*** (0.0071)
## ROA -0.0014 (0.0048) -0.0009 (0.0049)
## ______________________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 3,003 3,003
## R2 0.08452 0.08500
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_t5s, "Table_5S_ParBenefit_Solvent",
title_text = "Par Benefit: Solvent Banks Only",
notes_text = "LPM with robust SEs. Par Benefit among solvent banks.")
## Saved: Table_5S_ParBenefit_Solvent.tex
models_t6s <- list(
"(1) Acute" = run_one(df_btfp_sol, "btfp_acute", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(2) Acute" = run_one(df_btfp_sol, "btfp_acute", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(3) Post" = run_one(df_btfp_post_sol, "btfp_post", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(4) Post" = run_one(df_btfp_post_sol, "btfp_post", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(5) Arb" = run_one(df_btfp_arb_sol, "btfp_arb", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(6) Arb" = run_one(df_btfp_arb_sol, "btfp_arb", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(7) Wind" = run_one(df_btfp_wind_sol, "btfp_wind", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(8) Wind" = run_one(df_btfp_wind_sol, "btfp_wind", EXPL_MTM_INTER, controls = CONTROLS_MTM)
)
t6s_extra <- list(
"__N(BTFP=1)" = c(sum(df_btfp_sol$btfp_acute), sum(df_btfp_sol$btfp_acute),
sum(df_btfp_post_sol$btfp_post), sum(df_btfp_post_sol$btfp_post),
sum(df_btfp_arb_sol$btfp_arb), sum(df_btfp_arb_sol$btfp_arb),
sum(df_btfp_wind_sol$btfp_wind), sum(df_btfp_wind_sol$btfp_wind)),
"__Subsample" = rep("Solvent", 8)
)
etable(models_t6s, fitstat = ~ n + r2, order = COEF_ORDER, extralines = t6s_extra,
title = "Table 6-S: BTFP Temporal — Solvent Banks (LPM)")
## (1) Acute (2) Acute
## Dependent Var.: btfp_acute btfp_acute
##
## Constant 0.1263*** (0.0064) 0.1303*** (0.0068)
## MTM Loss (z) 0.0193** (0.0062) 0.0262*** (0.0070)
## Uninsured Leverage (z) 0.0162* (0.0068) 0.0255** (0.0080)
## MTM $\times$ Uninsured 0.0185** (0.0059)
## Log(Assets) 0.0621*** (0.0080) 0.0610*** (0.0080)
## Cash Ratio -0.0236*** (0.0047) -0.0221*** (0.0047)
## Loan-to-Deposit -0.0111. (0.0058) -0.0091 (0.0057)
## Book Equity Ratio -0.0088* (0.0044) -0.0074. (0.0045)
## Wholesale Funding 0.0264*** (0.0070) 0.0257*** (0.0070)
## ROA 0.0011 (0.0049) -0.0005 (0.0049)
## ______________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 3,003 3,003
## R2 0.08692 0.08991
## N(BTFP=1) 330 330
## Subsample Solvent Solvent
##
## (3) Post (4) Post
## Dependent Var.: btfp_post btfp_post
##
## Constant 0.2331*** (0.0085) 0.2381*** (0.0088)
## MTM Loss (z) 0.0240** (0.0084) 0.0326*** (0.0094)
## Uninsured Leverage (z) 0.0136 (0.0087) 0.0253* (0.0102)
## MTM $\times$ Uninsured 0.0222** (0.0074)
## Log(Assets) 0.0734*** (0.0100) 0.0720*** (0.0100)
## Cash Ratio -0.0498*** (0.0069) -0.0476*** (0.0069)
## Loan-to-Deposit -0.0241** (0.0079) -0.0215** (0.0080)
## Book Equity Ratio -0.0182** (0.0066) -0.0163* (0.0066)
## Wholesale Funding 0.0168* (0.0085) 0.0156. (0.0085)
## ROA -0.0086 (0.0067) -0.0107 (0.0068)
## ______________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 2,742 2,742
## R2 0.08383 0.08636
## N(BTFP=1) 561 561
## Subsample Solvent Solvent
##
## (5) Arb (6) Arb
## Dependent Var.: btfp_arb btfp_arb
##
## Constant 0.1887*** (0.0069) 0.1908*** (0.0070)
## MTM Loss (z) 0.0192** (0.0073) 0.0225** (0.0077)
## Uninsured Leverage (z) 0.0147* (0.0068) 0.0204** (0.0078)
## MTM $\times$ Uninsured 0.0124* (0.0059)
## Log(Assets) 0.0520*** (0.0076) 0.0515*** (0.0076)
## Cash Ratio -0.0348*** (0.0059) -0.0338*** (0.0059)
## Loan-to-Deposit -0.0137* (0.0065) -0.0121. (0.0066)
## Book Equity Ratio -0.0063 (0.0059) -0.0050 (0.0059)
## Wholesale Funding 0.1052*** (0.0086) 0.1047*** (0.0086)
## ROA -0.0221*** (0.0060) -0.0233*** (0.0060)
## ______________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 3,256 3,256
## R2 0.14620 0.14712
## N(BTFP=1) 556 556
## Subsample Solvent Solvent
##
## (7) Wind (8) Wind
## Dependent Var.: btfp_wind btfp_wind
##
## Constant 0.0542*** (0.0040) 0.0543*** (0.0041)
## MTM Loss (z) 0.0003 (0.0043) 0.0004 (0.0045)
## Uninsured Leverage (z) 0.0103* (0.0041) 0.0105* (0.0046)
## MTM $\times$ Uninsured 0.0003 (0.0034)
## Log(Assets) -0.0019 (0.0044) -0.0019 (0.0044)
## Cash Ratio -0.0131*** (0.0038) -0.0131*** (0.0038)
## Loan-to-Deposit -0.0102* (0.0042) -0.0101* (0.0042)
## Book Equity Ratio -0.0025 (0.0035) -0.0025 (0.0035)
## Wholesale Funding 0.0499*** (0.0063) 0.0499*** (0.0063)
## ROA -0.0071. (0.0040) -0.0072. (0.0040)
## ______________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 3,482 3,482
## R2 0.05877 0.05878
## N(BTFP=1) 171 171
## Subsample Solvent Solvent
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_t6s, "Table_6S_Temporal_Solvent",
title_text = "BTFP Temporal: Solvent Banks Only",
notes_text = "LPM with robust SEs. Solvent: AdjEquity $\\geq$ 0.",
extra_lines = t6s_extra)
## Saved: Table_6S_Temporal_Solvent.tex
# ==============================================================================
# THRESHOLD GRADIENT: SOLVENT VS INSOLVENT SUBSAMPLES
# ==============================================================================
# Function to run gradient analysis on a specific subsample
run_gradient_analysis <- function(data, sub_name, color_fill) {
# 1. Create Terciles WITHIN this subsample
# (Ensures balanced groups for regression even if N is small)
df_grad <- data %>%
filter(!is.na(uninsured_lev_w)) %>%
mutate(
unins_tercile = cut(
uninsured_lev_w,
breaks = quantile(uninsured_lev_w, probs = c(0, 1/3, 2/3, 1), na.rm = TRUE),
labels = c("Low", "Med", "High"),
include.lowest = TRUE
)
)
# 2. Run Regressions (Low, Med, High)
models <- list(
"Low UL" = run_one(df_grad %>% filter(unins_tercile == "Low"), "btfp_acute", "mtm_total", controls = CONTROLS_MTM),
"Med UL" = run_one(df_grad %>% filter(unins_tercile == "Med"), "btfp_acute", "mtm_total", controls = CONTROLS_MTM),
"High UL" = run_one(df_grad %>% filter(unins_tercile == "High"), "btfp_acute", "mtm_total", controls = CONTROLS_MTM)
)
# 3. Print Table
cat(paste0("\n=== TABLE: THRESHOLD GRADIENT (", sub_name, ") ===\n"))
etable(models, fitstat = ~ n + r2, se.below = TRUE, digits = 4,
signif.code = c("***"=0.01, "**"=0.05, "*"=0.10),
title = paste0("Gradient: ", sub_name))
# 4. Save Table
save_etable(models, paste0("Table_Gradient_", sub_name),
title_text = paste0("Threshold Gradient: ", sub_name, " Banks"),
notes_text = paste0("Sample: ", sub_name, " banks only. Split by Uninsured Leverage terciles."))
# 5. Create Plot
plot_data <- map_dfr(names(models), function(name) {
tidy(models[[name]], conf.int = TRUE) %>%
filter(term == "mtm_total") %>%
mutate(Group = factor(name, levels = c("Low UL", "Med UL", "High UL")))
})
p <- ggplot(plot_data, aes(x = Group, y = estimate)) +
geom_bar(stat = "identity", fill = color_fill, width = 0.6, alpha = 0.9) +
geom_errorbar(aes(ymin = conf.low, ymax = conf.high), width = 0.2) +
geom_hline(yintercept = 0, linetype = "dashed", color = "grey50") +
geom_text(aes(label = round(estimate, 3)), vjust = -2.5, fontface = "bold") +
labs(title = paste0(sub_name, ": MTM Coefficient by Fragility"),
y = "MTM Beta", x = "") +
scale_y_continuous(limits = c(min(0, min(plot_data$conf.low)),
max(plot_data$conf.high) * 1.3)) +
theme_paper
return(p)
}
# --- RUN ANALYSIS FOR SOLVENT BANKS ---
# (Expectation: Strong gradient. Only fragile solvent banks run.)
plot_sol <- run_gradient_analysis(df_btfp_sol, "Solvent", "#2a9d8f")
##
## === TABLE: THRESHOLD GRADIENT (Solvent) ===
## Saved: Table_Gradient_Solvent.tex
# --- RUN ANALYSIS FOR INSOLVENT BANKS ---
# (Expectation: Flatter gradient? Insolvent banks borrow regardless of leverage?)
plot_ins <- run_gradient_analysis(df_btfp_ins, "Insolvent", "#e76f51")
##
## === TABLE: THRESHOLD GRADIENT (Insolvent) ===
## Saved: Table_Gradient_Insolvent.tex
# --- COMBINE AND SAVE PLOTS ---
combined_plot <- plot_sol + plot_ins +
plot_layout(ncol = 2) +
plot_annotation(
title = "Threshold Gradient: Solvent vs. Insolvent Banks",
subtitle = "Sensitivity to MTM Losses across Uninsured Leverage Terciles",
theme = theme(plot.title = element_text(face = "bold", size = 14))
)
print(combined_plot)
save_figure(combined_plot, "Fig_Threshold_Gradient_Split")
## Saved: Fig_Threshold_Gradient_Split.pdf
Subsample: Banks with Adjusted Equity < 0. Among insolvent banks, borrowing is expected regardless of fragility because the bank genuinely needs the money. The interaction term may be less informative here.
# ==============================================================================
# INSOLVENT SUBSAMPLE
# ==============================================================================
# Intensive margin
df_btfp_int_ins <- df_btfp_int %>% filter(mtm_insolvent == 1)
# Post-Acute
df_btfp_post_ins <- df_btfp_post_s %>% filter(mtm_insolvent == 1)
df_dw_post_ins <- df_dw_post_s %>% filter(mtm_insolvent == 1)
# Arbitrage
df_btfp_arb_ins <- df_btfp_arb_s %>% filter(mtm_insolvent == 1)
# Wind-down
df_btfp_wind_ins <- df_btfp_wind_s %>% filter(mtm_insolvent == 1)
cat("=== INSOLVENT SUBSAMPLE COUNTS ===\n")
## === INSOLVENT SUBSAMPLE COUNTS ===
cat("BTFP Acute:", nrow(df_btfp_ins), "(BTFP=1:", sum(df_btfp_ins$btfp_acute), ")\n")
## BTFP Acute: 734 (BTFP=1: 132 )
cat("DW Acute:", nrow(df_dw_ins), "(DW=1:", sum(df_dw_ins$dw_acute), ")\n")
## DW Acute: 679 (DW=1: 77 )
cat("Intensive:", nrow(df_btfp_int_ins), "\n")
## Intensive: 132
models_t2i <- list(
"(1) Acute" = run_one(df_btfp_ins, "btfp_acute", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(2) Acute" = run_one(df_btfp_ins, "btfp_acute", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(3) Arb" = run_one(df_btfp_arb_ins, "btfp_arb", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(4) Arb" = run_one(df_btfp_arb_ins, "btfp_arb", EXPL_MTM_INTER, controls = CONTROLS_MTM)
)
t2i_extra <- list(
"__N(BTFP=1)" = c(sum(df_btfp_ins$btfp_acute), sum(df_btfp_ins$btfp_acute),
sum(df_btfp_arb_ins$btfp_arb), sum(df_btfp_arb_ins$btfp_arb)),
"__Subsample" = rep("Insolvent", 4)
)
etable(models_t2i, fitstat = ~ n + r2, order = COEF_ORDER, extralines = t2i_extra,
title = "Table 2-I: BTFP Acute vs Arb — Insolvent Banks (LPM)")
## (1) Acute (2) Acute
## Dependent Var.: btfp_acute btfp_acute
##
## Constant 0.0017 (0.0544) 0.0168 (0.0541)
## MTM Loss (z) 0.0314 (0.0260) 0.0289 (0.0259)
## Uninsured Leverage (z) 0.0233 (0.0177) -0.0213 (0.0262)
## MTM $\times$ Uninsured 0.0443. (0.0226)
## Log(Assets) 0.1016*** (0.0207) 0.0995*** (0.0208)
## Cash Ratio -0.0599** (0.0203) -0.0588** (0.0202)
## Loan-to-Deposit 0.0143 (0.0196) 0.0176 (0.0197)
## Book Equity Ratio -0.1332** (0.0404) -0.1269** (0.0403)
## Wholesale Funding 0.0185 (0.0144) 0.0170 (0.0143)
## ROA -0.0341* (0.0146) -0.0344* (0.0145)
## ______________________ __________________ __________________
## S.E. type Heteroskedas.-rob. Heteroskedas.-rob.
## Observations 734 734
## R2 0.08892 0.09341
## N(BTFP=1) 132 132
## Subsample Insolvent Insolvent
##
## (3) Arb (4) Arb
## Dependent Var.: btfp_arb btfp_arb
##
## Constant 0.1688** (0.0593) 0.1782** (0.0596)
## MTM Loss (z) 0.0168 (0.0273) 0.0150 (0.0272)
## Uninsured Leverage (z) 0.0090 (0.0171) -0.0208 (0.0284)
## MTM $\times$ Uninsured 0.0273 (0.0229)
## Log(Assets) 0.0638** (0.0194) 0.0627** (0.0194)
## Cash Ratio -0.0750*** (0.0188) -0.0727*** (0.0188)
## Loan-to-Deposit 0.0515* (0.0215) 0.0530* (0.0215)
## Book Equity Ratio -0.0603 (0.0442) -0.0560 (0.0442)
## Wholesale Funding 0.0841*** (0.0154) 0.0834*** (0.0155)
## ROA -0.0281 (0.0191) -0.0292 (0.0192)
## ______________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 782 782
## R2 0.11858 0.11993
## N(BTFP=1) 210 210
## Subsample Insolvent Insolvent
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_t2i, "Table_2I_AcuteArb_Insolvent",
title_text = "BTFP Acute vs.\\ Arb: Insolvent Banks Only",
notes_text = "LPM with robust SEs. Insolvent: AdjEquity $<$ 0.",
extra_lines = t2i_extra)
## Saved: Table_2I_AcuteArb_Insolvent.tex
models_t3i <- list(
"(1) OMO Base" = run_one(df_btfp_ins, "btfp_acute", EXPL_OMO_BASE, controls = CONTROLS_MTM),
"(2) OMO Inter" = run_one(df_btfp_ins, "btfp_acute", EXPL_OMO_INTER, controls = CONTROLS_MTM)
)
etable(models_t3i, fitstat = ~ n + r2, order = COEF_ORDER,
title = "Table 3-I: OMO Losses — Insolvent Banks (LPM)")
## (1) OMO Base (2) OMO Inter
## Dependent Var.: btfp_acute btfp_acute
##
## Constant 0.0507 (0.0357) 0.0506 (0.0357)
## MTM Loss OMO (z) 0.0083 (0.0126) 0.0089 (0.0126)
## Uninsured Leverage (z) 0.0209 (0.0175) 0.0235 (0.0180)
## MTM OMO $\times$ Uninsured -0.0046 (0.0122)
## Log(Assets) 0.1015*** (0.0212) 0.1017*** (0.0212)
## Cash Ratio -0.0670*** (0.0196) -0.0673*** (0.0198)
## Loan-to-Deposit 0.0146 (0.0196) 0.0152 (0.0196)
## Book Equity Ratio -0.1122** (0.0386) -0.1126** (0.0386)
## Wholesale Funding 0.0173 (0.0143) 0.0171 (0.0143)
## ROA -0.0334* (0.0148) -0.0335* (0.0148)
## __________________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 734 734
## R2 0.08753 0.08772
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_t3i, "Table_3I_OMO_Insolvent",
title_text = "OMO Losses Only: Insolvent Banks",
notes_text = "LPM with robust SEs. Insolvent banks only.")
## Saved: Table_3I_OMO_Insolvent.tex
models_t4i <- list(
"(1) Base" = run_one(df_btfp_int_ins, "btfp_pct", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(2) +Interaction" = run_one(df_btfp_int_ins, "btfp_pct", EXPL_MTM_INTER, controls = CONTROLS_MTM)
)
etable(models_t4i, fitstat = ~ n + r2, order = COEF_ORDER,
title = "Table 4-I: Intensive Margin — Insolvent Banks (OLS)")
## (1) Base (2) +Interaction
## Dependent Var.: btfp_pct btfp_pct
##
## Constant -0.0052 (2.477) -0.0487 (2.329)
## MTM Loss (z) 1.100 (0.8181) 1.124 (0.8628)
## Uninsured Leverage (z) 0.3530 (0.8325) 0.4801 (1.476)
## MTM $\times$ Uninsured -0.1052 (1.412)
## Log(Assets) 0.9710 (0.7586) 0.9736 (0.7490)
## Cash Ratio 0.1516 (1.206) 0.1765 (1.241)
## Loan-to-Deposit -1.035 (1.062) -1.044 (1.129)
## Book Equity Ratio -3.118 (2.041) -3.141 (1.935)
## Wholesale Funding -0.2644 (0.3471) -0.2613 (0.3581)
## ROA -2.127 (1.373) -2.117 (1.403)
## ______________________ ________________ ________________
## S.E. type Heterosked.-rob. Heterosked.-rob.
## Observations 132 132
## R2 0.12500 0.12507
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_t4i, "Table_4I_Intensive_Insolvent",
title_text = "Intensive Margin: Insolvent Borrowers Only",
notes_text = "OLS with robust SEs. BTFP borrowers with AdjEquity $<$ 0.")
## Saved: Table_4I_Intensive_Insolvent.tex
models_t5i <- list(
"(1) Par Base" = run_one(df_btfp_ins, "btfp_acute", EXPL_PAR_BASE, controls = CONTROLS_MTM),
"(2) Par Inter" = run_one(df_btfp_ins, "btfp_acute", EXPL_PAR_INTER, controls = CONTROLS_MTM)
)
etable(models_t5i, fitstat = ~ n + r2, order = COEF_ORDER,
title = "Table 5-I: Par Benefit — Insolvent Banks (LPM)")
## (1) Par Base (2) Par Inter
## Dependent Var.: btfp_acute btfp_acute
##
## Constant 0.0493 (0.0355) 0.0500 (0.0356)
## Uninsured Leverage (z) 0.0207 (0.0175) 0.0230 (0.0173)
## Par Benefit $\times$ Uninsured -0.0182 (0.0158)
## Par Benefit (z) 0.0022 (0.0150) 0.0011 (0.0152)
## Log(Assets) 0.1041*** (0.0206) 0.1062*** (0.0205)
## Cash Ratio -0.0675*** (0.0197) -0.0661*** (0.0198)
## Loan-to-Deposit 0.0123 (0.0199) 0.0119 (0.0199)
## Book Equity Ratio -0.1154** (0.0381) -0.1156** (0.0381)
## Wholesale Funding 0.0175 (0.0143) 0.0169 (0.0144)
## ROA -0.0353* (0.0147) -0.0353* (0.0147)
## ______________________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 734 734
## R2 0.08693 0.08790
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_t5i, "Table_5I_ParBenefit_Insolvent",
title_text = "Par Benefit: Insolvent Banks Only",
notes_text = "LPM with robust SEs. Par Benefit among insolvent banks.")
## Saved: Table_5I_ParBenefit_Insolvent.tex
models_t6i <- list(
"(1) Acute" = run_one(df_btfp_ins, "btfp_acute", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(2) Acute" = run_one(df_btfp_ins, "btfp_acute", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(3) Post" = run_one(df_btfp_post_ins, "btfp_post", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(4) Post" = run_one(df_btfp_post_ins, "btfp_post", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(5) Arb" = run_one(df_btfp_arb_ins, "btfp_arb", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(6) Arb" = run_one(df_btfp_arb_ins, "btfp_arb", EXPL_MTM_INTER, controls = CONTROLS_MTM),
"(7) Wind" = run_one(df_btfp_wind_ins, "btfp_wind", EXPL_MTM_BASE, controls = CONTROLS_MTM),
"(8) Wind" = run_one(df_btfp_wind_ins, "btfp_wind", EXPL_MTM_INTER, controls = CONTROLS_MTM)
)
t6i_extra <- list(
"__N(BTFP=1)" = c(sum(df_btfp_ins$btfp_acute), sum(df_btfp_ins$btfp_acute),
sum(df_btfp_post_ins$btfp_post), sum(df_btfp_post_ins$btfp_post),
sum(df_btfp_arb_ins$btfp_arb), sum(df_btfp_arb_ins$btfp_arb),
sum(df_btfp_wind_ins$btfp_wind), sum(df_btfp_wind_ins$btfp_wind)),
"__Subsample" = rep("Insolvent", 8)
)
etable(models_t6i, fitstat = ~ n + r2, order = COEF_ORDER, extralines = t6i_extra,
title = "Table 6-I: BTFP Temporal — Insolvent Banks (LPM)")
## (1) Acute (2) Acute
## Dependent Var.: btfp_acute btfp_acute
##
## Constant 0.0017 (0.0544) 0.0168 (0.0541)
## MTM Loss (z) 0.0314 (0.0260) 0.0289 (0.0259)
## Uninsured Leverage (z) 0.0233 (0.0177) -0.0213 (0.0262)
## MTM $\times$ Uninsured 0.0443. (0.0226)
## Log(Assets) 0.1016*** (0.0207) 0.0995*** (0.0208)
## Cash Ratio -0.0599** (0.0203) -0.0588** (0.0202)
## Loan-to-Deposit 0.0143 (0.0196) 0.0176 (0.0197)
## Book Equity Ratio -0.1332** (0.0404) -0.1269** (0.0403)
## Wholesale Funding 0.0185 (0.0144) 0.0170 (0.0143)
## ROA -0.0341* (0.0146) -0.0344* (0.0145)
## ______________________ __________________ __________________
## S.E. type Heteroskedas.-rob. Heteroskedas.-rob.
## Observations 734 734
## R2 0.08892 0.09341
## N(BTFP=1) 132 132
## Subsample Insolvent Insolvent
##
## (3) Post (4) Post
## Dependent Var.: btfp_post btfp_post
##
## Constant 0.0955 (0.0679) 0.0975 (0.0687)
## MTM Loss (z) 0.0270 (0.0313) 0.0266 (0.0313)
## Uninsured Leverage (z) 0.0257 (0.0214) 0.0202 (0.0340)
## MTM $\times$ Uninsured 0.0054 (0.0275)
## Log(Assets) 0.0720** (0.0253) 0.0718** (0.0253)
## Cash Ratio -0.1026*** (0.0247) -0.1026*** (0.0247)
## Loan-to-Deposit 0.0145 (0.0253) 0.0148 (0.0254)
## Book Equity Ratio -0.1556** (0.0516) -0.1548** (0.0519)
## Wholesale Funding 0.0104 (0.0167) 0.0102 (0.0167)
## ROA -0.0294 (0.0204) -0.0294 (0.0204)
## ______________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 695 695
## R2 0.06098 0.06103
## N(BTFP=1) 214 214
## Subsample Insolvent Insolvent
##
## (5) Arb (6) Arb
## Dependent Var.: btfp_arb btfp_arb
##
## Constant 0.1688** (0.0593) 0.1782** (0.0596)
## MTM Loss (z) 0.0168 (0.0273) 0.0150 (0.0272)
## Uninsured Leverage (z) 0.0090 (0.0171) -0.0208 (0.0284)
## MTM $\times$ Uninsured 0.0273 (0.0229)
## Log(Assets) 0.0638** (0.0194) 0.0627** (0.0194)
## Cash Ratio -0.0750*** (0.0188) -0.0727*** (0.0188)
## Loan-to-Deposit 0.0515* (0.0215) 0.0530* (0.0215)
## Book Equity Ratio -0.0603 (0.0442) -0.0560 (0.0442)
## Wholesale Funding 0.0841*** (0.0154) 0.0834*** (0.0155)
## ROA -0.0281 (0.0191) -0.0292 (0.0192)
## ______________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 782 782
## R2 0.11858 0.11993
## N(BTFP=1) 210 210
## Subsample Insolvent Insolvent
##
## (7) Wind (8) Wind
## Dependent Var.: btfp_wind btfp_wind
##
## Constant 0.0910 (0.0657) 0.0979 (0.0682)
## MTM Loss (z) -0.0135 (0.0279) -0.0153 (0.0283)
## Uninsured Leverage (z) -0.0076 (0.0144) -0.0253 (0.0283)
## MTM $\times$ Uninsured 0.0146 (0.0220)
## Log(Assets) 0.0140 (0.0152) 0.0137 (0.0152)
## Cash Ratio 0.0100 (0.0172) 0.0112 (0.0172)
## Loan-to-Deposit 0.0033 (0.0155) 0.0043 (0.0157)
## Book Equity Ratio -0.0193 (0.0402) -0.0170 (0.0408)
## Wholesale Funding 0.0592*** (0.0140) 0.0587*** (0.0140)
## ROA -0.0005 (0.0155) 0.0002 (0.0157)
## ______________________ __________________ __________________
## S.E. type Heteroskedas.-rob. Heteroskedas.-rob.
## Observations 561 561
## R2 0.06076 0.06151
## N(BTFP=1) 58 58
## Subsample Insolvent Insolvent
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_t6i, "Table_6I_Temporal_Insolvent",
title_text = "BTFP Temporal: Insolvent Banks Only",
notes_text = "LPM with robust SEs. Insolvent: AdjEquity $<$ 0.",
extra_lines = t6i_extra)
## Saved: Table_6I_Temporal_Insolvent.tex
Repeat the main extensive-margin specifications using Logit
instead of LPM. This checks whether the linear probability
model results are robust to a nonlinear specification. Coefficients are
in log-odds; marginal effects can be computed via
marginaleffects.
# ==============================================================================
# LOGIT TABLE 1: EXTENSIVE MARGIN — BTFP (ACUTE)
# ==============================================================================
models_logit_t1 <- list(
"(1) All" = run_one(df_btfp_s, "btfp_acute", EXPL_MTM_BASE, "logit", CONTROLS_MTM),
"(2) All" = run_one(df_btfp_s, "btfp_acute", EXPL_MTM_INTER, "logit", CONTROLS_MTM),
"(3) Solvent" = run_one(df_btfp_sol, "btfp_acute", EXPL_MTM_BASE, "logit", CONTROLS_MTM),
"(4) Solvent" = run_one(df_btfp_sol, "btfp_acute", EXPL_MTM_INTER, "logit", CONTROLS_MTM),
"(5) Insolvent" = run_one(df_btfp_ins, "btfp_acute", EXPL_MTM_BASE, "logit", CONTROLS_MTM),
"(6) Insolvent" = run_one(df_btfp_ins, "btfp_acute", EXPL_MTM_INTER, "logit", CONTROLS_MTM)
)
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
logit_t1_extra <- list(
"__N(BTFP=1)" = c(sum(df_btfp_s$btfp_acute), sum(df_btfp_s$btfp_acute),
sum(df_btfp_sol$btfp_acute), sum(df_btfp_sol$btfp_acute),
sum(df_btfp_ins$btfp_acute), sum(df_btfp_ins$btfp_acute)),
"__Subsample" = c("All", "All", "Solvent", "Solvent", "Insolvent", "Insolvent"),
"__Method" = rep("Logit", 6)
)
etable(models_logit_t1, fitstat = ~ n + pr2,
order = COEF_ORDER, extralines = logit_t1_extra,
title = "Logit Table 1: BTFP Extensive Margin — Acute Period")
## (1) All (2) All
## Dependent Var.: btfp_acute btfp_acute
##
## Constant -2.330*** (0.0706) -2.320*** (0.0711)
## MTM Loss (z) 0.2177*** (0.0619) 0.2141*** (0.0622)
## Uninsured Leverage (z) 0.2129*** (0.0625) 0.2089*** (0.0626)
## MTM $\times$ Uninsured 0.0461 (0.0569)
## Log(Assets) 0.5738*** (0.0620) 0.5743*** (0.0619)
## Cash Ratio -0.5236*** (0.1050) -0.5171*** (0.1050)
## Loan-to-Deposit -0.0064 (0.0669) 0.0009 (0.0664)
## Book Equity Ratio -0.3338*** (0.0817) -0.3289*** (0.0825)
## Wholesale Funding 0.1831*** (0.0466) 0.1826*** (0.0465)
## ROA -0.0127 (0.0628) -0.0164 (0.0629)
## ______________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 3,737 3,737
## Pseudo R2 0.13099 0.13124
## N(BTFP=1) 462 462
## Subsample All All
## Method Logit Logit
##
## (3) Solvent (4) Solvent
## Dependent Var.: btfp_acute btfp_acute
##
## Constant -2.294*** (0.0751) -2.286*** (0.0762)
## MTM Loss (z) 0.2902*** (0.0801) 0.2888*** (0.0802)
## Uninsured Leverage (z) 0.2352** (0.0731) 0.2438*** (0.0737)
## MTM $\times$ Uninsured 0.0401 (0.0737)
## Log(Assets) 0.5523*** (0.0715) 0.5507*** (0.0713)
## Cash Ratio -0.4974*** (0.1101) -0.4927*** (0.1102)
## Loan-to-Deposit -0.0307 (0.0777) -0.0272 (0.0772)
## Book Equity Ratio -0.3205*** (0.0973) -0.3147** (0.0986)
## Wholesale Funding 0.2074*** (0.0552) 0.2065*** (0.0551)
## ROA 0.0501 (0.0738) 0.0477 (0.0739)
## ______________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 3,003 3,003
## Pseudo R2 0.13709 0.13724
## N(BTFP=1) 330 330
## Subsample Solvent Solvent
## Method Logit Logit
##
## (5) Insolvent (6) Insolvent
## Dependent Var.: btfp_acute btfp_acute
##
## Constant -3.096*** (0.4421) -2.971*** (0.4444)
## MTM Loss (z) 0.1991 (0.1905) 0.1508 (0.1928)
## Uninsured Leverage (z) 0.1866 (0.1247) -0.1789 (0.2298)
## MTM $\times$ Uninsured 0.3453. (0.1894)
## Log(Assets) 0.6566*** (0.1320) 0.6536*** (0.1350)
## Cash Ratio -0.6931* (0.2953) -0.6784* (0.2883)
## Loan-to-Deposit 0.1132 (0.1524) 0.1478 (0.1562)
## Book Equity Ratio -0.9912** (0.3211) -0.9606** (0.3262)
## Wholesale Funding 0.1077 (0.0862) 0.1033 (0.0848)
## ROA -0.2328. (0.1188) -0.2429* (0.1219)
## ______________________ __________________ __________________
## S.E. type Heteroskedas.-rob. Heteroskedas.-rob.
## Observations 734 734
## Pseudo R2 0.09733 0.10257
## N(BTFP=1) 132 132
## Subsample Insolvent Insolvent
## Method Logit Logit
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_logit_t1, "Logit_Table_1_BTFP",
title_text = "Logit: BTFP Extensive Margin (Acute Period)",
notes_text = "Logit with robust SEs. Coefficients in log-odds. Sample: BTFP borrowers vs. pure non-borrowers.",
fitstat_use = ~ n + pr2, extra_lines = logit_t1_extra)
## Saved: Logit_Table_1_BTFP.tex
# ==============================================================================
# LOGIT TABLE 1-DW: DW (ACUTE)
# ==============================================================================
models_logit_t1_dw <- list(
"(1) All" = run_one(df_dw_s, "dw_acute", EXPL_MTM_BASE, "logit", CONTROLS_MTM),
"(2) All" = run_one(df_dw_s, "dw_acute", EXPL_MTM_INTER, "logit", CONTROLS_MTM),
"(3) Solvent" = run_one(df_dw_sol, "dw_acute", EXPL_MTM_BASE, "logit", CONTROLS_MTM),
"(4) Solvent" = run_one(df_dw_sol, "dw_acute", EXPL_MTM_INTER, "logit", CONTROLS_MTM),
"(5) Insolvent" = run_one(df_dw_ins, "dw_acute", EXPL_MTM_BASE, "logit", CONTROLS_MTM),
"(6) Insolvent" = run_one(df_dw_ins, "dw_acute", EXPL_MTM_INTER, "logit", CONTROLS_MTM)
)
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
logit_t1_dw_extra <- list(
"__N(DW=1)" = c(sum(df_dw_s$dw_acute), sum(df_dw_s$dw_acute),
sum(df_dw_sol$dw_acute), sum(df_dw_sol$dw_acute),
sum(df_dw_ins$dw_acute), sum(df_dw_ins$dw_acute)),
"__Method" = rep("Logit", 6)
)
etable(models_logit_t1_dw, fitstat = ~ n + pr2,
order = COEF_ORDER, extralines = logit_t1_dw_extra,
title = "Logit Table 1-DW: DW Extensive Margin — Acute Period")
## (1) All (2) All (3) Solvent
## Dependent Var.: dw_acute dw_acute dw_acute
##
## Constant -2.441*** (0.0675) -2.426*** (0.0672) -2.373*** (0.0759)
## MTM Loss (z) 0.2034** (0.0675) 0.1944** (0.0679) 0.3046*** (0.0807)
## Uninsured Leverage (z) 0.1554* (0.0615) 0.1603** (0.0615) 0.1400* (0.0689)
## MTM $\times$ Uninsured 0.0832 (0.0535)
## Log(Assets) 0.7773*** (0.0614) 0.7765*** (0.0615) 0.7738*** (0.0688)
## Cash Ratio -0.0996 (0.0886) -0.0884 (0.0886) -0.0554 (0.0913)
## Loan-to-Deposit 0.0802 (0.0732) 0.0905 (0.0735) 0.0648 (0.0822)
## Book Equity Ratio -0.1752* (0.0792) -0.1668* (0.0795) -0.2289* (0.0942)
## Wholesale Funding 0.1731** (0.0531) 0.1717** (0.0531) 0.1761** (0.0626)
## ROA 0.0450 (0.0658) 0.0380 (0.0660) 0.0913 (0.0728)
## ______________________ __________________ __________________ __________________
## S.E. type Heteroskedas.-rob. Heteroskedas.-rob. Heteroskedas.-rob.
## Observations 3,668 3,668 2,989
## Pseudo R2 0.13366 0.13452 0.14188
## N(DW=1) 393 393 316
## Method Logit Logit Logit
##
## (4) Solvent (5) Insolvent (6) Insolvent
## Dependent Var.: dw_acute dw_acute dw_acute
##
## Constant -2.363*** (0.0766) -3.356*** (0.5644) -3.181*** (0.5717)
## MTM Loss (z) 0.3012*** (0.0808) 0.1373 (0.2546) 0.0507 (0.2579)
## Uninsured Leverage (z) 0.1569* (0.0721) 0.2542. (0.1523) -0.1630 (0.2776)
## MTM $\times$ Uninsured 0.0578 (0.0652) 0.4016. (0.2221)
## Log(Assets) 0.7709*** (0.0690) 0.8253*** (0.1493) 0.8304*** (0.1565)
## Cash Ratio -0.0491 (0.0911) -0.5642 (0.3743) -0.5283 (0.3587)
## Loan-to-Deposit 0.0684 (0.0823) 0.1490 (0.1871) 0.1812 (0.1898)
## Book Equity Ratio -0.2211* (0.0940) -0.7780* (0.3952) -0.7438. (0.4101)
## Wholesale Funding 0.1744** (0.0625) 0.1435 (0.1054) 0.1468 (0.1069)
## ROA 0.0871 (0.0730) -0.1884 (0.1662) -0.1988 (0.1676)
## ______________________ __________________ __________________ __________________
## S.E. type Heteroskedas.-rob. Heteroskedas.-rob. Heteroskedas.-rob.
## Observations 2,989 679 679
## Pseudo R2 0.14222 0.12324 0.13058
## N(DW=1) 316 77 77
## Method Logit Logit Logit
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_logit_t1_dw, "Logit_Table_1DW",
title_text = "Logit: DW Extensive Margin (Acute Period)",
notes_text = "Logit with robust SEs.",
fitstat_use = ~ n + pr2, extra_lines = logit_t1_dw_extra)
## Saved: Logit_Table_1DW.tex
# ==============================================================================
# LOGIT TABLE 1-FHLB: FHLB (ACUTE)
# ==============================================================================
models_logit_t1_fhlb <- list(
"(1) All" = run_one(df_fhlb_s, "fhlb_user", EXPL_MTM_BASE, "logit", CONTROLS_MTM),
"(2) All" = run_one(df_fhlb_s, "fhlb_user", EXPL_MTM_INTER, "logit", CONTROLS_MTM),
"(3) Solvent" = run_one(df_fhlb_sol, "fhlb_user", EXPL_MTM_BASE, "logit", CONTROLS_MTM),
"(4) Solvent" = run_one(df_fhlb_sol, "fhlb_user", EXPL_MTM_INTER, "logit", CONTROLS_MTM),
"(5) Insolvent" = run_one(df_fhlb_ins, "fhlb_user", EXPL_MTM_BASE, "logit", CONTROLS_MTM),
"(6) Insolvent" = run_one(df_fhlb_ins, "fhlb_user", EXPL_MTM_INTER, "logit", CONTROLS_MTM)
)
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
logit_t1_fhlb_extra <- list(
"__N(FHLB=1)" = c(sum(df_fhlb_s$fhlb_user), sum(df_fhlb_s$fhlb_user),
sum(df_fhlb_sol$fhlb_user), sum(df_fhlb_sol$fhlb_user),
sum(df_fhlb_ins$fhlb_user), sum(df_fhlb_ins$fhlb_user)),
"__Method" = rep("Logit", 6)
)
etable(models_logit_t1_fhlb, fitstat = ~ n + pr2,
order = COEF_ORDER, extralines = logit_t1_fhlb_extra,
title = "Logit Table 1-FHLB: FHLB Extensive Margin — Acute Period")
## (1) All (2) All
## Dependent Var.: fhlb_user fhlb_user
##
## Constant -2.631*** (0.0718) -2.653*** (0.0736)
## MTM Loss (z) 0.0801 (0.0692) 0.0597 (0.0712)
## Uninsured Leverage (z) 0.1342* (0.0650) 0.1157. (0.0673)
## MTM $\times$ Uninsured -0.0996. (0.0600)
## Log(Assets) 0.2474*** (0.0709) 0.2483*** (0.0710)
## Cash Ratio -0.6115*** (0.1089) -0.6259*** (0.1088)
## Loan-to-Deposit 0.3964*** (0.0762) 0.3824*** (0.0764)
## Book Equity Ratio 0.1100. (0.0658) 0.0912 (0.0661)
## Wholesale Funding 0.0178 (0.0633) 0.0193 (0.0635)
## ROA -0.1163. (0.0649) -0.1055 (0.0654)
## ______________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 3,577 3,577
## Pseudo R2 0.07943 0.08059
## N(FHLB=1) 302 302
## Method Logit Logit
##
## (3) Solvent (4) Solvent
## Dependent Var.: fhlb_user fhlb_user
##
## Constant -2.610*** (0.0824) -2.642*** (0.0847)
## MTM Loss (z) 0.0574 (0.0791) 0.0248 (0.0816)
## Uninsured Leverage (z) 0.1308. (0.0686) 0.0826 (0.0758)
## MTM $\times$ Uninsured -0.1290. (0.0688)
## Log(Assets) 0.2546*** (0.0755) 0.2600*** (0.0759)
## Cash Ratio -0.6393*** (0.1100) -0.6567*** (0.1096)
## Loan-to-Deposit 0.3660*** (0.0839) 0.3544*** (0.0842)
## Book Equity Ratio 0.0817 (0.0739) 0.0541 (0.0752)
## Wholesale Funding 0.0203 (0.0692) 0.0230 (0.0694)
## ROA -0.1183. (0.0698) -0.1075 (0.0703)
## ______________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 2,930 2,930
## Pseudo R2 0.08354 0.08529
## N(FHLB=1) 257 257
## Method Logit Logit
##
## (5) Insolvent (6) Insolvent
## Dependent Var.: fhlb_user fhlb_user
##
## Constant -2.981*** (0.7069) -2.970*** (0.7213)
## MTM Loss (z) 0.5439 (0.3479) 0.5422 (0.3471)
## Uninsured Leverage (z) 0.1727 (0.2121) 0.1234 (0.4998)
## MTM $\times$ Uninsured 0.0399 (0.3285)
## Log(Assets) 0.1371 (0.1968) 0.1367 (0.1969)
## Cash Ratio -0.4029 (0.4686) -0.4008 (0.4750)
## Loan-to-Deposit 0.3649* (0.1858) 0.3667* (0.1852)
## Book Equity Ratio 0.3470 (0.4489) 0.3506 (0.4508)
## Wholesale Funding 0.0125 (0.1596) 0.0116 (0.1566)
## ROA -0.1054 (0.1800) -0.1070 (0.1785)
## ______________________ __________________ __________________
## S.E. type Heteroskedas.-rob. Heteroskedas.-rob.
## Observations 647 647
## Pseudo R2 0.06492 0.06497
## N(FHLB=1) 45 45
## Method Logit Logit
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_logit_t1_fhlb, "Logit_Table_1FHLB",
title_text = "Logit: FHLB Extensive Margin (Acute Period)",
notes_text = "Logit with robust SEs.",
fitstat_use = ~ n + pr2, extra_lines = logit_t1_fhlb_extra)
## Saved: Logit_Table_1FHLB.tex
# ==============================================================================
# LOGIT TABLE 2: ACUTE VS ARB
# ==============================================================================
models_logit_t2 <- list(
"(1) Acute" = run_one(df_btfp_s, "btfp_acute", EXPL_MTM_BASE, "logit", CONTROLS_MTM),
"(2) Acute" = run_one(df_btfp_s, "btfp_acute", EXPL_MTM_INTER, "logit", CONTROLS_MTM),
"(3) Arb" = run_one(df_btfp_arb_s, "btfp_arb", EXPL_MTM_BASE, "logit", CONTROLS_MTM),
"(4) Arb" = run_one(df_btfp_arb_s, "btfp_arb", EXPL_MTM_INTER, "logit", CONTROLS_MTM)
)
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 17 observations removed because of NA values (RHS: 17).
## NOTE: 17 observations removed because of NA values (RHS: 17).
etable(models_logit_t2, fitstat = ~ n + pr2, order = COEF_ORDER,
title = "Logit Table 2: BTFP Acute vs Arb")
## (1) Acute (2) Acute
## Dependent Var.: btfp_acute btfp_acute
##
## Constant -2.330*** (0.0706) -2.320*** (0.0711)
## MTM Loss (z) 0.2177*** (0.0619) 0.2141*** (0.0622)
## Uninsured Leverage (z) 0.2129*** (0.0625) 0.2089*** (0.0626)
## MTM $\times$ Uninsured 0.0461 (0.0569)
## Log(Assets) 0.5738*** (0.0620) 0.5743*** (0.0619)
## Cash Ratio -0.5236*** (0.1050) -0.5171*** (0.1050)
## Loan-to-Deposit -0.0064 (0.0669) 0.0009 (0.0664)
## Book Equity Ratio -0.3338*** (0.0817) -0.3289*** (0.0825)
## Wholesale Funding 0.1831*** (0.0466) 0.1826*** (0.0465)
## ROA -0.0127 (0.0628) -0.0164 (0.0629)
## ______________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 3,737 3,737
## Pseudo R2 0.13099 0.13124
##
## (3) Arb (4) Arb
## Dependent Var.: btfp_arb btfp_arb
##
## Constant -1.802*** (0.0529) -1.805*** (0.0533)
## MTM Loss (z) 0.1648** (0.0526) 0.1648** (0.0526)
## Uninsured Leverage (z) 0.1416** (0.0509) 0.1433** (0.0510)
## MTM $\times$ Uninsured -0.0141 (0.0438)
## Log(Assets) 0.3645*** (0.0488) 0.3638*** (0.0488)
## Cash Ratio -0.5258*** (0.0777) -0.5279*** (0.0777)
## Loan-to-Deposit 0.0327 (0.0540) 0.0314 (0.0543)
## Book Equity Ratio -0.1666** (0.0588) -0.1688** (0.0592)
## Wholesale Funding 0.5459*** (0.0382) 0.5463*** (0.0382)
## ROA -0.2242*** (0.0581) -0.2226*** (0.0583)
## ______________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 4,038 4,038
## Pseudo R2 0.15311 0.15314
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_logit_t2, "Logit_Table_2_AcuteArb",
title_text = "Logit: BTFP Acute vs.\\ Arbitrage",
notes_text = "Logit with robust SEs.",
fitstat_use = ~ n + pr2)
## Saved: Logit_Table_2_AcuteArb.tex
# ==============================================================================
# LOGIT TABLE 3: OMO ONLY
# ==============================================================================
models_logit_t3 <- list(
"(1) OMO Base" = run_one(df_btfp_s, "btfp_acute", EXPL_OMO_BASE, "logit", CONTROLS_MTM),
"(2) OMO Inter" = run_one(df_btfp_s, "btfp_acute", EXPL_OMO_INTER, "logit", CONTROLS_MTM)
)
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
etable(models_logit_t3, fitstat = ~ n + pr2, order = COEF_ORDER,
title = "Logit Table 3: OMO Losses Only")
## (1) OMO Base (2) OMO Inter
## Dependent Var.: btfp_acute btfp_acute
##
## Constant -2.321*** (0.0706) -2.321*** (0.0706)
## MTM Loss OMO (z) 0.0853 (0.0548) 0.0848 (0.0562)
## Uninsured Leverage (z) 0.1806** (0.0610) 0.1803** (0.0620)
## MTM OMO $\times$ Uninsured 0.0019 (0.0444)
## Log(Assets) 0.5601*** (0.0640) 0.5600*** (0.0638)
## Cash Ratio -0.6052*** (0.1032) -0.6051*** (0.1032)
## Loan-to-Deposit -0.0199 (0.0720) -0.0198 (0.0720)
## Book Equity Ratio -0.3769*** (0.0829) -0.3769*** (0.0830)
## Wholesale Funding 0.1697*** (0.0465) 0.1696*** (0.0465)
## ROA -0.0283 (0.0635) -0.0284 (0.0635)
## __________________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 3,737 3,737
## Pseudo R2 0.12784 0.12784
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_logit_t3, "Logit_Table_3_OMO",
title_text = "Logit: OMO Losses Only",
notes_text = "Logit with robust SEs.",
fitstat_use = ~ n + pr2)
## Saved: Logit_Table_3_OMO.tex
# ==============================================================================
# LOGIT TABLE 5: PAR BENEFIT
# ==============================================================================
models_logit_t5 <- list(
"(1) Par Base" = run_one(df_btfp_s, "btfp_acute", EXPL_PAR_BASE, "logit", CONTROLS_MTM),
"(2) Par Inter" = run_one(df_btfp_s, "btfp_acute", EXPL_PAR_INTER, "logit", CONTROLS_MTM)
)
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
etable(models_logit_t5, fitstat = ~ n + pr2, order = COEF_ORDER,
title = "Logit Table 5: Par Benefit")
## (1) Par Base (2) Par Inter
## Dependent Var.: btfp_acute btfp_acute
##
## Constant -2.323*** (0.0708) -2.348*** (0.0729)
## Uninsured Leverage (z) 0.1752** (0.0604) 0.1838** (0.0617)
## Par Benefit $\times$ Uninsured -0.1938** (0.0695)
## Par Benefit (z) -0.0042 (0.0645) 0.0667 (0.0779)
## Log(Assets) 0.5866*** (0.0616) 0.6033*** (0.0626)
## Cash Ratio -0.6239*** (0.1021) -0.6423*** (0.1026)
## Loan-to-Deposit -0.0627 (0.0650) -0.0699 (0.0662)
## Book Equity Ratio -0.3861*** (0.0836) -0.3899*** (0.0843)
## Wholesale Funding 0.1753*** (0.0464) 0.1726*** (0.0463)
## ROA -0.0416 (0.0629) -0.0331 (0.0628)
## ______________________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 3,737 3,737
## Pseudo R2 0.12701 0.13053
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_logit_t5, "Logit_Table_5_Par",
title_text = "Logit: Par Benefit",
notes_text = "Logit with robust SEs.",
fitstat_use = ~ n + pr2)
## Saved: Logit_Table_5_Par.tex
# ==============================================================================
# LOGIT TABLE 6: TEMPORAL — BTFP
# ==============================================================================
models_logit_t6 <- list(
"(1) Acute" = run_one(df_btfp_s, "btfp_acute", EXPL_MTM_BASE, "logit", CONTROLS_MTM),
"(2) Acute" = run_one(df_btfp_s, "btfp_acute", EXPL_MTM_INTER, "logit", CONTROLS_MTM),
"(3) Post" = run_one(df_btfp_post_s, "btfp_post", EXPL_MTM_BASE, "logit", CONTROLS_MTM),
"(4) Post" = run_one(df_btfp_post_s, "btfp_post", EXPL_MTM_INTER, "logit", CONTROLS_MTM),
"(5) Arb" = run_one(df_btfp_arb_s, "btfp_arb", EXPL_MTM_BASE, "logit", CONTROLS_MTM),
"(6) Arb" = run_one(df_btfp_arb_s, "btfp_arb", EXPL_MTM_INTER, "logit", CONTROLS_MTM),
"(7) Wind" = run_one(df_btfp_wind_s, "btfp_wind", EXPL_MTM_BASE, "logit", CONTROLS_MTM),
"(8) Wind" = run_one(df_btfp_wind_s, "btfp_wind", EXPL_MTM_INTER, "logit", CONTROLS_MTM)
)
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 17 observations removed because of NA values (RHS: 17).
## NOTE: 17 observations removed because of NA values (RHS: 17).
## NOTE: 18 observations removed because of NA values (RHS: 18).
## NOTE: 18 observations removed because of NA values (RHS: 18).
logit_t6_extra <- list(
"__N(BTFP=1)" = c(sum(df_btfp_s$btfp_acute), sum(df_btfp_s$btfp_acute),
sum(df_btfp_post_s$btfp_post), sum(df_btfp_post_s$btfp_post),
sum(df_btfp_arb_s$btfp_arb), sum(df_btfp_arb_s$btfp_arb),
sum(df_btfp_wind_s$btfp_wind), sum(df_btfp_wind_s$btfp_wind)),
"__Method" = rep("Logit", 8)
)
etable(models_logit_t6, fitstat = ~ n + pr2,
order = COEF_ORDER, extralines = logit_t6_extra,
title = "Logit Table 6: BTFP Temporal")
## (1) Acute (2) Acute
## Dependent Var.: btfp_acute btfp_acute
##
## Constant -2.330*** (0.0706) -2.320*** (0.0711)
## MTM Loss (z) 0.2177*** (0.0619) 0.2141*** (0.0622)
## Uninsured Leverage (z) 0.2129*** (0.0625) 0.2089*** (0.0626)
## MTM $\times$ Uninsured 0.0461 (0.0569)
## Log(Assets) 0.5738*** (0.0620) 0.5743*** (0.0619)
## Cash Ratio -0.5236*** (0.1050) -0.5171*** (0.1050)
## Loan-to-Deposit -0.0064 (0.0669) 0.0009 (0.0664)
## Book Equity Ratio -0.3338*** (0.0817) -0.3289*** (0.0825)
## Wholesale Funding 0.1831*** (0.0466) 0.1826*** (0.0465)
## ROA -0.0127 (0.0628) -0.0164 (0.0629)
## ______________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 3,737 3,737
## Pseudo R2 0.13099 0.13124
## N(BTFP=1) 462 462
## Method Logit Logit
##
## (3) Post (4) Post
## Dependent Var.: btfp_post btfp_post
##
## Constant -1.372*** (0.0477) -1.366*** (0.0481)
## MTM Loss (z) 0.1280** (0.0490) 0.1307** (0.0493)
## Uninsured Leverage (z) 0.1229* (0.0513) 0.1222* (0.0514)
## MTM $\times$ Uninsured 0.0319 (0.0433)
## Log(Assets) 0.3959*** (0.0527) 0.3964*** (0.0526)
## Cash Ratio -0.5106*** (0.0707) -0.5062*** (0.0710)
## Loan-to-Deposit -0.0987. (0.0511) -0.0942. (0.0516)
## Book Equity Ratio -0.2212*** (0.0560) -0.2169*** (0.0563)
## Wholesale Funding 0.0741. (0.0414) 0.0734. (0.0414)
## ROA -0.0804. (0.0489) -0.0831. (0.0490)
## ______________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 3,437 3,437
## Pseudo R2 0.08889 0.08902
## N(BTFP=1) 775 775
## Method Logit Logit
##
## (5) Arb (6) Arb
## Dependent Var.: btfp_arb btfp_arb
##
## Constant -1.802*** (0.0529) -1.805*** (0.0533)
## MTM Loss (z) 0.1648** (0.0526) 0.1648** (0.0526)
## Uninsured Leverage (z) 0.1416** (0.0509) 0.1433** (0.0510)
## MTM $\times$ Uninsured -0.0141 (0.0438)
## Log(Assets) 0.3645*** (0.0488) 0.3638*** (0.0488)
## Cash Ratio -0.5258*** (0.0777) -0.5279*** (0.0777)
## Loan-to-Deposit 0.0327 (0.0540) 0.0314 (0.0543)
## Book Equity Ratio -0.1666** (0.0588) -0.1688** (0.0592)
## Wholesale Funding 0.5459*** (0.0382) 0.5463*** (0.0382)
## ROA -0.2242*** (0.0581) -0.2226*** (0.0583)
## ______________________ ___________________ ___________________
## S.E. type Heteroskedast.-rob. Heteroskedast.-rob.
## Observations 4,038 4,038
## Pseudo R2 0.15311 0.15314
## N(BTFP=1) 766 766
## Method Logit Logit
##
## (7) Wind (8) Wind
## Dependent Var.: btfp_wind btfp_wind
##
## Constant -3.170*** (0.0842) -3.183*** (0.0857)
## MTM Loss (z) 0.0424 (0.0897) 0.0421 (0.0898)
## Uninsured Leverage (z) 0.1775* (0.0872) 0.1852* (0.0869)
## MTM $\times$ Uninsured -0.0689 (0.0743)
## Log(Assets) 0.0162 (0.0838) 0.0130 (0.0842)
## Cash Ratio -0.3306** (0.1042) -0.3401** (0.1046)
## Loan-to-Deposit -0.1729. (0.0886) -0.1801* (0.0888)
## Book Equity Ratio -0.2008* (0.0971) -0.2080* (0.0976)
## Wholesale Funding 0.6113*** (0.0491) 0.6136*** (0.0493)
## ROA -0.1758. (0.0988) -0.1715. (0.0983)
## ______________________ __________________ __________________
## S.E. type Heteroskedas.-rob. Heteroskedas.-rob.
## Observations 4,043 4,043
## Pseudo R2 0.11320 0.11368
## N(BTFP=1) 229 229
## Method Logit Logit
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_logit_t6, "Logit_Table_6_Temporal",
title_text = "Logit: BTFP Temporal Evolution",
notes_text = "Logit with robust SEs. Coefficients in log-odds.",
fitstat_use = ~ n + pr2, extra_lines = logit_t6_extra)
## Saved: Logit_Table_6_Temporal.tex
# ==============================================================================
# LOGIT TABLE 7: PRE-BTFP DW
# ==============================================================================
models_logit_t7 <- list(
"(1) Mar10" = run_one(df_dw_mar10_s, "dw_mar10", EXPL_MTM_BASE, "logit", CONTROLS_MTM),
"(2) Mar10" = run_one(df_dw_mar10_s, "dw_mar10", EXPL_MTM_INTER, "logit", CONTROLS_MTM),
"(3) Mar10-13" = run_one(df_dw_mar10_13_s, "dw_mar10_13", EXPL_MTM_BASE, "logit", CONTROLS_MTM),
"(4) Mar10-13" = run_one(df_dw_mar10_13_s, "dw_mar10_13", EXPL_MTM_INTER, "logit", CONTROLS_MTM)
)
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
logit_t7_extra <- list(
"__N(DW=1)" = c(sum(df_dw_mar10_s$dw_mar10), sum(df_dw_mar10_s$dw_mar10),
sum(df_dw_mar10_13_s$dw_mar10_13), sum(df_dw_mar10_13_s$dw_mar10_13)),
"__Method" = rep("Logit", 4)
)
etable(models_logit_t7, fitstat = ~ n + pr2,
order = COEF_ORDER, extralines = logit_t7_extra,
title = "Logit Table 7: Pre-BTFP DW")
## (1) Mar10 (2) Mar10 (3) Mar10-13
## Dependent Var.: dw_mar10 dw_mar10 dw_mar10_13
##
## Constant -5.752*** (0.4085) -5.770*** (0.3956) -4.779*** (0.2065)
## MTM Loss (z) -0.2155 (0.2137) -0.1931 (0.2259) -0.0909 (0.1527)
## Uninsured Leverage (z) 0.1593 (0.1599) 0.1622 (0.1618) 0.3690** (0.1222)
## MTM $\times$ Uninsured -0.0641 (0.1644)
## Log(Assets) 0.8648*** (0.1447) 0.8642*** (0.1451) 0.9365*** (0.1125)
## Cash Ratio -1.376* (0.5494) -1.390* (0.5415) -0.6538* (0.2732)
## Loan-to-Deposit 0.0591 (0.2057) 0.0520 (0.2092) -0.0094 (0.1566)
## Book Equity Ratio -0.4911. (0.2717) -0.4930. (0.2693) -0.3050. (0.1824)
## Wholesale Funding 0.3329*** (0.0986) 0.3344*** (0.0996) 0.2374** (0.0853)
## ROA -0.3268. (0.1717) -0.3265. (0.1722) 0.0252 (0.1352)
## ______________________ __________________ __________________ __________________
## S.E. type Heteroskedas.-rob. Heteroskedas.-rob. Heteroskedas.-rob.
## Observations 3,986 3,986 3,988
## Pseudo R2 0.18573 0.18604 0.19815
## N(DW=1) 47 47 90
## Method Logit Logit Logit
##
## (4) Mar10-13
## Dependent Var.: dw_mar10_13
##
## Constant -4.762*** (0.2047)
## MTM Loss (z) -0.1653 (0.1668)
## Uninsured Leverage (z) 0.3781** (0.1200)
## MTM $\times$ Uninsured 0.1289 (0.1073)
## Log(Assets) 0.9310*** (0.1120)
## Cash Ratio -0.6415* (0.2726)
## Loan-to-Deposit 0.0069 (0.1592)
## Book Equity Ratio -0.3044. (0.1840)
## Wholesale Funding 0.2361** (0.0850)
## ROA 0.0214 (0.1343)
## ______________________ __________________
## S.E. type Heteroskedas.-rob.
## Observations 3,988
## Pseudo R2 0.19968
## N(DW=1) 90
## Method Logit
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
save_etable(models_logit_t7, "Logit_Table_7_PreBTFP_DW",
title_text = "Logit: Pre-BTFP Discount Window",
notes_text = "Logit with robust SEs.",
fitstat_use = ~ n + pr2, extra_lines = logit_t7_extra)
## Saved: Logit_Table_7_PreBTFP_DW.tex
Goldstein-Pauzner prediction: The AE coefficient should be steeper among banks with high uninsured leverage, because the run threshold shifts when fragility is high.
# ==============================================================================
# THRESHOLD GRADIENT — AE coefficient across UL terciles
# ==============================================================================
tercile_coefs <- map_dfr(c("Low", "Med", "High"), function(terc) {
df_sub <- df_btfp_s %>% filter(unins_lev_tercile == terc)
mod <- feols(btfp_acute ~ adjusted_equity + uninsured_lev + ln_assets + cash_ratio +
loan_to_deposit + wholesale + roa, data = df_sub, vcov = "hetero")
tidy(mod) %>%
filter(term == "adjusted_equity") %>%
mutate(tercile = terc, n = nrow(df_sub), n_btfp = sum(df_sub$btfp_acute))
})
## NOTE: 3 observations removed because of NA values (RHS: 3).
## NOTE: 1 observation removed because of NA values (RHS: 1).
## NOTE: 6 observations removed because of NA values (RHS: 6).
tercile_coefs$tercile <- factor(tercile_coefs$tercile, levels = c("Low", "Med", "High"))
fig1 <- ggplot(tercile_coefs, aes(x = tercile, y = estimate)) +
geom_col(fill = "#003049", width = 0.6) +
geom_errorbar(aes(ymin = estimate - 1.96 * std.error,
ymax = estimate + 1.96 * std.error),
width = 0.2, linewidth = 0.7) +
geom_hline(yintercept = 0, linetype = "dashed", color = "grey50") +
labs(title = "Figure 1: Threshold Gradient — AE Coefficient by Uninsured Leverage Tercile",
subtitle = "BTFP Acute Period | LPM with robust SEs",
x = "Uninsured Leverage Tercile", y = "AE Coefficient (LPM)") +
theme_paper
fig1
save_figure(fig1, "Fig1_ThresholdGradient_AE")
## Saved: Fig1_ThresholdGradient_AE.pdf
# ==============================================================================
# TEMPORAL COEFFICIENT EVOLUTION
# ==============================================================================
temporal_data <- list(
list(data = df_btfp_s, dv = "btfp_acute", period = "Acute"),
list(data = df_btfp_post_s, dv = "btfp_post", period = "Post"),
list(data = df_btfp_arb_s, dv = "btfp_arb", period = "Arb"),
list(data = df_btfp_wind_s, dv = "btfp_wind", period = "Wind")
)
temporal_coefs <- map_dfr(temporal_data, function(item) {
mod <- run_one(item$data, item$dv, EXPL_MTM_INTER, controls = CONTROLS_MTM)
tidy(mod) %>%
filter(term %in% c("adjusted_equity", "uninsured_lev", "ae_x_uninsured")) %>%
mutate(period = item$period)
})
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 10 observations removed because of NA values (RHS: 10).
## NOTE: 17 observations removed because of NA values (RHS: 17).
## NOTE: 18 observations removed because of NA values (RHS: 18).
temporal_coefs$period <- factor(temporal_coefs$period,
levels = c("Acute", "Post", "Arb", "Wind"))
fig2 <- ggplot(temporal_coefs, aes(x = period, y = estimate, color = term, group = term)) +
geom_point(size = 3, position = position_dodge(0.3)) +
geom_errorbar(aes(ymin = estimate - 1.96 * std.error,
ymax = estimate + 1.96 * std.error),
width = 0.2, position = position_dodge(0.3)) +
geom_hline(yintercept = 0, linetype = "dashed", color = "grey50") +
scale_color_manual(values = c("adjusted_equity" = "#003049",
"uninsured_lev" = "#D62828",
"ae_x_uninsured" = "#F77F00"),
labels = c("Adj. Equity", "Uninsured Leverage", "AE × UL")) +
labs(title = "Figure 2: Temporal Coefficient Evolution — BTFP",
subtitle = " | LPM with robust SEs",
x = "Period", y = "Coefficient", color = "") +
theme_paper
fig2
save_figure(fig2, "Fig2_Temporal_Coefs_BTFP")
## Saved: Fig2_Temporal_Coefs_BTFP.pdf
# ==============================================================================
# DESCRIPTIVE STATS: FHLB BORROWING BY PERIOD
# ==============================================================================
# 1. Define the raw variable list
fhlb_vars <- c(
"fhlb_adv", # Total Advances
"fhlb_less_than_1yr", "fhlb_1to3yr", # Maturities
"fhlb_3to5yr", "fhlb_more_than_5yr",
"change_fhlb_adv_fwd_q", # Change in advances
"abnormal_fhlb_borrowing_10pct" # Abnormal indicator
)
# 2. Variable Labels for clean output
fhlb_labels <- c(
"Total FHLB Advances ($000s)",
"Maturity: < 1 Year",
"Maturity: 1-3 Years",
"Maturity: 3-5 Years",
"Maturity: > 5 Years",
"Change in FHLB Adv. (Next Q)",
"Abnormal Borrowing (10% Threshold)"
)
# 3. Calculate Summary Stats for FHLB Users in the Acute Sample
fhlb_stats_table <- df_fhlb_s %>%
filter(fhlb_user == 1) %>% # Restrict to actual FHLB borrowers
select(all_of(fhlb_vars)) %>%
# USE CUSTOM SEPARATOR (___) so we don't split variable names with underscores
summarise(across(everything(), list(
Mean = ~ mean(., na.rm = TRUE),
SD = ~ sd(., na.rm = TRUE),
Median = ~ median(., na.rm = TRUE)
), .names = "{col}___{fn}")) %>%
pivot_longer(everything(), names_to = c("Variable", "Stat"), names_sep = "___") %>%
pivot_wider(names_from = Stat, values_from = value) %>%
mutate(
Variable = factor(Variable, levels = fhlb_vars, labels = fhlb_labels),
Mean = formatC(Mean, format = "f", digits = 2, big.mark = ","),
SD = formatC(SD, format = "f", digits = 2, big.mark = ","),
Median = formatC(Median, format = "f", digits = 2, big.mark = ",")
)
# 4. Display Table (HTML)
kbl(fhlb_stats_table, caption = "FHLB Borrowing Characteristics (Acute Period Users)") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = F)
| Variable | Mean | SD | Median |
|---|---|---|---|
| Total FHLB Advances ($000s) | 114,622.40 | 712,943.60 | 10,000.00 |
| Maturity: < 1 Year | 83,365.01 | 648,685.55 | 4,000.00 |
| Maturity: 1-3 Years | 11,961.41 | 58,688.95 | 282.50 |
| Maturity: 3-5 Years | 9,282.11 | 65,918.11 | 0.00 |
| Maturity: > 5 Years | 10,013.87 | 115,988.88 | 0.00 |
| Change in FHLB Adv. (Next Q) | 1,311.63 | 8,921.94 | 120.00 |
| Abnormal Borrowing (10% Threshold) | 1.00 | 0.00 | 1.00 |
# 5. Save Table (LaTeX)
latex_fhlb_stats <- kable(fhlb_stats_table, format = "latex", booktabs = TRUE,
caption = "FHLB Borrowing Amounts and Maturities (Acute Period Users). \\textit{Note: Amounts in thousands.}")
safe_writeLines(as.character(latex_fhlb_stats), file.path(TABLE_PATH, "Table_Desc_FHLB_Details.tex"))
message("Saved: Table_Desc_FHLB_Details.tex")
## Saved: Table_Desc_FHLB_Details.tex
# ==============================================================================
# DESCRIPTIVE STATS: FHLB BORROWING BY TEMPORAL PERIOD
# ==============================================================================
# 1. Define Period Datasets
fhlb_periods <- list(
"Acute" = df_fhlb_s,
"Post-Acute" = df_fhlb_post_s,
"Arbitrage" = df_fhlb_arb_s,
"Wind-down" = df_fhlb_wind_s
)
# 2. Function to Calculate Weighted Average Term (Years)
# Midpoints: <1yr (0.5), 1-3yr (2.0), 3-5yr (4.0), >5yr (7.0)
calc_wa_term <- function(df) {
# Avoid division by zero
total_adv <- sum(df$fhlb_adv, na.rm = TRUE)
if (total_adv == 0) return(NA)
w_sum <- sum(
(df$fhlb_less_than_1yr * 0.5) +
(df$fhlb_1to3yr * 2.0) +
(df$fhlb_3to5yr * 4.0) +
(df$fhlb_more_than_5yr * 7.0),
na.rm = TRUE
)
return(w_sum / total_adv)
}
# 3. Generate Summary Data
fhlb_temporal_stats <- map_dfr(names(fhlb_periods), function(p_name) {
# Full sample (for "Banks" count)
df_all <- fhlb_periods[[p_name]]
# Borrowers only (for amounts and term)
df_bor <- df_all %>% filter(fhlb_user == 1)
tibble(
Period = p_name,
Abnormal10pct = nrow(df_bor), # Number of Borrowers
Banks = nrow(df_all), # Total Sample N
Total_B = sum(df_bor$fhlb_adv, na.rm = TRUE) / 1e6, # Billions
Mean_M = mean(df_bor$fhlb_adv, na.rm = TRUE) / 1e3, # Millions
Median_M = median(df_bor$fhlb_adv, na.rm = TRUE) / 1e3, # Millions
Term_Yrs = calc_wa_term(df_bor)
)
})
# 4. Format for Display
fhlb_display <- fhlb_temporal_stats %>%
mutate(
`Total ($B)` = formatC(Total_B, format = "f", digits = 2, big.mark = ","),
`Mean ($M)` = formatC(Mean_M, format = "f", digits = 1, big.mark = ","),
`Median ($M)` = formatC(Median_M,format = "f", digits = 1, big.mark = ","),
`Term (Years)` = formatC(Term_Yrs,format = "f", digits = 2)
) %>%
select(Period, Abnormal10pct , Banks, `Total ($B)`, `Mean ($M)`, `Median ($M)`, `Term (Years)`)
# 5. Create LaTeX Table with Custom Header
# We use kable listing the bottom row of headers, then add the top row with add_header_above
kbl(fhlb_display, booktabs = TRUE, format = "latex", align = "lccrrrr",
caption = "FHLB Borrowing Characteristics by Period") %>%
add_header_above(c(" " = 3, "Total" = 1, "Mean" = 1, "Median" = 1, "Term" = 1)) %>%
kable_styling(latex_options = c("hold_position")) %>%
save_kable(file.path(TABLE_PATH, "Table_Desc_FHLB_Temporal.tex"))
# 6. HTML Preview
kbl(fhlb_display, caption = "FHLB Borrowing Characteristics by Period") %>%
kable_styling(bootstrap_options = "striped", full_width = F) %>%
add_header_above(c(" " = 3, "Total" = 1, "Mean" = 1, "Median" = 1, "Term" = 1))
| Period | Abnormal10pct | Banks | Total (\(B) </th> <th style="text-align:left;"> Mean (\)M) | Median ($M) | Term (Years) | |
|---|---|---|---|---|---|---|
| Acute | 302 | 3587 | 34.62 | 114.6 | 10.0 | 1.51 |
| Post-Acute | 302 | 2974 | 34.62 | 114.6 | 10.0 | 1.51 |
| Arbitrage | 196 | 3485 | 18.18 | 92.8 | 10.0 | 2.17 |
| Wind-down | 145 | 3977 | 12.05 | 83.1 | 10.1 | 1.19 |
cat("\n=== SESSION COMPLETE ===\n")
##
## === SESSION COMPLETE ===
cat("Tables saved to:", TABLE_PATH, "\n")
## Tables saved to: C:/Users/mohua/OneDrive - Louisiana State University/Finance_PhD/DW_Stigma_paper/Liquidity_project_2025/03_documentation/analysis_all_specification/tables
cat("Figures saved to:", FIG_PATH, "\n")
## Figures saved to: C:/Users/mohua/OneDrive - Louisiana State University/Finance_PhD/DW_Stigma_paper/Liquidity_project_2025/03_documentation/analysis_all_specification/figures
sessionInfo()
## R version 4.3.1 (2023-06-16 ucrt)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 11 x64 (build 26200)
##
## Matrix products: default
##
##
## locale:
## [1] LC_COLLATE=English_United States.utf8
## [2] LC_CTYPE=English_United States.utf8
## [3] LC_MONETARY=English_United States.utf8
## [4] LC_NUMERIC=C
## [5] LC_TIME=English_United States.utf8
##
## time zone: America/Chicago
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] readxl_1.4.5 readr_2.1.5 patchwork_1.3.2
## [4] scales_1.4.0 gridExtra_2.3 ggplot2_3.5.2
## [7] DescTools_0.99.60 kableExtra_1.4.0 knitr_1.50
## [10] modelsummary_2.4.0 broom_1.0.9 nnet_7.3-19
## [13] marginaleffects_0.25.1 fixest_0.12.1 tibble_3.2.1
## [16] purrr_1.0.4 lubridate_1.9.4 stringr_1.5.1
## [19] tidyr_1.3.1 dplyr_1.1.4 data.table_1.17.0
##
## loaded via a namespace (and not attached):
## [1] tidyselect_1.2.1 Exact_3.3 viridisLite_0.4.2
## [4] rootSolve_1.8.2.4 farver_2.1.2 fastmap_1.2.0
## [7] bayestestR_0.16.1 digest_0.6.33 timechange_0.3.0
## [10] estimability_1.5.1 lifecycle_1.0.4 dreamerr_1.4.0
## [13] lmom_3.2 magrittr_2.0.3 compiler_4.3.1
## [16] rlang_1.1.1 sass_0.4.10 tools_4.3.1
## [19] yaml_2.3.10 labeling_0.4.3 bit_4.6.0
## [22] xml2_1.3.8 RColorBrewer_1.1-3 tinytable_0.13.0
## [25] expm_1.0-0 withr_3.0.2 numDeriv_2016.8-1.1
## [28] datawizard_1.2.0 grid_4.3.1 fansi_1.0.6
## [31] xtable_1.8-4 e1071_1.7-16 emmeans_1.11.2-8
## [34] MASS_7.3-60 insight_1.3.1 cli_3.6.1
## [37] mvtnorm_1.3-3 crayon_1.5.3 rmarkdown_2.29
## [40] ragg_1.3.3 generics_0.1.4 performance_0.15.0
## [43] rstudioapi_0.17.1 httr_1.4.7 tzdb_0.5.0
## [46] parameters_0.27.0 gld_2.6.8 cachem_1.1.0
## [49] proxy_0.4-27 parallel_4.3.1 cellranger_1.1.0
## [52] stringmagic_1.1.2 vctrs_0.6.5 boot_1.3-28.1
## [55] Matrix_1.5-4.1 sandwich_3.1-1 jsonlite_2.0.0
## [58] litedown_0.7 hms_1.1.3 bit64_4.6.0-1
## [61] Formula_1.2-5 systemfonts_1.2.2 jquerylib_0.1.4
## [64] glue_1.8.0 stringi_1.8.7 gtable_0.3.6
## [67] tables_0.9.31 pillar_1.11.0 htmltools_0.5.9
## [70] R6_2.6.1 textshaping_1.0.0 vroom_1.6.5
## [73] evaluate_1.0.4 lattice_0.21-8 haven_2.5.4
## [76] backports_1.5.0 bslib_0.9.0 class_7.3-22
## [79] Rcpp_1.0.14 checkmate_2.3.2 svglite_2.1.3
## [82] coda_0.19-4.1 nlme_3.1-162 xfun_0.52
## [85] fs_1.6.5 zoo_1.8-13 forcats_1.0.0
## [88] pkgconfig_2.0.3