Category: Momentum

Can Donchian Channel Breakouts Beat Buy-and-Hold? A 3-Year QQQ Test

Research by quantstr.at Quantitative Team
Backtest window 2016-01-01 to 2025-01-01 · Published oct. 07, 2026
⚠ Integrity audit: 4 of 6 checks passed
ResultsBreakdownRiskMonthly ReturnsSetup & Audit

What Did We Test?

This strategy aims to capture price movements by entering long positions when specific conditions, based on recent price action, signal potential upward trends. The approach uses two indicators: the 20-day high, which reflects the highest price over the last 20 days, and two different exponential moving averages (EMAs). The 10-day EMA, which averages prices over ten days while giving more weight to recent prices, must be greater than the 30-day EMA, a longer-term average. This setup suggests that the short-term momentum is stronger than the longer-term trend, potentially indicating a bullish phase.

When the conditions for entry are met, the strategy will exit either when the price drops to the 10-day low or when a trailing stop is reached, set at twice the Average True Range (ATR). The ATR is a measure of market volatility, providing a dynamic stop that adjusts based on recent price fluctuations.

Component Description
Indicator 20-day high represents the highest price in the past 20 days; 10-day EMA is the average price over the last 10 days, weighted toward recent prices; 30-day EMA is the average over the last 30 days.
Signal Enter long when the price breaks above the 20-day high and the 10-day EMA is greater than the 30-day EMA.
Rule Exit the position when the price reaches the 10-day low or hits a trailing stop set at twice the ATR.

What Happened?

The backtest aimed to evaluate a strategy using price indicators to identify potential upward trends in seven symbols from January 1, 2016, to January 1, 2025. Starting with an initial equity of $100,000, the strategy ended with a value of approximately $207,892, resulting in a total return of 107.89%. However, the strategy lagged behind both benchmark comparisons. The equal-weight buy-and-hold basket achieved a total return of 244,286.40%, while the buy-and-hold QQQ index generated a return of 399.43%.

One significant caveat of this backtest is that while it generated a positive overall return, the strategy exhibited a maximum drawdown of 25.63%. This decline was notably less severe compared to the 60.66% maximum drawdown of the equal-weight buy-and-hold basket and 35.12% of the QQQ index, indicating better risk management in this regard. Despite this, the strategy’s overall performance did not match the benchmarks, highlighting the importance of evaluating risk alongside return.

Strategy
+107.9% (CAGR: +8.5%)
Worst decline: -25.6%
Equal-weight buy & hold basket (7 symbols)
+244286.4% (CAGR: +138.1%)
Worst decline: -60.7%
Buy & hold QQQ
+399.4% (CAGR: +19.6%)
Worst decline: -35.1%

How Did It Compare With the Benchmarks?

Side-by-Side Comparison
Benchmark CAGR and drawdown are computed from daily closes over the same window; Sharpe is omitted because the methodologies differ
Metric Strategy Equal-weight buy & hold basket (7 symbols) Buy & hold QQQ
Total return +107.9% +244286.4% +399.4%
Annualized return (CAGR) +8.5% +138.1% +19.6%
Max drawdown -25.6% -60.7% -35.1%
Ending equity $207,892 $244,386,399 $499,433
Backtest Summary
Every figure computed from this run’s saved results
Metric Value
Starting capital $100,000
Ending capital $207,892
Total return +107.89%
Annualized return (CAGR) +8.47%
Closed trades 152
Sharpe ratio (annualized) 1.34
Calmar ratio 1.08
Max drawdown -25.63%
Equal-weight buy & hold basket (7 symbols) +244286.40%
Strategy vs. Equal-weight buy & hold basket (7 symbols) -244178.51 pp
Buy & hold QQQ +399.43%
Strategy vs. Buy & hold QQQ -291.54 pp

The strategy executed a total of 152 trades during the backtest period from January 1, 2016, to January 1, 2025. It achieved a total return of 107.89%, which translates to a compounded annual growth rate (CAGR) of 8.47%. However, this performance lagged behind the benchmark of an equal-weight buy-and-hold basket, which returned 244,286.40%, indicating the strategy underperformed by a significant margin.

The Sharpe ratio stands at 1.34, which means that the returns earned were 1.34 times the volatility taken on. In other words, for each unit of risk, the strategy generated a positive return, suggesting a reasonable risk-reward balance. At the trade level, the absence of closed trades listed means that there are no trade details to analyze; thus, it is hard to gauge specific patterns in profitability or consistency.

The monthly returns reveal that the strategy had 22 positive months compared to 13 negative months, indicating a tendency for more frequent gains. The average return during positive months was higher than the average loss during negative months, specifically demonstrating that gains were stronger on average, this pattern aligns with the strategy’s design to capitalize on upward momentum while managing risk through its exit criteria.

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

