How Alpha scoring works
§1
Principles
Alpha is a screen, not a recommendation. Each tab answers one narrow question with a weighted composite of disclosed, objective signals, and every card shows its full component breakdown so you can see why it ranked where it did. Thresholds exist to filter noise (dust volume, near-zero caps, spam duplicates), not to imply quality.
The underlying question every screen is really asking is “is this worth a closer look, or is it more likely to be a dead-cat bounce, a rug in progress, or an abandoned shell?”, never “will this go up.” No formula can answer that; markets this young and this thin are dominated by noise. What the signals below can do is separate real, broadly-held, currently-active trading from common look-alikes: a finished pump-and-dump (impressive totals, no present: caught by last-hour volume and momentum) and a wash-traded mint (volume with no distinct buyers: caught by holders paid and on-chain distribution).
§2
Shared math
logScore(value, floor, ceiling)
Maps a raw number onto 0–100 on a logarithmic scale: floor → 0, ceiling → 100, clamped beyond either end. Log scaling means going from $1k to $10k of volume is worth as much as $10k to $100k, the right shape for quantities that span orders of magnitude.
Weighted average
Every component is 0–100; the score is the weight-normalised average, so adding an optional component (like “Graduation pace” for bonding-curve tokens) never inflates or deflates a score by itself.
momentumScore(recentRate, olderRate)
Compares one window's per-hour volume with the token's own earlier per-hour rate over that same window. 50 means steady; above 50 is accelerating; below is fading. Symmetric on a log scale, clamped to a 4× band either way, so 4× faster and 4× slower sit the same distance from centre. If there is no earlier volume at all, any recent volume scores 100.
computeMomentum(activity, hours): the blend
Every screen that scores momentum runs this same function on two independent windows: the hourly trend (the last hour vs. the rest of the lookback the token has actually existed for) and the 15-minute pulse (the last completed 15-minute candle vs. the one before it). The hourly window needs at least 15 minutes of “before” to compare against (so effectively age ≳ 1h15m); the 15-minute window only needs the token to be at least 30 minutes old, independent of the hourly one. When both exist, the score used for ranking is the average weighted 2:1 toward the hourly read, steadier, but slower to notice a turn than the immediate pulse. When only one exists, that one is used directly. When neither does (a token under 30 minutes old), there is no Momentum component at all: not a guessed 50.
The reason text is built from the same two windows, not the blended number: when they agree, the sentence quotes whichever window's ratio is more extreme (“Accelerating: 3.2x its earlier pace”); when they disagree (the hourly trend still looks fine but the last 15 minutes have gone quiet, or the reverse), that disagreement is treated as the more decision-relevant fact and leads instead (“Cooling in just the last 15 minutes, after a strong hour overall”). A token that's steady on every timeframe gets no momentum line at all: nothing worth saying isn't forced into a sentence.
distributionScore(top10Pct): on-chain concentration
This reads live from chain (not from StonkFun's snapshot) via the same holder scan the token page's Holders panel uses. It is piecewise-linear through the exact thresholds that panel colours amber and red at, so the score and holder view cannot disagree about what counts as concentrated. Higher means the top 10 real wallets hold less supply.
An unreadable scan (RPC unavailable, refused, or simply hasn't run for that mint yet) scores 50, a neutral middle, never a penalty. A token this screen hasn't checked at all (the first, free-signals pass) gets no Distribution component whatsoever, the same “didn't check” vs. “checked, unknown” distinction used everywhere else on this page.
freeFloatScore(pct): Potential CTO only
A direct 1:1 mapping (0% → 0, 100% → 100), not a risk-anchored curve like the two above: for a takeover candidate, a bigger float is unambiguously the more promising situation, with no “too spread out” downside the way heavy concentration is a downside elsewhere. pct is freeFloatPct: 100 minus whatever share sits in a program-owned account (an unsold curve reserve or pool). Same neutral-default rule: unknown scores 50.
§3
New launches gaining traction
Question: among tokens created in the last 6 / 24 hours, which ones are actually being traded right now, by real people, and still holding what they built?
Candidate pool
The newest-sorted StonkFun list, 10 pages for 6h, 25 pages for 24h (100 tokens per page). StonkFun launches ~100 tokens per 50 minutes, so the pool has to scale with the window or every setting would see the same few dozen mints. Duplicate name+symbol launches (spam clusters) collapse to their best-scoring instance.
Hard exclusions
- 24h volume under $5,000 or market cap under $15,000: a mint nobody but the deployer has touched.
- After the chart check: no trade in the last 120 minutes, or under $250 of volume in the last hour. The second rule is what removes a launch that did $150k early and $40 since.
- More than 75% below its own peak market cap. A dump prints a lot of volume; that volume is not traction, and a weight can't outvote it: only a gate can.
Components (total weight 26)
| Component | Weight | Share | Signal | 0 → 100 scaling |
|---|---|---|---|---|
| Last hour volumesecond pass only | 2 | 8% | Dollar volume in the trailing 60 minutes (Jupiter candles) | logScore(v, 250, 50,000) |
| Holders paid | 4 | 15% | Distinct wallets paid a reward so far (reward ledger) | logScore(n, 5, 500); 35 if no ledger row yet |
| Distributionsecond pass only | 4 | 15% | Top-10 real-wallet share of supply, read live from chain | distributionScore(pct), see below; 50 if the scan hasn't run yet |
| Market cap | 1 | 4% | Current market cap | logScore(mc, 5,000, 5,000,000) |
| Holding its peakwhen a peak is known | 4 | 15% | Current market cap ÷ peak market cap | round(ratio × 100), clamped 0–100 |
| Still tradingsecond pass only | 4 | 15% | Minutes since the last candle with volume | 100 × (1 − minutes / 120) |
| Momentumsecond pass only | 2 | 8% | Blend of the hourly trend and the 15-minute pulse; see below | computeMomentum(...).value |
| 24h volume | 1 | 4% | StonkFun trailing-24h dollar volume | logScore(v, 1,000, 500,000) |
| Graduation pacebonding-curve tokens only | 2 | 8% | Bonding-curve progress ÷ age in hours | logScore(pace, 0.02, 1) |
| Turnover | 2 | 8% | 24h volume ÷ market cap | logScore(x, 0.05, 15) |
Table 1: New Launches component weights
Why these weights: last-hour volume, momentum, and holders paid are weighted heaviest: real trading happening right now, by distinct people, is what “gaining traction” actually means, and none of the three can be inflated by a mint that's already finished its run. Distribution sits right behind them: a low-turnout, highly concentrated mint is a common way traction turns out to be fake. Market cap and 24h volume are deliberately light: both reward cumulative size, which a token can only have once it's already grown; weighting them heavily would make this screen mostly find launches after the run, not during it. Turnover (24h volume ÷ market cap) carries more weight than either, since it's scale-invariant: a brand-new launch can score well on it without needing to already be large.
§4
Overlooked, seeing renewed interest
Question: which tokens has the market priced as dead (far below their own peak) that are nonetheless trading live today, and picking up rather than fading?
Candidate pool
The top 300 projects by 24h volume, minus the top 25. The busiest markets on the platform are the opposite of overlooked, whatever their drawdown: a coin at #4 by volume was showing up here before this cut.
Hard exclusions
- Younger than 7 days: a same-day launch shows a 90% “drawdown” from ordinary first-day volatility.
- Less than 50% below its peak market cap, or under $500 of 24h volume.
- After the chart check: no trade in the last 120 minutes.
- After the chart check: momentum under 50, the last hour slower than the token's own earlier pace. “Picking up, not fading” is the screen's question, so fading is an exclusion, not a low score.
Components (total weight 11)
| Component | Weight | Share | Signal | 0 → 100 scaling |
|---|---|---|---|---|
| Still tradingsecond pass only | 3 | 27% | Minutes since the last candle with volume | 100 × (1 − minutes / 120) |
| Momentumsecond pass only; also the momentum gate | 3 | 27% | Blend of the hourly trend (last hour vs. the previous 11) and the 15-minute pulse; see Shared math | computeMomentum(...).value |
| Below peak | 2 | 18% | Drawdown from all-time-peak market cap | 100 × (drawdown − 0.5) / (1 − 0.5) |
| Current turnover | 2 | 18% | 24h volume ÷ market cap | logScore(x, 0.05, 10) |
| Track record | 1 | 9% | Lifetime reward payout count (reward ledger) | logScore(n, 10, 20,000); 0 if no ledger row |
Table 2: Overlooked component weights
Deliberately no market-cap-size component: a $9k comeback with real accelerating volume is exactly what this screen exists to surface and must not be outranked by a $500k one for being larger. A unit test enforces this. Also deliberately no on-chain distribution component, unlike New Launches: these candidates are already established (≥7 days old, traded at real scale), so a live holder scan is both less informative and (for a pool of hundreds of sometimes large-holder-count tokens) meaningfully more expensive to run than on a pool of brand-new, small-holder-count mints.
§5
Potential CTO (community takeover)
Question: which quiet tokens (nobody clearly running them) hold something a new team could build on?
Candidate pool
The union of: the first-ever launch against each non-custom quote asset (~90 stocks, indices, and named assets; the identity is cached for 6 hours, market data is live), name-search results for stock-like pairs, and the top 500 by market cap.
Hard exclusions
- Younger than 7 days, or market cap under $2,000 (nothing left to take over).
- More than $2,500 of 24h volume: someone is running it.
- Anything the Overlooked scorer would accept (mutual exclusion).
- No hook: must be the first pair, or have a name claim of 55+, or a plausible peak of at least $50,000.
Name claim
Both the token's name and symbol are normalised and compared against the quote asset's symbol, its symbol without a trailing wrapped-asset “X” (TSLAX → TSLA), and its name. Exact symbol match scores 100, exact name 90, the key as a whole word inside the name 55. Substrings inside other words (“Teslasaurus”) score 0.
Circulating supply
A hook and a quiet market only say a takeover is possible. Free float adds the on-chain share already outside unsold curve reserves and program-owned accounts (freeFloatScore, computed from freeFloatPct = 100 − programOwnedPct). The card also reports the real on-chain holder count when a full scan was possible, rather than relying only on the reward ledger's lifetime payout count.
Components (total weight 18)
| Component | Weight | Share | Signal | 0 → 100 scaling |
|---|---|---|---|---|
| First pair | 4 | 22% | Is this the first token ever launched against its quote asset? | 100 or 0 |
| Name claim | 3 | 17% | Does the token's own name/symbol claim the paired stock? | 100 exact symbol · 90 exact name · 55 whole word · 0 |
| Peak interest | 3 | 17% | Plausible peak market cap | logScore(peak, 20,000, 5,000,000) |
| Holder base | 2 | 11% | Distinct wallets ever paid a reward (reward ledger, lifetime) | logScore(n, 10, 2,000); 25 if unknown |
| Free floatsecond pass only | 2 | 11% | Share of supply outside program-owned accounts, read live from chain | freeFloatScore(pct), see Shared math; 50 if unknown |
| Graduated | 1 | 6% | Trades on a full AMM market rather than a bonding curve | 100 or 0 |
| Below peak | 1 | 6% | Drawdown from peak | 100 × min(1, drawdown / 0.95) |
| Survived | 1 | 6% | Age in days | logScore(days, 7, 180) |
| Still tradablesecond pass only | 1 | 6% | Any volume at all in the last 12h | logScore(v, 10, 5,000) |
Table 3: Potential CTO component weights
“Unique concept” and lore can't be read from market data, and this screen doesn't pretend to: every input is a number or a verifiable fact. One scope line still holds from Overlooked: the top-10-real-wallet concentration score (“Distribution” on New Launches) is not read here: for a market nobody is currently running, whether the existing holders happen to cluster is less useful here than how much float exists outside program-owned reserves.
§6
DEGEN MODE
Question: among launches under an hour old, which ones show no confirmed rug mechanism? This is deliberately not the same question the other three screens ask. For most of this screen's population, none of their proven signals exist yet: New Launches' own volume and market-cap gates ($5,000 / $15,000) are essentially unclearable in the first few minutes, and computeMomentum (see “Shared math”) returns nothing at all under roughly 30 minutes of age — and this screen never leans on momentum even for the older end of its window, close to the one-hour mark, where it could technically be computed. Rather than guess at “potential” from data that mostly doesn't exist yet, this screen is elimination-first: drop anything showing a real, permanent rug mechanism, then score what survives on how clean its remaining on-chain signals are.
Candidate pool
Every StonkFun launch created within the last 60 minutes: the newest-sorted list is fetched fresh every 30 seconds, so a launch enters and leaves this screen entirely based on age, not on how it's performing.
Hard exclusions: only on confirmed-true data
- Top 10 wallets hold 60% or more of supply, confirmed, but only once at least 15 holders exist. Below that, “top 10 hold X%” is an artifact of being new (top 10 of 8 holders is ~100% by construction), not a real concentration signal, so the gate, and the score component below, simply don't apply yet.
- Mint or freeze authority confirmed still active: verified on-chain per token, kept as a defensive check, but see the callout below for why this should never actually be what excludes a real StonkFun launch.
true. A field the RPC hasn't answered yet (common in the first minute or two of a token's life) leaves that gate un-tripped and its score component at a neutral default: never treated as a pass or a fail it can't actually support.The whale signal
StonkScout already maintains a registry of wallets holding real size (10M+ tokens) in the platform's 30 largest tokens: see How holder data works. DEGEN MODE cross-references a brand-new launch's current top holders against that same registry: no new infrastructure, just the holder scan this screen already runs for the concentration gate, checked against data already being fetched for every token page. This is deliberately weighted alongside the safety gates rather than left as a footnote: unlike raw volume or holder count, it's genuinely hard to fake: reproducing it requires an existing wallet with a real, costly prior position in an established token, not a few dollars of self-directed buying.
Components (total weight 80)
Every candidate that remains in Degen Mode receives a Degen Mode Scout Score, including candidates whose newest on-chain checks are still unavailable. Unread signals use the documented neutral value and lower the separate data-confidence label. Top New, Overlooked, and Potential CTO each calculate their own Scout Score from their own components and gates; scores only compare candidates within the same mode.
| Component | Weight | Share | Signal | 0 → 100 scaling |
|---|---|---|---|---|
| Known whale bought in | 30 | 38% | Is any current top holder a known whale of an established platform token? | 100 found · 40 checked, none found · 50 couldn't check |
| Curve pacepre-graduation only | 20 | 25% | Bonding-curve progress ÷ age in minutes: real buy pressure so far | logScore(pace, 0.002, 0.15) |
| Distinct holders | 15 | 19% | How many separate wallets already hold it, read live from chain | logScore(n, 2, 100); 50 if unknown |
| Holder concentrationonly once 15+ holders exist | 15 | 19% | Top-10 real-wallet share of supply, read live from chain | linear, 20%→100, 60%→0 |
Table 4: DEGEN MODE component weights
Feeds the same learning loop as every other screen (see “Continuous learning”, below): once enough sightings resolve, each component's weight adjusts based on what actually happened to tokens that showed it.
§7
The two-pass pipeline
Real-time recency and momentum need one Jupiter chart fetch per token (48 fifteen-minute candles, a 12-hour lookback); on New Launches and Potential CTO, the on-chain components need one holder scan per token on top of that (Overlooked is the one screen that reads neither; see its own section for why). Both are cheap individually but not for a pool that can run to thousands, so every tab runs the same shape:
- Score every candidate on the free signals only (no chart data, no holder scan) and rank.
- Take the top 40, fetch their recent-activity candles and (where applicable) their holder scans in parallel.
- Re-score those with the real-time and on-chain components added, drop anything that turned out stale, rank again.
- Show the top 12.
Only the active tab's pipeline runs per request; the other tabs cost nothing until you open them.
§8
Continuous learning
Every sighting recorded above (see “Data sources & caveats”) already carries the score's full component breakdown. Once 24 hours have genuinely passed, StonkScout checks the mint's real market cap against a Jupiter candle from that exact moment and records the two together: the sighted value plus the real outcome. That pairing, across every sighting a component has accumulated, is what lets the algorithm ask a question no single launch can answer on its own: did this factor's value actually correlate with what happened next?
The full live numbers (every component's current correlation, sample size, and adjustment, for every live screen, plus retired Graduating's historical-only data) are on /alpha/learning, not just described here.
- 1
Record
Save a sighting and its component values.
- 2
Measure
After 24 hours, pair it with the observed outcome.
- 3
Adjust
Require 20 pairs, then move 25% toward the bounded target.
Spearman rank correlation
Memecoin outcomes are wildly right-skewed (a handful of 30x sightings alongside hundreds sitting near 1x), so a raw value-based correlation would be dominated by a few outliers. A rank correlation asks only whether higher values tend to rank with better outcomes, not by how much, which is the right question for data shaped like this, written ρ, the conventional symbol for a Spearman coefficient:
Three guardrails
lib/alpha/scoring.ts, which stay authoritative.When this runs
There is no background worker or cron job: this app doesn't have one (see “Known limitations” in the README). Real traffic drives the loop instead: opening any Alpha screen checks whether it's been at least 3 hours since the last cycle, and if so, measures a bounded batch of newly-matured sightings and recomputes adjustments before that request's own scores are shown, gated so it never meaningfully slows a page load on the requests in between, and resilient so a failed cycle (an upstream hiccup, say) just tries again once the gate reopens rather than breaking the screen it was triggered from.
§9
Data sources & caveats
- StonkFun public API: market snapshot (price, market cap, peak, 24h volume, 24h change), the platform reward ledger (payout counts, holders paid), pairs, and launch timestamps. Its only server-side sorts are market cap, volume, and newest, which is why candidate pools are bounded rather than exhaustive.
- Jupiter chart data (datapi.jup.ag): per-candle volume for recency and momentum. It aggregates every venue a token trades on and runs well above StonkFun's own 24h figure, so last-hour dollar amounts are comparable between launches, not with StonkFun's totals. It is an unofficial endpoint and could change without notice.
- Holders paid only exists for reward-mode tokens that have completed at least one distribution. Standard launches and pre-first-payout tokens get the neutral default, not zero.
- On-chain holder scan (Distribution on New Launches; Free float on Potential CTO; Holder concentration and Known whale bought in on DEGEN MODE): the same live `getProgramAccounts` read used by the token page's Holders panel, not part of the StonkFun snapshot. Not run on Overlooked (see its section for why). Cached 2 minutes per mint, so a token shown on more than one screen, or reloaded within the window, costs nothing extra. A refused or failed scan scores neutral, never a penalty; see “Shared math” above.
- Mint/freeze authority (DEGEN MODE only): read from the same on-chain mint account StonkScout already fetches for a token's decimals, cached 24 hours since it's immutable once renounced. A defensive gate only, not a scored or displayed signal; see “Why mint/freeze authority isn't scored” above.
- Scores are recomputed on every request from live data; the same token can move between screens minute to minute as its activity changes. That's the intended behaviour.
Source: src/lib/alpha/scoring.ts and its unit tests, which pin the regression cases each rule was added for.