# Konatus Bearing: a backtest series on public data

Date: 20 September 2026. Indicator tested: `konatus_bearing.pine`, version 2.2,
md5 `6a03d888278adb8820fdefa81acb29bc` (the desktop text copy, the project copy
and the website copy are byte-identical; the pre-audit backup is not).

The instrument tested here is `kbsignal.py`, the Python extractor in this
directory, which reproduces the Pine listing bar for bar on top of the
numerical primitives in `../dsp.py`. No Pine compiler exists on this machine,
so nothing below is a measurement of TradingView's own engine. What is
measured is the published logic, translated faithfully and then checked
against two independent implementations of the same rules.

## Why the exits are specified here and not in the indicator

The Pine file deliberately specifies no target, no trailing stop and no
maximum holding period. It writes one frozen invalidation level per accepted
signal and says so in its own header comment: a plan is an illustration, not a
backtest. The September 2026 audit recorded this as recommendation 2, define
exits separately before interpreting performance, and recommendation 3, use
untouched evaluation data with a predeclared universe and report uncertainty.

That is what this document does. Four exit policies are simulated, each
labelled, and the indicator is never credited with an exit rule it does not
contain.

## What the numbers are

Every trade result is in R, the distance from the signal close to the frozen
stop, so a 1993 SPY trade and a 2026 BTC trade are on the same scale. Costs
are charged on both sides of every trade in basis points of notional, covering
commission, spread and slippage in one number. The equity curve is
illustrative only: 0.5 per cent risk per trade, notional capped at one times
equity, and it is the weakest number in the study because it mixes a
risk-normalised signal with a position-sizing assumption the indicator does
not state.

## Data

Two public sources, no keys, no accounts, both cached as CSV in `data/` and
re-fetchable with `python3 kbdata.py --group all`.

Daily US equities and ETFs come from the stockanalysis.com history endpoint,
which returns split-adjusted, dividend-unadjusted open/high/low/close and
volume. Dividend-unadjusted is the right default here: a default TradingView
chart shows the same prices, so the backtest reads what the chart shows, and
the omission makes long trades slightly worse than a total-return series would.

Crypto comes from the Binance.US klines endpoint, paginated from each pair's
first listed bar. Crypto trades continuously, so the Auto VWAP anchor resolves
to the UTC session rather than to a market day.

Twenty daily series and fifteen crypto series, 1993 to 2026 for the equity
index and 2019 to 2026 for crypto.

## Method

**Signal extraction.** `kbsignal.py` maps each block of the Pine listing to a
function already covered by the DSP propositions and the JavaScript parity
check: the adaptive ROC/RMS period and the three engines to
`dsp.adaptive_period_ss2`, the RMS-normalised geometry and the participated
Conatus to `dsp.geometry_participated`, participation to
`dsp.relative_volume`, the anchored VWAP and regime to `dsp.vwap` and
`dsp.vwap_regime`, adequacy to `dsp.adequacy`, the turning-point and breakout
state machines to `_turning_points` and `_breakouts` written directly from the
Pine control flow, including the `tpCnt == confirmN` single-fire rule and the
frozen breakout level.

**Gates before any result.** `verify_signals.py` runs three checks on real
data and they must pass before the battery is read at all.

1. Non-repainting. The indicator is recomputed on bars 0 to each of many cut
   points and the marker printed at the cut bar must be identical to the
   marker the full series prints there, along with the frozen Conatus, slope
   and regime readings. 40 cut points for SPY daily, 40 for BTCUSDT 15m, 25
   for AAPL daily: zero disagreements.
2. Invariants. No marker inside warm-up, none on a bar the indicator itself
   abstains from, every direction agreeing with the filter's own slope, every
   event traceable to a named detector, no duplicated markers inside one
   episode. All hold on all three series.
3. Attribution. Differences against the independent detectors in `dsp.py` are
   counted and assigned to a named cause rather than left as a residue. On all
   three series every turning-point marker this build prints is also in the
   reference set, and every reference-only event is attributed: it fired on a
   bar the indicator abstains from (105 of 144 on SPY, 3391 of 3904 on BTCUSDT
   15m, 164 of 204 on AAPL), or against the VWAP regime (1, 264 and 5), or
   against the filter's own slope (0 everywhere). Residual: zero. The breakout
   cross-check is informational, not a gate: its remaining reference-only
   events come from a difference in when an episode is allowed to restart, and
   they are counted and printed rather than absorbed.

**Trades.** Entry at the signal bar's close, which is the price the Pine plan
writes down, with a second variant entering at the next bar's open for anyone
who does not believe a closing print is tradable. The stop sits 1.5 ATR(14)
from the signal close, frozen, and is filled at the stop price unless the bar
opens beyond it, in which case the fill is the open. Opposite signals replace
the plan, which in practice almost never happens: on the whole daily set the
stop-only and the reverse policies produce identical trade lists, so this
indicator behaves as a stop-exit system.

**Statistics.** Mean and median R per trade, a confidence interval on the
mean (percentile bootstrap under 3000 trades, normal approximation above it,
labelled in the tables), win rate, profit factor, maximum drawdown in R, and
two controls that matter more than any of them: random entries with identical
risk management, and plain-price signals with identical risk management.
Twenty-seven distinct parameter configurations are then swept one factor at a
time (28 labelled rows, of which two names describe the same setting). The
sweep is exploratory and no variant is recommended on the strength of it.

## Results

### Data used