Risk-Adjusted Returns

A raw return figure can be misleading because it doesn’t account for the risk taken to achieve those returns. For example, a strategy might return 20% by taking significant risks, but a more modest 10% return could be achieved with much less volatility.

In this backtest, the Sharpe ratio of 1.34 indicates that the strategy generated returns 1.34 times the total volatility, suggesting a reasonable risk-reward balance. The Sortino ratio of 0.13, however, focuses only on downside volatility, revealing that the risk of losing is not well compensated by the upside, which contrasts starkly with the higher Sharpe. The Calmar ratio of 1.08 compares the return to maximum drawdown, further underscoring the strategy’s susceptibility to significant declines.

The "Probability Sharpe > 0" at 99.15% suggests strong statistical confidence that the true Sharpe ratio is positive, which is meaningful. Additionally, since this backtest’s Sharpe ratio is statistically significant, we can trust it provides insightful data. For your own reports, start by comparing the Sharpe and Sortino ratios, followed by the Calmar ratio for a comprehensive risk assessment.

Where Did the Result Come From?

The results of this strategy are concentrated among just a few symbols. The analysis shows that out of the seven symbols traded, only three contributed significantly to the overall profit. Specifically, QQQ led with a net profit of $51,119 from 37 trades and a win rate of 54.1%. MSFT followed with $35,846 from 37 trades and a slightly lower win rate of 45.9%. In contrast, NVDA, while generating a profit of $15,656 from 41 trades, had a win rate of only 29.3%. Other symbols, such as AAPL, had a modest contribution of $6,664 from 37 trades, but there were no trades for XLF, XLP, and XLE.

The trade-level significance test indicates that the results should be interpreted with caution. Since there were multiple trades across a few symbols and the p-value reflects the statistical relevance of the results, it is important to note that the nature of overlapping trades means the p-value serves as an indicative measure rather than conclusive evidence.

Performance by Symbol
7 symbols, closed trades only
Symbol Trades Net P&L Win rate Profit factor
QQQ 37 +$51,119 54.1% 2.95
MSFT 37 +$35,846 45.9% 2.19
NVDA 41 +$15,656 29.3% 1.58
AAPL 37 +$6,664 40.5% 1.28
XLF 0 – – –
XLP 0 – – –
XLE 0 – – –

How Bad Did It Get?

The strategy experienced a maximum drawdown of 25.63%, which means that the account value fell by that percentage from its peak to its lowest point during the backtest. This drawdown lasted for approximately 193 days before hitting the trough and took an additional 60 days to recover. Such statistics indicate that a trader might face considerable losses before seeing their investment recover to previous highs.

In terms of risk-adjusted return metrics, the Sharpe ratio is 1.34, suggesting that the returns generated were relatively favorable compared to the level of risk taken. However, the Sortino ratio of 0.13 indicates a substantial disparity between upside potential and downside risk. This divergence highlights a caveat: although the total returns are positive, they may not adequately compensate for the risks involved, especially given that the average drawdown length was 17.82 days. This risk profile should give potential traders pause, as it signals that while profits exist, they come with significant volatility.

The integrity audit failed due to two key issues: first, the trade list did not reconcile with the per-symbol trade statistics, indicating a potential mismatch in reported trades. Second, there was no verification of the gross long exposure relative to the portfolio equity, leaving uncertainty about the strategy’s true risk exposure. These failures suggest that the reported performance might not fully reflect reliable trade execution or risk management.

What Stood Out

  • The strategy had a total return of 107.89%, but this was significantly lower than the benchmark’s return of 244,286.40%, highlighting a considerable performance gap.
  • Despite a positive monthly return count of 22, the total profit did not keep pace, raising questions about the effectiveness of individual trades contributing to the overall strategy.
  • The maximum drawdown of 25.63% indicates notable potential exposure to risk, which could be unexpected given the strategy’s positive return trend.

Limits of This Backtest

  • The integrity audit failed, specifically highlighting that the trade list did not reconcile with per-symbol trade statistics, suggesting potential discrepancies in the reported performance.
  • There were no closed trade details available, making it difficult to analyze specific performance metrics or patterns.
  • The gross long exposure could not be verified relative to portfolio equity, meaning the reported performance may overstate actual returns.
  • The total length of the backtest period is 9.00 years, which, while substantial, may not fully capture varying market conditions necessary for a comprehensive evaluation of the strategy.

Monthly Returns

Monthly Returns Matrix (%)
Month-by-month and YTD cumulative performance summary
Year Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec YTD
2023 0.0% 2.2% 5.8% 2.9% 11.4% 2.5% 4.9% -5.7% -2.5% 1.0% 13.4% 2.1% 43.1%
2024 4.1% 0.4% -0.6% -2.8% 2.8% 26.5% -1.4% -0.6% 1.3% -3.9% 0.5% 6.2% 33.7%
2025 -9.5% -0.6% -6.7% -5.1% 13.4% 7.9% 11.4% 0.7% 3.1% 7.2% -8.1% -2.1% 8.6%

