2008 en intermediary asset pricing
1Overzicht¶
Dit college vraagt of de balans van gefinancierde intermediairs, zoals dealerbanken, de risicopremies in alle markten bepaalt. Het antwoord is een voorzichtig ja: als hun kapitaal schaars wordt, stijgt de premie sneller dan evenredig, en schokken in hun kapitaalratio krijgen een positieve prijs, al is die kapitaalratio moeilijk te meten.
We rekenen met de hand na hoe één intermediair na een prijsdaling van 2% zijn balans afbouwt en daarmee de daling versterkt.
We leiden af waarom markt- en financieringsliquiditeit elkaar versterken, en waarom de premie omgekeerd evenredig is met de kapitaalratio.
We simuleren hoe vaak een factortoets op vijftig jaar kwartaaldata een ware prijs van intermediairrisico ziet.
We repliceren de kapitaalratio en de cross-sectionele toets van He et al. (2017), de broker-dealer-leverage van Adrian et al. (2014) en de voorspellende kracht van de kapitaalratio.
De theorie lag voor een deel klaar toen de crisis kwam. Zo hadden Shleifer & Vishny (1997) en Gromb & Vayanos (2002) arbitrageurs met beperkt kapitaal gemodelleerd, en Brunnermeier & Pedersen (2009) lieten zien dat markt- en financieringsliquiditeit elkaar versterken. Na de crisis bouwden He & Krishnamurthy (2013) een evenwichtsmodel met de kapitaalratio van de intermediair als toestandsvariabele. Daarna maakten Adrian et al. (2014) en He et al. (2017) van die balans een factor in de cross-sectie, en daarmee begon een tijdvak waarin een theorie weer het teken van een prijs van risico voorspelt. Voor de vraag theorie of feit is dat een stap terug naar de theorie. Santa-Clara vat de verschuiving samen als een nieuwe opvatting over de marginale belegger, die niet meer de representatieve consument is maar een gehefboomde intermediair Santa-Clara (2026).
2Intuïtie: waarom zou dit waar zijn?¶
In de herfst van 2008 schoten de spreads op krediet en financiering omhoog, en de grote verliezen zaten niet bij huishoudens maar bij de dealers die de markten maakten. Dat past slecht bij de consumptiemodellen van Lucas, Breeden en de SDF, waarin het marginale nut van huishoudens de prijzen bepaalt.
Zo’n dealerbank koopt obligaties met een klein beetje eigen geld en veel geleend geld. De geldgever houdt een haircut in (het deel van de waarde dat de bank met eigen vermogen moet financieren). Bij een haircut van 10% mag de balans dus hoogstens tien keer het eigen vermogen zijn, een leverage (activa gedeeld door eigen vermogen) van tien.
Daalt de prijs 2%, dan verliest zo’n bank een vijfde van het eigen vermogen en moet ze verkopen. Als veel banken tegelijk verkopen, daalt de prijs verder en dwingt het nieuwe verlies tot nieuwe verkopen, wat de verliesspiraal heet. Wanneer de geldgevers bovendien hun haircuts verhogen, moet de balans ook zonder nieuw verlies krimpen, en dat is de margespiraal. Volgens Gorton & Metrick (2012) steeg het gemiddelde haircut op onderpand dat geen staatsobligatie was, van nul begin 2007 tot bijna 50% eind 2008.
Als de dealer de marginale belegger is, bepaalt zijn marginale nut de stochastic discount factor. Een dollar is voor hem het meest waard als zijn kapitaal schaars is. Een activum dat juist dan slecht rendeert, moet daarom gemiddeld meer opleveren. Volgens Santa-Clara zegt de aandelenpremie misschien minder over de risicoaversie van huishoudens dan over wie het risico mag dragen Santa-Clara (2026).
We verwachten dus twee dingen. De risicopremie stijgt als de kapitaalratio van de intermediairs daalt, en wel sneller dan evenredig, omdat de spiralen ook de volatiliteit opdrijven. Bovendien is de prijs van het risico dat die kapitaalratio daalt positief, en omdat dealers in bijna elke markt handelen, hoort in elke activaklasse dezelfde prijs te gelden.
3Toy-voorbeeld: één intermediair, één schok, twee rondes verkopen¶
Alle cellen van dit college gebruiken dezelfde pakketten en één toevalsgenerator met een vaste seed.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import optimize
import hap
from hap import data as hap_data
hap.plotting.setup()
rng = np.random.default_rng(20240101)Het voorbeeld draait om één intermediair die precies aan zijn financieringsgrens zit. De tabel geeft zijn balans en de markt waarop hij moet verkopen.
| grootheid | symbool | waarde |
|---|---|---|
| eigen vermogen | 10 | |
| activa, tegen prijs | 100 | |
| leverage | 10 | |
| kapitaalratio | 10% | |
| haircut, met restrictie | 10% | |
| vermogen van buitenstaanders | 1000 | |
| vraagelasticiteit van buitenstaanders | 2 |
De enige formule die we nog niet hebben afgeleid, is het recept voor de prijsimpact. Een verkoop ter waarde moet door de buitenstaanders worden opgenomen en drukt de prijs daardoor met .
Schok. De prijs daalt 2%, dus het verlies is , met en . De kapitaalratio daalt tot .
Eerste verkoop. Met mag de balans hoogstens zijn, dus de intermediair verkoopt . De prijs daalt , wat op de resterende 80 een verlies van geeft.
Tweede verkoop. Na dat verlies is het eigen vermogen en de balans . De toegestane balans is dan , dus .
Waarom het ophoudt. Elke dollar verlies dwingt dollar verkoop af. Op een balans van ongeveer 80 is elke ronde daardoor ongeveer keer zo groot als de vorige.
Margespiraal. Stijgt de haircut na de schok naar 12,5%, dan mag de balans hoogstens zijn. De eerste verkoop is dan , die de prijs drukt, met een verlies van . Het eigen vermogen is dan en de balans , zodat de toegestane balans is en de tweede verkoop .
De code rekent beide spiralen af tot er niets meer verkocht hoeft te worden, en zet de handstappen naast de uitkomsten van de code.
def deleverage(E, A, h, absorb, shock, h_after=None):
"""Loss (and margin) spiral of one intermediary that starts at its constraint E = h A.
A fundamental price shock hits the assets. The intermediary then sells until
E = h_after * A; a sale of S depresses the price by S / absorb, which is a new
loss on the assets it still holds. Returns the capital ratio right after the
shock, the table of selling rounds and the final price.
"""
loss = shock * A
E, A = E - loss, A - loss
eta_on_impact = E / A
h = h if h_after is None else h_after
price, rounds = 1.0 - shock, []
while (sale := A - E / h) > 1e-12:
impact = sale / absorb
kept = A - sale
loss = impact * kept
E, A = E - loss, kept - loss
price *= 1.0 - impact
rounds.append({"verkoop": sale, "prijsimpact (%)": 100 * impact, "verlies": loss, "E": E, "A": A})
return eta_on_impact, pd.DataFrame(rounds), price
E0, A0, H0, EPS, W_OUT, SHOCK = 10.0, 100.0, 0.10, 2.0, 1000.0, 0.02
eta_impact, const_rounds, const_price = deleverage(E0, A0, H0, EPS * W_OUT, SHOCK)
_, margin_rounds, margin_price = deleverage(E0, A0, H0, EPS * W_OUT, SHOCK, h_after=0.125)
for name, rounds, price in [("haircut 10%", const_rounds, const_price), ("haircut 10% → 12,5%", margin_rounds, margin_price)]:
final_equity, final_assets = rounds["E"].iloc[-1], rounds["A"].iloc[-1]
print(f"{name}: {len(rounds)} rondes, eigen vermogen na afloop {final_equity:.3f}, "
f"leverage na afloop {final_assets / final_equity:.1f}, prijsdaling {100 * (1 - price):.3f}%")
steps = ["kapitaalratio na de schok", "verkoop ronde 1", "verlies ronde 1", "verkoop ronde 2",
"verkoop ronde 1, haircut 12,5%", "verlies ronde 1, haircut 12,5%", "verkoop ronde 2, haircut 12,5%"]
by_hand = [8 / 98, 18.0, 0.72, 6.48, 34.0, 1.088, 7.616]
by_code = [eta_impact, const_rounds.loc[0, "verkoop"], const_rounds.loc[0, "verlies"], const_rounds.loc[1, "verkoop"],
margin_rounds.loc[0, "verkoop"], margin_rounds.loc[0, "verlies"], margin_rounds.loc[1, "verkoop"]]
pd.DataFrame({"met de hand": by_hand, "code": by_code}, index=steps).round(4)haircut 10%: 27 rondes, eigen vermogen na afloop 6.935, leverage na afloop 10.0, prijsdaling 3.347%
haircut 10% → 12,5%: 19 rondes, eigen vermogen na afloop 6.653, leverage na afloop 8.0, prijsdaling 4.120%
Code en handberekening komen op elke stap overeen. Bij een vaste haircut is de prijs na afloop 3,35% gedaald, zodat de eigen verkopen 1,35 procentpunt aan de schok toevoegen. Het eigen vermogen is dan 31% lager dan vóór de schok.
Bij de hogere haircut daalt de prijs 4,12%, en daalt de leverage na afloop tot 8, tegen 10 bij een vaste haircut. Dezelfde slechte toestand geeft dus eerst een dalende kapitaalratio, omdat het verlies het eigen vermogen raakt, en daarna een dalende leverage, omdat de balans krimpt. Die tegenstelling komt terug bij twee empirische factoren die allebei een positieve prijs krijgen.
4Theorie¶
De theorie gaat in drie stappen van de balans van één intermediair naar de prijzen van alle activa. Eerst laten we zien dat de twee spiralen een markt met twee evenwichten kunnen opleveren. Daarna leiden we af dat de risicopremie omgekeerd evenredig is met de kapitaalratio, en dat de volatiliteit dat verband versterkt. Tot slot maken we van het marginale nut van de intermediair een factormodel. Alles draait om de gedachte dat de prijs van risico afhangt van het kapitaal van wie het risico draagt.
4.1Opzet en notatie¶
Een intermediair heeft op eigen vermogen , activa en schuld . We schrijven voor de leverage en voor de kapitaalratio, het symbool van He et al. (2017). Voor de primary dealers daalde die ratio in 2009 tot ongeveer 2%. De financieringsrestrictie luidt met haircut , in het toy-voorbeeld 10%.
De artikelen gebruiken voor de marge of voor de inbreng van huishoudens, maar hier blijft de stochastische discontofactor. Een verliesspiraal met vaste haircut is al afgeleid in Proposition 4. Nieuw is dat de haircut, en daarmee de toegestane balans, zelf van de marktliquiditeit afhangt.
4.2Marktliquiditeit en financieringsliquiditeit¶
Markt- en financieringsliquiditeit versterken elkaar, en daardoor kan dezelfde markt liquide of illiquide zijn. Waarom zou dit waar zijn? Speculanten zoals dealers vangen tijdelijke verkoopdruk op en verdienen daarmee de prijskorting. Is de korting groot, dan hebben ze op hun bestaande posities verloren, en vragen geldgevers bovendien een hogere marge. Met minder kapitaal en een hogere marge kunnen ze minder opvangen, zodat de korting verder oploopt.
We vereenvoudigen het model van Brunnermeier & Pedersen (2009) tot één periode. Klanten bieden eenheden aan van een activum met fundamentele waarde . Speculanten kopen en buitenstaanders met vraaghelling nemen de rest, zodat de prijskorting is. Dat is de prijsimpact uit het toy-voorbeeld, waarin de vraaghelling was, de elasticiteit maal het vermogen van de buitenstaanders.
De speculanten zijn risiconeutraal en kopen zoveel ze mogen zolang . Door hun bestaande positie is hun kapitaal na de korting . Per eenheid storten ze een marge (de haircut uit het toy-voorbeeld) , die met de korting stijgt als . Ze kopen dus , en een evenwicht is een vast punt van
Rechts staat de korting die ontstaat als de speculanten bij korting zoveel mogelijk kopen. In een evenwicht is dat dezelfde korting als die waarmee we begonnen.
Hoe hard reageert een evenwicht op extra aanbod? Impliciet differentiëren van (1) bij geeft
zolang . Extra verkoopdruk verhoogt de korting dus met de directe impact , vermenigvuldigd met een factor die groeit met beide spiralen. Een grotere positie zet elke extra korting om in meer verlies, en een marge die met de korting oploopt, holt de koopkracht verder uit.
In het toy-voorbeeld is de resterende balans van 80 en de vraaghelling van 2000, zodat de verliesterm is. De factor per ronde was daar 0,36, omdat het verlies de balans zelf al verkleint en elke dollar verlies dus in plaats van dollar verkoop afdwingt. Met wordt de eerste impact ongeveer anderhalf keer zo groot, zoals de eerste daling van 0,9% daar tot 1,35 procentpunt opliep.
In het volledige model stijgen marges omdat geldgevers een liquiditeitsschok niet van een fundamentele schok kunnen onderscheiden. Zo verklaart het model waarom liquiditeit plotseling en in veel effecten tegelijk opdroogt Brunnermeier & Pedersen (2009). Voor de figuur kiezen we , , , en , en tekenen we voor drie niveaus van aanbod. Elk snijpunt met de 45-graden-lijn is een evenwicht.
Source
def bp_map(delta, z, K0, x0, h0, theta, eps):
"""Right-hand side G(Delta) of the simplified Brunnermeier-Pedersen fixed point."""
capital = np.maximum(K0 - x0 * delta, 0.0)
return np.maximum((z - capital / (h0 + theta * delta)) / eps, 0.0)
BP = dict(K0=1.0, x0=8.0, h0=0.08, theta=2.0, eps=60.0)
grid_d = np.linspace(0.0, 0.25, 5001)
fig, ax = plt.subplots()
for i, z_supply in enumerate([7.0, 10.0, 13.0]):
gap = bp_map(grid_d, z_supply, **BP) - grid_d
roots = [0.0] if gap[0] == 0 else [] # Delta = 0 is an equilibrium when G(0) = 0
for k in np.flatnonzero(np.diff(np.sign(gap)) != 0):
if gap[k] != 0: # sign change between grid points k and k + 1
root = optimize.brentq(lambda d: bp_map(d, z_supply, **BP) - d, grid_d[k], grid_d[k + 1])
roots.append(round(float(root), 6))
roots = sorted(set(roots))
ax.plot(grid_d, bp_map(grid_d, z_supply, **BP), color=hap.plotting.COLORS[i], label=f"$G(\\Delta)$, aanbod $z$ = {z_supply:g}")
ax.plot(roots, roots, "o", color=hap.plotting.COLORS[i])
print(f"z = {z_supply:>4}: evenwichten Delta* = {roots}")
ax.plot(grid_d, grid_d, color="black", lw=0.8, ls="--", label="45-graden-lijn")
ax.set_title("Liquide en illiquide evenwichten bij hetzelfde fundament")
ax.set_xlabel("Prijskorting $\\Delta$")
ax.set_ylabel("$G(\\Delta)$")
ax.legend()
plt.show()z = 7.0: evenwichten Delta* = [0.0, 0.033333, 0.11]
z = 10.0: evenwichten Delta* = [0.0, 0.009044, 0.166667]
z = 13.0: evenwichten Delta* = [0.216667]

