Skip to main content

Portfolio Beta Calculation

A complete guide to measuring market sensitivity in Sable.

What is Beta?

Beta (β) measures how sensitive an asset or portfolio is to market movements. It's the cornerstone of understanding market risk.

The Fundamental Formula

β = Cov(A, B) / Var(B)

Where A is your asset/portfolio and B is the market benchmark (SPY).

Beta Interpretation

Beta ValueMeaningExample
β = 1.0Moves in lockstep with the marketIf SPY rises 1%, expect 1% gain
β > 1.0More volatile than the marketβ = 1.5 means 50% more volatile
β < 1.0Less volatile than the marketβ = 0.5 means 50% less volatile
β < 0Moves inversely to the marketRare (e.g., inverse ETFs)
Key Point

Beta is always calculated relative to a benchmark. In Sable, we use SPY (S&P 500 ETF) as the market proxy.


Four Levels of Beta Calculation

Sable calculates beta at four distinct levels, each serving a different purpose in risk management.

Entity Level

Account Level

Strategy Level

Position Level

Entity Level

  • Aggregates: All accounts within an entity
  • Use case: Overall organizational market exposure
  • Formula: Return = (Entity NAV_t - Entity NAV_t-1 - CashFlows) / Entity NAV_t-1

Account Level

  • Aggregates: All positions within an account
  • Use case: Individual account risk profile
  • Formula: Return = (Account NAV_t - Account NAV_t-1 - CashFlows) / Account NAV_t-1

Strategy Level

  • Aggregates: All positions within a strategy
  • Use case: Strategy-specific market sensitivity
  • Formula: Return = (Strategy NAV_t - Strategy NAV_t-1 - CashFlows) / Strategy NAV_t-1

Position Level

  • Individual: Single security beta
  • Use case: Stock-specific market sensitivity
  • Formula: β = Cov(R_position, R_SPY) / Var(R_SPY)
Key Difference

Entity, Account, and Strategy levels use realized portfolio returns (actual P&L / NAV). Position level uses individual security returns from price data.


Calculation Methodology

Portfolio Return Calculation (Time-Weighted Return)

For any aggregation level (Entity, Account, Strategy):

Return_t = (NAV_t - NAV_t-1 - NetCashFlows_t) / NAV_t-1

Components:

ComponentDescription
NAV_tNet Asset Value at end of day t
NAV_t-1Net Asset Value at end of previous day
NetCashFlows_tDeposits (+) minus Withdrawals (-) during day t
Why Time-Weighted Return (TWR)?

TWR removes the impact of external cash flows (deposits/withdrawals) to show pure investment performance. This makes your returns directly comparable to SPY, which has no external cash flows.

Then Calculate Beta

Once you have daily returns for your portfolio and SPY:

β = Cov(Return_Portfolio, Return_SPY) / Var(Return_SPY)

Lookback Period Options:

PeriodDaysUse Case
Default252 trading days~1 year
Minimum60 trading days~3 months for statistical significance
Options63d, 126d, 252d, 504dRolling windows

Why This Approach is Superior

Old Method: Weighted Average (Incorrect)

β_portfolio = Σ(w_i × β_i)

Issues:

  • Uses current weights with historical betas
  • Doesn't capture actual trading activity
  • Ignores positions sold during period
  • Assumes constant allocation
  • Hypothetical, not realized

New Method: Realized Returns (Correct)

β = Cov(Actual Returns, SPY) / Var(SPY)

Benefits:

  • Based on actual realized returns
  • Captures all trading activity
  • Includes positions held during period
  • Handles time-varying weights
  • True historical sensitivity

Worked Examples

Example 1: Account-Level Beta

Given Data:

DateAccount NAVCash FlowsSPY Price
Day 0$1,000,000-$450.00
Day 1$1,012,000$0$454.50
Day 2$1,030,000$10,000$458.59
Day 3$1,025,000$0$456.13

Step 1: Calculate Daily Returns

Day 1 Account Return = (1,012,000 - 1,000,000 - 0) / 1,000,000 = 1.2%
Day 1 SPY Return = (454.50 - 450.00) / 450.00 = 1.0%

Day 2 Account Return = (1,030,000 - 1,012,000 - 10,000) / 1,012,000 = 0.79%
Day 2 SPY Return = (458.59 - 454.50) / 454.50 = 0.9%

Day 3 Account Return = (1,025,000 - 1,030,000 - 0) / 1,030,000 = -0.49%
Day 3 SPY Return = (456.13 - 458.59) / 458.59 = -0.54%

Step 2: Calculate Covariance and Variance