Over the tracking period from January 2023 to January 2025, the strategy experienced a mix of monthly returns, with 22 positive months and 13 negative months. The best month was June 2024, which had a return of 24.48%. Conversely, the worst month was January 2025, with a return of -6.33%.

The most significant drawdown occurred between July 11, 2024, and April 16, 2025, where it reached a depth of -25.63%. This episode aligns with the overall trend of returns, showing that significant declines occurred during a challenging market period.

Conclusions

This backtest revealed a total return of 107.89% over nearly nine years, which amounted to a compounded annual growth rate (CAGR) of 8.47%. However, this performance significantly lagged behind the benchmark of an equal-weight buy-and-hold basket that returned an astonishing 244,286.40%.

The primary caveat to consider is the considerable maximum drawdown of 25.63%, which indicates potential for substantial losses during the backtest period. Moreover, the integrity audit highlighted issues, particularly regarding the reconciliation of trade statistics, suggesting that the reported figures may not fully reflect reliable trade execution. Always remember, this walkthrough demonstrates past performance and does not predict future results. If you’re interested, feel free to explore your own strategy ideas using quantstr.at.

Ideas to Test Next

The strategy’s maximum drawdown of 25.63% indicates significant risk exposure, which could make it challenging for traders during downturns. Consider adjusting the exit conditions to include a stop-loss to potentially limit losses and manage risk more effectively.

Enter long when the price breaks above the 20-day high and the 10-day EMA is greater than the 30-day EMA. Exit when the price reaches the 10-day low or hits a trailing stop set at twice the ATR, or if the price falls below a stop-loss of 1.5% from the entry price.

More Backtests

Setup, Audit & Replication

Backtest Setup
Read from this run’s saved configuration
Parameter Setting / Value
Asset universe 7 symbols (XLF, XLP, XLE, AAPL, MSFT, NVDA, QQQ)
Time window 2016-01-01 to 2025-01-01 (9.0 years)
Starting equity $100,000
Benchmarks Equal-weight buy & hold basket (7 symbols) (+244286.4%); Buy & hold QQQ (+399.4%)
Full per-symbol trade statistics ▾
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
XLF 0 $0 — — — — — — — — — — — — — — — — — — — — — — — — —
XLP 0 $0 — — — — — — — — — — — — — — — — — — — — — — — — —
XLE 0 $0 — — — — — — — — — — — — — — — — — — — — — — — — —
AAPL 37 $6,664 40.5% 59.5% $15,684 -$1,542 $30,859 -$24,195 -$17,226 74 $180 $180 $6,664 $6,807 -$5,467 1.28 $2,057 -$1,100 -$229 1.27 $2,243 $2,243 1.87 -$868 $1,409 1.62 0.39
MSFT 37 $35,846 45.9% 54.1% $42,783 $0 $66,072 -$30,227 -$16,173 75 $969 $969 $35,346 $25,652 -$4,329 2.19 $3,887 -$1,511 -$171 3.09 $4,977 $4,977 2.57 -$1,195 $1,403 1.17 2.19
NVDA 41 $15,656 29.3% 70.7% $26,302 $0 $42,555 -$26,899 -$18,061 83 $382 $382 $16,172 $22,605 -$3,469 1.58 $3,546 -$928 -$63 1.41 $4,304 $4,304 3.82 -$948 $538 0.57 0.90
QQQ 37 $51,119 54.1% 45.9% $59,632 -$1,585 $77,361 -$26,243 -$11,237 75 $1,382 $1,382 $49,711 $18,476 -$4,199 2.95 $3,868 -$1,544 $190 4.97 $4,414 $4,414 2.51 -$1,776 $1,472 0.83 4.42

Backtest Integrity Audit: 4 of 6 checks passed

  • ✅ Headline Return Matches Ending vs Starting Equity: From dollars: 107.89% | Reported field: 107.89% | Gap: 0.000 pp
  • ✅ Equity Curve Starts at Initial Equity and Ends at Ending Value: First point: $100,000 (initial $100,000) | Last point: $207,892 (ending $207,892)
  • ✅ Monthly Returns Compound to Headline Return: Compounded months: 107.89% | Headline: 107.89% | Gap: 0.00 pp
  • ⚠️ Trade List Reconciles With Per-Symbol Trade Statistics: Trade-list rows: 0 | open at end: 0 | closed per trade statistics: 152 | open rows isolated: yes
  • ✅ Sharpe Ratio Consistent With Annualized Return and Volatility: Reported Sharpe: 1.34 | Annualized return / volatility: 1.34
  • ⚠️ Gross Long Exposure Never Exceeds Portfolio Equity: No position series in results; cannot verify

