Case study · Backtest
🗓 Backtest period: 2018-01-01..2023-01-01
Spec ID: spec-sector-momentum-rotation-etf-top50-1767027941 · Generated: 2026-09-08 11:05 UTC
Cluster: Momentum · Sub Cluster: Multi-Horizon Sector Relative-Strength Rotation
Each month, the strategy ranks eligible U.S. sector ETFs by composite 3-, 6-, and 12-month relative strength versus SPY. It holds the top three at equal weight, subject to liquidity, history, and 40% position limits.
Sector returns can persist because economic cycles, institutional reallocations, fund flows, and gradual information diffusion affect industries over multiple months. Ranking each sector against SPY seeks to isolate leadership relative to the broad equity market rather than simply rewarding a rising market.
Combining standardized 63-, 126-, and 252-day signals reduces dependence on a single formation horizon. Monthly rebalancing aims to capture medium-term persistence while limiting unnecessary turnover, although momentum remains exposed to sharp leadership reversals and crowded exits.
[code omitted from public view] The configuration requires at least three positions and does not specify a cash fallback when fewer than three candidates qualify.
| Param | Value | Notes |
|---|---|---|
| Rule type | SECTOR_MOMENTUM | Long-only sector rotation |
| Relative-strength definition | total_return(etf,LB)/total_return(benchmark,LB) |
Benchmark-relative signal |
| Lookbacks | 63, 126, 252 trading days | Approximately 3, 6, and 12 months |
| Composite score | mean_zscore_across_lookbacks |
Cross-sectional z-scores averaged across horizons |
| Benchmark | SPY&US&ETF |
Used for every relative-strength horizon |
| Selection count | 3 | Highest composite scores |
| Position sizing | Equal weight | Subject to position and sector caps |
| Rebalance schedule | Monthly; first trading day | Turnover threshold is 0.0 |
| Signal lag | 1 day | Rule configuration specifies trade_on: next_open; the recorded platform execution convention is daily-bar MOC |
| Gross leverage | 1.0 | Fully invested, unlevered target |
| Position/sector cap | 0.4 | Maximum 40% in one position or sector |
| Minimum positions | 3 | cash_if_fewer_candidates: false |
| Stop loss | None | stop_loss_pct: null |
| Volatility targeting | False | No dynamic risk scaling |
| Price-history rule | 252 full days | Drop candidates missing any required lookback |
| Liquidity rule | 250,000 shares average daily volume | Measured over 63 trading days |
| Sector filter | GICS 11 sector ETFs | Provider metadata or name matching; Sector Equity category fallback |
| Universe | Top 50 ETFs by capitalization | 50 supplied symbols; sector filter is applied afterward |
| Backtest bar | 1 day | Daily data |
| Backtest window | 2018-01-01..2023-01-01 | No walk-forward analysis specified |
| Commissions | $0.0040 per share, minimum $1.00 per order, capped at 1.00% of trade value | Charged per fill by the results module; all metrics are net of them |
| Slippage | 0 bps — not applied | Not an omission — MOC (market-on-close) fills at the auction print the backtest uses |
| Costs not modelled | short borrow fees / rebate, margin financing on leverage, market impact, exchange/regulatory/clearing pass-through fees, taxes | Excluded deliberately, not unknown |
| Execution | daily bars, MOC (market-on-close) by default | Official auction-print convention |
| Minimum order notional | $0.00 | No minimum-notional restriction |
| # | Concern | Status |
|---|---|---|
| 1 | Signal timestamp versus execution | ✓ Signals use a one-day lag and the recorded daily-bar execution convention is MOC at the official auction print |
| 2 | Incomplete momentum histories | ✓ A full 252-day history and all three lookbacks are required |
| 3 | Point-in-time ETF universe and sector metadata | ⚠ Annual universe refresh is indicated, but the supplied record does not establish fully point-in-time constituents and classifications |
| 4 | Benchmark synchronization | ✓ SPY and ETF returns use the same daily lookback structure; calendar and corporate-action alignment should still be regression-tested |
| 5 | Trading costs | ✓ commissions $0.0040/share (min $1.00/order); slippage 0 bps — not applied |
| 6 | Ranking information set | ✓ Only trailing price and volume fields are used; no fundamental release-timing issue applies |
From 2018-01-01 through 2023-01-01, the commission-net backtest produced a 50.67% total return with a 0.71 Sharpe ratio, 0.86 Sortino ratio, 14.28% volatility, and a -22.45% maximum drawdown. The 66.67% win rate and 4.85 profit factor are favorable, but they are based on only 24 trades and should not be treated as stable estimates.
Recomputed here from the run's stored daily returns, because every annualized figure in the table above is derived from the LENGTH of that array (years = n / 252). Only total return and max drawdown do not depend on the row count, so when the recomputed and stored values differ by a common factor, those two are the numbers to trust.
| Check | From the run's own returns | Note |
|---|---|---|
| Daily-return rows | 1,259 | 1.00x the 1,260 trading days in 2018-01-01..2023-01-01 |
| Distinct dates | 1,259 | one row per date |
| Date span | 2018-01-02 .. 2022-12-30 | |
| Sum of daily returns | 50.67% | matches the reported total return |
| Sharpe from these rows | 0.71 | stored 0.71 |
| Volatility from these rows | 14.28% | stored 14.28% |
| Max drawdown from these rows | -22.45% | stored -22.45% |
| CAGR from these rows | 8.55% | stored 8.55% |
| Metric | Value |
|---|---|
| Total Return | 50.67% |
| Sharpe | 0.71 |
| Sortino | 0.86 |
| Calmar | 0.38 |
| Max Drawdown | -22.45% |
| Volatility | 14.28% |
| Beta vs SPY | 0.54 |
| Win Rate | 66.67% |
| Profit Factor | 4.85 |
| Total Trades | 24 |
| Symbols | 50 (AGG, BIL, BIV, BND, BNDX, BSV, DIA, EFA, GLD, IEFA, +40 more) |