Account Returns: [1.2%, 0.79%, -0.49%]
SPY Returns: [1.0%, 0.9%, -0.54%]

Cov(Account, SPY) = Calculate covariance
Var(SPY) = Calculate variance of SPY returns

Step 3: Calculate Beta

β = Cov(Account, SPY) / Var(SPY)

With more data points (252 days), this gives you the account's actual market sensitivity.

Example 2: Strategy-Level Beta Comparison

Portfolio Composition:

StrategyNAVCalculated BetaInterpretation
Growth Equity$5,000,0001.35Aggressive - 35% more volatile than market
Dividend Value$3,000,0000.72Defensive - 28% less volatile than market
Long/Short$2,000,0000.15Market neutral - minimal market exposure

Entity-Level Beta Calculation:

Instead of weighted average, we calculate entity returns from actual P&L:

Entity Return_t = (Entity NAV_t - Entity NAV_t-1 - CashFlows_t) / Entity NAV_t-1

Then: β_Entity = Cov(Entity Returns, SPY Returns) / Var(SPY Returns)

Result: Entity beta reflects actual blended exposure, accounting for how strategies performed together, rebalancing, and any strategy-level interactions.

Example 3: Impact of Cash Flows

Scenario: Large Deposit Mid-Period

Without TWR Adjustment (WRONG):

Day 1: NAV = $1,000,000 → $1,020,000 (2% gain from trading)
Day 2: Deposit $500,000 → NAV = $1,520,000
Day 3: NAV = $1,520,000 → $1,535,200 (1% gain from trading)

Wrong Return = (1,535,200 - 1,000,000) / 1,000,000 = 53.52% ❌
(This includes the $500K deposit!)

With TWR Adjustment (CORRECT):

Day 1 Return = (1,020,000 - 1,000,000 - 0) / 1,000,000 = 2.0%
Day 2 Return = (1,520,000 - 1,020,000 - 500,000) / 1,020,000 = 0.0%
Day 3 Return = (1,535,200 - 1,520,000 - 0) / 1,520,000 = 1.0%

Correct: Only trading gains/losses, no deposit impact ✅
Why This Matters

Without removing cash flows, deposits would artificially inflate returns and withdrawals would deflate them. Beta would be meaningless.


Critical Issues Fixed

Issue #1: Short Beta Formula

Old (Incorrect):

Short β = Σ(|MV_i| × β_i) / Σ|MV_i|

Problem: Treated shorts as adding market exposure instead of reducing it.

Example: Short $1M stock with β=1.5
Old calc: Showed +$1.5M exposure ❌
Should be: -$1.5M exposure

New (Correct):

Short β = Σ(MV_i × β_i) / Σ|MV_i|

Fix: Removed absolute value from numerator. MV_i is negative for shorts.

Example: Short $1M stock with β=1.5
MV = -$1,000,000
Numerator: -$1,000,000 × 1.5 = -$1,500,000
Denominator: $1,000,000
Result: -1.5 ✅ (negative exposure as expected)

Issue #2: GMV Denominator Problem

Using Gross Market Value (GMV) made beta incomparable to traditional metrics.

Example Portfolio:

PositionMarket ValueBetaMarket Exposure
Long positions$100M1.2+$120M
Short positions-$80M1.0-$80M
Net$20M (NMV)-$40M
Gross$180M (GMV)--

Old: Using GMV

β = $40M / $180M = 0.22

Problem: Not comparable to standard beta.

New: Using NMV (Capital)

β = $40M / $20M = 2.0

Interpretation: If market moves 1%, expect 2% return on capital.

Solution: Dual Reporting
  • Primary: Beta on Capital (NMV-based) - standard interpretation
  • Secondary: Beta on Exposure (GMV-based) - for granular analysis
  • Edge case: Market-neutral portfolios (NMV ≈ 0) show warning and rely on exposure metric

Issue #3: Missing SPY Data

Old implementation silently excluded days with missing SPY data. This is a data integrity failure.

Hard Stop Required

SPY is the most liquid ETF in the world. Missing data indicates:

  • Broken data feed
  • Calendar error
  • Pipeline failure

New Behavior:

IF missing_spy_days > 0:
RAISE EXCEPTION:
"BETA CALCULATION BLOCKED: Missing SPY data for X trading days: [dates]
Fix data pipeline before proceeding."

Forces immediate fix rather than working around bad data.

Enhancement #1: Confidence Intervals

Beta is an estimate with uncertainty. Now we can optionally show:

