No move yet
Result and evidence
- Pool health scoring over MCPCapabilityClassifier scores are from a labelled training set, not measured on live pools.
The case
- Problem
- A new liquidity pool can be drained within hours of launch. Someone about to trade it, or an agent about to recommend it, needs a risk read in the first hour, not after the fact.
- Decision
- Split ingestion from judgement. An async Python worker polls GeckoTerminal, drops pools it has already seen with a Bloom filter, and queues the rest in Redis. Rails 8 scores each pool, keeps its history in PostgreSQL, pushes it to the dashboard over Turbo Streams, and answers agents through an MCP server.
- Constraint
- Only what a pool shows in its first hour can feed the score.
- Example
- Input: a pool address and network. Output: a 0-100 health score, SAFE, WARNING or DANGER, a recommendation such as DO_NOT_TRADE, and flags such as critical_low_liquidity or wash_trading_suspected.
- Considered and rejected
- A single-page frontend: the dashboard is server-rendered Hotwire, so a new scan reaches the page without a client app.
Architecture
- GeckoTerminalnew pools
- Python workerBloom filter
- Redisqueue
- Rails 8score
Beside the path
- Hotwirelive dashboard
- MCP serveragent tools
Around it
- PostgreSQLscan history
What was built
- An asyncio ingestion worker with a scalable Bloom filter for duplicates and back-off on GeckoTerminal rate limits.
- A risk calculator that scores with an XGBoost classifier on five first-hour features, and falls back to liquidity, wash-trading and pool-age rules when the model is unavailable.
- A live Hotwire dashboard, with the N+1 query on the pool list removed by tracking each pool's latest scan.
- An MCP server with tools to analyse a token, get a pool's status, list tracked tokens and check the backend, with a static fallback when Rails is unreachable.
- Dockerfiles for running the Rails app and the worker as separate ECS Fargate tasks.
- Limitations
- The classifier's near-perfect test scores come from the training pipeline's labelled set, which it can generate synthetically; they are not a measurement on live pools, so none is quoted here. The 500-user MCP load test is written but has no recorded run.
- What I would change now
- Label real pools that were drained, measure the classifier against them, and run the load test.
Built with Chang Kai Zhe; both of us committed to the repository.
Python · asyncio · Redis · Rails 8 · Hotwire · PostgreSQL · XGBoost · MCP
Next, 1…Nf6Financial Risk Predictor