Long-only grid DCA trading bot for Bybit spot with pairwise loss compensation.
trader/— async trading worker (long-running)web/— Django dashboard + config UI + REST APItgbot/— Telegram bot (notifications + control commands)core/— shared domain code (exchange client, strategy, models, services)
uv sync
cp .env.example .env # fill in secrets
uv run pre-commit install
uv run python manage.py migrate
uv run python manage.py createsuperuseruv run python manage.py runserver # web (http://127.0.0.1:8000)
uv run python -m trader # trading worker
uv run python -m tgbot # telegram botuv run ruff check
uv run ruff format --check
uv run mypy .
uv run pytest # unit + integration (integration skipped without keys)
uv run pytest --ignore=tests/integration # unit only (what CI runs)uv run python manage.py add_tg_admin <chat_id> --label "Owner"Send /start to the bot from that chat — only admins listed in TelegramUser can use commands.
GitHub Actions (.github/workflows/ci.yml) runs on push/PR to main:
ruff check,ruff format --checkmypy --strictpytest(unit, integration excluded)
Integration tests against Bybit testnet are kept out of CI by default — run them locally
with BYBIT_API_KEY=... BYBIT_API_SECRET=... BYBIT_TESTNET=1 uv run pytest -m integration tests/integration.
Before placing real orders, run the validator (checks Bybit creds, balance, instrument, Redis):
uv run python manage.py preflightSet TRADER_DRY_RUN=1 to have the trader log intended orders without placing them:
TRADER_DRY_RUN=1 uv run python -m traderFull smoke-test walkthrough: see docs/DEPLOY.md.
Three services off the same repo, sharing a single Postgres + Redis plugin:
| Service | Start command | Config file |
|---|---|---|
web |
migrate && gunicorn web.wsgi:application --bind 0.0.0.0:$PORT |
railway.json (default) |
trader |
python -m trader |
railway.trader.json |
tgbot |
python -m tgbot |
railway.tgbot.json |
For each service in the Railway dashboard, set "Config Path" to the matching file. All three services share these env vars:
DATABASE_URL— auto-injected from the Postgres pluginREDIS_URL— auto-injected from the Redis pluginBYBIT_API_KEY,BYBIT_API_SECRET,BYBIT_TESTNETTELEGRAM_BOT_TOKENDJANGO_SECRET_KEY,DJANGO_DEBUG=0,DJANGO_ALLOWED_HOSTS=<service-domain>
First-run after deploy:
- Open
webservice shell (orrailway runlocally with prod env):python manage.py createsuperuser python manage.py add_tg_admin <your_chat_id> --label "Owner"- Log in at
<web-domain>/admin/, set upStrategyConfigvia the dashboard config UI - Ensure
BotStatus.paused = False - Restart
traderservice to pick up config
Health check on web: GET /healthz (unauthenticated, no DB).
See /home/grenkoff/.claude/plans/velvet-sprouting-lampson.md for the full plan.