FastAPI service that powers Coven β on-chain alpha intelligence for Solana + BNB Chain.
This is the engine behind usecoven.vercel.app. It ingests every swap from tracked smart-money wallets, runs three independent signal algorithms over the stream, executes paper trades on behalf of user-created bots, and dispatches Telegram alerts β all in a single live loop.
π Built for the AVE Γ CLAW Hackathon. AVE is the primary data source: 11 of 16 documented REST endpoints + the live multi_tx WebSocket stream.
Frontend repo β Β· Live site β Β· Demo video β
- What this does
- Architecture at a glance
- Project layout
- Setup
- Environment variables
- Running with Docker
- Background jobs
- API surface
- Event bus
- Deployment
- Common operations
- Troubleshooting
A continuous detection-to-action loop:
Smart-money wallet swaps a token
β (Helius / AVE WSS β sub-second)
Background jobs ingest + normalize the swap
β
contagion_detector / rank_poller score the move
β
SIGNAL_FIRED β signal_enricher β SIGNAL_SCORED
β
bot_runner opens paper trades for matching user bots
telegram_dispatcher DMs alerts to opted-in users
SSE stream pushes everything to open browser tabs
End-to-end latency: typically under 2 seconds from on-chain confirmation to the user's screen.
flowchart TD
subgraph FEEDS["External Feeds"]
AVE["β AVE Data API<br/>11 REST + multi_tx WSS"]
HELIUS["Helius WSS"]
BSCSCAN["BSCScan v2"]
JUPITER["Jupiter (price fallback)"]
end
subgraph JOBS["Background Jobs (asyncio)"]
ws_listener
helius_stream
wallet_poller
price_listener
rank_poller
position_monitor
end
subgraph SERVICES["Event-bus Services"]
contagion_detector
signal_enricher
bot_runner
telegram_dispatcher
end
MONGO[("MongoDB")]
REST["REST API"]
SSE["SSE stream"]
TGBOT["Telegram poller"]
USER(("π€ User"))
AVE --> ws_listener & rank_poller & price_listener
HELIUS --> helius_stream
BSCSCAN --> wallet_poller
JUPITER -.fallback.-> bot_runner
JOBS --> contagion_detector --> signal_enricher
rank_poller --> signal_enricher
signal_enricher --> bot_runner & telegram_dispatcher
price_listener --> position_monitor --> bot_runner
SERVICES <--> MONGO
MONGO --> REST
SERVICES --> SSE
REST <--> USER
SSE -.live.-> USER
telegram_dispatcher --> TGBOT -.alert.-> USER
style AVE fill:#3CC47B,color:#0b0e14,stroke:#3CC47B,stroke-width:2px
backend/
βββ app/
β βββ main.py # FastAPI app + lifespan (boots all jobs)
β βββ config.py # Settings (env vars via pydantic)
β βββ auth/ # JWT + cookie deps + password hashing
β βββ db/ # MongoDB connection (motor async)
β βββ models/ # User, signal, trade, bot pydantic models
β βββ routers/ # FastAPI route modules
β β βββ auth.py # /api/auth/{signup,signin,me,logout}
β β βββ signals.py # /api/signals/{live,...}
β β βββ tokens.py # /api/tokens/{trending,movers,search,...}
β β βββ trades.py # /api/trades/{active,history,pnl,open,close}
β β βββ bots.py # /api/bots β CRUD for signal/copy bots
β β βββ telegram.py # /api/telegram link + prefs
β β βββ balance.py # /api/balance β paper wallet ops
β β βββ settings.py # /api/settings β user preferences
β β βββ stream.py # /api/stream/signals β SSE
β β βββ system.py # /api/system/next-scan-at β countdown
β β βββ clusters.py # /api/clusters
β β βββ wallets.py # /api/wallets
β β βββ backtests.py # /api/backtests
β β βββ health.py
β βββ jobs/ # asyncio background jobs
β β βββ ws_listener.py # AVE WSS multi_tx β token-level swaps
β β βββ helius_stream.py # Helius WSS β Solana wallet swaps
β β βββ wallet_poller.py # BSCScan REST + SOL fallback
β β βββ price_listener.py # Live token prices (AVE WSS)
β β βββ rank_poller.py # AVE leaderboard scan every 5 min
β β βββ position_monitor.py # TP / SL / trailing stop enforcement
β β βββ telegram_poller.py # Inbound TG bot updates
β βββ services/ # In-process event-bus consumers
β βββ ave_client.py # β AVE Data API wrapper
β βββ event_bus.py # asyncio pub/sub
β βββ contagion_detector.py # Cluster signal detection
β βββ signal_enricher.py # Conviction scoring
β βββ bot_runner.py # Opens paper trades for user bots
β βββ telegram_dispatcher.py # Fans signals out to TG
β βββ execution_engine.py # (legacy auto-trader, disabled)
β βββ exit_monitor.py
β βββ risk_checker.py
β βββ balance_ledger.py # Paper-balance debit/credit
β βββ graph_builder.py # Wallet cluster graph builder
β βββ backtester.py
βββ scripts/ # One-off CLI utilities (seed, dedupe, etc.)
βββ requirements.txt
βββ Dockerfile
βββ README.md
| Tool | Version |
|---|---|
| Python | 3.11 |
| MongoDB | β₯ 6 (local or Atlas) |
| AVE Data API key | ave.ai |
| Helius API key | helius.dev (optional but recommended) |
| BSCScan API key | bscscan.com/apis (optional) |
| Telegram bot token | @BotFather (optional) |
git clone https://github.com/victorjayeoba/coven-backend.git
cd coven-backend
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env # then fill in your keys
uvicorn app.main:app --reload --port 8000Boot logs you should see (in order):
[startup] graph index loaded: {'clusters_loaded': N, ...}
[telegram_dispatcher] registered
[ws_listener] starting
[price_listener] starting
[helius_stream] starting
[rank_poller] starting Β· interval=300s
INFO: Application startup complete.
API is now live at http://localhost:8000 Β· interactive docs at http://localhost:8000/docs.
| Variable | Required | Default | Purpose |
|---|---|---|---|
MONGO_URI |
β | mongodb://localhost:27017 |
Mongo connection string |
MONGO_DB |
optional | contagion |
Database name |
JWT_SECRET |
β | (dev fallback) | Signing key for session JWTs β change in prod |
JWT_EXPIRE_MINUTES |
optional | 1440 |
Session lifetime |
AVE_API_KEY |
β | β | Primary data feed |
AVE_BASE_URL |
optional | https://prod.ave-api.com/v2 |
|
AVE_WSS_URL |
optional | wss://wss.ave-api.xyz |
|
HELIUS_API_KEY |
recommended | β | Solana wallet WSS β without this copy bots can't track SOL wallets |
HELIUS_BASE_URL |
optional | https://api.helius.xyz |
|
BSCSCAN_API_KEY |
recommended | β | BSC wallet polling |
WALLET_POLL_INTERVAL_SECONDS |
optional | 10 |
How often wallet_poller ticks |
ENABLE_WALLET_POLLER |
optional | false |
Force REST polling for SOL too (when WSS down) |
ENABLE_TX_POLLER |
optional | false |
Force per-pair tx REST fallback |
TELEGRAM_BOT_TOKEN |
optional | β | Enables outbound TG alerts |
TELEGRAM_BOT_USERNAME |
optional | β | For deep-link generation t.me/<bot>?start=<code> |
CORS_ORIGINS |
optional | http://localhost:3000 |
Comma-separated allowed origins |
COOKIE_SECURE |
optional | false |
Set true in production |
COOKIE_SAMESITE |
optional | lax |
none for cross-domain |
# Build
docker build -t coven-backend .
# Run (assumes Mongo on host)
docker run -d \
--name coven-backend \
-p 8084:8000 \
--env-file .env \
--add-host=host.docker.internal:host-gateway \
--restart unless-stopped \
--ulimit nofile=65536:65536 \
coven-backendThe --ulimit flag is important β without it, the container hits "too many open files" under load (lots of concurrent WebSocket + httpx clients).
When using MONGO_URI=mongodb://host.docker.internal:27017 the --add-host flag lets the container reach a Mongo running on the host machine.
All jobs are started in app/main.py:lifespan and run as long-lived asyncio tasks. They communicate via the in-process event bus (no Redis, no broker β direct fanout, sub-millisecond latency).
| Job | Cadence | What it does |
|---|---|---|
helius_stream |
live (WSS) | Subscribes to Helius logsSubscribe for every tracked Solana wallet. Fetches parsed swaps, normalizes to SWAP_EVENT. |
ws_listener |
live (WSS) | Subscribes to AVE multi_tx for ~145 trending + pump-phase tokens. Source of cluster signals. |
price_listener |
live (WSS) | Subscribes to AVE price/volume/tvl updates for the same token set. Drives live UI ticks + position marking. |
wallet_poller |
every 10s | REST fallback for BSC (no WSS equivalent). For Solana, only runs if helius_stream is disabled. |
rank_poller |
every 5 min | Polls 10+ AVE leaderboard topics + per-chain trending + pump-phase. Fires rank_stack signals when a token appears on β₯2 topics. |
position_monitor |
every 10s | Marks every open paper trade with current price (via AVE batch endpoint) and fires TP / SL / trailing-stop exits. |
bot_position_monitor |
every 10s | Same as above but for bot-created trades. |
telegram_poller |
live (long-poll) | Inbound TG bot updates β handles /start <code> linking and inline button callbacks (Buy / View). |
All /api/* routes (except /api/health + /api/system/next-scan-at) require a session JWT in the coven_session HTTP-only cookie.
POST /api/auth/signupPOST /api/auth/signinPOST /api/auth/logoutGET /api/auth/me
GET /api/signalsΒ· list with filters (status, conviction, chain)GET /api/signals/live?minutes=60Β· feed for the dashboard tableGET /api/signals/{id}Β· single signal detailGET /api/tokens/movers?chains=solana,bscΒ· merged momentum-ranked feedGET /api/tokens/trending?chain=solanaΒ· raw AVE passthroughGET /api/tokens/search?q=...&chain=...GET /api/tokens/{token_id}Β· detail + market statsGET /api/tokens/{token_id}/candles?interval=60GET /api/tokens/{token_id}/riskΒ· honeypot + ownership checksPOST /api/tokens/batchΒ· batch token-detail lookupGET /api/tokens/{token_id}/signalsΒ· all signals + backtests for a token
GET /api/tradesΒ· everything for the user (signal + bot + manual)GET /api/trades/activeΒ· open positionsGET /api/trades/historyΒ· closed positionsGET /api/trades/pnlΒ· aggregate stats (win rate, best/worst, equity)POST /api/trades/openΒ· manual paper trade (used by Phantom-style swap)POST /api/trades/{id}/closeΒ· manual closeGET /api/botsΒ· user's botsPOST /api/botsΒ· create signal or copy botPATCH /api/bots/{id}Β· update settingsPOST /api/bots/{id}/{start|stop}DELETE /api/bots/{id}GET /api/bots/{id}/trades
GET /api/telegram/configGET /api/telegram/statusPOST /api/telegram/start-linkΒ· returns deep-link codePOST /api/telegram/unlinkPATCH /api/telegram/prefsPOST /api/telegram/testGET /api/settingsPATCH /api/settings
GET /api/balanceΒ· paper balances per chainPOST /api/balance/depositPOST /api/balance/resetGET /api/system/next-scan-atΒ· public β countdown timer source
GET /api/stream/signalsΒ· SSE feed:signal.firedΒ·signal.scoredprice.updateΒ·swapbot.trade.openedΒ·bot.trade.closedΒ·bot.updated
Interactive Swagger docs: http://localhost:8000/docs
Internal pub/sub for service decoupling. Located at app/services/event_bus.py. Topics:
| Event | Published by | Subscribers |
|---|---|---|
SWAP_EVENT |
ws_listener, helius_stream, wallet_poller |
contagion_detector, bot_runner (copy bots) |
SIGNAL_FIRED |
contagion_detector, rank_poller |
signal_enricher, SSE stream |
SIGNAL_SCORED |
signal_enricher, rank_poller |
bot_runner (signal bots), telegram_dispatcher, SSE stream |
PRICE_UPDATE |
price_listener |
position_monitor, SSE stream |
BOT_TRADE_OPENED |
bot_runner |
telegram_dispatcher, SSE stream |
BOT_TRADE_CLOSED |
bot_runner |
telegram_dispatcher, SSE stream |
BOT_UPDATED |
routers/bots.py |
SSE stream |
TRADE_OPENED / TRADE_CLOSED |
(legacy execution engine, disabled) | SSE stream |
Currently running on a single VPS via Docker:
- Backend:
coven-api.discretliaison.comβ Cloudflare β reverse proxy β Docker container on port8084. - MongoDB: separate Docker container on the same host.
- Frontend: Vercel, calls backend via Next.js rewrite proxy (
BACKEND_URLenv var).
cd ~/coven-backend && git pull && \
docker build -t coven-backend . && \
docker stop coven-backend && docker rm coven-backend && \
docker run -d --name coven-backend -p 8084:8000 \
--env-file .env --add-host=host.docker.internal:host-gateway \
--restart unless-stopped --ulimit nofile=65536:65536 \
coven-backend && \
docker logs -f coven-backendWatch for [startup] graph index loaded then Ctrl+C to detach.
# Tail live logs
docker logs -f coven-backend
# Drop into the container shell
docker exec -it coven-backend /bin/bash
# Restart without rebuilding
docker restart coven-backend
# Mongo CLI from the host
docker exec -it mongodb mongosh contagion
# Clear all signals (useful after schema changes)
docker exec -it mongodb mongosh contagion --eval "db.signals.deleteMany({})"
# Manually trigger the dedupe cleanup
curl -X POST http://localhost:8084/api/signals/dedupe \
-H "Cookie: coven_session=..."| Symptom | Likely cause | Fix |
|---|---|---|
[Errno 24] Too many open files |
Default ulimit too low for the WSS + httpx workload | Add --ulimit nofile=65536:65536 to docker run |
[rank_poller] DIAG NO OVERLAP |
AVE leaderboards returning ETH/Base tokens, no SOL/BSC overlap | Wait β usually resolves on next 5-min poll, or expand TOPIC_HINTS in rank_poller.py |
[helius_stream] connection closed |
Helius free tier rate limit or WSS hiccup | Auto-reconnects after 5s β safe to ignore unless persistent |
401 Unauthorized from frontend |
Cookie not being sent (cross-domain) | Frontend should call /api/* via same-domain proxy (see frontend next.config.js) |
| Bot trades all close at -100% instantly | AVE returns 0 price for unindexed pump.fun tokens, position_monitor fires false stop loss |
Use a higher liquidity_min on the bot, or skip TP/SL for the first 30s |
| Signals fire but no Telegram alerts | User's conviction_threshold higher than the signal score |
Lower threshold in /settings (default 70) |
[telegram_dispatcher] bot token missing |
TELEGRAM_BOT_TOKEN env var not set |
Set it and restart β alerts are silently disabled without it |
MIT β built for the AVE Γ CLAW Hackathon. Credit Coven if you reuse the code.