Methodology

How the market-state signals are computed.

Every patternfetch response links here, so here is the honest version: exactly how each field of a brief is derived from raw candles, what each score means, and — just as important — what it does not mean. No black box, no magic, no implied edge.

Binance + YahoostocksETFscrypto1m → 1w

A brief is deterministic: the same candles produce the same output. Nothing here is trained, fitted to future returns, or tuned to "win" — these are transparent, classical techniques applied to recent price history and packaged compactly so an AI agent can reason over them without dumping a raw OHLCV array into its context. Read the limitations section before you act on anything.

01Data source

Inputs are OHLCV candles (open, high, low, close, volume) for one ticker and timeframe. patternfetch covers US stocks, ETFs and crypto in one schema: crypto candles come from Binance spot (realtime), while stocks & ETFs come from Yahoo. Supported timeframes are 1m, 5m, 15m, 30m, 1h, 4h, 1d and 1w. The header.src field records which source produced the series.

Equity data path (stocks & ETFs)

Stock and ETF candles come from Yahoo on an adjusted-close basis — split- and dividend-adjusted, so prices and the patterns computed from them stay continuous across splits and dividend payments. Equity data is delayed / end-of-day rather than realtime, while crypto stays realtime. Everything downstream — codec, patterns, levels, regime, indicators — is computed identically regardless of asset class.

A brief fetches up to ~200 candles for the requested timeframe (the no-signup /v1/demo uses 120). If the series has fewer than 20 candles, the API returns INSUFFICIENT_DATA instead of computing a brief on too little history. Every value below is a point-in-time snapshot over exactly those candles.

02Compact candle codec (codec.rows)

The candle series is encoded for tokens, not for humans. Each candle becomes one row "o,h,l,c,v", and rows are joined by ;. There is no per-row timestamp — the header's t0/t1 (epoch milliseconds) bound the whole series, and the timeframe fixes the spacing, so the index of a row is its position in time.

Prices are written at a single fixed precision (the number of decimal places), with trailing zeros stripped. The precision is chosen from the magnitude of the maximum high in the series:

Max high in seriesPrice decimals (precision)
≥ 10001
≥ 1 and < 10002
≥ 0.01 and < 15
< 0.018

Volume is written at 0 decimals when ≥ 1, otherwise at 4 decimals. The codec is loss-tolerant by design: it preserves the shape the downstream analysis needs while keeping the payload small. The precision value is returned so a consumer can interpret the digits correctly.

"codec": {
  "rows": "60125.4,60480,59890.1,60310.7,1284;60310.7,60590,60180,60420.2,977;…",
  "sax": "dcefdcbe",
  "precision": 1
}

03SAX shape signature (codec.sax)

The sax string is a coarse fingerprint of the recent shape of the series — a way for an agent to compare or recall shapes cheaply. It is computed as Symbolic Aggregate approXimation over the series of close-to-close returns:

  1. Take consecutive close-to-close returns.
  2. z-normalize them (subtract the mean, divide by the standard deviation).
  3. PAA-collapse the normalized series into at most 8 segments (Piecewise Aggregate Approximation — average within each segment).
  4. Quantize each segment into the alphabet a–g using the fixed breakpoints [-1.2, -0.5, -0.15, 0.15, 0.5, 1.2] (so values below −1.2 → a, between −1.2 and −0.5 → b, … above 1.2 → g).

The length of the string is min(8, candles − 1). It is a shape descriptor, not a forecast — two identical sax strings mean two stretches looked alike after normalization, nothing about what comes next.

04Patterns (analysis.patterns[])

Patterns are detected geometrically from the candle geometry — there is no model "predicting" them. Each hit is an object:

{ "name": "double_bottom", "confidence": 0.86, "at": 142, "span": 18,
  "evidence": { "tf": "4h", "band": "0.75-1.00", "horizon": 10, "n": 1508, "hitRate": 0.475,
                "lift": { "baseline": 0.497, "lift": -0.022, "informative": false } } }

Important: confidence is a geometric quality score, not a probability of profit. A 0.86 means the shape is a clean example of that pattern — not that a trade has an 86% chance of working.

04bEvidence — the backtested base rate (analysis.patterns[].evidence)

A clean geometric score is still only a shape match. To stop a confident-looking label from being quoted blindly, each directional pattern also carries an evidence block: how that exact pattern, on that timeframe and confidence band, has actually resolved historically — and, crucially, how often the same market went that way with no pattern present at all. Only the second number makes the first one readable.

