,

GLD, TLT, DBC, UUP Donchian ADX ATR Multi-Asset Long Only Backtest: Profitable, But Risk-Adjusted Evidence Is Weak

A concise quant research note testing Donchian ADX ATR Multi-Asset Long Only on GLD, TLT, DBC, UUP, with rules, benchmark comparison, friction, and stress testing.

Desk: ,
Equity curve and drawdown for Donchian ADX ATR Multi-Asset Long Only

Executive Summary

Can a simple, volatility-scaled breakout model capture persistent trends across gold, bonds, commodities, and the U.S. dollar without relying on equity-market direction? This run covers 2010-01-01 to 2026-01-01 using quantstrat in R, with initial equity of $100,000. I compare it with buy-and-hold, apply base friction, and run a first sequence <span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="A deliberate check that makes assumptions harsher to see whether the result survives outside ideal conditions." data-tooltip="A deliberate check that makes assumptions harsher to see whether the result survives outside ideal conditions." aria-label="stress test: A deliberate check that makes assumptions harsher to see whether the result survives outside ideal conditions.">stress test</span>.

Research Card

FieldDetail
InstrumentGLD, TLT, DBC, UUP
Strategy typeDiversified trend following / Donchian breakout
Publication archetypetransparent cross-asset replication and robustness study
Test period2010-01-01 to 2026-01-01
Enginequantstrat in R
Starting equity$100,000
Strategy net P&L$18,878
Strategy <span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="Compound annual growth rate. It converts the full-period return into an annualized growth pace." data-tooltip="Compound annual growth rate. It converts the full-period return into an annualized growth pace." aria-label="CAGR: Compound annual growth rate. It converts the full-period return into an annualized growth pace.">CAGR</span>1.09%
Strategy max <span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="The fall from a prior equity high to a later low. It measures the pain a trader had to sit through." data-tooltip="The fall from a prior equity high to a later low. It measures the pain a trader had to sit through." aria-label="drawdown: The fall from a prior equity high to a later low. It measures the pain a trader had to sit through.">drawdown</span>-17.46%
Trades87
<span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="The comparison hurdle. For a single ETF strategy, buy-and-hold of the same ETF is the first test to beat." data-tooltip="The comparison hurdle. For a single ETF strategy, buy-and-hold of the same ETF is the first test to beat." aria-label="Benchmark: The comparison hurdle. For a single ETF strategy, buy-and-hold of the same ETF is the first test to beat.">Benchmark</span>Buy and hold GLD
Benchmark CAGR8.35%
CAGR spread-7.27%
Costs/stressBase friction and <span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="A simulation method that repeatedly reshuffles the same historical trades to see how much the sequence alone changes the path." data-tooltip="A simulation method that repeatedly reshuffles the same historical trades to see how much the sequence alone changes the path." aria-label="Monte Carlo: A simulation method that repeatedly reshuffles the same historical trades to see how much the sequence alone changes the path.">Monte Carlo</span> sequence stress included
VerdictTrailed buy-and-hold on CAGR before costs

The Setup

The counter-narrative here is that some ETFs don’t reward constant attention so much as they reward waiting for a clean break and then staying with it. My hypothesis is that a long-only trend filter can work across very different macro exposures-gold, duration, commodities, and the dollar-because each can spend long stretches in persistent regimes driven by policy cycles, inflation shocks, or flight-to-quality flows.

Results