z = 7.0: evenwichten Delta* = [0.0, 0.033333, 0.11]
z = 10.0: evenwichten Delta* = [0.0, 0.009044, 0.166667]
z = 13.0: evenwichten Delta* = [0.216667]

Figure 1:Bij en liggen een liquide en een illiquide evenwicht naast elkaar, gescheiden door een instabiel evenwicht. Welke markt er komt, hangt dan af van verwachtingen en kleine schokken. Bij geldt voorwaarde 3 van de propositie niet, want , maar die voorwaarde is voldoende en niet nodig. De margespiraal met drukt de koopkracht daar zo ver dat er toch drie evenwichten zijn, en het illiquide evenwicht ligt op 0,11, net onder , omdat de speculanten nog wat kapitaal over hebben. Bij is het aanbod groter dan de opvangcapaciteit , en blijft alleen de illiquide markt over.
Twintig intermediairs: de spiraal op schaal
Hier houden twintig intermediairs met een leverage tussen 6 en 12 hetzelfde activum, en wie na een schok te groot is, verkoopt tegen afgeprijsde koersen (een fire sale). We vergelijken drie werelden bij dezelfde twintigduizend schokken, getrokken met een standaarddeviatie van 3%.
def fire_sale(shock, leverage, h0, theta, absorb, max_rounds=300):
"""Common-asset fire sale among intermediaries with equity 1 and the given leverage.
shock is a vector of fundamental price declines (one scenario per entry). In every
round each intermediary sells down to E / h with h = h0 + theta * (cumulative
decline); total sales depress the price by sales / absorb. Returns the total price
decline and the share of intermediaries with non-positive equity, per scenario.
"""
assets = np.outer(1.0 - shock, leverage)
equity = 1.0 - np.outer(shock, leverage)
price = 1.0 - shock
for _ in range(max_rounds):
haircut = h0 + theta * (1.0 - price)
sales = np.clip(assets - np.maximum(equity, 0.0) / haircut[:, None], 0.0, None)
total = sales.sum(axis=1)
if total.max() < 1e-10:
break
impact = np.minimum(total / absorb, 1.0)
assets = assets - sales
loss = assets * impact[:, None]
assets, equity = assets - loss, equity - loss
price = price * (1.0 - impact)
return 1.0 - price, (equity <= 0).mean(axis=1)
N_INTERMEDIARIES, H0_SIM, ABSORB_SIM = 20, 0.08, 2000.0
leverage_sim = rng.uniform(6.0, 12.0, N_INTERMEDIARIES)
shocks_sim = np.abs(rng.normal(0.0, 0.03, 20_000))
worlds = {"geen prijsimpact": (0.0, np.inf), "fire sale, vaste haircut": (0.0, ABSORB_SIM),
"fire sale + margespiraal": (1.0, ABSORB_SIM)}
declines = {name: fire_sale(shocks_sim, leverage_sim, H0_SIM, theta, absorb)[0]
for name, (theta, absorb) in worlds.items()}
small, large = shocks_sim < 0.02, shocks_sim > 0.06
pd.DataFrame(
{name: {"mediane daling (%)": 100 * np.median(d), "95e percentiel (%)": 100 * np.quantile(d, 0.95),
"99e percentiel (%)": 100 * np.quantile(d, 0.99), "P(daling > 10%)": np.mean(d > 0.10),
"versterking, schok < 2%": np.median(d[small] / shocks_sim[small]),
"versterking, schok > 6%": np.median(d[large] / shocks_sim[large])}
for name, d in declines.items()}
).T.round(3)Kleine schokken worden met een vaste haircut nauwelijks versterkt, grote wel. De figuur toont links die knik en rechts de kans op een grote daling.
Source
shock_grid = np.linspace(0.0005, 0.12, 240)
fig, axes = plt.subplots(1, 2, figsize=(11, 4.3))
for i, (name, (theta, absorb)) in enumerate(worlds.items()):
curve, _ = fire_sale(shock_grid, leverage_sim, H0_SIM, theta, absorb)
axes[0].plot(100 * shock_grid, 100 * curve, color=hap.plotting.COLORS[i], label=name)
tail = np.sort(declines[name])
axes[1].plot(100 * tail, 1.0 - np.arange(tail.size) / tail.size, color=hap.plotting.COLORS[i], label=name)
axes[0].set_title("(a) Totale prijsdaling tegen fundamentele schok")
axes[0].set_xlabel("Fundamentele schok (%)")
axes[0].set_ylabel("Totale prijsdaling (%)")
axes[0].legend()
axes[1].set_yscale("log")
axes[1].set_xlim(0, None)
axes[1].set_title("(b) Kans op een daling groter dan x")
axes[1].set_xlabel("Totale prijsdaling x (%)")
axes[1].set_ylabel("P(daling > x), log-schaal")
fig.tight_layout()
plt.show()
De kans op een daling van meer dan 10% stijgt van 0,1% zonder prijsimpact naar 12% met de margespiraal. Een risicomodel dat alleen de fundamentele schokken kent, zit er dus twee ordes van grootte naast.
4.3Kapitaalratio en risicopremie¶
Als alleen intermediairs het risicovolle activum kunnen houden, is de risicopremie omgekeerd evenredig met hun kapitaalratio. Na verliezen krimpt het eigen vermogen van de sector, terwijl het activum toch gehouden moet worden. Elke dollar eigen vermogen draagt dan meer risico, en de bankiers vragen per eenheid risico een hogere premie. Het model van He & Krishnamurthy (2013) reproduceert zo de sprong van de crisispremies en hun snelle terugval, met een grens aan het eigen vermogen dat huishoudens bij intermediairs willen inleggen.
We nemen de kern over in een lokale versie. De bankiers hebben vermogen en relatieve risicoaversie , en halen bij huishoudens hoogstens aan extra eigen vermogen op, met de inbreng die huishoudens per dollar bankiersvermogen willen doen. Het activum heeft waarde , verwacht overrendement en volatiliteit . Zolang de restrictie bindt, is de kapitaalratio , en anders ligt ze op een vaste .
Bij gegeven volatiliteit is de premie in het beperkte gebied dus omgekeerd evenredig met de kapitaalratio. Ze stijgt met , omdat de bankiers dan meer beloning per eenheid risico vragen. Met en is de premie bij gelijk aan . Het teken uit de intuïtie klopt dus, maar bij vaste volatiliteit groeit de premie precies evenredig met .
4.4Numerieke oplossing: de volatiliteit versterkt de premie¶
Dat de premie sneller dan evenredig groeit, komt door de volatiliteit, die zelf stijgt als de kapitaalratio daalt. Een dividendschok verlaagt de prijs, waarop de gehefboomde intermediair een veelvoud verliest. Daardoor daalt zijn kapitaalratio en stijgt de vereiste premie, die de prijs nog verder drukt.
Om dat uit te rekenen, nemen we een eenvoudige prijsregel. Dividenden groeien met volatiliteit , en een hogere premie verlaagt de log-prijs met , waarbij de verwachte duur van een premieschok in jaren is. In het beperkte gebied verandert het vermogen van de bankiers met maal het rendement.
De volatiliteit is dus de dividendvolatiliteit gedeeld door één min de lusversterking . Onder een kritieke kapitaalratio is die versterking één of meer, en bestaat er geen eindige oplossing. We lossen (4) op voor , en jaar, met een restrictie die onder gaat binden. Deze is het deel van de activa dat met eigen vermogen is gefinancierd, niet de beurswaarde-ratio van de dealers, dus de vorm van de curve telt en niet het niveau.
def hk_premium_vol(eta, gamma=2.0, sigma_d=0.12, tau=0.25, eta_bar=0.5, max_iter=5000):
"""Smallest fixed point of the first-order He-Krishnamurthy amplification (premium, volatility)."""
eta_eff, sigma = min(eta, eta_bar), sigma_d
for _ in range(max_iter):
premium = gamma * sigma**2 / eta_eff
loop_gain = tau * premium * (1.0 / eta_eff - 1.0) if eta < eta_bar else 0.0
if loop_gain >= 1.0:
return np.nan, np.nan
new = sigma_d / (1.0 - loop_gain)
if abs(new - sigma) < 1e-12:
return gamma * new**2 / eta_eff, new
sigma = new
return np.nan, np.nan
eta_grid = np.linspace(0.10, 1.0, 361)
hk_curve = pd.DataFrame([hk_premium_vol(e) for e in eta_grid], index=eta_grid, columns=["premie", "volatiliteit"])
eta_crit = hk_curve["premie"].first_valid_index()
print(f"kritieke kapitaalratio (eerste eindige oplossing op het rooster): {eta_crit:.3f}")
hk_curve.iloc[[int(np.abs(eta_grid - e).argmin()) for e in (1.0, 0.5, 0.4, 0.3, 0.25, 0.2)]].round(3)kritieke kapitaalratio (eerste eindige oplossing op het rooster): 0.198
Onder een kapitaalratio van 0,198 bestaat er geen eindige oplossing meer. De figuur laat zien hoe ver de getrokken premie uitstijgt boven de premie zonder versterking.
Source
fig, ax = plt.subplots()
ax.plot(hk_curve.index, 100 * hk_curve["premie"], color=hap.plotting.COLORS[0], label="risicopremie $\\pi(\\eta)$")
ax.plot(hk_curve.index, 100 * hk_curve["volatiliteit"], color=hap.plotting.COLORS[1], label="volatiliteit $\\sigma_R(\\eta)$")
ax.plot(hk_curve.index, 100 * 2 * 0.12**2 / np.minimum(hk_curve.index, 0.5), color=hap.plotting.COLORS[7], ls="--",
label="premie zonder versterking, $\\gamma\\sigma_D^2/\\eta$")
ax.axvline(0.5, color="black", lw=0.8, ls=":")
ax.axvline(eta_crit, color="black", lw=0.8, ls="--")
ax.set_ylim(0, 40)
ax.set_title("Risicopremie en volatiliteit als functie van de kapitaalratio")
ax.set_xlabel("Kapitaalratio $\\eta$ (stippel: restrictie gaat binden)")
ax.set_ylabel("Procent per jaar")
ax.legend()
plt.show()

