Free key · 10 calls/day

What just happened — and how often did it work out?

Price-action traders name recurring events: a breakout from a tight range, a climactic spike, price pressing the edge of a range. This endpoint detects those events in the window trading right now and attaches to each one the outcome rate we measured for it — pre-registered, on sixteen years of S&P 500 futures data — instead of the number the classroom quotes.

The name credits the tradition the definitions come from: the price-action vocabulary Al Brooks teaches. The output is descriptive — an events array with measured historical frequencies and sample sizes attached, not a verdict and not a signal. Where the tradition’s number failed our measurement, the response says so.

Two further blocks come with every read. day_type is the calibrated five-class day-type distribution for the session in progress — the read this endpoint was originally built around — served in the day session, where a session exists to read. shape is plain arithmetic over the bars already printed: where price closed inside its own range, how much of the travel was one-way, the deepest pullback. One estimates an unfinished day; the other measures a finished stretch. They are kept apart on purpose, and shape carries no accuracy figure because nothing is being predicted.

Get free API key View a real response

Live around the clock — including the gap nobody covers

A day-type read needs a session; an event read only needs recent bars. That difference is why this endpoint has three windows and always has something live to say on 24-hour instruments: the US day session (rth), the Asian session (asia, 09:00–16:00 Beijing), and the stretch in between — gap, 16:00–21:30 Beijing, after the Asian close and before the US open. Each window’s rates were measured separately: the same breakout definition fails 62.6% within 10 bars in the US session and 62.8% in the gap window, so the structure holds there too, and the response always pairs an event with the rates for the window it fired in. Omit ?window and the read follows whichever window has the freshest bars.

The same asymmetry decides which of the other two blocks you get. In the gap and Asian windows day_type returns available: false with the reason attached — the model reads a session from its open, and there is no open to read from — while shape still measures what those hours printed. In the day session you get both.

The five event types

Event What it is, and what comes attached
range_breakout The day’s first close outside a tight trading range. Carries the measured failure rates and a calibrated per-event estimate of whether this one closes back inside.
breakout_follow_through The bar after a breakout closes beyond the breakout bar’s extreme. Carries the measured rate at which the projected measured-move target was touched.
range_position A close in the top or bottom third of an established range. Carries the measured rate of moving one third of the range height each way — including the half of the claim that did not confirm.
long_range_breakout A breakout from a range that has held for 20+ bars. The tradition says the direction is 50/50; the measured split is skewed upward — reported as measured.
climactic_spike Two or more consecutive strong bars closing near their extremes with little overlap. Carries the measured rate at which the spike’s projected target was touched.

Each type reports at most its first occurrence per window per day — that is the definition the reference rates were measured under, so the live detection and the published numbers describe the same population.

The endpoint

Base URL https://api.quantdata.uk. It takes one header, X-API-Key: qd_.... No SDK, no OAuth, no session state. One subscription covers every endpoint on the site — the Weis Wave API and the options-positioning pair, /v1/maxpain and /v1/gamma, included. Full parameter and error tables live in the API reference. There is also a POST /v1/brooks/backtest that runs the identical detectors over bars you supply, so you can check the calibration on your own instrument yourself.

GET /v1/brooks/{symbol} — events in the live window

Returns the events detected in the freshest window, the quote the read was computed from, and the volatility base every definition is scaled by. This example is a gap-window read — the hours when no day-type read can exist at all, which is exactly what its day_type block says.

$ curl -H "X-API-Key: qd_9f3c..." \
       https://api.quantdata.uk/v1/brooks/ES=F

