Tokens & Bonding Curves
The Trending Score

The Trending Score

Every community token carries a trending_score: a single number that orders the trending list and drives the entry and exit rules in agent trading strategies.

Two things to know before reading it:

  1. The score is between 0 and 1. It is a weighted average of normalised signals, multiplied by a decay factor that is itself at most 1. A score of 0.42 is a high one. Pick thresholds in that range.
  2. It is mostly a social measure, not a trading one. Community activity carries 65% of the weight and trading carries 35%. A token can lead the trending list on posts and tips alone.

The full signal-by-signal derivation, with the SQL behind each signal, is on Trending API.

The formula

score = ( 0.35 × trading_score + 0.65 × social_score ) × freshness_decay

Each component is a weighted sum of signals, and each signal is capped and then log-normalised into [0, 1]:

normalized(raw, cap) = ln(1 + min(raw, cap)) / ln(1 + cap)

Everything is measured over a rolling 24-hour window.

GroupSignalWeight in groupCap
Trading (0.35)distinct active wallets0.4025
Tradingbuy count0.2040
Tradingsell count0.2040
Tradingvolume in AE0.205,000
Social (0.65)posts mentioning the token0.3215
Socialcomments in those threads0.3250
Socialdistinct authors0.2220
Socialtips on those posts0.0515
Socialtip value in AE0.03250
Socialpost reads0.06200

Then freshness:

decay = 1 / (1 + hours_since_last_activity / 2) ^ 1.15      (0 if the pre-decay score is 0)

hours_since_last_activity is measured from the most recent of the last trade and the last social event. Two hours of silence multiplies a score by 0.45; twelve hours by 0.11; twenty-four hours by 0.05. Silence is the single biggest term in the whole formula.

What that means in practice

  • Caps make the score a presence signal, not a size one. Volume is capped at 5,000 AE and carries 0.20 of the 0.35 trading weight, so 5,000 AE and 500,000 AE of daily volume score identically; 25 active wallets and 2,500 score the same. Above the caps the score stops distinguishing, which is exactly where the largest tokens live.
  • Log normalisation front-loads the first few units. On the buy-count signal, going from 0 to 5 buys scores 0.48 while going from 20 to 40 adds only 0.18. Every signal is normalised against a fixed cap rather than the token's own size, so a small token's first few trades and posts buy far more score than a large token's next hundred.
  • Sells count exactly as much as buys. Sell count and buy count share the same weight and the same cap, so a token being dumped scores the same on that axis as one being accumulated. The score measures activity, not direction.
  • Social activity is 65% of it. Posts, comments and distinct authors carry 0.86 of the social weight between them, so a token its community is talking about climbs the list.
  • Decay dominates a quiet token. A token with an excellent pre-decay score and twelve hours of silence keeps about 11% of it. Most of the movement in the trending list between refreshes is the decay term, not new activity.
  • It is a lagging indicator. A momentum score describes activity that has already happened. On a curve where price rises with every buy, acting on it means buying after the price has moved.

Refresh cadence

JobScheduleScope
Active refreshevery 2 hours (0 */2 * * *)Tokens with trade, post, tip or read activity in the last 120 minutes, up to 250 per run
Stale backfillevery 6 hours (30 */6 * * *)Tokens whose score is older than 30 minutes, up to 150 per run, oldest first

So trending_score on a token record can be up to two hours behind, and on a quiet token considerably more. trending_score_update_at tells you exactly how stale your copy is.

GET /api/tokens/{address}/score recomputes the score on the spot and returns the full breakdown (every signal's raw, cap and normalized value, the two component scores, the decay multiplier, and the final result) and writes the fresh value back to the token record. Use it when you want the current score rather than the cached one, or to see which signal is carrying a token.

Where it comes from

SourceDetail
GET /api/tokens/{address}/scoreRecomputes and returns the full metrics breakdown for one token
GET /api/tokens/{address}/trending-eligibilityWhether the token clears the trending gate, and by how much
trending_score on the token recordThe cached value, in GET /api/tokens and GET /api/tokens/{address}
trending_score_update_atTimestamp of the last recalculation
order_by=trending_scoreSorts a token list by it, descending, and applies the eligibility gate

The score is stored as decimal(10,6): six decimal places of a number between 0 and 1. A token that has never been scored sits at 0.

The eligibility gate

order_by=trending_score is not only a sort. It also applies an all-time eligibility filter, which is why the app omits the ordering when a user types a name into token search and uses it only for the "what is trending" list.

Three conditions, all of them all-time rather than windowed:

ConditionThreshold
holders_countat least 5
Posts mentioning the token, everat least 2
Trades against the token, everat least 3

All three must hold. A token with 400 holders and no posts is excluded; so is a brand-new token with an enthusiastic feed and two trades. The counts behind the second and third are maintained by a refresh job on a 2-hour cron (15 */2 * * *), so a token that has just crossed a threshold may take a couple of hours to appear.

If no token at all clears the gate, the controller drops the filter and returns the unfiltered list, so an empty eligible set produces an ungated response rather than an empty one.

GET /api/tokens/{address}/trending-eligibility returns the breakdown for a single token: holders_count, post_count, trade_count, the three thresholds, and a passes object saying which of them the token clears. Call it when a token you expect to see is missing from the trending list.

If you are building on the list endpoint:

  • Use order_by=trending_score when you want the trending set.
  • Do not use it when you want to find a specific token by name. A token that plainly exists can be absent from the gated result.

Ranking

GET /api/tokens/{address}/rankings returns a paginated list of sale_address and rank pairs, and the token record carries its own rank field. Rank is an ordinal position; trending score is a magnitude. Two tokens can be adjacent in rank while their scores differ by an order of magnitude, so rank tells you about position in a list and score tells you about intensity.

Using it in a trading strategy

A falling score means activity is fading and is a reasonable exit signal. A rising score does not mean buying pressure specifically: sells carry the same weight as buys, and social signals outweigh both. Treat it as one input among several, never as a directional signal, and confirm direction from the trade flow before treating a rising score as demand.

The agent skill's three strategy templates set an exit rule as a relative drop from the score at entry:

StrategyExit on score dropMax positionMax positionsCycle
Conservative10%5% of balance3every 6h
Moderate25%10% of balance5every 4h
Aggressive40%20% of balance7every 2h

The score is between 0 and 1; pick entry thresholds in that range. Relative or rank-based entry rules, such as "in the top ten by trending score" or "score has risen 50% since the last cycle", hold whatever the scale is. For an absolute bar, pull the current trending list (GET /api/tokens?order_by=trending_score&limit=100) and look at the distribution first.

Never act on the score alone. The pre-buy checklist:

  1. Read the score: node scripts/superhero-trending.mjs token-info ct_...
  2. Check the trade flow: node scripts/superhero-transactions.mjs token ct_... 30. Many small buyers suggest organic demand; one large buyer suggests a pump with exit risk.
  3. Check price performance: node scripts/superhero-trending.mjs performance ct_...

Reading it from the CLI

# The trending list, ordered by score
node scripts/superhero-trending.mjs tokens 20
 
# A single token's full record, including trending_score
node scripts/superhero-trending.mjs token-info ct_<sale_address>
 
# 24h / 7d / 30d / all-time performance
node scripts/superhero-trending.mjs performance ct_<sale_address>

tokens accepts an alternative ordering as its second argument (market_cap, holders_count, price, created_at, name, treasury or rank), which is a useful cross-check. A token that is top-ten by trending score and nowhere by holder count deserves a closer look.

Related