# BTC 4H volatility-scaled trailing-stop experiment

**Recommendation: shelve these ATR variants. Do not expand to more timeframes or
start a TradingView trial on this evidence.** Neither candidate passed the frozen
validation rule. The later bullish windows also favored the existing 5% stop.
This rejects the tested design, not every possible volatility-based stop.

The [protocol](mlp_volatility_stop_protocol_2026_09_21.md) was recorded after a
training-only baseline event study and before candidate results. No production
defaults, Pine files/presets, promoted winners or entry weights were changed.

## Baseline stop-out study

Training discovery, 2017-12-01–2020-12-31, found 16 stop exits and no score exits.
Its four-day continuations had median price return +4.38%. That apparent recovery
opportunity did not generalize across the seven later, previously inspected
windows: these reproduce the prior experiment's **35 stop exits and 11 score
exits**. The table uses greedy nonoverlapping horizons across both exit types,
with incomplete and window-crossing horizons excluded.

| Exit / horizon | Events | Higher at horizon | Median subsequent price return | Mean terminal loss avoided |
|---|---:|---:|---:|---:|
| Stop / 1 day | 35 | 48.6% | −0.23% | 1.55% |
| Stop / 4 days | 34 | 35.3% | −1.67% | 3.29% |
| Stop / 7 days | 34 | 32.4% | −4.23% | 4.38% |
| Score / 4 days | 9 | 33.3% | −1.23% | 4.47% |

For four-day stop events in those seven windows, mean maximum subsequent close
recovery was 2.59%, versus 4.69% mean maximum subsequent close decline. Median
holding duration was 162 hours. Median ATR/price rose from 1.31% at entry to 1.49%
at exit; realized 20-bar log-return volatility rose from 0.85% to 0.91% per bar.
Score-exit holding duration was 52 hours, with median entry/exit ATR of
1.40%/1.25%. This distinguishes stop behavior from the score exits.

Across training plus the seven windows, there were 62 baseline exits. All 62 had
complete one-day labels; 61 had complete four- and seven-day labels. Retaining
nonoverlapping events leaves 60/59/58 respectively. No event horizon crossed a
known data gap; one late stop was censored at the data boundary for the longer
horizons. All events, including excluded ones and concurrent score/stop flags,
are saved. The separate all-event summaries avoid hiding overlap sensitivity.

Loss avoided is the positive part of the fee-aware wealth difference between
selling at the exit and continuing to the horizon. Maximum recovery/decline uses
future closes. These are descriptive counterfactuals, not profits to add together:
holding longer changes subsequent entry opportunities. The initial event draft
used a multiplicative exit fee; review corrected it to canonical additive final-
bar fee accounting. Candidate returns and the predeclared choice were unaffected.

## Bounded comparison

Frozen entry model: BTC 4H `bb50_seed3020`; entry threshold −190, score exit
threshold 220, confirmation −240; original stop 5%. Calibration uses only
2017-12-01–2020-12-31: 6,759 bars and 786 baseline holding bars.

| Policy | Definition |
|---|---|
| Baseline | Existing fixed 5% stop |
| ATR 5 | Wilder ATR14/price × 2.7937229194; training median requested width 5% |
| Fixed control 5 | 3.9906961142%, matching ATR 5's mean requested width on training baseline holding bars |
| ATR 7.5 | ATR14/price × 4.1905843791; training median requested width 7.5% |
| Fixed control 7.5 | 5.6404478732%, matched by the same training-only procedure |

ATR widths are clipped to 2.5–10%. Every alternative uses the same entry-based
5% threshold floor and monotonic stop ratchet. Matching requested width controls
for typical stop size, though endogenous holding paths and ratcheting mean it
cannot perfectly match realized stop distance. Realized volatility is an event
diagnostic, not another searched family.

Initialize at the entry signal close; skip the next bar's high, matching the
engine's fill-bar convention. Thereafter update the high-water mark using the
current high and use the previous closed bar's ATR/price. The stop is the maximum
of its previous value, high-water mark × (1−width), and entry close × 0.95.
Stops take attribution priority over simultaneous score exits; both flags are
recorded. Score exits and entry gates otherwise remain unchanged, including the
dead-zone fallback. No same-bar reentry or use of exported position state.

Accounting preserves close-to-close returns, a 0.5% entry fee on the first holding
bar, and a 0.5% exit fee on the exit bar. Each window starts flat, with warmed
indicators. Open terminal positions remain marked to market without an invented
exit fee. Calmar uses elapsed calendar years and equity drawdown includes initial
capital. Window CAGRs are not stitched together.

## Results and frozen decision

Validation years 2021–2023 nominate a candidate only if it improves median return
and Calmar against both controls, wins in at least two years against each, has
at least 20 entries, and never worsens drawdown by more than two percentage points.
The saved nomination was **none**, frozen before replaying the later windows.

| Validation candidate | Entries | Median P&L change vs baseline | Median Calmar change vs baseline | Worst drawdown deterioration vs baseline |
|---|---:|---:|---:|---:|
| ATR 5 | 22 | −2.48 pp | −0.27 | 2.04 pp |
| ATR 7.5 | 20 | −4.69 pp | −0.46 | 5.90 pp |

Both beat baseline P&L in only one of three validation years. Against their
matched fixed controls, ATR 5's median return/Calmar deltas were +1.11 pp/+0.10,
but its worst drawdown deterioration was 4.34 pp. ATR 7.5's deltas were
−3.49 pp/−0.31, with drawdown deterioration of 5.50 pp. Neither qualified.

