📊 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
| Correlation | Meaning | Market Behavior |
|---|---|---|
| > +0.5 | Trend supported by new capital | Healthy, self-reinforcing move |
| +0.1 → +0.5 | Mild participation | Range expansion, cautious entry |
| 0 → –0.3 | Divergent move | Covering, forced liquidations |
| < –0.3 | Strong divergence | Trap / 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
-
Normalize OI
df['notional_oi'] = df['open_interest'] * df['price']Gives a truer measure of capital deployed.
-
Add funding-rate overlay Combine correlation with funding to spot fueled vs. forced rallies.
-
Tag regime shifts Detect correlation sign flips → potential accumulation/distribution transitions.
-
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.