Can Keltner Channel Breakouts Beat Buy-and-Hold? A 3-Year Test on Tech Equities & QQQ

ResultsBreakdownRiskMonthly ReturnsSetup & Audit

What Did We Test?

This strategy uses a Keltner Channel Volatility Breakout approach, targeting stocks like QQQ, NVDA, AAPL, MSFT, AMD, and AMZN. The Keltner Channel consists of an upper band, a lower band, and a center line, measuring the average price along with average true range (ATR) to capture volatility and identify trends. The Average Directional Index (ADX) is also utilized, which indicates trend strength. Entering long positions when the price breaks above the upper band of the Keltner Channel, combined with an ADX value above 25, suggests a strong upward trend. Exiting positions occurs when the price falls below the center line of the channel.
Component Description
Indicator Keltner Channel: Comprised of an upper band and a central line (20-period EMA) based on price and volatility. Average Directional Index (ADX): Measures trend strength (14-period).
Signal Enter long when the price breaks above the 20-period upper band and the 14-period ADX is greater than 25.
Rule Exit the position when the price closes below the 20-period EMA center line.

What Happened?

The backtest of the Keltner Channel Volatility Breakout strategy ran from August 29, 2023, to August 27, 2026. The starting capital of $100,000 grew to approximately $207,035, resulting in a total return of 107% and a compound annual growth rate (CAGR) of 27.5%. However, the strategy lagged behind an equal-weight buy-and-hold basket of the same stocks, which achieved a total return of 166% and a CAGR of 38.7%. In contrast, it outperformed the buy-and-hold SPY, which returned 77%.

A significant consideration is that the strategy’s results were based on 97 closed trades (+1 still open), which indicates that performance may depend heavily on a limited number of trades. The maximum drawdown for this strategy was approximately 29.9%, compared to the equal-weight benchmark’s max drawdown of 28.9%, suggesting a slightly worse risk profile.

Strategy
+107.0% (CAGR: +27.5%)
Worst decline: -29.9%
Equal-weight buy & hold basket (6 symbols)
+166.3% (CAGR: +38.7%)
Worst decline: -28.9%
Buy & hold SPY
+77.0% (CAGR: +21.0%)
Worst decline: -18.8%

How Did It Compare With the Benchmarks?

Side-by-Side Comparison
Benchmark CAGR and drawdown are computed from daily closes over the same window; Sharpe is omitted because the methodologies differ
Metric Strategy Equal-weight buy & hold basket (6 symbols) Buy & hold SPY
Total return +107.0% +166.3% +77.0%
Annualized return (CAGR) +27.5% +38.7% +21.0%
Max drawdown -29.9% -28.9% -18.8%
Ending equity $207,035 $266,324 $177,038
Backtest Summary
Every figure computed from this run’s saved results
Metric Value
Starting capital $100,000
Ending capital $207,035
Total return +107.03%
Annualized return (CAGR) +27.50%
Closed trades 97 (+1 still open at the end)
Win rate (closed trades) 41.2%
Profit factor (closed trades) 2.11
Sharpe ratio (annualized) 1.12
Calmar ratio 0.93
Max drawdown -29.92%
Equal-weight buy & hold basket (6 symbols) +166.32%
Strategy vs. Equal-weight buy & hold basket (6 symbols) -59.29 pp
Buy & hold SPY +77.04%
Strategy vs. Buy & hold SPY 30.00 pp

The Keltner Channel Volatility Breakout strategy generated a total return of 107% over the 3-year period, resulting in an ending equity value of approximately $207,035 from an initial equity of $100,000. The strategy executed 97 closed trades, with a win rate of about 41%. When compared to the equal-weight buy-and-hold strategy for the same stocks, which achieved a total return of 166%, this strategy lagged significantly by approximately 59%. However, it outperformed the SPY buy-and-hold strategy, which returned 77%, by around 30%.

The Sharpe ratio of 1.12 indicates that the strategy earned 1.12 units of return for every unit of risk taken, which is generally considered acceptable in quantitative trading. At the trade level, the average closed-trade profit and loss was about $1,107. While some trades were profitable, the distribution revealed that a small number of large gains were responsible for a significant portion of the overall profit, indicating a "few big winners" trading pattern.

Monthly returns averaged about 14 positive months versus 20 negative months, with average gains of around 12% during winning months and smaller average losses of about 3%. This suggests that while the strategy struggled to avoid bad months, it was capable of capturing larger upswings, aligning with its volatility breakout approach. However, the strategy faced a maximum drawdown of 30%, indicating considerable risk during its worst performance period.

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