Figure 2:Rechts van zijn premie en volatiliteit constant. Links daarvan stijgt de premie eerst ongeveer als en daarna veel sneller, omdat de volatiliteit toeneemt. Onder de gestreepte lijn bestaat in deze benadering geen evenwicht meer, wat het model van een crash is.
Halveert de kapitaalratio van tot , dan stijgt de premie van 7,6% naar 27,1%, veel meer dan de verdubbeling die bij evenredigheid hoort. De premie groeit dus sneller dan , wat He & Krishnamurthy (2013) de niet-lineariteit van crisispremies noemen.
4.5Wat het voorspelt: een factormodel¶
Uit het marginale nut van de intermediair volgt een factormodel met de markt en de groei van de kapitaalratio. Waar komt dat vandaan? Een intermediair die een activum koopt dat slecht rendeert wanneer zijn vermogen krimpt, verliest op het slechtste moment. Hij koopt het daarom alleen tegen een lagere prijs, zodat het verwachte rendement stijgt. Omdat zijn vermogen het totale vermogen maal zijn aandeel daarin is, krijgen een daling van het totale vermogen en een daling van dat aandeel elk een eigen beloning.
He, Kelly en Manela (hierna HKM) schrijven het vermogen van de intermediair als , met het totale vermogen. Omdat de intermediair in het model alle activa houdt, is zijn eigen vermogen gedeeld door de activa, de kapitaalratio , ook zijn aandeel in het totale vermogen. Met consumptie evenredig met vermogen is de SDF dan
zodat een dollar het meest waard is als de markt en de kapitaalratio allebei dalen. De factor is de innovatie uit een AR(1) voor de kapitaalratio, gedeeld door de vorige ratio. HKM rapporteren een AR(1)-coëfficiënt van 0,94 per kwartaal. Met de bèta-representatie uit Het CAPM, die een lineaire SDF omzet in premies evenredig met bèta’s, wordt (5)
Het verwachte overrendement is dus een beloning voor de bèta op de markt plus een beloning voor de bèta op de kapitaalratio, en beide prijzen zijn positief. Dat is de voorspelling uit de intuïtie, met als extra eis dat in elke activaklasse gelijk is.
Adrian, Etula en Muir (hierna AEM) kozen de leverage als toestandsvariabele. De schaduwprijs van de financieringsrestrictie is volgens hen hoog wanneer de leverage laag is, omdat intermediairs dan onder dwang hun balans hebben ingekrompen. Hun SDF is met , waarin de verandering in de log-leverage van broker-dealers is, in het laatste kwartaal van 2008 bijvoorbeeld . Ook bij hen is de prijs van leverage-risico positief.
Hier zit een puzzel, want leverage is het omgekeerde van de kapitaalratio, en toch krijgen schokken in allebei een positieve prijs. Twee verschillen maken de resultaten verenigbaar. AEM meten de boekleverage uit de Flow of Funds, die volgens Adrian & Shin (2010) sterk procyclisch is, omdat dealers hun balans laten krimpen als prijzen dalen. HKM meten het eigen vermogen tegen beurswaarde, en die daalt in een crisis veel sneller. Bovendien bevat de Flow of Funds alleen de broker-dealeractiviteiten, terwijl HKM de beursgenoteerde moederbedrijven nemen. Beide maten kunnen dus in dezelfde slechte toestand dalen, net als in het toy-voorbeeld.
5Simulatie: ziet een factortoets de prijs van intermediairrisico?¶
Stel dat het model waar is. Hoe vaak geeft een tweestapstoets op vijftig jaar kwartaaldata dan een significante ? Zo’n toets schat eerst per portefeuille de bèta’s en regresseert daarna per kwartaal de rendementen op die bèta’s, zoals bij Fama en MacBeth. De standaardfouten van Shanken corrigeren ervoor dat de bèta’s zelf geschat zijn.
We simuleren 35 portefeuilles waarvan de intermediairbèta’s gemiddeld 0,07 zijn, met een standaarddeviatie van 0,11, zoals HKM voor de 25 Fama-French-portefeuilles rapporteren. Elke portefeuille krijgt een kleine vaste pricing error (een afwijking die geen factor verklaart). We variëren de ware van 0 tot 7% per kwartaal, en voegen een nutteloze factor toe die met geen enkel rendement samenhangt Kan & Zhang (1999).
def two_pass_arrays(R, F):
"""Two-pass OLS with intercept on arrays: prices of risk, Fama-MacBeth and Shanken (1992) standard errors."""
# TODO: naar hap.stats (Shanken-correctie voor meerdere factoren in fama_macbeth)
T, N = R.shape
betas = np.linalg.lstsq(np.column_stack([np.ones(T), F]), R, rcond=None)[0][1:].T
Z = np.column_stack([np.ones(N), betas])
gammas = np.linalg.lstsq(Z, R.T, rcond=None)[0].T
lam = gammas.mean(axis=0)
V_fm = np.cov(gammas, rowvar=False) / T
Sigma_f = np.atleast_2d(np.cov(F, rowvar=False))
c = lam[1:] @ np.linalg.solve(Sigma_f, lam[1:])
border = np.zeros_like(V_fm)
border[1:, 1:] = Sigma_f / T
V_sh = (1.0 + c) * (V_fm - border) + border
return lam, np.sqrt(np.diag(V_fm)), np.sqrt(np.clip(np.diag(V_sh), 0.0, None)), betas, Z
def simulate_factor_test(lam_eta, n_sim=1000, T=200, N=35, useless=False):
"""Share of samples with |t| > 1.96 for the intermediary factor in a two-pass test."""
t_stats, estimates = np.empty((n_sim, 2)), np.empty(n_sim)
for s in range(n_sim):
b_m, b_eta = rng.normal(1.0, 0.2, N), rng.normal(0.07, 0.11, N)
f_m, f_eta = rng.normal(0.015, 0.09, T), rng.normal(0.0, 0.12, T)
alpha = rng.normal(0.0, 0.005, N)
R = alpha + 0.015 * b_m + np.outer(f_m - 0.015, b_m) + rng.normal(0.0, 0.04, (T, N))
if not useless:
R += lam_eta * b_eta + np.outer(f_eta, b_eta)
lam, se_fm, se_sh, _, _ = two_pass_arrays(R, np.column_stack([f_m, f_eta]))
estimates[s], t_stats[s] = lam[2], (lam[2] / se_fm[2], lam[2] / se_sh[2])
return {"gem. schatting (% p.k.)": 100 * estimates.mean(), "sd schatting (% p.k.)": 100 * estimates.std(),
"P(|t FM| > 1,96)": np.mean(np.abs(t_stats[:, 0]) > 1.96),
"P(|t Shanken| > 1,96)": np.mean(np.abs(t_stats[:, 1]) > 1.96)}
power = {f"ware lambda {100 * lam:.0f}%": simulate_factor_test(lam) for lam in (0.0, 0.01, 0.02, 0.03, 0.05, 0.07)}
power["nutteloze factor"] = simulate_factor_test(0.0, useless=True)
power_table = pd.DataFrame(power).T
power_table.round(3)De toets ziet een ware prijs pas bij enkele procenten per kwartaal betrouwbaar, en de gestreepte lijn in de figuur laat zien dat de nutteloze factor ver boven de nominale 5% uitkomt.
Source
true_lams = [0, 1, 2, 3, 5, 7]
fig, ax = plt.subplots()
ax.plot(true_lams, power_table["P(|t Shanken| > 1,96)"].iloc[:6], "o-", color=hap.plotting.COLORS[0],
label="ware factor, Shanken")
ax.axhline(power_table.loc["nutteloze factor", "P(|t Shanken| > 1,96)"], color=hap.plotting.COLORS[1], ls="--",
label="nutteloze factor, Shanken")
ax.axhline(0.05, color="black", lw=0.8, ls=":", label="nominaal 5%")
ax.set_title("Kans op een significante prijs van intermediairrisico in 50 jaar kwartaaldata")
ax.set_xlabel("Ware prijs van risico $\\lambda_\\eta$ (% per kwartaal)")
ax.set_ylabel("Fractie steekproeven met |t| > 1,96")
ax.legend()
plt.show()

