Long softs outright when El Nino is established

Buy coffee, cocoa, sugar and cotton whenever El Niño takes hold, and hold nothing the rest of the time. Over twenty-five years it gained about 5% a year, but the swings were wide enough that the result cannot be told apart from luck.

Conclusion: Failed

Failed: the 95% range spans zero and 54.8% of profit arrived in five months. Indicative performance of 5.1% a year at 20.3% volatility, return for risk taken 0.25 (95% range (0.13) to 0.66). Exposure was held in 282 of 298 months.

Figure 1 · Growth of $1

2001–2026 · net of 15bp round-trip costs
What one dollar became, after costs: $3.57 by 2026. Shown against S&P 500, rebased to the same starting dollar.
Return for risk
0.25
range (0.13) to 0.66
Ann. return
5.1%
volatility 20.3%
Max drawdown
110.4%
over 298 months
Beats the search
1.8%
chance, after correction

Method

Hypothesis

The textbook trade, stated plainly, for contrast.

Rule

UNIVERSE = [SB=F KC=F CC=F CT=F]
TRIGGER = -1.0 standard deviations
function positions(bar):
z = zscore(feature(soi_3m), expanding, at least 120 months)
if z >= TRIGGER:
return {} # only while El Nino is established
return equal_risk(UNIVERSE, bar)
# ── 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

2001-09 – 2026-06 · 298 months, 282 invested
MeasureValue
Annualised return5.1%
Annualised volatility20.3%
Return for risk taken0.25
95% range(0.13) to 0.66
t-statistic1.26
Maximum drawdown110.4%
Hit rate52.8%
Annual turnover0.78×
Return skew(0.23)
Return kurtosis3.56
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.34return for risk≤ 00 – 0.40≥ 0.40
Holdout half (2015+)0.14return for risk≤ 00 – 0.40≥ 0.40
Top-5-month share of profit54.8%how much rode on a few months — lower is better≥ 40%25 – 40%< 25%
Top-2-year share of profit63.9%concentration
Distinct trading episodes7how many independent runs this really is< 5050 – 100≥ 100
Chance the edge is real89.3%non-normality corrected
Chance it beats the whole search1.8%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 · Net return by calendar year

after costs
20012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026+41%-41%
YearNet return
20012.8%
200230.1%
2003(1.4%)
200412.6%
200523.4%
2006(3.7%)
200710.8%
2008(5.2%)
200940.7%
201040.5%
2011(29.8%)
2012(20.1%)
2013(3.9%)
20142.2%
2015(9.6%)
2016(3.6%)
2017(8.0%)
2018(7.0%)
20198.6%
20206.5%
202128.2%
2022(12.1%)
20238.4%
202439.0%
2025(19.5%)
2026(2.7%)

Table 4 · Concentration detail

MeasureValue
Best years2009:+41%, 2010:+40%, 2024:+39%
Worst year2011:-30%

Reference

Instruments

last price and one-month change, live from Yahoo Finance
SymbolNotes
SB=FSugar No. 11 — continuous futures on ICE Futures, quoted in US cents.
KC=FCoffee C — continuous futures on ICE Futures, quoted in US cents.
CC=FCocoa — continuous futures on ICE Futures, in USD.
CT=FCotton No. 2 — continuous futures on ICE Futures, quoted in US cents.

Sample

2001-09 – 2026-06 · 298 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
02TestedFailedBenchmark, not scored against the criteria
algorithmic-trading-anthology-july-2026-s6-elnino-softs-long | d/rksci