MetricFormula
Standard ErrorSE(β) = √[(1-R²)σ²_asset / (σ²_market(n-2))]
95% CIβ ± 1.96 × SE(β)

Wide confidence intervals flag positions where beta estimate is unreliable.

Enhancement #2: Rolling Windows

Different time horizons for different use cases:

PeriodDaysUse CaseCharacteristics
1 Year252Industry standardBalanced, comparable
6 Months126Tactical allocationMore responsive
3 Months63Short-term trendsQuick to adapt, noisier
2 Years504Long-term stableSmooth, strategic

EWMA Alternative: Exponentially Weighted Moving Average (λ = 0.94) weights recent observations more heavily without hard cutoff. Industry standard for risk management.


Implementation Requirements

Data Requirements

Required Tables:

  • sable.nav_daily - Daily NAV by entity/account/strategy
  • sable.cash_flows_daily - Daily deposits/withdrawals
  • sable.instrument_price_daily - SPY prices (must be complete)
  • sable.calendar_date - Trading day calendar

Required Columns for nav_daily:

  • date, org_id
  • entity_id, account_id, strategy_id
  • nav (decimal)

Required Columns for cash_flows_daily:

  • date, org_id
  • entity_id, account_id, strategy_id
  • net_cash_flow (decimal, positive for deposits)

Database Function Signature

CREATE OR REPLACE FUNCTION sable.f_calculate_portfolio_beta_v2(
p_org_id UUID,
p_entity_id UUID DEFAULT NULL,
p_account_id UUID DEFAULT NULL,
p_strategy_id UUID DEFAULT NULL,
p_lookback_days INTEGER DEFAULT 252,
p_min_trading_days INTEGER DEFAULT 60,
p_benchmark_instrument_id UUID DEFAULT NULL,
p_include_confidence_intervals BOOLEAN DEFAULT FALSE,
p_rolling_window_days INTEGER DEFAULT NULL
)
RETURNS TABLE (
level TEXT, -- 'entity', 'account', 'strategy', 'position'
id UUID,
name TEXT,
beta NUMERIC,
beta_std_error NUMERIC,
beta_ci_lower NUMERIC,
beta_ci_upper NUMERIC,
correlation NUMERIC,
r_squared NUMERIC,
trading_days INTEGER,
data_quality_warning TEXT
)

Implementation Phases

Phase 1: Critical Fixes (BLOCKER)

  1. Fix short beta formula (remove numerator absolute value)
  2. Implement dual beta reporting (capital vs exposure)
  3. Add hard stop on missing SPY data
  4. Document portfolio beta timing caveat

Timeline: Must complete before next release

Phase 2: Enhancements (NEXT SPRINT)

  1. Implement NAV-based portfolio beta calculation
  2. Add rolling window selector (63d, 126d, 252d, 504d)
  3. Add optional confidence intervals toggle
  4. Implement EWMA beta overlay

Timeline: 2-3 weeks

Phase 3: Future Improvements

  1. Multi-factor models (Fama-French 3-factor)
  2. Options delta integration
  3. Regime-conditional beta (crisis vs normal)
  4. Custom benchmarks beyond SPY

Testing Strategy

Unit Tests:

  • Short positions reduce market exposure (negative contribution)
  • Beta on capital vs exposure differ for leveraged portfolios
  • Missing SPY data raises exception
  • Market-neutral portfolios handle gracefully
  • Confidence intervals widen with fewer observations
  • TWR correctly removes cash flow impact

Integration Tests:

  • Entity beta aggregates all account betas correctly
  • Rolling windows (63d, 126d, 252d) produce different results
  • EWMA weights recent data appropriately
  • Time-series beta evolves logically

Data Quality Tests:

  • Compare to Bloomberg/FactSet published betas (±10% tolerance)
  • Out-of-sample prediction accuracy
  • Backtest: High beta predicts higher volatility
Migration Note

Existing beta calculations will show different values after this update. This is expected and correct.

Communication plan: Notify users that beta calculations now use realized returns instead of weighted averages, providing more accurate market sensitivity measurements.


Quick Reference

ConceptFormula/Value
Core Formulaβ = Cov(A, B) / Var(B) where B = SPY
Portfolio ReturnsR = (NAV_t - NAV_t-1 - CF) / NAV_t-1
β = 1.0Matches market
β = 1.550% more volatile
β = 0.550% less volatile
β < 0Inverse to market

Four Calculation Levels: Entity → Account → Strategy → Position

Key Takeaway

Beta measures market sensitivity. By calculating it from actual realized returns at multiple organizational levels, we get a true picture of risk rather than a hypothetical estimate.