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:
- Signal — a single recorded row: symbol, direction (
longorshort), UTC timestamp, the reference price at that instant, and a metadata blob holding the confidence tier. It is an observation about market context, not an order. - Call (episode) — one decision. Because a live confirmation is re-emitted while it persists, many signal rows can belong to the same call. Consecutive same-symbol, same-direction rows are collapsed into one call; a gap of more than 12 hours starts a new one (
_EPISODE_GAP_S = 12h). - Horizon — a fixed forward holding period at which a signal is scored:
4h,12h,24h,48h,72h,7d. Horizons resolve independently, so one signal can produce up to six outcome rows. - Outcome — a resolved row: the price observed at the horizon, the percentage move from entry, and a binary hit flag.
- Hit rate / win rate — hits divided by resolved outcomes, on whichever denominator the block names. The two denominators used on this site are distinct calls and raw outcome rows, and they give very different numbers. That is explained below, with the live figures, because it is the single easiest place to mislead someone.
- Expectancy — the mean direction-adjusted move per observation, in percent. A short that fell 1% contributes +1%. It is a pre-fee price move, not a return on capital, because no position size is implied.
- Profit factor (PF) — gross sum of winning moves divided by the absolute gross sum of losing moves. PF 1.00 is break-even before costs; below 1.00 the losers are larger in aggregate than the winners.
- In-sample — measured over a window in which the scoring thresholds were already chosen. Descriptive only. Out-of-sample — measured only on signals generated after the scorer was frozen, with no further tuning.
- Basis point (bp) — one hundredth of a percent. Used in the research reports where the effects being argued about are smaller than a typical trading cost.
Where the inputs come from
A confirmation is assembled from context layers that are collected independently and on different clocks:
- Derivatives — funding rates and open interest collected from nine venues (Binance USDₚ-M, Bybit, OKX, Bitget, KuCoin, MEXC, BitMEX, Kraken Futures and Hyperliquid), plus long/short account ratios. Those statistics do not share a venue list, and an earlier version of this sentence implied they did. Long/short ratio is published by four of the nine: measured over the 24 hours to 2026-09-02, the median symbol carries it from two venues, 423 of 519 symbols carry it from exactly Binance and Bybit, and 40 symbols have none at all — including every symbol on Hyperliquid, which was one of the three venues that sentence named. The confirmation aggregate that feeds a
HIGH/MEDIUMlabel is separately frozen to Bybit, Binance and Hyperliquid; the wider venue set is what the public screener reads. Per-symbol, per-venue, per-field coverage is served atGET /v1/coverage. - Whale positioning — perpetual positions of large Hyperliquid accounts discovered from the multi-timeframe leaderboard, plus multi-chain wallet flow. The public stats endpoint reports 956 tracked whale wallets at the time of writing.
- On-chain — free community metrics. If a component's inputs are missing, that component is dropped and the remaining weights must still clear a minimum weight sum (
MIN_WEIGHT_SUM = 0.35) before a confidence is emitted — one surviving weak component is not allowed to masquerade as a full-strength reading. - Price — Binance spot, used for both the entry stamp and every horizon stamp.
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
- 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.
- 2Rows that come back
HIGHorMEDIUMare 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. - 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 separatesignal_outcomestable keyed on(signal_id, horizon), so a horizon can be written exactly once. - 4The signal is then evaluated at fixed forward horizons —
4h,12h,24h, and72h— against the price at each horizon. (48hand7dare also resolved and shown on the analytics endpoints.) - 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:
- A 30-minute grace window. A horizon is only resolvable from the moment it elapses until 30 minutes past its target. The current price is a snapshot of now — if the worker were down for a day, stamping a 4-hour outcome with a 24-hour-old signal's current price would be a fabricated observation. Past the window the horizon is skipped permanently and left unresolved rather than filled in.
- Skipped horizons still close the signal. Once every horizon is either resolved or permanently skipped, the signal is marked done. It is not left in a queue to be re-scored later at a more convenient price.
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:
- Win — price moved in the signalled direction at that horizon (up for a long, down for a short).
- Loss — price moved against the signalled direction.
- Neutral — in the per-symbol accuracy log, price was effectively flat within a small threshold (0.2%), so the horizon is treated as inconclusive rather than forced into win or loss. The stricter scorer behind the headline number has no neutral band at all; both conventions are spelled out immediately below, because quietly mixing them would be the easiest way on this page to flatter a hit rate.
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:
- The signal outcome scorer behind
/v1/statsand the calibration and edge endpoints is strictly directional and has no neutral band: any move above zero in the signalled direction is a win, any move below it is a loss, and an exactly-flat print is scored as a loss for a long. There is no dead zone in which an outcome is discarded. - The accuracy log behind the per-symbol tables on the performance page does use a neutral band: a move smaller than 0.2% in either direction is recorded as
neutraland excluded from the denominator, so accuracy there iscorrect / (correct + incorrect).
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:
| Endpoint | Denominator | Window | n |
|---|---|---|---|
/v1/stats | distinct calls, 24h horizon | trailing 30d | 89 |
/v1/signals/analytics/calibration | raw outcome rows, 4h+12h+24h | trailing 90d | 2,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:
| Tier | n (rows) | Observed hit rate | 95% interval | Expectancy |
|---|---|---|---|---|
| HIGH | 2,446 | 48.9% | 47.0% – 50.9% | +0.209% |
| MEDIUM | 13,703 | 52.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:
- HIGH-tier rows, 90 days: 48.9% on n=2,446, interval 47.0%–50.9%. The interval crosses 50%, so this is a coin flip as far as the data can tell — despite a sample of nearly two and a half thousand rows.
- Confirmation calls at the 48h horizon, 30 days: 33.7% on n=1,197, interval 31.1%–36.4%. That interval sits entirely below 50%, so it is not noise: at that horizon, on that window, the direction was wrong more often than right.
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.
- In-sample fit. Thresholds tuned on a window will describe that window. Anything labelled in-sample on this site is a description of the past, and we say so on the label rather than in a footnote.
- Denominator inflation. Counting re-emissions as independent observations multiplies n by roughly forty and shrinks every interval accordingly. Our own live-feed rows and our published sample differ by about two orders of magnitude for exactly this reason.
- Look-ahead. Any scoring rule that peeks past the timestamp — including stamping an outcome with a price observed long after the horizon — manufactures accuracy. That is why a horizon resolved more than 30 minutes late is discarded rather than filled.
- Survivorship and coverage drift. Long horizons only exist for signals that survived a week of uninterrupted resolver uptime, and the tracked symbol set changes as wallets and venues are discovered. Both quietly select which periods and which assets are represented.
- Direction concentration. If nearly all calls point the same way, the hit rate mostly measures the tape, not the model. On the current 30-day confirmation sample the split is 89.2% long against 10.8% short, and the two sides behave differently: 47.8% on n=4,831 long rows versus 57.4% on n=582 short rows. A skill-free reference — how a constant-direction position would have done over the same window — is what disambiguates this, and our endpoint currently reports that baseline as not computable and says so in the payload rather than omitting the caveat.
- Multiple comparisons. Six horizons, several signal types, several windows, several tiers. Some cell of that grid will look significant by chance. Choosing the cell after looking is how most published edges are made.
- Regime dependence. A crypto sample of a few months is often one market condition wearing several dates. Our own cross-sectional research found the only cross-sectional information in the panel was short-term price reversal, which inverts every few weeks — a strategy fitted to it looks excellent right up until it doesn't.
- Costs applied to the wrong number. A 0.2% per-call expectancy and a 1.8% average winner are not the same base. Subtracting a cost from the winner rather than the expectancy makes a losing system look profitable.
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.
- Cross-sectional ranking book (pre-registered, NO-SHIP). A long/short ranking strategy over the signal panel was specified in advance with a written advance-to-holdout bar: mean net above zero, win rate at least 55%, profit factor at least 1.30, and both legs positive. On the exploration window it cleared the gate marginally, at +3.1bp per period — already smaller than one turnover's worth of slippage variance. On the single one-shot confirmation window it inverted: win rate 0.391, profit factor 0.33, −67.6bp per period on n=46, with both legs negative and a full-window permutation test at p=0.60. A variant with an extra dispersion gate did no better (0.370 / 0.31 / −53.9bp). Per the pre-registration's honest-failure clause it was killed without ever touching the reserved holdout, which therefore remains unused.
- Leakage-free walk-forward on the raw features. Derivatives, whale, and combined feature sets showed no out-of-sample directional edge. The higher rates visible on in-sample outcome tables come from a single short window and one market regime, and are labelled in-sample wherever they appear.
- Copy-trading v3. Five out-of-sample tests, all failed; the pre-set bar of profit factor at least 1.6 and win rate at least 60% was not reachable. Not shipped.
- Congressional-disclosure and centralised-exchange flow studies. Both were measured before being built, both came back no-go as signals, and both ship as data and transparency features rather than as predictions.
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.
GET /v1/stats— headline distinct-call win rate, profit factor, expectancy, and theforward_holdoutblock with its status, n, and freeze timestamp.GET /v1/signals/analytics/calibration— row-level hit rate per confidence bucket with Wilson intervals, window length, and minimum-sample rule.GET /v1/signals/analytics/edge?type=smart_money_confirm— per-horizon n, win rate, interval, expectancy, profit factor, direction split, and the drift-baseline block (including itsbaseline_notewhen the baseline cannot be computed).GET /v1/signals/recentandGET /v1/signals/{id}/outcome— individual calls and how each one resolved.
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.
GET /v1/funding/clock— per venue: instruments measured, settlement gaps observed, the histogram of settlement intervals actually in use, the artifact filename each interval came from, how old that measurement is, and the age at which it starts refusing.GET /v1/funding/normalised?symbol=BTC— one underlying’s live funding on every venue that carries it, each rate annualised by its own measured interval, with the interval and the number of settlement gaps behind it attached. Every venue we hold a rate for appears in exactly one ofvenues_contributing,venues_refusedorvenues_unmapped, so none can silently drop out of the average. The payload also carries the same rates read the naive way, so the size of the correction is checkable from the response alone without trusting our arithmetic.GET /v1/coverageandGET /v1/coverage?symbol=BTC— the per-symbol, per-venue, per-field coverage matrix described above, including the measured collection cadence the staleness threshold is derived from.GET /v1/options/iv-surface?book=BTCandGET /v1/options/iv-surface/books— the Deribit implied-volatility surface, published sparse: the BTC book had 490 quoted strike-by-expiry cells inside a 94×12 rectangle, and the other 638 are reported as holes rather than interpolated into existence. Every leg carries aquote_state, because a venue mark on a leg with no bid and no ask is the venue’s model output, not a market observation.GET /v1/derivatives/provenance— which rows we watched and which we replayed. A backfilled row is labelledbackfilledat the database level by a constraint that makesobservedunstorable in that table, so no later writer can launder a replay into an observation. The observed side reports the earliest row in the current shard, which is a floor on when collection started, not the date it started.
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.