How to Analyze Crypto Liquidations and Detect Cascades with Python

Learn how to analyze liquidation events from futures markets, find the biggest liquidation events, and measure their price impact using Python and CryptoHFTData.

By CryptoHFTData Team
12 min read
#liquidations#futures#cascade#python#tutorial

Liquidations are one of the most important events in crypto derivatives markets. When leveraged positions get forcibly closed, they create selling (or buying) pressure that can cascade into dramatic price moves. Understanding liquidation data gives you insight into market stress and potential reversal points.

In this tutorial, we'll analyze real liquidation data from Binance Futures to:

  • Find the biggest liquidation events (top 10 long and short windows)
  • Measure price impact during and after major liquidations
  • Compare liquidation patterns during different market conditions

What Are Liquidations?

When a trader opens a leveraged position on a futures exchange, they must maintain a minimum margin. If the position moves against them and their margin drops below the maintenance requirement, the exchange forcibly closes their position—this is a liquidation.

  • Long liquidation: A long position is force-closed by selling. This creates downward price pressure.
  • Short liquidation: A short position is force-closed by buying. This creates upward price pressure.

Liquidations tend to cluster because they're self-reinforcing: a price drop causes long liquidations, which creates more selling pressure, which causes more long liquidations. Finding these clusters reveals the most significant market stress events.

Loading Liquidation Data

Let's start by loading liquidation data from CryptoHFTData:

import cryptohftdata as chd
import pandas as pd

# Configure API
chd.configure_client(api_key="your-api-key-here")

# Load liquidation data (2 weeks)
liqs_df = chd.get_liquidations(
    symbol="BTCUSDT",
    exchange="binance_futures",
    start_date="2025-08-01",
    end_date="2025-08-14"
)

# Preprocess
liqs_df["datetime"] = pd.to_datetime(liqs_df["event_time"], unit="ms")
liqs_df["price"] = pd.to_numeric(liqs_df["average_price"], errors="coerce")
liqs_df["quantity"] = pd.to_numeric(liqs_df["filled_quantity"], errors="coerce")
liqs_df["volume_usd"] = liqs_df["quantity"] * liqs_df["price"]

# Classify liquidation type (SELL = long position liquidated, BUY = short position liquidated)
liqs_df["liq_type"] = liqs_df["side"].apply(
    lambda x: "long" if x == "SELL" else "short"
)

The data includes the execution price, quantity, timestamp, and which side was liquidated.

Data Overview

Over two weeks (August 1-14, 2025), BTCUSDT on Binance Futures saw:

MetricValue
Total liquidations15,108
Total volume$162.3M
Long liquidations (longs rekt)$78.7M (48.5%)
Short liquidations (shorts rekt)$83.6M (51.5%)

The liquidation volume was nearly balanced between longs and shorts over the two-week period—quite different from looking at a single day where one side often dominates.

Finding the Top Liquidation Windows

Instead of using statistical thresholds, we take a simpler approach: aggregate liquidations into 5-minute windows and find the top 10 windows for each side. This directly identifies the biggest liquidation events.

def aggregate_by_window(liqs_df, window="5min"):
    """Aggregate liquidations into time windows."""
    df = liqs_df.copy().set_index("datetime")

    # Total aggregation
    agg = df.resample(window).agg({
        "volume_usd": "sum",
        "quantity": "sum",
        "price": "mean",
        "side": "count"
    }).rename(columns={"side": "count", "price": "avg_price"})

    # By side
    long_agg = df[df["liq_type"] == "long"].resample(window)["volume_usd"].sum()
    short_agg = df[df["liq_type"] == "short"].resample(window)["volume_usd"].sum()

    agg["long_volume"] = long_agg.reindex(agg.index, fill_value=0)
    agg["short_volume"] = short_agg.reindex(agg.index, fill_value=0)

    return agg.reset_index()


def find_top_liquidation_windows(agg_df, n=10):
    """Find the top N windows for long and short liquidations."""
    top_long = agg_df.nlargest(n, "long_volume")
    top_short = agg_df.nlargest(n, "short_volume")
    return top_long, top_short

Top 10 Long Liquidation Windows (Longs Getting Rekt)

TimeVolumeCountAvg Price
2025-08-01 13:50$3,194,56457$114,262
2025-08-11 18:00$2,589,48528$119,334
2025-08-14 12:30$2,405,641142$119,824
2025-08-01 00:50$2,086,45785$114,807
2025-08-11 11:15$2,078,14831$120,764
2025-08-14 06:20$1,712,85120$121,395
2025-08-14 06:00$1,463,44060$121,871
2025-08-01 19:20$1,412,18056$113,387
2025-08-02 18:55$1,098,39032$112,156
2025-08-05 12:35$941,58254$113,924

