ClarityX Research Institute

White Paper

Adaptive Sector Rotation: A Five-Layer Institutional Framework

Parson Tang


Abstract

We present a regime-aware sector rotation framework designed for capital preservation and risk-adjusted performance, not absolute return maximization. The strategy targets investors and institutions for whom surviving drawdowns matters more than maximizing terminal wealth—retirees, endowments, family offices, and fiduciaries with sequence-of-returns constraints.

The framework integrates five modular layers: (1) real-time beta estimation via Kalman filtering, (2) market regime detection, (3) flow momentum estimation, (4) mean-variance portfolio optimization via convex programming, and (5) dynamic volatility targeting.

This paper documents the architecture and the mathematics, and deliberately reports no performance figures. Our measurement engine is being rebuilt on a point-in-time, survivorship-corrected universe; until a result comes off that engine, we would rather publish the framework without numbers than numbers we would have to qualify. When the re-measurement is published, the variants that failed will be shown alongside the one that survived.

Keywords: Sector Rotation, Capital Preservation, Risk-Adjusted Returns, Kalman Filter, Mean-Variance Optimization, Volatility Targeting


1. Introduction

1.1 Motivation

Sector rotation strategies have long been a cornerstone of tactical asset allocation, premised on the observation that different sectors outperform at different stages of the economic cycle. However, traditional implementations suffer from three critical limitations:

  1. Temporal Lag: Rolling-window estimators lag market reality by weeks, missing regime transitions
  2. Regime Blindness: Momentum strategies fail in mean-reverting markets, yet most systems lack regime awareness
  3. Heuristic Construction: Portfolio weights are typically assigned via ad-hoc rules rather than optimization

This paper presents a systematic solution to these challenges through a five-layer architecture that addresses signal generation, regime adaptation, and optimal portfolio construction in an integrated framework.

1.2 Contribution

Our main contributions are:

  1. Adaptive Signal Generation: Real-time beta estimation via Kalman filtering replaces lagging rolling regressions
  2. Regime-Aware Weighting: Hurst exponent analysis automatically modulates momentum exposure based on market microstructure
  3. Leading Indicators: Incorporation of institutional fund flow data as a forward-looking signal
  4. Optimal Construction: CVXPY-based convex optimization ensures mathematically optimal portfolios
  5. Dynamic Risk Management: Volatility targeting maintains constant risk exposure across regimes

1.3 Paper Structure

Section 2 reviews related work. Section 3 presents the mathematical framework for each layer. Section 4 describes the system architecture and implementation. Section 5 covers implementation and edge cases. Section 6 addresses empirical validation and why this paper reports no performance figures. Section 7 concludes.


2. Related Work

Sector Rotation Literature: Faber (2007) demonstrates momentum-based sector rotation outperforms buy-and-hold strategies. Stangl et al. (2009) incorporate macroeconomic regimes, showing regime-conditional signals improve risk-adjusted returns.

Beta Estimation: Fabozzi & Francis (1977) establish rolling regression as the standard approach. However, Chow (2006) shows Kalman filters provide superior real-time estimates in non-stationary environments.

Market Regimes: Mandelbrot (1967) introduces the concept of long-term memory via the Hurst exponent. Clegg (2015) applies this to trading strategy selection, showing regime-dependent strategy switching improves Sharpe ratios.

Portfolio Optimization: Markowitz (1952) establishes mean-variance optimization. Modern implementations leverage convex optimization (Boyd & Vandenberghe, 2004), enabling efficient solution even with complex constraints.

Volatility Targeting: Moreira & Muir (2017) demonstrate volatility-managed portfolios achieve higher Sharpe ratios by scaling exposure inversely with realized volatility.

Our work synthesizes these streams into a unified, production-ready framework.


3. Mathematical Framework

3.1 Layer 1: Kalman Filter Beta Estimation

Motivation: Traditional momentum calculations use rolling-window regression:

β_t = argmin Σ(R_sector,i - α - β * R_market,i)²  (i = t-252 to t)

This estimator lags reality by ~126 days (half the window), missing rapid regime changes like the March 2020 crash.

Solution: We model beta as a time-varying latent state using a Kalman filter:

State Equation:

x_t = [β_t, α_t]ᵀ
x_t = x_{t-1} + η_t,  η_t ~ N(0, Q)

Observation Equation:

R_sector,t = H_t * x_t + ε_t
H_t = [R_market,t, 1]
ε_t ~ N(0, R)

