Crypto-Native·Staking & Yield Analysis·Intermediate

Yield Comparison Dashboard

Build a comprehensive yield comparison analytics dashboard aggregating yields across DeFi lending protocols, centralized exchange earn products, and native protocol staking options with risk adjustment scoring, lock-up period consideration, and historical yield stability and consistency analysis.

cryptodefivisualization

Yield Comparison Dashboard — Crypto-Native

Category: Crypto-Native | Subcategory: Staking & Yield


What This Notebook Does

This notebook builds an interactive yield comparison dashboard covering the full spectrum of crypto yield sources — from protocol-level staking to DeFi money markets and liquidity provision.

Sources tracked:

  • Staking: ETH staking, SOL staking, MATIC staking
  • Lending: Aave v3, Compound v3, Morpho
  • Stablecoin LP: Curve 3pool, Curve FRAX-USDC
  • Volatile LP: Uniswap v3 concentrated positions
  • Yield Aggregators: Yearn, Convex

Dashboard panels:

  1. Current APY snapshot — bar chart by category
  2. 90-day yield history — line chart with rolling trends
  3. Risk-adjusted leaderboard — Sharpe-analog score
  4. Optimal allocation for different risk profiles
[1]
!pip install numpy pandas matplotlib seaborn requests --quiet
[2]
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import seaborn as sns
import requests
import warnings

warnings.filterwarnings('ignore')
%matplotlib inline
plt.rcParams['figure.figsize'] = (16, 6)
plt.rcParams['axes.spines.top'] = False
plt.rcParams['axes.spines.right'] = False
print('Imports ready.')
Imports ready.

Section 1 — Configuration

This section defines key configuration parameters for the dashboard.

  • USE_LIVE_DATA: A boolean flag to determine whether to fetch live yield data from DeFiLlama or use synthetic data. Setting this to True requires an active internet connection and can introduce delays.
  • HISTORY_DAYS: The number of past days for which yield history will be generated or fetched.
  • CAPITAL: The hypothetical capital (in USD) used for portfolio allocation simulations (though not yet implemented in the current dashboard panels).
[3]
USE_LIVE_DATA = False   # set True to pull from DeFiLlama
HISTORY_DAYS  = 90
CAPITAL       = 100_000
print('Config ready.')
Config ready.

Section 2 — Data

This section contains functions for data acquisition and generation. It provides a mechanism to either fetch real-time yield data or generate synthetic data for demonstration and testing purposes. The dashboard then uses this data to visualize yield comparisons.

[4]
def fetch_live_yields() -> pd.DataFrame:
    try:
        r = requests.get('https://yields.llama.fi/pools', timeout=30)
        if r.status_code != 200: return None
        df = pd.DataFrame(r.json()['data'])
        df = df[df['tvlUsd'] > 5_000_000].dropna(subset=['apy'])
        df = df.sort_values('tvlUsd', ascending=False).head(20)
        return df[['project', 'symbol', 'chain', 'apy', 'tvlUsd']]
    except:
        return None


def generate_yield_dashboard_data(n_days: int = 90, seed: int = 42) -> tuple:
    """
    Generate synthetic yield universe (current snapshot + 90-day history).

    Returns
    -------
    tuple  (current_df, history_df)
    """
    rng = np.random.default_rng(seed)

    protocols = [
        ('ETH Staking',    'stETH',    'Staking',      4.2,   'Low',    28e9),
        ('SOL Staking',    'SOL',      'Staking',      7.1,   'Low',    16e9),
        ('MATIC Staking',  'MATIC',    'Staking',      5.8,   'Low',    2e9),
        ('Aave v3',        'USDC',     'Lending',      6.2,   'Medium', 8e9),
        ('Aave v3',        'USDT',     'Lending',      5.9,   'Medium', 6e9),
        ('Compound v3',    'USDC',     'Lending',      5.4,   'Medium', 3e9),
        ('Morpho',         'USDC',     'Lending',      7.8,   'Medium', 1e9),
        ('Curve',          '3pool',    'Stablecoin LP',4.1,   'Medium', 10e9),
        ('Curve',          'FRAX-USDC','Stablecoin LP',7.3,   'Medium', 3e9),
        ('Uniswap v3',     'ETH-USDC', 'Volatile LP', 18.4,  'High',   2e9),
        ('Balancer',       'wstETH',   'Volatile LP',  5.8,  'Medium', 0.8e9),
        ('Yearn',          'yvUSDC',   'Vault',         8.1,  'Medium', 0.5e9),
        ('Convex',         'cvxCRV',   'Vault',        15.2,  'High',   1e9),
    ]

    current_rows = []
    for proj, sym, cat, base_apy, risk, tvl in protocols:
        apy = max(0.5, base_apy + rng.normal(0, 0.2))
        current_rows.append({'project': proj, 'symbol': sym, 'category': cat,
                              'apy': apy, 'tvlUsd': tvl, 'risk': risk})
    current_df = pd.DataFrame(current_rows)

    # 90-day history
    idx = pd.date_range(end=pd.Timestamp.today(), periods=n_days, freq='D')
    history = {}
    for _, row in current_df.iterrows():
        key = f"{row['project']} {row['symbol']}"
        vol = 0.3 if row['risk'] == 'Low' else (0.8 if row['risk'] == 'Medium' else 2.0)
        series = row['apy'] + np.cumsum(rng.normal(0, vol / 90, n_days))
        history[key] = np.maximum(series, 0.5)
    history_df = pd.DataFrame(history, index=idx)

    return current_df, history_df