Top 10 Short Liquidation Windows (Shorts Getting Rekt)

TimeVolumeCountAvg Price
2025-08-13 11:10$6,638,9136$120,671
2025-08-11 02:50$1,971,52512$121,813
2025-08-06 04:05$1,142,3999$113,404
2025-08-14 00:20$1,084,80212$124,013
2025-08-03 19:35$908,32418$114,655
2025-08-11 02:35$899,50110$121,683
2025-08-11 09:35$885,9376$121,171
2025-08-13 13:50$874,80889$121,583
2025-08-04 17:15$830,27712$115,660
2025-08-11 03:00$823,8518$121,923

The $6.6M short liquidation window on August 13 was the largest single event—shorts got squeezed hard as BTC pumped through $120k. Notice how short liquidation windows tend to have fewer individual liquidations but larger average sizes.

BTCUSDT price with top liquidation events marked showing major liquidation windows highlighted on the price chart

The chart shows price with the top 5 long (red) and short (green) liquidation events marked. You can see how these cluster around sharp price movements.

Price Impact Analysis

The key question: what happens to price during and after these major liquidation events? We measure price change during the 5-minute window, then 5 minutes, 15 minutes, and 1 hour after.

def calculate_price_impact(events_df, price_df, window_minutes=5):
    """Calculate price movement during and after each event window."""
    results = []

    for _, event in events_df.iterrows():
        event_time = event["datetime"]
        event_end = event_time + pd.Timedelta(minutes=window_minutes)

        # Price at window start
        pre = price_df[price_df["datetime"] <= event_time]
        price_start = pre.iloc[-1]["close"]

        # Price at window end
        post = price_df[price_df["datetime"] >= event_end]
        price_end = post.iloc[0]["close"]

        # Price at various intervals after
        for minutes, label in [(5, "5min"), (15, "15min"), (60, "1hr")]:
            after = price_df[price_df["datetime"] >= event_end + pd.Timedelta(minutes=minutes)]
            if len(after) > 0:
                change = (after.iloc[0]["close"] - price_end) / price_end * 100
                result[f"change_{label}_after"] = change

        result["change_during"] = (price_end - price_start) / price_start * 100
        results.append(result)

    return pd.DataFrame(results)

Long Liquidations (Longs Rekt) - Price Impact

MetricMeanMedianMinMax
Price change during-0.21%-0.03%-1.54%+0.15%
Price change 5 min after+0.12%+0.05%-0.16%+0.71%
Price change 15 min after+0.11%+0.14%-0.38%+0.66%
Price change 1 hour after+0.10%+0.05%-0.53%+1.00%

Long liquidations show a slight negative price impact during the event (-0.21% mean), with modest recovery afterward. The largest drop was -1.54% during a single 5-minute window, followed by a +0.71% bounce in the next 5 minutes.

Short Liquidations (Shorts Rekt) - Price Impact

MetricMeanMedianMinMax
Price change during-0.02%-0.02%-0.19%+0.26%
Price change 5 min after+0.07%+0.03%-0.17%+0.35%
Price change 15 min after+0.05%+0.06%-0.24%+0.39%
Price change 1 hour after-0.09%-0.09%-0.75%+0.38%

Short liquidations show minimal immediate impact, with price essentially flat during events. Interestingly, 1 hour after major short liquidation windows, price tends to drift slightly lower (-0.09% mean)—the squeeze exhausts itself.

Price impact comparison showing changes during and after top liquidation events

Long vs Short Comparison

Understanding the balance between long and short liquidations reveals market sentiment:

Long vs short liquidation comparison showing cumulative volume and breakdown

Key observations:

  • Nearly balanced over two weeks—neither longs nor shorts dominated overall
  • Daily variation: Individual days showed strong imbalances, but they averaged out
  • Size distribution: Both sides had similar total volumes but different event distributions

Case Study: 1000PEPEUSDT During 80% Pump

To see how liquidations behave during a strong directional move, let's analyze 1000PEPEUSDT from January 1-5, 2026—a period when the coin rose approximately 80%.

# Load PEPE liquidation data during pump
pepe_df = chd.get_liquidations(
    symbol="1000PEPEUSDT",
    exchange="binance_futures",
    start_date="2026-01-01",
    end_date="2026-01-05"
)

PEPE Liquidation Overview

MetricValue
Total liquidations4,589
Total volume$13.6M
Long liquidations (longs rekt)$4.4M (32.6%)
Short liquidations (shorts rekt)$9.2M (67.4%)

During the pump, shorts were liquidated over 2x more than longs. Short sellers betting against the rally got squeezed hard.

