The strategy does not use a separate stock-level indicator. It assigns 100% of the allotted capital to the stock when the market condition is satisfied, with no leverage, and moves to cash otherwise. The condition is evaluated at each day’s close, with any resulting action taken at the next day’s open.
| Component | Description |
|---|---|
| Indicator | SPY 100-day simple moving average: the average of SPY’s closing prices over the 100-day lookback period. |
| Signal | A buy or hold signal occurs when SPY closes above its own 100-day SMA. A cash signal occurs when SPY does not close above that average. |
| Rule | At the next day’s open, hold the tested stock with 100% of allotted capital when the buy or hold signal is present. Otherwise, exit the stock position and hold cash. |
| How the Rule Was Chosen (Training Data Only) | |||||
| 9 variants evaluated on 214 training tickers, 2006-01-01 to 2014-12-31; none of this data is in the test below | |||||
| Variant | Sharpe (strategy) | Sharpe (buy & hold) | Max drawdown (strategy) | Max drawdown (buy & hold) | Selected |
|---|---|---|---|---|---|
| Own-trend filter, 100-day | 0.63 | 0.76 | 28.63% | 52.58% | |
| Market-trend filter, 100-day | 1.07 | 0.76 | 19.96% | 52.58% | Yes |
| Own + market trend filter, 100-day | 0.88 | 0.76 | 12.16% | 52.58% | |
| Own-trend filter, 150-day | 0.63 | 0.76 | 26.88% | 52.58% | |
| Market-trend filter, 150-day | 0.84 | 0.76 | 27.52% | 52.58% | |
| Own + market trend filter, 150-day | 0.80 | 0.76 | 16.01% | 52.58% | |
| Own-trend filter, 200-day | 0.64 | 0.76 | 26.42% | 52.58% | |
| Market-trend filter, 200-day | 0.80 | 0.76 | 27.70% | 52.58% | |
| Own + market trend filter, 200-day | 0.74 | 0.76 | 21.32% | 52.58% | |
The main caveat is that the tickers were current S&P 500, S&P 400, and S&P 600 constituents, so companies removed from those indexes during the window are absent, creating survivorship bias. Also, the 989 tickers were not independent: their mean pairwise return correlation was 0.29, and the portfolio excess-return interval ran from -12.42 to 2.67 percentage points, which includes zero.
| Headline Results | |
| Every ticker is its own backtest, compared with buying and holding that ticker | |
| Metric | Value |
|---|---|
| Tickers tested | 989 |
| Strategy beat buy-and-hold (CAGR) | 306 of 989 (30.9%) |
| Mean excess CAGR | -2.40 pp |
| Median excess CAGR | -2.68 pp |
| Bootstrap 95% interval for the mean | -2.86 to -1.93 pp |
| Smaller max drawdown than buy-and-hold | 81.5% of tickers |
| Median max drawdown (strategy vs buy-and-hold) | 51.83% vs 62.86% |
| Median time in market | 79.1% |