MetricStrategyBenchmarkWhy it matters
Final Equity$118,878$360,938Ending account value before full friction modeling.
CAGR1.09%8.35%Annualized growth; the first hurdle is buy-and-hold.
Max Drawdown-17.46%-45.56%Worst peak-to-trough decline.
<span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="Return per unit of volatility. It asks whether the strategy was paid enough for the ride it took." data-tooltip="Return per unit of volatility. It asks whether the strategy was paid enough for the ride it took." aria-label="Sharpe Ratio: Return per unit of volatility. It asks whether the strategy was paid enough for the ride it took.">Sharpe Ratio</span>1.900.59Return earned per unit of volatility.
<span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="Like Sharpe, but it only penalizes downside volatility, which is often what traders actually care about." data-tooltip="Like Sharpe, but it only penalizes downside volatility, which is often what traders actually care about." aria-label="Sortino Ratio: Like Sharpe, but it only penalizes downside volatility, which is often what traders actually care about.">Sortino Ratio</span>0.210.80Like Sharpe, but focused on downside volatility.
<span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="Annualized return divided by maximum drawdown. It asks how much growth the strategy earned for the pain it caused." data-tooltip="Annualized return divided by maximum drawdown. It asks how much growth the strategy earned for the pain it caused." aria-label="Calmar Ratio: Annualized return divided by maximum drawdown. It asks how much growth the strategy earned for the pain it caused.">Calmar Ratio</span>0.06N/ACAGR divided by max drawdown.
<span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="Gross profits divided by gross losses. Above 1 means winners outweighed losers before considering other constraints." data-tooltip="Gross profits divided by gross losses. Above 1 means winners outweighed losers before considering other constraints." aria-label="Profit Factor: Gross profits divided by gross losses. Above 1 means winners outweighed losers before considering other constraints.">Profit Factor</span>1.14N/AGross profits divided by gross losses.
<span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="Average winning trade divided by average losing trade. It shows whether wins are large enough to pay for losses." data-tooltip="Average winning trade divided by average losing trade. It shows whether wins are large enough to pay for losses." aria-label="Payoff Ratio: Average winning trade divided by average losing trade. It shows whether wins are large enough to pay for losses.">Payoff Ratio</span>1.71N/AAverage winner divided by average loser.
Median Trade$-105N/AA reality check against a few outsized trades.
<span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="The share of the test period where capital was actually in the market. Lower exposure can change how returns should be judged." data-tooltip="The share of the test period where capital was actually in the market. Lower exposure can change how returns should be judged." aria-label="Exposure: The share of the test period where capital was actually in the market. Lower exposure can change how returns should be judged.">Exposure</span>64.39%100.00%Percentage of days with capital at risk.
Equity Curve And Drawdown
Monthly Return Heatmap

The backtest only weakly supported my original hypothesis. Final equity was $118,877.74 on $100,000 start, with net P&L of $18,877.74 and CAGR of 1.09%, so the return story is modest at best.

Statistical Limits And Regime Dependence

The key question is whether the evidence survives sampling uncertainty, clustered trades, and regime shifts. P-values are diagnostics, not trading signals.

Statistical CheckEvidenceWhy it matters
Bootstrap mean trade P&L$-44.56 to $207.26 95% intervalTests whether the <span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="The typical profit or loss per closed trade. Small values are fragile because costs can erase them quickly." data-tooltip="The typical profit or loss per closed trade. Small values are fragile because costs can erase them quickly." aria-label="average trade: The typical profit or loss per closed trade. Small values are fragile because costs can erase them quickly.">average trade</span> edge survives resampling.
Bootstrap mean positive rate84.3%Useful for direction, but not a substitute for <span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="The later portion of the historical test, used here to check whether the result held up after the initial sample." data-tooltip="The later portion of the historical test, used here to check whether the result held up after the initial sample." aria-label="out-of-sample: The later portion of the historical test, used here to check whether the result held up after the initial sample.">out-of-sample</span> proof.
Binomial win-rate diagnosticp=0.6476Checks whether the hit rate is unusual versus a neutral 50% baseline.
Wilcoxon trade-P&L diagnosticp=0.7628Non-parametric check of whether trade P&L is centered above zero.
Equity-return serial dependencep=0.0000Flags whether daily strategy returns are path-dependent.
Closed-trade serial dependencep=N/AChecks whether trade outcomes cluster.
Analysis AreaStatusArtifact
trade_edgelimitedstatistical_diagnostics.rds/per_trade_stats.rds
win_ratelimitedstatistical_diagnostics.rds/per_trade_stats.rds
path_risklimitedstress_summary.rds
cost_sensitivityreadyfriction_summary.rds
parameter_robustnessmissingparameter_sensitivity.rds
sample_splitlimitedstress_summary.rds
walk_forwardmissingwalk_forward_summary.rds
serial_dependencereadystatistical_diagnostics.rds