| series | source | bars | first bar | last bar | timeframe |
|---|---|---|---|---|---|
| AAPL | stockanalysis.com | 11078 | 1982-10-05 00:00 | 2026-09-18 00:00 | 1d |
| AMZN | stockanalysis.com | 7381 | 1997-05-16 00:00 | 2026-09-18 00:00 | 1d |
| DIA | stockanalysis.com | 7210 | 1998-01-21 00:00 | 2026-09-18 00:00 | 1d |
| EEM | stockanalysis.com | 5896 | 2003-04-14 00:00 | 2026-09-18 00:00 | 1d |
| GLD | stockanalysis.com | 5491 | 2004-11-19 00:00 | 2026-09-18 00:00 | 1d |
| IWM | stockanalysis.com | 6616 | 2000-05-30 00:00 | 2026-09-18 00:00 | 1d |
| JPM | stockanalysis.com | 14507 | 1969-03-06 00:00 | 2026-09-18 00:00 | 1d |
| KO | stockanalysis.com | 14775 | 1968-01-03 00:00 | 2026-09-18 00:00 | 1d |
| META | stockanalysis.com | 3603 | 2012-05-21 00:00 | 2026-09-18 00:00 | 1d |
| MSFT | stockanalysis.com | 10207 | 1986-03-14 00:00 | 2026-09-18 00:00 | 1d |
| NVDA | stockanalysis.com | 6956 | 1999-01-25 00:00 | 2026-09-18 00:00 | 1d |
| QQQ | stockanalysis.com | 6924 | 1999-03-11 00:00 | 2026-09-18 00:00 | 1d |
| SMH | stockanalysis.com | 3663 | 2012-02-24 00:00 | 2026-09-18 00:00 | 1d |
| SPY | stockanalysis.com | 8466 | 1993-02-01 00:00 | 2026-09-18 00:00 | 1d |
| TLT | stockanalysis.com | 6075 | 2002-07-29 00:00 | 2026-09-18 00:00 | 1d |
| TSLA | stockanalysis.com | 4080 | 2010-06-30 00:00 | 2026-09-18 00:00 | 1d |
| WMT | stockanalysis.com | 13678 | 1972-04-04 00:00 | 2026-09-18 00:00 | 1d |
| XLE | stockanalysis.com | 6969 | 1998-12-23 00:00 | 2026-09-18 00:00 | 1d |
| XLF | stockanalysis.com | 6973 | 1998-12-23 00:00 | 2026-09-18 00:00 | 1d |
| XOM | stockanalysis.com | 14775 | 1968-01-03 00:00 | 2026-09-18 00:00 | 1d |
| ADAUSDT 1d | api.binance.us | 2552 | 2019-09-25 00:00 | 2026-09-19 00:00 | 1d |
| ADAUSDT 1h | api.binance.us | 61037 | 2019-10-02 08:00 | 2026-09-20 14:00 | 1h |
| BTCUSDT 15m | api.binance.us | 244141 | 2019-10-02 07:15 | 2026-09-20 15:00 | 15m |
| BTCUSDT 1d | api.binance.us | 2554 | 2019-09-23 00:00 | 2026-09-19 00:00 | 1d |
| BTCUSDT 1h | api.binance.us | 61037 | 2019-10-02 08:00 | 2026-09-20 14:00 | 1h |
| BTCUSDT 4h | api.binance.us | 15263 | 2019-10-02 08:00 | 2026-09-20 08:00 | 4h |
| ETHUSDT 15m | api.binance.us | 244141 | 2019-10-02 07:15 | 2026-09-20 15:00 | 15m |
| ETHUSDT 1d | api.binance.us | 2554 | 2019-09-23 00:00 | 2026-09-19 00:00 | 1d |
| ETHUSDT 1h | api.binance.us | 61037 | 2019-10-02 08:00 | 2026-09-20 14:00 | 1h |
| ETHUSDT 4h | api.binance.us | 15263 | 2019-10-02 08:00 | 2026-09-20 08:00 | 4h |
| LINKUSDT 1d | api.binance.us | 1710 | 2022-01-14 00:00 | 2026-09-19 00:00 | 1d |
| LINKUSDT 1h | api.binance.us | 41038 | 2022-01-14 01:00 | 2026-09-20 14:00 | 1h |
| SOLUSDT 1d | api.binance.us | 2193 | 2020-09-18 00:00 | 2026-09-19 00:00 | 1d |
| SOLUSDT 1h | api.binance.us | 52608 | 2020-09-18 13:00 | 2026-09-20 14:00 | 1h |
| SOLUSDT 4h | api.binance.us | 13154 | 2020-09-18 12:00 | 2026-09-20 08:00 | 4h |

### Baseline, daily: published defaults