The strategy beat buy-and-hold on an annualized growth rate (CAGR) basis for 306 of 989 tickers, or 30.9%. The excess CAGR versus buy-and-hold had a mean of -2.40 percentage points, a median of -2.68 percentage points, a 10th percentile of -9.15 percentage points, and a 90th percentile of 4.37 percentage points. This means the strategy lagged on most tickers, although the gap was positive for some.
The strategy had a smaller maximum drawdown, the largest decline from a previous peak, on 81.5% of tickers. Median maximum drawdown was 51.83% for the strategy versus 62.86% for buy-and-hold. Its Sharpe ratio, a return measure adjusted for volatility, was better on 32.2% of tickers, with median values of 0.39 versus 0.45, and median time in the market was 79.1%.
Results were consistent in the broad share of winners across the two sub-periods: the strategy beat buy-and-hold on 33.7% of tickers in the first half and 33.5% in the second half. Median excess CAGR remained negative in both, at -22.83 percentage points and -18.64 percentage points. By sector, Communication Services had the highest beat share at 48.5%, followed by Real Estate at 45.3% and Consumer Discretionary at 41.2%; Utilities stood out on the low end at 2.7%, while every sector listed had a negative median excess CAGR.
| By Sector | |||
| Sectors with at least 15 tickers | |||
| Sector | Tickers | Beat buy-and-hold | Median excess CAGR |
|---|---|---|---|
| Communication Services | 33 | 48.5% | -0.70 pp |
| Consumer Discretionary | 131 | 41.2% | -1.01 pp |
| Consumer Staples | 51 | 19.6% | -2.72 pp |
| Energy | 42 | 28.6% | -3.03 pp |
| Financials | 176 | 38.6% | -1.26 pp |
| Health Care | 109 | 36.7% | -2.03 pp |
| Industrials | 174 | 24.7% | -3.99 pp |
| Information Technology | 124 | 21.0% | -4.76 pp |
| Materials | 48 | 14.6% | -3.91 pp |
| Real Estate | 64 | 45.3% | -0.10 pp |
| Utilities | 37 | 2.7% | -4.19 pp |
| Best and Worst Tickers | ||||
| Ranked by strategy CAGR minus buy-and-hold CAGR | ||||
| Group | Ticker | Strategy CAGR | Buy & hold CAGR | Trades |
|---|---|---|---|---|
| Best 10 (vs buy-and-hold) | DAVE | 61.6% | 0.7% | 21 |
| Best 10 (vs buy-and-hold) | CVNA | 84.1% | 39.8% | 39 |
| Best 10 (vs buy-and-hold) | RELY | 15.2% | -16.5% | 15 |
| Best 10 (vs buy-and-hold) | BE | 71.3% | 39.9% | 33 |
| Best 10 (vs buy-and-hold) | ARLO | 26.2% | -4.8% | 33 |
| Best 10 (vs buy-and-hold) | PTCT | 25.4% | 1.8% | 56 |
| Best 10 (vs buy-and-hold) | KD | -3.7% | -26.0% | 15 |
| Best 10 (vs buy-and-hold) | OMCL | 17.0% | 0.3% | 56 |
| Best 10 (vs buy-and-hold) | SHC | 9.8% | -6.2% | 21 |
| Best 10 (vs buy-and-hold) | OGN | 5.3% | -10.7% | 19 |
| Worst 10 (vs buy-and-hold) | FG | 1.4% | 26.0% | 6 |
| Worst 10 (vs buy-and-hold) | VAL | 1.8% | 26.5% | 21 |
| Worst 10 (vs buy-and-hold) | INSW | 10.1% | 37.0% | 39 |
| Worst 10 (vs buy-and-hold) | CEG | 24.7% | 52.4% | 14 |
| Worst 10 (vs buy-and-hold) | AMR | 25.2% | 58.6% | 21 |
| Worst 10 (vs buy-and-hold) | PLTR | 28.3% | 63.0% | 21 |
| Worst 10 (vs buy-and-hold) | ULS | -4.9% | 30.7% | 4 |
| Worst 10 (vs buy-and-hold) | SITM | 40.4% | 76.9% | 24 |
| Worst 10 (vs buy-and-hold) | PECO | 6.5% | 44.4% | 21 |
| Worst 10 (vs buy-and-hold) | GEV | 54.9% | 133.6% | 4 |
| Significance Tests | ||
| Mean pairwise correlation between tickers: 0.29. Naive tests are optimistic; the adjusted and portfolio tests are the ones to lean on. | ||
| Test | Statistic | p-value |
|---|---|---|
| Share of tickers beating buy-and-hold vs a coin flip | 30.9% | less than 0.0001 |
| Mean excess CAGR, t-test (tickers treated as independent) | t = -10.14 | less than 0.0001 |
| Mean excess CAGR, Wilcoxon signed-rank | – | less than 0.0001 |
| Mean excess CAGR, t-test adjusted for correlation between tickers | t = -0.60 (effective n = 3.5) | 0.5999 |
| Equal-weight portfolio excess return, block bootstrap | 95% interval -12.42 to 2.67 pp/yr | interval includes zero |
| Pooled trades, mean return vs zero | t = 26.93 | less than 0.0001 |
The naive tests point in one direction. The strategy beat buy-and-hold on 306 of 989 tickers, or 30.9%, which is below a 50% coin flip; the exact binomial test gave a p-value of less than 0.0001. A p-value is the probability of seeing a result at least this extreme if there were no real edge. The mean excess CAGR was -2.40 percentage points, with a bootstrap 95% interval of -2.86 to -1.93 percentage points, and the t-test gave t = -10.14 with p = less than 0.0001. On their own, these tests suggest the strategy underperformed buy-and-hold.
Those tests treat the tickers as more independent than they really are. Their mean pairwise correlation was 0.29, making the 989 tickers behave like about 3.5 independent observations; after adjusting for this dependence, the t-statistic was -0.60 and the p-value was 0.5999. That adjusted result is NOT strong evidence of an edge. The equal-weight portfolio comparison also showed lower performance for the strategy, with a CAGR of 11.80% versus 16.33% for buy-and-hold, while the block-bootstrap 95% interval for annualised excess return was -12.42 to 2.67 percentage points, which includes zero and is therefore NOT strong evidence of an edge.
Ticker-level tests found 77 significant tickers at the 5% level, including 76 with a positive mean, while chance alone would produce about 48.6. After the Benjamini-Hochberg false-discovery correction, 0 remained significant, including 0 positive. The pooled-trade test found 50,759 closed trades, a mean return per trade of 2.61%, and p = less than 0.0001, with a 95% interval of 2.42% to 2.80%, but trades overlap in time across tickers, so that p-value is indicative only. The data can show that naive tests and pooled trades look positive in places, but after accounting for ticker dependence and multiple testing, it cannot establish a statistically significant edge over buy-and-hold.
| Ticker-Level Significance vs Chance | |
| With many tickers, some look significant by luck; compare the count with what chance gives | |
| Statistic | Value |
|---|---|
| Tickers with at least 8 trades (testable) | 972 |
| Significant at 5% (own trades) | 77 |
| …of which positive mean | 76 |
| Expected from chance alone | 48.6 |
| Significant after false-discovery correction | 0 |
| …of which positive mean | 0 |

