# Standard Error Bands reversal scalping on ES futures, 2-minute bars - specification (RIP Backtest protocol v1.0 plus the 8 Oct 2026 rules)

Specification written and **frozen 2026-11-07**, approved in advance by the owner the same day ("toate le facem": all three variants). It was written before any band, signal or trade of this test was computed. Once frozen, any change of a rule is documented and treated as a separate test; bugs may be fixed.

## Why this test, and what was looked at before
- **The idea:** a Reddit post in r/FuturesTrading ("How to be insanely profitable scalping by doing everything 'wrong'...", 30 April 2025): on 2-minute charts, fade a touch of the Standard Error Bands and target their middle line. The owner sent the post and its comments.
- **The rules below are ours.** The post gives no stop, and its filters are judged by eye. The owner decided (7 Nov 2026) that we test the idea, not the poster: our own stop and our own filter, fixed here. The poster's performance claims are not used.
- **Earlier work on this file:** many ES studies, among them two band rules (Bollinger Band reversal on ES 1-hour, the VWAP bands on ES 5-minute), both Buried. Mean-reversion bands have been tried in many variants on the site, so the development bar is **t >= 2.5**. No Standard Error Band number has been computed on any market.

## Data
- **ES** (E-mini S&P 500 futures), `ES_1min.csv` (NinjaTrader, back-adjusted continuous contract, SHA-256 `ce7d4408...`), the file of our ES tests.
  - 1-minute bars on the New York clock, stamped at their close, all trading hours.
  - Cut at **2026-09-25 17:00 New York**.
  - $50 a point, tick 0.25 point.
- **2-minute bars:** the bar ending at an even minute E holds the 1-minute bars stamped E-1 and E; it exists if either exists. Open = the first's open, close = the last's close, high and low from both.
- **The bands use every 2-minute bar, all trading hours, in sequence**, as a chart does.

## The bands (on 2-minute closes)
- **Regression:** for each 2-minute bar t, with 21 bars t-20..t, fit an ordinary least-squares line of close on x = 0..20.
  - **mid_raw** = the fitted value at x = 20; **b** = its slope per bar.
  - **SE** = sqrt(sum of squared residuals / 19).
- **Raw bands:** **up_raw** = mid_raw + 2 x SE; **lo_raw** = mid_raw - 2 x SE.
- **Smoothing:** **mid, up, lo** = the simple mean of the last 3 raw values. Each needs 23 bars.
- A minute uses the bands of the **last 2-minute bar completed before the minute started**: for the minute stamped m, the bar ending at or before m - 1. The bands are never computed from the minute being traded.

## Rules
- **Session:** the US session of the exchange calendar of our ES tests (`rb_lib.sessionClose`).
  - Entries are allowed in the minutes stamped 09:46 to 15 minutes before the close (15:45 normally).
  - Every position is closed at the close of the last minute at or before the session close.
- **Entry:** when flat:
  - a **buy limit at lo** and a **sell limit at up** (the bands that apply to the minute);
  - a buy fills when the minute's low is at or below lo, at lo, or at the minute's open if it opens below;
  - a sell is mirrored;
  - if both are touched in one minute, the one nearer the minute's open is taken.
  - One position at a time; after an exit, a new entry can fill from the next minute on.
- **Target:** the **mid** of the entry minute, fixed for the trade. D = the distance from the fill to the target, in points. No trade if D <= 0.
- **Stop:**
  - **SEB-1** and **SEB-1F**: D beyond the fill, so the target and the stop are the same distance away;
  - **SEB-2**: 2 x D beyond the fill.
- **Inside the minutes:**
  - In the fill minute only the stop is checked.
  - In later minutes, a level the minute opens beyond fills at its open; otherwise the stop, if touched, is taken before the target.
- **Trend filter (SEB-1F only):** no new entry when |b x 20| > (up - lo), that is, when the regression line moved more over its 21 bars than the width of the band. b is from the last completed bar.
- **Variants:** **SEB-1, SEB-2, SEB-1F**, all long and short.

## Costs
- **$30.76 per round trip** (commission $5.76 plus one tick of slippage on each fill), as in all our ES tests.
- A limit fill may get no slippage in reality; the full cost is kept.
- Results in dollars per contract and in R (R = the stop distance).

## Periods, gates, control
- **Periods (by trade date):**
  - development 2020 to 2023;
  - validation 2024-01 to 2025-03;
  - holdout 2025-04 to 2026-09-25, **already used** by earlier ES tests.