| instrument | years | trades | signals/yr | win | median R | mean R | total R | exposure |
|---|---|---|---|---|---|---|---|---|
| AAPL | 44.0 | 37 | 1.0 | 0.189 | -1.007 | +2.812 | +104.1 | 0.36 |
| AMZN | 29.3 | 23 | 0.9 | 0.087 | -1.008 | +2.695 | +62.0 | 0.22 |
| DIA | 28.7 | 19 | 0.9 | 0.263 | -1.015 | +0.411 | +7.8 | 0.51 |
| EEM | 23.4 | 19 | 0.8 | 0.105 | -1.016 | +1.950 | +37.0 | 0.17 |
| GLD | 21.8 | 18 | 0.9 | 0.111 | -1.022 | +0.611 | +11.0 | 0.22 |
| IWM | 26.3 | 22 | 0.9 | 0.182 | -1.013 | -0.059 | -1.3 | 0.23 |
| JPM | 57.5 | 49 | 1.0 | 0.143 | -1.013 | +0.343 | +16.8 | 0.32 |
| KO | 58.7 | 63 | 1.2 | 0.111 | -1.019 | +0.702 | +44.2 | 0.34 |
| META | 14.3 | 12 | 0.8 | 0.083 | -1.009 | -0.782 | -9.4 | 0.06 |
| MSFT | 40.5 | 32 | 0.8 | 0.125 | -1.009 | +1.509 | +48.3 | 0.10 |
| NVDA | 27.6 | 24 | 0.9 | 0.083 | -1.006 | +1.054 | +25.3 | 0.21 |
| QQQ | 27.5 | 19 | 0.8 | 0.000 | -1.017 | -1.040 | -19.8 | 0.03 |
| SMH | 14.6 | 5 | 0.4 | 0.000 | -1.017 | -1.047 | -5.2 | 0.03 |
| SPY | 33.6 | 33 | 1.2 | 0.091 | -1.023 | -0.026 | -0.9 | 0.17 |
| TLT | 24.1 | 49 | 2.1 | 0.143 | -1.025 | -0.324 | -15.9 | 0.26 |
| TSLA | 16.2 | 17 | 1.1 | 0.118 | -1.005 | +1.192 | +20.3 | 0.11 |
| WMT | 54.5 | 55 | 1.1 | 0.091 | -1.013 | -0.406 | -22.4 | 0.22 |
| XLE | 27.7 | 13 | 0.6 | 0.077 | -1.012 | +5.445 | +70.8 | 0.43 |
| XLF | 27.7 | 33 | 1.4 | 0.182 | -1.013 | -0.543 | -17.9 | 0.38 |
| XOM | 58.7 | 46 | 0.8 | 0.130 | -1.014 | +0.026 | +1.2 | 0.28 |

Pooled: 588 trades, mean +0.606 R, CI [+0.039, +1.247] (bootstrap), median -1.014 R, win 0.124, total +356.1 R.

### Baseline, crypto: published defaults

| instrument | years | trades | signals/yr | win | median R | mean R | total R | exposure |
|---|---|---|---|---|---|---|---|---|
| ADAUSDT 1d | 7.0 | 3 | 0.6 | 0.333 | -1.023 | +0.771 | +2.3 | 0.40 |
| ADAUSDT 1h | 7.0 | 241 | 37.0 | 0.166 | -1.091 | +0.593 | +143.0 | 0.27 |
| BTCUSDT 15m | 7.0 | 259 | 39.0 | 0.093 | -1.334 | +0.503 | +130.3 | 0.17 |
| BTCUSDT 1d | 7.0 | 4 | 0.6 | 0.250 | -1.029 | +1.053 | +4.2 | 0.10 |
| BTCUSDT 1h | 7.0 | 75 | 12.9 | 0.093 | -1.164 | +1.657 | +124.3 | 0.33 |
| BTCUSDT 4h | 7.0 | 34 | 5.3 | 0.176 | -1.064 | -0.237 | -8.1 | 0.09 |
| ETHUSDT 15m | 7.0 | 271 | 41.8 | 0.085 | -1.284 | -0.177 | -48.0 | 0.17 |
| ETHUSDT 1d | 7.0 | 2 | 0.3 | 0.000 | -1.015 | -1.022 | -2.0 | 0.08 |
| ETHUSDT 1h | 7.0 | 86 | 13.6 | 0.128 | -1.119 | +1.547 | +133.0 | 0.17 |
| ETHUSDT 4h | 7.0 | 35 | 5.2 | 0.029 | -1.059 | -1.020 | -35.7 | 0.10 |
| LINKUSDT 1d | 4.7 | 6 | 1.3 | 0.000 | -1.017 | -0.946 | -5.7 | 0.10 |
| LINKUSDT 1h | 4.7 | 76 | 18.1 | 0.079 | -1.112 | +1.160 | +88.2 | 0.17 |
| SOLUSDT 1d | 6.0 | 3 | 0.5 | 0.000 | -1.013 | -1.020 | -3.1 | 0.05 |
| SOLUSDT 1h | 6.0 | 73 | 13.7 | 0.137 | -1.083 | +4.111 | +300.1 | 0.29 |
| SOLUSDT 4h | 6.0 | 27 | 5.2 | 0.259 | -1.028 | +1.175 | +31.7 | 0.39 |

Pooled: 1195 trades, mean +0.715 R, CI [+0.198, +1.348] (bootstrap), median -1.153 R, win 0.115, total +854.6 R.

### Trade distribution and tail concentration

| block | trades | mean R | trimmed 5% mean | median R | win | stop exits | best trade | top 1 share | top 5 share | top 10 share | longest losing run |
|---|---|---|---|---|---|---|---|---|---|---|---|
| daily | 588 | +0.606 | -0.718 | -1.014 | 0.124 | 84.9% | +83.3R | 0.23 | 0.89 | 1.38 | 19 |
| crypto | 1195 | +0.715 | -0.847 | -1.153 | 0.115 | 87.6% | +216.5R | 0.25 | 0.68 | 0.99 | 55 |

Shares are the fraction of total R contributed by that many best trades, so a top 10 share above 1.0 means everything outside the ten best trades is net negative.

### Exit policy, fill convention, cost

