Find the top 5 tokens that ran hardest in the past 24h across major chains using GeckoTerminal
Scanned 6/6/2026
Install to Claude Code
npx -y skills add anajuliabit/aeon --skill monitor-runners --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Monitor Runners?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/anajuliabit-monitor-runners)More formats (shields.io, HTML) on the badges page.
---
name: Monitor Runners
description: Find the top 5 tokens that ran hardest in the past 24h across major chains using GeckoTerminal
var: ""
tags: [crypto]
---
<!-- autoresearch: variation B — sharper output via composite Runner Score, actionable tags, session verdict, repeat-runner delta -->
> **${var}** — Filter by chain (e.g. "solana", "eth", "base", "bsc", "arbitrum"). If empty, scans all major networks.
Read `memory/MEMORY.md` for context.
Read the last 2 days of `memory/logs/` to find any tokens previously flagged as runners — **repeat runners across days are the real signal**.
## Why this skill exists
A flat "top 5 by 24h %" list is dominated by micro-cap meme coins with <$50k liquidity. That output trains the operator to ignore it. The biggest lever is **ranking by a composite Runner Score and tagging each pick with an actionable category** — so the operator can tell at a glance which picks are serious (deep-liq, sustained momentum) vs speculative (micro-cap, brand-new pool).
## Data Source
GeckoTerminal API (free, no API key). Docs: https://apiguide.geckoterminal.com
Endpoints used:
- `GET /networks/trending_pools?page=1` — trending pools across all networks (the % movers)
- `GET /networks/{network}/trending_pools?page=1` — per-network trending
- `GET /networks/{network}/pools?page=1&sort=h24_volume_usd_desc` — volume leaders (catches runners that aren't on the trending list yet)
- `GET /networks/new_pools?page=1` — newly created pools (brand-new breakouts)
Each pool object includes:
- `attributes.name` — pool name (e.g. "TOKEN / SOL")
- `attributes.price_change_percentage.{m5,m15,m30,h1,h6,h24}` — price changes
- `attributes.volume_usd.{m5,m15,m30,h1,h6,h24}` — volume
- `attributes.market_cap_usd` / `attributes.fdv_usd` — market cap
- `attributes.transactions.h24.{buys,sells,buyers,sellers}` — activity
- `attributes.pool_created_at` — pool creation timestamp
- `attributes.reserve_in_usd` — liquidity
- `relationships.network.data.id` — chain name
- `relationships.base_token.data.id` — base token address (for dedup)
## Steps
### 1. Fetch data (sequential, rate-limit aware)
```bash
TMPDIR=$(mktemp -d)
TODAY=$(date -u +%Y-%m-%d)
# Networks to scan. If ${var} is set, restrict to that one.
if [ -n "${var}" ]; then
NETWORKS="${var}"
else
NETWORKS="solana eth base bsc arbitrum"
fi
fetch_with_backoff() {
local url="$1" out="$2"
for delay in 0 2 4; do
[ $delay -gt 0 ] && sleep $delay
curl -s --max-time 15 "$url" > "$out"
if ! grep -q '"status":"429"' "$out" 2>/dev/null && [ -s "$out" ]; then
return 0
fi
done
return 1
}
# Global trending
fetch_with_backoff "https://api.geckoterminal.com/api/v2/networks/trending_pools?page=1" "$TMPDIR/global.json" \
&& GLOBAL_OK=1 || GLOBAL_OK=0
sleep 1
# Per-network trending + volume leaders
for N in $NETWORKS; do
fetch_with_backoff "https://api.geckoterminal.com/api/v2/networks/${N}/trending_pools?page=1" "$TMPDIR/${N}-trend.json" \
&& eval "${N}_TREND_OK=1" || eval "${N}_TREND_OK=0"
sleep 1
fetch_with_backoff "https://api.geckoterminal.com/api/v2/networks/${N}/pools?page=1&sort=h24_volume_usd_desc" "$TMPDIR/${N}-vol.json" \
&& eval "${N}_VOL_OK=1" || eval "${N}_VOL_OK=0"
sleep 1
done
# New pools (for BREAKOUT tagging)
fetch_with_backoff "https://api.geckoterminal.com/api/v2/networks/new_pools?page=1" "$TMPDIR/new.json" \
&& NEW_OK=1 || NEW_OK=0
```
**Sandbox fallback:** if `curl` fails for any URL (file is empty or has `"status":"429"` after retries), retry that URL with **WebFetch** using the same URL. Parse the JSON response body.
### 2. Merge, dedupe, gate
From every fetched file, extract all pool objects. Then:
1. **Dedupe** by `relationships.base_token.data.id` — keep the highest-volume pool per token (same token may have multiple pools across DEXes).
2. **Gate on quality** — drop a pool if ANY of:
- `volume_usd.h24 < 50000` (too thin to be a real runner)
- `price_change_percentage.h24 <= 0` (we want runners, not dumps)
- `reserve_in_usd < 10000` (liquidity floor)
- `transactions.h24.sells / transactions.h24.buys > 10` (dumping pattern)
- `transactions.h24.buys / transactions.h24.sells > 50` (honeypot pattern — nobody can sell)
- pool_created < 1h ago AND `volume_usd.h24 < 100000` (too new to judge)
- `price_change_percentage.h24 > 10000` (>100x — almost certainly a rug-in-progress)
Record the count of pre-gate and post-gate pools for the log.
### 3. Score each surviving pool
Compute a **Runner Score** (0-100) per pool. Use simple normalized components so the math is transparent:
```
pct_pts = clamp(price_change_percentage.h24 / 500, 0, 1) # 500% maps to full
vol_pts = clamp(log10(volume_usd.h24 + 1) / 7, 0, 1) # $10m vol = full
liq_pts = clamp(log10(reserve_in_usd + 1) / 6, 0, 1) # $1m liq = full
mom_pts = clamp((price_change_percentage.h1 + 50) / 100, 0, 1) # +50% h1 = full, -50% = 0
skew_pts = clamp(buys / (buys + sells), 0, 1) # 0.5 = neutral
runner_score = 40*pct_pts + 25*vol_pts + 15*liq_pts + 10*mom_pts + 10*skew_pts
```
This weights absolute move (40%) + liquidity-adjusted volume (25%) + liquidity depth (15%) + live momentum (10%) + buy pressure (10%). Pct_pts is clamped to avoid meme-coin moonshots flooding the ranking.
### 4. Tag each pool (exactly one tag)
Apply tags in priority order — first match wins:
| Tag | Condition |
|-----|-----------|
| **DEEP-LIQ** | `reserve_in_usd >= 1_000_000` AND `volume_usd.h24 >= 1_000_000` |
| **BREAKOUT** | `pool_created_at` within last 48h AND `volume_usd.h24 >= 250_000` |
| **CONTINUATION** | `price_change_percentage.h1 > 2` AND `price_change_percentage.h24 > 50` |
| **REVERSAL** | `price_change_percentage.h1 < -5` AND `price_change_percentage.h24 > 0` (fading) |
| **MICRO-SPEC** | everything else (default — small-cap speculation) |
### 5. Select the top 5 + session verdict
Rank by Runner Score descending, take top 5.
Compute a session verdict from the tag distribution among the top 5:
- **STRONG** — ≥2 DEEP-LIQ picks (real money moving)
- **MIXED** — 1 DEEP-LIQ OR ≥2 CONTINUATION (signal but speculative)
- **SPECULATIVE** — majority MICRO-SPEC/BREAKOUT (retail casino)
- **SLEEPY** — fewer than 5 pools survived the quality gate
### 6. Cross-reference prior days
From the last 2 days of `memory/logs/`, extract any token names flagged under `## Monitor Runners`. For each of today's top 5, mark **★ repeat** if the token name appears in either prior day's log. Sustained runners across multiple days deserve extra attention.
### 7. Notify
Send via `./notify` (under 4000 chars, no leading spaces). Format:
```
*runners — ${TODAY}* — verdict: STRONG
1. [TAG] TOKEN (chain) +X% 24h ★ repeat
vol $X.Xm | liq $X.Xm | fdv $Xm | h1 +X% | buys:sells X:Y
— [one-line actionable take: e.g. "sustained multi-day momentum with deep liquidity — watch for continuation"]
2. [TAG] TOKEN (chain) +X% 24h
vol $Xm | liq $Xk | fdv $Xm | h1 -X% | buys:sells X:Y
— [one-line take]
3. ...
4. ...
5. ...
sources: gt-global=ok gt-{networks}=ok/fail
vibe: [one-line read on overall tape mood]
```
**Formatting rules:**
- Format dollar values human-readable: `$2.3m`, `$450k`, `$75k`. Never show raw dollar amounts with comma separators.
- Format percentages: `+347%` (no decimals unless <10%, then `+4.2%`).
- If `market_cap_usd` is null, show `fdv $Xm (no mcap)`.
- Include the ★ repeat marker only for tokens appearing in prior days' logs.
- The one-line take MUST say something the operator can act on — not a restatement of the numbers. Good: "clean breakout, pool <24h old but already $500k liq locked". Bad: "price went up a lot with high volume".
**Edge cases:**
- If verdict is **SLEEPY** (<5 pools passed): send a short note instead — `*runners — ${TODAY}* — sleepy session, only N pools cleared quality gate. Skipping top-5.` Include the 1-2 survivors if any.
- If ALL sources failed (every `*_OK=0`): send `*runners — ${TODAY}* — MONITOR_RUNNERS_ERROR, all GeckoTerminal endpoints failed. Check sandbox/rate-limits.` and skip the rest.
### 8. Log
Append to `memory/logs/${TODAY}.md`:
```
## Monitor Runners
- **Networks scanned:** N (list)
- **Source status:** gt-global=ok|fail, per-network: ...
- **Pools pre-gate:** N / **post-gate:** N
- **Verdict:** STRONG|MIXED|SPECULATIVE|SLEEPY
- **Top 5:**
1. [TAG] TOKEN (chain) +X% — score XX, vol $Xm, liq $Xk — [one-line take]
2. ...
- **Repeat runners (seen in prior 2 days):** [list or "none"]
- **Gate rejections breakdown:** thin-vol=N, dumping=N, honeypot=N, too-new=N, rug-like=N
- **Notification sent:** yes|no (reason if no)
```
If a token appears as a runner on **3 days in a row**, flag it in `memory/MEMORY.md` under "Active topics" — sustained multi-day runners are worth a deeper look.
## Sandbox note
The sandbox may block outbound curl. Use **WebFetch** as a fallback for any URL fetch — for each URL that `curl` fails, call WebFetch on the same URL and parse the JSON body. GeckoTerminal requires no auth so no pre-fetch pattern needed.
## Constraints
- Never recommend trades. This is pure observation — "watch", "monitor", "interesting" are fine; "buy", "ape", "enter" are not.
- Don't inflate the list. If only 3 pools pass the gate, publish 3 — don't backfill with low-quality picks.
- The Runner Score math is deterministic — if two runs on the same data produce different top-5s, something is wrong.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!