Figure 3:Een prijs van risico van de omvang die HKM rapporteren (7 tot 9% per kwartaal) is in vijftig jaar vrijwel altijd te zien, een prijs van 2% maar in de helft van de steekproeven. Een factor die met geen enkel rendement samenhangt, krijgt in ruim een kwart van de steekproeven toch een significante prijs.
Zonder ware prijs verwerpt de toets in 10% van de steekproeven in plaats van 5%, omdat de pricing errors de nulhypothese van een perfect model schenden. Met de standaardfouten van Shanken is de nutteloze factor in 28% van de steekproeven significant, omdat de geschatte bèta’s pure ruis zijn en de pricing errors toevallig aan die ruis worden toegeschreven. Hier zien we de standaardfout van 2% terug in de cross-sectie, want de standaardfout van de prijs van een niet-verhandelde factor zegt minder dan hij lijkt.
6Replicatie op echte data¶
6.1De kapitaalratio in de tijd¶
We laden de kapitaalratio en twee spreads, en berekenen het minimum, de correlaties en de jaargemiddelden rond de crisis.
hkm_monthly = hap_data.hkm("monthly")
hkm_quarterly = hap_data.hkm("quarterly")
eta_m = hkm_monthly["intermediary_capital_ratio"]
spreads = pd.concat([hap_data.fred(s)[s].resample("ME").mean() for s in ["BAA10Y", "TEDRATE"]], axis=1, sort=True)
print(f"minimum kapitaalratio: {eta_m.min():.2%} in {eta_m.idxmin():%B %Y}")
print(f"correlatie niveau met BAA10Y: {pd.concat([eta_m, spreads['BAA10Y']], axis=1, sort=True).dropna().corr().iloc[0, 1]:.2f},"
f" met TED-spread: {pd.concat([eta_m, spreads['TEDRATE']], axis=1, sort=True).dropna().corr().iloc[0, 1]:.2f}")
print(f"kwartaalvolatiliteit van de factor: {hkm_quarterly['intermediary_capital_risk_factor'].std():.1%}")
eta_m.resample("YE").mean().loc["2005":"2012"].rename("gemiddelde kapitaalratio").to_frame().T.round(3)minimum kapitaalratio: 2.23% in February 2009
correlatie niveau met BAA10Y: -0.33, met TED-spread: -0.14
kwartaalvolatiliteit van de factor: 12.3%
Het dieptepunt ligt op 2,23% in februari 2009, zoals verwacht, en het jaargemiddelde daalde van 8,2% in 2006 naar 4,1% in 2008. In de figuur vallen de pieken in de spreads samen met de dalen in de kapitaalratio.
Source
fig, axes = plt.subplots(2, 1, figsize=(10, 6.5), sharex=True)
axes[0].plot(eta_m.index, 100 * eta_m, color=hap.plotting.COLORS[0])
axes[0].annotate(f"{eta_m.min():.1%}, {eta_m.idxmin():%b %Y}", (eta_m.idxmin(), 100 * eta_m.min()),
xytext=(15, -5), textcoords="offset points", fontsize=8)
axes[0].set_title("Kapitaalratio van de primary dealers (He-Kelly-Manela)")
axes[0].set_ylabel("Marktwaarde eigen vermogen / activa (%)")
hap.plotting.recession_shading(axes[0])
for i, (s, name) in enumerate({"BAA10Y": "Baa-spread (Baa min 10-jaarsrente)", "TEDRATE": "TED-spread"}.items()):
axes[1].plot(spreads.index, spreads[s], color=hap.plotting.COLORS[i + 1], label=name)
axes[1].set_title("Krediet- en financieringsspreads")
axes[1].set_ylabel("Procentpunt")
axes[1].set_xlabel("Jaar")
axes[1].legend()
hap.plotting.recession_shading(axes[1])
hap.plotting.timeline_axis(axes[1], every=5)
plt.show()

