Theory-to-Code Map
Every equation in theory_standalone_final.pdf is mapped
below.
| Eq. 1 |
\(C+S+L = D^I + D^U +
E\) |
Balance sheet identity |
§3 |
| Eq. 2 |
\(\lambda = (1-\theta_S)S +
(1-\theta_L)L\) |
mtm_total_raw |
§3 |
| Eq. 3 |
\(F =
(1-\beta^U)r\,D^U/\delta\) |
f_pp (via DSSW Eq.20) |
§3 |
| Eq. 4 |
\(E^{MV} = E - \lambda +
F\) |
emv_pp |
§3 |
| Eq. 5 |
\(F^U =
(D^U/D)F\) |
f_u_pp |
§3 |
| Eq. 6 |
\(g = w - (C + \theta_S
S)\) |
liquid_buffer_raw |
§3 |
| Eq. 7 |
\(v = E^{MV} -
F^U\) |
v_pp |
§3 |
| Eq. 11 |
\(\partial^2\Pr(\text{borrow})/\partial\lambda\,\partial
F^U > 0\) |
Interaction test |
§7 |
| Eq. 12 |
\(\lambda^\dagger = E + F -
F^U\) |
threshold_run |
§3 |
| Eq. 13 |
LPM: \(\Pr(\text{Borrow}_i) =
\alpha + c_1\ell + c_2 f^U + c_3(\ell\times f^U) + \gamma'X +
\varepsilon\) |
Main regression |
§7 |
| Eq. 14 |
\(\Delta_{\text{BTFP}} =
(1-\theta_S)S_{\text{el}}\) |
mtm_btfp_raw |
§8 |
| Eq. 15 |
\(\text{Amount}_i = \alpha +
d_1\ell_{S,i} + \gamma'X + \varepsilon\) |
Intensive margin |
§8 |
| Falsif. |
FHLB: \(c_3 \approx
0\); Arb period: \(c_3 \approx
0\) |
§9 |
|
SETUP
Packages
rm(list = ls())
library(data.table); library(dplyr); library(tidyr); library(stringr)
library(lubridate); library(purrr); library(tibble)
library(fixest); library(modelsummary)
library(knitr); library(kableExtra)
library(ggplot2); library(scales); library(patchwork)
library(readr); library(readxl)
cat("All packages loaded.\n")
## All packages loaded.
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)
s <- sd(x, na.rm = TRUE)
if (is.na(s) || s == 0) return(rep(0, length(x)))
(x - mean(x, na.rm = TRUE)) / s
}
safe_div <- function(num, denom, default = NA_real_) {
ifelse(is.na(denom) | denom == 0, default, num / denom)
}
format_pval <- function(p) {
case_when(is.na(p) ~ "", p < 0.01 ~ "***", p < 0.05 ~ "**", p < 0.10 ~ "*", TRUE ~ "")
}
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")
}
dv_summary_rows <- function(model_list) {
stats <- lapply(model_list, function(m) {
y <- fitted(m) + resid(m)
c(N_DV1 = sum(round(y) == 1, na.rm = TRUE),
Mean_DV = round(mean(y, na.rm = TRUE), 4))
})
df <- data.frame(term = c("N (DV = 1)", "Mean (DV)"), stringsAsFactors = FALSE)
for (nm in names(stats)) df[[nm]] <- as.character(stats[[nm]])
df
}
save_reg_latex <- function(model_list, filename, ...) {
tex_path <- file.path(TABLE_PATH, paste0(filename, ".tex"))
msummary(model_list, output = tex_path,
stars = c("*" = .10, "**" = .05, "***" = .01),
gof_omit = "AIC|BIC|Log|RMSE", ...)
cat(sprintf("Saved: %s.tex\n", filename))
}
save_kbl_latex <- function(df, filename, col.names = NULL, caption = "", align = NULL) {
tex <- kbl(df, format = "latex", booktabs = TRUE, escape = FALSE,
col.names = col.names, caption = caption, align = align) %>%
kable_styling(latex_options = c("hold_position", "scale_down"))
writeLines(tex, file.path(TABLE_PATH, paste0(filename, ".tex")))
cat(sprintf("Saved: %s.tex\n", filename))
}
Paths &
Dates
BASE_PATH <- "C:/Users/mferdo2/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/new_run_Analysis_revised")
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)
BASELINE_MAIN <- "2022Q4"; BASELINE_ARB <- "2023Q3"
CRISIS_START <- as.Date("2023-03-08"); CRISIS_END <- as.Date("2023-05-04")
ARB_START <- as.Date("2023-11-15"); ARB_END <- as.Date("2024-01-24")
DW_DATA_END <- as.Date("2023-12-31")
DATA LOADING
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))
dssw_betas <- read_csv(file.path(DATA_PROC, "dssw_deposit_betas.csv"), show_col_types = FALSE) %>%
mutate(idrssd = as.character(idrssd))
dssw_beta_2022q4 <- dssw_betas %>% filter(estimation_date == "2022Q4") %>%
select(idrssd, beta_overall, beta_insured, beta_uninsured,
beta_insured_w, beta_uninsured_w, gamma_hat, alpha_hat)
public_flag <- read_csv(file.path(DATA_PROC, "public_bank_flag.csv"), show_col_types = FALSE) %>%
mutate(idrssd = as.character(idrssd)) %>% select(idrssd, period, is_public)
deposit_costs_file <- file.path(DATA_PROC, "dssw_deposit_costs.csv")
if (file.exists(deposit_costs_file)) {
deposit_costs <- read_csv(deposit_costs_file, show_col_types = FALSE) %>%
mutate(idrssd = as.character(idrssd))
deposit_costs_2022q4 <- deposit_costs %>%
filter(period == "2022Q4") %>%
select(idrssd, deposit_cost_weighted, deposit_cost_insured, deposit_cost_uninsured)
HAS_DEPOSIT_COSTS <- TRUE
} else {
deposit_costs_2022q4 <- NULL
HAS_DEPOSIT_COSTS <- FALSE
}
cat("Call Report:", nrow(call_q), "obs |", n_distinct(call_q$idrssd), "banks\n")
## Call Report: 75989 obs | 5074 banks
Exclusions &
Borrower Indicators
excluded_banks <- call_q %>%
filter(period == BASELINE_MAIN, failed_bank == 1 | gsib == 1) %>% pull(idrssd)
btfp_loans <- btfp_loans_raw %>% filter(!rssd_id %in% excluded_banks)
dw_loans <- dw_loans_raw %>% filter(!rssd_id %in% excluded_banks)
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_crisis <- create_borrower_indicator(btfp_loans,"btfp_loan_date","rssd_id","btfp_loan_amount",CRISIS_START,CRISIS_END,"btfp_crisis")
btfp_arb <- create_borrower_indicator(btfp_loans,"btfp_loan_date","rssd_id","btfp_loan_amount",ARB_START,ARB_END,"btfp_arb")
dw_crisis <- create_borrower_indicator(dw_loans,"dw_loan_date","rssd_id","dw_loan_amount",CRISIS_START,min(CRISIS_END,DW_DATA_END),"dw_crisis")
dw_arb <- create_borrower_indicator(dw_loans,"dw_loan_date","rssd_id","dw_loan_amount",ARB_START,DW_DATA_END,"dw_arb")
cat("BTFP Crisis:", nrow(btfp_crisis), "| DW Crisis:", nrow(dw_crisis), "\n")
## BTFP Crisis: 526 | DW Crisis: 459
CALIBRATION &
VARIABLE CONSTRUCTION
Calibration [DSSW Eq.
20]
# ==============================================================================
# DSSW Eq. (20): Decaying perpetuity
# f_i = pmax( ((1 - β^U_i) * y - c^U_i) / (y + δ) × μ_i, 0 )
#
# Theory Eq. (3) writes F = (1-β)rD^U/δ for exposition.
# Empirically we use the DSSW decaying-perpetuity capitalization:
# y = 10-year Treasury yield (discount rate matching 1/δ duration)
# δ = 0.10 (deposit decay rate; DSSW baseline)
# c^U_i = bank-level uninsured deposit operating cost (DSSW hedonic)
# ==============================================================================
y_10yr <- 0.0392 # 10-year Treasury yield (Feb 2023) — DSSW calibration
delta_decay <- 0.10 # Deposit decay rate (DSSW baseline)
cap_factor <- 1 / (y_10yr + delta_decay) # = 7.18
cat("=== CALIBRATION (DSSW Eq. 20) ===\n")
## === CALIBRATION (DSSW Eq. 20) ===
cat(sprintf(" y (10yr Treasury) = %.4f\n", y_10yr))
## y (10yr Treasury) = 0.0392
cat(sprintf(" δ (decay rate) = %.2f\n", delta_decay))
## δ (decay rate) = 0.10
cat(sprintf(" Cap factor = 1/(y+δ) = 1/%.4f = %.2f\n", y_10yr + delta_decay, cap_factor))
## Cap factor = 1/(y+δ) = 1/0.1392 = 7.18
Variable Construction
[Theory Eq. 1–7, 12, Table 1]
# ==============================================================================
# Maps EVERY variable in theory Table 1 to data:
# ℓ = total MTM losses / A (Eq. 2)
# ℓ_S = BTFP eligible securities MTM / A (Eq. 2, Eq. 14)
# ℓ_L = loan MTM / A (Eq. 2)
# f = pmax(((1-β^U)y - c^U)/(y+δ) × μ, 0) (Eq. 3, DSSW Eq. 20)
# e^MV = e - ℓ + f (Eq. 4)
# f^U = (D^U/D) × f (Eq. 5) ← KEY NEW VARIABLE
# v = e^MV - f^U = e - ℓ + (D^I/D)f (Eq. 7)
# λ† = E + F - F^U = e + (D^I/D)f (Eq. 12)
# ==============================================================================
construct_analysis_vars <- function(baseline_data) {
baseline_data %>%
mutate(
# ── Eq. 2: MTM loss components ──
mtm_total_raw = mtm_loss_to_total_asset,
mtm_loan_raw = mtm_loss_total_loan_to_total_asset, # ℓ_L: Loan losses
mtm_sec_raw = mtm_loss_to_total_asset - # ℓ_S: Securities losses
mtm_loss_total_loan_to_total_asset, # = ℓ - ℓ_L
# BTFP-eligible (OMO) vs non-BTFP decomposition (for P3 par-channel tests)
mtm_btfp_raw = mtm_loss_omo_eligible_to_total_asset, # OMO-eligible securities
mtm_other_raw = mtm_loss_non_omo_eligible_to_total_asset, # non-OMO sec + loans
# ── Balance sheet ratios ──
uninsured_lev_raw = uninsured_deposit_to_total_asset,
insured_lev_raw = r_insured_deposit,
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,
roa_raw = roa,
fhlb_ratio_raw = fhlb_to_total_asset,
loan_to_deposit_raw = loan_to_deposit,
wholesale_raw = safe_div(
replace_na(fed_fund_purchase, 0) + replace_na(repo, 0) +
replace_na(other_borrowed_less_than_1yr, 0),
total_liability, 0) * 100,
# ── Deposit beta: clip to [0,1] per theory ──
uninsured_beta_raw = ifelse(!is.na(beta_uninsured), beta_uninsured, NA_real_),
beta_u_clipped = pmin(pmax(uninsured_beta_raw, 0), 1),
# ── Uninsured deposit cost (DSSW hedonic) ──
cost_u_raw = ifelse(!is.na(deposit_cost_uninsured), deposit_cost_uninsured, 0),
# ── Jiang AE: adjusted equity = e - ℓ ──
adjusted_equity_raw = book_equity_to_total_asset - mtm_loss_to_total_asset,
# ── Key ratios ──
mu_decimal = uninsured_deposit / total_asset,
insured_share = safe_div(insured_deposit, insured_deposit + uninsured_deposit, NA_real_),
uninsured_share_d = safe_div(uninsured_deposit, insured_deposit + uninsured_deposit, NA_real_),
# ── Eq. 3 (DSSW Eq. 20): Total franchise value f ──
gross_rent = (1 - beta_u_clipped) * y_10yr,
net_rent = gross_rent - cost_u_raw,
f_decimal = ifelse(!is.na(beta_u_clipped),
pmax(net_rent * cap_factor * mu_decimal, 0), NA_real_),
f_pp = ifelse(!is.na(f_decimal), f_decimal * 100, NA_real_),
# ── Eq. 4: E^MV = e - ℓ + f ──
emv_pp = ifelse(!is.na(f_pp),
book_equity_to_total_asset - mtm_total_raw + f_pp, NA_real_),
# ── Eq. 5: F^U = (D^U/D) × f [THE KEY VARIABLE] ──
f_u_pp = ifelse(!is.na(f_pp) & !is.na(uninsured_share_d),
uninsured_share_d * f_pp, NA_real_),
# ── Eq. 7: v = E^MV - F^U [run value: v < 0 ⟹ run possible] ──
v_pp = ifelse(!is.na(emv_pp) & !is.na(f_u_pp),
emv_pp - f_u_pp, NA_real_),
# ── Eq. 12: λ† = e + f - f^U = e + (D^I/D)f [run threshold] ──
threshold_run = ifelse(!is.na(f_pp) & !is.na(insured_share),
book_equity_to_total_asset + insured_share * f_pp, NA_real_),
# ── Eq. 6: Liquid buffer = C/A + θ_S·S/A ──
liquid_buffer_raw = cash_to_total_asset + security_to_total_asset - mtm_btfp_raw ,
# ── Run indicator: v < 0 ──
run_possible = as.integer(!is.na(v_pp) & v_pp < 0),
# ── Collateral (for intensive margin, Eq. 14) ──
collateral_capacity_raw = omo_eligible_to_total_asset,
# ── Winsorized ──
mtm_total_w=winsorize(mtm_total_raw), mtm_sec_w=winsorize(mtm_sec_raw),
mtm_loan_w=winsorize(mtm_loan_raw),
mtm_btfp_w=winsorize(mtm_btfp_raw), # BTFP-eligible (OMO)
mtm_other_w=winsorize(mtm_other_raw), # non-BTFP (non-OMO sec + loans)
uninsured_lev_w=winsorize(uninsured_lev_raw),
insured_lev_w=winsorize(r_insured_deposit), adjusted_equity_w=winsorize(adjusted_equity_raw),
f_pp_w=winsorize(f_pp), f_u_pp_w=winsorize(f_u_pp), emv_pp_w=winsorize(emv_pp),
uninsured_beta_w=winsorize(uninsured_beta_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),
deposit_cost_w=winsorize(cost_u_raw),
# ── Z-standardized (full-sample z-scores) ──
mtm_total=standardize_z(mtm_total_w), mtm_sec=standardize_z(mtm_sec_w),
mtm_loan=standardize_z(mtm_loan_w),
mtm_btfp=standardize_z(mtm_btfp_w), # BTFP-eligible (OMO) z-score
mtm_other=standardize_z(mtm_other_w), # non-BTFP z-score
uninsured_lev=standardize_z(uninsured_lev_w),
insured_lev=standardize_z(insured_lev_w), adjusted_equity=standardize_z(adjusted_equity_w),
franchise_value=standardize_z(f_pp_w), uninsured_franchise=standardize_z(f_u_pp_w), emv=standardize_z(emv_pp_w),
uninsured_beta=standardize_z(uninsured_beta_w), 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),
# ── Eq. 11/13: THE interaction ──
# ℓ × f^U (NOT ℓ × f — the theory's cross-partial is w.r.t. F^U)
mtm_x_f_u = mtm_total * uninsured_franchise
)
}
BUILD DATASETS
df_2022q4 <- call_q %>%
filter(period == BASELINE_MAIN, !idrssd %in% excluded_banks,
!is.na(omo_eligible) & omo_eligible > 0) %>%
left_join(dssw_beta_2022q4, by = "idrssd") %>%
{ if (HAS_DEPOSIT_COSTS) left_join(., deposit_costs_2022q4, by = "idrssd") else . } %>%
left_join(public_flag %>% filter(period == "2022Q4") %>% select(idrssd, is_public), by = "idrssd") %>%
mutate(is_public = replace_na(is_public, 0L)) %>%
construct_analysis_vars()
df_2023q3 <- call_q %>%
filter(period == BASELINE_ARB, !idrssd %in% excluded_banks,
!is.na(omo_eligible) & omo_eligible > 0) %>%
left_join(dssw_beta_2022q4, by = "idrssd") %>%
{ if (HAS_DEPOSIT_COSTS) left_join(., deposit_costs_2022q4, by = "idrssd") else . } %>%
left_join(public_flag %>% filter(period == "2023Q3") %>% select(idrssd, is_public), by = "idrssd") %>%
mutate(is_public = replace_na(is_public, 0L)) %>%
construct_analysis_vars()
cat("2022Q4:", nrow(df_2022q4), "| β^U available:", sum(!is.na(df_2022q4$f_pp)), "\n")
## 2022Q4: 4292 | β^U available: 4226
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),
any_fed = as.integer(!!sym(btfp_var) == 1 | !!sym(dw_var) == 1),
both_fed = as.integer(!!sym(btfp_var) == 1 & !!sym(dw_var) == 1),
# Total Fed amount borrowed (for intensive margin)
fed_amt = replace_na(!!sym(paste0(btfp_var, "_amt")), 0) +
replace_na(!!sym(paste0(dw_var, "_amt")), 0))
}
df_crisis <- join_all_borrowers(df_2022q4, btfp_crisis, dw_crisis, "btfp_crisis", "dw_crisis")
df_arb <- df_2023q3 %>%
left_join(btfp_arb %>% select(idrssd, btfp_arb, btfp_arb_amt), by = "idrssd") %>%
left_join(dw_arb %>% select(idrssd, dw_arb), by = "idrssd") %>%
mutate(btfp_arb = replace_na(btfp_arb, 0L), dw_arb = replace_na(dw_arb, 0L),
fhlb_user = as.integer(abnormal_fhlb_borrowing_10pct == 1),
any_fed = as.integer(btfp_arb == 1 | dw_arb == 1))
cat("Crisis:", nrow(df_crisis), "| Any Fed:", sum(df_crisis$any_fed), "\n")
## Crisis: 4292 | Any Fed: 828
Sample Cleaning
core_vars <- c("book_equity_to_total_asset", "total_asset", "total_liability",
"cash_to_total_asset", "security_to_total_asset",
"total_loan_to_total_asset", "loan_to_deposit", "roa",
"uninsured_deposit_to_total_asset", "wholesale_raw")
df_crisis_clean <- df_crisis %>%
filter(!is.na(mtm_loss_to_total_asset)) %>%
filter(if_all(all_of(core_vars), ~ !is.na(.))) %>%
filter(!is.na(uninsured_deposit) & !is.na(insured_deposit) & insured_deposit > 0) %>%
filter(!is.na(abnormal_fhlb_borrowing_10pct)) %>%
filter(total_asset > 0 & is.finite(ln_assets_raw))
# Theory model sample: require β^U (and hence f, f^U, v)
df_model <- df_crisis_clean %>% filter(!is.na(f_u_pp))
df_arb_clean <- df_arb %>%
filter(!is.na(mtm_loss_to_total_asset)) %>%
filter(if_all(all_of(core_vars), ~ !is.na(.))) %>%
filter(!is.na(uninsured_deposit) & !is.na(insured_deposit) & insured_deposit > 0) %>%
filter(!is.na(abnormal_fhlb_borrowing_10pct)) %>%
filter(total_asset > 0)
cat(sprintf("Clean: %d | Theory model (β^U available): %d | Arb: %d\n",
nrow(df_crisis_clean), nrow(df_model), nrow(df_arb_clean)))
## Clean: 4251 | Theory model (β^U available): 4226 | Arb: 4168
cat(sprintf("Fed borrowers in model sample: %d (%.1f%%)\n",
sum(df_model$any_fed), 100 * mean(df_model$any_fed)))
## Fed borrowers in model sample: 822 (19.5%)
DESCRIPTIVE STATISTICS
Franchise
Diagnostics
cat("=== FRANCHISE & RUN VALUE DIAGNOSTICS ===\n\n")
## === FRANCHISE & RUN VALUE DIAGNOSTICS ===
cat(sprintf("Banks: %d | Borrowers: %d (%.1f%%)\n",
nrow(df_model), sum(df_model$any_fed), 100 * mean(df_model$any_fed)))
## Banks: 4226 | Borrowers: 822 (19.5%)
cat(sprintf("\nTotal franchise f: mean=%.2f, med=%.2f, sd=%.2f\n",
mean(df_model$f_pp), median(df_model$f_pp), sd(df_model$f_pp)))
##
## Total franchise f: mean=2.66, med=2.38, sd=1.61
cat(sprintf("Uninsured franchise f^U: mean=%.2f, med=%.2f, sd=%.2f\n",
mean(df_model$f_u_pp, na.rm=T), median(df_model$f_u_pp, na.rm=T), sd(df_model$f_u_pp, na.rm=T)))
## Uninsured franchise f^U: mean=0.91, med=0.62, sd=1.05
cat(sprintf("Run value v: mean=%.2f, med=%.2f, sd=%.2f\n",
mean(df_model$v_pp, na.rm=T), median(df_model$v_pp, na.rm=T), sd(df_model$v_pp, na.rm=T)))
## Run value v: mean=5.86, med=5.33, sd=6.31
cat(sprintf("\nInputs: β^U(clipped): mean=%.3f | μ: mean=%.3f | D^U/D: mean=%.3f\n",
mean(df_model$beta_u_clipped, na.rm=T), mean(df_model$mu_decimal, na.rm=T),
mean(df_model$uninsured_share_d, na.rm=T)))
##
## Inputs: β^U(clipped): mean=0.331 | μ: mean=0.238 | D^U/D: mean=0.277
cat(sprintf(" c^U: mean=%.4f | Net rent ((1-β^U)y - c^U): mean=%.4f\n",
mean(df_model$cost_u_raw, na.rm=T),
mean(df_model$net_rent, na.rm=T)))
## c^U: mean=0.0108 | Net rent ((1-β^U)y - c^U): mean=0.0154
cat(sprintf(" Cap factor = 1/(y+δ) = %.2f | y=%.2f%%, δ=%.0f%%\n",
cap_factor, y_10yr*100, delta_decay*100))
## Cap factor = 1/(y+δ) = 7.18 | y=3.92%, δ=10%
Borrower vs
Non-Borrower
desc_vars <- c("ln_assets_raw", "mtm_total_raw", "mtm_btfp_raw", "mtm_other_raw",
"book_equity_ratio_raw", "cash_ratio_raw", "loan_to_deposit_raw",
"wholesale_raw", "roa_raw",
"uninsured_lev_raw", "uninsured_share_raw", "uninsured_beta_raw",
"cost_u_raw", "f_pp", "f_u_pp", "emv_pp", "v_pp")
desc_labels <- c("Log(Assets)", "Total MTM (ℓ)", "Securities MTM (ℓ_S)", "Loan MTM (ℓ_L)",
"Book Equity (e)", "Cash/TA", "Loan/Deposit",
"Wholesale (%)", "ROA",
"Uninsured/TA (μ)", "D^U/D", "β^U",
"Cost c^U", "Franchise (f)", "Uninsured Franchise (f^U)", "MV Equity (E^MV)", "Run Value (v)")
desc_tbl <- map_dfr(seq_along(desc_vars), function(i) {
v <- desc_vars[i]
b <- df_model[[v]][df_model$any_fed == 1]
n <- df_model[[v]][df_model$any_fed == 0]
tt <- tryCatch(t.test(b, n), error = function(e) NULL)
tibble(Variable = desc_labels[i],
Borrower = sprintf("%.3f (%.3f)", mean(b, na.rm=T), sd(b, na.rm=T)),
NonBorrower = sprintf("%.3f (%.3f)", mean(n, na.rm=T), sd(n, na.rm=T)),
Diff = round(mean(b, na.rm=T) - mean(n, na.rm=T), 3),
p = if (!is.null(tt)) tt$p.value else NA_real_,
Stars = format_pval(p))
}) %>% mutate(Difference = sprintf("%.3f%s", Diff, Stars))
kbl(desc_tbl %>% select(Variable, Borrower, NonBorrower, Difference),
format = "html", escape = FALSE,
col.names = c("Variable", "Borrower Mean (SD)", "Non-Borrower Mean (SD)", "Difference"),
caption = sprintf("Borrower vs Non-Borrower (N_B=%d, N_NB=%d)",
sum(df_model$any_fed), sum(df_model$any_fed == 0))) %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE) %>%
footnote(general = "*** p<0.01, ** p<0.05, * p<0.10 (Welch t-test).")
Borrower vs Non-Borrower (N_B=822, N_NB=3404)
|
Variable
|
Borrower Mean (SD)
|
Non-Borrower Mean (SD)
|
Difference
|
|
Log(Assets)
|
13.771 (1.525)
|
12.701 (1.367)
|
1.071***
|
|
Total MTM (ℓ)
|
5.936 (1.978)
|
5.384 (2.223)
|
0.551***
|
|
Securities MTM (ℓ_S)
|
0.812 (0.883)
|
0.649 (0.819)
|
0.163***
|
|
Loan MTM (ℓ_L)
|
4.984 (1.847)
|
4.542 (2.074)
|
0.443***
|
|
Book Equity (e)
|
8.585 (3.110)
|
9.851 (6.096)
|
-1.266***
|
|
Cash/TA
|
5.251 (5.521)
|
8.619 (9.161)
|
-3.367***
|
|
Loan/Deposit
|
74.216 (19.811)
|
70.645 (24.382)
|
3.572***
|
|
Wholesale (%)
|
1.529 (3.480)
|
0.868 (3.004)
|
0.661***
|
|
ROA
|
1.094 (0.586)
|
1.060 (1.919)
|
0.034
|
|
Uninsured/TA (μ)
|
27.133 (12.061)
|
22.944 (11.821)
|
4.189***
|
|
D^U/D
|
31.816 (14.201)
|
26.750 (13.805)
|
5.066***
|
|
β^U
|
0.342 (0.115)
|
0.329 (0.104)
|
0.014***
|
|
Cost c^U
|
0.011 (0.000)
|
0.011 (0.000)
|
0.000
|
|
Franchise (f)
|
2.953 (1.660)
|
2.583 (1.588)
|
0.369***
|
|
Uninsured Franchise (f^U)
|
1.106 (1.103)
|
0.866 (1.032)
|
0.240***
|
|
MV Equity (E^MV)
|
5.602 (4.347)
|
7.050 (6.838)
|
-1.448***
|
|
Run Value (v)
|
4.496 (4.061)
|
6.184 (6.704)
|
-1.688***
|
|
Note:
|
|
*** p<0.01, ** p<0.05, * p<0.10 (Welch t-test).
|
save_kbl_latex(desc_tbl %>% select(Variable, Borrower, NonBorrower, Difference),
"Table_Desc_BorrowerVsNon",
col.names = c("Variable", "Borrower", "Non-Borrower", "Difference"),
caption = "Borrower vs Non-Borrower: Descriptive Statistics")
## Saved: Table_Desc_BorrowerVsNon.tex
EQUITY WATERFALL: Jiang
→ Franchise → Run Value
Jiang AE says ~825 banks insolvent. Adding franchise value
rescues most. F^U shows what’s at stake in a run.
wf <- df_model %>%
mutate(B = ifelse(any_fed == 1, "Fed Borrower", "Non-Borrower")) %>%
group_by(B) %>%
summarise(
`Book Equity (e)` = mean(book_equity_ratio_raw, na.rm=T),
`− MTM Loss (ℓ)` = -mean(mtm_total_raw, na.rm=T),
`= Jiang AE` = mean(adjusted_equity_raw, na.rm=T),
`+ Franchise (f)` = mean(f_pp, na.rm=T),
`= E^MV` = mean(emv_pp, na.rm=T),
`− Unins. Franchise (f^U)` = -mean(f_u_pp, na.rm=T),
`= Run Value (v)` = mean(v_pp, na.rm=T),
.groups = "drop") %>%
pivot_longer(-B, names_to = "Component", values_to = "Value") %>%
mutate(
Component = factor(Component, levels = c("Book Equity (e)", "− MTM Loss (ℓ)",
"= Jiang AE", "+ Franchise (f)", "= E^MV",
"− Unins. Franchise (f^U)", "= Run Value (v)")),
Type = case_when(grepl("^=", Component) ~ "Sub", grepl("^−", Component) ~ "Neg", TRUE ~ "Pos"))
p_wf <- ggplot(wf, aes(x = Component, y = Value, fill = Type)) +
geom_col(alpha = 0.85, width = 0.65) +
geom_hline(yintercept = 0, linetype = "dashed") +
geom_text(aes(label = sprintf("%.2f", Value)),
vjust = ifelse(wf$Value >= 0, -0.3, 1.3), size = 3) +
facet_wrap(~ B) +
scale_fill_manual(values = c("Pos" = "#43A047", "Neg" = "#E53935", "Sub" = "#37474F"), guide = "none") +
scale_x_discrete(labels = function(x) str_wrap(x, 12)) +
labs(title = "Equity Waterfall: Book → MTM → Franchise → Run Value",
subtitle = "Mean (pp of TA). v = E^MV − F^U: negative means a run equilibrium is possible.",
x = NULL, y = "pp of TA") +
theme_minimal(base_size = 13) +
theme(plot.title = element_text(face = "bold"), axis.text.x = element_text(size = 9))
print(p_wf)

