12m time-series momentum, equity indices + FX only

The same trend-following rule, restricted to stock markets and currencies where the price data can be trusted. Still positive, still short of the bar, and it lost to simply buying and holding.

Conclusion: Failed

Failed on the reward for the risk, consistency across the history, the comparison against buying and holding and standing out from the search. Indicative performance of 3.9% a year is 1.9 points below the 5.8% returned by holding the same universe continuously, return for risk taken sits at 0.33 against the benchmark's 0.47, and the worst fall of 50.3% is 11.6 points below the benchmark's 61.9%.

Figure 1 · Growth of $1

1971–2026 · net of 15bp round-trip costs
What one dollar became, after costs: $8.54 by 2026. Shown against Control, S&P 500, rebased to the same starting dollar.
Return for risk
0.33
range 0.04 to 0.62
Ann. return
3.9%
volatility 11.8%
Max drawdown
50.3%
over 658 months
Beats the search
0.7%
chance, after correction

Method

Hypothesis

The same trend-following rule with the unreliable futures histories removed, leaving only stock indices and currencies. Trend-following is the best-documented pattern available on data this project can trust.

Rule

UNIVERSE = 7 equity indices + 7 FX pairs # no futures
LOOKBACK = 12 months
function positions(bar):
# identical rule to the previous experiment; only the universe
# differs, and it holds only instruments whose price history can
# be trusted
for symbol in UNIVERSE:
direction = sign(sum(returns(symbol, LOOKBACK, ending bar)))
vol = stdev(returns(symbol, 36 months, ending bar.prev))
w[symbol] = direction / vol
return w
# ── execution ────────────────────────────────────────────────────────
COST_PER_TRADE = 15 bps of the notional that changes hands
function on_bar(bar, book):
if not bar.is_month_end:
return # decisions are made monthly only
target = positions(bar) # the rule above
target = scale_to_gross(target, 1.0)
rebalance(book, target, bar)
function scale_to_gross(w, limit):
gross = sum(abs(w)) # total capital at work
if gross == 0:
return w # flat is a valid target
return w * limit / gross # leverage fixed, never implicit
function rebalance(book, target, bar):
for symbol in union(book.symbols, target.symbols):
delta = target[symbol] - book.weight(symbol)
if delta > 0:
buy(symbol, delta, fill = next_bar.close)
else if delta < 0:
sell(symbol, -delta, fill = next_bar.close)
book.charge(COST_PER_TRADE * abs(delta))
# Orders are filled at the next month's close, never the one the decision
# was made on. There is no stop_loss(), take_profit(), limit order or
# position cap: a holding changes only when positions() returns a
# different target at the next month end.

Evaluation

Table 1 · Performance

1971-09 – 2026-06 · 658 months, 658 invested
MeasureValue
Annualised return3.9%
Annualised volatility11.8%
Return for risk taken0.33
95% range0.04 to 0.62
t-statistic2.46
Maximum drawdown50.3%
Hit rate55.9%
Annual turnover2.50×
Return skew(0.58)
Return kurtosis9.01
The range is measured by resampling the history in blocks, so runs of good and bad months stay intact.

Table 2 · Robustness

DiagnosticValueReads asBadOkayYayHmm
Design half (pre-2015)0.39return for risk≤ 00 – 0.40≥ 0.40
Holdout half (2015+)(0.09)return for risk≤ 00 – 0.40≥ 0.40
Top-5-month share of profit27.8%how much rode on a few months — lower is better≥ 40%25 – 40%< 25%
Top-2-year share of profit27.7%concentration
Distinct trading episodes1how many independent runs this really is< 5050 – 100≥ 100
Chance the edge is real99.1%non-normality corrected
Chance it beats the whole search0.7%chance of beating the whole search by luck
bad/ok boundary is the pre-registered criterion · ok/good is a margin above it, not a criterion · blank where none was registered
The last row asks whether the result would stand out from the hundred-odd ideas tried here, rather than being judged alone.

Table 3 · Pre-registered criteria

measured against C5_RP_CLEAN
1FAILED2PASS3PASS4FAILED5FAILED6FAILED
CheckWhat it asksResult
1Reward large enough for the riskFAIL
2Confidence range clear of zeropass
3Profit not concentrated in a few monthspass
4Worked in both halves of the historyFAIL
5Beat buying and holdingFAIL
6Stood out from the whole searchFAIL
These six checks were written down before the strategy was run.

Table 4 · Net return by calendar year

after costs
+35%-35%
YearNet return
19712.9%
197214.5%
1973(9.0%)
197435.3%
1975(29.8%)
197617.5%
1977(5.7%)
1978(18.1%)
197911.3%
198022.9%
1981(21.2%)
198217.0%
198315.9%
1984(12.1%)
198523.1%
198613.7%
1987(12.4%)
1988(5.6%)
198924.1%
1990(11.8%)
199114.9%
19924.4%
19936.8%
1994(8.9%)
19956.2%
199616.1%
199723.9%
199816.0%
19998.8%
2000(7.9%)
20013.1%
20029.4%
20033.2%
200415.0%
20053.3%
20067.4%
20079.1%
200813.8%
2009(11.8%)
20104.4%
2011(8.8%)
20121.0%
201314.7%
20142.4%
20155.2%
2016(4.0%)
20178.4%
2018(0.3%)
20195.0%
2020(13.7%)
2021(3.1%)
2022(1.7%)
2023(4.3%)
20241.3%
20250.5%
20262.2%

Table 5 · Concentration detail

MeasureValue
Best years1974:+35%, 1989:+24%, 1997:+24%
Worst year1975:-30%

Reference

Instruments

last price and one-month change, live from Yahoo Finance
SymbolNotes
^GSPCS&P 500 — market index, in USD.
^N225Nikkei 225 — market index, in JPY.
^AXJOS&P/ASX 200 — market index, in AUD.
^BVSPIBOVESPA — market index, in BRL.
^NSEINIFTY 50 — market index, in INR.
^KLSEFTSE Bursa Malaysia KLCI — market index, in MYR.
^NZ50S&P/NZX 50 Gross — market index, in NZD.
AUD/USDAUD/USD — spot exchange rate.
NZD/USDNZD/USD — spot exchange rate.
AUD/NZDAUD/NZD — spot exchange rate.
AUD/JPYAUD/JPY — spot exchange rate.
USD/BRLUSD/BRL — spot exchange rate.
USD/INRUSD/INR — spot exchange rate.
USD/ZARUSD/ZAR — spot exchange rate.

Sample

1971-09 – 2026-06 · 658 months

Sources

NOAA Physical Sciences Laboratory. Climate indices. ONI, Niño 3.4, MEI v2, DMI, PDO, AMO, TNA, QBO, solar flux. psl.noaa.gov/data/climateindices/list
Queensland Department of Agriculture and Fisheries. Southern Oscillation Index. The Long Paddock. Monthly, 1876–. www.longpaddock.qld.gov.au/soi/soi-data-files
Yahoo Finance. Historical market prices. Adjusted close, month-end. Retrieved with yfinance. finance.yahoo.com

Revision history

RevStageStatusChange
01DraftDirection and criteria fixed before the experiment ran
02TestedFailedFell short on the reward for the risk, consistency across the history, the comparison against buying and holding, standing out from the search; control C5_RP_CLEAN
algorithmic-trading-anthology-july-2026-p1c-tsmom-clean | d/rksci