Figure 4:De kapitaalratio daalt in bijna elke recessie, en nergens zo diep als in 2008–2009, toen de spreads hun hoogste niveau van de steekproef bereikten. De dip rond 1998 valt samen met LTCM, zonder recessie.
Het niveau van de kapitaalratio correleert met met de Baa-spread en met met de TED-spread. Lage kapitaalratio’s gaan dus samen met hoge spreads, maar het verband is niet mechanisch.
6.2Broker-dealer-leverage uit de Flow of Funds¶
We bouwen de boekleverage uit de Flow of Funds en zetten de crisiskwartalen naast de maten van HKM.
assets_bd = hap_data.fred("BOGZ1FL664090005Q")["BOGZ1FL664090005Q"]
liabilities_bd = hap_data.fred("BOGZ1FL664190005Q")["BOGZ1FL664190005Q"]
book_leverage = assets_bd / (assets_bd - liabilities_bd)
book_leverage.index = book_leverage.index + pd.offsets.QuarterEnd(0) # FRED stamps the quarter start
dlog_lev = np.log(book_leverage).diff().loc["1968":]
lev_factor = (dlog_lev - dlog_lev.groupby(dlog_lev.index.quarter).transform("mean") + dlog_lev.mean()).rename("lev")
eta_factor = hkm_quarterly["intermediary_capital_risk_factor"].rename("eta")
eta_q = hkm_quarterly["intermediary_capital_ratio"]
crisis = pd.concat([book_leverage.rename("boekleverage (FoF)"), lev_factor.rename("AEM-factor"),
eta_q.rename("kapitaalratio (HKM)"), (1 / eta_q).rename("marktleverage (HKM)"),
eta_factor.rename("HKM-factor")], axis=1, sort=True)
changes = pd.concat([np.log(book_leverage).diff(), np.log(1 / eta_q).diff()], axis=1, sort=True).dropna()
print(f"correlatie AEM-factor en HKM-factor: {crisis[['AEM-factor', 'HKM-factor']].dropna().corr().iloc[0, 1]:.2f}")
print(f"correlatie log-veranderingen boekleverage (FoF) en marktleverage (HKM): {changes.corr().iloc[0, 1]:.2f}")
crisis.loc["2007-06":"2009-12"].round(3)correlatie AEM-factor en HKM-factor: 0.06
correlatie log-veranderingen boekleverage (FoF) en marktleverage (HKM): -0.10
De boekleverage van de broker-dealers halveerde van 47,0 in het eerste kwartaal van 2008 naar 22,6 eind 2009, het procyclische patroon van Adrian & Shin (2010). De marktleverage van de primary dealers steeg intussen van 22 naar een piek van 38 eind 2008, bijna een verdubbeling, en viel eind 2009 terug tot 19,8.
Toch zijn beide factoren in het vierde kwartaal van 2008 sterk negatief, met voor AEM en voor HKM, zodat ze in het slechtste kwartaal dezelfde kant op wijzen. Over de hele steekproef is hun correlatie met 0,06 bijna nul. Ze meten hetzelfde begrip op een andere manier, en als factoren zijn ze toch bijna onafhankelijk.
6.3De cross-sectie¶
We schatten elk model op twee manieren, met de tweestapstoets uit de simulatie en met een GMM-schatting van de SDF zonder intercept. Daarin is , en de gewichten zijn van Newey-West met twee lags. De Treasury-portefeuilles zijn houdrendementen van zero-coupon-obligaties uit de Svensson-curve. Het rendement op een obligatie met looptijd jaar is de prijs een maand later, , gedeeld door de prijs nu, , min één.
gsw = hap_data.gsw()
svensson = gsw[["BETA0", "BETA1", "BETA2", "BETA3", "TAU1", "TAU2"]].dropna(subset=["BETA0", "TAU1"]).resample("ME").last()
svensson = svensson.fillna({"BETA3": 0.0, "TAU2": 1.0})
def svensson_yield(par, maturity):
"""Continuously compounded zero-coupon yield from Svensson parameters (decimals, maturity in years)."""
x1, x2 = maturity / par["TAU1"], maturity / par["TAU2"]
s1, s2 = (1 - np.exp(-x1)) / x1, (1 - np.exp(-x2)) / x2
return par["BETA0"] + par["BETA1"] * s1 + par["BETA2"] * (s1 - np.exp(-x1)) + par["BETA3"] * (s2 - np.exp(-x2))
bond_returns = pd.DataFrame({
f"OBL {n}j": np.exp(-(n - 1 / 12) * svensson_yield(svensson, n - 1 / 12).shift(-1) + n * svensson_yield(svensson, n)).shift(1) - 1
for n in (1, 2, 3, 4, 5)
})
ff3 = hap_data.french("F-F_Research_Data_Factors")
momentum_factor = hap_data.french("F-F_Momentum_Factor")
size_bm = hap_data.french("25_Portfolios_5x5")
momentum_dec = hap_data.french("10_Portfolios_Prior_12_2").add_prefix("MOM ")
monthly = pd.concat([size_bm, momentum_dec, bond_returns, ff3, momentum_factor], axis=1, sort=True).dropna(subset=["RF"])
def to_quarterly(frame):
"""Compound monthly simple returns to quarters; quarters with missing months become NaN."""
return ((1 + frame).resample("QE").prod() - 1).where(frame.resample("QE").count() == 3)
quarterly = to_quarterly(monthly)
cols_25, cols_mom, cols_bond = list(size_bm.columns), list(momentum_dec.columns), list(bond_returns.columns)
excess_q = quarterly[cols_25 + cols_mom + cols_bond].sub(quarterly["RF"], axis=0)
factors_q = pd.DataFrame({"Mkt": to_quarterly(ff3["Mkt-RF"] + ff3["RF"]) - quarterly["RF"],
"SMB": to_quarterly(ff3["SMB"]), "HML": to_quarterly(ff3["HML"]),
"Mom": to_quarterly(momentum_factor["Mom"])})
factors_q = pd.concat([factors_q, eta_factor, lev_factor], axis=1, sort=True)
print(f"correlatie HKM-factor met het marktrendement: {factors_q[['eta', 'Mkt']].dropna().corr().iloc[0, 1]:.2f}")
excess_q.loc["1970":].describe().loc[["count", "mean", "std"]].T.groupby(lambda c: c.split()[0]).mean().round(4)correlatie HKM-factor met het marktrendement: 0.76
De HKM-factor correleert met 0,76 met het marktrendement, een getal waarop we aan het eind terugkomen. De volgende cel schat alle modellen op drie combinaties van testactiva en steekproef.
def two_pass(excess, factors):
"""Two-pass test with intercept on DataFrames; returns prices of risk, Shanken t, R2, MAPE and pricing errors."""
data = pd.concat([excess, factors], axis=1, sort=True).dropna()
R, F = data[excess.columns].to_numpy(), data[factors.columns].to_numpy()
lam, _, se_sh, _, Z = two_pass_arrays(R, F)
errors = R.mean(axis=0) - Z @ lam
names = ["const", *factors.columns]
return {"T": len(R), "lam": pd.Series(lam, names), "t_shanken": pd.Series(lam / se_sh, names),
"r2": 1 - errors.var() / R.mean(axis=0).var(), "mape": np.abs(errors).mean(),
"mean": pd.Series(R.mean(axis=0), excess.columns), "fitted": pd.Series(Z @ lam, excess.columns)}
def gmm_sdf(excess, factors, lags=2):
"""First-stage GMM for m = 1 - (f - E f)'b with the factor means as extra moments (Cochrane 2005, ch. 13)."""
# TODO: naar hap.stats (GMM-schatting van een lineaire SDF)
data = pd.concat([excess, factors], axis=1, sort=True).dropna()
R, F = data[excess.columns].to_numpy(), data[factors.columns].to_numpy()
T, N = R.shape
K = F.shape[1]
F_dm = F - F.mean(axis=0)
D, mean_R = R.T @ F_dm / T, R.mean(axis=0)
b = np.linalg.lstsq(D, mean_R, rcond=None)[0]
u = np.column_stack([R * (1 - F_dm @ b)[:, None], F_dm])
u = u - u.mean(axis=0)
S = u.T @ u / T
for j in range(1, lags + 1):
gamma_j = u[j:].T @ u[:-j] / T
S += (1 - j / (lags + 1)) * (gamma_j + gamma_j.T)
# d: Jacobian of the N pricing moments and K mean moments w.r.t. (b, E f);
# a: first-stage selection, D' on the pricing moments and the identity on the mean moments
d = np.block([[-D, mean_R[:, None] * b[None, :]], [np.zeros((K, K)), -np.eye(K)]])
a = np.block([[D.T, np.zeros((K, K))], [np.zeros((K, N)), np.eye(K)]])
ad_inv = np.linalg.inv(a @ d)
V = ad_inv @ a @ S @ a.T @ ad_inv.T / T
Sigma_f = np.atleast_2d(np.cov(F, rowvar=False, bias=True))
lam = Sigma_f @ b
se_lam = np.sqrt(np.diag(Sigma_f @ V[:K, :K] @ Sigma_f))
errors = mean_R - D @ b
return {"lam": pd.Series(lam, factors.columns), "t": pd.Series(lam / se_lam, factors.columns),
"r2": 1 - (errors**2).sum() / ((mean_R - mean_R.mean())**2).sum()}
MODELS = {"CAPM": ["Mkt"], "FF3": ["Mkt", "SMB", "HML"], "Carhart": ["Mkt", "SMB", "HML", "Mom"],
"HKM": ["Mkt", "eta"], "AEM": ["lev"]}
TEST_SETS = [("FF25, 1970–2012", "1970", "2012", cols_25),
("25+10+5 obl., 1970–2012", "1970", "2012", cols_25 + cols_mom + cols_bond),
("25+10+5 obl., 1970–2025", "1970", "2025", cols_25 + cols_mom + cols_bond)]
rows, fits = {}, {}
for label, start, end, cols in TEST_SETS:
for model, fcols in MODELS.items():
R_s, F_s = excess_q.loc[start:end, cols], factors_q.loc[start:end, fcols]
tp, gm = two_pass(R_s, F_s), gmm_sdf(R_s, F_s)
key = {"HKM": "eta", "AEM": "lev"}.get(model)
rows[(label, model)] = {
"kwartalen": tp["T"],
"λ interm. (% p.k.)": 100 * tp["lam"][key] if key else np.nan,
"t Shanken": tp["t_shanken"][key] if key else np.nan,
"λ GMM (% p.k.)": 100 * gm["lam"][key] if key else np.nan,
"t GMM": gm["t"][key] if key else np.nan,
"R² twee stappen": tp["r2"], "MAPE (% p.k.)": 100 * tp["mape"], "R² GMM": gm["r2"],
}
fits[(label, model)] = tp
cross_section = pd.DataFrame(rows).T
cross_section.round(2)In het HKM-model is de prijs van intermediairrisico in elke specificatie positief. De tabel hieronder zet de kern naast het origineel.
| grootheid | origineel | hier |
|---|---|---|
| minimum kapitaalratio | rond 2,2%, februari 2009 | 2,23%, februari 2009 |
| op FF25, GMM, 1970–2012 (% per kwartaal) | 7 (aandelen) | 6,96 () |
| op alle testactiva, GMM, 1970–2012 (% per kwartaal) | 9 () | 4,94 () |
| op FF25, HKM tegen CAPM | hoger dan CAPM | 0,44 tegen 0,09 |
| prijs van leverage-risico (AEM), alle testactiva | 62% per jaar | 8,79% per kwartaal (Shanken- 2,08) |
| van AEM | 77% | 0,41 |
De replicatie is gedeeltelijk geslaagd, want het dieptepunt ligt waar het moet liggen en het teken is overal positief. Op aandelen komt de GMM-schatting bovendien vrijwel precies op de 7% van HKM uit, al is de tweestapsschatting met intercept op FF25 lager, 4,98% met een Shanken- van 1,61.
Op alle testactiva ligt onze GMM-schatting onder de 9% van HKM en is de iets lager, wat binnen de verwachte afwijking valt, want zes activaklassen ontbreken en onze obligaties komen uit een geschatte curve. Of obligaties dezelfde prijs krijgen als aandelen, toetsen we niet apart, want de vijf obligatieportefeuilles zitten alleen in de gemengde testset. Ook de AEM-factor krijgt een positieve prijs, maar lager dan de 62% per jaar van AEM, en hij haalt maar ruim de helft van de gepubliceerde .
De figuur zet voor drie modellen de gemiddelde rendementen uit tegen de voorspelling, en vooral bij de momentumportefeuilles lopen de modellen uiteen.
Source
fig, axes = plt.subplots(1, 3, figsize=(12, 4.2), sharex=True, sharey=True)
groups = {"size/BM": cols_25, "momentum": cols_mom, "obligaties": cols_bond}
for ax, model in zip(axes, ["CAPM", "HKM", "Carhart"], strict=True):
fit = fits[("25+10+5 obl., 1970–2025", model)]
for i, (group, cols) in enumerate(groups.items()):
ax.scatter(100 * fit["fitted"][cols], 100 * fit["mean"][cols], s=18, color=hap.plotting.COLORS[i], label=group)
ax.plot([-0.5, 4.5], [-0.5, 4.5], color="black", lw=0.8)
ax.set_title(f"{model}: $R^2$ = {fit['r2']:.2f}")
ax.set_xlabel("Voorspeld (% per kwartaal)")
axes[0].set_ylabel("Gerealiseerd gemiddelde (% per kwartaal)")
axes[0].legend()
fig.tight_layout()
plt.show()