PREDICTION 1: EXTENSIVE MARGIN [Eq. 13]
Theory Eq. (11/13): \(c_3
> 0\). The sensitivity of borrowing probability to MTM losses
is increasing in the uninsured franchise \(f^U\). The interaction \(\ell \times f^U\) is THE coordination
signature.
7.1 Main Table: Eq.
(13)
controls <- c("ln_assets", "cash_ratio", "loan_to_deposit", "book_equity_ratio", "wholesale", "roa")
ctrl_str <- paste(controls, collapse = " + ")
# Eq. 13: Pr(Borrow) = α + c₁ℓ + c₂f^U + c₃(ℓ×f^U) + γ'X + ε
fml_main <- as.formula(paste0("any_fed ~ mtm_total + uninsured_franchise + mtm_x_f_u + ", ctrl_str))
coef_map <- c("mtm_total" = "ℓ (MTM Loss)", "uninsured_franchise" = "f^U (Uninsured Franchise)",
"mtm_x_f_u" = "ℓ × f^U (KEY: c₃>0)",
"ln_assets" = "Log(Assets)", "cash_ratio" = "Cash/TA",
"loan_to_deposit" = "Loan/Dep", "book_equity_ratio" = "Equity/TA",
"wholesale" = "Wholesale", "roa" = "ROA")
reg_main <- list(
"Any Fed" = feols(fml_main, data = df_model, vcov = "hetero"),
"BTFP" = feols(update(fml_main, btfp_crisis ~ .), data = df_model, vcov = "hetero"),
"DW" = feols(update(fml_main, dw_crisis ~ .), data = df_model, vcov = "hetero")
)
msummary(reg_main, stars = c("*"=.10, "**"=.05, "***"=.01), gof_omit = "AIC|BIC|Log|RMSE",
add_rows = dv_summary_rows(reg_main), coef_rename = coef_map,
title = "Eq. (13): Extensive Margin — Probability of Emergency Borrowing")
Eq. (13): Extensive Margin — Probability of Emergency Borrowing
| |
Any Fed |
BTFP |
DW |
| * p < 0.1, ** p < 0.05, *** p < 0.01 |
| (Intercept) |
0.194*** |
0.117*** |
0.102*** |
|
(0.006) |
(0.005) |
(0.005) |
| ℓ (MTM Loss) |
0.027*** |
0.018*** |
0.011** |
|
(0.006) |
(0.005) |
(0.005) |
| f^U (Uninsured Franchise) |
0.019*** |
0.017*** |
0.010* |
|
(0.007) |
(0.006) |
(0.006) |
| ℓ × f^U (KEY: c₃>0) |
0.021*** |
0.013** |
0.017*** |
|
(0.006) |
(0.005) |
(0.005) |
| Log(Assets) |
0.103*** |
0.054*** |
0.075*** |
|
(0.008) |
(0.006) |
(0.007) |
| Cash/TA |
-0.021*** |
-0.026*** |
0.001 |
|
(0.006) |
(0.005) |
(0.005) |
| Loan/Dep |
-0.005 |
-0.010* |
0.001 |
|
(0.007) |
(0.006) |
(0.006) |
| Equity/TA |
-0.019*** |
-0.018*** |
-0.002 |
|
(0.006) |
(0.005) |
(0.004) |
| Wholesale |
0.029*** |
0.022*** |
0.015*** |
|
(0.007) |
(0.006) |
(0.005) |
| ROA |
-0.002 |
-0.005 |
-0.002 |
|
(0.006) |
(0.005) |
(0.005) |
| Num.Obs. |
4226 |
4226 |
4226 |
| R2 |
0.110 |
0.068 |
0.074 |
| R2 Adj. |
0.109 |
0.066 |
0.072 |
| Std.Errors |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
| N (DV = 1) |
822 |
496 |
430 |
| Mean (DV) |
0.1945 |
0.1174 |
0.1018 |
save_reg_latex(reg_main, "Table_Eq13_Main",
add_rows = dv_summary_rows(reg_main), coef_rename = coef_map,
title = "Eq.\\ (13): Extensive Margin")
## Saved: Table_Eq13_Main.tex
7.2 Progressive
Build-Up
Shows the reader what each ingredient adds. Column 1
= Jiang (losses only). Column 2 = add f^U. Column 3 = add the
interaction (full theory).
reg_prog <- list(
"Losses only" = feols(as.formula(paste0("any_fed ~ mtm_total + ", ctrl_str)),
data = df_model, vcov = "hetero"),
"+ f^U" = feols(as.formula(paste0("any_fed ~ mtm_total + uninsured_franchise + ", ctrl_str)),
data = df_model, vcov = "hetero"),
"+ ℓ×f^U (Eq.13)" = feols(fml_main, data = df_model, vcov = "hetero")
)
msummary(reg_prog, stars = c("*"=.10, "**"=.05, "***"=.01), gof_omit = "AIC|BIC|Log|RMSE",
add_rows = dv_summary_rows(reg_prog), coef_rename = coef_map,
title = "Progressive Build-Up: Jiang → + Uninsured Franchise → + Interaction")
Progressive Build-Up: Jiang → + Uninsured Franchise → + Interaction
| |
Losses only |
+ f^U |
+ ℓ×f^U (Eq.13) |
| * p < 0.1, ** p < 0.05, *** p < 0.01 |
| (Intercept) |
0.192*** |
0.192*** |
0.194*** |
|
(0.006) |
(0.006) |
(0.006) |
| ℓ (MTM Loss) |
0.022*** |
0.024*** |
0.027*** |
|
(0.006) |
(0.006) |
(0.006) |
| Log(Assets) |
0.109*** |
0.104*** |
0.103*** |
|
(0.007) |
(0.008) |
(0.008) |
| Cash/TA |
-0.022*** |
-0.023*** |
-0.021*** |
|
(0.006) |
(0.006) |
(0.006) |
| Loan/Dep |
-0.010 |
-0.007 |
-0.005 |
|
(0.007) |
(0.007) |
(0.007) |
| Equity/TA |
-0.021*** |
-0.021*** |
-0.019*** |
|
(0.006) |
(0.006) |
(0.006) |
| Wholesale |
0.028*** |
0.029*** |
0.029*** |
|
(0.007) |
(0.007) |
(0.007) |
| ROA |
0.000 |
-0.001 |
-0.002 |
|
(0.006) |
(0.006) |
(0.006) |
| f^U (Uninsured Franchise) |
|
0.013* |
0.019*** |
|
|
(0.007) |
(0.007) |
| ℓ × f^U (KEY: c₃>0) |
|
|
0.021*** |
|
|
|
(0.006) |
| Num.Obs. |
4226 |
4226 |
4226 |
| R2 |
0.107 |
0.108 |
0.110 |
| R2 Adj. |
0.105 |
0.106 |
0.109 |
| Std.Errors |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
| N (DV = 1) |
822 |
822 |
822 |
| Mean (DV) |
0.1945 |
0.1945 |
0.1945 |
save_reg_latex(reg_prog, "Table_Eq13_Progressive",
add_rows = dv_summary_rows(reg_prog), coef_rename = coef_map,
title = "Progressive Build-Up: Losses $\\to$ $+f^U$ $\\to$ $+\\ell \\times f^U$")
## Saved: Table_Eq13_Progressive.tex
7.3 Observable vs
Unobservable Signal
Theory Eq. (14): Securities losses ℓ_S are publicly
observable and determine BTFP capacity. Loan losses ℓ_L are
unobservable. Coordination should amplify through ℓ_S × f^U, not ℓ_L ×
f^U.
df_signal <- df_model %>%
mutate(sec_x_f_u = mtm_sec * uninsured_franchise,
loan_x_f_u = mtm_loan * uninsured_franchise)
fml_signal <- as.formula(paste0(
"any_fed ~ mtm_sec + mtm_loan + uninsured_franchise + sec_x_f_u + loan_x_f_u + ", ctrl_str))
reg_signal <- list(
"Any Fed" = feols(fml_signal, data = df_signal, vcov = "hetero"),
"BTFP" = feols(update(fml_signal, btfp_crisis ~ .), data = df_signal, vcov = "hetero"),
"DW" = feols(update(fml_signal, dw_crisis ~ .), data = df_signal, vcov = "hetero")
)
msummary(reg_signal, stars = c("*"=.10, "**"=.05, "***"=.01), gof_omit = "AIC|BIC|Log|RMSE",
add_rows = dv_summary_rows(reg_signal),
coef_rename = c("mtm_sec" = "ℓ_S (Securities)", "mtm_loan" = "ℓ_L (Loans)",
"uninsured_franchise" = "f^U", "sec_x_f_u" = "ℓ_S × f^U (observable; expect >0)",
"loan_x_f_u" = "ℓ_L × f^U (unobservable; expect ≈0)"),
title = "Observable vs Unobservable Signal: ℓ_S × f^U vs ℓ_L × f^U")
Observable vs Unobservable Signal: ℓ_S × f^U vs ℓ_L × f^U
| |
Any Fed |
BTFP |
DW |
| * p < 0.1, ** p < 0.05, *** p < 0.01 |
| (Intercept) |
0.195*** |
0.117*** |
0.103*** |
|
(0.006) |
(0.005) |
(0.005) |
| ℓ_S (Securities) |
0.029*** |
0.022*** |
0.010 |
|
(0.009) |
(0.008) |
(0.007) |
| ℓ_L (Loans) |
0.022*** |
0.011** |
0.009 |
|
(0.007) |
(0.006) |
(0.006) |
| f^U |
0.020*** |
0.017*** |
0.011* |
|
(0.007) |
(0.006) |
(0.006) |
| ℓ_S × f^U (observable; expect >0) |
0.012** |
0.009* |
0.011** |
|
(0.006) |
(0.005) |
(0.005) |
| ℓ_L × f^U (unobservable; expect ≈0) |
0.023*** |
0.010* |
0.016*** |
|
(0.006) |
(0.006) |
(0.006) |
| ln_assets |
0.102*** |
0.054*** |
0.075*** |
|
(0.008) |
(0.006) |
(0.007) |
| cash_ratio |
-0.018** |
-0.022*** |
0.002 |
|
(0.007) |
(0.005) |
(0.006) |
| loan_to_deposit |
0.001 |
-0.001 |
0.002 |
|
(0.010) |
(0.009) |
(0.008) |
| book_equity_ratio |
-0.018*** |
-0.017*** |
-0.002 |
|
(0.006) |
(0.005) |
(0.004) |
| wholesale |
0.028*** |
0.021*** |
0.015*** |
|
(0.007) |
(0.006) |
(0.005) |
| roa |
-0.002 |
-0.005 |
-0.002 |
|
(0.006) |
(0.005) |
(0.005) |
| Num.Obs. |
4226 |
4226 |
4226 |
| R2 |
0.111 |
0.069 |
0.075 |
| R2 Adj. |
0.109 |
0.066 |
0.072 |
| Std.Errors |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
| N (DV = 1) |
822 |
496 |
430 |
| Mean (DV) |
0.1945 |
0.1174 |
0.1018 |
save_reg_latex(reg_signal, "Table_Eq13_Signal",
add_rows = dv_summary_rows(reg_signal),
coef_rename = c("mtm_sec"="$\\ell_S$", "mtm_loan"="$\\ell_L$",
"uninsured_franchise"="$f^U$",
"sec_x_f_u"="$\\ell_S \\times f^U$", "loan_x_f_u"="$\\ell_L \\times f^U$"),
title = "Observable vs.\\ Unobservable Signal")
## Saved: Table_Eq13_Signal.tex
7.4 By Facility: ℓ_S
vs ℓ_L
Theory Eq. (14): Securities losses should predict
BTFP (par channel), not DW.
fml_sep <- function(dv) as.formula(paste0(dv, " ~ mtm_sec + mtm_loan + uninsured_franchise + ", ctrl_str))
reg_facility <- list(
"BTFP" = feols(fml_sep("btfp_crisis"), data = df_model, vcov = "hetero"),
"DW" = feols(fml_sep("dw_crisis"), data = df_model, vcov = "hetero"),
"FHLB" = feols(fml_sep("fhlb_user"), data = df_model, vcov = "hetero")
)
msummary(reg_facility, stars = c("*"=.10, "**"=.05, "***"=.01), gof_omit = "AIC|BIC|Log|RMSE",
add_rows = dv_summary_rows(reg_facility),
coef_rename = c("mtm_sec" = "ℓ_S (Securities)", "mtm_loan" = "ℓ_L (Loans)",
"uninsured_franchise" = "f^U"),
title = "Par-Value Channel: ℓ_S Should Predict BTFP, Not DW or FHLB (Eq. 14)")
Par-Value Channel: ℓ_S Should Predict BTFP, Not DW or FHLB (Eq. 14)
| |
BTFP |
DW |
FHLB |
| * p < 0.1, ** p < 0.05, *** p < 0.01 |
| (Intercept) |
0.115*** |
0.100*** |
0.070*** |
|
(0.005) |
(0.004) |
(0.004) |
| ℓ_S (Securities) |
0.022*** |
0.010 |
-0.008 |
|
(0.008) |
(0.007) |
(0.006) |
| ℓ_L (Loans) |
0.008 |
0.005 |
0.007 |
|
(0.005) |
(0.005) |
(0.005) |
| f^U |
0.013** |
0.005 |
-0.002 |
|
(0.006) |
(0.006) |
(0.004) |
| ln_assets |
0.054*** |
0.076*** |
0.016*** |
|
(0.006) |
(0.007) |
(0.005) |
| cash_ratio |
-0.022*** |
0.001 |
-0.020*** |
|
(0.005) |
(0.006) |
(0.004) |
| loan_to_deposit |
-0.001 |
0.002 |
0.013* |
|
(0.009) |
(0.008) |
(0.007) |
| book_equity_ratio |
-0.018*** |
-0.004 |
0.008* |
|
(0.004) |
(0.004) |
(0.004) |
| wholesale |
0.021*** |
0.015*** |
-0.001 |
|
(0.006) |
(0.005) |
(0.004) |
| roa |
-0.005 |
-0.001 |
-0.007* |
|
(0.005) |
(0.005) |
(0.004) |
| Num.Obs. |
4226 |
4226 |
4226 |
| R2 |
0.067 |
0.072 |
0.025 |
| R2 Adj. |
0.065 |
0.070 |
0.023 |
| Std.Errors |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
| N (DV = 1) |
496 |
430 |
299 |
| Mean (DV) |
0.1174 |
0.1018 |
0.0708 |
save_reg_latex(reg_facility, "Table_Eq14_Facility",
add_rows = dv_summary_rows(reg_facility),
coef_rename = c("mtm_sec"="$\\ell_S$", "mtm_loan"="$\\ell_L$", "uninsured_franchise"="$f^U$"),
title = "Par-Value Channel: $\\ell_S$ Predicts BTFP (Eq.\\ 14)")
## Saved: Table_Eq14_Facility.tex
PREDICTION 2: INTENSIVE MARGIN [Eq. 15]
Theory Eq. (15): Among borrowers, amounts should
increase in securities losses ℓ_S (which determine BTFP capacity
Δ_BTFP), but not necessarily in total losses or loan losses.
8.1 Amount
Regressions
# Among Fed borrowers only
df_borr <- df_model %>%
filter(any_fed == 1) %>%
mutate(
# Log amount (in thousands)
ln_fed_amt = log(fed_amt + 1),
ln_btfp_amt = log(replace_na(btfp_crisis_amt, 0) + 1),
# Normalized: amount / total assets
fed_amt_ta = fed_amt / (total_asset * 1000), # total_asset likely in thousands
btfp_amt_ta = replace_na(btfp_crisis_amt, 0) / (total_asset * 1000)
)
cat(sprintf("Borrowers: %d | Mean Fed amount: $%.0fM | Mean BTFP amount: $%.0fM\n",
nrow(df_borr),
mean(df_borr$fed_amt, na.rm=T) / 1e6,
mean(df_borr$btfp_crisis_amt, na.rm=T) / 1e6))
## Borrowers: 822 | Mean Fed amount: $813M | Mean BTFP amount: $263M
if (nrow(df_borr) >= 30) {
# Eq. 15: Amount = α + d₁ ℓ_S + γ'X + ε
fml_amt <- as.formula(paste0("ln_fed_amt ~ mtm_sec + mtm_loan + uninsured_franchise + ", ctrl_str))
fml_btfp_amt <- as.formula(paste0("ln_btfp_amt ~ mtm_sec + mtm_loan + uninsured_franchise + ", ctrl_str))
reg_intensive <- list(
"ln(Fed Amount)" = feols(fml_amt, data = df_borr, vcov = "hetero"),
"ln(BTFP Amount)" = feols(fml_btfp_amt, data = df_borr %>% filter(btfp_crisis == 1), vcov = "hetero")
)
msummary(reg_intensive, stars = c("*"=.10, "**"=.05, "***"=.01), gof_omit = "AIC|BIC|Log|RMSE",
coef_rename = c("mtm_sec" = "ℓ_S (Securities; d₁>0)",
"mtm_loan" = "ℓ_L (Loans)", "uninsured_franchise" = "f^U"),
title = "Eq. (15): Intensive Margin — Amount Borrowed (Among Borrowers Only)",
notes = "Theory: d₁ > 0. Securities losses determine BTFP capacity (Δ_BTFP).")
save_reg_latex(reg_intensive, "Table_Eq15_Intensive",
coef_rename = c("mtm_sec"="$\\ell_S$ ($d_1>0$)", "mtm_loan"="$\\ell_L$",
"uninsured_franchise"="$f^U$"),
title = "Eq.\\ (15): Intensive Margin --- Amount Borrowed")
}
## Saved: Table_Eq15_Intensive.tex
FALSIFICATION TESTS
9.1 FHLB
Falsification
Theory scope condition: FHLB borrowing is routine,
not run-driven. \(c_3 \approx 0\).
fml_fhlb <- as.formula(paste0("fhlb_user ~ mtm_total + uninsured_franchise + mtm_x_f_u + ", ctrl_str))
reg_falsif <- list(
"Any Fed (crisis)" = feols(fml_main, data = df_model, vcov = "hetero"),
"FHLB (crisis)" = feols(fml_fhlb, data = df_model, vcov = "hetero")
)
msummary(reg_falsif, stars = c("*"=.10, "**"=.05, "***"=.01), gof_omit = "AIC|BIC|Log|RMSE",
add_rows = dv_summary_rows(reg_falsif), coef_rename = coef_map,
title = "Falsification: FHLB Should NOT Show ℓ × f^U > 0",
notes = "FHLB = routine advance. Significant ℓ × f^U for FHLB would undermine coordination interpretation.")
Falsification: FHLB Should NOT Show ℓ × f^U > 0
| |
Any Fed (crisis) |
FHLB (crisis) |
| * p < 0.1, ** p < 0.05, *** p < 0.01 |
| FHLB = routine advance. Significant ℓ × f^U for FHLB would undermine coordination interpretation. |
| (Intercept) |
0.194*** |
0.069*** |
|
(0.006) |
(0.004) |
| ℓ (MTM Loss) |
0.027*** |
-0.000 |
|
(0.006) |
(0.005) |
| f^U (Uninsured Franchise) |
0.019*** |
-0.004 |
|
(0.007) |
(0.004) |
| ℓ × f^U (KEY: c₃>0) |
0.021*** |
-0.007** |
|
(0.006) |
(0.003) |
| Log(Assets) |
0.103*** |
0.016*** |
|
(0.008) |
(0.005) |
| Cash/TA |
-0.021*** |
-0.016*** |
|
(0.006) |
(0.004) |
| Loan/Dep |
-0.005 |
0.022*** |
|
(0.007) |
(0.005) |
| Equity/TA |
-0.019*** |
0.007* |
|
(0.006) |
(0.004) |
| Wholesale |
0.029*** |
-0.002 |
|
(0.007) |
(0.004) |
| ROA |
-0.002 |
-0.007* |
|
(0.006) |
(0.004) |
| Num.Obs. |
4226 |
4226 |
| R2 |
0.110 |
0.025 |
| R2 Adj. |
0.109 |
0.023 |
| Std.Errors |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
| N (DV = 1) |
822 |
299 |
| Mean (DV) |
0.1945 |
0.0708 |
save_reg_latex(reg_falsif, "Table_Falsif_FHLB",
add_rows = dv_summary_rows(reg_falsif), coef_rename = coef_map,
title = "Falsification: FHLB vs.\\ Fed Borrowing")
## Saved: Table_Falsif_FHLB.tex
9.2 Temporal
Falsification: Arb Period
Theory scope condition: After Nov 2023, BTFP rate
fell below IOR → carry trade. \(c_3 \approx
0\).
df_arb_model <- df_arb_clean %>% filter(!is.na(f_u_pp))
df_arb_reg <- df_arb_model %>%
mutate(
mtm_total_a = standardize_z(winsorize(mtm_total_raw)),
f_u_a = standardize_z(winsorize(f_u_pp)),
mtm_x_f_u_a = mtm_total_a * f_u_a,
ln_assets_a = standardize_z(winsorize(ln_assets_raw)),
cash_ratio_a = standardize_z(winsorize(cash_ratio_raw)),
loan_to_deposit_a = standardize_z(winsorize(loan_to_deposit_raw)),
book_equity_ratio_a = standardize_z(winsorize(book_equity_ratio_raw)),
wholesale_a = standardize_z(winsorize(wholesale_raw)),
roa_a = standardize_z(winsorize(roa_raw))
)
arb_ctrl <- c("ln_assets_a", "cash_ratio_a", "loan_to_deposit_a",
"book_equity_ratio_a", "wholesale_a", "roa_a")
fml_arb <- as.formula(paste0("any_fed ~ mtm_total_a + f_u_a + mtm_x_f_u_a + ",
paste(arb_ctrl, collapse = " + ")))
reg_arb <- list(
"Crisis: Any Fed" = feols(fml_main, data = df_model, vcov = "hetero")
)
if (sum(df_arb_reg$any_fed) >= 10 & sum(df_arb_reg$any_fed == 0) >= 10)
reg_arb[["Arb: Any Fed"]] <- feols(fml_arb, data = df_arb_reg, vcov = "hetero")
arb_map <- c("mtm_total" = "ℓ", "uninsured_franchise" = "f^U", "mtm_x_f_u" = "ℓ × f^U",
"mtm_total_a" = "ℓ", "f_u_a" = "f^U", "mtm_x_f_u_a" = "ℓ × f^U",
"ln_assets" = "Log(Assets)", "ln_assets_a" = "Log(Assets)",
"cash_ratio" = "Cash/TA", "cash_ratio_a" = "Cash/TA",
"loan_to_deposit" = "Loan/Dep", "loan_to_deposit_a" = "Loan/Dep",
"book_equity_ratio" = "Equity/TA", "book_equity_ratio_a" = "Equity/TA",
"wholesale" = "Wholesale", "wholesale_a" = "Wholesale",
"roa" = "ROA", "roa_a" = "ROA")
msummary(reg_arb, stars = c("*"=.10, "**"=.05, "***"=.01), gof_omit = "AIC|BIC|Log|RMSE",
coef_rename = arb_map,
title = "Temporal Falsification: Crisis vs Arb Period",
notes = "ℓ × f^U significant in crisis, ≈ 0 in arb period (carry trade, not coordination).")
Temporal Falsification: Crisis vs Arb Period
| |
Crisis: Any Fed |
Arb: Any Fed |
| * p < 0.1, ** p < 0.05, *** p < 0.01 |
| ℓ × f^U significant in crisis, ≈ 0 in arb period (carry trade, not coordination). |
| (Intercept) |
0.194*** |
0.241*** |
|
(0.006) |
(0.006) |
| ℓ |
0.027*** |
0.014* |
|
(0.006) |
(0.007) |
| f^U |
0.019*** |
0.007 |
|
(0.007) |
(0.007) |
| ℓ × f^U |
0.021*** |
0.006 |
|
(0.006) |
(0.006) |
| Log(Assets) |
0.103*** |
0.092*** |
|
(0.008) |
(0.008) |
| Cash/TA |
-0.021*** |
-0.033*** |
|
(0.006) |
(0.007) |
| Loan/Dep |
-0.005 |
-0.001 |
|
(0.007) |
(0.007) |
| Equity/TA |
-0.019*** |
-0.012** |
|
(0.006) |
(0.006) |
| Wholesale |
0.029*** |
0.099*** |
|
(0.007) |
(0.008) |
| ROA |
-0.002 |
-0.024*** |
|
(0.006) |
(0.006) |
| Num.Obs. |
4226 |
4151 |
| R2 |
0.110 |
0.150 |
| R2 Adj. |
0.109 |
0.148 |
| Std.Errors |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
save_reg_latex(reg_arb, "Table_Falsif_Arb",
coef_rename = arb_map,
title = "Temporal Falsification: Crisis vs.\\ Arb Period")
## Saved: Table_Falsif_Arb.tex
ROBUSTNESS
10.1 Logit
fml_logit <- as.formula(paste0("any_fed ~ mtm_total + uninsured_franchise + mtm_x_f_u + ", ctrl_str))
reg_logit <- list(
"LPM (baseline)" = feols(fml_main, data = df_model, vcov = "hetero"),
"Logit" = feglm(fml_logit, data = df_model, family = binomial("logit"), vcov = "hetero")
)
msummary(reg_logit, stars = c("*"=.10, "**"=.05, "***"=.01), gof_omit = "AIC|BIC|Log|RMSE",
coef_rename = coef_map,
title = "Robustness: LPM vs Logit")
Robustness: LPM vs Logit
| |
LPM (baseline) |
Logit |
| * p < 0.1, ** p < 0.05, *** p < 0.01 |
| (Intercept) |
0.194*** |
-1.684*** |
|
(0.006) |
(0.048) |
| ℓ (MTM Loss) |
0.027*** |
0.188*** |
|
(0.006) |
(0.048) |
| f^U (Uninsured Franchise) |
0.019*** |
0.109** |
|
(0.007) |
(0.045) |
| ℓ × f^U (KEY: c₃>0) |
0.021*** |
0.085** |
|
(0.006) |
(0.042) |
| Log(Assets) |
0.103*** |
0.654*** |
|
(0.008) |
(0.048) |
| Cash/TA |
-0.021*** |
-0.268*** |
|
(0.006) |
(0.071) |
| Loan/Dep |
-0.005 |
0.020 |
|
(0.007) |
(0.055) |
| Equity/TA |
-0.019*** |
-0.257*** |
|
(0.006) |
(0.061) |
| Wholesale |
0.029*** |
0.159*** |
|
(0.007) |
(0.039) |
| ROA |
-0.002 |
0.033 |
|
(0.006) |
(0.048) |
| Num.Obs. |
4226 |
4226 |
| R2 |
0.110 |
0.117 |
| R2 Adj. |
0.109 |
0.112 |
| Std.Errors |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
save_reg_latex(reg_logit, "Table_Robust_Logit",
coef_rename = coef_map,
title = "Robustness: LPM vs.\\ Logit")
## Saved: Table_Robust_Logit.tex
10.2 Decay Rate
Sensitivity
DSSW baseline: δ = 10%. Lower δ → slower deposit
decay → higher f and f^U.
delta_grid <- c(0.05, 0.10, 0.15)
reg_delta <- list()
for (dd in delta_grid) {
cf_dd <- 1 / (y_10yr + dd)
lab <- sprintf("δ=%.0f%% (1/(y+δ)=%.2f)%s", dd*100, cf_dd,
ifelse(dd == delta_decay, " [baseline]", ""))
df_d <- df_model %>%
mutate(
c_u = ifelse(!is.na(cost_u_raw), cost_u_raw, 0),
f_d = pmax(((1 - beta_u_clipped) * y_10yr - c_u) * cf_dd * mu_decimal, 0) * 100,
f_u_d = uninsured_share_d * f_d,
f_u_d_w = winsorize(f_u_d),
fu_d = standardize_z(f_u_d_w),
mtm_x_fu_d = mtm_total * fu_d
)
fml_d <- as.formula(paste0("any_fed ~ mtm_total + fu_d + mtm_x_fu_d + ", ctrl_str))
reg_delta[[lab]] <- feols(fml_d, data = df_d, vcov = "hetero")
}
msummary(reg_delta, stars = c("*"=.10, "**"=.05, "***"=.01), gof_omit = "AIC|BIC|Log|RMSE",
coef_rename = c("mtm_total" = "ℓ", "fu_d" = "f^U", "mtm_x_fu_d" = "ℓ × f^U"),
title = "Robustness: Eq. (13) Under Alternative Deposit Decay Rates",
notes = "DSSW baseline: δ=10%. ℓ × f^U should be stable across decay rates.")
Robustness: Eq. (13) Under Alternative Deposit Decay Rates
| |
δ=5% (1/(y+δ)=11.21) |
δ=10% (1/(y+δ)=7.18) [baseline] |
δ=15% (1/(y+δ)=5.29) |
| * p < 0.1, ** p < 0.05, *** p < 0.01 |
| DSSW baseline: δ=10%. ℓ × f^U should be stable across decay rates. |
| (Intercept) |
0.194*** |
0.194*** |
0.194*** |
|
(0.006) |
(0.006) |
(0.006) |
| ℓ |
0.027*** |
0.027*** |
0.027*** |
|
(0.006) |
(0.006) |
(0.006) |
| f^U |
0.019*** |
0.019*** |
0.019*** |
|
(0.007) |
(0.007) |
(0.007) |
| ℓ × f^U |
0.021*** |
0.021*** |
0.021*** |
|
(0.006) |
(0.006) |
(0.006) |
| ln_assets |
0.103*** |
0.103*** |
0.103*** |
|
(0.008) |
(0.008) |
(0.008) |
| cash_ratio |
-0.021*** |
-0.021*** |
-0.021*** |
|
(0.006) |
(0.006) |
(0.006) |
| loan_to_deposit |
-0.005 |
-0.005 |
-0.005 |
|
(0.007) |
(0.007) |
(0.007) |
| book_equity_ratio |
-0.019*** |
-0.019*** |
-0.019*** |
|
(0.006) |
(0.006) |
(0.006) |
| wholesale |
0.029*** |
0.029*** |
0.029*** |
|
(0.007) |
(0.007) |
(0.007) |
| roa |
-0.002 |
-0.002 |
-0.002 |
|
(0.006) |
(0.006) |
(0.006) |
| Num.Obs. |
4226 |
4226 |
4226 |
| R2 |
0.110 |
0.110 |
0.110 |
| R2 Adj. |
0.109 |
0.109 |
0.109 |
| Std.Errors |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
save_reg_latex(reg_delta, "Table_Robust_Delta",
coef_rename = c("mtm_total"="$\\ell$", "fu_d"="$f^U$", "mtm_x_fu_d"="$\\ell \\times f^U$"),
title = "Robustness: Alternative Deposit Decay Rates")
## Saved: Table_Robust_Delta.tex
delta_grid <- c(0.05, 0.10, 0.15)
reg_delta <- list()
for (dd in delta_grid) {
cf_dd <- 1 / (y_10yr + dd)
lab <- sprintf("δ=%.0f%% (1/(y+δ)=%.2f)%s", dd*100, cf_dd,
ifelse(dd == delta_decay, " [baseline]", ""))
df_d <- df_model %>%
mutate(
c_u = ifelse(!is.na(cost_u_raw), cost_u_raw, 0),
f_d = pmax(((1 - beta_u_clipped) * y_10yr - c_u) * cf_dd * mu_decimal, 0) * 100,
f_u_d = uninsured_share_d * f_d,
f_u_d_w = winsorize(f_u_d),
# MEAN-CENTER THE RAW VARIABLES
mtm_c = mtm_total_w - mean(mtm_total_w, na.rm = TRUE),
f_u_c = f_u_d_w - mean(f_u_d_w, na.rm = TRUE),
# INTERACT THE CENTERED VARIABLES
mtm_x_fu_c = mtm_c * f_u_c
)
# Use the centered variables in the regression
fml_d <- as.formula(paste0("any_fed ~ mtm_c + f_u_c + mtm_x_fu_c + ", ctrl_str))
reg_delta[[lab]] <- feols(fml_d, data = df_d, vcov = "hetero")
}
# 1. Print to RMarkdown HTML document
msummary(reg_delta, stars = c("*"=.10, "**"=.05, "***"=.01), gof_omit = "AIC|BIC|Log|RMSE",
coef_rename = c("mtm_c" = "ℓ (Centered)", "f_u_c" = "f^U (Centered)", "mtm_x_fu_c" = "ℓ × f^U (Centered)"),
title = "Robustness: Eq. (13) Under Alternative Deposit Decay Rates (Mean-Centered)",
notes = "Explanatory variables are mean-centered raw values. Controls are Z-standardized.")
Robustness: Eq. (13) Under Alternative Deposit Decay Rates (Mean-Centered)
| |
δ=5% (1/(y+δ)=11.21) |
δ=10% (1/(y+δ)=7.18) [baseline] |
δ=15% (1/(y+δ)=5.29) |
| * p < 0.1, ** p < 0.05, *** p < 0.01 |
| Explanatory variables are mean-centered raw values. Controls are Z-standardized. |
| (Intercept) |
0.195*** |
0.195*** |
0.195*** |
|
(0.006) |
(0.006) |
(0.006) |
| ℓ (Centered) |
0.013*** |
0.013*** |
0.013*** |
|
(0.003) |
(0.003) |
(0.003) |
| f^U (Centered) |
0.015*** |
0.023*** |
0.031*** |
|
(0.006) |
(0.009) |
(0.012) |
| ℓ × f^U (Centered) |
0.008*** |
0.012*** |
0.016*** |
|
(0.002) |
(0.003) |
(0.004) |
| ln_assets |
0.103*** |
0.103*** |
0.103*** |
|
(0.008) |
(0.008) |
(0.008) |
| cash_ratio |
-0.021*** |
-0.021*** |
-0.021*** |
|
(0.006) |
(0.006) |
(0.006) |
| loan_to_deposit |
-0.005 |
-0.005 |
-0.005 |
|
(0.007) |
(0.007) |
(0.007) |
| book_equity_ratio |
-0.019*** |
-0.019*** |
-0.019*** |
|
(0.006) |
(0.006) |
(0.006) |
| wholesale |
0.029*** |
0.029*** |
0.029*** |
|
(0.007) |
(0.007) |
(0.007) |
| roa |
-0.002 |
-0.002 |
-0.002 |
|
(0.006) |
(0.006) |
(0.006) |
| Num.Obs. |
4226 |
4226 |
4226 |
| R2 |
0.110 |
0.110 |
0.110 |
| R2 Adj. |
0.109 |
0.109 |
0.109 |
| Std.Errors |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
# 2. Save to LaTeX folder
save_reg_latex(reg_delta, "Table_Robust_Delta_Centered",
coef_rename = c("mtm_c" = "$\\ell$ (Centered)", "f_u_c" = "$f^U$ (Centered)", "mtm_x_fu_c" = "$\\ell \\times f^U$ (Centered)"),
title = "Robustness: Eq. (13) Under Alternative Deposit Decay Rates (Mean-Centered)",
notes = "DSSW baseline: $\\delta=10\\%$. Explanatory variables are mean-centered raw values to preserve magnitude shifts. Controls are Z-standardized.")
## Saved: Table_Robust_Delta_Centered.tex
PLOTS
borr_colors <- c("Fed Borrower" = "#C62828", "Non-Borrower" = "#1565C0")
theme_gp <- theme_minimal(base_size = 13) +
theme(plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(color = "grey40", size = 11),
plot.caption = element_text(color = "grey50", size = 9, hjust = 0),
legend.position = "bottom", panel.grid.minor = element_blank())
Scatter: ℓ vs
f^U
p1 <- ggplot(df_model, aes(x = mtm_total_raw, y = f_u_pp)) +
geom_point(aes(color = ifelse(any_fed == 1, "Fed Borrower", "Non-Borrower"),
shape = ifelse(any_fed == 1, "Fed Borrower", "Non-Borrower")),
alpha = 0.5, size = 1.8) +
scale_color_manual(values = borr_colors, name = NULL) +
scale_shape_manual(values = c("Fed Borrower" = 17, "Non-Borrower" = 1), name = NULL) +
labs(title = "MTM Losses vs Uninsured Franchise Value",
subtitle = "Triangles = Fed borrowers. Theory: borrowing concentrates in the upper-right (high ℓ, high f^U)",
x = "Total MTM Loss / TA (pp)", y = "Uninsured Franchise f^U / TA (pp)") +
theme_gp
print(p1)