| block | variant | trades | mean R | CI | median R | win | total R |
|---|---|---|---|---|---|---|---|
| daily | reverse, fill close, base cost | 588 | +0.606 | [+0.039, +1.247] | -1.014 | 0.124 | +356.1 |
| daily | reverse, fill next open, base cost | 588 | +0.611 | [+0.043, +1.244] | -0.950 | 0.124 | +359.0 |
| daily | stop only, no reverse, fill close | 588 | +0.606 | [+0.039, +1.247] | -1.014 | 0.124 | +356.1 |
| daily | reverse + 50-bar time stop | 643 | -0.059 | [-0.216, +0.116] | -1.012 | 0.236 | -38.2 |
| daily | reverse + 200-bar time stop | 633 | +0.371 | [+0.036, +0.746] | -1.013 | 0.169 | +234.8 |
| daily | reverse + ATR trail | 652 | -0.189 | [-0.269, -0.105] | -0.500 | 0.311 | -123.5 |
| daily | reverse, zero cost | 588 | +0.618 | [+0.052, +1.259] | -1.000 | 0.124 | +363.2 |
| daily | reverse, low cost | 588 | +0.606 | [+0.039, +1.247] | -1.014 | 0.124 | +356.1 |
| daily | reverse, high cost | 588 | +0.557 | [-0.009, +1.200] | -1.068 | 0.124 | +327.6 |
| daily | reverse, next open, 20 bp/side | 588 | +0.502 | [-0.070, +1.138] | -1.071 | 0.124 | +295.0 |
| crypto | reverse, fill close, base cost | 1195 | +0.715 | [+0.198, +1.348] | -1.153 | 0.115 | +854.6 |
| crypto | reverse, fill next open, base cost | 1195 | +0.718 | [+0.202, +1.347] | -1.143 | 0.114 | +858.1 |
| crypto | stop only, no reverse, fill close | 1195 | +0.715 | [+0.198, +1.348] | -1.153 | 0.115 | +854.6 |
| crypto | reverse + 50-bar time stop | 1284 | +0.113 | [-0.049, +0.289] | -1.103 | 0.266 | +145.2 |
| crypto | reverse + 200-bar time stop | 1264 | +0.354 | [+0.056, +0.666] | -1.134 | 0.170 | +447.8 |
| crypto | reverse + ATR trail | 1294 | -0.516 | [-0.558, -0.473] | -0.631 | 0.215 | -668.2 |
| crypto | reverse, zero cost | 1195 | +0.935 | [+0.419, +1.568] | -1.000 | 0.115 | +1117.2 |
| crypto | reverse, low cost | 1195 | +0.825 | [+0.308, +1.456] | -1.077 | 0.115 | +985.9 |
| crypto | reverse, high cost | 1195 | +0.495 | [-0.025, +1.130] | -1.305 | 0.115 | +591.9 |
| crypto | reverse, next open, 20 bp/side | 1195 | +0.498 | [-0.022, +1.134] | -1.292 | 0.114 | +595.5 |

### Plain-price controls

| block | control | trades | mean R | CI | win | median R | exposure | bars/trade |
|---|---|---|---|---|---|---|---|---|
| daily | indicator (defaults) | 588 | +0.606 | [+0.039, +1.247] | 0.124 | -1.014 | 0.23 | 71.2 |
| daily | ema20 | 22108 | +0.010 | [-0.009, +0.029] | 0.242 | -0.312 | 1.00 | 7.5 |
| daily | donchian50 | 2670 | +0.504 | [+0.253, +0.790] | 0.176 | -1.014 | 0.71 | 44.7 |
| daily | vwapreg | 33830 | -0.017 | [-0.029, -0.006] | 0.358 | -0.320 | 1.00 | 4.8 |
| crypto | indicator (defaults) | 1195 | +0.715 | [+0.198, +1.348] | 0.115 | -1.153 | 0.19 | 126.0 |
| crypto | ema20 | 117063 | -0.047 | [-0.058, -0.037] | 0.213 | -0.312 | 0.99 | 7.6 |
| crypto | donchian50 | 13515 | +0.068 | [-0.028, +0.164] | 0.170 | -1.211 | 0.68 | 45.4 |
| crypto | vwapreg | 83205 | -0.058 | [-0.072, -0.043] | 0.261 | -0.485 | 0.99 | 7.0 |

### Random-entry control

