Methodology

How we record
and score signals.

Accuracy numbers only mean something if you know exactly how they're produced. This page documents how a signal is recorded, how its outcome is measured, and the limits of what the published figures prove.

It is written to be checked. Every rule below corresponds to code in the running service, every published figure is served by a public endpoint you can call without a key, and the sections at the end list the studies we ran that found nothing — because a methodology page that only describes the wins is marketing, not methodology.


Terms, defined once

Most disagreements about performance numbers are really disagreements about definitions. Ours:

Where the inputs come from

A confirmation is assembled from context layers that are collected independently and on different clocks:

Layer coverage is not uniform across symbols or across time. A symbol with no whale positioning and no on-chain coverage is scored on derivatives alone, and that is a materially thinner reading than a symbol where all three agree — even though both come back wearing the same HIGH/MEDIUM label. The label does not encode how much evidence was available.

Coverage is a distribution, not a product

“519 symbols across nine venues” is the kind of sentence that is true in each half and false as a whole. Every dimension being true does not make the product true, so we measured the product directly. Over the 24 hours to 2026-09-02, counting how many venues actually carried each symbol’s open interest: 8 symbols of 519 reached all nine venues, 33 reached exactly one, and the median symbol reached six. That histogram is published in full at GET /v1/coverage, and no page on this site quotes the product instead.

The same endpoint keeps three answers apart that are usually rendered identically, which is how a gap quietly becomes a claim: the venue does not list this instrument, the venue lists it but publishes no such statistic, and we failed to collect it. Only the third is our fault, and only the first two are the venue’s limits. Where our own cached instrument table cannot account for something we demonstrably collect from a venue, that venue loses the right to say “not listed” at all and the cell reports NOT_LISTED_UNVERIFIED instead — an unverified negative is not a fact.

Funding rates are put on one clock before they are compared

A funding rate is quoted per settlement, and venues do not settle on the same schedule — so comparing two venues’ raw rates, or averaging them, compares different quantities. The usual shortcut is to assume every venue settles every eight hours. We measured instead: for each of the nine venues we pull its own funding-history endpoint per perpetual, difference consecutive settlement timestamps, and take the current clock from the newest run of agreeing gaps. That is 4,954 perpetuals over 3,115,434 settlement gaps, and the per-venue artifacts are committed alongside the code that reads them.

Three results are worth stating because each contradicts something it would have been reasonable to assume. Six of the nine venues run more than one settlement clock at the same time, so a per-venue constant is wrong for 428 of Binance’s 567 perpetuals — the interval has to be resolved per instrument. Two of the nine, Kraken and Hyperliquid, publish no interval field at all, so measurement is the only route to them. And a median over an instrument’s full history is the wrong statistic: Binance settled COTIUSDT hourly until 2026-08-31 and four-hourly from 2026-09-01, so its median of 499 settlements says 1 hour and is four times wrong today.

The 47 instruments whose current clock we could not establish are refused by name rather than defaulted to eight hours. A wrong interval does not fail loudly — it silently doubles or halves every annualised number that depends on it, which is why the refusal is preferable to the guess.

What this does not yet do. Stored history is not normalised: the derivatives table has no interval column, so the interval in force when an old row was written cannot be recovered. The daemon’s own published avg_funding also still does not use this — it remains the raw mean over Binance, Bybit and Hyperliquid annualised as if all three settled eight-hourly, and Hyperliquid settles hourly. Across 477 underlyings the difference between that figure and the same three venues on their measured clocks has a median of 5.47 annualised percentage points. Until that path is changed, GET /v1/funding/normalised is the corrected reading and avg_funding is not.

How a signal is recorded

  1. 1The daemon scores its tracked symbols on a fixed cycle (every 5 minutes) whether or not anyone is asking. This matters: an earlier version of the feed only logged a symbol when a client happened to call it, which quietly turned the record into a survey of what customers polled rather than what the engine decided.
  2. 2Rows that come back HIGH or MEDIUM are forwarded into the signal log; anything weaker is not published as a call. A 5-minute de-duplication window prevents the same decision being written twice by two paths.
  3. 3When a signal fires we log the symbol, direction, timestamp, and the entry price at that moment into signal_log. Nothing about the row is editable afterwards; outcomes are appended to a separate signal_outcomes table keyed on (signal_id, horizon), so a horizon can be written exactly once.
  4. 4The signal is then evaluated at fixed forward horizons — 4h, 12h, 24h, and 72h — against the price at each horizon. (48h and 7d are also resolved and shown on the analytics endpoints.)
  5. 5Each horizon resolves independently, so one signal contributes several data points as time passes.

A second, separate signal type — regime_flip, derived from whale-positioning reversals — is logged through the same tables and scored by the same rules. It is published as a weak-to-negative signal and is deliberately excluded from the headline win rate, because including it would dilute a number we describe as the confirmation engine's. Its record is shown in full on the calibration page rather than hidden.

Price source and how outcomes are resolved

Entry and horizon prices are taken from a consistent public market reference (Binance spot for the tracked symbol). Using one source for both ends of every measurement keeps the comparison internally consistent and avoids cherry-picking the most favorable venue.