Regime dependency: this setup can degrade when volatility expands, gaps dominate, or the sector enters a persistent trend. The in-sample/out-of-sample split further down tests stability, not parameter-optimization skill.

Costs And Stress Test

I now run a simple friction pass before drafting the article. For this strategy test, the base case assumes 3.00 bps of <span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="The difference between the expected execution price and the actual fill. It usually gets worse in fast or illiquid markets." data-tooltip="The difference between the expected execution price and the actual fill. It usually gets worse in fast or illiquid markets." aria-label="slippage: The difference between the expected execution price and the actual fill. It usually gets worse in fast or illiquid markets.">slippage</span> per side and 1.00 bps of spread cost per side, or 8.00 bps per round trip. That is still a model, but it is a better starting point than pretending fills are free.

Friction CheckValue
Gross net P&L$18,878
Base <span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="The full entry-plus-exit trading cost for one completed trade, expressed here in basis points." data-tooltip="The full entry-plus-exit trading cost for one completed trade, expressed here in basis points." aria-label="round-trip cost: The full entry-plus-exit trading cost for one completed trade, expressed here in basis points.">round-trip cost</span>$63
<span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="Strategy profit after the friction model subtracts estimated spread, slippage, and commission costs." data-tooltip="Strategy profit after the friction model subtracts estimated spread, slippage, and commission costs." aria-label="Cost-adjusted net P&amp;L: Strategy profit after the friction model subtracts estimated spread, slippage, and commission costs.">Cost-adjusted net P&L</span>$1,174
Gross profit factor1.14
Cost-adjusted profit factor2.16
<span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="The estimated round-trip cost level where the historical net profit would be fully consumed by trading costs." data-tooltip="The estimated round-trip cost level where the historical net profit would be fully consumed by trading costs." aria-label="Break-even round-trip cost: The estimated round-trip cost level where the historical net profit would be fully consumed by trading costs.">Break-even round-trip cost</span>2395.24 bps
Stress CheckValue
Monte Carlo simulations5000
Observed trade-order max drawdown-0.36%
<span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="A conservative drawdown estimate: only about 5% of reshuffled paths were worse than this level." data-tooltip="A conservative drawdown estimate: only about 5% of reshuffled paths were worse than this level." aria-label="95th percentile drawdown: A conservative drawdown estimate: only about 5% of reshuffled paths were worse than this level.">95th percentile drawdown</span>-0.47%
Median simulated ending P&L$1,096
<span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="The low-end bootstrapped outcome. It asks what the strategy looked like when the sampled trades were less favorable than history." data-tooltip="The low-end bootstrapped outcome. It asks what the strategy looked like when the sampled trades were less favorable than history." aria-label="Weak-path ending P&amp;L: The low-end bootstrapped outcome. It asks what the strategy looked like when the sampled trades were less favorable than history.">Weak-path ending P&L</span>$-650
High-cost net P&L$1,111
<span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="The earlier portion of the historical test, used here as a rough development-period diagnostic." data-tooltip="The earlier portion of the historical test, used here as a rough development-period diagnostic." aria-label="In-sample: The earlier portion of the historical test, used here as a rough development-period diagnostic.">In-sample</span> net P&L$83 over 13 trades
Out-of-sample net P&L$1,154 over 6 trades
Out-of-sample profit factor4.06
In-sample avg trade / profit factor$6.36 / 1.14 (13 trades)
Out-of-sample avg trade / profit factor$192.31 / 4.06 (6 trades)
Walk-forward windowsNot available — rolling walk-forward needs at least 30 closed trades with dated windows; this run only produced 19
<span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="A rerun grid that changes strategy settings to see whether the result survives nearby parameter choices." data-tooltip="A rerun grid that changes strategy settings to see whether the result survives nearby parameter choices." aria-label="Parameter sensitivity: A rerun grid that changes strategy settings to see whether the result survives nearby parameter choices.">Parameter sensitivity</span>Pending rerun grid; Parameter sensitivity requires rerunning the strategy across a parameter grid; no rerun grid artifact is available for this saved backtest yet.