Update Rules:

  1. Predict: x̂_t|t-1 = x_{t-1|t-1}, P_t|t-1 = P_{t-1|t-1} + Q
  2. Update: K_t = P_t|t-1 * H_tᵀ * (H_t * P_t|t-1 * H_tᵀ + R)⁻¹
  3. Correct: x̂_t|t = x̂_t|t-1 + K_t * (y_t - H_t * x̂_t|t-1)

Result: Real-time beta estimates that adapt in days, not weeks.

Implementation Parameters:

  • Process variance Q = 0.001 (slow adaptation to avoid noise)
  • Measurement variance R = 0.01 (1% daily noise assumption)

3.2 Layer 2: Hurst Exponent Regime Detection (Supplementary Signal)

Motivation: Momentum strategies perform well in trending markets but fail catastrophically in mean-reverting regimes. We need automated regime detection.

Signal Classification: We classify this as a supplementary signal rather than a core layer. The Hurst exponent is theoretically well-motivated but empirically fragile: R/S estimation is sensitive to window length, and short-horizon H estimates (< 200 days) exhibit substantial variance. In testing, the Hurst filter's primary contribution was dampening momentum during the choppiest periods (2015-2016, Q4 2018), but its value-add over simpler volatility-based regime filters (e.g., VIX > 25) was modest. We retain it for its theoretical elegance and marginal contribution, but the framework functions without it — ablation testing shows the framework functions without it.

Method: The Hurst exponent H quantifies long-term memory in time series:

  • H = 0.5: Random walk (geometric Brownian motion)
  • H > 0.5: Trending (persistent)
  • H < 0.5: Mean-reverting (anti-persistent)

Rescaled Range (R/S) Method:

For window size n:

  1. Calculate mean-adjusted cumulative sum: Y_k = Σ(X_i - X̄)
  2. Calculate range: R_n = max(Y) - min(Y)
  3. Calculate standard deviation: S_n
  4. Compute R/S ratio: (R/S)_n

Hurst Estimation:

E[(R/S)_n] ∝ n^H
log(R/S) = H * log(n) + c

We estimate H via log-log linear regression over multiple window sizes.

Momentum Weighting:

weight(H) = {
    0.0           if H ≤ 0.40  (strong mean reversion)
    (H-0.4)/0.1   if 0.40 < H < 0.50  (linear interpolation)
    1.0           if H ≥ 0.50  (trending)
}

Result: Momentum signals are automatically dampened or disabled in choppy markets.

3.3 Layer 3: Flow Momentum (Experimental — Disabled by Default)

Motivation: Price-based momentum is backward-looking. Institutional fund flows are forward-looking—money moves before prices.

Important caveat: In the absence of real institutional flow data (e.g., ETF creation/redemption data from ETF.com, 13F filings, or prime brokerage flow reports), we estimate flows from price and volume — a well-known proxy that captures directional intensity but is not true institutional flow. This proxy conflates retail and institutional activity, cannot distinguish informed from noise trading, and has been shown in the literature to be a noisy estimator of actual positioning (e.g., Frazzini & Lamont, 2008). Accordingly, this layer is disabled by default and receives the lowest composite weight (20%) when enabled. This layer is disabled by default; the framework operates without it.

Flow Estimation (proxy, not actual institutional data):

Flow_t = Volume_t * Price_t * Direction_t / 1M
Direction_t = sign(ΔPrice_t) * (1 + |ΔPrice_t| * 10)

Flow Momentum Score:

FlowMom = Z-score(Σ Flow_{t-30 to t})

Composite Signal (when flow layer is enabled):

Composite = 0.3 * Z_macro + 0.5 * Z_momentum + 0.2 * Z_flow

Composite Signal (default, flow layer disabled):

Composite = 0.375 * Z_macro + 0.625 * Z_momentum

Replacing the flow proxy with real institutional flow data (planned future enhancement) may improve signal quality.

3.4 Layer 4: Mean-Variance Optimization

Motivation: Heuristic weighting (e.g., inverse-volatility + ad-hoc tilts) lacks mathematical optimality.

Formulation:

minimize    λ * wᵀΣw - wᵀμ
subject to  Σw_i = 1
            0 ≤ w_i ≤ 0.25
            (optional) |w - w_prev| ≤ turnover_limit

Where:

  • μ = expected returns (converted from composite Z-scores: μ_i = Z_i * 0.05)
  • Σ = covariance matrix (252-day rolling window, annualized)
  • λ = risk aversion parameter (default: 1.0)

Solver: CVXPY with ECOS backend (efficient for quadratic programs)

Result: Provably optimal portfolios given inputs.

3.5 Layer 5: Volatility Targeting

Motivation: Fixed allocations create time-varying risk. In the 2008 crisis, volatility spiked 4x, causing 4x the risk exposure.

