Claude Code Plugins

Community-maintained marketplace

Feedback

bond-benchmarking

@keith-mvs/ordinis
1
0

Compare bond performance against market benchmarks and indices. Analyzes yield spreads, duration matching, and relative value. Requires numpy>=1.24.0, pandas>=2.0.0. Use when evaluating bonds against peers, measuring portfolio attribution, or identifying value opportunities in fixed income markets.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name bond-benchmarking
description Compare bond performance against market benchmarks and indices. Analyzes yield spreads, duration matching, and relative value. Requires numpy>=1.24.0, pandas>=2.0.0. Use when evaluating bonds against peers, measuring portfolio attribution, or identifying value opportunities in fixed income markets.

Bond Benchmarking and Relative Performance Analysis Skill Development

Objective

Compare bond yields, durations, and credit quality against market benchmarks and indices to identify relative value opportunities and measure portfolio performance. Master systematic frameworks for peer comparison and excess return attribution.

Skill Classification

Domain: Fixed Income Portfolio Management Level: Advanced Prerequisites: Bond Pricing, Yield Measures, Duration, Credit Risk Estimated Time: 12-15 hours

Focus Areas

1. Benchmark Selection

Treasury Curve as Benchmark:

def treasury_benchmark_yield(maturity, treasury_curve):
    """
    Interpolate Treasury yield for given maturity.

    Parameters:
    -----------
    maturity : float
        Bond maturity in years
    treasury_curve : dict
        {maturity: yield} mapping for Treasury curve

    Returns:
    --------
    float : Interpolated benchmark yield
    """
    from scipy.interpolate import interp1d

    maturities = sorted(treasury_curve.keys())
    yields = [treasury_curve[m] for m in maturities]

    interpolator = interp1d(maturities, yields, kind='cubic',
                           fill_value='extrapolate')
    return float(interpolator(maturity))

Corporate Bond Index Benchmarks:

  • Bloomberg Barclays US Corporate Bond Index
  • ICE BofA US Corporate Index
  • S&P US Issued Investment Grade Corporate Bond Index

Index Characteristics:

class BondIndexProfile:
    """Corporate bond index profile."""

    def __init__(self, name):
        self.name = name
        self.constituents = []

    def add_bond(self, bond_info):
        """Add bond to index."""
        self.constituents.append(bond_info)

    def calculate_metrics(self):
        """Calculate index-level metrics."""
        weights = np.array([b['market_value'] for b in self.constituents])
        weights = weights / weights.sum()

        durations = np.array([b['duration'] for b in self.constituents])
        yields = np.array([b['ytm'] for b in self.constituents])

        return {
            'weighted_duration': np.dot(weights, durations),
            'weighted_yield': np.dot(weights, yields),
            'total_market_value': weights.sum(),
            'num_constituents': len(self.constituents)
        }

2. Spread to Benchmark

G-Spread (Spread to Government):

G-Spread = YTM_Corporate - YTM_Treasury_Interpolated

I-Spread (Interpolated Spread):

I-Spread = YTM_Corporate - Swap_Rate_Interpolated

Z-Spread (Zero-Volatility Spread):

P = Σ [CF_t / (1 + r_t + Z)^t]

Where:
  P = Bond price
  r_t = Spot rate at time t
  Z = Constant spread (Z-spread)
  CF_t = Cash flow at time t

Python Implementation:

def g_spread(corporate_ytm, treasury_ytm):
    """Calculate G-spread (in basis points)."""
    return (corporate_ytm - treasury_ytm) * 10000

def calculate_z_spread(bond_price, cash_flows, spot_rates, times):
    """
    Calculate Z-spread using root-finding.

    Parameters:
    -----------
    bond_price : float
        Current bond price
    cash_flows : array-like
        Future cash flows
    spot_rates : array-like
        Risk-free spot rates for each cash flow
    times : array-like
        Time to each cash flow (years)

    Returns:
    --------
    float : Z-spread (as decimal)
    """
    from scipy.optimize import newton

    def price_diff(z_spread):
        pv = sum(cf / (1 + spot + z_spread)**t
                for cf, spot, t in zip(cash_flows, spot_rates, times))
        return pv - bond_price

    z_spread = newton(price_diff, 0.01, maxiter=100)
    return z_spread

def spread_duration(bond_duration, spread_change):
    """
    Estimate price impact of spread change.

    ΔP/P ≈ -Duration × ΔSpread
    """
    return -bond_duration * spread_change

3. Relative Value Analysis

Comparing Bonds:

