Loanable Funds & Crowding Out
The market for loanable funds
Think of all saving and borrowing in the economy as one market for a single good — loanable funds — whose “price” is the real interest rate \(r\).
- Demand = investment \(I(r)\). Firms borrow to build capital; they borrow less when \(r\) is high, so demand slopes down.
- Supply = national saving \(S\). It splits into private saving (households) and public saving (the government):
\[S = \underbrace{(Y - T - C)}_{\text{private}} + \underbrace{(T - G)}_{\text{public}} = Y - C - G\]
- Equilibrium: \(r\) adjusts until saving equals investment, \(S = I(r)\).
The three “savings”
- Private saving \(= Y - T - C\) — what households keep after taxes and consumption.
- Public saving \(= T - G\) — the government’s balance (a deficit when \(G > T\)).
- National saving \(= Y - C - G\) — the total pool that funds investment.
Comparative statics
- Deficit up (raise \(G\), or cut \(T\)): national saving falls → the saving line shifts left → \(r\) rises and investment is squeezed. This is crowding out.
- Investment demand up: the \(I(r)\) line shifts right → \(r\) rises. Whether investment actually rises depends on saving:
- if saving is vertical (consumption doesn’t depend on \(r\)), total investment is unchanged — the higher \(r\) chokes off exactly the extra demand;
- if saving slopes up (consumption responds to \(r\)), higher \(r\) pulls in more saving, so investment rises.
#| standalone: true
#| viewerHeight: 560
library(shiny)
ui <- fluidPage(
tags$head(tags$style(HTML("
body { font-family: 'Inter', system-ui, -apple-system, sans-serif; }
.stats-box { background:#f0f4f8; border-radius:6px; padding:12px 14px; margin-top:10px;
font-size:14px; line-height:1.85; }
.stats-box b { color:#1f3b73; }
.up { color:#b5462a; font-weight:bold; }
.dn { color:#1c6b4a; font-weight:bold; }
"))),
sidebarLayout(
sidebarPanel(
width = 4,
sliderInput("G", "Government purchases G:", min = 800, max = 2500, value = 1500, step = 50),
sliderInput("T", "Taxes T:", min = 800, max = 2500, value = 1500, step = 50),
sliderInput("dI0","Investment-demand shift:", min = -400, max = 400, value = 0, step = 50),
checkboxInput("rsens", "Consumption responds to r (saving slopes up)", value = FALSE),
uiOutput("stats")
),
mainPanel(
width = 8,
plotOutput("plot", height = "460px")
)
)
)
server <- function(input, output, session) {
# fixed structural parameters
Y <- 6000; C0 <- 600; mpc <- 0.6; b <- 100; I0 <- 1600
eq <- function(G, T, dI0, cr) {
rstar <- (I0 + dI0 - (Y - C0 - mpc*(Y - T) - G)) / (cr + b)
Cstar <- C0 + mpc*(Y - T) - cr*rstar
list(r = rstar,
I = I0 + dI0 - b*rstar,
Spriv = Y - T - Cstar,
Spub = T - G,
Snat = (Y - T - Cstar) + (T - G))
}
vals <- reactive({
cr <- if (isTRUE(input$rsens)) 50 else 0
cur <- eq(input$G, input$T, input$dI0, cr)
base <- eq(1500, 1500, 0, cr) # reference: balanced budget, no shift
Sr <- function(r) Y - (C0 + mpc*(Y - input$T) - cr*r) - input$G
Ir <- function(r) I0 + input$dI0 - b*r
list(cur = cur, base = base, Sr = Sr, Ir = Ir, cr = cr)
})
output$plot <- renderPlot({
v <- vals(); rg <- seq(0, 16, length.out = 200)
par(mar = c(4.2, 4.4, 1.2, 1))
plot(NA, xlim = c(0, 2500), ylim = c(0, 16),
xlab = "Loanable funds (S, I)", ylab = "Real interest rate r (%)",
las = 1, bty = "l", cex.lab = 1.15)
points(v$base$I, v$base$r, pch = 1, col = "#9aa4b2", cex = 1.4, lwd = 2)
lines(v$Sr(rg), rg, col = "#1f3b73", lwd = 3)
lines(v$Ir(rg), rg, col = "#b5462a", lwd = 3)
points(v$cur$I, v$cur$r, pch = 19, col = "#1c6b4a", cex = 1.7)
segments(0, v$cur$r, v$cur$I, v$cur$r, lty = 3, col = "#5a6472")
segments(v$cur$I, 0, v$cur$I, v$cur$r, lty = 3, col = "#5a6472")
legend("topright",
c("Saving S(r)", "Investment I(r)", "Equilibrium", "Baseline (G=T, no shift)"),
col = c("#1f3b73", "#b5462a", "#1c6b4a", "#9aa4b2"),
lwd = c(3, 3, NA, NA), pch = c(NA, NA, 19, 1), bty = "n", cex = 1.0)
})
output$stats <- renderUI({
v <- vals(); c <- v$cur
deficit <- input$G - input$T
dI <- c$I - v$base$I
budget <- if (deficit > 0) sprintf("deficit of %.0f", deficit)
else if (deficit < 0) sprintf("surplus of %.0f", -deficit)
else "balanced budget"
crowd <- if (abs(dI) < 1) "investment is unchanged vs. baseline"
else if (dI < 0) sprintf("investment is <span class='up'>crowded out by %.0f</span> vs. baseline", -dI)
else sprintf("investment is <span class='dn'>higher by %.0f</span> vs. baseline", dI)
HTML(sprintf(
"<div class='stats-box'>
Budget: <b>%s</b><br>
Private saving = <b>%.0f</b> Public saving = <b>%.0f</b><br>
National saving = <b>%.0f</b><br>
Equilibrium: <b>r* = %.2f%%</b>, <b>I* = %.0f</b><br>
%s
</div>",
budget, c$Spriv, c$Spub, c$Snat, c$r, c$I, crowd))
})
}
shinyApp(ui, server)
What to notice
- Run a deficit — raise \(G\) above \(T\). The saving line slides left, \(r\) climbs, and the equilibrium dot moves up the investment curve: less investment. That’s crowding out, live.
- Shift investment demand with saving vertical (box unchecked): \(r\) rises but \(I^*\) stays put — the extra demand is exactly offset by the higher rate.
- Now check “consumption responds to \(r\)” and shift investment demand again: the saving line tilts, so higher \(r\) coaxes out more saving and investment actually rises. This is the difference between the two exam cases.