Skip to main content

📊 Quantifying Open Interest–Price Correlation

How to measure whether capital is fueling or fleeing a move.


🧩 1. Overview

Understanding that price + OI direction = conviction is powerful, but we can take it further by quantifying how closely the two move together.

A rising open interest (OI) doesn’t always mean “trend confirmation.” Sometimes OI grows in a choppy range, or price moves without new participation. By computing the rolling correlation between ∆Price and ∆OI, you can see whether the market’s capital inflow is fueling or fighting the move.


⚙️ 2. Data Inputs

You’ll need:

  • Price data: OHLCV candles from your exchange (1 m, 5 m, or 15 m).
  • Open interest data: From Drift, Hyperliquid, Coinglass, etc.
  • (Optional) Funding rate or volume delta to cross-check directional pressure.

Align them by timestamp and resample if necessary.


🧮 3. Derived Features

import pandas as pd

# df must have columns: ['timestamp', 'price', 'open_interest']
df = df.sort_values('timestamp').set_index('timestamp')

# Compute first differences
df['dPrice'] = df['price'].diff()
df['dOI'] = df['open_interest'].diff()

# Rolling correlation (example: 96 x 15 m ≈ 1 day)
window = 96
df['corr_dOI_dPrice'] = (
df['dPrice']
.rolling(window)
.corr(df['dOI'])
)

This produces a rolling metric between –1 and +1 showing how tightly OI and price move together.


🔍 4. Interpretation Bands

CorrelationMeaningMarket Behavior
> +0.5Trend supported by new capitalHealthy, self-reinforcing move
+0.1 → +0.5Mild participationRange expansion, cautious entry
0 → –0.3Divergent moveCovering, forced liquidations
< –0.3Strong divergenceTrap / exhaustion phase

📈 5. Visualization

import matplotlib.pyplot as plt

# ---------- 3-panel dashboard ----------
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(12, 10), sharex=True,
gridspec_kw={'height_ratios':[2,1,1]})

# Panel 1 — Price
ax1.plot(df.index, df['price'], label='Price', linewidth=1.2)
ax1.set_ylabel('Price')
ax1.set_title('Price vs Open Interest')
ax1.legend(loc='upper left')

# Panel 2 — Open Interest
ax2.plot(df.index, df['open_interest'], color='gray', alpha=0.7, label='Open Interest')
ax2.set_ylabel('Open Interest')
ax2.legend(loc='upper left')

# Panel 3 — Rolling correlation
ax3.plot(df.index, df['corr_dOI_dPrice'], label='ΔOI × ΔPrice Corr', color='purple')
ax3.axhline(0, color='black', linestyle='--', linewidth=0.7)
ax3.fill_between(df.index, 0, df['corr_dOI_dPrice'],
where=(df['corr_dOI_dPrice']>0), color='green', alpha=0.2)
ax3.fill_between(df.index, 0, df['corr_dOI_dPrice'],
where=(df['corr_dOI_dPrice']<0), color='red', alpha=0.2)
ax3.set_ylabel('Correlation')
ax3.set_title('Rolling Correlation (ΔOI × ΔPrice)')
ax3.legend(loc='upper left')

plt.tight_layout()
plt.show()

Interpretation

  • 🟩 Green zones → OI rising with price → fresh capital entering.
  • 🟥 Red zones → OI rising against price → capitulation or covering.
  • Crossing zero often marks an accumulation ↔ distribution shift.

🧠 6. Extensions

  1. Normalize OI

    df['notional_oi'] = df['open_interest'] * df['price']

    Gives a truer measure of capital deployed.

  2. Add funding-rate overlay Combine correlation with funding to spot fueled vs. forced rallies.

  3. Tag regime shifts Detect correlation sign flips → potential accumulation/distribution transitions.

  4. Multi-timeframe stack Compute correlation on 15 m, 1 h, 4 h to see alignment across micro↔macro conviction.


🧭 7. Takeaway

Correlation of ∆OI × ∆Price quantifies the belief entering the system.

  • Positive → Fuel entering (trend with participation)
  • Negative → Fuel leaving (covering / trap)

It’s one of the cleanest, most objective ways to see whether a move has real money behind it — or is just running on fumes.