| block | series | trades | indicator R | random R | random 95% range | percentile |
|---|---|---|---|---|---|---|
| daily | AAPL | 37 | +2.812 | +3.446 | [-0.934, +14.906] | 68 |
| daily | AMZN | 23 | +2.695 | +1.779 | [-1.075, +11.341] | 76 |
| daily | DIA | 19 | +0.411 | +0.470 | [-1.079, +5.084] | 64 |
| daily | EEM | 19 | +1.950 | +0.190 | [-1.181, +3.887] | 88 |
| daily | GLD | 18 | +0.611 | +0.317 | [-1.151, +4.519] | 71 |
| daily | IWM | 22 | -0.059 | -0.043 | [-1.105, +2.562] | 62 |
| daily | JPM | 49 | +0.343 | +0.074 | [-0.960, +2.544] | 70 |
| daily | KO | 63 | +0.702 | +0.653 | [-0.844, +3.319] | 63 |
| daily | META | 12 | -0.782 | +0.778 | [-1.581, +7.922] | 42 |
| daily | MSFT | 32 | +1.509 | +4.282 | [-0.858, +25.292] | 40 |
| daily | NVDA | 24 | +1.054 | +2.261 | [-1.133, +24.694] | 66 |
| daily | QQQ | 19 | -1.040 | +0.652 | [-1.080, +9.968] | 8 |
| daily | SMH | 5 | -1.047 | +3.445 | [-1.195, +27.616] | 26 |
| daily | SPY | 33 | -0.026 | +1.513 | [-0.895, +6.421] | 26 |
| daily | TLT | 49 | -0.324 | -0.009 | [-0.746, +1.044] | 32 |
| daily | TSLA | 17 | +1.192 | +3.262 | [-1.114, +22.656] | 60 |
| daily | WMT | 55 | -0.406 | +2.835 | [-0.746, +17.307] | 9 |
| daily | XLE | 13 | +5.445 | +0.997 | [-1.135, +9.998] | 91 |
| daily | XLF | 33 | -0.543 | -0.024 | [-1.032, +1.715] | 28 |
| daily | XOM | 46 | +0.026 | +0.671 | [-0.929, +3.674] | 33 |
| crypto | ADAUSDT 1d | 3 | +0.771 | +3.464 | [-1.029, +52.896] | 86 |
| crypto | ADAUSDT 1h | 241 | +0.593 | +0.210 | [-0.349, +0.626] | 88 |
| crypto | BTCUSDT 15m | 259 | +0.503 | -0.241 | [-0.942, +0.406] | 92 |
| crypto | BTCUSDT 1d | 4 | +1.053 | +4.301 | [-1.042, +43.936] | 76 |
| crypto | BTCUSDT 1h | 75 | +1.657 | +0.558 | [-0.997, +1.864] | 92 |
| crypto | BTCUSDT 4h | 34 | -0.237 | +1.177 | [-1.054, +8.525] | 32 |
| crypto | ETHUSDT 15m | 271 | -0.177 | -0.026 | [-0.794, +0.753] | 42 |
| crypto | ETHUSDT 1d | 2 | -1.022 | -0.540 | [-1.039, +3.863] | 64 |
| crypto | ETHUSDT 1h | 86 | +1.547 | +0.357 | [-0.799, +1.756] | 84 |
| crypto | ETHUSDT 4h | 35 | -1.020 | +0.418 | [-1.072, +3.546] | 6 |
| crypto | LINKUSDT 1d | 6 | -0.946 | -0.633 | [-1.027, +2.674] | 68 |
| crypto | LINKUSDT 1h | 76 | +1.160 | -0.039 | [-1.037, +2.065] | 90 |
| crypto | SOLUSDT 1d | 3 | -1.020 | +1.923 | [-1.028, +32.860] | 36 |
| crypto | SOLUSDT 1h | 73 | +4.111 | +2.293 | [-0.819, +19.961] | 92 |
| crypto | SOLUSDT 4h | 27 | +1.175 | +1.364 | [-1.050, +13.665] | 72 |

### Random-entry control, matched spacing and direction sequence

| block | series | trades | indicator R | random R | random 95% range | percentile |
|---|---|---|---|---|---|---|
| daily | AAPL | 37 | +2.812 | +1.340 | [-0.737, +6.839] | 82 |
| daily | AMZN | 23 | +2.695 | +3.431 | [-1.009, +17.089] | 59 |
| daily | DIA | 19 | +0.411 | +0.736 | [-0.948, +4.120] | 49 |
| daily | EEM | 19 | +1.950 | +0.507 | [-1.146, +3.791] | 84 |
| daily | GLD | 18 | +0.611 | +0.004 | [-1.160, +2.948] | 80 |
| daily | IWM | 22 | -0.059 | +0.139 | [-1.034, +2.762] | 57 |
| daily | JPM | 49 | +0.343 | +0.002 | [-0.840, +1.539] | 76 |
| daily | KO | 63 | +0.702 | +1.764 | [-0.608, +7.897] | 35 |
| daily | META | 12 | -0.782 | +1.669 | [-1.306, +8.120] | 16 |
| daily | MSFT | 32 | +1.509 | +2.964 | [-0.981, +13.473] | 49 |
| daily | NVDA | 24 | +1.054 | +1.438 | [-1.093, +13.019] | 64 |
| daily | QQQ | 19 | -1.040 | +0.574 | [-1.061, +8.593] | 6 |
| daily | SMH | 5 | -1.047 | +5.764 | [-1.281, +111.923] | 28 |
| daily | SPY | 33 | -0.026 | +2.146 | [-0.955, +8.104] | 16 |
| daily | TLT | 49 | -0.324 | -0.160 | [-0.744, +0.611] | 37 |
| daily | TSLA | 17 | +1.192 | +2.039 | [-0.963, +14.924] | 62 |
| daily | WMT | 55 | -0.406 | +1.236 | [-0.680, +4.957] | 9 |
| daily | XLE | 13 | +5.445 | +1.451 | [-1.122, +9.239] | 88 |
| daily | XLF | 33 | -0.543 | -0.034 | [-0.841, +1.095] | 16 |
| daily | XOM | 46 | +0.026 | +0.514 | [-0.889, +2.451] | 33 |
| crypto | ADAUSDT 1h | 241 | +0.593 | +0.309 | [-0.543, +1.135] | 84 |
| crypto | BTCUSDT 15m | 259 | +0.503 | -0.345 | [-1.052, +0.298] | 100 |
| crypto | BTCUSDT 1h | 75 | +1.657 | +1.084 | [-0.755, +6.263] | 76 |
| crypto | BTCUSDT 4h | 34 | -0.237 | +1.546 | [-0.886, +10.001] | 25 |
| crypto | ETHUSDT 15m | 271 | -0.177 | +0.159 | [-0.784, +0.502] | 17 |
| crypto | ETHUSDT 1h | 86 | +1.547 | +0.182 | [-0.867, +1.660] | 92 |
| crypto | ETHUSDT 4h | 35 | -1.020 | -0.101 | [-1.068, +2.078] | 6 |
| crypto | LINKUSDT 1d | 6 | -0.946 | -0.023 | [-1.023, +2.747] | 16 |
| crypto | LINKUSDT 1h | 76 | +1.160 | -0.260 | [-0.831, +0.546] | 100 |
| crypto | SOLUSDT 1h | 73 | +4.111 | +0.765 | [-0.838, +4.573] | 95 |
| crypto | SOLUSDT 4h | 27 | +1.175 | +0.474 | [-1.037, +5.314] | 83 |

