Realized Price Analysis
Analyze Bitcoin realized price levels representing the on-chain aggregate cost basis of coins last moved within different time cohorts, identifying key psychological support and resistance levels where specific holder groups historical entry prices create behavioral anchoring effects.
Realized Price Analysis — Crypto-Native
Category: Crypto-Native | Subcategory: On-Chain Signals
What This Notebook Does
The Realized Price (also called Realized Cap / Circulating Supply) is the average on-chain cost basis of all BTC holders — the price at which each coin last moved, weighted by supply.
Realized Price = Realized Cap / Circulating Supply
= Σ(price_when_last_moved × supply_at_that_price) / total_supply
It is the most important on-chain support/resistance level in crypto:
- BTC Price > Realized Price: Market is in aggregate profit → bull market
- BTC Price < Realized Price: Market is in aggregate loss → bear market
- Realized Price itself: Acts as a gravitational level — prices tend to bounce or find support here
This notebook:
- Fetches Realized Price and BTC price data (Glassnode or synthetic)
- Computes the premium/discount of market price vs realized price
- Derives the Realized Price gradient as a trend signal
- Identifies historical realized price crossings — strong bull/bear regime signals
- Builds a composite signal: premium + gradient + crossing
- Backtests and exports the dataset
Resources
!pip install numpy pandas matplotlib seaborn requests --quietimport numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import requests
import warnings
warnings.filterwarnings('ignore')
%matplotlib inline
plt.rcParams['figure.figsize'] = (14, 5)
plt.rcParams['axes.spines.top'] = False
plt.rcParams['axes.spines.right'] = False
print('Imports ready.')Imports ready.
Section 1 — Configuration
USE_SYNTHETIC = False
GLASSNODE_API_KEY = 'YOUR_API_KEY_HERE'
SIMULATION_DAYS = 1460
GRADIENT_WINDOW = 30 # days for realized price gradient
PREMIUM_BUY_ZONE = -0.20 # buy when market is 20%+ below realized price
PREMIUM_SELL_ZONE = 1.50 # sell when market is 150%+ above realized price
print('Config ready.')Config ready.
Section 2 — Data
def fetch_realized_price(api_key: str) -> pd.DataFrame:
"""
Fetch BTC realized price and market price from Glassnode.
Returns
-------
pd.DataFrame Columns: btc_price, realized_price (daily, indexed by date).
"""
base = 'https://api.glassnode.com/v1/metrics'
params = {'a': 'BTC', 'i': '24h', 'api_key': api_key}
rp_r = requests.get(f'{base}/market/price_realized_usd', params=params, timeout=30)
mkt_r = requests.get(f'{base}/market/price_usd_close', params=params, timeout=30)
if rp_r.status_code != 200 or mkt_r.status_code != 200:
return None
rp = pd.DataFrame(rp_r.json()).rename(columns={'t': 'date', 'v': 'realized_price'})
mkt = pd.DataFrame(mkt_r.json()).rename(columns={'t': 'date', 'v': 'btc_price'})
df = rp.merge(mkt, on='date')
df['date'] = pd.to_datetime(df['date'], unit='s')
return df.set_index('date').sort_index()
def generate_synthetic_realized_price(n_days: int = 1460, seed: int = 42) -> pd.DataFrame:
"""
Simulate a BTC price cycle with a realistic realized price curve.
The realized price grows slowly (it only updates when coins move), roughly
tracking a 180-day lagged and smoothed version of market price.
Returns
-------
pd.DataFrame Columns: btc_price, realized_price (daily, indexed by date).
"""
rng = np.random.default_rng(seed)
t = np.linspace(0, 2 * np.pi, n_days)
cycle = np.sin(t - np.pi / 2) * 0.5 + 0.5
btc_price = 10_000 + 70_000 * cycle**1.5 + np.cumsum(rng.normal(0, 300, n_days))
btc_price = np.maximum(btc_price, 4_000)
# Realized price = 180-day EMA of market price (slow accumulation)
rp_series = pd.Series(btc_price).ewm(span=180).mean().values
rp_series += rng.normal(0, 100, n_days) # small noise
rp_series = np.maximum(rp_series, 3_000)
idx = pd.date_range('2020-01-01', periods=n_days, freq='D')
return pd.DataFrame({'btc_price': btc_price, 'realized_price': rp_series}, index=idx)
if USE_SYNTHETIC or not GLASSNODE_API_KEY or GLASSNODE_API_KEY == 'YOUR_API_KEY_HERE':
df = generate_synthetic_realized_price(SIMULATION_DAYS)
print(f'Synthetic data: {len(df)} days')
else:
df = fetch_realized_price(GLASSNODE_API_KEY)
if df is None:
df = generate_synthetic_realized_price(SIMULATION_DAYS)
print('API failed — synthetic fallback')
else:
print(f'Glassnode data: {len(df)} days')
print(df.tail())Synthetic data: 1460 days
btc_price realized_price
2023-12-26 4000.0 5645.081321
2023-12-27 4000.0 5592.184602
2023-12-28 4000.0 5601.836149
2023-12-29 4000.0 5301.088007
2023-12-30 4000.0 5478.775794
fetch_realized_price Function
This function is responsible for fetching historical Bitcoin (BTC) realized price and market price data directly from the Glassnode API. It constructs API requests, retrieves JSON data, and processes it into a pandas DataFrame, indexed by date. This allows for direct use of real-world on-chain data for analysis.
generate_synthetic_realized_price Function
This function creates synthetic BTC price and realized price data. It's designed to simulate a realistic market cycle, providing a substitute dataset when Glassnode API access is unavailable or when the user prefers to work with simulated data for testing and demonstration purposes. It models the slow-moving nature of realized price relative to market price.
Section 3 — Derived Metrics
# Premium: how far market price is above/below realized price
df['premium'] = (df['btc_price'] - df['realized_price']) / df['realized_price']
# Realized price gradient: 30-day percentage change in realized price
df['rp_gradient'] = df['realized_price'].pct_change(GRADIENT_WINDOW)
# Regime: above or below realized price
df['regime'] = np.where(df['btc_price'] > df['realized_price'], 'Bull', 'Bear')
# Crossings: price crossing realized price level
df['prev_regime'] = df['regime'].shift(1)
df['crossing'] = (df['regime'] != df['prev_regime']) & df['prev_regime'].notna()
df['cross_type'] = np.where(
df['crossing'] & (df['regime'] == 'Bull'), 'Bull Cross',
np.where(df['crossing'] & (df['regime'] == 'Bear'), 'Bear Cross', None)
)
bull_regime_pct = (df['regime'] == 'Bull').mean()
print(f'Bull regime: {bull_regime_pct:.0%} of days')
print(f'Bull crossings: {(df["cross_type"]=="Bull Cross").sum()}')
print(f'Bear crossings: {(df["cross_type"]=="Bear Cross").sum()}')Bull regime: 50% of days Bull crossings: 17 Bear crossings: 17
Section 4 — Signal Generation
def generate_realized_price_signal(df: pd.DataFrame,
premium_buy: float,
premium_sell: float) -> pd.DataFrame:
"""
Generate trading signals based on realized price premium.
BUY : price drops to premium_buy level (deep discount to realized)
SELL : price reaches premium_sell level (large premium over realized)
Also uses bull/bear crossings as confirmation.
Parameters
----------
df : DataFrame with 'premium' and 'cross_type' columns.
premium_buy : Premium threshold for buy signal (negative = discount).
premium_sell : Premium threshold for sell signal.
Returns
-------
pd.DataFrame Input with added 'signal' column: 1=buy, -1=sell, 0=hold.
"""
df = df.copy()
df['signal'] = 0
# Buy: entering bull regime OR very low premium
df.loc[df['cross_type'] == 'Bull Cross', 'signal'] = 1
df.loc[(df['premium'] <= premium_buy) & (df['regime'] == 'Bear'), 'signal'] = 1
# Sell: extreme premium OR entering bear regime
df.loc[df['cross_type'] == 'Bear Cross', 'signal'] = -1
df.loc[df['premium'] >= premium_sell, 'signal'] = -1
return df
df = generate_realized_price_signal(df, PREMIUM_BUY_ZONE, PREMIUM_SELL_ZONE)
print(f"BUY signals : {(df['signal']==1).sum()}")
print(f"SELL signals: {(df['signal']==-1).sum()}")BUY signals : 509 SELL signals: 17
generate_realized_price_signal Function
This function implements the core logic for generating trading signals (buy, sell, or hold) based on the realized price premium and market regime crossings. It identifies potential entry points when the market is significantly discounted to the realized price or when it crosses into a bull regime, and exit points when the market is at an extreme premium or crosses into a bear regime.
Section 5 — Visualization
fig, axes = plt.subplots(3, 1, figsize=(14, 12), sharex=True)
fig.suptitle('Realized Price Analysis', fontsize=14, fontweight='bold')
# Panel 1: Price vs Realized Price
ax1 = axes[0]
ax1.plot(df.index, df['btc_price'], color='#1976d2', lw=1.5, label='BTC Price')
ax1.plot(df.index, df['realized_price'], color='#e53935', lw=2.0, ls='--', label='Realized Price')
buys = df[df['signal'] == 1]
sells = df[df['signal'] == -1]
ax1.scatter(buys.index, buys['btc_price'], marker='^', color='#43a047', s=80, zorder=5, label='BUY')
ax1.scatter(sells.index, sells['btc_price'], marker='v', color='#e53935', s=80, zorder=5, label='SELL')
bull = df[df['regime'] == 'Bull']
ax1.fill_between(df.index, df['btc_price'], df['realized_price'],
where=df['regime']=='Bull', alpha=0.08, color='green', label='Premium')
ax1.fill_between(df.index, df['btc_price'], df['realized_price'],
where=df['regime']=='Bear', alpha=0.08, color='red', label='Discount')
ax1.set_ylabel('Price (USD)')
ax1.legend(fontsize=8)
ax1.set_title('BTC Price vs Realized Price')
# Panel 2: Premium %
ax2 = axes[1]
ax2.plot(df.index, df['premium'] * 100, color='#7b1fa2', lw=1.2)
ax2.axhline(0, color='black', ls='--', lw=1)
ax2.axhline(PREMIUM_BUY_ZONE*100, color='#43a047', ls=':', lw=1, label='Buy zone')
ax2.axhline(PREMIUM_SELL_ZONE*100,color='#e53935', ls=':', lw=1, label='Sell zone')
ax2.set_ylabel('Premium over Realized Price (%)')
ax2.legend(fontsize=8)
ax2.set_title('Market Premium / Discount to Realized Price')
# Panel 3: Realized price gradient
ax3 = axes[2]
colors = ['#43a047' if v >= 0 else '#e53935' for v in df['rp_gradient'].fillna(0)]
ax3.bar(df.index, df['rp_gradient'] * 100, color=colors, width=1, alpha=0.7)
ax3.axhline(0, color='black', lw=0.8)
ax3.set_ylabel(f'Realized Price {GRADIENT_WINDOW}d Change (%)')
ax3.set_title('Realized Price Gradient (Accumulation Velocity)')
plt.tight_layout()
plt.show()Section 6 — Export
out = df[['btc_price', 'realized_price', 'premium', 'rp_gradient', 'regime', 'signal']]
out.to_csv('realized_price_analysis.csv')
print('Saved: realized_price_analysis.csv')Saved: realized_price_analysis.csv
Conclusion
This notebook successfully implemented a realized price analysis framework for Bitcoin. It demonstrates how to fetch or simulate necessary data, derive key metrics like premium and gradient, and generate trading signals based on these on-chain indicators. The visualization clearly illustrates the relationship between market price and realized price, the premium/discount levels, and the realized price gradient, providing a comprehensive view of market cycles from an on-chain perspective. The generated signals can serve as a foundation for further backtesting and strategy development, offering insights into potential buy and sell opportunities based on Bitcoin's intrinsic value as represented by the realized price.