class RelativeValueAnalysis:
    """Framework for relative value comparison."""

    @staticmethod
    def compare_bonds(bond1, bond2, benchmark_yield):
        """
        Compare two bonds for relative value.

        Parameters:
        -----------
        bond1, bond2 : dict
            Bond characteristics: {'ytm', 'duration', 'rating', 'price'}
        benchmark_yield : float
            Benchmark Treasury yield

        Returns:
        --------
        dict : Comparative metrics
        """
        spread1 = (bond1['ytm'] - benchmark_yield) * 10000  # bps
        spread2 = (bond2['ytm'] - benchmark_yield) * 10000

        spread_per_duration1 = spread1 / bond1['duration']
        spread_per_duration2 = spread2 / bond2['duration']

        return {
            'bond1_spread': spread1,
            'bond2_spread': spread2,
            'bond1_spread_per_duration': spread_per_duration1,
            'bond2_spread_per_duration': spread_per_duration2,
            'relative_value_winner': 'Bond 1' if spread_per_duration1 > spread_per_duration2 else 'Bond 2'
        }

    @staticmethod
    def cheapness_score(actual_spread, model_spread):
        """
        Calculate cheapness metric.

        Positive = bond is cheap (offers excess spread)
        Negative = bond is rich (offers insufficient spread)
        """
        return actual_spread - model_spread

4. Peer Comparison (Sector/Rating)

Sector Classification:

SECTOR_GROUPS = {
    'Financial': ['Banks', 'Insurance', 'Asset Managers', 'REITs'],
    'Industrial': ['Manufacturing', 'Transportation', 'Capital Goods'],
    'Utility': ['Electric', 'Gas', 'Water'],
    'Technology': ['Software', 'Hardware', 'Semiconductors'],
    'Consumer': ['Retail', 'Food & Beverage', 'Automobiles'],
    'Energy': ['Oil & Gas', 'Renewables'],
    'Healthcare': ['Pharmaceuticals', 'Biotechnology', 'Hospitals']
}

def sector_peer_analysis(bond, peer_bonds):
    """
    Compare bond against sector peers.

    Parameters:
    -----------
    bond : dict
        Subject bond characteristics
    peer_bonds : list of dict
        Peer bond characteristics

    Returns:
    --------
    dict : Peer comparison metrics
    """
    peer_spreads = np.array([b['spread'] for b in peer_bonds])
    peer_durations = np.array([b['duration'] for b in peer_bonds])

    return {
        'bond_spread': bond['spread'],
        'peer_median_spread': np.median(peer_spreads),
        'peer_mean_spread': np.mean(peer_spreads),
        'spread_percentile': (peer_spreads < bond['spread']).sum() / len(peer_spreads) * 100,
        'peer_duration_range': (peer_durations.min(), peer_durations.max()),
        'relative_cheapness': bond['spread'] - np.median(peer_spreads)
    }

Rating-Based Comparison:

def rating_tier_analysis(bond_rating, bonds_universe):
    """
    Compare bond spread within rating tier.

    Parameters:
    -----------
    bond_rating : str
        Subject bond rating
    bonds_universe : list of dict
        All bonds with ratings

    Returns:
    --------
    dict : Rating tier statistics
    """
    same_rating = [b for b in bonds_universe
                   if b['rating'] == bond_rating]

    if not same_rating:
        return None

    spreads = np.array([b['spread'] for b in same_rating])

    return {
        'rating': bond_rating,
        'count': len(same_rating),
        'spread_mean': spreads.mean(),
        'spread_median': np.median(spreads),
        'spread_std': spreads.std(),
        'spread_min': spreads.min(),
        'spread_max': spreads.max()
    }

5. Excess Return Measurement

Total Return Components:

Total Return = Coupon Income + Price Change + Reinvestment Income

Excess Return (Alpha):

Excess Return = Portfolio Return - Benchmark Return

Beta vs. Benchmark:

β = Cov(R_portfolio, R_benchmark) / Var(R_benchmark)

Python Framework:

class PerformanceAttribution:
    """Performance attribution framework."""

    @staticmethod
    def total_return(beginning_value, ending_value, coupon_income):
        """
        Calculate total return.

        Returns:
        --------
        float : Total return (as decimal)
        """
        return (ending_value + coupon_income - beginning_value) / beginning_value

    @staticmethod
    def excess_return(portfolio_return, benchmark_return):
        """Calculate alpha."""
        return portfolio_return - benchmark_return

    @staticmethod
    def calculate_beta(portfolio_returns, benchmark_returns):
        """
        Calculate portfolio beta vs. benchmark.

        Parameters:
        -----------
        portfolio_returns : array-like
            Historical portfolio returns
        benchmark_returns : array-like
            Historical benchmark returns

        Returns:
        --------
        float : Beta coefficient
        """
        covariance = np.cov(portfolio_returns, benchmark_returns)[0, 1]
        benchmark_variance = np.var(benchmark_returns)
        return covariance / benchmark_variance

    @staticmethod
    def attribution_decomposition(portfolio_return, benchmark_return,
                                  duration_effect, spread_effect,
                                  selection_effect):
        """
        Decompose excess return into components.

        Parameters:
        -----------
        duration_effect : float
            Return from duration positioning
        spread_effect : float
            Return from spread changes
        selection_effect : float
            Return from security selection

        Returns:
        --------
        dict : Attribution breakdown
        """
        total_alpha = portfolio_return - benchmark_return

        return {
            'total_excess_return': total_alpha,
            'duration_contribution': duration_effect,
            'spread_contribution': spread_effect,
            'selection_contribution': selection_effect,
            'residual': total_alpha - (duration_effect + spread_effect + selection_effect)
        }