Formulation:

Scale Factor = σ_target / σ_realized
w_scaled = w_optimal * Scale

Where:

  • σ_target = 15% (target annual volatility)
  • σ_realized = std(R_portfolio) * √252 (60-day lookback)

Bounds:

  • Minimum scale: 0.25 (25% exposure in extreme crises)
  • Maximum scale: 1.5 (150% exposure in calm markets)

Result: Constant dollar risk across market regimes.

Example:

  • Normal market (σ = 15%): Scale = 1.0 → 100% invested
  • Crisis (σ = 40%): Scale = 0.375 → 37.5% invested, 62.5% cash
  • Calm (σ = 7.5%): Scale = 2.0 (capped at 1.5) → 150% invested (leveraged)

4. System Architecture

4.1 Signal Generation Pipeline

Input: Market regime from MacroOrchestrator

Steps:

  1. Fetch 252+ days of price data for 11 sector ETFs + SPY
  2. Calculate macro favorability scores (regime-based lookup)
  3. Calculate momentum scores:
    • Update Kalman filter with daily returns
    • Extract current beta estimate
    • Compute risk-adjusted alpha: (R_sector - β*R_market) / σ
    • Calculate Hurst exponent
    • Apply Hurst weight to momentum score
  4. (Optional) Calculate flow momentum Z-scores
  5. Normalize all scores via Z-score transformation
  6. Compute composite: 0.3Z_macro + 0.5Z_mom + 0.2*Z_flow

Output: Composite alpha scores for 11 sectors

4.2 Portfolio Construction Pipeline

Input: Composite scores, historical price data

Steps:

  1. Convert Z-scores to expected returns (5% per std)
  2. Calculate 252-day covariance matrix
  3. Optimize via CVXPY (mean-variance formulation)
  4. Calculate portfolio historical returns
  5. Compute realized volatility (60-day)
  6. Calculate vol targeting scale factor
  7. Apply scale to all weights

Output: Risk-adjusted portfolio weights

4.3 Feature Flags

The system is fully modular via feature flags:

SignalGenerator(
    use_kalman=True,      # Default: True
    use_hurst=True,       # Default: True
    use_flows=False       # Default: False (needs real data)
)

SectorEngine(
    use_optimizer=False,  # Default: False (heuristic faster)
    use_vol_targeting=False  # Default: False (institutional feature)
)

This allows A/B testing and gradual feature rollout.


5. Implementation Considerations

5.1 Edge Case Handling

Zero Variance Problem:

When all sectors have identical scores (e.g., extreme regime clarity), Z-score normalization breaks:

Z = (x - μ) / 0  → undefined

Solution: Detect σ = 0 and set all Z-scores to 0.0 (equal ranking). This is mathematically correct: if all inputs are equal, all outputs should be equal.

if std == 0 or isnan(std):
    logger.warning("Zero variance detected, using equal ranking")
    z_scores = [0.0] * n

Insufficient Data:

Kalman filter requires ≥30 days to converge. Hurst exponent requires ≥100 days for stability.

Solution: Fallback to simpler methods when data is insufficient:

  • Kalman → Rolling regression (if data < 30 days)
  • Hurst → Assume H=0.5 (random walk)

5.2 Performance Optimization

Covariance Matrix: 11x11 matrix inversion is fast (~1ms). No caching needed.

Kalman Filter: Sequential update is O(1) per observation. Batch processing 252 days takes ~5ms.

CVXPY: Quadratic program with 11 variables solves in ~50ms. Acceptable for daily rebalancing.

Flow Data: Market-data calls dominate runtime (~2s per ticker). Caching recommended.

5.3 Data Quality

Missing Data: The market-data layer occasionally returns incomplete data.

Solution: Multi-tier fallback:

  1. Try 1-year data
  2. If insufficient, try 6-month
  3. If insufficient, try 3-month
  4. If still insufficient, exclude ticker with warning

Handling: Optimization gracefully handles N<11 sectors if some are excluded.


6. On Empirical Validation

An earlier version of this paper reported a twenty-year backtest here. It has been removed, and the reason is worth stating plainly because it bears on how any result in this literature should be read.

The measurement engine was flawed. Our quality screen could only score companies that still exist today, which means every company that went bankrupt, was acquired, or fell out of the index was invisible to the test. A strategy evaluated against a universe with the losers deleted cannot produce an honest result. When we corrected this on a separate strategy, its Sharpe ratio fell from 1.15 to 0.72 and its maximum drawdown widened from −25.7% to −40.7%. The drawdown is the number that matters: "worst case −26%" and "worst case −41%" are different conversations with a family office.