Figure 5:Gemiddelde kwartaalrendementen 1970–2025 tegen de voorspelling van drie modellen. Het HKM-model trekt de size/BM-portefeuilles in de goede richting, maar laat de momentumportefeuilles als een verticale wolk staan. Carhart, met een factor die op momentum gesorteerd is, legt die wolk op de lijn.
Met momentum- en obligatieportefeuilles erbij daalt de van het HKM-model over 1970–2012 naar 0,22, ver onder Carhart (0,85). Het model verklaart momentum dus niet (oefening 3). Dat de AEM-factor het slechter doet dan in het artikel, kan aan de obligaties liggen, aan de herziene Flow of Funds of aan de seizoenscorrectie, en met gratis data kunnen we dat niet uitmaken.
6.4Voorspelt de kapitaalratio rendementen en spreads?¶
HKM rapporteren dat de kapitaalratio toekomstige rendementen voorspelt in vijf van de zeven activaklassen. We regresseren het overrendement van de markt over maanden, en de verandering in de Baa-spread, op het niveau van de kapitaalratio. Omdat de ratio persistent is en de horizonnen overlappen, gebruiken we de standaardfouten van Hodrick (1992) (variant 1B). Daarbovenop komt nog de Stambaugh-bias uit Voorspelbaarheid van rendementen.
def hodrick_1b(y, x, horizon):
"""Regress y_{t+1} + ... + y_{t+h} on x_t; Hodrick (1992) 1B standard errors under no predictability."""
# TODO: naar hap.stats (method="hodrick-1b" in long_horizon_regression)
frame = pd.concat([y.rename("y"), x.rename("x")], axis=1, sort=True).dropna()
r, z = frame["y"].to_numpy(), frame["x"].to_numpy()
T = len(r)
target = np.convolve(r, np.ones(horizon), "valid")[1:]
n = len(target)
X = np.column_stack([np.ones(n), z[:n]])
coef = np.linalg.lstsq(X, target, rcond=None)[0]
shocks = r[1:] - r[1:].mean()
cum = np.vstack([np.zeros(2), np.cumsum(np.column_stack([np.ones(T), z]), axis=0)])
idx = np.arange(horizon - 1, T - 1)
rolled = cum[idx + 1] - cum[idx + 1 - horizon] # x_t + ... + x_{t-h+1}
S = (rolled * shocks[idx, None] ** 2).T @ rolled / n
Q_inv = np.linalg.inv(X.T @ X / n)
V = Q_inv @ S @ Q_inv / n
resid = target - X @ coef
return {"helling": coef[1], "t (Hodrick 1B)": coef[1] / np.sqrt(V[1, 1]), "R²": 1 - resid.var() / target.var(),
"waarnemingen": n}
market_m = ff3["Mkt-RF"].loc["1970":]
baa_change = spreads["BAA10Y"].diff() / 100
predict = {("marktrendement", h): hodrick_1b(market_m, eta_m, h) for h in (1, 12, 24, 36)}
predict.update({("verandering Baa-spread", h): hodrick_1b(baa_change, eta_m, h) for h in (1, 12, 24)})
pd.DataFrame(predict).T.rename_axis(["te voorspellen", "horizon (maanden)"]).round(3)Het teken klopt, want een kapitaalratio die één procentpunt lager ligt, gaat samen met een marktrendement dat over drie jaar 4,1 procentpunt hoger ligt, en met een dalende Baa-spread. Toch haalt geen van de -waarden de 1,96, want voor de markt zijn ze op een jaar en op drie jaar. In 55 jaar zit maar een handvol crises, en de voorspellende kracht rust op dezelfde paar episodes die de kapitaalratio laag maakten.
7Wat er brak, en wat daarna kwam¶
Wat het model verklaart. Intermediary asset pricing gaf 2008 een mechanisme dat consumptiemodellen missen, met gedwongen verkopen, oplopende marges en een marginale belegger wiens kapitaal in een paar kwartalen verdwijnt. Het verklaart waarom liquiditeit overal tegelijk opdroogt en waarom premies niet-lineair stijgen en snel terugvallen. Ook verklaart het waarom identieke kasstromen verschillende prijzen kregen, want bij Gârleanu & Pedersen (2011) hangt het vereiste rendement ook af van de marge. Daarnaast beschreven Mitchell & Pulvino (2012) hoe hedgefondsen na de val van hun prime brokers gelijke activa niet meer op gelijke prijzen konden houden. In onze data heeft het model in elk geval het teken goed.
Waar het breekt. Het model loopt vast op de meting. Leverage en kapitaalratio zijn elkaars omgekeerde, maar krijgen allebei een positieve prijs, en als factoren zijn ze bijna ongecorreleerd. De van AEM halen we met de huidige Flow of Funds niet, en het HKM-model laat momentum liggen. De HKM-factor correleert bovendien met 0,76 met het marktrendement, zodat een deel van zijn succes de bèta van dealeraandelen kan zijn. Tot slot laat Figuur 3 zien dat een factor zonder enige samenhang met rendementen in ruim een kwart van de steekproeven significant lijkt.
Risico of vergissing? In de Chicago-lezing is het marginale nut van de intermediair de SDF, en waren de hoge premies van 2008 de rationele prijs van risico toen kapitaal schaars was. Wie toen kocht, werd in de termen van Santa-Clara betaald voor het dragen van risico en niet voor een inzicht dat de markt miste. In de Yale-lezing zijn dit de limits of arbitrage van Behavioral finance en limits of arbitrage op macroschaal, en is een prijsverschil tussen identieke kasstromen een mispricing en geen premie. Beide lezingen zijn het eens over de kapitaalratio en de spreads. De kampen zouden te scheiden zijn door na te gaan of kortingen terugliepen zodra er kapitaal binnenkwam, ongeacht het risico, maar de benodigde posities zijn grotendeels niet openbaar. Santa-Clara trekt een les die voor beide kampen geldt: hefboom, waardering tegen marktprijzen en een deadline vormen samen het recept voor de ondergang Santa-Clara (2026).
Wat er daarna kwam. Als voorspelbare premies die in crises exploderen zowel een rationele discontovoet als een tijdelijke mispricing kunnen zijn, rijst de vraag wat het vakgebied daarmee deed toen Fama en Shiller in 2013 samen de Nobelprijs kregen. Die vraag behandelt Fama, Shiller en “Discount Rates”.
8Oefeningen¶
Solution to Exercise 1
(1) Het verlies is , dus , en . De toegestane balans is , zodat de intermediair verkoopt.
eta_4, rounds_4, price_4 = deleverage(E0, A0, H0, EPS * W_OUT, 0.04)
pd.DataFrame({
"schok 2%": {"eerste verkoop": const_rounds.loc[0, "verkoop"], "eigen vermogen na afloop": const_rounds["E"].iloc[-1],
"prijsdaling (%)": 100 * (1 - const_price), "versterking": (1 - const_price) / SHOCK},
"schok 4%": {"eerste verkoop": rounds_4.loc[0, "verkoop"], "eigen vermogen na afloop": rounds_4["E"].iloc[-1],
"prijsdaling (%)": 100 * (1 - price_4), "versterking": (1 - price_4) / 0.04},
}).round(3)(2) De versterking daalt licht, van 1,67 naar 1,58, omdat de balans na de eerste verkoop kleiner is. Het eigen vermogen daalt daarentegen tot 4,62, een verlies van 54% tegen 31% bij de kleine schok. Bij één intermediair aan zijn grens groeit de prijsdaling dus ongeveer evenredig met de schok, en de sterke niet-lineariteit ontstaat pas als intermediairs buffers hebben, zoals bij de twintig intermediairs in de theorie.
Solution to Exercise 2
(1) Omdat is . Bij en bindende restrictie geldt . Met is die oplossing negatief, zodat alleen het illiquide evenwicht overblijft. Met is en .
De cel zoekt het kleinste positieve vaste punt en zet de formule naast een eindige differentie.
def bp_equilibrium(z, K0, x0, h0, theta, eps):
"""Smallest positive fixed point of the simplified Brunnermeier-Pedersen map."""
grid = np.linspace(1e-9, z / eps, 200_001)
gap = bp_map(grid, z, K0, x0, h0, theta, eps) - grid
k = np.flatnonzero(np.diff(np.sign(gap)) != 0)
return optimize.brentq(lambda d: bp_map(d, z, K0, x0, h0, theta, eps) - d, grid[k[0]], grid[k[0] + 1]) if k.size else z / eps
rows = {}
for x0_ex in (8.0, 3.0):
for theta_ex in (0.0, 0.5):
pars = dict(K0=1.0, x0=x0_ex, h0=0.08, theta=theta_ex, eps=60.0)
d_star = bp_equilibrium(13.0, **pars)
h_star, K_star = 0.08 + theta_ex * d_star, max(1.0 - x0_ex * d_star, 0.0)
loss_part = x0_ex / (60.0 * h_star) if K_star > 0 else 0.0
margin_part = theta_ex * K_star / (60.0 * h_star**2)
dz = 1e-4
numeric = (bp_equilibrium(13.0 + dz, **pars) - d_star) / dz
rows[(x0_ex, theta_ex)] = {"Delta*": d_star, "G' verlies": loss_part, "G' marge": margin_part,
"multiplier formule": (1 / 60) / (1 - loss_part - margin_part),
"multiplier numeriek": numeric}
pd.DataFrame(rows).T.rename_axis(["x0", "theta"]).round(4)(2) Bij ligt het evenwicht in de hoek , waar de speculanten geen kapitaal meer hebben en niets kopen, zodat de multiplier daar voor beide de directe impact is. Bij en is en de multiplier . Met springt het evenwicht naar een korting van 16,5%, waar de lokale multiplier lager is, omdat de speculanten weinig kapitaal over hebben. De margespiraal werkt dus vooral op het niveau van de korting, en de eindige differentie bevestigt de formule.
(3) Bij is , dus het evenwicht wordt instabiel zodra . Dezelfde verkoopdruk geeft dus een kleine of een catastrofale daling, afhankelijk van de positie die de speculanten al hadden.
Solution to Exercise 3
De cel herhaalt de schattingen uit de replicatie voor de vier combinaties.
rows = {}
for label, cols in [("FF25", cols_25), ("FF25 + momentum", cols_25 + cols_mom)]:
for start, end in [("1970", "2006"), ("1970", "2025")]:
R_s, F_s = excess_q.loc[start:end, cols], factors_q.loc[start:end, ["Mkt", "eta"]]
tp, gm = two_pass(R_s, F_s), gmm_sdf(R_s, F_s)
rows[(label, f"{start}–{end}")] = {"λ (% p.k.)": 100 * tp["lam"]["eta"], "t Shanken": tp["t_shanken"]["eta"],
"λ GMM (% p.k.)": 100 * gm["lam"]["eta"], "t GMM": gm["t"]["eta"],
"R² twee stappen": tp["r2"]}
pd.DataFrame(rows).T.round(2)Vóór de crisis is de prijs ongeveer 9 tot 11% per kwartaal, met hoge GMM--waarden. Tot 2025 zakt de tweestapsschatting met momentum tot bijna nul, terwijl de GMM-schatting zonder intercept rond 5% blijft. De momentumportefeuilles spreiden sterk in gemiddeld rendement maar weinig in , en een vrij intercept neemt dat verschil op. Het teken blijft dus in alle vier de combinaties positief, maar het niveau hangt sterk af van de jaren na 2006, van de testactiva en van de methode Lewellen et al. (2010).
- He, Z., Kelly, B., & Manela, A. (2017). Intermediary Asset Pricing: New Evidence from Many Asset Classes. Journal of Financial Economics, 126(1), 1–35. 10.1016/j.jfineco.2017.08.002
- Adrian, T., Etula, E., & Muir, T. (2014). Financial Intermediaries and the Cross-Section of Asset Returns. The Journal of Finance, 69(6), 2557–2596. 10.1111/jofi.12189
- Shleifer, A., & Vishny, R. W. (1997). The Limits of Arbitrage. Journal of Finance, 52(1), 35–55. 10.1111/j.1540-6261.1997.tb03807.x
- Gromb, D., & Vayanos, D. (2002). Equilibrium and Welfare in Markets with Financially Constrained Arbitrageurs. Journal of Financial Economics, 66(2–3), 361–407. 10.1016/S0304-405X(02)00228-3
- Brunnermeier, M. K., & Pedersen, L. H. (2009). Market Liquidity and Funding Liquidity. The Review of Financial Studies, 22(6), 2201–2238. 10.1093/rfs/hhn098
- He, Z., & Krishnamurthy, A. (2013). Intermediary Asset Pricing. American Economic Review, 103(2), 732–770. 10.1257/aer.103.2.732
- Santa-Clara, P. (2026). What I Learned About Asset Pricing. LinkedIn post. https://www.linkedin.com/pulse/what-i-learned-asset-pricing-pedro-santa-clara-apjce/
- Gorton, G., & Metrick, A. (2012). Securitized Banking and the Run on Repo. Journal of Financial Economics, 104(3), 425–451. 10.1016/j.jfineco.2011.03.016
- Adrian, T., & Shin, H. S. (2010). Liquidity and Leverage. Journal of Financial Intermediation, 19(3), 418–437. 10.1016/j.jfi.2008.12.002
- Kan, R., & Zhang, C. (1999). Two-Pass Tests of Asset Pricing Models with Useless Factors. The Journal of Finance, 54(1), 203–235. 10.1111/0022-1082.00102
- Hodrick, R. J. (1992). Dividend Yields and Expected Stock Returns: Alternative Procedures for Inference and Measurement. Review of Financial Studies, 5(3), 357–386. 10.1093/rfs/5.3.351
- Gârleanu, N., & Pedersen, L. H. (2011). Margin-Based Asset Pricing and Deviations from the Law of One Price. The Review of Financial Studies, 24(6), 1980–2022. 10.1093/rfs/hhr027
- Mitchell, M., & Pulvino, T. (2012). Arbitrage Crashes and the Speed of Capital. Journal of Financial Economics, 104(3), 469–490. 10.1016/j.jfineco.2011.09.002
- Lewellen, J., Nagel, S., & Shanken, J. (2010). A Skeptical Appraisal of Asset Pricing Tests. Journal of Financial Economics, 96(2), 175–194. 10.1016/j.jfineco.2009.09.001
Deze onafhankelijke site is niet verbonden aan of onderschreven door QuantEcon.
De vormgeving is gebaseerd op het MIT-gelicentieerde
QuantEcon MyST-thema.
Zie THIRD