| Equal-Weight Portfolio of All Tickers | |||
| Strategy and both benchmarks measured with identical methodology | |||
| Metric | Strategy | Buy & hold (same tickers) | Buy & hold SPY |
|---|---|---|---|
| Total return | 270.4% | 490.4% | 349.2% |
| CAGR | 11.80% | 16.33% | 13.65% |
| Max drawdown | 17.66% | 40.79% | 33.72% |
| Sharpe ratio | 0.89 | 0.84 | 0.82 |
| Annualized volatility | 13.60% | 20.43% | 17.53% |
The biggest caveat is survivorship bias: the test used current index constituents, so companies removed, acquired, or delisted during the window were absent. This is an educational backtest walkthrough, not investment advice, and past performance in a backtest does not predict future results.
| Test Setup | |
| Read from this run’s saved configuration | |
| Item | Setting |
|---|---|
| Tickers requested | 1000 |
| Available in source lists | 1504 |
| Usable | 989 |
| Excluded | 11 |
| Universe source | Current S&P 500, S&P 400 and S&P 600 index constituents (Wikipedia lists) |
| Evaluation window | 2015-01-01 to 2026-09-30 |
| Execution | signal at bar close, fill at next bar open, long-only, 100% of capital per ticker, no leverage, idle cash earns 0% |
| Slippage / commission | 5 bps per fill / none |
| Parameters | filter = market; lookback = 100 |
| Parameter fitting | The rule’s two free choices (filter type and lookback) were selected from 9 pre-declared variants using a separate set of 300 training tickers over 2006-01-01 to 2014-12-31. They were then frozen. The test tickers and the test period were not used in any selection. |
# =========================================================
# trend_regime_family.R -- PRE-REGISTERED strategy family (written before any
# results were examined).
#
# One rule family, nine variants: be long a stock while a simple moving-average
# trend condition holds, otherwise sit in cash.
# filter = "own" : the stock's close is above its own N-day simple moving average
# filter = "market" : the market's (SPY) close is above SPY's own N-day moving average
# filter = "both" : both conditions hold
# lookback N in {100, 150, 200} trading days
# Entry when the condition becomes true, exit when it becomes false. Signals are
# evaluated at each bar's close and filled at the next open (engine rule).
# The only free choices are `filter` and `lookback`; they are picked on a
# training set of tickers and an earlier period, then frozen and tested on
# different tickers and a later period (see research_trend_regime.R).
# =========================================================
trend_regime_signals <- function(ohlc, params, market_close) {
cl <- as.numeric(quantmod::Cl(ohlc))
n <- params$lookback
own_ok <- cl > TTR::SMA(cl, n)
market_ok <- market_close > TTR::SMA(market_close, n)
cond <- switch(params$filter, own = own_ok, market = market_ok, both = own_ok & market_ok)
list(entry = cond %in% TRUE, exit = (!cond) %in% TRUE) # NA (insufficient history) -> no action
}
trend_regime_description <- function(filter, lookback) {
what <- switch(filter,
own = sprintf("the stock's closing price is above its own %d-day simple moving average", lookback),
market = sprintf("the S&P 500 index fund (SPY) closes above its own %d-day simple moving average", lookback),
both = sprintf("both the stock's closing price is above its own %d-day simple moving average and SPY closes above its own %d-day simple moving average", lookback, lookback))
sprintf("Hold the stock (100%% of the capital allotted to it, no leverage) while %s; otherwise hold cash. The condition is checked at each day's close and acted on at the next day's open.", what)
}
make_variant_spec <- function(filter, lookback) {
list(name = sprintf("Trend filter (%s, %d-day)", filter, lookback),
description = trend_regime_description(filter, lookback),
params = list(filter = filter, lookback = lookback),
needs_market = TRUE,
signals = trend_regime_signals)
}
trend_regime_variants <- function() {
g <- expand.grid(filter = c("own", "market", "both"), lookback = c(100, 150, 200), stringsAsFactors = FALSE)
lapply(seq_len(nrow(g)), function(i) list(id = sprintf("%s_%d", g$filter[i], g$lookback[i]), filter = g$filter[i], lookback = g$lookback[i],
label = sprintf("%s filter, %d-day", c(own = "Own-trend", market = "Market-trend", both = "Own + market trend")[[g$filter[i]]], g$lookback[i])))
}
# FROZEN after selection on training data (see selection.json): filter = "market", lookback = 100
strategy_spec <- make_variant_spec("market", 100)A 3-Year Backtest of a 10/30 EMA Crossover with RSI Confirmation vs. SPY (2023–2026)
The strategy buys a sector ETF when its short-term trend rises above its longer-term trend, provided momentum is also positive. Positions are closed when the short-term trend reverses below the longer-term trend.
Technically, we measure this across six liquid sector ETFs, XLK (Technology), XLF (Financials), XLI (Industrials), XLE (Energy), XLV (Health Care), and XLY (Consumer Discretionary), using a 10-day and 30-day exponential moving average (EMA) crossover combined with a 14-day Relative Strength Index (RSI) above 50.
We backtested a 10/30 EMA trend-following strategy with an RSI(14) > 50 filter across six major U.S. sector ETFs using $100,000 starting capital over the 3-year period from 2023-08-29 to 2026-08-27.
Over the 3-year backtest, the active sector strategy grew $100,000 to $158,574 (+58.57% total return, +$58,574 net profit, +16.66% CAGR across 79 closed trades). Over the exact same period, the broad S&P 500 benchmark (SPY) returned +77.04% ($177,038 ending equity, +21.01% CAGR), while an unhedged equal-weight buy-and-hold basket of the six sector ETFs returned +69.44% ($169,438 ending equity, +19.27% CAGR).
💡 The key takeaway: This strategy meaningfully lagged SPY on raw return (Strategy: +58.6% vs. SPY: +77.0%). But it had a smaller maximum decline than either benchmark (Strategy: -9.8% vs. SPY: -18.2% vs. the sector basket: -17.5%), and it actually posted a better risk-adjusted return than SPY (1.36 Sharpe vs. 1.12) — for each unit of volatility taken on, the strategy delivered more return than buy-and-hold. That combination makes it a reasonable fit for a more risk-averse investor willing to trade some upside for a smoother ride, even though it gave up the “matches the market’s raw return” claim.
| Side-by-Side Performance Comparison | |||
| Strategy vs. Sector Basket Buy & Hold vs. SPY S&P 500 Benchmark | |||
| Metric | Sector Strategy | Sector Basket (Equal-Weight) | SPY (S&P 500) |
|---|---|---|---|
| Total Return | 58.57% | 69.44% | 77.04% |
| Annualized Return (CAGR) | 16.66% | 19.27% | 21.01% |
| Maximum Drawdown | -9.81% | -17.51% | -18.20% |
| Annualized Sharpe Ratio | 1.36 | 1.31 | 1.12 |
| Backtest Summary | ||
| The core figures from this run, at a glance | ||
| Metric | Value | What It Measures |
|---|---|---|
| Starting Capital | $100,000 | Initial portfolio equity at start of backtest |
| Ending Capital | $158,574 | Final portfolio value at end of backtest window |
| Total Return | 58.57% | Growth of the account over the full backtest window |
| Annualized Return | 16.66% | Return scaled to a one-year rate, for comparing across different time windows |
| Number of Trades | 79 | Closed round-trip trades this strategy actually made |
| Position Size | 300 units per trade | How many shares/units each trade actually bought or sold — see “A note on position sizing” below |
| Win Rate | 40.5% | Share of closed trades that were profitable |
| Sharpe Ratio | 1.361 | Return per unit of total volatility |
| Sortino Ratio | 1.926 | Return per unit of downside volatility only |
| Calmar Ratio | 1.698 | Return relative to the worst peak-to-trough drawdown |
| Max Drawdown | 9.81% | Largest peak-to-trough decline over the backtest |
| Buy & Hold XLK + XLF + XLI + XLE + XLV + XLY Return | 69.44% | What simply buying and holding XLK + XLF + XLI + XLE + XLV + XLY the whole time would have returned — the baseline this strategy is measured against |
| Strategy vs. Buy & Hold | -81.20% | How much better (positive) or worse (negative) the strategy did than that baseline |
The strategy completed 79 closed round-trip trades across the six sector ETFs, growing the initial $100,000 capital to $158,574 for a total return of +58.57% (+16.66% annual return). In terms of raw total return, the active strategy meaningfully lagged the broad S&P 500 benchmark (SPY), which returned +77.04% ($177,038) over the exact same period, trailing it by 18.47 percentage points. Meanwhile, simply buying all six sector ETFs equally and holding them returned +69.44% ($169,438) — also ahead of the active strategy, though by a much smaller margin than SPY.
The one advantage the strategy retained was downside risk protection. Maximum drawdown means the largest fall in the account from a previous high. The strategy’s maximum drawdown was -9.81% (a $12,752 drop from a $129,932 peak in December 2024, recovering by June 2025), clearly smaller than -18.20% on SPY and -17.51% on the Sector Basket — and it still posted the best risk-adjusted return of the three, at 1.36 Sharpe versus 1.12 for SPY and 1.31 for the Sector Basket.
Across the 79 closed trades, 32 were profitable, producing an overall win rate of 40.5%. Two sectors, Industrials (XLI +$17,849) and Health Care (XLV +$16,303), plus Technology (XLK +$16,943), drove essentially all of the strategy’s net profit; Financials (XLF +$5,268) and Consumer Discretionary (XLY +$5,467) contributed modestly, while Energy (XLE -$3,256) was a net loser over the window.
A central question for systematic trend strategies is cash drag: how much time does the strategy sit in cash, and does sitting idle harm overall compounding?
| Strategy Exposure & Capital Deployment Profile | ||
| How the 10/30 EMA + RSI strategy allocates capital between active ETFs and cash | ||
| Portfolio Metric | Strategy Value | Quantitative Role / Insight |
|---|---|---|
| Time in Market (% Days Invested) | 91.7% of trading days | Strategy sat 100% in cash for 8.3% of the 3-year backtest window |
| Average Invested Exposure | 68.5% of portfolio | Capital deployed into active sector trend crossovers |
| Average Cash Balance | 31.5% of portfolio | Unallocated cash buffer sitting idle during market corrections |
| Average Active Positions | 4.11 ETFs (out of 6 max) | Diversified across roughly 4 sector ETFs during strong trends |
| Maximum Concurrent Positions | 6 Sector ETFs | All 6 sector ETFs were active simultaneously during broad bull trends |
| Annual Portfolio Turnover | ~2.1x per year | Low transaction drag across 79 closed trades |
| Idle Cash Interest Yield | 0.00% (as published) | Tested for real below: parking cash in BIL (T-Bills) improves return to +59.49% |
Equal-Weight Sector Basket Baseline: Equal-weighted once at inception ($16,667 per ETF on 2023-08-29) and allowed to drift over the 3-year period without periodic rebalancing back to 16.67%.
Dividend Handling: Both benchmark (SPY) and sector ETF returns reflect total returns (adjusted daily prices with dividend reinvestment).
Rather than assume a flat T-bill rate applied to the strategy’s average cash balance, we re-ran the backtest’s actual daily cash ledger and let every dollar of idle cash earn a real instrument’s daily return instead — sweeping cash in and out exactly when the strategy itself deposits or withdraws cash to enter and exit sector ETF positions. We tested three options, each backed by real downloaded price data (not simulated or assumed): BIL (a 1-3 month Treasury-bill ETF — the standard liquid, near-zero-duration way to actually "buy T-bills" through a brokerage), BND (the Vanguard Total Bond Market ETF, intermediate duration), and TLT (long-duration Treasuries), since "a bond ETF" isn’t one thing and duration turns out to matter a lot here.
| Cash Treatment | Total Return | Sharpe | Max Drawdown |
|---|---|---|---|
| 0% (as published above) | +58.57% | 1.36 | -9.81% |
| Parked in BIL (1-3mo T-Bills) | +59.49% | 1.39 | -9.08% |
| Parked in BND | +56.87% | 1.29 | -10.50% |
| Parked in TLT | +47.43% | 0.93 | -14.97% |
Duration is the whole story here. BIL returned a real +14.2% over 2023-08-29 to 2026-08-27 (about +4.5% annualized, right in line with prevailing T-bill rates) with almost no price volatility of its own, since 1-3 month bills barely move — and parking idle cash there made the strategy modestly better on every dimension: total return, Sharpe ratio, and max drawdown all improved. BND (intermediate duration, +13.3% real return) and TLT (long duration, -1.9% real return) both made the strategy worse despite BND’s real return being positive, because their own price volatility dragged on the cash balance during the strategy’s own drawdown periods — more than offsetting whatever yield they earned. The practical takeaway: “put idle cash to work” is a reasonable instinct, but the instrument matters as much as the yield — a genuinely liquid, non-volatile T-bill proxy like BIL is the right tool for cash that needs to be available on short notice to fund the next trade, while longer-duration bond funds introduce exactly the kind of price risk that idle cash is supposed to avoid. One caveat: the real, dollar-weighted average cash balance over this backtest is only about 6% of equity (see the code note below), well below the 31.5% slot-based average used for the flat idle-cash-yield estimate above, so the dollar impact of any of these three overlays is smaller than it might first appear.
Comparing a raw 10/30 EMA trend crossover strategy against the 10/30 EMA + RSI momentum filter across the exact same 3-year period:
Adding the RSI > 50 filter made zero measurable difference: both variants produced exactly 79 trades and identical returns, meaning every EMA-crossover entry in this backtest already had RSI above 50. The drawdown reduction the strategy shows versus buy-and-hold comes entirely from the EMA trend-following mechanism itself, not from the RSI filter. Want to see if RSI matters over a longer window or a different threshold?
| Indicator Contribution Analysis | |||||
| Deconstructing Strategy Rules to Isolate Component Value | |||||
| Model Variant | Total Return | Annualized CAGR | Max Drawdown | Annualized Sharpe | Role / Incremental Value |
|---|---|---|---|---|---|
| SPY (S&P 500 Benchmark) | 77.04% | 21.01% | -18.20% | 1.12 | Broad Market Passive Baseline |
| Equal-Weight Sector Basket | 69.44% | 19.27% | -17.51% | 1.31 | Sector Diversification Baseline |
| EMA 10/30 Trend Only | +58.57% | +16.66% | -9.81% | 1.36 | Isolates Trend Crossover Effect |
| Full Strategy (EMA 10/30 + RSI 50) | 58.57% | 16.66% | -9.81% | 1.36 | No Incremental Value — Identical to EMA-Only |
The Full Strategy’s raw return (+58.57%) trails both SPY (+77.04%) and the Equal-Weight Sector Basket (+69.44%) — but it still posts the best risk-adjusted return of all three real variants: a 1.36 Sharpe ratio versus 1.31 for the basket and 1.12 for SPY. Note that the “EMA 10/30 Trend Only” row above is identical to the Full Strategy: the RSI filter makes no difference to which trades are taken (see the Mini-Experiment above), so it can’t be credited with the drawdown reduction either. What the trend-following rule itself contributed was cutting maximum drawdown to -9.81%, clearly better than either passive benchmark (-17.51% and -18.20%). The strategy’s Calmar ratio of 1.70 and Sortino ratio of 1.93 (see the Backtest Summary above) reinforce the same point: for each unit of downside risk taken, this strategy delivered more return than either buy-and-hold alternative, even though its total dollar return was smaller than both.
This strategy evaluates systematic trend filtering across six highly liquid U.S. Economic Sector ETFs:
Note on Universe Selection: These six sector ETFs were selected as the primary liquid universe because they represent over 80% of total S&P 500 market capitalization and exhibit distinct, non-correlated business cycle sensitivities. Defensive and niche sectors (such as Consumer Staples XLP, Utilities XLU, and Real Estate XLRE) were excluded from this baseline to focus specifically on cyclical trend dynamics.
| Sector ETF Universe Performance Breakdown | ||||
| Individual trade stats and net returns across traded economic sectors (79 total trades) | ||||
| Sector / ETF | Trades | Net Realized P&L | Win Rate | Profit Factor |
|---|---|---|---|---|
| Consumer Discretionary (XLY) | 13 | +$5,467 | 30.8% | 1.89 |
| Industrials (XLI) | 9 | +$17,849 | 55.6% | 6.07 |
| Health Care (XLV) | 10 | +$16,303 | 60.0% | 4.63 |
| Technology (XLK) | 11 | +$16,943 | 45.5% | 3.47 |
| Energy (XLE) | 25 | -$3,256 | 20.0% | 0.47 |
| Financials (XLF) | 12 | +$5,268 | 66.7% | 3.39 |
| Trade P&L Concentration & Robustness Audit | |||
| Evaluating Strategy Reliance on Rare Outlier Winners | |||
| P&L Concentration Level | Net Gain ($) | Total Return (%) | Concentration Insight |
|---|---|---|---|
| Headline Strategy Result | +$58,574 | 58.57% | Full backtest result with 79 trades |
| Excluding Largest Winning Trade (Top 1) | +$48,889 | 48.89% | Largest winner (XLK) contributed $ 9,685 |
| Excluding Top 5 Winning Trades | +$22,443 | 22.44% | Top 5 trades contributed $ 36,131 of total gain |
The distribution above shows real fat tails: trade-level P&L skewness is 1.87 and excess kurtosis is 3.44, both above the 0 you’d see from a symmetric, bell-shaped distribution. Losses cluster mostly between roughly -$2,400 and breakeven, while the right tail stretches out to three standout winners of +$9,685, +$9,207, and +$5,988. That shape — many small, capped losses and a few large, uncapped gains — is exactly what you’d expect from a trend-following exit rule that cuts losers quickly on a bearish EMA cross but lets winners run for as long as the trend holds.
This second chart is different: it’s the distribution of the strategy’s own day-to-day portfolio returns across all 750 trading days in the backtest, not per-trade P&L. It is only mildly fat-tailed — skewness of -0.26 and excess kurtosis of 3.01. About 8% of days sit at exactly 0% (fully in cash), which produces the sharply peaked spike at the center, and the largest single-day moves are a +3.79% gain on 2025-05-12 and a -3.59% loss on 2025-10-10 — both ordinary market moves.
Horizontal range-bound markets create repeated false EMA crossovers, causing whipsaw stop-outs.
Lagging 10/30 moving averages react after rapid market pivots, giving back open gains during sharp turnarounds.
Cash protection rules cause the active strategy to lag 100% unhedged long-only sector holdings in runaway bull runs.
Drawdown Summary: The strategy experienced its worst decline between December 2024 and May 2025 (-6.82%) when markets moved sideways, producing several false trend signals before recovering to new highs by June 2025.
One backtest per week—including what worked, what failed, and exact R/Python rules.
| Month | 2023 | 2024 | 2025 | 2026 |
|---|---|---|---|---|
| Jan | – | 0.40% | -1.26% | 3.73% |
| Feb | – | 6.40% | -1.04% | 1.58% |
| Mar | – | 4.00% | -1.74% | -2.75% |
| Apr | – | -4.80% | -2.54% | 3.95% |
| May | – | -0.72% | 5.72% | 5.38% |
| Jun | – | 3.22% | 4.39% | 3.22% |
| Jul | – | 1.68% | 1.18% | -3.56% |
| Aug | – | -1.07% | 1.73% | 1.83% |
| Sep | 0.00% | 0.80% | 4.43% | – |
| Oct | -0.52% | -1.78% | 2.19% | – |
| Nov | 3.95% | 8.47% | -1.18% | – |
| Dec | 6.75% | -2.43% | -1.42% | – |
| YTD | 10.39% | 14.23% | 10.52% | 13.78% |
Inspecting the month-by-month performance matrix highlights key market regimes during the 2023–2026 backtest:
Strong Sector Trend Regimes (e.g. Late 2024 / Q1 2025): Major monthly gains in the strategy occurred during multi-month bull rallies in Tech (XLK) and Financials (XLF). Because active sector positions aligned with positive EMA trend and RSI > 50, portfolio equity compounded rapidly alongside SPY.
Volatile & Pullback Regimes (e.g. Early 2026): During sharp market pullbacks (such as early 2026), the active strategy experienced brief initial dips before trailing EMA crossover rules triggered cash exits. In contrast to unhedged sector ETFs which suffered severe unhedged drawdowns, systematic exit signals capped monthly portfolio losses, preserving accumulated equity.
Asymmetric Monthly Returns: Across the 35 months with nonzero returns, 21 were positive (averaging +3.57%, with a best month of +8.47%) versus 14 negative months (averaging -1.91%, with a worst month of -4.80%). Both the frequency and the average size of gains outweigh the losses. Mechanically, this follows from the strategy’s own rules: the EMA/RSI entry filter keeps it out of the market during choppy or declining stretches (it was invested only 68.5% of the time), and the EMA-cross exit closes losing trades quickly rather than riding out a full down month, while winning trends are held for as long as the crossover stays bullish.
In summary, this 3-year backtest (2023–2026) shows a systematic 10/30 EMA trend filter meaningfully reducing sector ETF drawdown risk, but at a real cost to raw return. By growing $100,000 to $158,574 (+58.57% total return, +16.66% CAGR) across 79 closed trades, the strategy lagged the SPY benchmark (+77.04% total return, $177,038) by 18.5 percentage points, while reducing maximum portfolio drawdown from -18.20% to -9.81% and posting a better Sharpe ratio (1.36 vs. 1.12). The RSI filter makes no measurable difference to the result — the drawdown reduction comes entirely from the EMA trend-following mechanism itself.
Unhedged long-only sector holdings also outperformed the active strategy on raw return (+69.44% for the Sector Basket), while exposing investors to a larger -17.51% drawdown and a lower Sharpe ratio (1.31). For risk-conscious traders willing to accept a lower raw return in exchange for a smoother, better risk-adjusted equity curve, systematic trend filtering provides a disciplined mechanism for managing downside risk — though this backtest does not support the stronger claim that it does so "without sacrificing returns."
As with any backtest, historical results in a 3-year sample do not guarantee future performance. Market regimes evolve, and execution friction (such as slippage and liquidity shifts) should always be accounted for when transitioning from backtest to live execution.
The strategy’s maximum drawdown was 9.8%. Adding a volatility-based stop-loss could make downside control more explicit while preserving the existing trend and momentum entries.
Trade XLK, XLF, XLI, XLE, XLV, and XLY. Enter long when the 10-day exponential moving average crosses above the 30-day exponential moving average and the 14-day RSI is above 50. Exit when the 10-day exponential moving average crosses below the 30-day exponential moving average, or when the closing price falls 2 times the 14-day Average True Range below the entry price, whichever happens first. Keep the same position sizing approach as the original strategy and make no other changes.
Copy this prompt into quantstr.at’s strategy builder to try it yourself.
Does a moving-average crossover strategy work without an RSI filter?
Read Walkthrough →How effective is RSI by itself for tactical asset allocation?
Read Walkthrough →How do volatility envelopes compare to moving average trend filters?
Read Walkthrough →The full R (quantstrat) and Python (VectorBT) implementations behind every number in this article — including the parameter optimization grid search and the look-ahead-free walk-forward validation — are published as two standalone, pre-executed research reports. Each one actually re-runs the backtest end to end against fresh market data when rendered; nothing in them is copy-pasted from this article, and re-rendering either one reproduces every result from scratch.
Both reports use a fixed 300-share position size per trade, 5 bps slippage, and next-bar-open fills — identical mechanics to this article’s headline backtest.
The two research reports linked above are real, tested, pre-executed code — here’s what’s in them if you want to run this strategy, or adapt it, yourself.
A 3-Year Backtest of MACD SMI & Trend Following vs. SPY (2023–2026)
This post walks through a real backtest run on quantstr.at, an AI-assisted quantitative backtesting platform. Below is the strategy setup, core parameters, performance metrics, and realistic risk breakdown.
This study evaluates a quantitative strategy based on the prompt below across a multi-asset ETF universe over the 2023-08-29 to 2026-08-27 period. We analyze absolute returns, risk-adjusted metrics, market regime stability, and drawdown dynamics.
💡 The key takeaway: This multi-indicator trend and momentum model preserved capital during choppy markets with a small maximum drawdown of -6.9% (vs. SPY -18.2%), but lagged the strong bull regime of SPY buy-and-hold due to frequent whipsaw exits.
Trade XLK, XLF, XLI, XLE, XLV, and XLY using an active trend-following and momentum strategy. Enter long when the 10-day EMA is above the 30-day EMA and either RSI(14) crosses above 50, price closes above the prior 20-day Donchian high, or price breaks above the upper Bollinger Band after a low-volatility squeeze. Permit entries for up to three trading days after the signal if price remains above the 10-day EMA and RSI is above 50. Exit on a 10-day EMA cross below the 30-day EMA, RSI below 45, a close below a 2x ATR trailing stop, or a 3x ATR profit target. Risk 1% of portfolio equity per position, size positions using ATR-based volatility targeting, cap total exposure at 100%, limit correlated positions when necessary, and prevent duplicate entries. Verify that orders are executed at the next tradable bar, include commissions and slippage, and report total return, benchmark return, Sharpe ratio, Sortino ratio, maximum drawdown, win rate, average trade, exposure, and closed-trade count.
| Strategy Initialization & Execution Parameters | |
| Core environment setup for this backtest run | |
| Parameter | Setting / Value |
|---|---|
| Asset Universe | U.S. Sector & Asset ETFs |
| Time Window | 2023-08-29 to 2026-08-27 |
| Starting Equity | $100,000 |
| Benchmark | SPY Buy & Hold |
| Execution Fill | Close of bar T |
| Slippage & Commission | 5 bps slippage, $0 commission |
| Position Sizing | Account-relative allocation |
This is a long-only trend-following and momentum strategy applied to XLK, XLF, XLI, XLE, XLV, and XLY. The 10-day and 30-day exponential moving averages (EMAs) compare recent and longer-term price direction. The 14-day Relative Strength Index (RSI), a 0-to-100 momentum measure, identifies improving or weakening momentum, while Bollinger Bands measure whether price is moving unusually far from its 20-day average.
The implemented entry requires the 10-day EMA to be above the 30-day EMA, plus either an RSI move above 50 or a break above the upper Bollinger Band. Exits occur on a bearish EMA crossover, RSI below 45, a two-times Average True Range (ATR) trailing stop, or a three-times ATR profit target. ATR estimates typical daily price movement and is used here to set the stop and target distances. Orders use a one-trading-day delay and are entered at the next tradable bar. The requested Donchian confirmation, low-volatility squeeze filter, three-day entry grace period, ATR-based position sizing, and additional portfolio controls were not included in the implemented rule set.
| Strategy Specification | |
| Quantitative component breakdown | |
| Component | Description |
|---|---|
| Indicator | ema_fast: Technical indicator calculation. ema_slow: Technical indicator calculation. rsi_14: Technical indicator calculation. donchian_20: Technical indicator calculation. bbands_20: Technical indicator calculation. atr_14: Technical indicator calculation. atr_stop_2x: Technical indicator calculation. atr_target_3x: Technical indicator calculation. |
| Signal | trend_up: The 10-day EMA remains above the 30-day EMA. rsi_cross_above_50: RSI(14) crosses above 50. rsi_above_50: RSI(14) remains above 50. bbands_break: Price breaks above the upper Bollinger Band, represented by Bollinger percent B crossing above 1. entry_signal: Long entry when the fast EMA is above the slow EMA and either RSI crosses above 50 or price breaks above the upper Bollinger Band. ema_exit: The 10-day EMA crosses below the 30-day EMA. rsi_exit: RSI(14) remains below 45. |
| Rule | Enter long on trend momentum signal: Order execution rule. Exit on bearish EMA crossover: Order execution rule. Exit on RSI weakness: Order execution rule. Two-ATR trailing stop: Order execution rule. Three-ATR profit target: Order execution rule. |
| Backtest Summary | ||
| The core figures from this run, at a glance | ||
| Metric | Value | What It Measures |
|---|---|---|
| Total Return | 3.34% | Growth of the account over the full backtest window |
| Annualized Return | 0.0429% | Return scaled to a one-year rate, for comparing across different time windows |
| Number of Trades | 120 | Closed round-trip trades this strategy actually made |
| Position Size | 54 to 171 units per trade | How many shares/units each trade actually bought or sold — see “A note on position sizing” below |
| Win Rate | 36.8% | Share of closed trades that were profitable |
| Sharpe Ratio | 0.015 | Return per unit of total volatility |
| Sortino Ratio | 0.019 | Return per unit of downside volatility only |
| Calmar Ratio | 0.160 | Return relative to the worst peak-to-trough drawdown |
| Max Drawdown | 6.94% | Largest peak-to-trough decline over the backtest |
| Value at Risk (VaR) | -0.600% | The loss threshold not expected to be exceeded in a typical period |
| Expected Shortfall (CVaR) | -1.28% | The average loss in the worst-case tail beyond the VaR threshold |
| Buy & Hold XLK + XLF + XLI + XLE + XLV + XLY Return | 157.82% | What simply buying and holding XLK + XLF + XLI + XLE + XLV + XLY the whole time would have returned — the baseline this strategy is measured against |
| Strategy vs. Buy & Hold | -154.47% | How much better (positive) or worse (negative) the strategy did than that baseline |