### In-sample / out-of-sample split

| split | half | trades | mean R | CI | win |
|---|---|---|---|---|---|
| daily 1993-2009 / 2010-2026 | A 1993-2009 | 319 | +0.988 | [+0.067, +2.086] | 0.125 |
| daily 1993-2009 / 2010-2026 | B 2010-2026 | 337 | +0.095 | [-0.393, +0.670] | 0.122 |
| crypto 2019-2022 / 2023-2026 | A 2019-2022 | 583 | +1.003 | [+0.156, +2.128] | 0.118 |
| crypto 2019-2022 / 2023-2026 | B 2023-2026 | 659 | +0.570 | [-0.098, +1.375] | 0.106 |

### Parameter sweep, one factor at a time

| block | variant | trades | mean R | CI | median R | win |
|---|---|---|---|---|---|---|
| daily | published defaults | 588 | +0.606 | [+0.039, +1.247] | -1.014 | 0.124 |
| daily | adapt off (fixed 20) | 734 | +0.843 | [+0.080, +1.868] | -1.015 | 0.128 |
| daily | engine 3-pole | 803 | +0.750 | [+0.234, +1.371] | -1.016 | 0.148 |
| daily | engine UltimateSmoother | 770 | +1.282 | [+0.221, +2.904] | -1.015 | 0.142 |
| daily | base period 10 | 886 | +0.512 | [+0.122, +0.962] | -1.013 | 0.149 |
| daily | base period 40 | 215 | +1.058 | [-0.642, +3.600] | -1.016 | 0.065 |
| daily | depth 0.3 | 628 | +0.742 | [+0.062, +1.585] | -1.014 | 0.127 |
| daily | geo/RMS window 40 | 764 | +0.266 | [-0.125, +0.702] | -1.015 | 0.124 |
| daily | geo/RMS window 160 | 343 | +1.392 | [+0.168, +3.006] | -1.013 | 0.117 |
| daily | confirm 1 | 833 | +0.589 | [+0.172, +1.040] | -1.013 | 0.162 |
| daily | confirm 3 | 434 | +1.208 | [+0.124, +2.609] | -1.014 | 0.106 |
| daily | runMin 2 | 626 | +0.694 | [+0.150, +1.348] | -1.014 | 0.128 |
| daily | runMin 8 | 348 | +1.941 | [+0.085, +4.459] | -1.016 | 0.080 |
| daily | conatus gate 0 | 1709 | +0.193 | [-0.022, +0.448] | -1.013 | 0.181 |
| daily | conatus gate 40 | 298 | +3.625 | [-0.282, +10.739] | -1.015 | 0.077 |
| daily | participation off | 1211 | +0.288 | [+0.056, +0.564] | -1.012 | 0.177 |
| daily | participation floor 0 | 1209 | +0.361 | [+0.112, +0.628] | -1.012 | 0.179 |
| daily | participation floor 1.2 | 238 | +5.796 | [+0.011, +15.905] | -1.015 | 0.109 |
| daily | regime gate off | 759 | +0.332 | [-0.059, +0.804] | -1.013 | 0.129 |
| daily | VWAP band 0.5 | 482 | +0.728 | [+0.012, +1.530] | -1.014 | 0.108 |
| daily | breakouts off | 469 | +0.510 | [-0.105, +1.242] | -1.013 | 0.113 |
| daily | turns off | 128 | -1.176 | [-1.241, -1.117] | -1.026 | 0.000 |
| daily | breakouts only | 128 | -1.176 | [-1.241, -1.117] | -1.026 | 0.000 |
| daily | ATR mult 1.0 | 603 | +0.624 | [-0.034, +1.423] | -1.022 | 0.091 |
| daily | ATR mult 2.5 | 549 | +0.678 | [+0.129, +1.320] | -1.007 | 0.180 |
| daily | ATR mult 4.0 | 508 | +0.570 | [+0.096, +1.162] | -1.004 | 0.244 |
| daily | lag gain 0.5 | 287 | +13.307 | [+0.511, +37.235] | -1.016 | 0.105 |
| daily | min period 8 | 823 | +0.751 | [+0.210, +1.350] | -1.014 | 0.145 |
| crypto | published defaults | 1081 | +0.806 | [+0.245, +1.493] | -1.172 | 0.112 |
| crypto | adapt off (fixed 20) | 2694 | +0.240 | [+0.023, +0.505] | -1.189 | 0.138 |
| crypto | engine 3-pole | 1896 | +0.316 | [+0.018, +0.652] | -1.183 | 0.121 |
| crypto | engine UltimateSmoother | 1024 | +0.286 | [-0.338, +1.208] | -1.166 | 0.094 |
| crypto | base period 10 | 1278 | +0.511 | [-0.189, +1.636] | -1.176 | 0.099 |
| crypto | base period 40 | 564 | +0.416 | [-0.288, +1.266] | -1.188 | 0.089 |
| crypto | depth 0.3 | 1475 | +1.027 | [+0.267, +2.184] | -1.184 | 0.127 |
| crypto | geo/RMS window 40 | 1717 | +1.130 | [+0.307, +2.335] | -1.202 | 0.110 |
| crypto | geo/RMS window 160 | 549 | +0.410 | [-0.141, +1.033] | -1.156 | 0.095 |
| crypto | confirm 1 | 794 | +1.369 | [+0.131, +3.096] | -1.200 | 0.079 |
| crypto | confirm 3 | 1035 | +0.185 | [-0.205, +0.571] | -1.180 | 0.099 |
| crypto | runMin 2 | 1217 | +0.820 | [+0.316, +1.431] | -1.176 | 0.117 |
| crypto | runMin 8 | 665 | +2.759 | [+0.823, +5.502] | -1.153 | 0.117 |
| crypto | conatus gate 0 | 4536 | +0.222 | [+0.019, +0.425] | -1.207 | 0.153 |
| crypto | conatus gate 40 | 559 | +1.313 | [+0.047, +3.046] | -1.166 | 0.084 |
| crypto | participation off | 2594 | +0.481 | [+0.108, +0.954] | -1.157 | 0.150 |
| crypto | participation floor 0 | 2604 | +0.353 | [+0.085, +0.669] | -1.156 | 0.151 |
| crypto | participation floor 1.2 | 750 | +0.781 | [+0.200, +1.439] | -1.186 | 0.107 |
| crypto | regime gate off | 1855 | +0.326 | [+0.023, +0.649] | -1.188 | 0.127 |
| crypto | VWAP band 0.5 | 978 | +1.210 | [+0.395, +2.132] | -1.176 | 0.110 |
| crypto | breakouts off | 963 | +0.535 | [+0.055, +1.135] | -1.168 | 0.110 |
| crypto | turns off | 128 | -1.273 | [-1.316, -1.229] | -1.211 | 0.008 |
| crypto | breakouts only | 128 | -1.273 | [-1.316, -1.229] | -1.211 | 0.008 |
| crypto | ATR mult 1.0 | 1113 | +0.966 | [+0.175, +1.895] | -1.270 | 0.084 |
| crypto | ATR mult 2.5 | 1024 | +0.643 | [+0.192, +1.103] | -1.096 | 0.166 |
| crypto | ATR mult 4.0 | 968 | +0.377 | [+0.107, +0.681] | -1.050 | 0.223 |
| crypto | lag gain 0.5 | 525 | +2.214 | [+0.086, +5.052] | -1.142 | 0.110 |
| crypto | min period 8 | 2088 | +0.490 | [+0.179, +0.831] | -1.184 | 0.129 |


