What you'll build
Ten steps and two small files take you from a clean machine to a real strategy trading live prices in simulation. This is the exact setup we run and verify ourselves — no conda, no Python version fights, no forty-page manual. Everything below is copy-paste complete.
Steps 1–2: install Docker, create the folder
Install Docker Desktop first (Windows, macOS, or Linux — the commands are identical). Then create a project folder:
docker version # sanity check
mkdir freqtrade-demo && cd freqtrade-demoStep 3: docker-compose.yml — the whole file
This is the entire file — twelve lines. The default command runs dry-run trading with a strategy called BBRSI2 (swap in your own later):
services:
freqtrade:
image: freqtradeorg/freqtrade:stable
restart: unless-stopped
container_name: freqtrade
volumes:
- "./user_data:/freqtrade/user_data"
# Default command when running `docker compose up`
command: >
trade
--config /freqtrade/user_data/config.json
--strategy BBRSI2Steps 4–5: pull the image, create user_data
docker compose pull
docker compose run --rm freqtrade create-userdir --userdir /freqtrade/user_dataStep 6: config.json — the whole file
Thirty-eight lines, and only five of them really matter. The file:
{
"max_open_trades": 1,
"stake_currency": "USDT",
"stake_amount": 100,
"tradable_balance_ratio": 0.99,
"dry_run_wallet": 1000,
"fiat_display_currency": "USD",
"dry_run": true,
"timeframe": "1m",
"cancel_open_orders_on_exit": true,
"exchange": {
"name": "binance",
"key": "",
"secret": "",
"ccxt_config": {},
"pair_whitelist": ["BTC/USDT"],
"pair_blacklist": []
},
"pairlists": [
{"method": "StaticPairList"}
],
"entry_pricing": {
"price_side": "same",
"use_order_book": true,
"order_book_top": 1,
"price_last_balance": 0.0,
"check_depth_of_market": {"enabled": false, "bids_to_ask_delta": 1}
},
"exit_pricing": {
"price_side": "same",
"use_order_book": true,
"order_book_top": 1
},
"bot_name": "freqtrade_docker_demo",
"initial_state": "running",
"internals": {"process_throttle_secs": 5},
"fee": 0.001
}- dry_run: true — simulation mode. No real money anywhere; exchange key and secret stay empty.
- stake_amount: 100 + max_open_trades: 1 — each trade uses 100 USDT, one position at a time.
- pair_whitelist — the bot only ever trades BTC/USDT.
- timeframe: 1m must match the strategy you run (BBRSI2 is a 1-minute strategy).
- fee: 0.001 — realistic Binance spot fee (0.1%). Backtests without fees are fiction.
Step 7: add a strategy
Download BBRSI2.py from the Vetta catalog and drop it into user_data/strategies/. The class name inside the file is what you pass to --strategy. Any other strategy works the same way — just match its timeframe in the config.
Step 8: download real market data
This pulls 200 days of 1-minute BTC/USDT candles from Binance public market data — no exchange account needed:
docker compose run --rm freqtrade download-data \
--config /freqtrade/user_data/config.json \
--pairs BTC/USDT --timeframes 1m --days 200 \
--data-format-ohlcv jsongz --eraseStep 9: backtest — and read it like a skeptic
docker compose run --rm freqtrade backtesting \
--config /freqtrade/user_data/config.json \
--strategy BBRSI2 --timerange 20260207- \
--data-format-ohlcv jsongz- Sample size first: hundreds of trades mean something; a handful means nothing.
- Always compare against the market: if the coin rose 11% and the strategy lost 17%, the strategy subtracted value.
- Win rate is a trap: 60% winners can still lose money if the losses are huge.
- Drawdown duration: could you sit through 186 days underwater without quitting?
Step 10: dry-run — live signals, fake money
The compose file's default command already runs dry-run trading. Start it in the background and watch the log:
docker compose up -d # start
docker compose logs -f # watch live signals
docker compose down # stop- Dry-run trades a simulated 1,000 USDT wallet against live prices. Let it run for at least 2–4 weeks and compare the result with the backtest for the same period — a big gap means the backtest was overfit. Days of observation prove nothing.
Going live — only four changes
When — and only when — dry-run confirms the backtest over weeks, live trading needs exactly four edits to config.json. Nothing else changes.
- dry_run: false — real orders from this point on.
- Fill in exchange key and secret — create an API key with trade-only permission. Never enable withdrawals.
- Set stake_amount to money you can genuinely afford to lose entirely.
- Keep fee realistic — it quietly decides whether small edges survive.
- Going live is not an upgrade, it is a risk decision. Start with the minimum stake, verify the stop-loss actually fires on the exchange, and remember docker compose down stops everything instantly.
The three errors everyone hits
- "pairlists is a required property" — the pairlists block is missing from config.json. Our file above already includes it.
- Data format mismatch — if you downloaded with --data-format-ohlcv jsongz, you must backtest with the same flag.
- Zero trades — your timerange is too short, or the strategy's timeframe doesn't match the data. Widen the window first.
Educational content, not financial advice. Past performance does not predict future results.