6. Benchmark Replication and Tracking Error

Tracking Error:

TE = σ(R_portfolio - R_benchmark)

Information Ratio:

IR = Excess Return / Tracking Error

Python Implementation:

def tracking_error(portfolio_returns, benchmark_returns):
    """
    Calculate tracking error.

    Parameters:
    -----------
    portfolio_returns : array-like
        Portfolio returns over time
    benchmark_returns : array-like
        Benchmark returns over time

    Returns:
    --------
    float : Annualized tracking error
    """
    excess_returns = np.array(portfolio_returns) - np.array(benchmark_returns)
    return excess_returns.std() * np.sqrt(12)  # Annualize if monthly

def information_ratio(portfolio_returns, benchmark_returns):
    """
    Calculate information ratio.

    Returns:
    --------
    float : IR (higher is better)
    """
    excess_returns = np.array(portfolio_returns) - np.array(benchmark_returns)
    mean_excess = excess_returns.mean() * 12  # Annualize
    te = excess_returns.std() * np.sqrt(12)

    return mean_excess / te if te > 0 else 0

def portfolio_replication(benchmark_holdings, target_budget):
    """
    Replicate benchmark with subset of holdings.

    Uses optimization to minimize tracking error.
    """
    from scipy.optimize import minimize

    n = len(benchmark_holdings)

    # Objective: minimize deviation from benchmark weights
    def objective(weights):
        benchmark_weights = np.array([h['weight'] for h in benchmark_holdings])
        return np.sum((weights - benchmark_weights)**2)

    # Constraints
    constraints = [
        {'type': 'eq', 'fun': lambda w: np.sum(w) - 1}  # Sum to 1
    ]

    bounds = [(0, 1) for _ in range(n)]
    initial_weights = np.ones(n) / n

    result = minimize(objective, initial_weights, method='SLSQP',
                     bounds=bounds, constraints=constraints)

    return result.x

Expected Competency

Select benchmarks, calculate spreads (G/I/Z), perform peer analysis, measure excess returns and attribution, compute tracking error/IR, identify relative value, replicate indices.

Deliverables

benchmark_comparison.ipynb: Yield curves, spread tools (G/I/Z), peer comparison, excess return attribution, tracking error, relative value screener

bond_benchmark_report.md: Spread visualization, peer tables, attribution charts, relative value ranking, portfolio positioning

Reference Materials

Texts: Fabozzi Handbook of Fixed Income Securities (Performance Attribution), Dynkin Quantitative Management of Bond Portfolios

Standards: Bloomberg indices methodology, ICE BofA index construction, S&P Dow Jones fixed income methodology

Data: Bloomberg Terminal (BVAL curves), FRED (Treasury/spreads), FINRA TRACE (transactions)

Validation Framework

Self-Assessment Checklist

  • Calculate G-spread for corporate vs. Treasury
  • Compute Z-spread using spot curve
  • Perform sector peer comparison
  • Calculate portfolio tracking error
  • Attribute excess returns to duration/spread/selection
  • Identify cheap/rich bonds vs. peers
  • Compute information ratio

Practice Problems

  1. Bond 5% YTM, 7-year, Treasury 3.5% → Calculate G-spread
  2. Portfolio 6% return, Benchmark 5.2%, TE 1.5% → Calculate IR
  3. 10 peer bonds → Identify 3 cheapest by spread-per-duration
  4. 150 bps excess return = duration (50) + spread (75) + selection (?)

Integration with Other Skills

  • Yield Measures: YTM serves as basis for spreads
  • Duration: Duration-adjusted spread comparison
  • Credit Risk: Credit quality peer grouping
  • Portfolio Management: Benchmark-aware construction

Standards and Compliance

All benchmarking analysis must document:

  • Benchmark selection rationale
  • Rebalancing frequency
  • Data sources and dates
  • Spread calculation methodology
  • Performance calculation conventions

Version Control

Version: 1.0.0 | Last Updated: 2025-12-07 | Status: Production Ready

Previous: credit-risk/ | Next: option-adjusted-spread/