{
  "symbol": "ES=F",
  "window": "gap",
  "window_is_live": true,
  "session": "2026-08-06",
  "bars": 41,
  "quote": { "open": 6362.25, "high": 6371.5, "low": 6355.0,
             "last": 6368.75, "prev_close": 6360.5, "change_pct": 0.13 },
  "source": "leandata",
  "volatility_base": { "avg_daily_range_5d": 62.75,
    "definition": "mean high-low range of the last 5 completed US regular sessions" },
  "events": [
    {
      "type": "range_breakout",
      "direction": "up",
      "at": "2026-08-06T18:05:00+08:00",
      "bars_since": 6,
      "breakout_level": 6366.0,
      "range": { "low": 6358.25, "high": 6366.0 },
      "outcome_so_far": { "failed_within_10_bars": null, "resolved": false,
                          "bars_observed": 6 },
      "conditional_estimate": {
        "p_fail_within_10_bars": 0.58,
        "meaning": "Of past breakouts whose own rolling context looked like this one,
                    this share closed back through the breakout level within 10 bars."
      },
      "reference": { "failure_rate_pct": {
        "within_5_bars":  { "rate_pct": 50.4, "n": 3918 },
        "within_10_bars": { "rate_pct": 62.8, "n": 3916 },
        "within_20_bars": { "rate_pct": 72.4, "n": 3882 } } }
    }
  ],
  "day_type": {
    "available": false,
    "why": "No day-type model has been trained or validated for the gap window.
            The day session has its own model (mode=day_session) and the Asian
            window has its own (mode=asia_session); the gap window is covered
            by the shape block and the event reads instead."
  },
  "shape": {
    "bars_measured": 41,
    "net_move_pct_of_range": 61.2,
    "close_position_in_range": 0.83,
    "one_way_share": 0.58,
    "deepest_pullback_pct_of_range": 24.5,
    "high_made_at": 0.92,
    "low_made_at": 0.04,
    "direction_changes": 3,
    "range_tightness": 0.74,
    "not_a_forecast": "Measured description of bars that have already printed.
                       It carries no accuracy figure because nothing is being
                       predicted, and it is not the day-type classification."
  },
  "disclaimer": "Educational analytics, not investment advice."
}

Read that response as: an upward breakout printed six bars ago and has not yet resolved; historically 62.8% of gap-window breakouts closed back through the level within 10 bars (n=3,916), and this particular one — given its own range tightness, breakout strength and context — carries a calibrated 58% estimate. The shape block adds that the stretch finished near the top of its own range (0.83) with a little over half the travel in one direction. Descriptive, dated, sample-sized.

The scoreboard

The conditional estimate was confirmed under a pre-registered protocol whose only judged cell was NQ futures 2023+ — an instrument and a period the model had never seen.

Zero-shot AUC on NQ 2023+
0.646
n=2,637 breakouts; logistic-regression control also passes
Lowest vs highest predicted tercile
54.2% / 79.1%
actual failure rates — a 24.9-point spread the model called in advance
Calibration error
5.1%
when it says 60%, the outcome happens about 60% of the time
QQQ zero-shot AUC
0.660
n=2,017 — the transfer is not an ES memory

Calibration is the part people skip and the part that matters most for an agent. An uncalibrated 60% is a vibe. A calibrated 60% is a number you can put into an expected-value calculation. The pre-registration documents and the full result tables are on the methodology page.

The famous claims, fact-checked

Price-action classrooms quote “80%” and “90%” constantly. We tested the claims one at a time against 3,946 S&P 500 E-mini trading days of 5-minute data, 2010 to 2026. This table is free to read here and on the methodology page, which carries all the verdicts. No key, no account.

Claim Classroom says Measured Sample
90 minutes after the open, one side’s daily extreme is already in place 90% 81.2% 3,946 days
On a bull day, the low of the day forms in the first third of the session 90% 83.3% 3,946 days
After an opening-range breakout, the opposite extreme holds 90% 83.5% 3,578 days
Trading-range breakouts fail (the famous “80% rule”) 80% 52.1% / 62.6% / 70.6%
within 5 / 10 / 20 bars, first breakout per day
~3,900 events
A breakout from a long-lived range goes either way 50/50 50/50 57.8% upward
skewed in every window — claim reversed
2,745 events

Meta-verdict: the directions are mostly correct, the numbers are systematically inflated by 7 to 10 percentage points — and two claims failed outright. The “80% rule” never reaches 80% under any definition of range tightness we tried. The 50/50 long-range breakout claim reversed: the split is skewed upward in every window we measured, consistent with the index’s long upward drift. And the sell-zone half of the range-position claim did not confirm in the US session or the gap window. The practical rule stands: trust the direction, discount the number — and check which claims did not survive at all.

Get free API key Read the API reference

Honest limits

Publishing where the numbers fail is not modesty, it is the only way the successes mean anything. Three limits matter before you buy.

Rates were measured on ES

