Data Analytic Investments
On-ChainIntermediate

Exchange Netflow

Tracks the net movement of coins onto and off exchanges — the most direct on-chain signal of sell pressure.

Daily, Weekly (on-chain)
Bitcoin (BTC), Ethereum (ETH)
Python 3

What is it?

Exchange Netflow = Exchange Inflow − Exchange Outflow. Inflow is the volume of coins moving onto exchange wallets (typically to sell); outflow is the volume moving off exchanges (typically to self-custody, indicating long-term holding intent). Positive netflow (more coins entering exchanges) is bearish — it suggests holders are preparing to sell. Negative netflow (more coins leaving exchanges) is bullish — it suggests accumulation and reduced sell pressure. Exchange reserve (the total balance held on all exchanges) is the cumulative version of netflow — a declining reserve over months is a strong bullish signal.

When to use it

  • Sell pressure detection: a sudden spike in exchange inflow (large positive netflow) often precedes a price decline as coins are deposited to sell.
  • Accumulation confirmation: sustained negative netflow (coins leaving exchanges) during a price consolidation confirms institutional accumulation.
  • Exchange reserve trend: a multi-month declining exchange reserve is one of the strongest bullish on-chain signals — supply is being removed from the market.
  • Whale alert correlation: combine exchange netflow with whale wallet tracking to identify whether large inflows are from long-term holders or short-term speculators.
  • Pre-breakout confirmation: a price breakout accompanied by negative netflow (coins leaving exchanges) is more reliable than a breakout with positive netflow.

Common pitfalls

  • Exchange netflow data quality varies significantly by provider. Different providers track different sets of exchange wallets — always use the same provider for consistency.
  • Internal exchange transfers (e.g. moving coins between cold and hot wallets within the same exchange) can be misclassified as inflow or outflow.
  • The relationship between netflow and price is not immediate. Large inflows can precede price drops by days or weeks — it is a leading indicator, not a timing tool.
  • DeFi and self-custody trends mean that coins leaving exchanges may be going to DeFi protocols rather than long-term cold storage. This reduces the bullish signal strength.
  • Exchange netflow is most reliable for Bitcoin. For altcoins, a significant portion of trading occurs on DEXs, making exchange-based netflow an incomplete picture.

Free code template

Requires Python 3.9+ and the listed pip packages. See inline comments for usage.

Python 3
# Exchange Netflow Analysis — DAI Python Template
# Data source: Glassnode API
# pip install requests pandas matplotlib

import requests
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors

GLASS_API_KEY = 'YOUR_GLASSNODE_API_KEY'

def fetch_glassnode(metric: str, asset: str = 'BTC', resolution: str = '24h') -> pd.DataFrame:
    url = f'https://api.glassnode.com/v1/metrics/{metric}'
    r = requests.get(url, params={'a': asset, 'i': resolution, 'api_key': GLASS_API_KEY, 'f': 'JSON'})
    r.raise_for_status()
    df = pd.DataFrame(r.json())
    df['t'] = pd.to_datetime(df['t'], unit='s')
    return df.set_index('t').rename(columns={'v': metric.split('/')[-1]})

def plot_exchange_netflow(asset: str = 'BTC'):
    price_df   = fetch_glassnode('market/price_usd_close', asset)
    inflow_df  = fetch_glassnode('transactions/transfers_volume_to_exchanges_sum', asset)
    outflow_df = fetch_glassnode('transactions/transfers_volume_from_exchanges_sum', asset)
    reserve_df = fetch_glassnode('distribution/balance_exchanges', asset)

    df = price_df.join(inflow_df).join(outflow_df).join(reserve_df)
    df.columns = ['price', 'inflow', 'outflow', 'reserve']
    df['netflow'] = df['inflow'] - df['outflow']
    df['netflow_7d'] = df['netflow'].rolling(7).mean()

    fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(14, 12), sharex=True)
    fig.patch.set_facecolor('#0d1f3c')

    ax1.semilogy(df.index, df['price'], color='#3b82f6', linewidth=1.5)
    ax1.set_facecolor('#0d1f3c'); ax1.tick_params(colors='white')
    ax1.set_ylabel(f'{asset} Price (log)', color='white')

    colors = ['#ef4444' if v > 0 else '#22c55e' for v in df['netflow_7d']]
    ax2.bar(df.index, df['netflow_7d'], color=colors, alpha=0.7, width=1)
    ax2.axhline(0, color='#94a3b8', linestyle=':', alpha=0.5)
    ax2.set_facecolor('#0d1f3c'); ax2.tick_params(colors='white')
    ax2.set_ylabel('Netflow 7D MA (BTC)', color='white')

    ax3.plot(df.index, df['reserve'], color='#f59e0b', linewidth=1.5)
    ax3.set_facecolor('#0d1f3c'); ax3.tick_params(colors='white')
    ax3.set_ylabel('Exchange Reserve (BTC)', color='white')

    print(f'Latest Netflow (7D MA): {df["netflow_7d"].iloc[-1]:,.0f} BTC')
    print(f'Exchange Reserve: {df["reserve"].iloc[-1]:,.0f} BTC')
    plt.tight_layout()
    plt.savefig('exchange_netflow.png', dpi=150, bbox_inches='tight')
    plt.show()

# plot_exchange_netflow('BTC')