Run Value
Distribution
p2 <- ggplot(df_model %>% mutate(B = ifelse(any_fed == 1, "Fed Borrower", "Non-Borrower")),
aes(x = v_pp, fill = B, color = B)) +
geom_density(alpha = 0.3, linewidth = 0.7) +
geom_vline(xintercept = 0, linetype = "dashed", linewidth = 0.8) +
scale_fill_manual(values = borr_colors) + scale_color_manual(values = borr_colors) +
labs(title = "Run Value (v = E^MV − F^U): Borrowers vs Non-Borrowers",
subtitle = "Dashed line: v=0. Left of line: run equilibrium possible (F^U > E^MV).",
x = "Run Value v / TA (pp)", y = "Density") +
theme_gp
print(p2)

New Date and period
classifications
NEW
REGRESSIONS: FINE-GRAINED DATES & SIZE SUBSAMPLES
Data Preparation for
New Timeframes & Size
# 1. Create specific date indicators from the raw loan data
dw_m1_7 <- create_borrower_indicator(dw_loans, "dw_loan_date", "rssd_id", "dw_loan_amount",
as.Date("2023-03-01"), as.Date("2023-03-07"), "dw_m1_7")
dw_m8_12 <- create_borrower_indicator(dw_loans, "dw_loan_date", "rssd_id", "dw_loan_amount",
as.Date("2023-03-08"), as.Date("2023-03-12"), "dw_m8_12")
btfp_r1 <- create_borrower_indicator(btfp_loans, "btfp_loan_date", "rssd_id", "btfp_loan_amount",
as.Date("2023-03-13"), as.Date("2023-04-28"), "btfp_r1")
dw_r1 <- create_borrower_indicator(dw_loans, "dw_loan_date", "rssd_id", "dw_loan_amount",
as.Date("2023-03-13"), as.Date("2023-04-28"), "dw_r1")
btfp_r2 <- create_borrower_indicator(btfp_loans, "btfp_loan_date", "rssd_id", "btfp_loan_amount",
as.Date("2023-04-29"), as.Date("2023-05-04"), "btfp_r2")
dw_r2 <- create_borrower_indicator(dw_loans, "dw_loan_date", "rssd_id", "dw_loan_amount",
as.Date("2023-04-29"), as.Date("2023-05-04"), "dw_r2")
# 2. Join these new indicators into our main df_model
# Note: total_asset in Call Reports is in thousands.
# So 1 Billion = 1,000,000 thousands; 10 Billion = 10,000,000 thousands.
df_model_extended <- df_model %>%
left_join(dw_m1_7 %>% select(idrssd, dw_m1_7), by = "idrssd") %>%
left_join(dw_m8_12 %>% select(idrssd, dw_m8_12), by = "idrssd") %>%
left_join(btfp_r1 %>% select(idrssd, btfp_r1), by = "idrssd") %>%
left_join(dw_r1 %>% select(idrssd, dw_r1), by = "idrssd") %>%
left_join(btfp_r2 %>% select(idrssd, btfp_r2), by = "idrssd") %>%
left_join(dw_r2 %>% select(idrssd, dw_r2), by = "idrssd") %>%
mutate(
dw_m1_7 = replace_na(dw_m1_7, 0L),
dw_m8_12 = replace_na(dw_m8_12, 0L),
btfp_r1 = replace_na(btfp_r1, 0L),
dw_r1 = replace_na(dw_r1, 0L),
any_fed_r1 = as.integer(btfp_r1 == 1 | dw_r1 == 1),
btfp_r2 = replace_na(btfp_r2, 0L),
dw_r2 = replace_na(dw_r2, 0L),
any_fed_r2 = as.integer(btfp_r2 == 1 | dw_r2 == 1),
# Create Size Buckets
size_bucket = case_when(
total_asset < 1000000 ~ "< 1B",
total_asset >= 1000000 & total_asset < 10000000 ~ "1B - 10B",
total_asset >= 10000000 ~ "> 10B"
)
)
# Base formula RHS for all the new models
fml_rhs_base <- paste0("mtm_total + uninsured_franchise + mtm_x_f_u + ", ctrl_str)
Early DW Usage: Mar
1-7 vs Mar 8-12
reg_dw_early <- list(
"DW (Mar 1-7)" = feols(as.formula(paste("dw_m1_7 ~", fml_rhs_base)), data = df_model_extended, vcov = "hetero"),
"DW (Mar 8-12)" = feols(as.formula(paste("dw_m8_12 ~", fml_rhs_base)), data = df_model_extended, vcov = "hetero")
)
msummary(reg_dw_early, stars = c("*"=.10, "**"=.05, "***"=.01), gof_omit = "AIC|BIC|Log|RMSE",
add_rows = dv_summary_rows(reg_dw_early), coef_rename = coef_map,
title = "Table DW: DW Usage Pre- and Early-Crisis")
Table DW: DW Usage Pre- and Early-Crisis
| |
DW (Mar 1-7) |
DW (Mar 8-12) |
| * p < 0.1, ** p < 0.05, *** p < 0.01 |
| (Intercept) |
0.016*** |
0.016*** |
|
(0.002) |
(0.002) |
| ℓ (MTM Loss) |
0.001 |
0.000 |
|
(0.002) |
(0.002) |
| f^U (Uninsured Franchise) |
0.001 |
-0.000 |
|
(0.003) |
(0.003) |
| ℓ × f^U (KEY: c₃>0) |
0.003 |
0.004* |
|
(0.002) |
(0.002) |
| Log(Assets) |
0.012*** |
0.016*** |
|
(0.003) |
(0.003) |
| Cash/TA |
-0.002 |
-0.002 |
|
(0.002) |
(0.002) |
| Loan/Dep |
-0.001 |
-0.001 |
|
(0.002) |
(0.002) |
| Equity/TA |
0.001 |
-0.002 |
|
(0.002) |
(0.002) |
| Wholesale |
0.011*** |
0.010*** |
|
(0.003) |
(0.003) |
| ROA |
-0.002 |
-0.003* |
|
(0.002) |
(0.002) |
| Num.Obs. |
4226 |
4226 |
| R2 |
0.021 |
0.027 |
| R2 Adj. |
0.019 |
0.025 |
| Std.Errors |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
| N (DV = 1) |
67 |
66 |
| Mean (DV) |
0.0159 |
0.0156 |
save_reg_latex(reg_dw_early, "Table_DW_Early",
add_rows = dv_summary_rows(reg_dw_early), coef_rename = coef_map,
title = "Table DW: DW Usage Pre- and Early-Crisis")
## Saved: Table_DW_Early.tex
# ==============================================================================
# 1. DESCRIPTIVE SPLIT: What characteristics define a Repeat Borrower?
# Comparing Means: Single vs Repeat (March 8-12 panic window)
# ==============================================================================
# 1. Calculate the exact number of DW loans per bank for the early periods
dw_counts_m1_7 <- dw_loans %>%
filter(dw_loan_date >= as.Date("2023-03-01") & dw_loan_date <= as.Date("2023-03-07")) %>%
group_by(rssd_id) %>%
summarise(dw_m1_7_count = n(), .groups = "drop") %>%
rename(idrssd = rssd_id)
dw_counts_m8_12 <- dw_loans %>%
filter(dw_loan_date >= as.Date("2023-03-08") & dw_loan_date <= as.Date("2023-03-12")) %>%
group_by(rssd_id) %>%
summarise(dw_m8_12_count = n(), .groups = "drop") %>%
rename(idrssd = rssd_id)
# 2. Merge counts AND amounts into the main dataset, create Repeat Dummies
df_model_extended <- df_model_extended %>%
select(-any_of(c("dw_m1_7_count", "dw_m8_12_count", "dw_m1_7_repeat", "dw_m8_12_repeat", "dw_m1_7_amt", "dw_m8_12_amt"))) %>%
left_join(dw_counts_m1_7, by = "idrssd") %>%
left_join(dw_counts_m8_12, by = "idrssd") %>%
left_join(dw_m1_7 %>% select(idrssd, dw_m1_7_amt), by = "idrssd") %>%
left_join(dw_m8_12 %>% select(idrssd, dw_m8_12_amt), by = "idrssd") %>%
mutate(
dw_m1_7_count = replace_na(dw_m1_7_count, 0L),
dw_m8_12_count = replace_na(dw_m8_12_count, 0L),
# Dummies for Repeat Borrowers
dw_m1_7_repeat = as.integer(dw_m1_7_count > 1),
dw_m8_12_repeat = as.integer(dw_m8_12_count > 1),
# Log Amounts
ln_dw_amt_m1_7 = log(replace_na(dw_m1_7_amt, 0) + 1),
ln_dw_amt_m8_12 = log(replace_na(dw_m8_12_amt, 0) + 1)
)
# 3. Create subsets for BORROWERS ONLY in each period
df_borr_1_7 <- df_model_extended %>% filter(dw_m1_7 == 1)
df_borr_8_12 <- df_model_extended %>% filter(dw_m8_12 == 1)
# 4. Define variables: Theory variables (to interact) vs Controls (standalone)
theory_vars <- c("mtm_total", "uninsured_franchise", "mtm_x_f_u")
# Note: ctrl_str is already defined globally in your script as "ln_assets + cash_ratio + ..."
# 5. Define the specific formulas: (Theory) * repeat_dummy + Controls
fml_interact_1_7 <- as.formula(
paste0("ln_dw_amt_m1_7 ~ (", paste(theory_vars, collapse = " + "), ") * dw_m1_7_repeat + ", ctrl_str)
)
fml_interact_8_12 <- as.formula(
paste0("ln_dw_amt_m8_12 ~ (", paste(theory_vars, collapse = " + "), ") * dw_m8_12_repeat + ", ctrl_str)
)
# 6. Run Regressions (Checking degrees of freedom first)
reg_interact <- list()
if(nrow(df_borr_1_7) > 15) {
reg_interact[["Mar 1-7 (Interacted)"]] <- feols(fml_interact_1_7, data = df_borr_1_7, vcov = "hetero")
} else {
cat("Not enough borrowers in Mar 1-7 to run the interaction.\n")
}
if(nrow(df_borr_8_12) > 15) {
reg_interact[["Mar 8-12 (Interacted)"]] <- feols(fml_interact_8_12, data = df_borr_8_12, vcov = "hetero")
} else {
cat("Not enough borrowers in Mar 8-12 to run the interaction.\n")
}
# 7. Print and Save
if(length(reg_interact) > 0) {
save_reg_latex(reg_interact, "Table_DW_Early_Repeat_Interacted",
title = "Intensive Margin: Repeat Borrower Sensitivities (Theory Interacted, Controls Standalone)")
interact_table <- msummary(reg_interact, stars = c("*"=.10, "**"=.05, "***"=.01), gof_omit = "AIC|BIC|Log|RMSE",
title = "Intensive Margin: Repeat Borrower Sensitivities (Theory Interacted, Controls Standalone)")
interact_table
}
## Saved: Table_DW_Early_Repeat_Interacted.tex
Intensive Margin: Repeat Borrower Sensitivities (Theory Interacted, Controls Standalone)
| |
Mar 1-7 (Interacted) |
Mar 8-12 (Interacted) |
| * p < 0.1, ** p < 0.05, *** p < 0.01 |
| (Intercept) |
12.362*** |
12.579*** |
|
(0.562) |
(0.807) |
| mtm_total |
0.597 |
-0.384 |
|
(0.537) |
(0.724) |
| uninsured_franchise |
1.440*** |
-0.328 |
|
(0.353) |
(0.759) |
| mtm_x_f_u |
0.590* |
4.714*** |
|
(0.328) |
(0.830) |
| dw_m1_7_repeat |
3.764*** |
|
|
(0.694) |
|
| ln_assets |
0.338 |
0.964** |
|
(0.534) |
(0.448) |
| cash_ratio |
-0.843* |
-0.446 |
|
(0.451) |
(0.673) |
| loan_to_deposit |
0.615 |
-0.242 |
|
(0.451) |
(0.572) |
| book_equity_ratio |
-0.114 |
0.250 |
|
(0.400) |
(0.807) |
| wholesale |
0.651*** |
-0.008 |
|
(0.174) |
(0.343) |
| roa |
-0.257 |
-1.018 |
|
(0.566) |
(0.644) |
| mtm_total × dw_m1_7_repeat |
-0.659 |
|
|
(0.619) |
|
| uninsured_franchise × dw_m1_7_repeat |
-1.486*** |
|
|
(0.434) |
|
| mtm_x_f_u × dw_m1_7_repeat |
0.293 |
|
|
(0.509) |
|
| dw_m8_12_repeat |
|
3.653*** |
|
|
(0.840) |
| mtm_total × dw_m8_12_repeat |
|
0.278 |
|
|
(0.837) |
| uninsured_franchise × dw_m8_12_repeat |
|
0.394 |
|
|
(0.785) |
| mtm_x_f_u × dw_m8_12_repeat |
|
-3.997*** |
|
|
(0.974) |
| Num.Obs. |
67 |
66 |
| R2 |
0.640 |
0.551 |
| R2 Adj. |
0.552 |
0.439 |
| Std.Errors |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
Comparing aggregate,
BTFP, and DW borrowing probabilities across two distinct periods: the
heavy initial panic (Mar 13 - Apr 28) and the subsequent tail (Apr 29 -
May 4).
# Range 1: March 13 - April 28
reg_full_r1 <- list(
"Any Fed (Mar 13-Apr 28)" = feols(as.formula(paste("any_fed_r1 ~", fml_rhs_base)), data = df_model_extended, vcov = "hetero"),
"BTFP (Mar 13-Apr 28)" = feols(as.formula(paste("btfp_r1 ~", fml_rhs_base)), data = df_model_extended, vcov = "hetero"),
"DW (Mar 13-Apr 28)" = feols(as.formula(paste("dw_r1 ~", fml_rhs_base)), data = df_model_extended, vcov = "hetero")
)
# Range 2: April 29 - May 4
reg_full_r2 <- list(
"Any Fed (Apr 29-May 4)" = feols(as.formula(paste("any_fed_r2 ~", fml_rhs_base)), data = df_model_extended, vcov = "hetero"),
"BTFP (Apr 29-May 4)" = feols(as.formula(paste("btfp_r2 ~", fml_rhs_base)), data = df_model_extended, vcov = "hetero"),
"DW (Apr 29-May 4)" = feols(as.formula(paste("dw_r2 ~", fml_rhs_base)), data = df_model_extended, vcov = "hetero")
)
msummary(reg_full_r1, stars = c("*"=.10, "**"=.05, "***"=.01), gof_omit = "AIC|BIC|Log|RMSE",
add_rows = dv_summary_rows(reg_full_r1), coef_rename = coef_map,
title = "New Full Regression 1: March 13 - April 28")
New Full Regression 1: March 13 - April 28
| |
Any Fed (Mar 13-Apr 28) |
BTFP (Mar 13-Apr 28) |
DW (Mar 13-Apr 28) |
| * p < 0.1, ** p < 0.05, *** p < 0.01 |
| (Intercept) |
0.176*** |
0.106*** |
0.091*** |
|
(0.006) |
(0.005) |
(0.004) |
| ℓ (MTM Loss) |
0.028*** |
0.018*** |
0.012** |
|
(0.006) |
(0.005) |
(0.005) |
| f^U (Uninsured Franchise) |
0.019*** |
0.015** |
0.013** |
|
(0.007) |
(0.006) |
(0.006) |
| ℓ × f^U (KEY: c₃>0) |
0.021*** |
0.014*** |
0.016*** |
|
(0.006) |
(0.005) |
(0.005) |
| Log(Assets) |
0.096*** |
0.052*** |
0.067*** |
|
(0.007) |
(0.006) |
(0.006) |
| Cash/TA |
-0.016*** |
-0.021*** |
0.002 |
|
(0.006) |
(0.004) |
(0.005) |
| Loan/Dep |
-0.003 |
-0.006 |
0.001 |
|
(0.007) |
(0.005) |
(0.005) |
| Equity/TA |
-0.018*** |
-0.016*** |
-0.002 |
|
(0.005) |
(0.004) |
(0.004) |
| Wholesale |
0.026*** |
0.019*** |
0.014*** |
|
(0.007) |
(0.006) |
(0.005) |
| ROA |
-0.001 |
-0.003 |
-0.001 |
|
(0.006) |
(0.004) |
(0.004) |
| Num.Obs. |
4226 |
4226 |
4226 |
| R2 |
0.104 |
0.064 |
0.068 |
| R2 Adj. |
0.102 |
0.062 |
0.066 |
| Std.Errors |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
| N (DV = 1) |
742 |
448 |
382 |
| Mean (DV) |
0.1756 |
0.106 |
0.0904 |
save_reg_latex(reg_full_r1, "Table_Full_Reg_R1",
add_rows = dv_summary_rows(reg_full_r1), coef_rename = coef_map,
title = "Emergency Facility Borrowing: March 13 - April 28")
## Saved: Table_Full_Reg_R1.tex
msummary(reg_full_r2, stars = c("*"=.10, "**"=.05, "***"=.01), gof_omit = "AIC|BIC|Log|RMSE",
add_rows = dv_summary_rows(reg_full_r2), coef_rename = coef_map,
title = "New Full Regression 2: April 29 - May 4")
New Full Regression 2: April 29 - May 4
| |
Any Fed (Apr 29-May 4) |
BTFP (Apr 29-May 4) |
DW (Apr 29-May 4) |
| * p < 0.1, ** p < 0.05, *** p < 0.01 |
| (Intercept) |
0.058*** |
0.039*** |
0.021*** |
|
(0.004) |
(0.003) |
(0.002) |
| ℓ (MTM Loss) |
0.007* |
0.007** |
0.001 |
|
(0.004) |
(0.003) |
(0.002) |
| f^U (Uninsured Franchise) |
0.008* |
0.009** |
0.000 |
|
(0.005) |
(0.004) |
(0.003) |
| ℓ × f^U (KEY: c₃>0) |
0.008** |
0.004 |
0.006** |
|
(0.004) |
(0.003) |
(0.003) |
| Log(Assets) |
0.026*** |
0.016*** |
0.012*** |
|
(0.005) |
(0.004) |
(0.003) |
| Cash/TA |
-0.015*** |
-0.013*** |
-0.002 |
|
(0.003) |
(0.003) |
(0.002) |
| Loan/Dep |
-0.005 |
-0.007** |
0.001 |
|
(0.004) |
(0.003) |
(0.003) |
| Equity/TA |
-0.004 |
-0.005 |
-0.000 |
|
(0.004) |
(0.003) |
(0.002) |
| Wholesale |
0.016*** |
0.011*** |
0.008*** |
|
(0.005) |
(0.004) |
(0.003) |
| ROA |
-0.007** |
-0.006** |
-0.002 |
|
(0.003) |
(0.003) |
(0.002) |
| Num.Obs. |
4226 |
4226 |
4226 |
| R2 |
0.033 |
0.027 |
0.014 |
| R2 Adj. |
0.031 |
0.025 |
0.012 |
| Std.Errors |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
| N (DV = 1) |
243 |
167 |
86 |
| Mean (DV) |
0.0575 |
0.0395 |
0.0204 |
save_reg_latex(reg_full_r2, "Table_Full_Reg_R2",
add_rows = dv_summary_rows(reg_full_r2), coef_rename = coef_map,
title = "Emergency Facility Borrowing: April 29 - May 4")
## Saved: Table_Full_Reg_R2.tex
Subsamples by Size:
Range 1 (Mar 13 - Apr 28)
run_size_regs <- function(dv, base_df) {
fml <- as.formula(paste(dv, "~", fml_rhs_base))
list(
"< 1B" = feols(fml, data = base_df %>% filter(size_bucket == "< 1B"), vcov = "hetero"),
"1B - 10B" = feols(fml, data = base_df %>% filter(size_bucket == "1B - 10B"), vcov = "hetero"),
"> 10B" = feols(fml, data = base_df %>% filter(size_bucket == "> 10B"), vcov = "hetero")
)
}
reg_r1_any <- run_size_regs("any_fed_r1", df_model_extended)
reg_r1_btfp <- run_size_regs("btfp_r1", df_model_extended)
reg_r1_dw <- run_size_regs("dw_r1", df_model_extended)
msummary(reg_r1_any, stars = c("*"=.10, "**"=.05, "***"=.01), gof_omit = "AIC|BIC|Log|RMSE",
add_rows = dv_summary_rows(reg_r1_any), coef_rename = coef_map,
title = "Size Subsamples (Mar 13 - Apr 28): ANY FED")
Size Subsamples (Mar 13 - Apr 28): ANY FED
| |
< 1B |
1B - 10B |
> 10B |
| * p < 0.1, ** p < 0.05, *** p < 0.01 |
| (Intercept) |
0.168*** |
0.233*** |
4.415** |
|
(0.008) |
(0.050) |
(2.044) |
| ℓ (MTM Loss) |
0.022*** |
0.042** |
-0.029 |
|
(0.007) |
(0.020) |
(0.060) |
| f^U (Uninsured Franchise) |
0.012 |
0.009 |
0.117*** |
|
(0.008) |
(0.015) |
(0.032) |
| ℓ × f^U (KEY: c₃>0) |
0.011* |
0.033** |
0.042 |
|
(0.006) |
(0.013) |
(0.035) |
| Log(Assets) |
0.090*** |
0.058 |
-1.579** |
|
(0.010) |
(0.037) |
(0.792) |
| Cash/TA |
-0.011* |
-0.047* |
-0.116* |
|
(0.006) |
(0.028) |
(0.068) |
| Loan/Dep |
-0.001 |
-0.026 |
0.027 |
|
(0.007) |
(0.023) |
(0.066) |
| Equity/TA |
-0.018*** |
-0.032 |
-0.107* |
|
(0.005) |
(0.025) |
(0.061) |
| Wholesale |
0.027*** |
0.022 |
-0.014 |
|
(0.007) |
(0.017) |
(0.036) |
| ROA |
0.000 |
0.019 |
-0.005 |
|
(0.005) |
(0.019) |
(0.057) |
| Num.Obs. |
3309 |
783 |
134 |
| R2 |
0.060 |
0.047 |
0.151 |
| R2 Adj. |
0.057 |
0.036 |
0.089 |
| Std.Errors |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
| N (DV = 1) |
429 |
250 |
63 |
| Mean (DV) |
0.1296 |
0.3193 |
0.4701 |
msummary(reg_r1_btfp, stars = c("*"=.10, "**"=.05, "***"=.01), gof_omit = "AIC|BIC|Log|RMSE",
add_rows = dv_summary_rows(reg_r1_btfp), coef_rename = coef_map,
title = "Size Subsamples (Mar 13 - Apr 28): BTFP")
Size Subsamples (Mar 13 - Apr 28): BTFP
| |
< 1B |
1B - 10B |
> 10B |
| * p < 0.1, ** p < 0.05, *** p < 0.01 |
| (Intercept) |
0.104*** |
0.141*** |
0.790 |
|
(0.006) |
(0.040) |
(2.142) |
| ℓ (MTM Loss) |
0.016*** |
0.028* |
-0.046 |
|
(0.005) |
(0.016) |
(0.054) |
| f^U (Uninsured Franchise) |
0.014* |
0.000 |
0.076** |
|
(0.007) |
(0.013) |
(0.034) |
| ℓ × f^U (KEY: c₃>0) |
0.012** |
0.010 |
0.023 |
|
(0.005) |
(0.013) |
(0.033) |
| Log(Assets) |
0.048*** |
0.025 |
-0.229 |
|
(0.008) |
(0.030) |
(0.830) |
| Cash/TA |
-0.018*** |
-0.036* |
-0.067 |
|
(0.004) |
(0.021) |
(0.061) |
| Loan/Dep |
-0.005 |
-0.017 |
0.024 |
|
(0.005) |
(0.019) |
(0.062) |
| Equity/TA |
-0.012*** |
-0.053*** |
-0.094* |
|
(0.004) |
(0.020) |
(0.055) |
| Wholesale |
0.015*** |
0.027* |
0.010 |
|
(0.006) |
(0.016) |
(0.035) |
| ROA |
0.000 |
-0.010 |
0.015 |
|
(0.004) |
(0.015) |
(0.051) |
| Num.Obs. |
3309 |
783 |
134 |
| R2 |
0.042 |
0.051 |
0.067 |
| R2 Adj. |
0.039 |
0.040 |
-0.001 |
| Std.Errors |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
| N (DV = 1) |
268 |
140 |
40 |
| Mean (DV) |
0.081 |
0.1788 |
0.2985 |
msummary(reg_r1_dw, stars = c("*"=.10, "**"=.05, "***"=.01), gof_omit = "AIC|BIC|Log|RMSE",
add_rows = dv_summary_rows(reg_r1_dw), coef_rename = coef_map,
title = "Size Subsamples (Mar 13 - Apr 28): DW")
Size Subsamples (Mar 13 - Apr 28): DW
| |
< 1B |
1B - 10B |
> 10B |
| * p < 0.1, ** p < 0.05, *** p < 0.01 |
| (Intercept) |
0.079*** |
0.089** |
5.199** |
|
(0.006) |
(0.042) |
(1.994) |
| ℓ (MTM Loss) |
0.008* |
0.013 |
0.011 |
|
(0.005) |
(0.017) |
(0.046) |
| f^U (Uninsured Franchise) |
0.004 |
0.012 |
0.085** |
|
(0.006) |
(0.013) |
(0.035) |
| ℓ × f^U (KEY: c₃>0) |
0.005 |
0.028** |
0.061** |
|
(0.005) |
(0.012) |
(0.029) |
| Log(Assets) |
0.049*** |
0.069** |
-1.930** |
|
(0.008) |
(0.032) |
(0.772) |
| Cash/TA |
0.005 |
-0.016 |
-0.082 |
|
(0.005) |
(0.025) |
(0.062) |
| Loan/Dep |
0.002 |
-0.009 |
0.022 |
|
(0.005) |
(0.020) |
(0.044) |
| Equity/TA |
-0.007** |
0.002 |
-0.046 |
|
(0.004) |
(0.021) |
(0.055) |
| Wholesale |
0.013** |
0.006 |
0.022 |
|
(0.005) |
(0.014) |
(0.035) |
| ROA |
0.000 |
0.015 |
0.003 |
|
(0.004) |
(0.017) |
(0.050) |
| Num.Obs. |
3309 |
783 |
134 |
| R2 |
0.026 |
0.020 |
0.144 |
| R2 Adj. |
0.023 |
0.009 |
0.082 |
| Std.Errors |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
| N (DV = 1) |
194 |
145 |
43 |
| Mean (DV) |
0.0586 |
0.1852 |
0.3209 |
# Save combined latex (or separate as preferred)
save_reg_latex(reg_r1_any, "Table_Size_R1_AnyFed", add_rows = dv_summary_rows(reg_r1_any), coef_rename = coef_map, title = "Any Fed by Size (Mar 1-Apr 28)")
## Saved: Table_Size_R1_AnyFed.tex
save_reg_latex(reg_r1_btfp, "Table_Size_R1_BTFP", add_rows = dv_summary_rows(reg_r1_btfp), coef_rename = coef_map, title = "BTFP by Size (Mar 1-Apr 28)")
## Saved: Table_Size_R1_BTFP.tex
save_reg_latex(reg_r1_dw, "Table_Size_R1_DW", add_rows = dv_summary_rows(reg_r1_dw), coef_rename = coef_map, title = "DW by Size (Mar 1-Apr 28)")
## Saved: Table_Size_R1_DW.tex
Subsamples by Size:
Range 2 (Apr 29 - May 4)
reg_r2_any <- run_size_regs("any_fed_r2", df_model_extended)
reg_r2_btfp <- run_size_regs("btfp_r2", df_model_extended)
reg_r2_dw <- run_size_regs("dw_r2", df_model_extended)
msummary(reg_r2_any, stars = c("*"=.10, "**"=.05, "***"=.01), gof_omit = "AIC|BIC|Log|RMSE",
add_rows = dv_summary_rows(reg_r2_any), coef_rename = coef_map,
title = "Size Subsamples (Apr 29 - May 4): ANY FED")
Size Subsamples (Apr 29 - May 4): ANY FED
| |
< 1B |
1B - 10B |
> 10B |
| * p < 0.1, ** p < 0.05, *** p < 0.01 |
| (Intercept) |
0.056*** |
0.097*** |
0.702 |
|
(0.005) |
(0.037) |
(1.528) |
| ℓ (MTM Loss) |
-0.006 |
0.037*** |
0.019 |
|
(0.004) |
(0.014) |
(0.035) |
| f^U (Uninsured Franchise) |
0.005 |
0.001 |
0.035 |
|
(0.005) |
(0.011) |
(0.024) |
| ℓ × f^U (KEY: c₃>0) |
-0.003 |
0.012 |
0.054** |
|
(0.004) |
(0.010) |
(0.022) |
| Log(Assets) |
0.030*** |
0.008 |
-0.247 |
|
(0.006) |
(0.026) |
(0.594) |
| Cash/TA |
-0.014*** |
-0.028* |
-0.060* |
|
(0.003) |
(0.017) |
(0.032) |
| Loan/Dep |
-0.004 |
-0.023 |
-0.020 |
|
(0.004) |
(0.015) |
(0.030) |
| Equity/TA |
-0.006* |
0.003 |
-0.000 |
|
(0.004) |
(0.017) |
(0.037) |
| Wholesale |
0.011** |
0.034** |
0.003 |
|
(0.005) |
(0.014) |
(0.021) |
| ROA |
-0.006* |
-0.010 |
0.012 |
|
(0.003) |
(0.011) |
(0.021) |
| Num.Obs. |
3309 |
783 |
134 |
| R2 |
0.021 |
0.055 |
0.095 |
| R2 Adj. |
0.019 |
0.044 |
0.029 |
| Std.Errors |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
| N (DV = 1) |
145 |
84 |
14 |
| Mean (DV) |
0.0438 |
0.1073 |
0.1045 |
msummary(reg_r2_btfp, stars = c("*"=.10, "**"=.05, "***"=.01), gof_omit = "AIC|BIC|Log|RMSE",
add_rows = dv_summary_rows(reg_r2_btfp), coef_rename = coef_map,
title = "Size Subsamples (Apr 29 - May 4): BTFP")
Size Subsamples (Apr 29 - May 4): BTFP
| |
< 1B |
1B - 10B |
> 10B |
| * p < 0.1, ** p < 0.05, *** p < 0.01 |
| (Intercept) |
0.040*** |
0.082*** |
0.355 |
|
(0.004) |
(0.031) |
(1.240) |
| ℓ (MTM Loss) |
-0.003 |
0.028** |
0.025 |
|
(0.003) |
(0.012) |
(0.023) |
| f^U (Uninsured Franchise) |
0.005 |
0.005 |
0.042* |
|
(0.004) |
(0.009) |
(0.022) |
| ℓ × f^U (KEY: c₃>0) |
-0.003 |
0.004 |
0.039** |
|
(0.003) |
(0.009) |
(0.019) |
| Log(Assets) |
0.022*** |
-0.009 |
-0.122 |
|
(0.005) |
(0.022) |
(0.482) |
| Cash/TA |
-0.012*** |
-0.025** |
-0.023 |
|
(0.003) |
(0.011) |
(0.019) |
| Loan/Dep |
-0.006* |
-0.022* |
-0.022 |
|
(0.003) |
(0.013) |
(0.027) |
| Equity/TA |
-0.005 |
-0.005 |
-0.027 |
|
(0.003) |
(0.013) |
(0.029) |
| Wholesale |
0.008** |
0.018 |
0.009 |
|
(0.004) |
(0.011) |
(0.019) |
| ROA |
-0.004* |
-0.016* |
0.018 |
|
(0.003) |
(0.009) |
(0.015) |
| Num.Obs. |
3309 |
783 |
134 |
| R2 |
0.019 |
0.049 |
0.118 |
| R2 Adj. |
0.016 |
0.038 |
0.054 |
| Std.Errors |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
| N (DV = 1) |
103 |
55 |
9 |
| Mean (DV) |
0.0311 |
0.0702 |
0.0672 |
msummary(reg_r2_dw, stars = c("*"=.10, "**"=.05, "***"=.01), gof_omit = "AIC|BIC|Log|RMSE",
add_rows = dv_summary_rows(reg_r2_dw), coef_rename = coef_map,
title = "Size Subsamples (Apr 29 - May 4): DW")
Size Subsamples (Apr 29 - May 4): DW
| |
< 1B |
1B - 10B |
> 10B |
| * p < 0.1, ** p < 0.05, *** p < 0.01 |
| (Intercept) |
0.019*** |
0.021 |
0.556 |
|
(0.003) |
(0.025) |
(0.948) |
| ℓ (MTM Loss) |
-0.004* |
0.010 |
0.002 |
|
(0.002) |
(0.009) |
(0.029) |
| f^U (Uninsured Franchise) |
-0.000 |
-0.001 |
-0.005 |
|
(0.003) |
(0.008) |
(0.012) |
| ℓ × f^U (KEY: c₃>0) |
0.000 |
0.014* |
0.022 |
|
(0.002) |
(0.008) |
(0.015) |
| Log(Assets) |
0.010** |
0.015 |
-0.205 |
|
(0.004) |
(0.018) |
(0.368) |
| Cash/TA |
-0.002 |
-0.001 |
-0.045 |
|
(0.002) |
(0.013) |
(0.029) |
| Loan/Dep |
0.002 |
-0.004 |
-0.007 |
|
(0.003) |
(0.008) |
(0.017) |
| Equity/TA |
-0.002 |
0.002 |
0.026 |
|
(0.002) |
(0.012) |
(0.024) |
| Wholesale |
0.005* |
0.020** |
0.005 |
|
(0.003) |
(0.010) |
(0.015) |
| ROA |
-0.002 |
0.003 |
0.006 |
|
(0.002) |
(0.008) |
(0.019) |
| Num.Obs. |
3309 |
783 |
134 |
| R2 |
0.007 |
0.026 |
0.053 |
| R2 Adj. |
0.004 |
0.015 |
-0.016 |
| Std.Errors |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
Heteroskedasticity-robust |
| N (DV = 1) |
48 |
32 |
6 |
| Mean (DV) |
0.0145 |
0.0409 |
0.0448 |
# Save outputs
save_reg_latex(reg_r2_any, "Table_Size_R2_AnyFed", add_rows = dv_summary_rows(reg_r2_any), coef_rename = coef_map, title = "Any Fed by Size (Apr 29-May 4)")
## Saved: Table_Size_R2_AnyFed.tex
save_reg_latex(reg_r2_btfp, "Table_Size_R2_BTFP", add_rows = dv_summary_rows(reg_r2_btfp), coef_rename = coef_map, title = "BTFP by Size (Apr 29-May 4)")
## Saved: Table_Size_R2_BTFP.tex
save_reg_latex(reg_r2_dw, "Table_Size_R2_DW", add_rows = dv_summary_rows(reg_r2_dw), coef_rename = coef_map, title = "DW by Size (Apr 29-May 4)")
## Saved: Table_Size_R2_DW.tex
Daily Borrowers:
March 1 - May 4
# 1. Aggregate daily unique borrowers for BTFP
btfp_daily <- btfp_loans %>%
filter(btfp_loan_date >= as.Date("2023-03-01") & btfp_loan_date <= as.Date("2023-05-04")) %>%
group_by(btfp_loan_date) %>%
summarise(Borrowers = n_distinct(rssd_id), .groups = "drop") %>%
rename(Date = btfp_loan_date) %>%
mutate(Facility = "BTFP")
# 2. Aggregate daily unique borrowers for DW
dw_daily <- dw_loans %>%
filter(dw_loan_date >= as.Date("2023-03-01") & dw_loan_date <= as.Date("2023-05-04")) %>%
group_by(dw_loan_date) %>%
summarise(Borrowers = n_distinct(rssd_id), .groups = "drop") %>%
rename(Date = dw_loan_date) %>%
mutate(Facility = "Discount Window")
# 3. Combine into a single dataframe
daily_counts <- bind_rows(btfp_daily, dw_daily)
# 4. Create the plot
# Using geom_col (bar plot) works best to show the episodic spikes in DW vs steady BTFP usage
p_daily <- ggplot(daily_counts, aes(x = Date, y = Borrowers, fill = Facility)) +
geom_col(position = position_dodge(width = 0.8), width = 0.8, alpha = 0.85) +
# Add vertical lines to delineate key date ranges used in regressions
geom_vline(xintercept = as.numeric(as.Date("2023-03-08")), linetype = "dashed", color = "grey40", linewidth = 0.6) +
geom_vline(xintercept = as.numeric(as.Date("2023-04-28")), linetype = "dashed", color = "grey40", linewidth = 0.6) +
scale_fill_manual(values = c("BTFP" = "#1565C0", "Discount Window" = "#C62828")) +
scale_x_date(date_breaks = "1 week", date_labels = "%b %d", expand = c(0.02, 0)) +
labs(
title = "Daily Number of Borrowers: BTFP vs. Discount Window",
subtitle = "March 1, 2023 – May 4, 2023. Dashed lines denote Mar 8 (Initial panic) and Apr 28.",
x = "Date",
y = "Number of Unique Borrowers"
) +
theme_gp +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
legend.title = element_blank()
)
print(p_daily)