"evidence": {
  "scope": "major crypto pairs",
  "tf": "4h",
  "band": "0.75-1.00",
  "horizon": 10,
  "n": 1508,
  "hitRate": 0.475,
  "ci95": 0.025,
  "dist": { "winRate": 0.475, "median": -0.002, "mean": 0.0, "min": -0.347, "max": 0.495 },
  "lift": {
    "baseline": 0.496997,
    "baselineN": 11990,
    "lift": -0.021997,
    "ci95": 0.026746,
    "ci95Clustered": 0.035172,
    "informative": false,
    "reading": "indistinguishable-from-baseline"
  },
  "definition": "realizable gross directional base rate: fraction of non-overlapping historical occurrences whose close-to-close return over the 10 bars AFTER the pattern becomes knowable went its expected direction. Forward window starts at detection (chart patterns: +2 pivot-confirming bars, no lookahead). No stop, fees or slippage. Not a prediction."
}
The example, on the scale double bottom · 4h · 47.5% vs base 49.7%
Hit rate · 10 bars 47.5% base 49.7%
30%baseline 49.7%70%
n = 1,508 · lift −2.2pp, 95% CI ±3.5pp (cluster-robust) · horizon 10 bars · no lookahead — the band covers the baseline, so the pattern adds nothing measurable. On crypto the baseline happens to sit at ~50%; on US equities it does not, which is exactly why the line moves with the market instead of being pinned at 50%.

What the measurement actually found. These are realizable gross directional base rates: the forward window starts only at the bar a consumer could actually act on. Chart patterns (double top/bottom, head & shoulders) become knowable two pivot-confirming bars after their low/high prints, so the window starts there — counting from the pivot itself leaks those bars and inflates the rate by ~10 points (a trap most naive backtests fall into; we removed it).

Measured this way and read against the pattern-free baseline, the result is close to uniform: across 105 buckets — 60 on US stocks & ETFs, 45 on major crypto pairs — exactly three clear an unadjusted cluster-robust 95% interval, and all three are crypto. That is fewer than the ~5.3 you would expect from chance alone at that threshold, and none of the three survives a correction for having tested 105 buckets. On US stocks & ETFs the count is 0 of 60. The three are named and tabulated below — a null result that quietly drops its own counter-examples is worth nothing. The geometric confidence barely moves the rate either.

Crypto is the cleanest demonstration. There is no structural drift in the crypto corpus, so its baselines sit at ~50% on every timeframe — and the bullish/bearish split seen on equities simply disappears: bullish engulfing 49.2%, bearish engulfing 49.1% at 4h, both against a ~50% baseline. The split on stocks was never the pattern. It was the drift.

We publish this because the alternative is worse for you: a model handed a bare 41% will narrate a bearish case around it. No stop, no fees, no slippage, not a prediction. Past performance does not guarantee future results.

The three buckets that clear an unadjusted interval

A bucket is flagged when the absolute lift exceeds its cluster-robust 95% interval (lift.informative in the published table). Three of the 105 do, all of them in the crypto corpus:

BucketTimeframeConfidence bandLiftInterval (±)nzReading
bullish_engulfing1h0.75–1.00−1.76pp1.368,8972.53below baseline
double_bottom1d0.50–0.75−12.25pp8.911432.69below baseline
double_top1d0.50–0.75+9.78pp8.421492.28above baseline

Two of the three sit below their baseline — the pattern resolved its way less often than an arbitrary window did. Only double_top on the daily lands above its baseline, and it is the only bucket of all 105 that does. It rests on n=149 occurrences.

Three things keep that from being an edge:

We name these rather than round them away because a study that reports only its nulls, and buries the one bucket that went the other way, is committing the error we built this measurement to correct.

The full measurement — all 105 buckets with their baselines, both intervals and sample sizes, the charts, and the limitations in long form — is written up separately: Do chart patterns beat the market's own baseline? A companion measurement counts the tokens the compact brief actually saves against raw candles, with the same insistence on stating limits: What does raw OHLCV cost an LLM in tokens?

04cWhere this measurement is weak

The result above is a null finding, and a null finding is only worth as much as the method behind it. These are the limitations we know about, stated so you can discount our numbers yourself rather than take them on trust.

Two of these — overlap and survivorship — make our own numbers look better than they are. We would rather name them than have you find them.

05Support / resistance (analysis.levels)

Levels are built by detecting swing pivots (local highs and lows) in the series and clustering nearby pivots into price levels. Each level is:

{ "price": 59820.4, "strength": 1 }

strength is in 0..1, where 1 is the strongest — i.e. the most-tested level, with the most pivots clustered at it. levels.support holds levels below the current price and levels.resistance holds levels above it. Strength reflects how often price has reacted there in this window, not whether it will hold next time.

06Regime (analysis.regime)

The regime is a compact classification of the current trend state:

{ "trend": "up", "strength": 0.42, "volPct": 2.13 }

07Interpreted indicators (analysis.indicators)

Indicators are returned as interpreted state, not a wall of numbers — an LLM reasons better over a label than over a bare float. Each indicator carries its value and a state:

