Volatility-managed Halloween

Two rules had already worked on their own: sit out the northern summer and hold shares only from November to April, and separately, hold less whenever markets have recently been turbulent. This combines them. The result was a portfolio identical to the first rule alone — the testing framework keeps total exposure fixed, so it cannot see the second rule at all.

Conclusion: Voided

Withdrawn. The combination is mathematically identical to one of its two ingredients — maximum weight difference 1.1e-16, return correlation 1.000 — because the framework holds total exposure fixed. The idea is untested rather than disproved.

Figure 1 · Growth of $1

1971–2026 · net of 15bp round-trip costs
What one dollar became, after costs: $19.27 by 2026. Shown against S&P 500, rebased to the same starting dollar.
Return for risk
0.58
range 0.32 to 0.85
Ann. return
5.4%
volatility 9.3%
Max drawdown
36.1%
over 656 months
Beats the search
25.6%
chance, after correction

Method

Hypothesis

Two rules that each passed on their own — holding shares only from November to April, and reducing exposure when markets have recently been turbulent — should work better combined than either does alone.

Rule

function positions(bar):
base = W3a.positions(bar)
realised = stdev(daily_returns(basket, 21 sessions, ending bar))
target = median(realised, expanding)
exposure = clip(target^2 / realised^2, 0, 1)
return base * exposure
# scale_to_gross() in execution renormalises the row, so multiplying
# every weight by one number leaves the target unchanged. See the
# write-up: this is why the experiment was withdrawn.
# ── 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.

Analysis

Basis for withdrawal

The harness normalises gross exposure to 1.0 by construction, so scaling an entire row of weights by a scalar is mathematically erased. Measured against W3a_HALLOWEEN_MULTI the maximum absolute weight difference is 1.1e-16, zero rows differ, and the net return streams correlate at 1.000. The hypothesis is not refuted — it is not expressible in this harness.

Rationale for combining

Two strategies had cleared the bar independently. The first is the old market saying 'sell in May and go away': hold a basket of stock markets from November through April, then sit in cash for the northern summer. The second ignores the calendar entirely and instead holds less whenever the market has recently been turbulent, on the grounds that turbulence persists while returns do not.

They read completely different information — one a date, the other recent volatility — so combining them is the obvious next question rather than a new idea, and it was pre-registered as a single additional trial.

It was initially scored as VALIDATED, beating its own base by 0.679 to 0.674. That margin is implausibly small for a change that should either help or hurt materially, which is what prompted the check.

Equivalence to the base strategy

Measured against the Halloween basket under the canonical harness, the maximum absolute weight difference is 1.1e-16, no row differs at all, and the two net return streams correlate at 1.000.

The cause is structural. The harness normalises every row to a gross exposure of 1.0 so that leverage is explicit and comparable across strategies. Multiplying an entire row of weights by a scalar — which is exactly what volatility targeting does — is removed by that normalisation. The earlier 0.679 came from an evaluation path that did not apply it.

The hypothesis is not refuted. Volatility targeting may well improve the seasonal basket; this harness simply cannot express the difference, because it deliberately holds gross exposure constant. Testing it properly requires an evaluator that permits varying leverage and charges for it.

A composition that cannot change gross exposure cannot express volatility targeting at all.

Evaluation

Table 1 · Performance

1971-11 – 2026-06 · 656 months, 330 invested
MeasureValue
Annualised return5.4%
Annualised volatility9.3%
Return for risk taken0.58
95% range0.32 to 0.85
t-statistic4.32
Maximum drawdown36.1%
Hit rate63.6%
Annual turnover2.07×
Return skew(0.09)
Return kurtosis10.62
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.64return for risk≤ 00 – 0.40≥ 0.40
Holdout half (2015+)0.36return for risk≤ 00 – 0.40≥ 0.40
Top-5-month share of profit18.7%how much rode on a few months — lower is better≥ 40%25 – 40%< 25%
Top-2-year share of profit17.7%concentration
Distinct trading episodes55how many independent runs this really is< 5050 – 100≥ 100
Chance the edge is real100.0%non-normality corrected
Chance it beats the whole search25.6%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
+27%-27%
YearNet return
19717.9%
197210.7%
1973(20.6%)
1974(15.5%)
197525.1%
197616.0%
1977(6.1%)
19784.6%
197911.2%
19804.5%
1981(2.0%)
1982(0.4%)
198316.2%
1984(2.6%)
198517.7%
19869.8%
198715.3%
19884.9%
198914.3%
19901.4%
199118.6%
19923.2%
19930.4%
1994(8.6%)
199513.1%
199612.8%
19977.3%
199820.1%
199927.1%
2000(4.9%)
20016.8%
2002(1.1%)
20035.5%
20047.9%
20050.5%
200615.2%
20074.4%
2008(12.4%)
200910.1%
20103.4%
2011(1.1%)
201210.5%
20135.4%
20142.2%
20154.1%
20162.7%
201710.0%
20180.0%
201911.9%
2020(3.8%)
20212.6%
2022(3.5%)
202310.8%
20242.7%
2025(0.7%)
20260.5%

Table 4 · Concentration detail

MeasureValue
Best years1999:+27%, 1975:+25%, 1998:+20%
Worst year1973:-21%

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.
CASHPathward Financial Inc. — listed on NasdaqGS, in USD.

Sample

1971-11 – 2026-06 · 656 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
01DraftLogged as a single additional trial, control W3a
02TestedValidatedReported 0.679 against the base strategy's 0.674
03ReviewedVoidedIdentical to the strategy it was built on — the framework holds total exposure fixed, so the change is invisible
algorithmic-trading-anthology-july-2026-vol-managed-halloween | d/rksci