Later-window total returns, shown for all predeclared policies without reselection:

| Window | Baseline | ATR 5 | Fixed control 5 | ATR 7.5 | Fixed control 7.5 |
|---|---:|---:|---:|---:|---:|
| Jan–Sep 2024 | 93.45% | 55.34% | 39.20% | 61.35% | 83.01% |
| Oct 2024–Sep 2025 | 49.14% | 18.86% | 31.90% | 23.46% | 47.69% |
| Oct 2025–Feb 2026 | −14.56% | −11.01% | −14.64% | −14.85% | −15.15% |
| Mar–Aug 2026, previously inspected | 9.24% | 17.41% | 12.99% | 10.68% | 9.53% |

Across the three historical evaluation windows, ATR 5/7.5 had median P&L changes
of −30.28/−25.68 pp and Calmar changes of −1.95/−1.95 versus baseline. ATR 5
reduced drawdown in all three, but gave up substantial upside. In Oct 2024–Sep
2025, exposure fell from baseline 31.46% to 13.38%/21.69%, with 10 entries for
each ATR policy versus nine for baseline. Full exposure, trade counts, stop
outcomes and bar-by-bar paired returns are in the artifacts.

The apparent improvement after March 2026 has only seven candidate entries,
versus six for baseline, and includes an open terminal position. It is below the
10-entry forward coverage requirement, was already inspected, and cannot rescue
a rejected validation choice.

The threshold floor is **not a guaranteed realized-loss bound**: the existing
engine decides at the close and can overshoot. Across all eight windows, baseline
had four exits below entry minus 5%, with maximum excess 1.067 pp before fees;
ATR 5 had one (0.021 pp) and ATR 7.5 had two (1.015 pp). The harness records both
stop-threshold overshoots and entry-bound breaches instead of assuming fills at
the stop price. TradingView next-open fills can differ further.

## Reproduction, checks and limitations

Canonical results: `results/mlp_experiments/volatility_stop_2026_09_21/`:

- `metrics.csv`, `paired_metrics.csv`: 40 policy/window rows and 48 paired rows.
- `bar_traces.csv.gz`: 95,550 bar rows with actions, requested widths, stops,
  high-water marks, returns and baseline-paired returns.
- `events.csv`, `event_summary.csv`: 984 event/horizon rows plus grouped outcomes.
- `calibration.json`, `selection.json`, `manifest.json`, `prerequisites.json`:
  configuration, frozen decision, environment and source/input/output hashes.
- `audit.py`, `audit.json`: a separate reconstruction of ATR, stop/action paths,
  fees, calendar metrics, labels, censoring, overlap handling and paired returns.
- `reproduction.json`: a clean rerun reproduced all seven result/configuration
  files byte for byte, including the compressed bar trace.

`volatility_stop/` retains the pre-work inventory, protected hashes, training event
study recorded before design, and first run. `volatility_stop_final/` is an
intermediate corrected-diagnostic run; use the dated directory above as canonical.

```bash
.venv/bin/python tools/run_mlp_volatility_stop_experiment.py \
  --output results/mlp_experiments/volatility_stop_reproduction
.venv/bin/python results/mlp_experiments/volatility_stop_2026_09_21/audit.py
.venv/bin/python -m pytest tests/ -q
```

Use a new output directory: completed results are not overwritten. The runner
enforces parameter sync, Pine checks and chart validation. All pass, with chart
warnings. System `python3` lacks NumPy; the project `.venv` is required. The final
fast suite reports **183 passed, 2 slow tests skipped**, with three existing
warnings. Twelve new tests cover control equivalence, execution timing, fees,
ratcheting, threshold equality and overshoot, causality, calibration/selection
isolation, missing inputs, time gates, terminal entries and overlapping events.
Every window also asserts disabled and fixed-5% control equivalence to the
canonical engine. The independent reconstruction passes all 40 windows/policies,
95,550 bar rows, 984 event/horizon rows and 48 paired rows, and verifies 20 source
hashes and **922 protected files unchanged**.

Local reuse, quality, efficiency, correctness and testing reviews retained the
shared input loader and calendar-metrics utility, and found the event-fee issue
described above. No independent reviewer or live TradingView execution was used.
The code and results remain local alongside the pre-existing uncommitted work.

The export ends at **2026-08-21 12:00 UTC**, after dropping one incomplete tail
row. There are two historical gaps and limited warm-up before the scoring start.
Promoted entry weights were selected using broader history; these are conditional
component screens, not leakage-free end-to-end walk-forward results. The eight
BTC windows are correlated, and none is newly untouched validation.

Fresh matching exports were unavailable because the accessible TradingView
session was signed out. To refresh: sign in, load the current MLP strategy on
**COINBASE:BTCUSD, 4H**, load full chart history, then export chart data and the
Strategy Tester trade list from the same unchanged chart state. Save the chart
CSV as `data/mlp/COINBASE_BTCUSD, 240.csv` and the matching trade list under
`data/mlp/`. Run chart validation first, then
`check_mlp_parity.py --asset COINBASE_BTCUSD --tf 4H` and `compare_tv_trades.py`
with that trade list and the current winner. Freshness alone does not make old
periods untouched. Any eventual Pine trial needs a separately frozen design,
fresh baseline parity and sufficient genuinely untouched forward trades.

For this design, additional timeframes are not warranted. Preserve the evidence
as a bounded negative result; do not tune another ATR length or multiplier on
these same windows to manufacture a winner.