The strategy completed 120 closed round-trip trades and turned the initial $100,000 into a 3.3444% total return. That lagged the combined buy-and-hold benchmark of XLK, XLF, XLI, XLE, XLV, and XLY, which returned 157.82%, by 154.47 percentage points. It also lagged SPY’s 77.04% return over the same period.
The Sharpe ratio was 0.0146. Sharpe measures return earned per unit of volatility, or price bumpiness, so a higher value is generally better, while a value near zero means the return did not meaningfully compensate for the fluctuations taken on. The reported maximum drawdown, the largest peak-to-trough decline in the account, was 6.9372%, and the low Sortino ratio of 0.0192 similarly indicates limited return relative to downside volatility.
At the trade level, 120 trades provide more observations than a single-trade result, but the modest total gain shows that winning and losing positions largely offset one another. The reported monthly returns ranged from -504.67% to +333.25%, which is inconsistent with the 3.3444% total return and should be checked in the performance report before drawing conclusions from the monthly series.


| Risk-Adjusted Return Metrics | ||
| Every number here comes straight from this backtest’s quantstr.at report | ||
| Metric | Value | What It Measures |
|---|---|---|
| Annualized Return | 1.11% | Return scaled to a one-year rate |
| Annualized Volatility | 5.39% | How much returns swing year to year |
| Sharpe Ratio | 0.015 | Return per unit of total volatility — the classic risk-adjusted return measure |
| Sortino Ratio | 0.019 | Return per unit of downside volatility only — ignores upside swings that Sharpe penalizes unfairly |
| Calmar Ratio | 0.160 | Return relative to the worst peak-to-trough drawdown |
| Omega Ratio | 0.050 | Probability-weighted ratio of gains to losses — doesn’t assume a normal return distribution the way Sharpe does |
| Downside Deviation | 0.259% | Volatility of losing periods only |
| Upside Potential Ratio | 0.511 | Upside capture relative to downside risk — a Sortino-style ratio facing the other direction |
| Probability Sharpe > 0 | 65.44% | Statistical confidence the strategy’s TRUE Sharpe ratio (not just this one sample) is greater than zero |
| Min. Track Record Needed | 12,866 | Minimum number of return observations needed for that confidence level to be meaningful |
| Sharpe Statistically Significant? | No | Whether this backtest has enough history for its Sharpe ratio to be statistically real, not noise |