Risk-Adjusted Returns

Understanding raw return figures can be misleading, as they fail to reveal the underlying risks involved. For instance, a strategy returning 20% with high volatility may not be preferable to one generating 10% returns with a smoother ride. In this backtest, the Sharpe ratio of 1.12 indicates that the strategy earned about 1.12 units of return per unit of total volatility, reflecting an acceptable risk-return trade-off. The Sortino ratio, at 0.11, looks specifically at downside volatility, signaling concerns since it shows that returns aren’t proportionate to the risks incurred when the strategy performed poorly. The Calmar ratio of 0.93 compares returns relative to the maximum drawdown, offering a different angle that emphasizes the substantial drawdown of nearly 30%.

A key metric, "Probability Sharpe > 0," sits at 97.94%, indicating high confidence that the strategy’s true Sharpe ratio is greater than zero, although the minimum track record needed to ensure this confidence is 487 observations. In this case, the Sharpe ratio is statistically significant, meaning it likely reflects a real pattern rather than being a result of chance. When reviewing your own quantstr.at report, it’s useful to examine the Sharpe and Sortino ratios together to understand risk-adjusted returns, followed by the Calmar ratio for additional insights into maximum drawdown.

Where Did the Result Come From?

The results of this backtest show a significant reliance on a few symbols for the overall performance. While the strategy is built around six symbols, the bulk of the profit is concentrated in just a couple of them. Notably, QQQ accounted for a net profit of $54,264 from 14 trades, translating to a win rate of 57.1% and a profit factor of 8.10. On the other hand, AMZN showed disappointing results, with a net loss of $15,936 across 16 trades, which reflects a win rate of only 25.0% and a low profit factor of 0.21.

The significance test indicates that the mean return per trade was 12.22%, with a 95% confidence interval for the mean ranging from -8.03% to 32.46%. The t-statistic was 1.20, and the two-sided p-value was 0.2339, suggesting there isn’t strong evidence that the strategy was definitively effective, primarily due to the small sample size of trades. This means that the returns could be due to random chance rather than a robust underlying strategy.

Performance by Symbol
6 symbols, closed trades only
Symbol Trades Net P&L Win rate Profit factor
QQQ 14 +$54,264 57.1% 8.10
AMD 15 +$43,092 66.7% 2.72
NVDA 16 +$21,770 31.2% 2.53
MSFT 16 +$5,528 37.5% 1.35
AAPL 20 -$1,303 35.0% 0.91
AMZN 16 -$15,936 25.0% 0.21
Profit Concentration
How much of the result depends on the largest winning trades (closed trades only)
Scenario Net profit Total return Profit removed
Headline result +$107,035 +107.0% –
Excluding the single largest winner +$66,235 +66.2% $40,800
Excluding the 5 largest winners -$14,524 -14.5% $121,559

Is the Average Trade Different From Zero?
One-sample t-test on per-trade returns; overlapping trades are not independent, so treat the p-value as indicative
Statistic Value
Closed trades tested 97
Mean return per trade +12.22%
Standard error 10.20 pp
95% confidence interval for the mean -8.03% to +32.46%
t-statistic (H0: mean = 0) 1.20
p-value (two-sided) 0.2339

How Bad Did It Get?

This Keltner Channel Volatility Breakout strategy faces noticeable risks, particularly highlighted by its maximum drawdown of 30%. This means that during its worst period, the strategy could lose close to one-third of its equity before starting to recover. The recovery from this drawdown took about 60 days, indicating significant potential stress over an extended time frame.

The risk-adjusted return metrics present a mixed picture. With a Sharpe ratio of 1.12, the strategy earned slightly more than one unit of return for each unit of risk taken, which is generally acceptable. However, the Sortino ratio, at 0.11, indicates that the returns were not proportionate to the downside risk. This suggests that while there were some profitable trades, the good months did not sufficiently outweigh the less favorable ones.

An integrity audit revealed some flaws in the backtesting process, specifically regarding exposure checks. The peak gross long exposure exceeded the actual equity in the account. This means that the reported returns might overstate what a strictly unleveraged account could have achieved. Additionally, the failed percentage checks imply that some of the data’s presentation may lack precision. Hence, while the strategy shows promise, caution is warranted when interpreting its performance metrics due to these inherent risks and issues.

What Stood Out

  • The strategy’s total return of 107% significantly lagged behind the equal-weight buy-and-hold benchmark return of 166%, presenting a difference of about 59%.
  • Despite a win rate of 41%, the strategy was able to achieve a profit factor of 2.11, indicating that its profitable trades substantially outweighed its losses.
  • Some of the most significant gains came from a few standout trades, as excluding the five largest winners resulted in a net loss of about 14%.