## Reading the evidence

**The median trade is a loss, by construction.** Every configuration in this
study has a median trade of about minus one R: daily minus 1.014, crypto minus
1.153. There is no target, so most trades end at the frozen stop and the win
rate sits between 8 and 18 per cent. That is not a defect, but it means the
win rate carries no information and the mean is the only interesting number.

**The mean is a handful of trades wearing a statistic.** On the daily set the
best five trades out of 588 account for 89 per cent of the total R, and the
single best trade is 23 per cent of it, at plus 83.3 R. On crypto the best five
of 1195 account for 68 per cent, the best trade at plus 216.5 R. Trim the
largest and smallest 5 per cent from each sample and the mean flips to minus
0.718 R on daily and minus 0.847 R on crypto. The longest losing run is 19
trades on daily and 55 on crypto. A pooled mean of plus 0.6 R across 588 trades
with this distribution is not evidence of a predictive marker; it is evidence
that a stop-only exit is convex, which was already known about stops.

**The daily result decays across the sample.** Split at 2010: the 1993 to 2009
half returns plus 0.988 R per trade, confidence interval plus 0.067 to plus
2.086, on 319 trades. The 2010 to 2026 half returns plus 0.095 R, interval
minus 0.393 to plus 0.670, on 337 trades. The later half is indistinguishable
from zero. Crypto splits the same way and holds up better: plus 1.003 R for
2019 to 2022 and plus 0.570 R for 2023 to 2026, the later half still containing
zero at the lower bound. The number of instruments in each half differs, so the
comparison is directional, not controlled.

**Against random entries with the same risk management, the daily timing adds
nothing.** Random entry locations carrying the indicator's own entry spacing
and its own long/short sequence produce a mean of plus 1.376 R per instrument
against the indicator's plus 0.726 R, and the indicator wins on only 5 of 20
daily instruments. Uniformly random entries give the same verdict, plus 1.377
against plus 0.726, with 6 of 20. On crypto the comparison reverses: matched
spacing gives plus 0.345 R to random against plus 0.761 R to the indicator,
which wins on 7 of 11 series. So the case for the marker's timing exists only
in the crypto intraday blocks, and in the equity blocks the entries are, on
average, placed slightly worse than chance.

**Against plain price rules, the machinery does not separate itself on
daily.** A 50-bar Donchian breakout with the identical frozen stop and
identical costs returns plus 0.504 R on the same 20 instruments with 2670
trades, against the indicator's plus 0.606 R with 588 trades. A 20-period EMA
crossover returns plus 0.010 R over 22108 trades and a VWAP regime flip minus
0.017 R over 33830 trades, so both are dead after costs. On crypto the
indicator's plus 0.715 R beats both plain rules by a wide margin, but the
Donchian control there trades 13515 times and returns plus 0.068 R, which is
also indistinguishable from zero after crypto costs.

