Nyanbot is an advanced, modular Solana trading automation and analytics system. It aggregates market data, applies rules for trading decisions, integrates with Telegram, and supports real-time streaming across multiple venues.
- 🔁 Real-time data aggregation for:
- Pump.fun
- PumpSwap
- Jupiter
- Solana-native sources
- 📈 Computes candles, market cap, TWAP, and USD prices
- 🧠 Rule engine for automated trading logic
- 🧵 Highly concurrent indexer using blockstream + slotstream
- 🤖 Telegram bot interface for user interaction
- 🐳 Fully containerized with Docker support
- 🗂 Postgres-backed storage with SQLx migrations
docker build . -t nyanbot/nyanbotSome integration tests require a .env file in the root directory with the following values:
TEST_TELEGRAM_TOKEN=*****************
DATABASE_URL=postgres://username:password@localhost:5432/dev
SOLANA_RPC_URL==*****************
WALLET_SECRET==*****************
TEST_SOLANA_KEY_ONE==*****************
TEST_SOLANA_KEY_TWO==*****************
TEST_SOLANA_KEY_THREE==*****************
TEST_SOLANA_KEY_FOUR==*****************
TEST_SOLANA_KEY_FIVE==*****************
where
- TEST_SOLANA_KEY_ONE - contains balance - used for the happy path tests
- TEST_SOLANA_KEY_TWO - receives token and native from TEST_SOLANA_KEY_ONE
- TEST_SOLANA_KEY_THREE - empty wallet - contains ata for 8j51khGE6ELcKvhQNXne448rVcLGMtqWMc5YNAdHpump and wsol
- TEST_SOLANA_KEY_FOUR - empty wallet
- TEST_SOLANA_KEY_FIVE - contains native and wsol
The latest build is pushed to Docker Hub. You can also build it yourself and run locally by updating credentials accordingly. Run all services using a docker-compose.yml file like the one shown above.
services:
postgres:
image: postgres:17-alpine
container_name: postgres
restart: always
environment:
- POSTGRES_USER=************
- POSTGRES_PASSWORD=*********
ports:
- "127.0.0.1:5432:5432"
volumes:
- ./data/postgres:/var/lib/postgresql/data
command: [ "postgres", "-c", "max_locks_per_transaction=1024", "-c", "max_wal_size=8192000" ]
agg-jup-candle:
image: nyanbot/nyanbot
container_name: agg-jup-candle
restart: always
environment:
- "TOKIO_THREADS=1"
- "JUPITER_CANDLE_ACTIVE=true"
- "JUPITER_CANDLE_POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
- "JUPITER_CANDLE_POSTGRES_POOL_MAX=4"
command: /app/aggregator/aggregator /app/aggregator/config.toml
depends_on: [ postgres ]
agg-jup-mcap:
image: nyanbot/nyanbot
container_name: agg-jup-mcap
restart: always
environment:
- "TOKIO_THREADS=1"
- "JUPITER_MCAP_ACTIVE=true"
- "JUPITER_MCAP_POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
- "JUPITER_MCAP_POSTGRES_POOL_MAX=2"
command: /app/aggregator/aggregator /app/aggregator/config.toml
depends_on: [ postgres ]
agg-jup-twap:
image: nyanbot/nyanbot
container_name: agg-jup-twap
restart: always
environment:
- "TOKIO_THREADS=1"
- "JUPITER_TWAP_ACTIVE=true"
- "JUPITER_TWAP_POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
- "JUPITER_TWAP_POSTGRES_POOL_MAX=2"
command: /app/aggregator/aggregator /app/aggregator/config.toml
depends_on: [ postgres ]
agg-jup-usd:
image: nyanbot/nyanbot
container_name: agg-jup-usd
restart: always
environment:
- "TOKIO_THREADS=1"
- "JUPITER_USD_ACTIVE=true"
- "JUPITER_USD_POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
- "JUPITER_USD_POSTGRES_POOL_MAX=2"
command: /app/aggregator/aggregator /app/aggregator/config.toml
depends_on: [ postgres ]
agg-pf-candle:
image: nyanbot/nyanbot
container_name: agg-pf-candle
restart: always
environment:
- "TOKIO_THREADS=1"
- "PUMPFUN_CANDLE_ACTIVE=true"
- "PUMPFUN_CANDLE_POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
- "PUMPFUN_CANDLE_POSTGRES_POOL_MAX=4"
command: /app/aggregator/aggregator /app/aggregator/config.toml
depends_on: [ postgres ]
agg-pf-mcap:
image: nyanbot/nyanbot
container_name: agg-pf-mcap
restart: always
environment:
- "TOKIO_THREADS=1"
- "PUMPFUN_MCAP_ACTIVE=true"
- "PUMPFUN_MCAP_POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
- "PUMPFUN_MCAP_POSTGRES_POOL_MAX=2"
command: /app/aggregator/aggregator /app/aggregator/config.toml
depends_on: [ postgres ]
agg-pf-progress:
image: nyanbot/nyanbot
container_name: agg-pf-progress
restart: always
environment:
- "TOKIO_THREADS=1"
- "PUMPFUN_PROGRESS_ACTIVE=true"
- "PUMPFUN_PROGRESS_POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
- "PUMPFUN_PROGRESS_POSTGRES_POOL_MAX=2"
command: /app/aggregator/aggregator /app/aggregator/config.toml
depends_on: [ postgres ]
agg-pf-summary:
image: nyanbot/nyanbot
container_name: agg-pf-summary
restart: always
environment:
- "TOKIO_THREADS=1"
- "PUMPFUN_SUMMARY_ACTIVE=true"
- "PUMPFUN_SUMMARY_POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
- "PUMPFUN_SUMMARY_POSTGRES_POOL_MAX=8"
command: /app/aggregator/aggregator /app/aggregator/config.toml
depends_on: [ postgres ]
agg-pf-twap:
image: nyanbot/nyanbot
container_name: agg-pf-twap
restart: always
environment:
- "TOKIO_THREADS=1"
- "PUMPFUN_TWAP_ACTIVE=true"
- "PUMPFUN_TWAP_POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
- "PUMPFUN_TWAP_POSTGRES_POOL_MAX=2"
command: /app/aggregator/aggregator /app/aggregator/config.toml
depends_on: [ postgres ]
agg-pf-usd:
image: nyanbot/nyanbot
container_name: agg-pf-usd
restart: always
environment:
- "TOKIO_THREADS=1"
- "PUMPFUN_USD_ACTIVE=true"
- "PUMPFUN_USD_POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
- "PUMPFUN_USD_POSTGRES_POOL_MAX=2"
command: /app/aggregator/aggregator /app/aggregator/config.toml
depends_on: [ postgres ]
agg-ps-candle:
image: nyanbot/nyanbot
container_name: agg-ps-candle
restart: always
environment:
- "TOKIO_THREADS=1"
- "PUMPSWAP_CANDLE_ACTIVE=true"
- "PUMPSWAP_CANDLE_POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
- "PUMPSWAP_CANDLE_POSTGRES_POOL_MAX=4"
command: /app/aggregator/aggregator /app/aggregator/config.toml
depends_on: [ postgres ]
agg-ps-mcap:
image: nyanbot/nyanbot
container_name: agg-ps-mcap
restart: always
environment:
- "TOKIO_THREADS=1"
- "PUMPSWAP_MCAP_ACTIVE=true"
- "PUMPSWAP_MCAP_POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
- "PUMPSWAP_MCAP_POSTGRES_POOL_MAX=2"
command: /app/aggregator/aggregator /app/aggregator/config.toml
depends_on: [ postgres ]
agg-ps-summary:
image: nyanbot/nyanbot
container_name: agg-ps-summary
restart: always
environment:
- "TOKIO_THREADS=1"
- "PUMPSWAP_SUMMARY_ACTIVE=true"
- "PUMPSWAP_SUMMARY_POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
- "PUMPSWAP_SUMMARY_POSTGRES_POOL_MAX=8"
command: /app/aggregator/aggregator /app/aggregator/config.toml
depends_on: [ postgres ]
agg-ps-twap:
image: nyanbot/nyanbot
container_name: agg-ps-twap
restart: always
environment:
- "TOKIO_THREADS=1"
- "PUMPSWAP_TWAP_ACTIVE=true"
- "PUMPSWAP_TWAP_POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
- "PUMPSWAP_TWAP_POSTGRES_POOL_MAX=2"
command: /app/aggregator/aggregator /app/aggregator/config.toml
depends_on: [ postgres ]
agg-ps-usd:
image: nyanbot/nyanbot
container_name: agg-ps-usd
restart: always
environment:
- "TOKIO_THREADS=1"
- "PUMPSWAP_USD_ACTIVE=true"
- "PUMPSWAP_USD_POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
- "PUMPSWAP_USD_POSTGRES_POOL_MAX=2"
command: /app/aggregator/aggregator /app/aggregator/config.toml
depends_on: [ postgres ]
agg-sol-sol:
image: nyanbot/nyanbot
container_name: agg-sol-sol
restart: always
environment:
- "TOKIO_THREADS=1"
- "SOLANA_SOL_ACTIVE=true"
- "SOLANA_SOL_POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
command: /app/aggregator/aggregator /app/aggregator/config.toml
depends_on: [ postgres ]
agg-leaderboard:
image: nyanbot/nyanbot
container_name: agg-leaderboard
restart: always
environment:
- "TOKIO_THREADS=1"
- "LEADERBOARD_ACTIVE=true"
- "LEADERBOARD_POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
command: /app/aggregator/aggregator /app/aggregator/config.toml
depends_on: [ postgres ]
api:
image: nyanbot/nyanbot
container_name: api
restart: always
environment:
- "WALLET_SECRET=d****************************************************************a"
- "POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
- "TELEGRAM_TOKEN=******************:***********************************"
ports:
- "127.0.0.1:8080:8080"
command: /app/api/api /app/api/config.toml
depends_on: [ postgres ]
engine-handle:
image: nyanbot/nyanbot
container_name: engine-handle
restart: always
environment:
- "HANDLE_ACTIVE=true"
- "HANDLE_MODE=LIVE"
- "HANDLE_RPC_URL=https://rpc.shyft.to?api_key=*********"
- "HANDLE_POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
- "WALLET_SECRET=d****************************************************************a"
command: /app/engine/engine /app/engine/config.toml
depends_on: [ postgres ]
engine-rule-pf:
image: nyanbot/nyanbot
container_name: engine-rule-pf
restart: always
environment:
- "RULE_PUMPFUN_ACTIVE=true"
- "RULE_POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
command: /app/engine/engine /app/engine/config.toml
depends_on: [ postgres ]
indexer:
image: nyanbot/nyanbot
container_name: indexer
restart: always
environment:
- "TOKIO_THREADS=8"
- "RAYON_THREADS=4"
- "POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
- "BLOCKSTREAM_CONCURRENCY=30"
- "BLOCKSTREAM_RPC_URL=https://rpc.shyft.to?api_key=*********"
- "SLOTSTREAM_WS_URL=wss://rpc.shyft.to?api_key=*********"
- "SLOTSTREAM_DELAY=10"
- "RPC_URL=https://rpc.shyft.to?api_key=*********"
command: /app/indexer/indexer /app/indexer/config.toml
depends_on: [ postgres ]
telegram:
image: nyanbot/nyanbot
container_name: telegram
restart: always
environment:
- "WALLET_SECRET=d****************************************************************a"
- "POSTGRES_CONNECTION_STRING=postgres://username:password@postgres:5432/db"
- "TELEGRAM_TOKEN=******************************"
- "WEBAPP_URL=https://telegram.nyan.bot"
command: /app/telegram/telegram /app/telegram/config.toml
depends_on: [ postgres ]Run database migrations:
sqlx migrate runThis project is in its prototyping phase – speed, clean code, and best practices have been intentionally ignored for now. Do NOT use in dbuction unless you enjoy debugging at 3 AM.
Yes, the code is open source, but we are not accepting PRs at this point in time. However, we highly appreciate feedback, bug reports, and ideas to make Nyanbot even better!
If you want to point out a bug or suggest an improvement, feel free to open an issue. If you include example code to illustrate your point, that’s great! But note that we will rewrite any submitted code ourselves before merging, in a similar fashion to how SQLite operates.
🐾 Your insights matter, and we value your input! Just don’t expect a merge button. 😼
This project is licensed under the AGPL-3.0. If you use or modify the code and deploy it publicly, you must also share your changes. Sharing is caring! 😼