Liquidation cascades in DeFi happen in minutes. When collateral prices fall, undercollateralized positions trigger liquidations, which sell collateral into the market, driving prices further down and triggering more liquidations. Detecting the cascade as it begins—not after it ends—requires sub-second streaming analytics.
Why Liquidation Cascade Detection Matters
The March 2020 "Black Thursday" event on MakerDAO saw ETH price drop 50% in hours, triggering cascading liquidations that exhausted the protocol's auction system. More recently, the collapse of LUNA/UST in May 2022 triggered cascading liquidations across Aave, Compound, and dozens of smaller protocols simultaneously.
A liquidation cascade forms when several conditions align:
- Cascade trigger: a sharp asset price decline brings multiple positions to their liquidation threshold simultaneously.
- Collateral contagion: the liquidated collateral (e.g., ETH) is sold on-chain, increasing sell pressure and pushing prices further down.
- Cross-protocol stress: the same collateral asset is used across multiple protocols; a price drop affects health factors everywhere simultaneously.
- Liquidation queue depth: if liquidatable positions exceed what keepers can process, the protocol accumulates bad debt.
Early detection—ideally when positions approach 90% of the liquidation threshold, before they cross it—allows risk managers to circuit-break, protocols to pause borrowing, and users to add collateral.
Key metrics for cascade detection:
- At-risk TVL: total value of positions within 5–10% of liquidation threshold.
- Liquidation queue depth: number of positions currently below health factor 1.0.
- Cascade trigger price: the ETH/BTC price at which the next 10%/25%/50% of collateral becomes liquidatable.
- Protocol stress score: a weighted sum of at-risk TVL as a percentage of total TVL.
How Streaming SQL Solves This
RisingWave maintains continuously updated materialized views of protocol health factors, at-risk TVL, and cascade trigger prices. As oracle prices update every block, the health factor calculations update automatically. When a cascade begins, the liquidation_queue view materializes within seconds—long before a batch system would complete its next cycle.
Building It Step by Step
Step 1: Connect the Data Source
CREATE SOURCE lending_positions (
position_id VARCHAR,
protocol_id VARCHAR,
user_address VARCHAR,
collateral_token VARCHAR,
collateral_amount NUMERIC,
collateral_usd NUMERIC,
debt_token VARCHAR,
debt_amount NUMERIC,
debt_usd NUMERIC,
health_factor NUMERIC, -- 1.0 = at liquidation threshold
liquidation_threshold NUMERIC, -- e.g. 0.825 for Aave ETH
block_ts TIMESTAMPTZ
) WITH (
connector = 'kafka',
topic = 'lending.position.updates',
properties.bootstrap.server = 'kafka:9092'
) FORMAT PLAIN ENCODE JSON;
CREATE SOURCE liquidation_events (
tx_hash VARCHAR,
protocol_id VARCHAR,
liquidator VARCHAR,
borrower VARCHAR,
collateral_token VARCHAR,
collateral_seized_usd NUMERIC,
debt_repaid_usd NUMERIC,
liquidation_bonus_pct NUMERIC,
block_ts TIMESTAMPTZ
) WITH (
connector = 'kafka',
topic = 'lending.liquidation.events',
properties.bootstrap.server = 'kafka:9092'
) FORMAT PLAIN ENCODE JSON;
Step 2: Build the Core Materialized View
Segment positions by health factor proximity to liquidation:
CREATE MATERIALIZED VIEW position_risk_tiers AS
SELECT
protocol_id,
collateral_token,
COUNT(*) AS position_count,
SUM(collateral_usd) AS total_collateral_usd,
SUM(debt_usd) AS total_debt_usd,
-- At-risk buckets by health factor
SUM(CASE WHEN health_factor < 1.0 THEN collateral_usd ELSE 0 END)
AS liquidatable_usd,
SUM(CASE WHEN health_factor BETWEEN 1.0 AND 1.05 THEN collateral_usd ELSE 0 END)
AS critical_risk_usd,
SUM(CASE WHEN health_factor BETWEEN 1.05 AND 1.15 THEN collateral_usd ELSE 0 END)
AS high_risk_usd,
SUM(CASE WHEN health_factor BETWEEN 1.15 AND 1.25 THEN collateral_usd ELSE 0 END)
AS moderate_risk_usd,
-- Cascade stress: at-risk as % of total
SUM(CASE WHEN health_factor < 1.15 THEN collateral_usd ELSE 0 END)
/ NULLIF(SUM(collateral_usd), 0) * 100 AS at_risk_pct
FROM lending_positions
WHERE block_ts > NOW() - INTERVAL '2 minutes'
GROUP BY protocol_id, collateral_token;
CREATE MATERIALIZED VIEW liquidation_rate AS
SELECT
protocol_id,
collateral_token,
COUNT(*) AS liquidation_count_1h,
SUM(collateral_seized_usd) AS seized_usd_1h,
SUM(debt_repaid_usd) AS debt_cleared_usd_1h,
AVG(liquidation_bonus_pct) AS avg_bonus_pct
FROM liquidation_events
WHERE block_ts > NOW() - INTERVAL '1 hour'
GROUP BY protocol_id, collateral_token;
Step 3: Detection Logic and Alerts
Emit cascade warnings when at-risk TVL crosses danger thresholds:
CREATE MATERIALIZED VIEW cascade_alerts AS
SELECT
prt.protocol_id,
prt.collateral_token,
prt.liquidatable_usd,
prt.critical_risk_usd,
prt.high_risk_usd,
prt.at_risk_pct,
lr.liquidation_count_1h,
lr.seized_usd_1h,
CASE
WHEN prt.liquidatable_usd > 10000000 AND lr.liquidation_count_1h > 50
THEN 'CASCADE_IN_PROGRESS'
WHEN prt.critical_risk_usd > 50000000
THEN 'CASCADE_IMMINENT'
WHEN prt.at_risk_pct > 20
THEN 'ELEVATED_SYSTEMIC_RISK'
WHEN prt.at_risk_pct > 10
THEN 'MODERATE_RISK'
ELSE
'NORMAL'
END AS risk_status,
NOW() AS evaluated_at
FROM position_risk_tiers prt
LEFT JOIN liquidation_rate lr
ON prt.protocol_id = lr.protocol_id
AND prt.collateral_token = lr.collateral_token
WHERE prt.at_risk_pct > 5
OR prt.liquidatable_usd > 1000000;
CREATE SINK cascade_alert_sink AS
SELECT * FROM cascade_alerts
WHERE risk_status IN ('CASCADE_IN_PROGRESS', 'CASCADE_IMMINENT', 'ELEVATED_SYSTEMIC_RISK')
WITH (
connector = 'kafka',
topic = 'alerts.defi.liquidation-cascade',
properties.bootstrap.server = 'kafka:9092'
) FORMAT PLAIN ENCODE JSON;
Step 4: Querying Live Results
View the current systemic risk snapshot across all monitored protocols:
SELECT
protocol_id,
collateral_token,
ROUND(liquidatable_usd / 1e6, 2) AS liquidatable_m_usd,
ROUND(critical_risk_usd / 1e6, 2) AS critical_risk_m_usd,
ROUND(at_risk_pct, 2) AS at_risk_pct,
liquidation_count_1h,
ROUND(seized_usd_1h / 1e6, 2) AS seized_1h_m_usd,
risk_status,
evaluated_at
FROM cascade_alerts
ORDER BY at_risk_pct DESC
LIMIT 10;
Comparison Table
| Detection Method | Latency | Cascade Stage Detected | Protocol Coverage |
| Protocol health endpoint polling | 1–5 min | Mid-cascade | Per-protocol |
| Liquidation event monitoring | Per-block | Already happening | Per-protocol |
| Streaming SQL (health factor view) | < 1s | Pre-cascade (HF approaching 1.0) | Cross-protocol |
| Batch warehouse query | 15–60 min | Post-cascade | Historical only |
FAQ
How do you compute the cascade trigger price—the asset price that would cause a wave of liquidations?
You need the current collateral amount and liquidation threshold per position. The liquidation trigger price per position is debt_usd / (collateral_amount * liquidation_threshold). Group these prices into buckets and sum the collateral at risk per price bucket to build a waterfall chart. This can be computed as a materialized view directly from lending_positions, giving you a live view of where liquidation cliffs exist in the market.
How does collateral contagion propagate in this model?
Contagion is indirect: liquidated collateral is sold on-chain, increasing sell pressure and reducing the oracle price for that asset. This causes the health factors of other positions using the same collateral to deteriorate further. To model contagion, monitor the liquidation_rate view alongside the oracle price feed. When seized_usd_1h is high and the oracle price is falling simultaneously, contagion is underway.
Can this system monitor cross-protocol risk across Aave, Compound, and Euler simultaneously?
Yes. The protocol_id field captures which protocol each position belongs to. The position_risk_tiers view groups by both protocol_id and collateral_token, giving you a per-protocol, per-asset breakdown. A cross-protocol aggregate view simply drops the protocol_id grouping dimension to show total at-risk TVL for a given collateral asset regardless of which protocol holds it.
Key Takeaways
- Liquidation cascades develop in minutes; detecting positions approaching the health factor threshold before they cross it allows preventative action.
- RisingWave maintains health factor segmentation (liquidatable, critical, high, moderate risk) as a continuously updated materialized view that refreshes with every oracle price update and position change.
- Cross-protocol cascade risk is visible in a single streaming SQL query, eliminating the need to poll individual protocol APIs.
- Cascade alerts differentiate between normal liquidation activity, elevated systemic risk, and an active cascade in progress—giving risk managers proportional response options.