**Almost all of the contribution comes from turning points, not breakouts.**
Disable the slope-flip detector and keep only confirmed breakouts, and the
daily block produces 135 signals, 128 trades and not one winner; the crypto
block, counted across all fifteen series, produces 155 signals, 144 trades and
one winner. Since exits are stops in nearly every case, a breakout entry with a
1.5 ATR stop is a machine for paying the stop. The consequence is that the
headline pooled numbers are a statement about turning-point entries plus long
holds, not about the breakout component or about the composite score.

**The exit rule, not the indicator, is doing the work.** A 50-bar time stop
takes the daily mean from plus 0.606 R to minus 0.059 R with more trades, a
200-bar time stop leaves plus 0.371 R, and an ATR trail takes it to minus 0.189
R. Cutting trends short destroys the result; letting them run is the entire
source of the positive mean. Costs are secondary: 20 basis points per side on a
next-open fill still leaves plus 0.502 R on daily, because the trades are rare
and long. The sensitivity is to the exit, never to the fee.

**The parameter sweep is a tail-hunting exercise.** The variants that look best
are the ones that trade least and hold longest. A lag-compensation gain of 0.5
returns plus 13.3 R per trade on 287 daily trades with a median of minus 1.016
R and a profit factor of 14: that is one outsized winner, not a better system.
A participation floor of 1.2 gives plus 5.8 R on 238 trades, a Conatus gate of
40 gives plus 3.6 R on 298, and a 200-bar normalization window gives plus 1.4 R
on 343. Every one of those has a confidence interval whose lower bound is at or
below zero, and every one of them has a median trade of minus one R. Loosening
the gates moves in the other direction: Conatus gate 0 gives plus 0.193 R over
1709 trades with participation off at plus 0.288 R over 1211. With 27 variants
tested there is no correction for selection, so no variant here should be
adopted on its own evidence.

## What the study does not establish

- TradingView parity. There is no Pine compiler on this machine. These results
  describe `kbsignal.py` and its agreement with `dsp.py` and the website
  JavaScript, not the platform's own engine, feed, or bar finality.
- Microstructure. Costs are one number in basis points per side. No order book,
  no partial fills, no slippage model beyond that number, and shorts are
  frictionless in a way real equity shorts are not.
- Universe honesty. Both baskets were chosen in 2026 from what survived. AAPL,
  NVDA and BTC are in it; the companies and chains that died are not. The daily
  mean is flattered by that and the crypto mean more so, since fewer than a
  third of 2019 altcoins are still listed.
- Dividends. Equities are dividend-unadjusted, matching a default chart and
  understating long trades.
- Position sizing. The equity curve uses 0.5 per cent risk and a one-times
  notional cap, which the indicator does not specify. The R statistics are the
  durable output; the curve is context.
- Significance. Pooled confidence intervals treat trades as independent. They
  are not: the daily trades cluster in 2008 and 2020, and crypto series
  correlate heavily with each other. The true uncertainty is wider than the
  intervals shown, which is a further reason to read the per-instrument
  dispersion and the random controls before the pooled mean.

## Reproduce

```
cd ~/Desktop/Folders/VibeCode/ehlers-supersmoother/backtest
python3 kbdata.py --group all      # fetch and cache public data (about 25 min)
python3 verify_signals.py          # gates: must pass before reading results
python3 run_series.py all          # the battery, about 18 minutes
python3 run_series.py random_spacing
python3 concentration.py           # tail diagnostics
python3 summarize.py all           # compact text summary of every stage
python3 mktables.py                # regenerate results/tables.md
python3 ledger.py daily_SPY.csv daily reverse close 2.0   # audit one trade list
```

`fetch.log`, `logs/battery.log`, `logs/verify_signals.log`,
`logs/concentration.log` and `logs/random_spacing.log` in this directory hold
the raw console output of the runs reported above.

## Files

| file | what it is |
|---|---|
| `kbdata.py` | fetches and caches the public OHLCV used here |
| `kbsignal.py` | the Pine v2.2 logic, bar for bar, as arrays |
| `kbbt.py` | trade simulation, R accounting, statistics, controls |
| `run_series.py` | the battery: baseline, parameters, exits, controls, splits |
| `verify_signals.py` | non-repainting, invariants and attribution gates |
| `concentration.py` | how much of the mean comes from how few trades |
| `summarize.py`, `mktables.py` | text and markdown extracts of the JSON |
| `ledger.py`, `smoke.py`, `count_bo.py` | single-series audit helpers |
| `results/backtest-*.json` | every number in this document, machine readable |
| `data/` | the cached public series and their manifest |

## Bottom line

The published indicator, taken as its own logic specifies, produces a positive
pooled mean R on both equity and crypto data, and that mean does not survive
scrutiny as evidence of timing skill: it is carried by a few extreme trades, it
is negative at the median, it decays to zero in the modern half of the equity
sample, and on equities it is beaten by random entries carrying the same
spacing, direction mix, stop and costs. What is left standing is narrower and
still useful: confirmed slope-flip entries that hold until a frozen 1.5 ATR
stop cuts them, on liquid instruments, with a crypto intraday edge that beats
both random and plain-price controls. The breakout component on its own
produced no winners at all. Read the indicator as a disciplined way to enter
rare trend continuations and to stand aside the other 87 per cent of the time,
not as a system with an edge that a backtest has now confirmed.
