Live: https://spine.rahilbhavan.com
Spine is a risk dashboard and liquidation backtest for the nine Morpho Blue markets on Base that fund the USDC loans in Coinbase's app. Python fetchers read positions, liquidations, Chainlink oracle updates, Coinbase 5-minute candles and CEX and DEX depth from public endpoints. The pipeline writes data/summary.json for a static dashboard. A numpy backtest replays rebuilt historical loan books through seven crash paths and recommends a liquidation LTV (LLTV) and maximum draw for each collateral.
The first screen summarizes observed borrowing, the selected market’s modeled capacity threshold, and its worst replay loss, with trough exposure shown separately. Daily comparisons use a saved snapshot near 24 hours before the displayed book and print both timestamps; stale or missing data is labeled explicitly. One market selection carries through a sortable/filterable nine-market watchlist, an exact-sample price-shock inspector, positions, liquidation charts, crash replays and supplier exposure. The URL preserves the investigation state and supports back/forward navigation. System/light/dark theme persists locally, and the view can be copied as a link.
Stress results identify their saved book, assumptions and held-out calibration limits. The UI improvement loop requires working charts, screenshots and a recorded reader check before accepting a candidate. Workstation implementation and verification record. The blue graph refinement adds stable matrix scales, phone-readable labels, explicit log views, source summaries and color-vision proof captures.
The headline: on a March 2020 path, losses depend on how much capital liquidators can deploy in a day. Cutting the haircut or the LLTV changes little.
- Book. $1.63B borrowed against $3.66B of collateral across 68,772 positions in nine markets. Coinbase Smart Wallets hold 97.5% of the borrowing. Source:
data/summary.json, snapshot 2026-09-23 19:05 UTC. The live site shows current figures. - Track record. The cbBTC market cleared $256M of liquidations in the four stress weeks of three events (Oct 2025, Feb 2026, Jun 2026) with zero bad debt. Source:
WRITEUP.md. - Calibration. The borrower-response parameters (70% responsive, two-hour reaction) were fitted on three cbBTC events, where the model reproduces realized liquidation volume at 0.85–0.99x (Oct 2025 0.95x, Feb 2026 0.99x, Jun 2026 0.85x). On five more cbBTC events chosen by a fixed rule (every UTC day with over $5M repaid, since launch) and kept out of the fit, it gives 0.42x to 1.08x (Oct 2025a 0.52x, Nov 2025 1.02x, Nov 2025a 1.08x, Jan 2026 0.77x, Jun 2026b 0.42x). Across all eight events the range is 0.42–1.08x, so the model is not within 15% across events. Held-out WETH events come in at 1.58x and 0.85x. Source:
data/backtest.json, keycalibrated.ratios; method in WRITEUP.md section 3. - Stress replay. 5,562 saved backtest runs over seven crash windows at 5-minute resolution. Source:
data/backtest.jsonanddata/prices/index.json. - Replay book. The replay ran on the cbBTC/USDC market's loan book as of 2026-09-19, the date of
WRITEUP.md(latest_bookindata/backtest.json). All percentages of supply below are of that book's $1.58B of USDC supplied. The Book figures above are a different, later snapshot (2026-09-23, all nine markets). - March 2020 at today's 86% LLTV, loss by end of path. $9.2M (0.6% of supply) realized, because BTC bounced and the queue was served above water on the way back up. Source:
WRITEUP.md. - March 2020 at today's 86% LLTV, supply left unserved at the trough. $158.4M (10.0% of supply) sat underwater with no liquidator able to act at the lowest print. This is a separate measure, taken at a different moment. A 77% LLTV leaves 8.4% unserved. Tripled liquidator capital leaves 6.2%. Source:
WRITEUP.md. - March 2020 with a Coinbase backstop liquidator. $0 left unserved and $13.1M (0.8%) realized loss. Source:
WRITEUP.md.
Recommended parameters, from WRITEUP.md:
| Collateral | Today (LLTV / max draw) | Recommended | Why |
|---|---|---|---|
| cbBTC | 86% / 75% | A committed backstop liquidator first, then 80% / 66–70% | Liquidator capital binds on the March 2020 path. At 80% every other path is clean. |
| WETH | 86% / 75% | 77% / 70% | ETH gaps more than the 4.38% liquidation bonus inside five minutes. At 86%, three of seven paths leave bad debt. |
| cbXRP, SOL, cbDOGE, cbADA, cbLTC, JitoSOL | 62.5% / 55% | 62.5%, max draw 47%, per-asset book cap | No on-chain venue on Base, so every seized unit clears through Coinbase's own order book. |
WRITEUP.md has the full argument. Its changelog shows how the headline moved as the model was corrected.
- Fetchers in
spine/(fetch_positions,fetch_liquidations,tag_coinbase,fetch_prices,fetch_depth,fetch_vaults,fetch_markets,fetch_oracle) read the Morpho API, Base RPC, Coinbase Exchange and Chainlink logs. They use only the standard library. spine/summarize.pywritesdata/summary.json.site/index.html,site/charts.jsandsite/app.jsrender it with no build step. The page's loan check reads one address's positions from Morpho's public API in the visitor's browser.spine/calibrate.pymeasures liquidator latency, bonus and throughput on the real events.spine/rebuild_book.pyrebuilds historical books from on-chain events.spine/backtest.py(numpy) replays books through the crash paths with fitted borrower response and liquidator capacity.spine/writeup_tables.pyprints the tables inWRITEUP.md.spine/stress_grid.pywrites the crash path x LLTV x liquidator capital grid todata/stress_grid.csv..github/workflows/refresh.ymlis scheduled hourly (GitHub runs it best-effort). It refreshes the data, advances the backtest, publishes to thedatabranch and GitHub Pages, and runsspine/alert.py. The alert opens a GitHub issue when any market is within 10% of its liquidation capacity. Vercel serves thedatabranch at the live URL.
CODEBASE_GUIDE.md explains the architecture module by module.
Prerequisites: Python 3.12 or later and uv. No API keys. Every endpoint is public.
Install and test:
uv sync --extra dev
.venv/bin/python -m pytest -q testsObserved on 2026-10-25: 26 passed. uv.lock pins the dependencies. The tests run against the frozen snapshot in data/, with no network.
Rebuild the dashboard data. The positions files are not committed, so run fetch_positions first on a fresh clone. These commands overwrite files in data/.
.venv/bin/python -m spine.fetch_positions # all positions, 9 markets (3 min 43 s on 2026-10-21)
.venv/bin/python -m spine.fetch_liquidations # every liquidation (incremental after the first run)
.venv/bin/python -m spine.tag_coinbase # Coinbase Smart Wallet tags via Multicall3
.venv/bin/python -m spine.fetch_prices # Coinbase 5-minute candles for 7 crash windows (cached)
.venv/bin/python -m spine.fetch_depth # CEX L2 depth and DEX quoter capacity
.venv/bin/python -m spine.fetch_vaults # vaults that fund each market
.venv/bin/python -m spine.fetch_markets # every Base market over $10M borrowed (market level)
.venv/bin/python -m spine.summarize # writes data/summary.json
.venv/bin/python -m http.server 8000 # local copy of https://spine.rahilbhavan.com/site/ on port 8000Run the stress-test pipeline. It is slow. fetch_oracle, calibrate, rebuild_book and backtest take --max-seconds N, save progress to data/cache/ and resume on the next run.
.venv/bin/python -m spine.fetch_oracle # Chainlink AnswerUpdated paths for the real events
.venv/bin/python -m spine.calibrate # realized latency, bonus and throughput
.venv/bin/python -m spine.rebuild_book cbBTC 2026-02-03 2026-06-01 2025-10-09
.venv/bin/python -m spine.backtest calib
.venv/bin/python -m spine.backtest grid --market cbBTCspine/: fetchers, summarizer, calibration, book rebuild, backtest, alert and writeup renderer.tests/: pytest checks for the summarizer, backtest, alert and committed data.data/: one frozen snapshot of the generated data, so a fresh clone renders and tests pass. Thedatabranch holds the latest.site/: the static dashboard andwriteup.html, rendered fromWRITEUP.md.research/: source sweeps, the self-liquidation check, outreach drafts andcrash_table.py.docs/: the dashboard screenshot and the social card..github/workflows/:ci.yml(tests),refresh.yml(hourly data) andlinks.yml(weekly link check).
- WRITEUP.md: the long-form argument for each haircut, with every table and its changelog.
- CODEBASE_GUIDE.md: architecture, design choices, a module tour and a reading plan.
- PLAN.md: the original specification and recorded decisions, from 2026-09-15.
- ROADMAP.md: later corrections and unfinished work.
- CONTRIBUTING.md: the code, data and review rules the repository follows.
- CHANGELOG.md: release notes.
- research/: sources for markets, crashes, depth and prior art.
- docs/screenshot.png: the dashboard on 2026-09-23.
- Morpho Blue API (
blue-api.morpho.org) and Base RPC: positions, liquidations, vaults and Coinbase Smart Wallet tags. - Chainlink
AnswerUpdatedlogs on Base: oracle paths for the real stress events. - Coinbase Exchange: 5-minute candles for seven crash windows from March 2020 to June 2026, and L2 order-book depth.
- Aerodrome and Uniswap quoters on Base: on-chain liquidation capacity.
main holds one frozen snapshot from 2026-09-23. The hourly job replaces the data branch with a single new commit each run. Every table in WRITEUP.md is printed by a script from the files in data/. If you reuse the calibration numbers, cite the writeup by date.
- Independent analysis from public on-chain data. Not affiliated with or endorsed by Coinbase or Morpho.
- Research, not risk advice. The recommendations are model outputs.
- The borrower-response fit has two parameters on three events.
WRITEUP.mdsays this deserves modest confidence. - Historical order-book depth for the crash windows is the least-known input.
- The busiest liquidation day was not a binding day, so it sets a floor on liquidator capacity, not an estimate.
- Numbers in this README come from the 2026-09-23 snapshot. A fresh fetch gives different live figures.
MIT. See LICENSE. The license covers this project's code, not third-party data from Morpho, Chainlink or Coinbase.
Part of Rahil Bhavan's projects.