# Optionally save the figure
save_figure(p_daily, "Plot_Daily_Borrowers_Mar_May", width = 12, height = 6)
# 1. Aggregate daily unique borrowers for BTFP
btfp_daily <- btfp_loans %>%
filter(btfp_loan_date >= as.Date("2023-03-01") & btfp_loan_date <= as.Date("2023-05-04")) %>%
group_by(btfp_loan_date) %>%
summarise(Borrowers = n_distinct(rssd_id), .groups = "drop") %>%
rename(Date = btfp_loan_date) %>%
mutate(Facility = "BTFP")
# 2. Aggregate daily unique borrowers for DW
dw_daily <- dw_loans %>%
filter(dw_loan_date >= as.Date("2023-03-01") & dw_loan_date <= as.Date("2023-05-04")) %>%
group_by(dw_loan_date) %>%
summarise(Borrowers = n_distinct(rssd_id), .groups = "drop") %>%
rename(Date = dw_loan_date) %>%
mutate(Facility = "Discount Window")
# 3. Combine into a single dataframe
daily_counts <- bind_rows(btfp_daily, dw_daily)
# 4. Create the plot
p_daily <- ggplot(daily_counts, aes(x = Date, y = Borrowers, fill = Facility)) +
geom_col(position = position_dodge(width = 0.8), width = 0.8, alpha = 0.85) +
# --- NEW: Add the actual numbers on top of the bars ---
# ifelse hides '0' values to prevent clutter on days with no borrowing
geom_text(aes(label = ifelse(Borrowers > 0, Borrowers, "")),
position = position_dodge(width = 0.8),
vjust = -0.5, # Nudges the text just above the bar
size = 3.5, # Adjust size if it looks too crowded
color = "black") +
# Add vertical lines to delineate key date ranges
geom_vline(xintercept = as.numeric(as.Date("2023-03-08")), linetype = "dashed", color = "grey40", linewidth = 0.6) +
geom_vline(xintercept = as.numeric(as.Date("2023-04-28")), linetype = "dashed", color = "grey40", linewidth = 0.6) +
scale_fill_manual(values = c("BTFP" = "#1565C0", "Discount Window" = "#C62828")) +
scale_x_date(date_breaks = "1 week", date_labels = "%b %d", expand = c(0.02, 0)) +
# --- NEW: Expand the top of the y-axis so high numbers don't get cut off ---
scale_y_continuous(expand = expansion(mult = c(0, 0.15))) +
labs(
title = "Daily Number of Borrowers: BTFP vs. Discount Window",
subtitle = "March 1, 2023 – May 4, 2023. Actual borrower counts displayed on bars.",
x = "Date",
y = "Number of Unique Borrowers"
) +
theme_gp +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
legend.title = element_blank()
)
print(p_daily)
## Warning in scale_x_date(date_breaks = "1 week", date_labels = "%b %d", expand = c(0.02, : A <numeric> value was passed to a Date scale.
## ℹ The value was converted to a <Date> object.
## A <numeric> value was passed to a Date scale.
## ℹ The value was converted to a <Date> object.

# Optionally save the figure
save_figure(p_daily, "Plot_Daily_Borrowers_Mar_May_Labeled", width = 18, height = 8)
## Warning in scale_x_date(date_breaks = "1 week", date_labels = "%b %d", expand = c(0.02, : A <numeric> value was passed to a Date scale.
## ℹ The value was converted to a <Date> object.
## A <numeric> value was passed to a Date scale.
## ℹ The value was converted to a <Date> object.
## Saved: Plot_Daily_Borrowers_Mar_May_Labeled.pdf