- **Six gates per variant**, in dollars per contract:
  1. validation profit factor >= 1.2;
  2. at least 50 trades in each period;
  3. development **t >= 2.5**;
  4. above the 95th percentile of the control;
  5. positive without the 10 best trades;
  6. holdout net positive (flagged as used).
  
  A variant with fewer than 30 trades is Inconclusive.
- **Control (moved days):**
  - Each trading day carries its list of real trades: entry minute, direction, and stop and target distances in points.
  - The day lists are moved, all together, by one random circular offset of at least 5% of the trading days.
  - On each real day, each moved trade is entered at the market at the open of its minute (or of the first minute after it, before the close), with the same distances, the same exits and costs.
  - Control trades are taken independently of each other.
  - 500 runs, seeds 1000 + run. Statistic: the average net dollars per trade.
  - It asks whether the bands pick better moments than the same trades placed on other days.
- **Missing data (rule 3):** trades open across a known hole of the file (`data_holes.json`, ES) are reported, and every gate and verdict is recomputed without them.
- **Also reported:**
  - trades per day;
  - exit reasons;
  - the win rate with its Wilson interval;
  - R before and after costs, and the cost as a share of R;
  - longs and shorts, results by year;
  - **April 2025 on its own** (the tariff crash month);
  - the median target distance in points.

## Engine and self-test (before the run)
New code:
- `seb_lib.js`: 2-minute bars, bands, rules and control, reading the ES minutes with the loader of `na_lib.js`;
- `seb_ref.js`: an independent re-implementation, with its own reading of the file, the bands computed from the closed-form regression sums, and its own walk;
- `seb_selftest.js`. It checks:
  1. the bands of a short hand-made series against a hand calculation;
  2. hand-built minutes:
     - a buy at the lower band reaching the target;
     - a sell stopped;
     - a fill at a lower open;
     - both bands in one minute;
     - the stop only in the fill minute;
     - stop and target in one minute;
     - a gap through the stop;
     - the session close;
     - no entry before 09:46 or after the entry window;
     - the filter blocking an entry;
  3. engine against reference, trade by trade, for the three variants on the real data;
  4. the control at offset 0 entering on the same days and minutes, in the same directions and with the same distances as the real trades.

## Before the article
Results go to the owner first. One article, published only with the owner's agreement, on a day after the two DAX articles.

## Reading the result
"Buried" or "Inconclusive" mean a convincing edge was not demonstrated; neither is proof that none exists. Our stop and filter are our choices; other stops could give other results.

### Run log
- 2026-11-07: frozen (owner: "toate le facem"). sha256 of this file above the "### Run log" heading: b1e07a962b615ca09185056d74ea0fe08cb73bacf48cd2f5eff1b05141ee2e67.
- 2026-11-07, before the run: the first self-test run failed 5 checks, none of them about a result. (a) Two hand-built cases had wrong expectations written in the test (a stop level the made-up prices never reached, and default minutes that touched the target); the cases were corrected. (b) Engine and reference differed on 3, 2 and 2 trades of about 18,000 to 32,000, all where the middle line was exactly a tick price and floating-point noise put it a hair above in one code and below in the other. Fixed in both: band values are rounded to 1e-8 points and the target is the rounded middle line itself. The self-test then passed with 0 differences. No rule changed.
- 2026-11-07: seb_selftest.js PASSED on the final code (0 differences on 32,145, 18,610 and 23,273 trades). seb_run.js -> seb_out/: all three variants Buried 1/6; gross per trade -$2.32, -$1.58 and -$0.17 against $30.76 of costs; control percentiles 13.4, 50.8 and 51.6; rule 3 changes no verdict. No rule was changed.
- 2026-11-07: the owner asked for the article ("Da, scrie-l"). Written from the outputs of the single run, unchanged, to be published on 2026-11-10, after the two DAX posts.
- 2026-11-09: published on 9 Nov, a day earlier than planned, at the owner's request ("hai sa facem azi seb ca maine nu am timp"), on the same day as the DAX Fibonacci post. Before publication a review checked the code against this specification (an independent implementation by the reviewer matched all 74,028 trades: no misreading) and the article; wording fixes only (the cost total, the stop rules, the FAQ's control, the target and cost share, the session times on early-close days, the win rate before costs, averages written as if for every trade, the re-entries). No number of the run changed.