Every reference rate comes from S&P 500 E-mini futures. The conditional model was additionally validated zero-shot on NQ and QQQ. On any other symbol — single stocks, Hong Kong, A-shares, crypto, FX — the response carries a coverage_note and the numbers are context about ES, not claims about that market.

Half of one claim is dead

The range-position claim is taught symmetrically: sell the top third, buy the bottom third, 60% each way. Measured, only the bottom-third half carries support (55.6% in the Asian window, its best), while the top-third half is indistinguishable from or below 50% in the US session and the gap. The response reports the asymmetry rather than averaging it away.

It never says buy or sell

There is no signal endpoint, no entry, no stop, no target. The output is a list of events with historical frequencies and sample sizes. If your agent turns that into a trade, that is your agent’s decision and your risk — and we would rather you use it as a context layer than a trigger.

Who this is for

A good fit

  • Students of price action who want an answer key: spot the event on your own chart, then call the API and compare — including how often it actually resolves the way the classroom says.
  • People building trading agents who need structural context in one line of code, with estimates that are actually calibrated, at any hour the market trades.
  • Researchers and sceptics who want measured base rates instead of course slides — including the claims that failed.

A bad fit

  • Anyone looking for entries, exits or alerts. This is a context layer; there is no signal in the box.
  • High-frequency work. The unit of analysis is a 5-minute bar, not a tick.
  • Anyone who needs the number to be big. Our honest numbers are breakout failure rates in the 50–72% band and an AUC of 0.646 — real, measured, and not huge. If that is disappointing, the products promising 90% are one search away, and they are lying.

The day-type read

This endpoint began as a five-class day-type probability read, and that read is still here — now one block of the response rather than the whole of it. It answers a different question from the events around it: not what just happened, but which of five scripts the session in progress is on course to finish as. Because that question needs a session, the block is served in the day session and reports available: false with its reason elsewhere.

$ curl -H "X-API-Key: qd_9f3c..." \
       "https://api.quantdata.uk/v1/brooks/ES=F?window=rth"

{
  "day_type": {
    "available": true,
    "bars_read": 54,
    "probs": { "TrendUp": 0.34, "Reversal": 0.22, "TrendingRange": 0.21,
               "Range": 0.2, "TrendDown": 0.03 },
    "meaning": "Calibrated probability distribution over the five day types this
                session could resolve into. Report the distribution, not just the
                top class — the second script is often very much alive.",
    "accuracy": "Measured on held-out S&P 500 E-mini years the model never trained
                 on: at 18 bars (90 minutes in) top-1 53.5% and top-2 68.6%; over a
                 complete session 66% and 80%. The majority class covers 37% of
                 days — quote that baseline alongside any accuracy figure."
  }
}
Complete session · top-1
66%
held-out ES years the model never trained on
Complete session · top-2
80%
the right class is in the top two four times in five
90 minutes in · top-1 / top-2
53.5% / 68.6%
at 18 bars — earlier than that, nothing has been measured
Majority-class baseline
37%
what always guessing the commonest class already gets you

Quote the 37% baseline whenever you quote the 66%: the gap between them is the whole claim, and 66% on its own reads as far more than it is. Calibration error is under 10%, so a stated 70% corresponds to roughly 70% of such sessions resolving that way. The same read powers the published day-type × volume-event gating study (56.5% vs 50.8% on ES, replicated zero-shot on NQ and QQQ) behind the QQQ strategy case study. The full record is on the methodology page.

The shape block — measurement, not prediction

shape exists because the honest answer during the eleven hours without a session is not silence, and it is not a day type either. It is arithmetic over the last stretch of bars: close_position_in_range (1.0 finished at the top of its own range, 0.0 at the bottom), one_way_share (near 1.0 the move was close to a straight line, near 0 it retraced most of what it covered), deepest_pullback_pct_of_range, high_made_at and low_made_at (0 = start of the stretch, 1 = the latest bar), direction_changes and range_tightness.

Every one of those is recomputable from the same bars, which is the only promise it makes. It has no accuracy figure, and it is not entitled to borrow the day-type model’s: our definitions, no pre-registered test behind them, nothing being predicted. Reported as a trend-day or reversal-day call it would be a claim we have not measured, so the response says plainly that it is not a forecast and not the day-type classification.

Frequently asked

What happened to the day-type probabilities?