A background worker wakes every 10 minutes, selects signals with an elapsed horizon, and fetches one current price per symbol per pass. Two rules exist to stop the resolver from flattering itself:

The visible consequence of the grace rule is that outcome counts fall as the horizon lengthens — a signal must survive an uninterrupted week of resolver uptime to get a 7d row. On the 30-day confirmation window at the time of writing, 4h has 2,027 outcome rows and 7d has 797. That attrition is not random with respect to time, so the long-horizon rows over-represent the periods when infrastructure was healthy. We would rather show you the gap than back-fill it.

What counts as a win, loss, or neutral

Outcomes are scored directionally, not by magnitude:

A "hit rate" is wins divided by resolved (non-neutral) outcomes. Symbols or windows with very few resolved signals are flagged as low-sample and should not be read as stable performance.

Two scorers exist, and they are not identical. Being precise about this matters more than being tidy:

Neither convention is "the honest one" in the abstract, but mixing them silently would be dishonest. A neutral band flatters a hit rate whenever tiny adverse moves are more common than tiny favourable ones; a strict rule punishes noise. We publish the headline number on the strict rule.

A measurement bug we shipped and then fixed. An earlier version of the outcome scorer required the move to exceed a rising per-horizon threshold (0.5% at 4h, 1.2% at 12h, 2.0% at 24h, up to 5.0% at 7d) before a correct-direction call counted as a hit. That conflated two different questions — "did it move enough" and "was the direction right" — and produced an artifact in which hit rates fell monotonically with horizon simply because the bar rose. It was replaced with the pure directional test above. Any figure you may have seen from us before that change is not comparable with the figures now.

Signals without a direction are never scored. The scorer returns "unscoreable" rather than a hit for a direction-less row. The previous behaviour counted any non-flat move as a hit, which would have scored close to 100% for such rows — price always moves — and silently inflated every aggregate the moment a caller logged one. All live callers pass a direction, so this is a guard rather than a live correction, but it is the kind of latent bug that makes other people's win rates unbelievable.

Distinct calls, not feed rows

A live confirmation call is re-emitted every few minutes for as long as it persists, so the raw feed contains many rows per decision — one call that stays live for eight hours can produce ~100 rows. All win rates and profit factors on this site are computed over distinct calls: consecutive same-symbol, same-direction confirmations are collapsed into one episode, and a gap of more than 12 hours starts a new call. This keeps the published sample small (we show the n everywhere it appears), but it prevents the rate from being weighted by how long a call happened to stay live or how often a client polled it. Feed-volume counts (signals streamed) are reported separately and are never used as a win-rate denominator.

Why this is worth a whole section. The two denominators are visible side by side on our own public endpoints, and the difference is roughly two orders of magnitude:

EndpointDenominatorWindown
/v1/statsdistinct calls,
24h horizon
trailing 30d89
/v1/signals/analytics/calibrationraw outcome rows,
4h+12h+24h
trailing 90d2,446 (HIGH)
13,703 (MEDIUM)

Both are real. Neither is wrong. But the second set is inflated by exactly the thing a vendor would want it inflated by: a call that stayed live longer, or was polled more, contributes more rows. If we quoted the 13,703-row number as our sample size, the confidence intervals would shrink by roughly an order of magnitude and every figure would look far more established than it is. So the headline stays on the small denominator, and the analytics pages carry the large one clearly labelled as row-level.

Confidence tiers — and what they currently show

Each confirmation carries a confidence of HIGH or MEDIUM, derived from how strongly the underlying context layers agree. Confidence is meant to size conviction, not to promise an outcome — a HIGH-confidence call can still lose, and we track both tiers honestly.

The tiers are ordinal labels, not probabilities. HIGH does not mean "high chance"; it means the layers were in stronger agreement. Nothing in the system converts that into a calibrated probability, which is why the calibration page plots labelled buckets rather than a predicted-versus-observed diagonal.

The uncomfortable part. On the row-level 90-day window at the time of writing, HIGH does not outperform MEDIUM — it underperforms it, and its interval straddles a coin flip:

Tiern (rows)Observed hit rate95% intervalExpectancy
HIGH2,44648.9%47.0% – 50.9%+0.209%
MEDIUM13,70352.7%51.9% – 53.5%+0.229%

On the distinct-call denominator over the trailing 30 days the two tiers are effectively indistinguishable — HIGH 60.0% on n=15 against MEDIUM 60.8% on n=74 — and n=15 is far too small to separate anything. Read together, the honest summary is: we do not currently have evidence that the HIGH label identifies better calls than the MEDIUM label. We publish the tier split anyway, and we publish it in this order, because suppressing it until it looked better is precisely the practice that makes vendor statistics worthless.

How to read a confidence interval here

Every hit rate we publish is an estimate from a finite sample, so it comes with a 95% Wilson interval — a range that accounts for the fact that a proportion near 50% on a small n is barely pinned down at all. The Wilson form is used rather than the textbook normal approximation because the latter misbehaves badly at small n and at rates near 0 or 1.