Limits of This Backtest

  • The strategy generated only 97 closed trades, which may limit statistical significance and overall reliability.
  • There was a maximum drawdown of 30%, indicating considerable risk during the worst performance period.
  • An integrity audit revealed issues with exposure checks, suggesting that the reported returns could overstate what an unleveraged account could realistically achieve.
  • Monthly returns demonstrated more negative months (20) than positive months (14), which indicates more frequent losing periods over the test duration.

Monthly Returns

Monthly Returns Matrix (%)
Month-by-month and YTD cumulative performance summary
Year Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec YTD
2023 – – – – – – – 0.0% 0.0% 0.8% 11.7% 4.2% 17.3%
2024 6.5% 0.7% -3.2% 0.2% 0.6% 22.5% -3.1% -3.7% -2.6% 0.0% -1.2% -1.3% 13.5%
2025 -4.8% -1.1% -3.9% -3.9% 9.9% 12.9% 20.6% -3.8% -2.2% 5.8% -5.9% -1.3% 20.3%
2026 0.0% -6.3% -1.2% 24.6% 22.5% -6.5% 0.9% -2.9% – – – – 29.3%

The backtest reported returns across 37 months, with 14 months showing positive returns, 20 months negative, and 3 months flat. The best month was May 2026, achieving a return of 26%, while the worst was July 2026, with a decline of 7%.

Notably, the largest drawdown episode occurred between July 2024 and April 2025, reaching a depth of 30% over 193 days. This episode aligns with several negative monthly returns, particularly during 2025, which included months of losses.

Conclusions

This backtest of the Keltner Channel Volatility Breakout strategy yielded a total return of 107% over three years, increasing the equity from $100,000 to approximately $207,035. While it did outperform the SPY buy-and-hold strategy, which returned 77%, it lagged significantly behind an equal-weight buy-and-hold basket of the same stocks, which achieved a total return of 166%. The strategy maintained a win rate of about 41% across 97 closed trades, with some large gains contributing disproportionately to the overall profit, implying a reliance on a few big winners.

One major caveat to consider is the maximum drawdown of approximately 30%, indicating significant risk during its worst performance phase. This drawdown required about 60 days to recover, highlighting potential stress for traders. It’s important to note that this analysis is purely for educational purposes, not investment advice, and past performance does not guarantee future results. Readers are encouraged to explore their own strategy ideas using quantstr.at.

Ideas to Test Next

The strategy’s maximum drawdown of 30% indicates significant risk during adverse market conditions. By adding a stop-loss, you may help limit losses during downturns, enhancing risk management and potentially improving overall performance.

Trade QQQ, NVDA, AAPL, MSFT, AMD, and AMZN using a Keltner Channel Volatility Breakout strategy. Enter long when price breaks above the 20-period upper band and 14-period ADX > 25 (confirming trend strength). Exit when price closes below the 20-period EMA center line. Additionally, set a stop-loss to exit the position if losses reach 5%.

More Backtests

Setup, Audit & Replication

Backtest Setup
Read from this run’s saved configuration
Parameter Setting / Value
Asset universe 6 symbols (QQQ, NVDA, AAPL, MSFT, AMD, AMZN)
Time window 2023-08-29 to 2026-08-27 (3.0 years)
Starting equity $100,000
Benchmarks Equal-weight buy & hold basket (6 symbols) (+166.3%); Buy & hold SPY (+77.0%)
Position size (as executed) 200 units per trade
Full per-symbol trade statistics ▾
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
QQQ 14 $54,264 57.1% 42.9% $58,804 $0 $61,903 -$7,639 -$11,040 28 $3,876 $3,876 $54,264 $26,405 -$2,767 8.10 $7,738 -$1,273 $718 7.28 $8,455 $8,455 6.08 -$1,365 $3,922 2.87 4.92
NVDA 16 $21,770 31.2% 62.5% $29,810 $0 $36,023 -$14,253 -$10,478 32 $1,361 $1,361 $21,770 $22,083 -$2,749 2.53 $7,205 -$1,425 -$560 3.27 $6,614 $6,614 5.05 -$1,560 $610 0.39 2.08
AAPL 20 -$1,303 35.0% 65.0% $9,880 -$1,437 $13,044 -$14,347 -$11,317 40 -$65 -$65 -$1,303 $6,145 -$4,476 0.91 $1,863 -$1,104 -$500 -0.49 $2,101 $2,101 1.69 -$835 $526 0.63 -0.12
MSFT 16 $5,528 37.5% 62.5% $11,197 -$8,827 $21,303 -$15,775 -$13,204 33 $346 $346 $5,148 $13,839 -$3,345 1.35 $3,551 -$1,577 -$718 1.34 $4,083 $4,083 2.25 -$1,494 $1,110 0.74 0.39
AMD 15 $43,092 66.7% 33.3% $55,694 -$310 $68,094 -$25,002 -$21,574 30 $2,873 $2,873 $43,092 $40,800 -$10,560 2.72 $6,809 -$5,000 $594 3.92 $11,649 $11,649 1.36 -$3,024 $2,221 0.73 2.00
AMZN 16 -$15,936 25.0% 75.0% $3,434 -$16,216 $4,150 -$20,086 -$19,650 32 -$996 -$996 -$15,936 $2,172 -$4,508 0.21 $1,037 -$1,674 -$1,104 -9.77 $1,618 $1,618 0.62 -$1,286 $959 0.75 -0.81
📊 View R Report 🐍 View Python Report
View R Strategy Code ▾
# Load required libraries
library(quantmod)
library(quantstrat)