if USE_LIVE_DATA:
    current_df = fetch_live_yields()
    _, history_df = generate_yield_dashboard_data(HISTORY_DAYS)
else:
    current_df, history_df = generate_yield_dashboard_data(HISTORY_DAYS)

if current_df is None:
    current_df, history_df = generate_yield_dashboard_data(HISTORY_DAYS)

print(f'Protocols tracked: {len(current_df)}')
Protocols tracked: 13

This function attempts to fetch live yield data from the DeFiLlama API. It filters for protocols with a Total Value Locked (TVL) greater than $5 million and selects the top 20 protocols by TVL. If the API call fails or returns an error, it gracefully returns None.

This function generates synthetic current yield data and historical yield data over a specified number of days. It defines a set of common crypto protocols with their base APYs, categories, risk profiles, and TVLs. For historical data, it simulates daily APY fluctuations based on the protocol's risk level, creating a realistic-looking time series.

Section 3 — Dashboard

This section is dedicated to visualizing the crypto yield data. It generates a multi-panel dashboard using matplotlib and seaborn to present different aspects of the yield landscape:

  • Current APY by Protocol: A horizontal bar chart showing the current Annual Percentage Yield for each tracked protocol, color-coded by category.
  • 90-Day APY History: A line chart displaying the rolling 7-day average APY over the last 90 days for the top 6 protocols, illustrating trends and volatility.
  • TVL by Category: A pie chart showing the distribution of Total Value Locked across different yield categories (Staking, Lending, Stablecoin LP, etc.), providing insight into where capital is deployed.
[5]
cat_colors = {'Staking': '#1976d2', 'Lending': '#43a047',
               'Stablecoin LP': '#ff9800', 'Volatile LP': '#e53935', 'Vault': '#9c27b0'}

fig = plt.figure(figsize=(16, 12))
gs = gridspec.GridSpec(2, 2, figure=fig, hspace=0.4, wspace=0.35)
fig.suptitle('Crypto Yield Comparison Dashboard', fontsize=15, fontweight='bold')

# Panel 1: Current APY bar chart
ax1 = fig.add_subplot(gs[0, :])
sorted_df = current_df.sort_values('apy', ascending=True)
bar_colors = [cat_colors.get(c, '#9e9e9e') for c in sorted_df.get('category', ['Other']*len(sorted_df))]
bars = ax1.barh(sorted_df['symbol'] + ' (' + sorted_df['project'] + ')',
                sorted_df['apy'], color=bar_colors, alpha=0.85)
for bar, apy in zip(bars, sorted_df['apy']):
    ax1.text(apy + 0.1, bar.get_y() + bar.get_height()/2, f'{apy:.1f}%', va='center', fontsize=8)
ax1.set_xlabel('APY (%)')
ax1.set_title('Current APY by Protocol')
patches = [plt.Rectangle((0,0),1,1, color=v, alpha=0.85) for v in cat_colors.values()]
ax1.legend(patches, cat_colors.keys(), fontsize=8, loc='lower right')

# Panel 2: 90-day history
ax2 = fig.add_subplot(gs[1, 0])
for col in history_df.columns[:6]:
    ax2.plot(history_df.index, history_df[col].rolling(7).mean(), lw=1.2, alpha=0.8, label=col[:20])
ax2.set_ylabel('APY (%)')
ax2.set_title('90-Day APY History (top 6)')
ax2.legend(fontsize=6)

# Panel 3: TVL distribution
ax3 = fig.add_subplot(gs[1, 1])
cat_tvl = current_df.groupby('category')['tvlUsd'].sum() / 1e9
cat_colors_list = [cat_colors.get(c, '#9e9e9e') for c in cat_tvl.index]
ax3.pie(cat_tvl.values, labels=cat_tvl.index, autopct='%1.0f%%',
        colors=cat_colors_list, startangle=140)
ax3.set_title('TVL by Category')

plt.show()
cell output

Section 4 — Export

This section handles the export of the generated data. The current yield snapshot and the historical yield data are saved as CSV files, allowing for external analysis, record-keeping, or integration with other tools.

[6]
current_df.to_csv('yield_comparison_snapshot.csv', index=False)
history_df.to_csv('yield_comparison_history.csv')
print('Saved: yield_comparison_snapshot.csv, yield_comparison_history.csv')
Saved: yield_comparison_snapshot.csv, yield_comparison_history.csv

Conclusion

This notebook provides a foundational interactive dashboard for comparing crypto yield sources. It demonstrates how to fetch or simulate yield data and visualize key metrics like current APY, historical trends, and TVL distribution. Future enhancements could include risk-adjusted metrics, optimal allocation strategies, and more advanced interactive features using libraries like Plotly or Dash.