The practical rule: if the interval contains 50%, the point estimate is not evidence of directional skill. Two worked examples from the live endpoints at the time of writing:

A related trap is our own. Six horizons multiplied by several signal types and several windows is a lot of independent looks at the same data; some of them will clear a 95% bar by chance alone. An interval that excludes 50% on one cell of a table you were free to choose after seeing it is much weaker evidence than the same interval on a hypothesis fixed in advance. This is exactly why the forward holdout below is defined by a frozen date rather than by whichever slice currently looks best.

Fees and slippage are not modelled

Published accuracy is measured on raw mid/last price moves. It does not deduct trading fees, funding payments, spread, or slippage. Your realised results will be lower than the raw directional hit rate once execution costs are included — treat the figures as a measure of directional context quality, not net trading profit.

The size of that haircut is easy to underestimate. On the trailing 30-day distinct-call sample at the time of writing, expectancy is +0.223% per call at the 24h horizon, against an average win of +1.797% and an average loss of −2.269% (profit factor 1.26). Costs come out of the 0.223%, not out of the 1.797%. We deliberately do not publish an assumed cost figure, because the honest input — your venue, your fee tier, your size, the spread you actually cross, and the funding you pay while holding — is not ours to assume. Substitute your own and see what survives.

Signal vs execution caveat

A recorded signal is a market-context observation at a point in time. It is not an order, a fill, or a round-trip trade. Real execution adds entry timing, position sizing, stop placement, exits, and latency — all of which are yours to control. The numbers describe the signal, not a strategy's bottom line.

Two specific gaps are worth naming. First, the scored exit is a clock, not a decision: the outcome is stamped at the horizon regardless of what happened in between, so a call that went 6% against you before recovering is recorded identically to one that never moved against you. Nothing here reflects a stop being hit. Second, we score at a single reference price, whereas you enter at whatever the book gives you some seconds later. Both gaps push realised results the same way — down.

Forward holdout (out-of-sample)

Signals generated on or after 2026-06-30 are tracked as the forward holdout relative to the scorer version frozen on that date. The API suppresses the overall rate until at least 40 outcomes span at least 7 calendar days; HIGH also requires 30 observations. This is stronger evidence than the in-sample window, but it can still reflect a narrow regime and does not model execution.

What "frozen" means concretely. The freeze date is a constant in the code (SCORER_FROZEN_TS), not a setting on a dashboard. Everything before it was produced by a scorer whose thresholds had been chosen with that period visible; everything after it is scored by rules that were fixed before the data existed. The holdout leads with the 24-hour horizon only — the mature outcome — and never blends in or falls back to a shorter horizon that happens to look better.

Why the span gate exists. A minimum sample size alone is not enough: forty outcomes accumulated over two days are forty views of one market move, not forty independent tests. The 7-day calendar-span requirement was added after a database rotation reset the tables and left a two-day window that would otherwise have been publishable. Below the gate the endpoint returns "accruing" and the site shows the word, not a number.

Where it stands. At the time of writing the holdout is past both gates and reports 58.1% on n=215 distinct calls at 24h, with HIGH at 56.1% on n=41, accruing since 2026-06-30. The endpoint also carries an is_distinct_from_insample flag so you can tell whether the holdout population still overlaps the in-sample one; it currently reports true. What this licenses is narrow: it says the published rule, fixed in advance, has stayed above a coin flip on this venue set over this stretch of market. It does not say the edge is durable, that it survives costs, or that the next regime will look like this one.

Why a good backtest can still be worthless

These are the failure modes we treat as live risks in our own numbers, not as textbook trivia. Each one has bitten this project.

Studies we ran that found nothing

Publishing only successful research is how an industry ends up with a literature of edges that don't exist. Our null results are written up the same way as everything else.

The conclusion we draw from our own research is deliberately unflattering: we do not claim a durable, cost-surviving directional edge. What the product sells is breadth and speed of real-time aggregation across venues, chains and wallets, plus a record you are able to audit. If that is not worth money to you, the honest answer is that you should not buy it.

Audit it yourself

Every figure quoted above is served by a public endpoint that needs no API key. The analytics views are pure functions over the same two tables that produce the published win rate, so a chart and the headline number are reconcilable by construction rather than by trust.

Six further endpoints exist only so the claims on this page can be checked by someone who does not trust us. They need no API key, for the same reason: a verification surface behind a paywall verifies nothing to the person deciding whether to pay. Each one carries a not_claimed block naming what it does not establish.

Payloads carry their own honesty fields: a label stating "in-sample, pre-fee, live forward-generated signals", a basis string naming the denominator, a generated_at timestamp, and null in place of any rate below its minimum sample. A suppressed rate returns null, never a placeholder value.

The specific figures quoted in this page's prose were read from those endpoints on 2026-08-26 and are frozen here so this text can be checked against what the API said that day. The live blocks on the calibration and performance pages move as outcomes resolve, so they will differ — that difference is expected, and comparing the two is a legitimate way to audit us.

See the live record

Review the tracked performance, read the docs, or learn more about what we're building.

Not financial advice. Historical accuracy does not guarantee future results. Crypto trading involves risk.