"indicators": {
  "rsi": { "v": 58.3, "state": "neutral" },
  "ema": { "v": 61240.8, "state": "above_20_50" }
}

The v is the raw indicator value if you want it; the state is the digested read your agent can act on directly.

08One-line summary (analysis.nl)

The nl field is a single natural-language line composed from the fields above — specifically the trend, the last-bar % change, the RSI value and state, and the top pattern. It exists so the model never has to do arithmetic on raw numbers (where LLMs hallucinate); the line is already a ready-to-reason summary.

"nl": "BTC/USDT: uptrend (moderate), +1.94% last 4h, RSI 58.3 (neutral), double_bottom (conf 0.86, n=18, hist 51% over 10b)."

It is a faithful restatement of the computed analysis, not an opinion or a call. The hist tail mirrors the top pattern's evidence base rate (section 4b) so the one-line summary can never sound more certain than the data behind it. Note the two n's: in this one-line summary n is the pattern's bar-span (how many candles it covers), while evidence.n is the historical sample size behind the rate.

09Analogs (/v1/analogs)

Analogs do in-series shape matching. patternfetch takes the current window and searches earlier windows of the same series for ones whose shape is similar, using cosine similarity over z-normalized windows. For the similar windows it then looks at what happened over a fixed forward horizon and returns the full outcome distribution:

{ "n": 14, "horizon": 12, "winRate": 0.57, "median": 0.011, "mean": 0.004, "min": -0.083, "max": 0.072 }

This is not a prediction and not a strategy backtest. It describes what happened in the past after similar-looking windows — and it includes the losers (that is the whole point of returning min and the full distribution rather than a single rosy number).

10Limitations — read this

We would rather you trust the data because we are candid about its limits:

Geometric heuristics can misfire. Pattern and pivot detection is rule-based on candle geometry; it can flag shapes that do not "work" and miss ones a human would see.
Confidence and strength are scores, not profit probabilities. A high confidence or strength means "clean shape" or "well-tested level" — never "likely to make money".
No pattern in our corpus shows a directional edge that survives correction. Of 105 buckets, three clear an unadjusted interval — fewer than the ~5.3 chance alone would produce, and none survives correcting for 105 comparisons; on US stocks it is 0 of 60. Treat analysis.patterns as a description of shape, not as a signal — see the three that clear an unadjusted interval and where that measurement is weak.
Analog distributions describe the past, not the future. Similar-looking history is not destiny; the distribution includes losing outcomes and offers no guarantee.
Values are point-in-time snapshots over the fetched candles. They go stale as new candles print, and a different fetch window can produce different levels, patterns and regime.
One source per asset class. Crypto is Binance spot only (no cross-exchange aggregation); stocks & ETFs are Yahoo only, split & dividend adjusted and delayed/EOD. No other venues.
It is impersonal market data, informational only. patternfetch is NOT investment, financial, legal or tax advice, is not personalized to you or any goal, and is non-executing (it never places orders).

See it for real, or go deeper:

Related: /docs · /try · /disclaimer

FAQQuestions we get asked

Where does patternfetch get its data?
patternfetch covers US stocks, ETFs and crypto in one schema. Crypto candles come from Binance spot (realtime); stocks and ETFs come from Yahoo, split- and dividend-adjusted and delayed/EOD. Supported timeframes are 1m, 5m, 15m, 30m, 1h, 4h, 1d and 1w. A brief fetches up to roughly 200 candles (the no-signup demo uses 120). If fewer than 20 candles are available the API returns INSUFFICIENT_DATA rather than a low-confidence brief.
What is the SAX shape signature?
It is a short string (codec.sax) computed over z-normalized close-to-close returns, PAA-collapsed to at most 8 segments and quantized into the alphabet a–g at the breakpoints [-1.2,-0.5,-0.15,0.15,0.5,1.2]. It is a coarse fingerprint of the recent shape of the series, not a forecast.
Does a high confidence or strength score mean the trade is likely to profit?
No. Pattern confidence (0..1) is a geometric quality score for how cleanly a shape matches its template, and level strength (0..1) measures how strongly a price level has been tested. Neither is a probability of profit. They are heuristic scores, not predictions.
Are the /v1/analogs results a backtest or a prediction?
Neither. Analogs find past windows in the SAME series whose z-normalized shape is similar (by cosine similarity) to the current window, then report the full distribution of what happened over a fixed forward horizon — count, win rate, median, mean, min and max. It describes the past, includes losers, and is not a strategy backtest or a forecast.
Is patternfetch investment advice?
No. Every value is impersonal, point-in-time market data and algorithmic signal output, for informational purposes only. It is not investment, financial, legal or tax advice, is not personalized, and is non-executing.