logo
RustyBT Documentation
Attribution Analysis
Initializing search
    jerryinyang/rustybt
    • Home
    • Getting Started
    • User Guides
    • Migration Guides
    • Examples & Tutorials
    • API Reference
    • About
    jerryinyang/rustybt
    • Home
      • Installation
      • Quick Start
      • Configuration
      • Decimal Precision
        • Backtest Output Organization
        • Strategy Code Capture
        • Cash Validation
        • CSV Data Import
        • Data Ingestion
        • Databento Data Import
        • Data Validation
        • Creating Data Adapters
        • Migrating to Unified Data
        • Caching System
        • Caching Guide
        • Broker Setup
        • Testnet Setup
        • Live vs Backtest Data
        • WebSocket Streaming
        • Type Hinting
        • Exception Handling
        • Execution Methods
        • Pipeline API
        • Advanced Pipeline Techniques
        • Multi-Strategy Portfolio
        • Deployment Guide
        • Production Checklist
        • Audit Logging
        • Troubleshooting
      • Cash Validation Migration
      • Overview
        • Overview
          • Crypto Backtesting with CCXT Data Adapter
          • Equity Backtesting with YFinance Data Adapter
          • Getting Started with RustyBT
          • Data Ingestion with RustyBT
          • Strategy Development with RustyBT
          • Performance Analysis
          • Strategy Optimization
          • Walk-Forward Optimization
          • Risk Analytics
          • Portfolio Construction (Single-Strategy Multi-Asset)
          • 09. Multi-Strategy Portfolio
          • Live Paper Trading
          • Complete Workflow: Data → Backtest → Analysis → Optimization
          • CCXT Data Ingestion
          • YFinance Data Ingestion
          • Custom Data Adapter
          • Backtest with Cache
          • Full Validation (Backtest & Paper)
          • Cache Warming
          • Generate Backtest Report
          • Live Trading (Simple)
          • Live Trading (Advanced)
          • Paper Trading (Simple)
          • Paper Trading Validation
          • Shadow Trading (Simple)
          • Shadow Trading Dashboard
          • Portfolio Allocator Tutorial
          • Allocation Algorithms
          • Attribution Analysis
          • Slippage Models
          • Borrow Costs
          • Overnight Financing
          • High-Frequency Custom Triggers
          • Latency Simulation
          • Pipeline API
          • WebSocket Streaming
          • Custom Broker Adapter
          • Grid Search MA Crossover
          • Random Search vs Grid
          • Bayesian Optimization (5 Params)
          • Parallel Optimization
          • Walk-Forward Analysis
      • Overview & Interactive Docs
        • Overview
        • Asset Finder
          • Overview
          • Selection Guide
          • Base Adapter
          • CCXT
          • YFinance
          • CSV
          • Polygon
          • Alpaca
          • AlphaVantage
          • Overview
          • Architecture
          • Catalog API
          • Bundle System
          • Metadata Tracking
          • Migration Guide
          • Overview
          • Data Portal
          • Polars Data Portal
          • Bar Reader
          • Daily Bars
          • Overview
          • Providers
          • Storage
          • Converters
          • FX & Caching
          • Caching
          • Optimization
          • Troubleshooting
          • Overview
          • Computation API
        • Overview
          • Overview
          • Types Reference
          • Blotter
          • Blotter System
          • Decimal Blotter
          • Execution Pipeline
          • Latency Models
          • Partial Fills
          • Order Status Tracking
          • Slippage
          • Slippage Models
          • Commissions
          • Commission Models
          • Borrow Costs & Financing
          • Order Lifecycle
          • Examples
        • Overview
          • Allocation Algorithms
          • Multi-Strategy Allocation
          • Portfolio Allocator
          • Allocators
          • Risk Management
          • Risk Metrics
          • Position Limits
          • Performance Tracking
          • Metrics
          • Order Aggregation
          • Analytics Suite
        • Overview
          • Parameter Spaces
          • Objective Functions
          • Grid Search
          • Random Search
          • Bayesian
          • Genetic
          • Overview
          • Monte Carlo
          • Noise Infusion
          • Sensitivity Analysis
        • Overview
        • Artifact Manager
        • Code Capture
        • Overview
        • Reports
        • Visualization
          • Overview
          • Overview
          • Metrics
          • VaR & CVaR
          • Drawdown
          • Overview
        • Overview
        • Production Deployment
          • Circuit Breakers
        • Overview
        • Datasource API
        • Optimization API
        • Analytics API
      • License
      • Contributing
      • Changelog
    In [ ]:
    Copied!
    """
    Performance Attribution Analysis Example
    
    This example demonstrates how to use the PerformanceAttribution class to analyze
    what drove your strategy's performance.
    
    Attribution analysis decomposes returns into:
    - Alpha: Excess return from skill
    - Beta: Market-driven returns
    - Factor exposures: Returns from factor tilts (size, value, momentum, etc.)
    - Timing: Skill in market timing
    - Selection: Skill in security selection
    """
    
    """ Performance Attribution Analysis Example This example demonstrates how to use the PerformanceAttribution class to analyze what drove your strategy's performance. Attribution analysis decomposes returns into: - Alpha: Excess return from skill - Beta: Market-driven returns - Factor exposures: Returns from factor tilts (size, value, momentum, etc.) - Timing: Skill in market timing - Selection: Skill in security selection """
    In [ ]:
    Copied!
    import numpy as np
    import pandas as pd
    
    import numpy as np import pandas as pd
    In [ ]:
    Copied!
    from rustybt.analytics.attribution import PerformanceAttribution
    
    from rustybt.analytics.attribution import PerformanceAttribution

    ============================================================================ Example 1: Basic Alpha/Beta Attribution¶

    In [ ]:
    Copied!
    print("=" * 80)
    print("Example 1: Basic Alpha/Beta Attribution")
    print("=" * 80)
    
    print("=" * 80) print("Example 1: Basic Alpha/Beta Attribution") print("=" * 80)
    In [ ]:
    Copied!
    # Create sample portfolio returns
    np.random.seed(42)
    dates = pd.date_range(start="2023-01-01", periods=252, freq="D")
    
    # Create sample portfolio returns np.random.seed(42) dates = pd.date_range(start="2023-01-01", periods=252, freq="D")
    In [ ]:
    Copied!
    # Generate benchmark returns (e.g., S&P 500)
    benchmark_returns = pd.Series(
        np.random.normal(0.0004, 0.015, 252),  # ~10% annual return, 15% vol
        index=dates,
        name="benchmark",
    )
    
    # Generate benchmark returns (e.g., S&P 500) benchmark_returns = pd.Series( np.random.normal(0.0004, 0.015, 252), # ~10% annual return, 15% vol index=dates, name="benchmark", )
    In [ ]:
    Copied!
    # Generate portfolio returns with some alpha and higher beta
    true_alpha = 0.0002  # 5% annual alpha
    true_beta = 1.3  # Higher market sensitivity
    
    # Generate portfolio returns with some alpha and higher beta true_alpha = 0.0002 # 5% annual alpha true_beta = 1.3 # Higher market sensitivity
    In [ ]:
    Copied!
    portfolio_returns = (
        true_alpha
        + true_beta * benchmark_returns
        + np.random.normal(0, 0.005, 252)  # Idiosyncratic risk
    )
    
    portfolio_returns = ( true_alpha + true_beta * benchmark_returns + np.random.normal(0, 0.005, 252) # Idiosyncratic risk )
    In [ ]:
    Copied!
    # Create backtest result DataFrame
    backtest_result = pd.DataFrame({"returns": portfolio_returns}, index=dates)
    
    # Create backtest result DataFrame backtest_result = pd.DataFrame({"returns": portfolio_returns}, index=dates)
    In [ ]:
    Copied!
    # Perform attribution analysis
    attrib = PerformanceAttribution(
        backtest_result=backtest_result, benchmark_returns=benchmark_returns
    )
    
    # Perform attribution analysis attrib = PerformanceAttribution( backtest_result=backtest_result, benchmark_returns=benchmark_returns )
    In [ ]:
    Copied!
    results = attrib.analyze_attribution()
    
    results = attrib.analyze_attribution()
    In [ ]:
    Copied!
    # Display results
    print("\nAlpha/Beta Results:")
    print("-" * 80)
    alpha_beta = results["alpha_beta"]
    print(f"Alpha (daily):        {alpha_beta['alpha']:.6f}")
    print(
        f"Alpha (annualized):   {alpha_beta['alpha_annualized']:.4f} ({float(alpha_beta['alpha_annualized']) * 100:.2f}%)"
    )
    print(f"Beta:                 {alpha_beta['beta']:.4f}")
    print(f"Alpha p-value:        {alpha_beta['alpha_pvalue']:.4f}")
    print(f"Alpha significant:    {alpha_beta['alpha_significant']}")
    print(f"Information Ratio:    {alpha_beta['information_ratio']:.4f}")
    print(f"IR (annualized):      {alpha_beta['information_ratio_annualized']:.4f}")
    print(f"R-squared:            {alpha_beta['r_squared']:.4f}")
    print(f"Tracking Error:       {alpha_beta['tracking_error']:.6f}")
    
    # Display results print("\nAlpha/Beta Results:") print("-" * 80) alpha_beta = results["alpha_beta"] print(f"Alpha (daily): {alpha_beta['alpha']:.6f}") print( f"Alpha (annualized): {alpha_beta['alpha_annualized']:.4f} ({float(alpha_beta['alpha_annualized']) * 100:.2f}%)" ) print(f"Beta: {alpha_beta['beta']:.4f}") print(f"Alpha p-value: {alpha_beta['alpha_pvalue']:.4f}") print(f"Alpha significant: {alpha_beta['alpha_significant']}") print(f"Information Ratio: {alpha_beta['information_ratio']:.4f}") print(f"IR (annualized): {alpha_beta['information_ratio_annualized']:.4f}") print(f"R-squared: {alpha_beta['r_squared']:.4f}") print(f"Tracking Error: {alpha_beta['tracking_error']:.6f}")
    In [ ]:
    Copied!
    print("\nInterpretation:")
    print(f"- Your strategy generated {float(alpha_beta['alpha_annualized']) * 100:.2f}% annual alpha")
    print(
        f"- Beta of {float(alpha_beta['beta']):.2f} means your strategy is {abs(float(alpha_beta['beta']) - 1) * 100:.0f}% more volatile than the benchmark"
    )
    if alpha_beta["alpha_significant"]:
        print("- The alpha is statistically significant (p < 0.05), suggesting real skill")
    else:
        print("- The alpha is NOT statistically significant - could be luck")
    
    print("\nInterpretation:") print(f"- Your strategy generated {float(alpha_beta['alpha_annualized']) * 100:.2f}% annual alpha") print( f"- Beta of {float(alpha_beta['beta']):.2f} means your strategy is {abs(float(alpha_beta['beta']) - 1) * 100:.0f}% more volatile than the benchmark" ) if alpha_beta["alpha_significant"]: print("- The alpha is statistically significant (p < 0.05), suggesting real skill") else: print("- The alpha is NOT statistically significant - could be luck")

    ============================================================================ Example 2: Multi-Factor Attribution (Fama-French)¶

    In [ ]:
    Copied!
    print("\n" + "=" * 80)
    print("Example 2: Multi-Factor Attribution (Fama-French 3-Factor)")
    print("=" * 80)
    
    print("\n" + "=" * 80) print("Example 2: Multi-Factor Attribution (Fama-French 3-Factor)") print("=" * 80)
    In [ ]:
    Copied!
    # Create Fama-French factor returns
    # Mkt-RF: Market minus risk-free rate
    # SMB: Small Minus Big (size factor)
    # HML: High Minus Low (value factor)
    factor_returns = pd.DataFrame(
        {
            "Mkt-RF": np.random.normal(0.0004, 0.015, 252),
            "SMB": np.random.normal(0.0001, 0.01, 252),  # Small cap premium
            "HML": np.random.normal(0.0001, 0.008, 252),  # Value premium
        },
        index=dates,
    )
    
    # Create Fama-French factor returns # Mkt-RF: Market minus risk-free rate # SMB: Small Minus Big (size factor) # HML: High Minus Low (value factor) factor_returns = pd.DataFrame( { "Mkt-RF": np.random.normal(0.0004, 0.015, 252), "SMB": np.random.normal(0.0001, 0.01, 252), # Small cap premium "HML": np.random.normal(0.0001, 0.008, 252), # Value premium }, index=dates, )
    In [ ]:
    Copied!
    # Create portfolio with known factor exposures
    true_factor_loadings = {
        "Mkt-RF": 1.1,  # Slightly more market exposure
        "SMB": 0.4,  # Tilt toward small caps
        "HML": -0.2,  # Growth tilt (negative value exposure)
    }
    true_factor_alpha = 0.0003  # 7.6% annual alpha
    
    # Create portfolio with known factor exposures true_factor_loadings = { "Mkt-RF": 1.1, # Slightly more market exposure "SMB": 0.4, # Tilt toward small caps "HML": -0.2, # Growth tilt (negative value exposure) } true_factor_alpha = 0.0003 # 7.6% annual alpha
    In [ ]:
    Copied!
    portfolio_returns_ff = (
        true_factor_alpha
        + true_factor_loadings["Mkt-RF"] * factor_returns["Mkt-RF"]
        + true_factor_loadings["SMB"] * factor_returns["SMB"]
        + true_factor_loadings["HML"] * factor_returns["HML"]
        + np.random.normal(0, 0.005, 252)
    )
    
    portfolio_returns_ff = ( true_factor_alpha + true_factor_loadings["Mkt-RF"] * factor_returns["Mkt-RF"] + true_factor_loadings["SMB"] * factor_returns["SMB"] + true_factor_loadings["HML"] * factor_returns["HML"] + np.random.normal(0, 0.005, 252) )
    In [ ]:
    Copied!
    backtest_result_ff = pd.DataFrame({"returns": portfolio_returns_ff}, index=dates)
    
    backtest_result_ff = pd.DataFrame({"returns": portfolio_returns_ff}, index=dates)
    In [ ]:
    Copied!
    # Perform factor attribution
    attrib_ff = PerformanceAttribution(
        backtest_result=backtest_result_ff, factor_returns=factor_returns
    )
    
    # Perform factor attribution attrib_ff = PerformanceAttribution( backtest_result=backtest_result_ff, factor_returns=factor_returns )
    In [ ]:
    Copied!
    results_ff = attrib_ff.analyze_attribution()
    
    results_ff = attrib_ff.analyze_attribution()
    In [ ]:
    Copied!
    # Display factor attribution results
    print("\nFactor Attribution Results:")
    print("-" * 80)
    factor_attrib = results_ff["factor_attribution"]
    print(f"Alpha (daily):        {factor_attrib['alpha']:.6f}")
    print(
        f"Alpha (annualized):   {factor_attrib['alpha_annualized']:.4f} ({float(factor_attrib['alpha_annualized']) * 100:.2f}%)"
    )
    print(f"Alpha significant:    {factor_attrib['alpha_significant']}")
    print(f"R-squared:            {factor_attrib['r_squared']:.4f}")
    
    # Display factor attribution results print("\nFactor Attribution Results:") print("-" * 80) factor_attrib = results_ff["factor_attribution"] print(f"Alpha (daily): {factor_attrib['alpha']:.6f}") print( f"Alpha (annualized): {factor_attrib['alpha_annualized']:.4f} ({float(factor_attrib['alpha_annualized']) * 100:.2f}%)" ) print(f"Alpha significant: {factor_attrib['alpha_significant']}") print(f"R-squared: {factor_attrib['r_squared']:.4f}")
    In [ ]:
    Copied!
    print("\nFactor Loadings (Exposures):")
    for factor, loading in factor_attrib["factor_loadings"].items():
        print(f"  {factor:10s}: {float(loading):7.4f}")
    
    print("\nFactor Loadings (Exposures):") for factor, loading in factor_attrib["factor_loadings"].items(): print(f" {factor:10s}: {float(loading):7.4f}")
    In [ ]:
    Copied!
    print("\nInterpretation:")
    print(f"- Market exposure: {float(factor_attrib['factor_loadings']['Mkt-RF']):.2f}x")
    if float(factor_attrib["factor_loadings"]["SMB"]) > 0:
        print(
            f"- Small cap tilt: {float(factor_attrib['factor_loadings']['SMB']):.2f} (favors small companies)"
        )
    else:
        print(
            f"- Large cap tilt: {abs(float(factor_attrib['factor_loadings']['SMB'])):.2f} (favors large companies)"
        )
    
    print("\nInterpretation:") print(f"- Market exposure: {float(factor_attrib['factor_loadings']['Mkt-RF']):.2f}x") if float(factor_attrib["factor_loadings"]["SMB"]) > 0: print( f"- Small cap tilt: {float(factor_attrib['factor_loadings']['SMB']):.2f} (favors small companies)" ) else: print( f"- Large cap tilt: {abs(float(factor_attrib['factor_loadings']['SMB'])):.2f} (favors large companies)" )
    In [ ]:
    Copied!
    if float(factor_attrib["factor_loadings"]["HML"]) > 0:
        print(
            f"- Value tilt: {float(factor_attrib['factor_loadings']['HML']):.2f} (favors value stocks)"
        )
    else:
        print(
            f"- Growth tilt: {abs(float(factor_attrib['factor_loadings']['HML'])):.2f} (favors growth stocks)"
        )
    
    if float(factor_attrib["factor_loadings"]["HML"]) > 0: print( f"- Value tilt: {float(factor_attrib['factor_loadings']['HML']):.2f} (favors value stocks)" ) else: print( f"- Growth tilt: {abs(float(factor_attrib['factor_loadings']['HML'])):.2f} (favors growth stocks)" )

    ============================================================================ Example 3: Timing Attribution (Market Timing Skill)¶

    In [ ]:
    Copied!
    print("\n" + "=" * 80)
    print("Example 3: Timing Attribution (Market Timing)")
    print("=" * 80)
    
    print("\n" + "=" * 80) print("Example 3: Timing Attribution (Market Timing)") print("=" * 80)
    In [ ]:
    Copied!
    # Create portfolio with timing skill (higher beta in up markets)
    portfolio_timing = []
    for bench_ret in benchmark_returns:
        if bench_ret > 0:
            # Higher exposure in up markets
            port_ret = 1.8 * bench_ret + np.random.normal(0, 0.005)
        else:
            # Lower exposure in down markets
            port_ret = 0.7 * bench_ret + np.random.normal(0, 0.005)
        portfolio_timing.append(port_ret)
    
    # Create portfolio with timing skill (higher beta in up markets) portfolio_timing = [] for bench_ret in benchmark_returns: if bench_ret > 0: # Higher exposure in up markets port_ret = 1.8 * bench_ret + np.random.normal(0, 0.005) else: # Lower exposure in down markets port_ret = 0.7 * bench_ret + np.random.normal(0, 0.005) portfolio_timing.append(port_ret)
    In [ ]:
    Copied!
    backtest_result_timing = pd.DataFrame({"returns": portfolio_timing}, index=dates)
    
    backtest_result_timing = pd.DataFrame({"returns": portfolio_timing}, index=dates)
    In [ ]:
    Copied!
    # Perform timing attribution
    attrib_timing = PerformanceAttribution(
        backtest_result=backtest_result_timing, benchmark_returns=benchmark_returns
    )
    
    # Perform timing attribution attrib_timing = PerformanceAttribution( backtest_result=backtest_result_timing, benchmark_returns=benchmark_returns )
    In [ ]:
    Copied!
    results_timing = attrib_timing.analyze_attribution()
    
    results_timing = attrib_timing.analyze_attribution()
    In [ ]:
    Copied!
    # Display timing results
    print("\nTiming Attribution Results:")
    print("-" * 80)
    timing = results_timing["timing"]
    print(f"Timing coefficient:   {timing['timing_coefficient']:.6f}")
    print(f"Timing p-value:       {timing['timing_pvalue']:.4f}")
    print(f"Has timing skill:     {timing['has_timing_skill']}")
    print(f"Timing direction:     {timing['timing_direction']}")
    
    # Display timing results print("\nTiming Attribution Results:") print("-" * 80) timing = results_timing["timing"] print(f"Timing coefficient: {timing['timing_coefficient']:.6f}") print(f"Timing p-value: {timing['timing_pvalue']:.4f}") print(f"Has timing skill: {timing['has_timing_skill']}") print(f"Timing direction: {timing['timing_direction']}")
    In [ ]:
    Copied!
    print("\nInterpretation:")
    if timing["has_timing_skill"]:
        print("- Strategy demonstrates statistically significant market timing skill")
        print("- Positive timing coefficient indicates higher exposure in up markets")
    else:
        print("- No statistically significant market timing detected")
    
    print("\nInterpretation:") if timing["has_timing_skill"]: print("- Strategy demonstrates statistically significant market timing skill") print("- Positive timing coefficient indicates higher exposure in up markets") else: print("- No statistically significant market timing detected")

    ============================================================================ Example 4: Rolling Attribution (Time-Varying Analysis)¶

    In [ ]:
    Copied!
    print("\n" + "=" * 80)
    print("Example 4: Rolling Attribution Analysis")
    print("=" * 80)
    
    print("\n" + "=" * 80) print("Example 4: Rolling Attribution Analysis") print("=" * 80)
    In [ ]:
    Copied!
    # Use the basic portfolio from Example 1
    # Perform rolling attribution with 60-day window
    attrib_rolling = PerformanceAttribution(
        backtest_result=backtest_result, benchmark_returns=benchmark_returns
    )
    
    # Use the basic portfolio from Example 1 # Perform rolling attribution with 60-day window attrib_rolling = PerformanceAttribution( backtest_result=backtest_result, benchmark_returns=benchmark_returns )
    In [ ]:
    Copied!
    results_rolling = attrib_rolling.analyze_attribution()
    
    results_rolling = attrib_rolling.analyze_attribution()
    In [ ]:
    Copied!
    if "rolling" in results_rolling:
        rolling = results_rolling["rolling"]
        print("\nRolling Attribution (60-day window):")
        print("-" * 80)
        print(f"Rolling alpha - Mean:  {rolling['rolling_alpha'].mean():.6f}")
        print(f"Rolling alpha - Std:   {rolling['rolling_alpha'].std():.6f}")
        print(f"Rolling beta - Mean:   {rolling['rolling_beta'].mean():.4f}")
        print(f"Rolling beta - Std:    {rolling['rolling_beta'].std():.4f}")
        print(f"Rolling IR - Mean:     {rolling['rolling_information_ratio'].mean():.4f}")
    
        print("\nInterpretation:")
        alpha_std = float(rolling["rolling_alpha"].std())
        if alpha_std > 0.001:
            print(f"- Alpha varies significantly over time (std = {alpha_std:.6f})")
            print("- Performance may be period-dependent")
        else:
            print("- Alpha is relatively stable over time")
    
    if "rolling" in results_rolling: rolling = results_rolling["rolling"] print("\nRolling Attribution (60-day window):") print("-" * 80) print(f"Rolling alpha - Mean: {rolling['rolling_alpha'].mean():.6f}") print(f"Rolling alpha - Std: {rolling['rolling_alpha'].std():.6f}") print(f"Rolling beta - Mean: {rolling['rolling_beta'].mean():.4f}") print(f"Rolling beta - Std: {rolling['rolling_beta'].std():.4f}") print(f"Rolling IR - Mean: {rolling['rolling_information_ratio'].mean():.4f}") print("\nInterpretation:") alpha_std = float(rolling["rolling_alpha"].std()) if alpha_std > 0.001: print(f"- Alpha varies significantly over time (std = {alpha_std:.6f})") print("- Performance may be period-dependent") else: print("- Alpha is relatively stable over time")

    ============================================================================ Example 5: Visualization¶

    In [ ]:
    Copied!
    print("\n" + "=" * 80)
    print("Example 5: Attribution Visualizations")
    print("=" * 80)
    
    print("\n" + "=" * 80) print("Example 5: Attribution Visualizations") print("=" * 80)

    Generate visualizations (commented out to avoid showing plots in this example) Uncomment these lines to see the plots

    In [ ]:
    Copied!
    print("\nGenerating attribution visualizations...")
    
    print("\nGenerating attribution visualizations...")
    In [ ]:
    Copied!
    # 1. Attribution waterfall chart
    # fig1 = attrib.plot_attribution_waterfall(results)
    # fig1.savefig('attribution_waterfall.png', dpi=300, bbox_inches='tight')
    print("- Waterfall chart: Shows decomposition of total return into alpha + beta")
    
    # 1. Attribution waterfall chart # fig1 = attrib.plot_attribution_waterfall(results) # fig1.savefig('attribution_waterfall.png', dpi=300, bbox_inches='tight') print("- Waterfall chart: Shows decomposition of total return into alpha + beta")
    In [ ]:
    Copied!
    # 2. Rolling attribution time series
    # fig2 = attrib_rolling.plot_rolling_attribution(results_rolling)
    # fig2.savefig('rolling_attribution.png', dpi=300, bbox_inches='tight')
    print("- Rolling attribution: Shows how alpha and beta evolve over time")
    
    # 2. Rolling attribution time series # fig2 = attrib_rolling.plot_rolling_attribution(results_rolling) # fig2.savefig('rolling_attribution.png', dpi=300, bbox_inches='tight') print("- Rolling attribution: Shows how alpha and beta evolve over time")
    In [ ]:
    Copied!
    # 3. Factor exposures bar chart
    # fig3 = attrib_ff.plot_factor_exposures(results_ff)
    # fig3.savefig('factor_exposures.png', dpi=300, bbox_inches='tight')
    print("- Factor exposures: Shows factor loadings (tilts)")
    
    # 3. Factor exposures bar chart # fig3 = attrib_ff.plot_factor_exposures(results_ff) # fig3.savefig('factor_exposures.png', dpi=300, bbox_inches='tight') print("- Factor exposures: Shows factor loadings (tilts)")
    In [ ]:
    Copied!
    print("\nVisualization files saved successfully!")
    
    print("\nVisualization files saved successfully!")

    ============================================================================ Example 6: Interpreting Results for Different Strategies¶

    In [ ]:
    Copied!
    print("\n" + "=" * 80)
    print("Example 6: How to Interpret Attribution Results")
    print("=" * 80)
    
    print("\n" + "=" * 80) print("Example 6: How to Interpret Attribution Results") print("=" * 80)
    In [ ]:
    Copied!
    print(
        """
    KEY METRICS AND INTERPRETATION:
    
    1. ALPHA (alpha)
       - What it means: Excess return beyond what's explained by risk factors
       - Good values: Positive and statistically significant (p < 0.05)
       - Interpretation:
         * alpha > 0 and significant: Strategy adds value (skill)
         * alpha ~= 0 or not significant: Returns explained by factor exposures
         * alpha < 0: Strategy destroys value
    
    2. BETA (β)
       - What it means: Sensitivity to benchmark/market movements
       - Typical values:
         * β = 1.0: Moves with market
         * β > 1.0: More volatile than market (aggressive)
         * β < 1.0: Less volatile than market (defensive)
       - Interpretation:
         * High beta: Amplifies market returns (both up and down)
         * Low beta: Dampens market returns
    
    3. INFORMATION RATIO (IR)
       - What it means: Risk-adjusted alpha (alpha / tracking error)
       - Good values: IR > 0.5 is good, IR > 1.0 is excellent
       - Interpretation:
         * Measures consistency of outperformance
         * Higher IR = more consistent alpha generation
    
    4. FACTOR LOADINGS
       - What it means: Exposure to systematic risk factors
       - Examples:
         * SMB > 0: Small cap tilt
         * HML > 0: Value tilt
         * Mom > 0: Momentum tilt
       - Interpretation:
         * Positive loading: Returns increase when factor performs well
         * Negative loading: Returns increase when factor performs poorly
    
    5. TIMING COEFFICIENT (gamma)
       - What it means: Market timing ability
       - Good values: gamma > 0 and statistically significant
       - Interpretation:
         * gamma > 0: Higher exposure in up markets (good timing)
         * gamma < 0: Lower exposure in up markets (poor timing)
    
    COMMON SCENARIOS:
    
    Scenario 1: High Alpha, Beta ≈ 1
    → Strategy generates consistent excess returns with market-like risk
    → Ideal for long-term investing
    
    Scenario 2: Low Alpha, Beta > 1
    → Strategy is just a leveraged bet on the market
    → No skill, just higher risk
    
    Scenario 3: Significant Factor Loadings, Low Alpha
    → Returns explained by factor exposures
    → Could replicate with factor ETFs
    
    Scenario 4: Positive Timing Coefficient
    → Strategy successfully adjusts exposure based on market conditions
    → Valuable skill, especially in volatile markets
    
    ACTIONABLE INSIGHTS:
    
    1. If alpha is not significant:
       - Re-examine strategy logic
       - Check if returns are just from factor bets
       - Consider lower transaction costs
    
    2. If beta is too high:
       - Reduce position sizes
       - Add hedges
       - Use market-neutral strategies
    
    3. If factor loadings are unintended:
       - Adjust portfolio construction
       - Use factor-neutral screening
       - Rebalance more frequently
    
    4. If timing coefficient is negative:
       - Avoid trying to time the market
       - Use consistent position sizing
       - Focus on security selection instead
    """
    )
    
    print( """ KEY METRICS AND INTERPRETATION: 1. ALPHA (alpha) - What it means: Excess return beyond what's explained by risk factors - Good values: Positive and statistically significant (p < 0.05) - Interpretation: * alpha > 0 and significant: Strategy adds value (skill) * alpha ~= 0 or not significant: Returns explained by factor exposures * alpha < 0: Strategy destroys value 2. BETA (β) - What it means: Sensitivity to benchmark/market movements - Typical values: * β = 1.0: Moves with market * β > 1.0: More volatile than market (aggressive) * β < 1.0: Less volatile than market (defensive) - Interpretation: * High beta: Amplifies market returns (both up and down) * Low beta: Dampens market returns 3. INFORMATION RATIO (IR) - What it means: Risk-adjusted alpha (alpha / tracking error) - Good values: IR > 0.5 is good, IR > 1.0 is excellent - Interpretation: * Measures consistency of outperformance * Higher IR = more consistent alpha generation 4. FACTOR LOADINGS - What it means: Exposure to systematic risk factors - Examples: * SMB > 0: Small cap tilt * HML > 0: Value tilt * Mom > 0: Momentum tilt - Interpretation: * Positive loading: Returns increase when factor performs well * Negative loading: Returns increase when factor performs poorly 5. TIMING COEFFICIENT (gamma) - What it means: Market timing ability - Good values: gamma > 0 and statistically significant - Interpretation: * gamma > 0: Higher exposure in up markets (good timing) * gamma < 0: Lower exposure in up markets (poor timing) COMMON SCENARIOS: Scenario 1: High Alpha, Beta ≈ 1 → Strategy generates consistent excess returns with market-like risk → Ideal for long-term investing Scenario 2: Low Alpha, Beta > 1 → Strategy is just a leveraged bet on the market → No skill, just higher risk Scenario 3: Significant Factor Loadings, Low Alpha → Returns explained by factor exposures → Could replicate with factor ETFs Scenario 4: Positive Timing Coefficient → Strategy successfully adjusts exposure based on market conditions → Valuable skill, especially in volatile markets ACTIONABLE INSIGHTS: 1. If alpha is not significant: - Re-examine strategy logic - Check if returns are just from factor bets - Consider lower transaction costs 2. If beta is too high: - Reduce position sizes - Add hedges - Use market-neutral strategies 3. If factor loadings are unintended: - Adjust portfolio construction - Use factor-neutral screening - Rebalance more frequently 4. If timing coefficient is negative: - Avoid trying to time the market - Use consistent position sizing - Focus on security selection instead """ )
    In [ ]:
    Copied!
    print("\n" + "=" * 80)
    print("Example complete! Attribution analysis helps you understand:")
    print("- WHERE your returns come from (alpha vs. beta vs. factors)")
    print("- WHETHER outperformance is skill or luck")
    print("- HOW to improve your strategy (timing, selection, factor tilts)")
    print("=" * 80)
    
    print("\n" + "=" * 80) print("Example complete! Attribution analysis helps you understand:") print("- WHERE your returns come from (alpha vs. beta vs. factors)") print("- WHETHER outperformance is skill or luck") print("- HOW to improve your strategy (timing, selection, factor tilts)") print("=" * 80)
    Previous
    Allocation Algorithms
    Next
    Slippage Models
    Made with Material for MkDocs