EBA calibration, regime discipline and nominal rigidities — consumption falls and the crisis now lasts - #31
Open
adambutlin wants to merge 30 commits into
Open
EBA calibration, regime discipline and nominal rigidities — consumption falls and the crisis now lasts#31adambutlin wants to merge 30 commits into
adambutlin wants to merge 30 commits into
Conversation
Two-country MU HANK gains genuine nominal price and wage Phillips curves, closed without any policy rate: the terms-of-trade identity p/p(-1) = (1+pi_F)/(1+pi_D) pins the inflation differential off the existing p unknown, and a union-inflation normalisation pins the level. Financial contracts stay real, so no Fisher relation or deposit-market rework. Markups are subsidy-neutralised, so the steady state is bit-identical and all five new residuals are exactly zero at it. Flex is the exact kappa -> inf limit, which supplies the regression gate and removes the need for a STICKY code switch. Scope: sticky becomes the paper's new baseline; flex demotes to an appendix robustness run. Union monetary channel explicitly out of scope. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
FRBSF WP 2025-10 is the closest published analogue (two-country MU, GK intermediaries, endogenous default, cross-border sovereign holdings; IT/DE 2012). Three corrections and one benchmark section: - kappa_p = 0.0871 agrees with their Calvo-equivalent 0.0846 to within 3%; the gap is entirely the discount factor. No recalibration needed. - mu_p is free to first order under subsidy-neutralisation (the gap linearises to mc_hat regardless), so their theta^c=11 vs eps=6 is a non-issue. - add-nkwpc's stated motivation is wrong for GHH: there is no wealth effect on labour supply by construction. Goal restated as adjustment moving off the real wage onto hours, which is what the distributional goal needs. - omega_pi_D = 0.071 retained over their 0.5/0.5: the closure gives pi_D = -(1-omega)*dlog p, so 0.071 puts 93% of terms-of-trade adjustment into Greek deflation, the 2010-12 pattern. 0.5 splits it evenly. Also records the live risk: their nominal side does little propagation (inflation +/-0.1% vs investment 9%), their output contraction comes from a loan-in-advance constraint this model lacks, and they never publish a flexible-price counterfactual -- which the kappa -> inf gate produces here for free. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Author decision 2026-08-05. Scope narrows to price stickiness; the labour block is untouched, so labor_market_D/F stays in every block list and keeps labor_mkt_res_D/F as the target for N. System count is unchanged at 27x27 -- the wage curve had replaced labor_mkt_res one-for-one, so removing it swaps a target name back rather than changing the count. Four new residuals (two price NKPCs, ToT identity, union normalisation), all exactly zero at the existing steady state. Flex is now the kappa_p -> inf limit of a single curve. kappa_w and epsilon_w drop out of the calibration; mu_w stays 1.0 and untouched. Scope now matches Bi-Foerster-Traum on the labour side (both price-sticky, flexible wages), which removes the need to defend a wage slope with no benchmark in this model class. A Taylor rule was scoped and rejected the same day; the reasoning is recorded under "Explicitly out of scope" so it is not re-derived: a policy rule only bites if it pins a real rate, which frees both deposit-market conditions and requires either a cross-border banking claim (rewriting external_account_D) or an ECB reserve asset inside the GK incentive constraint. Also records that wage rigidity's loss costs the hours-rationing component of distributional incidence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two additions, both surfaced by asking whether the spec fixes C_D[0] = +0.2164%.
Nominal deposits. rdep_{D,F} is reinterpreted as a nominal rate i_dep_{D,F} and
stays the free unknown clearing deposit_mkt_{D,F} -- no absorber, no cross-border
claim, external_account_D untouched, still 27x27. The absorber problem belonged
to the Taylor rule alone, not to nominal contracts. deposit_return already has the
right shape (predetermined rate x period-t deflator) so it is a one-line change,
and T-2 is not reopened. Two derived real rates, ex-post and ex-ante, substitute
into bank_return, capital_fund, P1 and the bond FOCs per a stated table; all three
collapse to today's rdep at pi = 0, so the SS stays bit-identical.
Banks hold real assets against nominal liabilities, so deflation deepens the
net-worth loss -- Fisher-Bernanke, aimed at Y_D[0] being two orders of magnitude
below the benchmark.
Markup rent routing, which was a genuine gap: with the markup in labour demand
only, w*N = mu_p*mc*(1-alpha)Y while mpk is untouched, leaving an unrouted rent
off steady state -- a Walras leak of the W-1/W-2 class. Distributed proportional
to productivity e (Auclert-Rognlie-Straub), so household labour+profit income is
(1-alpha)Y*e exactly as in the flex model, the wedge affects hiring only, and the
marginal wage stays w so labor_market is unchanged. The textbook lump-sum rebate
was rejected: markups are countercyclical, so it would push C_D[0] further up.
Rollout is now staged so price stickiness and the Fisher channel are separately
attributable, and the spec states plainly that C_D[0] may not flip -- BFT get
consumption rising on impact too, with a Taylor rule, nominal debt and a
loan-in-advance constraint.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
16 tasks, 87 steps, across three phases: a behaviour-preserving refactor, price Phillips curves with real deposits, then nominal deposits, followed by recalibration and regeneration. Adds fast unit tests (code/test_nkpc_blocks.py) that evaluate SSJ @simple blocks directly via .steady_state(), so sign and identity errors surface in seconds rather than after a 10-minute pipeline run. One refinement to the spec, adopted for a strictly smaller diff: rdep_i keeps its name and becomes the derived EX-ANTE real rate, which is already exactly what intermediation_P1, divert_bond_foc and divert_portfolio_adj mean by it. Those three blocks and steady_state.py's eight rdep reads therefore need no changes at all; only deposit_return, bank_return and capital_fund do, plus one new block per country. Three hard gates: main.py output byte-identical after the refactor; IRFs matching the pre-change baseline at kappa_p = 1e4; and n_inter_D[0] strictly more negative once deposits go nominal, which catches a reversed Fisher channel. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Read .claude/hooks/require-docs-before-commit.sh: it denies any commit staging code/** or *.py unless STATE.md, PROGRESS.md AND HANDOFF.md are all staged. Every intermediate commit in the plan staged only PROGRESS.md and would have been blocked. All 13 code-staging git add lines now list all three; the two docs-only commits are untouched since the gate does not fire on them. Also records that main.py (~8-12 min) and regime_model.py --force exceed the Bash tool's 10-minute cap and must run in the background, so a timeout is not mistaken for a model failure. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CLAUDE.md's goods_mkt_D <= 1e-14 refers to the DYNAMIC solve, where it is an explicit solver target and never printed on its own. What main.py prints under "SS goods residuals" are the steady-state values, which sit at ~4e-7 on unmodified code. A subagent checking the doc line against the printed line would report a false failure. Records the full measured baseline from the verified Task 1 run at f3711bd -- SS residuals, the block-table verdict, b_gov_D[499] stability, rho_b, both impact signs, and the TPI per-gamma maxima -- so every later task compares against reality. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
No-op. Verified main.py output is byte-identical before and after.
2/3 tests green. test_firm_profit_restores_factor_exhaustion_off_steady_state is intentionally red until Task 4 adds mu_p/mc to labor_demand_D.
Turns test_firm_profit_restores_factor_exhaustion_off_steady_state green. Full nkpc block test file now 8 passed, 0 failed.
Two new global blocks: terms_of_trade turns the fixed-exchange-rate identity p/p(-1) = (1+pi_F)/(1+pi_D) into a residual on p (already an unknown via goods_mkt_D), pinning pi_D - pi_F. union_inflation supplies the level normalisation (omega_pi_D*pi_D + (1-omega_pi_D)*pi_F = 0, the ECB's phi_pi -> inf union-PPI-stabilisation limit) with no policy rate or Fisher relation anywhere in the model. At omega_pi_D = 0.071 this is a 93/7 split of any terms-of-trade move into Greek deflation vs German inflation -- the internal-devaluation pattern. Also fixes a bug found in the new test itself: test_closure_puts_93pct_of_tot_move_into_D_deflation used dlog_p=1e-4, but the closed-form pi's are only first-order accurate against the exact nonlinear tot_res, leaving an O(dlog_p^2) gap (~4e-5 relative at that scale) that misses the test's own rel=1e-6 tolerance. Narrowed to dlog_p=1e-6, verified numerically to clear tolerance with margin; the block implementations were correct as specified throughout. code/test_nkpc_blocks.py: 11 passed, 0 failed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The previous form asserted log((1+pi_F)/(1+pi_D)) == dlog_p, which holds only to first order; the O(dlog_p^2) truncation is 0.429*dlog_p relative and forced an artificially small dlog_p to pass. Asserting the net-rate split instead is exact and survives changes to omega_pi_D, which is swept later in this plan.
Solved steady state verified byte-identical to the pre-change baseline.
…e gate build_block_list gains firm_profit_D/F, price_nkpc_D/F, terms_of_trade and union_inflation; build_and_solve goes 23x23 -> 27x27 (+mc_D, pi_D, mc_F, pi_F / +nkpc_p_res_D/F, tot_res, union_pi_res). No existing unknown or target renamed or dropped. Gate: as kappa_p -> inf the NKPC forces mu_p*mc -> 1 and labour demand collapses to competitive, so the system must reproduce the pre-change 23x23 IRFs. It does, with textbook O(1/kappa_p) convergence -- worst relative deviation 2.925e-03 at kappa_p=1e4, 2.925e-04 at 1e5 (threshold 1e-3, PASSED), 2.925e-05 at 1e6, with every one of the 30 IRF series shrinking by exactly 10.00x per decade and the SS levels bit-identical. Harness: code/dump_irfs.py. Also fixes an SSJ 1.0.0 defect this system is the first to hit. CombinedBlock._jacobian only visits a block whose inputs intersect the shock list, so a target that is a pure function of the solver's own unknowns never enters H_Z. All four new targets are exactly that, so SSJ handed a 23-row H_Z to a 27x27 H_U and numpy raised a core-dimension mismatch. full_model. solve_jacobian_padded() restores the missing rows as zeros -- exact, not an approximation, since dH/dZ at fixed unknowns is identically zero when the shock does not appear in the equation -- and otherwise mirrors Block.solve_jacobian line-for-line. tpi.py routes through it too, and it prints the padded row names on every solve so the padding cannot go silent. Full code/main.py at the flex limit reproduces every monitored baseline number, including both TPI sanity checks at exactly 0.00e+00. test_nkpc_blocks.py: 11 passed, 0 failed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Task 9 hit a real SSJ 1.0.0 defect. CombinedBlock._jacobian seeds total_Js from the shock list and returns total_Js[original_outputs & total_Js.outputs, :], so a target reachable from no shock is silently dropped from H_Z; solve_jacobian then hands mismatched shapes to np.linalg.solve. All four new targets are pure functions of the solver's own unknowns, so H_Z came back with 23 rows against a 27x27 H_U. Fixed by full_model.solve_jacobian_padded(), which restores the rows as zeros -- exact, since dH/dZ at fixed unknowns is identically zero when the shock never appears in the equation. Records why the proposed 25x25 rewrite does NOT avoid this: nkpc_p_res would still depend only on unknowns, so it would still be dropped, giving 23 rows against 25x25. Same defect, at the cost of rewriting committed work. Adds Task 9b to convert the seven remaining call sites. regime_model.py blocks the E1-E4 cache rebuild and e4_distribution.py blocks E4, so this is not optional cleanup. Gate kappa corrected to 1e5: at 1e4 the worst deviation was 2.925e-03. The deviation falls exactly 10x per decade with an identical 2.925 mantissa at 1e4, 1e5 and 1e6 -- a clean O(1/kappa_p) limit, which is the convergent-limit diagnostic the gate was designed to test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
SSJ 1.0.0 drops H_Z rows for targets reachable from no shock. Seven call sites would have hit the core-dimension mismatch on first contact with the 27x27 system; regime_model.py blocks the E1-E4 cache rebuild.
At the calibrated kappa_p = 0.0871 with deposits still real:
Y_D[0] -0.0149% -> -0.4923% (33x)
C_D[0] +0.2164% -> -0.4904% SIGN FLIP
I_D[0] -0.7718% -> -0.9907%
n_inter_D[0] -3.3804% -> -4.0140%
Steady state byte-identical; both impact signs negative; all four TPI gammas
inside 1e-7. The flex column reproduces STATE.md's own E1 passive row to every
printed digit, which is the check that the SS divisors are right.
Y_D[0] is now in range of Bi-Foerster-Traum's -0.6% without their loan-in-advance
constraint; the pre-change model was two orders of magnitude below it.
Caveat recorded prominently: this is a ONE-QUARTER SPIKE, not a downturn. Output
and consumption are both positive from quarter 1, and flex consumption is more
persistently negative from quarter 2 on. The claim is that sticky prices fix the
impact quarter, not that they resolve the investment-bust counterfactual.
kappa_p sweep {0.03, 0.0871, 0.2} is monotone and stable throughout, so the sign
flip is not knife-edge -- C_D[0] is negative across the whole sticky range.
Also fixes dump_irfs.py to store SS levels for every series with an assertion.
I_D had no ss__ entry, so a consumer fell back to a divisor of 1.0 and reported a
level deviation as a percentage -- the mislabelling class CLAUDE.md records for
n_inter and K.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Splits rdep_D/F's two meanings without a wide rename. New i_dep_D/F is the nominal deposit rate (Task 13 wires it as the solver unknown). rdep_D/F keeps its name and its existing ex-ante meaning -- the real rate for t -> t+1 locked at t -- so intermediation_P1_D/F, divert_bond_foc_D/F and divert_portfolio_adj need zero changes. New rdep_expost_D/F is the realised real rate on deposits placed at t-1, carrying the inflation surprise that drives the Fisher-Bernanke channel (banks hold real assets against nominal liabilities, so deflation deepens the net-worth loss). T-2 not reopened: deposit_return_D/F still locks the rate at i_dep(-1); only the deflator is period-t. At pi=0 all three collapse to i_dep, so the steady state stays bit-identical. code/test_nkpc_blocks.py: 11 -> 15 passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rices Sticky prices (NKPC blocks, Task 9-10) and nominal deposit contracts (Task 11-13) both raised GR-DE spread transmission on a 1pp default shock from the pre-change 150.4bp to 162.0bp at psi_lambda_B=8.5, an 8% overshoot of the paper's 150bp calibration target. Re-bisected psi_lambda_B_D/F holding everything else fixed: 8.5 -> 162.14bp, 7.0 -> 136.21bp, 7.8 -> 149.16bp, 7.85 -> 150.14bp (adopted, within 1bp). b_gov_D[499] stayed in the ~1e-5..1e-4 band throughout, no instability opened up while descending from 8.5. Full-pipeline re-verification: SS residuals byte-identical to every prior task's baseline (the dial only touches dynamics). gamma=0 peak spread = 150.0bp on target; gamma=2/5/10 still monotone declining; n_inter_D[0]/Y_D[0] both negative (correct doom-loop sign). 17/17 block tests still pass. E1-E4 in docs/experiments_results.md are now stale until the regime cache is rebuilt (Task 15). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…model Cache rebuilt (psilam7p85_cal685f7838) BEFORE run_all.py — experiments read cached Jacobians and never re-solve, so the reverse order silently reports the flexible-price model. E1: loading 4.00/3.17 -> 3.82/2.90, still monotone decreasing and above 1 throughout (schedule 4.43 -> 1.49). Live Claims 1 and 5 survive. Y_D[0] and C_D[0] still positive under medium/aggressive. E2: market_clearing_D closes at 2e-16 vs the 1e-07 assertion — nothing leaked into the resource constraint. Headline-vs-channels ordering reverses. E3: full writeoff now inverts Live Claim 1 only at aggressive (0.26); medium holds at 2.46. E4: stale decile cache rebuilt; paper figures and tables re-emitted. run_all.py: two hardcoded flex-price captions in the GENERATED document now compute live — E3's psi_lambda_B was stale, E2's was stating the opposite of the table above it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Task 16 of the nominal-rigidities plan: consolidate the living docs, which
had accumulated one appended section per task across STATE.md, PROGRESS.md
and HANDOFF.md, and correct several places where the prose no longer matched
the numbers.
What the docs now record. The model is sticky-price with nominal deposit
contracts. Rotemberg price Phillips curves in both countries with a markup
wedge in labour demand and a markup rent distributed proportional to
productivity e; wages stay flexible. The nominal side closes with no policy
rate: the monetary-union terms-of-trade identity pins the inflation
differential off the existing unknown p, and a union-inflation normalisation
at omega_pi_D = 0.071 pins the level, putting 93% of any terms-of-trade
adjustment into Greek deflation. Deposits are nominal (i_dep is the solver
unknown, rdep stays ex-ante, rdep_expost carries the inflation surprise into
bank_return and capital_fund); sovereign bonds stay real, a deliberate
asymmetry that maximises banks' Fisher exposure and is now flagged in SPEC.md
as something the paper must state. Solver system 23x23 -> 27x27. The steady
state is bit-identical to pre-change, because markups are subsidy-neutralised
and pi_ss = 0.
Headline numbers, impact on a 1pp default shock, % of own SS, both columns on
the same 150bp peak-spread moment:
flex/real deposits sticky+nominal (psi_lambda_B 8.5 -> 7.85)
peak spread 150.4 bp 150.0 bp
Y_D[0] -0.0149 -0.5064
C_D[0] +0.2164 -0.5103
I_D[0] -0.7718 -1.0114
n_inter_D[0] -3.3804 -4.2962
Live Claim 5 (self-extinguishing premium) survives: the loading is monotone
decreasing, 4.43 -> 1.49 over 59 grid points on gamma in [0.51, 30.00], above
1 throughout. Live Claim 1 (over-compensation) survives at 3.82 / 2.90. E2's
identity closes at 3.5e-17 against its 1e-07 assertion.
Caveat recorded prominently and not to be dropped: this is a one-quarter
spike, not a downturn. Output and consumption are both positive from quarter 1
and flexible-price consumption is more persistently negative from quarter 2
on. The honest claim is that the model fixes the impact quarter, not that it
resolves the investment-bust counterfactual.
New watch item: n_inter_D[0] is now positive (+0.924) under the aggressive
backstop, where it was -1.099, so with Y_D[0] = +0.8721 and C_D[0] = +1.5143
the aggressive regime produces an impact boom rather than a cushioned bust.
Also documented as a hard requirement: full_model.solve_jacobian_padded() and
the SSJ 1.0.0 defect it works around, with the grep invariant that must stay
empty.
Three prose corrections. SPEC.md's E2 caution now rests on the channels
cancelling rather than on the headline being the smaller object (the magnitude
ordering reversed: largest channel is 0.25x the headline, was ~4x). CLAUDE.md's
S-1 appendix framing now says full writeoff inverts Live Claim 1 only at
aggressive (0.26), with medium holding at 2.46. CLAUDE.md now states that E4 is
not wired into experiments/run_all.py.
Further stale prose found and either fixed or logged: SPEC.md's calibration
strategy and Live Claims 1 and 5 quoted a superseded psi_lambda_B and loading;
HANDOFF.md's E1/E2/E3/E4 headlines and its 2026-07-31 state blockquote were
flex-price. All corrected. Not fixed, and logged as a blocking open item:
experiments/paper_outputs.py's CAPTIONS dict is stale and in two places
inverted -- fig08_deciles contradicts Table 4 of the document it generates --
which needs a code change plus a figure regeneration and two re-derived
numbers.
Tests: 40 passed (code/test_nkpc_blocks.py 17, code/test_eba_calibration.py
10, experiments/ 13). Grep invariant holds. No code changed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
experiments/paper_outputs.py carried a module-level CAPTIONS dict of literal
prose written against the flexible-price model. The sticky-price conversion and
the psi_lambda_B 8.5 -> 7.85 re-tune left every caption stale and three of them
inverted. Because captions are baked into the PNGs, the repo was shipping eight
tracked figures and a generated docs/paper_draft_results.md whose prose
contradicted its own tables — fig08_deciles claimed the lowest income quintile
"gains 0.95%" and the highest loses 0.59%, against a Table 4 in the same file
reading +0.4250 and -0.9073. Same hazard Task 15 fixed inside run_all.py.
CAPTIONS is now empty at import and filled at run time. save() takes the caption
as a required argument; each figure builds it from the arrays it just plotted via
a _caption_figNN helper. Directional claims are selected from the data by
_monotone, _first_quarter and sign tests, so a sign flip rewrites the sentence
instead of lying inside it: fig02 will print "does NOT fall — the
self-extinguishing-premium claim fails at this calibration" if the loading
schedule stops declining, and fig05 will refuse the German-ledger reading if
exposure and loading stop opposing. main() gained a prose-vs-table assertion:
fig01's caption and Table 3 must agree on impact bank net worth via their two
independent routes (the cache directly vs e1.run()'s payload).
Corrections, all now derived:
fig01 net worth 3.4% / investment -0.77% -> -4.3% / -1.0%, plus the
reversal quarters (net worth q5, spread q8)
fig02 4.5x ... 2.1x -> 4.43x at gamma=0.51 -> 1.49x at gamma=30,
monotone, above 1 throughout
fig03 INVERTED: "each roughly four times the headline" -> consumption
carries 0.99x the headline, investment +0.25x, net exports -0.21x
fig04 3% / 97% -> 3.4% / 96.6%, re-derived (below)
fig05 qualitative -> exposure 0 -> 0.92% of quarterly Y_D, loading
4.43x -> 1.49x
fig06 INVERTED: net path smaller than its components "at every horizon" is
false in the impact quarter; true in 14 of the first 16
fig07 hardcoded 23/52/25 -> read from the npz (22.9/52.4/24.7); hawk span
2010-2014 derived. Estimated from market data, so model-independent —
confirmed rather than assumed.
fig08 INVERTED: Q1 +0.4250 / Q5 -0.9073, backstop gain +2.01 / +1.34;
consumption FALLS ~0.51% in every quintile on impact, and the minimum
over 40 quarters is the impact quarter, not quarter five.
fig04 re-derivation. Default loading per unit of default probability is
EL_price_D + psi_spread_D (bond-pricing FOC, equations_D.py:566).
EL_price_D = (1-0.30)*0.0777006/0.968941 = 0.056134 and does not depend on
psi_lambda_B. psi_spread_D = lambda_gk_D*psi_lambda_B_D/(beta_inter_D*Omega_D)
(steady_state.py:104) is LINEAR in psi_lambda_B, so 8.5 -> 7.85 took it
1.737724 -> 1.604839. Split 0.056134/1.660973 = 3.4% fundamental expected loss,
96.6% collateral-friction wedge (was 3.1%/96.9% at 8.5).
No model, calibration or equation change: the result tables are numerically
identical to the pre-fix run. 8 figures and docs/paper_draft_results.md
regenerated; every caption cross-checked against its table, no contradictions
remain. Tests: 40 passed. Removes the standing "do not quote any figure caption"
warning from STATE.md and HANDOFF.md.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
capital_adj_D/F gain S(x) = (omega_I/2)(x-1)^2 with x = I/I(-1); effective
investment (1-S)*I drives the installation technology, and the FOC picks up
the intertemporal term. S(1) = S'(1) = 0 makes this exactly SS-neutral, and
omega_I is calibrated to 0 so the committed model is provably unchanged:
the equivalence gate reproduces the pre-change IRFs to 1.08e-13 across all
45 dumped arrays.
Discounted at beta, not SDF. That is first-order exact here (S'(1) = 0, so
only SDF_ss = beta survives linearisation -- the same argument price_nkpc_D
already uses) and it is required, since taking SDF_D makes SSJ's topological
sort fail: hh_D -> capital_fund_D -> capital_adj_D -> sdf_D -> ghh_composite_D.
Sweeping omega_I in {0,2,5,10} does NOT fix the one-quarter-contraction
problem (issue I-1): it shrinks the contraction toward zero rather than
lengthening it, and at omega_I >= 5 Y_D[0] turns positive. Same failure mode
as the earlier rejected chi1 diagnostic -- both reallocate the impact between
I and C without deepening it. Recorded in STATE.md as a negative result.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…da_B rho_def and rho_Z were hardcoded in code/full_model.py. Both are now calibration entries (with the old literals as a fallback), and rho_def is set to 0.9408 -- the quarterly equivalent (0.9798499^3) of the crisis-state monthly persistence in Empirics/outputs/ms_regime_GRC.npz. The old 0.80 implied a 14-month crisis against an estimated 50-month expected duration and a realised 92-month episode. rho_Z is deliberately left at 0.80. psi_lambda_B re-tuned 7.85 -> 2.92 to hold the 150bp GR-DE peak-spread moment (470.62bp at the old value). Full pipeline re-solve per bisection point; the patch-the-SS shortcut is wrong and is documented as such. Consequences: cumulative 40q Y goes -0.049 -> -2.542, negative-Y quarters 5 -> 37, all four impact signs still negative. The default-loading split moves 3.4%/96.6% -> 8.6%/91.4% fundamental/friction, which weakens the paper's constrained-seller claim quantitatively. SS bit-identical; b_gov_D[499] falls 4.63e-05 -> 2.04e-05; TPI loading 5.55/5.37/5.13 still monotone and above 1. E1-E4 and the paper artefacts are now stale and must be regenerated. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Cache, E1-E4 and the eight paper figures rebuilt at rho_def=0.9408, psi_lambda_B=2.92. E2 closes at 1.1e-16 against its 1e-7 assertion. fig07_ms_regimes correctly unchanged -- it is estimated from market data. LIVE CLAIM 5 IS MATERIALLY WEAKER. The loading schedule over gamma in [0.51, 30] was 4.43 -> 1.49 (a 66% decline, approaching the fair-value floor of 1); it is now 5.65 -> 4.59 (19%). Monotone decreasing still holds, so the claim survives literally, but the economic punchline -- that the premium extinguishes itself as the backstop grows -- does not: it bottoms at 4.6x over-compensation. A longer crisis accumulates premium over more quarters than expected loss, so the ratio stays high even under heavy intervention. Live Claim 1 is correspondingly STRONGER: the floor rises 1.49 -> 4.59, so over-compensation no longer depends on where on the schedule you look. E1 also shifts: investment is negative even under the aggressive backstop (-0.0508, was +0.3977) and the aggressive net-worth boom is milder (+0.469, was +0.924) with a larger consumption effect (+2.24, was +1.51). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
Nominal rigidities. Prices are now sticky in both countries, and deposits are nominal contracts. Previously the model was fully real — no inflation, no nominal side at all.
Because Greece and Germany share a currency, the terms of trade is the inflation differential, so the nominal side closes without needing an ECB policy rate: a Greek downturn shows up as Greek deflation (about 93% of the adjustment, on the ECB capital key) rather than German inflation. That matches the internal-devaluation pattern of 2010–14.
Banks now hold real assets against nominal deposits, so that deflation raises the real value of what they owe and deepens the hit to bank capital — a debt-deflation channel the real model had no way to express.
A crisis that lasts. The sovereign-risk shock's persistence was a hardcoded
0.80, implying a 14-month crisis. Our own Markov-switching estimation says otherwise: the Greek crisis state has a 50-month expected duration and the realised episode ran 2010-04 to 2017-12 (92 months), giving a quarterly persistence of 0.94. That parameter is now disciplined by the regime estimate rather than picked by hand, and the amplification dial was re-tuned alongside it so the model still hits its 150bp spread target.Where the model now stands
Three pillars, all up for review together:
Where to find each: the EBA and regime work went onto
mainahead of this branch, so the diff here is the nominal-rigidities and shock-persistence layer on top. The results below are the combined model.Outcomes
Consumption now falls, and the crisis now lasts
The model previously said a sovereign crisis raised Greek consumption on impact, and that output recovered within a quarter. Both are fixed.
Investment is now negative in all 40 quarters and the spread stays above half its peak until quarter 11. Both columns hit the same 150bp Greece–Germany spread target, so this is like-for-like.
Two capital-adjustment frictions were tried first and both rejected — they only shuffled the impact between investment and consumption. The persistence of the shock was the binding constraint, and it was an empirical question we already had the data to answer.
Distributional incidence is progressive
The crisis costs the top income quintile 1.12% of its own consumption while the bottom quintile gains 0.64%, monotone across the distribution. The backstop's protection runs the same way — worth +5.4% to the poorest against +3.5% to the richest.
The backstop results — one of them needs a decision
The one thing I'd most like your view on
The self-extinguishing premium claim has weakened, and I don't think I should decide alone how to write it.
The loading schedule — the ECB's compensation per unit of expected loss — still declines monotonically in the size of the backstop, and is still comfortably above 1. But its shape has changed:
The over-compensation result is now stronger — the floor rises from 1.49 to 4.59, so it no longer depends on where on the schedule you look. But the self-extinguishing story was compelling because the loading fell toward 1: intervene hard enough and the ECB earns roughly the actuarially fair expected loss. It no longer approaches that. A longer crisis accumulates premium over more quarters than it accumulates expected loss, so the ratio stays high even under heavy intervention.
Three readings seem open, and it's your call which the paper takes:
p³mapping is a first pass, not an identity.Two smaller flags
psi_lambda_Bsweeps in the repo may be unreliable. A shortcut that patches the parameter onto a solved steady state misses one of the two places it enters, and was off by 10bp where it was checked. The adopted values were re-derived without it; the brackets around them were not.All figures and results are regenerated on the current model, and the steady state is unchanged throughout, so the calibration targets you've already signed off on still hold.
🤖 Generated with Claude Code