The largest drawdown was 6.9372%, or about $6,937 on the initial $100,000 account. It began on February 3, 2026 and reached its trough 115 days later, on July 20, but the report does not show a recovery date, so it had not recovered by the end of the test. The average drawdown lasted 20.8125 days and took 8.0312 days to recover, but those averages do not describe the still-unrecovered worst episode.
The risk-adjusted results provide little support for the 3.3444% total return. The Sharpe ratio was 0.0146 and the Sortino ratio was 0.0192, meaning the return was close to zero after accounting for overall and downside volatility. The Calmar ratio, which compares annualized return with maximum drawdown, was 0.16, also indicating limited return relative to the largest decline. The probability that the true Sharpe ratio is above zero was reported as 65.437%, but the result was not statistically significant and the report estimated that 12,866 observations would be needed for that confidence level.
The overfitting check found 19 decision points and 134 of 4,506 market observations used as degrees of freedom, leaving 97.0% remaining. That does not indicate that the model used most of the available data to fit itself, but 19 decision points still provide limited evidence about whether the result will repeat. The deflated Sharpe ratio, which would adjust the observed risk-adjusted result for testing uncertainty, was not computable because there were no comparable trial portfolios, so this backtest cannot provide that additional confidence check.

Before publication, this strategy was evaluated by our automated Strategy Review Agent across 2 review round(s).
RETRY_OPTIMIZEDA raw return number alone can mislead: a strategy earning 20% through wild swings is not automatically better than one earning 10% more smoothly.
Sharpe divides return by total volatility, so this backtest’s 0.0146 asks whether its return compensated for all price fluctuations. Sortino divides return by downside-only volatility, so its 0.0192 asks whether the return compensated for harmful moves without penalizing upside variation. Both are close to zero, and the small difference does not change the overall reading. Calmar divides annualized return by maximum drawdown, and the 0.16 result compares the 1.11% annualized return with the 6.9372% worst decline, also indicating limited compensation for risk. The reported 65.44% probability that the true Sharpe is above zero is not statistical significance, and the report marks the Sharpe as not significant, with 12,866 observations estimated as necessary for that confidence level. In your own quantstr.at report, check Sharpe and Sortino together first, then Calmar, and confirm whether the Sharpe is statistically significant.
This backtest showed a modest 3.3444% total return from 120 closed round-trip trades, far below the 157.82% buy-and-hold return for the six ETFs and SPY’s 77.04% return. Its Sharpe ratio of 0.0146 and Sortino ratio of 0.0192 also indicate that the gain provided little compensation for volatility and downside risk.
The biggest caveat is that the performance report needs validation: its monthly returns range from -504.67% to +333.25%, which is inconsistent with the reported total return. The reported 6.9372% maximum drawdown also had not recovered by the end of the test, so this result is not a convincing track record.
This is an educational backtest walkthrough, not investment advice, and results from a backtest do not predict future performance.
The strategy returned 3.3444% while the six-ETF buy-and-hold benchmark returned 157.82%, and its Sharpe ratio was only 0.0146. Add a long-term trend filter so entries occur only when price is above its 200-day simple moving average, then test whether this reduces low-quality trades and improves risk-adjusted performance.
Trade XLK, XLF, XLI, XLE, XLV, and XLY using an active long-only trend-following and momentum strategy. Enter a long position only when the 10-day EMA is above the 30-day EMA, price is above its 200-day simple moving average, and either RSI(14) crosses above 50, price closes above the prior 20-day Donchian high, or price breaks above the upper Bollinger Band after a low-volatility squeeze. Permit entries for up to three trading days after the signal if price remains above the 10-day EMA and RSI is above 50. Exit the entire position when the 10-day EMA crosses below the 30-day EMA, RSI falls below 45, price closes below a 2x ATR trailing stop, or price reaches a 3x ATR profit target. Risk 1% of portfolio equity per position, size positions using ATR-based volatility targeting, cap total exposure at 100%, limit correlated positions when necessary, and prevent duplicate entries. Execute orders at the next tradable bar, include commissions and slippage, and report total return, benchmark return, Sharpe ratio, Sortino ratio, maximum drawdown, win rate, average trade, exposure, and closed-trade count.
Copy this prompt into quantstr.at, or just click below — it loads straight into the chat, ready to run.
| Trade Statistics by Symbol | |||||||||||||||||||||||||||
| Detailed performance and risk metrics breakdown per traded asset | |||||||||||||||||||||||||||
| Symbol | Trades | Net P&L | Win Rate | Loss Rate | Max Equity | Min Equity | Gross Profits | Gross Losses | Max Drawdown | Transactions | Avg Daily P&L | Avg Trade P&L | Ending Equity | Largest Winner | Largest Loser | Profit Factor | Avg Winning Trade | Avg Losing Trade | Median Trade P&L | Annualized Sharpe | Daily P&L Std Dev | Trade P&L Std Dev | Avg Win/Loss Ratio | Median Losing Trade | Median Winning Trade | Median Win/Loss Ratio | Profit / Max Drawdown |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| XLK | 19 | -3524.24 | 36.8% | 63.2% | 1735.31 | -3524.24 | 3870.86 | -7395.10 | -5259.55 | 65 | -185.49 | -185.49 | -3524.24 | 1427.41 | -4150.32 | 0.52 | 552.98 | -616.26 | -31.46 | -2.64 | 1115.30 | 1115.30 | 0.90 | -213.56 | 525.94 | 2.46 | -0.67 |
| XLF | 16 | 2946.06 | 50.0% | 50.0% | 3531.83 | -68.36 | 4554.15 | -1608.09 | -2675.28 | 52 | 184.13 | 184.13 | 3221.37 | 1518.66 | -348.32 | 2.83 | 569.27 | -201.01 | 29.98 | 5.78 | 505.77 | 505.77 | 2.83 | -202.80 | 437.78 | 2.16 | 1.20 |
| XLI | 22 | 451.41 | 36.4% | 63.6% | 2521.62 | -3.70 | 5480.75 | -5029.34 | -2436.51 | 74 | 20.52 | 20.52 | 451.41 | 1215.72 | -691.78 | 1.09 | 685.09 | -359.24 | -255.56 | 0.57 | 575.67 | 575.67 | 1.91 | -358.94 | 627.20 | 1.75 | 0.19 |
| XLE | 19 | 299.54 | 26.3% | 73.7% | 5602.10 | -1054.14 | 5581.53 | -5281.99 | -5307.36 | 63 | 15.77 | 15.77 | 1507.54 | 1990.42 | -1645.73 | 1.06 | 1116.31 | -377.29 | -79.35 | 0.29 | 868.84 | 868.84 | 2.96 | -117.12 | 1189.67 | 10.16 | 0.28 |
| XLV | 19 | 1020.35 | 42.1% | 57.9% | 3493.57 | -1556.21 | 4991.32 | -3970.98 | -3486.61 | 59 | 53.70 | 53.70 | 2968.60 | 2140.72 | -1393.72 | 1.26 | 623.92 | -361.00 | -131.58 | 1.19 | 717.26 | 717.26 | 1.73 | -193.94 | 328.44 | 1.69 | 0.85 |
| XLY | 21 | -1185.91 | 28.6% | 71.4% | 1997.46 | -1280.26 | 2985.58 | -4171.49 | -3277.72 | 65 | -56.47 | -56.47 | -1280.26 | 1548.56 | -921.46 | 0.72 | 497.60 | -278.10 | -74.63 | -1.74 | 515.84 | 515.84 | 1.79 | -191.35 | 322.04 | 1.68 | -0.39 |
Built with quantstr.at. Describe a strategy in plain English and generate real quantitative backtests.