# Set parameters
start_date <- '2023-08-29'
end_date <- '2026-08-27'
init_equity <- 100000
symbols <- c('QQQ', 'AAPL', 'MSFT', 'NVDA', 'AMD', 'AMZN')

# Get historical data
getSymbols(symbols, from = start_date, to = end_date)

# Initialize the portfolio, account, and orders
initPortf('KeltnerADX', symbols = symbols)
initAcct('KeltnerADX', portfolios = 'KeltnerADX', initEq = init_equity)
initOrders(portfolio = 'KeltnerADX')

# Define the strategy
strategy.st <- 'KeltnerADX'
rm <- 'KeltnerADX'

# Initialize strategy
initStrategy(strategy.st)

# Define indicators
add.indicator(strategy = strategy.st, name = 'EMA', arguments = list(x = quote(Cl(mktdata)), n = 20), label = 'EMA20')
add.indicator(strategy = strategy.st, name = 'KeltnerChannel', arguments = list(HLC = quote(HLC(mktdata)), n = 20, sd = 1.5), label = 'KC')
add.indicator(strategy = strategy.st, name = 'ADX', arguments = list(x = quote(HLC(mktdata)), n = 14), label = 'ADX')

# Define signals
add.signal(strategy = strategy.st, name = 'sigThreshold', arguments = list(column = 'KC.upper', threshold = 0, relationship = 'gt'), label = 'longEntry')
add.signal(strategy = strategy.st, name = 'sigThreshold', arguments = list(column = 'EMA20', threshold = 0, relationship = 'lt'), label = 'longExit')

# Define rules
add.rule(strategy = strategy.st, name = 'ruleSignal', arguments = list(sigcol = 'longEntry', sigval = TRUE, ordertype = 'market', orderside = 'long', replace = FALSE, TxnFees = 0), type = 'enter')
add.rule(strategy = strategy.st, name = 'ruleSignal', arguments = list(sigcol = 'longExit', sigval = TRUE, ordertype = 'market', orderside = 'long', replace = TRUE, TxnFees = 0), type = 'exit')

# Apply the strategy
applyStrategy(strategy = strategy.st, portfolios = 'KeltnerADX')

# Update the portfolio
updatePortf('KeltnerADX')

# Get performance summary
chart.Posn('KeltnerADX', Symbol = 'QQQ')

# Print final portfolio summary
getPortfolio('KeltnerADX')
View Python Strategy Code ▾
import yfinance as yf
import pandas as pd
import vectorbt as vbt

# Set parameters
symbols = ['QQQ', 'AAPL', 'MSFT', 'NVDA']
start_date = '2023-08-29'
end_date = '2026-08-27'

# Download historical data
prices = yf.download(symbols, start=start_date, end=end_date)['Adj Close']

# Calculate indicators
ema20 = prices.ewm(span=20).mean()
upper_band = prices.rolling(window=20).mean() + (prices.rolling(window=20).std() * 1.5)
lower_band = prices.rolling(window=20).mean() - (prices.rolling(window=20).std() * 1.5)
adx = vbt.ADX.run(prices, window=14).adx

# Define entry and exit signals
long_entry = (prices > upper_band) & (adx > 25)
long_exit = prices < ema20

# Create portfolio
portfolio = vbt.Portfolio.from_signals(prices, long_entry, long_exit, init_cash=100000)

# Print portfolio stats
print(portfolio.stats())
View Quarto Markdown Replication (.qmd) ▾
---
title: "Strategy Replication Guide: Keltner ADX Volatility Breakout Strategy"
format: html
editor: visual
execute:
  eval: false
---

# Overview
This Quarto Markdown notebook provides reproducible R and Python code to replicate the **Keltner ADX Volatility Breakout Strategy** backtest (2023-08-29 to 2026-08-27).

## R Replication (quantstrat)