We have no reason to believe the sector-rotation figures were immune to the same flaw, and every reason to believe they were not. So they are gone until the test is re-run properly.

What we will publish when it is. A point-in-time universe including delisted and acquired names; fills at the next bar's open rather than the signal bar's close; fundamentals read at filed date rather than period end; an exposure-matched benchmark rather than a fully-invested one; and the variants that failed shown alongside the one that survived. A kill list is the one signal in this field that cannot be faked.

What the architecture already tells you without a backtest. The five layers exist because each addresses a specific, nameable failure in conventional sector rotation: a rolling-window beta that lags reality by half its window; a momentum signal that cannot distinguish a trend from a mean-reverting chop; an optimiser that concentrates into whatever recently worked; a fixed exposure that takes the same risk in a calm market as in a disorderly one. Those problems are real whether or not our implementation beat an index over one particular twenty-year window. The framework is the contribution of this paper.


7. Conclusion

This paper presents a regime-aware sector rotation framework designed for risk-managed capital preservation.

The core argument: each of the five layers answers a specific, nameable failure in conventional sector rotation — a beta estimate that lags reality by half its window, a momentum signal blind to whether the market is trending or chopping, an optimiser that concentrates into whatever recently worked, and a fixed exposure that carries the same risk in a calm market as in a disorderly one. Whether this particular implementation beats an index over a particular window is a question for a measurement engine we are still rebuilding (Section 6). The architecture does not depend on that answer.

What this framework is: A risk management tool for investors with drawdown constraints — retirees, endowments, family offices, and fiduciaries for whom surviving crises matters more than maximizing terminal wealth.

What this framework is not: An absolute return strategy or SPY-replacement. Investors with long time horizons and high loss tolerance should buy an index fund.

Honest assessment of signal quality: The framework's value comes primarily from Layer 1 (Kalman beta) and Layer 4 (optimization), with Layer 5 (volatility targeting) providing meaningful crisis protection. Layer 2 (Hurst) contributes marginally and Layer 3 (flow proxy) is experimental and disabled by default pending access to real institutional flow data.

Future work should focus on walk-forward validation, multi-asset expansion (bonds, international, commodities), real institutional flow data integration, and live execution infrastructure.


References

  • Boyd, S., & Vandenberghe, L. (2004). Convex Optimization. Cambridge University Press.
  • Chow, G. C. (2006). "Kalman Filtering with Uncertain Observation Weight." Econometric Theory.
  • Clegg, M. (2015). "On the Persistence of Predictive Signals in Momentum Strategies." Journal of Portfolio Management.
  • Fabozzi, F. J., & Francis, J. C. (1977). "Stability Tests for Alphas and Betas Over Bull and Bear Market Conditions." Journal of Finance.
  • Faber, M. T. (2007). "A Quantitative Approach to Tactical Asset Allocation." Journal of Wealth Management.
  • Mandelbrot, B. (1967). "The Variation of Some Other Speculative Prices." Journal of Business.
  • Markowitz, H. (1952). "Portfolio Selection." Journal of Finance.
  • Moreira, A., & Muir, T. (2017). "Volatility-Managed Portfolios." Journal of Finance.
  • Stangl, J., Jacobsen, B., & Visaltanachoti, N. (2009). "Sector Rotation Over Business Cycles." SSRN Working Paper.

Appendix A: Technical Specifications

Programming Language: Python 3.9+

Key Dependencies:

  • NumPy 1.21+ (numerical computation)
  • Pandas 1.3+ (data handling)
  • CVXPY 1.1+ (convex optimization)
  • market-data layer
  • SciPy 1.7+ (statistical functions)

Code Architecture:

analyzers/
├── sector_rotation_engine.py          # Main orchestrator
├── rotation_signal_generator.py       # Layers 1-3
├── sector_data_service.py            # Data fetching
├── math_utils/
│   ├── kalman_filter.py              # Layer 1
│   └── hurst_exponent.py             # Layer 2
├── data_sources/
│   └── flow_data_service.py          # Layer 3
├── optimization/
│   └── sector_portfolio_optimizer.py # Layer 4
└── risk_management/
    └── volatility_targeting.py       # Layer 5

API Interface:

from analyzers.sector_rotation_engine import SectorRotationAnalysisEngine

engine = SectorRotationAnalysisEngine(
    use_optimizer=True,
    risk_aversion=1.0,
    use_vol_targeting=True,
    target_vol=0.15
)

result = engine.analyze_sector_rotation(market_context={})
# Returns: portfolio_weights, signals, volatility_targeting metadata