Watch the break-even round-trip cost: above that level, the historical edge is gone. Monte Carlo separates edge from path luck; the in/out row is a chronological closed-trade split (first 70% of trades vs. the rest), not walk-forward optimization. The out-of-sample half looks stronger here, but with only 13 in-sample and 6 out-of-sample trades this is far too small a sample to read as a real improvement — it mainly shows that this particular split isn’t a red flag, not that the edge is confirmed.

What Breaks It

Per-Trade P&L Distribution

The trade P&L distribution is ugly in the way that matters: most trades cluster near flat to slightly negative, while a few outsized winners carry the whole result. Across all 87 trades, the median trade was -$105.03 and the <span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="The percentage of trades that made money. It only matters alongside payoff size and loss size." data-tooltip="The percentage of trades that made money. It only matters alongside payoff size and loss size." aria-label="win rate: The percentage of trades that made money. It only matters alongside payoff size and loss size.">win rate</span> was only 38.9%, so the typical trade lost money rather than paying for the losers — the result rests on a small number of large winners (the largest single winner made $6,336) offsetting a majority of small losses.

Trade Log

Order Blotter — Donchian ADX ATR Multi-Asset Long Only – GLD, TLT, DBC, UUP
Live
Fills0 / 0 Positionflat Win rate Realized P&L$0.00
TimeSideSymbolQty PriceTrade P&L Cum P&L
scroll into view to replay fills…

Verdict

I set out to test whether a simple long-only breakout system, filtered by trend strength and regime, could extract durable upside from a small set of macro ETFs without relying on constant participation.

What would change my mind: the same rule needs to survive costs while closing the gap versus buy-and-hold, or prove useful as a timing overlay rather than a standalone strategy.

R Notes

The reproducibility contract is the saved artifact set plus the R scripts that created it. The key JSON/RDS files are recorded in the frontmatter.

# Rebuild the evidence layer for one saved strategy output
source('backtesting_blog_posts/statistical_diagnostics.R')
source('backtesting_blog_posts/walk_forward_diagnostics.R')
source('backtesting_blog_posts/analysis_test_selector.R')

out_dir <- file.path('backtesting_blog_posts', 'outputs', 'strat_donchian_adx_atr_multiasset_long_only_multiasset_donchian_adx_atr_trend_test')
per_trade <- readRDS(file.path(out_dir, 'per_trade_stats.rds'))
portfolio <- readRDS(file.path(out_dir, 'portfolio_summary.rds'))

diagnostics <- build_statistical_diagnostics(per_trade, portfolio)
walk_forward <- build_walk_forward_summary(per_trade)
test_plan <- build_analysis_test_plan(out_dir)

For the current <span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="Percentage Bands, a band-style indicator that marks stretched prices relative to recent behavior." data-tooltip="Percentage Bands, a band-style indicator that marks stretched prices relative to recent behavior." aria-label="PBands: Percentage Bands, a band-style indicator that marks stretched prices relative to recent behavior.">PBands</span> series, parameter_sensitivity uses a deterministic next-bar rerun helper because native quantstrat parameter clones can fail to propagate osMaxPos limits safely. Future strategy families should call register_parameter_sensitivity_builder() with their own sensitivity builder.

Important: Backtests are simulations. Past performance of a simulated strategy does not guarantee future results. Results do not yet fully account for real-world factors including <span class="lf0-tooltip" tabindex="0" style="border-bottom: 1px dotted currentColor; cursor: help;" title="The gap between the price buyers bid and sellers ask. Crossing that gap is a real trading cost." data-tooltip="The gap between the price buyers bid and sellers ask. Crossing that gap is a real trading cost." aria-label="bid-ask spread: The gap between the price buyers bid and sellers ask. Crossing that gap is a real trading cost.">bid-ask spread</span>, market impact, borrow costs, or taxes. This is for educational purposes only and is not investment advice.

Research disclaimer

This material is provided for research and educational purposes only. It is not investment advice, a recommendation, or an offer to buy or sell any security or strategy.

New stock analysis in your inbox.

Independent equity research on under-the-radar companies from lf0 — free, when new work publishes.




No spam. Unsubscribe anytime.

Prefer Substack? Follow lf0 Research on Substack

Leave a Comment

Your email address will not be published. Required fields are marked *