```{r}
#| label: r-strategy-replication
#| message: false
#| warning: false

# Load required libraries
library(quantmod)
library(quantstrat)

# Set parameters
start_date <- '2023-08-29'
end_date <- '2026-08-27'
init_equity <- 100000
symbols <- c('QQQ', 'AAPL', 'MSFT', 'NVDA', 'AMD', 'AMZN')

# Get historical data
getSymbols(symbols, from = start_date, to = end_date)

# Initialize the portfolio, account, and orders
initPortf('KeltnerADX', symbols = symbols)
initAcct('KeltnerADX', portfolios = 'KeltnerADX', initEq = init_equity)
initOrders(portfolio = 'KeltnerADX')

# Define the strategy
strategy.st <- 'KeltnerADX'
rm <- 'KeltnerADX'

# Initialize strategy
initStrategy(strategy.st)

# Define indicators
add.indicator(strategy = strategy.st, name = 'EMA', arguments = list(x = quote(Cl(mktdata)), n = 20), label = 'EMA20')
add.indicator(strategy = strategy.st, name = 'KeltnerChannel', arguments = list(HLC = quote(HLC(mktdata)), n = 20, sd = 1.5), label = 'KC')
add.indicator(strategy = strategy.st, name = 'ADX', arguments = list(x = quote(HLC(mktdata)), n = 14), label = 'ADX')

# Define signals
add.signal(strategy = strategy.st, name = 'sigThreshold', arguments = list(column = 'KC.upper', threshold = 0, relationship = 'gt'), label = 'longEntry')
add.signal(strategy = strategy.st, name = 'sigThreshold', arguments = list(column = 'EMA20', threshold = 0, relationship = 'lt'), label = 'longExit')

# Define rules
add.rule(strategy = strategy.st, name = 'ruleSignal', arguments = list(sigcol = 'longEntry', sigval = TRUE, ordertype = 'market', orderside = 'long', replace = FALSE, TxnFees = 0), type = 'enter')
add.rule(strategy = strategy.st, name = 'ruleSignal', arguments = list(sigcol = 'longExit', sigval = TRUE, ordertype = 'market', orderside = 'long', replace = TRUE, TxnFees = 0), type = 'exit')

# Apply the strategy
applyStrategy(strategy = strategy.st, portfolios = 'KeltnerADX')

# Update the portfolio
updatePortf('KeltnerADX')

# Get performance summary
chart.Posn('KeltnerADX', Symbol = 'QQQ')

# Print final portfolio summary
getPortfolio('KeltnerADX')
```

## Python Replication (VectorBT)

```{python}
#| label: python-strategy-replication

import yfinance as yf
import pandas as pd
import vectorbt as vbt

# Set parameters
symbols = ['QQQ', 'AAPL', 'MSFT', 'NVDA']
start_date = '2023-08-29'
end_date = '2026-08-27'

# Download historical data
prices = yf.download(symbols, start=start_date, end=end_date)['Adj Close']

# Calculate indicators
ema20 = prices.ewm(span=20).mean()
upper_band = prices.rolling(window=20).mean() + (prices.rolling(window=20).std() * 1.5)
lower_band = prices.rolling(window=20).mean() - (prices.rolling(window=20).std() * 1.5)
adx = vbt.ADX.run(prices, window=14).adx

# Define entry and exit signals
long_entry = (prices > upper_band) & (adx > 25)
long_exit = prices < ema20

# Create portfolio
portfolio = vbt.Portfolio.from_signals(prices, long_entry, long_exit, init_cash=100000)

# Print portfolio stats
print(portfolio.stats())
```

Backtest Integrity Audit: 7 of 9 checks passed

  • ✅ Headline Return Matches Ending vs Starting Equity: From dollars: 107.03% | Reported field: 107.03% | Gap: 0.000 pp
  • ✅ Equity Curve Starts at Initial Equity and Ends at Ending Value: First point: $100,000 (initial $100,000) | Last point: $207,035 (ending $207,035)
  • ✅ Monthly Returns Compound to Headline Return: Compounded months: 107.03% | Headline: 107.03% | Gap: 0.00 pp
  • ✅ Trade List Reconciles With Per-Symbol Trade Statistics: Trade-list rows: 98 | open at end: 1 | closed per trade statistics: 97 | open rows isolated: yes
  • ✅ Sharpe Ratio Consistent With Annualized Return and Volatility: Reported Sharpe: 1.12 | Annualized return / volatility: 1.12
  • ⚠️ Gross Long Exposure Never Exceeds Portfolio Equity: Peak gross long exposure was 218.9% of contemporaneous equity (on 2024-08-16)
  • ✅ Backtest Generated Closed Trades (>0): Total closed trades executed: 97
  • ✅ Reproducible R and Python Code Files Present: strategy_code.R: present | python_vectorbt.py: present
  • ⚠️ Prose Percentages Are Integer-Rounded to Whole Numbers: Found unrounded percentages: 29.92%, 13.60%, 681.43%