Top PEPE Short Liquidation Windows

TimeVolumeCountAvg Price
2026-01-04 08:15$669,08837$0.0122
2026-01-01 16:45$614,36113$0.0098
2026-01-02 02:35$504,0862$0.0100
2026-01-01 16:50$342,8216$0.0099
2026-01-02 12:30$259,45241$0.0105
1000PEPEUSDT price with top liquidation events during the pump

The chart shows the cascade of liquidation events as price rallied—short liquidations (green) dominate as bears got crushed.

PEPE Price Impact

The price impact during the PEPE pump tells a different story:

Long Liquidations:

MetricMeanMedianMinMax
During-0.45%-0.13%-2.03%+0.41%
1 hour after+1.11%+1.21%-2.57%+8.37%

Short Liquidations:

MetricMeanMedianMinMax
During+0.86%+0.35%-0.92%+5.74%
1 hour after+2.36%+2.51%-0.89%+6.21%

Short liquidation events during the pump saw price continue rising +2.36% on average over the next hour—the momentum had legs beyond just the squeeze. The +5.74% move during one short liquidation window shows the violence of a short squeeze in a low-cap altcoin.

PEPE long vs short liquidation breakdown

Complete Example Code

Here's a complete script for analyzing liquidation data:

import cryptohftdata as chd
import pandas as pd
import numpy as np

# Configure API
chd.configure_client(api_key="your-api-key-here")


def load_and_preprocess(symbol, exchange, start_date, end_date):
    """Load and preprocess liquidation data."""
    liqs_df = chd.get_liquidations(
        symbol=symbol, exchange=exchange,
        start_date=start_date, end_date=end_date
    )

    liqs_df["datetime"] = pd.to_datetime(liqs_df["event_time"], unit="ms")
    liqs_df["price"] = pd.to_numeric(liqs_df["average_price"], errors="coerce")
    liqs_df["quantity"] = pd.to_numeric(liqs_df["filled_quantity"], errors="coerce")
    liqs_df["volume_usd"] = liqs_df["quantity"] * liqs_df["price"]
    liqs_df["liq_type"] = liqs_df["side"].apply(
        lambda x: "long" if x == "SELL" else "short"
    )

    return liqs_df.sort_values("datetime").reset_index(drop=True)


def get_price_timeseries(symbol, exchange, start_date, end_date):
    """Load trade data and create price timeseries."""
    trades_df = chd.get_trades(
        symbol=symbol, exchange=exchange,
        start_date=start_date, end_date=end_date
    )

    trades_df["datetime"] = pd.to_datetime(trades_df["event_time"], unit="ms")
    trades_df["price"] = pd.to_numeric(trades_df["price"], errors="coerce")
    trades_df = trades_df[trades_df["price"] > 0]

    price_df = trades_df.set_index("datetime").resample("1min").agg({
        "price": ["first", "max", "min", "last"]
    }).dropna()
    price_df.columns = ["open", "high", "low", "close"]

    return price_df.reset_index()


def aggregate_by_window(liqs_df, window="5min"):
    """Aggregate liquidations into time windows."""
    df = liqs_df.copy().set_index("datetime")

    agg = df.resample(window).agg({
        "volume_usd": "sum",
        "quantity": "sum",
        "price": "mean",
        "side": "count"
    }).rename(columns={"side": "count", "price": "avg_price"})

    long_agg = df[df["liq_type"] == "long"].resample(window)["volume_usd"].sum()
    short_agg = df[df["liq_type"] == "short"].resample(window)["volume_usd"].sum()

    agg["long_volume"] = long_agg.reindex(agg.index, fill_value=0)
    agg["short_volume"] = short_agg.reindex(agg.index, fill_value=0)

    return agg.reset_index()


def find_top_windows(agg_df, n=10):
    """Find top N windows for long and short liquidations."""
    top_long = agg_df.nlargest(n, "long_volume").copy()
    top_long["type"] = "long"
    top_long["volume"] = top_long["long_volume"]

    top_short = agg_df.nlargest(n, "short_volume").copy()
    top_short["type"] = "short"
    top_short["volume"] = top_short["short_volume"]

    return top_long, top_short