They are part of this response, in the day_type block — each window served by the model trained for it. In the regular day session it is mode=day_session: the same five-class model, the same held-out numbers (66% top-1 and 80% top-2 over a complete session, 53.5% and 68.6% at the 90-minute mark, against a 37% majority-class baseline). In the Asian window of 24-hour instruments it is mode=asia_session: a separate model trained natively on Asian windows under a pre-registered protocol and confirmed zero-shot on NQ — top-1 35.7/38.5/41.0% at 18/42/63 bars on held-out ES windows (n=908, majority-class baseline 30.2%) and 35.2/40.1/45.4% on NQ (n=915, baseline 29.9%). Its five classes come from their own frozen labelling rule, so those figures are not comparable to the day-session 66%/80% and the two sets must never be mixed. The gap window has no validated model and returns available: false; the shape block describes every window with plain arithmetic either way.

What is the difference between day_type and shape?

They answer different questions. day_type estimates what an unfinished session will resolve into, so it is a prediction, it can be wrong, and it comes with a measured accuracy and its majority-class baseline attached. shape measures the bars that have already printed — where price closed inside its own range, how much of the travel was one-way, the deepest pullback, where the high and the low landed, how many times direction changed. It is arithmetic, so there is nothing to be right or wrong about, it carries no accuracy figure, and it exists in every window including the gap. shape is not a day type and borrows none of the day-type numbers; anyone reporting it as one is misreading the response.

Does the famous "80% of range breakouts fail" rule actually work?

Not at 80%. Measured on S&P 500 E-mini trading days from 2010 to 2026 under a pre-registered definition, breakouts from a tight trading range failed 52.1% of the time within 5 bars, 62.6% within 10 bars and 70.6% within 20 bars in the US session (about 3,900 events per horizon, one per day). The direction of the claim is right — most breakouts do fail — but the number never reaches 80% under any definition we tested. This endpoint attaches those measured rates to the breakout it just detected, so you quote the measurement, not the folklore.

What is the conditional estimate, and should I trust it on my symbol?

Every breakout looks different: how tight the range was, how strong the breakout bar was, how often the range had been tested. The conditional estimate feeds twelve such features of the event you are looking at into a small gradient-boosted model and returns a calibrated probability that this breakout closes back through its level within 10 bars. It was validated zero-shot on NQ futures 2023+ — an instrument and period the model never trained on — where it separated breakouts into a lowest tercile that failed 54.2% of the time and a highest that failed 79.1% (AUC 0.646, calibration error 5.1%, n=2,637), and on QQQ (AUC 0.660). On symbols other than ES, NQ and QQQ the response carries a coverage_note and you should treat the estimate as descriptive.

Is this investment advice?

No. Quant Data publishes descriptive analytics and developer tooling for people learning quantitative trading. The API never returns a buy or sell instruction, never sets a price target, and never sizes a position. It reports which classical events just occurred and how events like them resolved historically, with sample sizes. Trading involves risk of loss and past performance does not guarantee future results.

Which symbols can I call?

US tickers (NVDA, TSLA, SPY), US futures with a =F suffix (ES=F, NQ=F, GC=F), Hong Kong stocks as digits plus .HK (3690.HK), China A-shares as 6-digit codes (600519), crypto pairs such as BTC-USD, and spot FX written as EURUSD — never EUR/USD, because the slash splits the URL path. The gap and asia windows exist for 24-hour instruments only. One caveat: callable is not the same as validated. The reference rates were measured on ES and the conditional model was validated on ES, NQ and QQQ; on anything else the response says so and you should relay that.

Do I need to be a programmer to use it?

Not for the early-access workflow, but your AI workspace must have a terminal and outbound network access so it can attach the private API header. The personalized prompt works in ChatGPT Work Desktop and Claude Cowork or Claude Code; standard ChatGPT and Claude web or mobile chat modes are not supported yet. Paying still requires the cardholder to complete Stripe checkout once.

Use Brooks without the full API bundle. Brooks-only access is $9.90 per month, starts immediately, and can be cancelled any time.

Buy Brooks only

Already holding one of the old single-product keys? It keeps working at the price you signed up at. If you want the endpoints it does not cover, email us and we will open them on the same key rather than have you pay twice.

Quant Data is an independent research project and is not affiliated with or endorsed by Al Brooks. If you want to learn the method itself, buy his books.