def calculate_price_impact(events_df, price_df, window_minutes=5):
    """Calculate price movement during and after each event."""
    results = []

    for _, event in events_df.iterrows():
        event_time = event["datetime"]
        event_end = event_time + pd.Timedelta(minutes=window_minutes)

        pre = price_df[price_df["datetime"] <= event_time]
        if len(pre) == 0:
            continue
        price_start = pre.iloc[-1]["close"]

        post = price_df[price_df["datetime"] >= event_end]
        if len(post) == 0:
            continue
        price_end = post.iloc[0]["close"]

        result = {
            "datetime": event_time,
            "type": event["type"],
            "volume": event["volume"],
            "change_during": (price_end - price_start) / price_start * 100,
        }

        # Price changes after the event
        for minutes, label in [(5, "5min"), (15, "15min"), (60, "1hr")]:
            after = price_df[price_df["datetime"] >= event_end + pd.Timedelta(minutes=minutes)]
            if len(after) > 0:
                result[f"change_{label}_after"] = (after.iloc[0]["close"] - price_end) / price_end * 100

        results.append(result)

    return pd.DataFrame(results)


def print_summary(liqs_df, symbol):
    """Print analysis summary."""
    total_vol = liqs_df["volume_usd"].sum()
    long_vol = liqs_df[liqs_df["liq_type"] == "long"]["volume_usd"].sum()
    short_vol = liqs_df[liqs_df["liq_type"] == "short"]["volume_usd"].sum()

    print(f"\n{'='*60}")
    print(f"LIQUIDATION ANALYSIS: {symbol}")
    print(f"{'='*60}")
    print(f"Total liquidations: {len(liqs_df):,}")
    print(f"Total volume: ${total_vol:,.0f}")
    print(f"Long liquidations: ${long_vol:,.0f} ({long_vol/total_vol*100:.1f}%)")
    print(f"Short liquidations: ${short_vol:,.0f} ({short_vol/total_vol*100:.1f}%)")


# Run analysis
if __name__ == "__main__":
    # Load BTCUSDT data
    btc_liqs = load_and_preprocess("BTCUSDT", "binance_futures", "2025-08-01", "2025-08-14")
    btc_price = get_price_timeseries("BTCUSDT", "binance_futures", "2025-08-01", "2025-08-14")

    print_summary(btc_liqs, "BTCUSDT")

    # Find top windows
    agg_df = aggregate_by_window(btc_liqs, window="5min")
    top_long, top_short = find_top_windows(agg_df, n=10)

    print("\nTop 10 Long Liquidation Windows:")
    print(top_long[["datetime", "long_volume", "count", "avg_price"]])

    print("\nTop 10 Short Liquidation Windows:")
    print(top_short[["datetime", "short_volume", "count", "avg_price"]])

    # Calculate price impact
    all_top = pd.concat([top_long, top_short])
    impact_df = calculate_price_impact(all_top, btc_price)

    print("\nPrice Impact Summary:")
    for liq_type in ["long", "short"]:
        type_df = impact_df[impact_df["type"] == liq_type]
        print(f"\n{liq_type.title()} liquidations:")
        for col in ["change_during", "change_5min_after", "change_15min_after", "change_1hr_after"]:
            if col in type_df.columns:
                vals = type_df[col].dropna()
                print(f"  {col}: mean={vals.mean():.2f}%, median={vals.median():.2f}%")

Practical Applications

1. Identifying Major Market Events

The top liquidation windows reveal the most significant forced position closures. These often coincide with:

  • News events (economic releases, project announcements)
  • Technical breakouts (support/resistance breaks)
  • Cascade effects (one liquidation triggering another)

2. Sentiment Analysis

The long/short liquidation ratio provides a sentiment signal:

  • High long liquidations = overleveraged bulls getting cleared out
  • High short liquidations = overleveraged bears getting squeezed

BTCUSDT showed 48.5%/51.5% balance (neutral), while PEPE during the pump showed 32.6%/67.4% (shorts dominating)—classic squeeze territory.

3. Mean Reversion Opportunities

After major long liquidation events, price showed modest recovery (+0.12% average in next 5 minutes). Some traders look for cascade exhaustion as contrarian entry points, though the edge is small and requires careful timing.

Conclusion

Analyzing the top liquidation events directly is more intuitive than statistical threshold approaches. Key takeaways:

  1. Find the biggest events: Ranking by 5-minute window volume identifies the most significant liquidations
  2. Long/short ratio reveals bias: 48.5%/51.5% for balanced markets, 32.6%/67.4% during a squeeze
  3. Price impact varies by context: BTC showed modest -0.21% during long liquidations with small recovery, while PEPE shorts saw +2.36% continuation after squeeze events
  4. Directional moves amplify effects: The same analysis on PEPE during a pump showed shorts getting liquidated 2x more than longs

Start analyzing liquidation patterns to understand market stress and identify potential opportunities.

Ready to explore liquidation data across different market conditions? Get started with CryptoHFTData and access tick-level liquidation data for all major futures exchanges.

Ready to access the data?

Join thousands of traders and researchers building on CryptoHFTData.