Tutorial 2 — From backtest to live¶
You have a strategy that backtests reasonably (Tutorial 1). Now you need to validate it works in real-time before risking money. This tutorial walks through the full validation pipeline: backtest → paper-trade → live demo broker. About 30 minutes.
By the end you'll know:
- Why paper-trading is a separate step from backtesting
- What changes between modes (and what stays the same — that's qkt's whole point)
- How to deploy on a live MT5 demo account through Docker
- What to monitor while a strategy runs
You need everything from Tutorial 1 plus:
- Docker + Docker Compose v2 installed
- A free Exness MT5 demo account (sign up here — takes 60 seconds, gives you fake $10,000 to play with)
Why three modes?¶
Most people think: "I have a backtest result, why can't I just go live?"
Three reasons why backtests aren't enough:
- Backtests fill at clean prices. Real venues have spreads, slippage, partial fills. A 50% win rate in backtest can become 40% live if your stops fire on the wrong side of the spread.
- Backtests don't experience downtime, fat-finger orders, broker outages, login expiry, or network blips. You need a few weeks of paper-trading to catch the operational edge cases.
- Real markets have news events your historical data may not capture. A perfectly-curved backtest equity line gets a 20% drawdown when an unexpected Fed announcement hits.
qkt's design guarantee — bit-identical fills between backtest and paper given the same ticks — means the gap between paper and backtest is small. The gap between paper and live is what you need to measure.
Mode 1 — backtest¶
You already did this in Tutorial 1. The command:
This is the fastest mode. It runs the strategy as fast as the data can stream through it (tens of thousands of ticks per second). It always uses historical data.
Limitations:
- No live data, no real fills
- Slippage / spread / partial fills not modeled by default
- Clock advances by tick timestamp, not wall time
Backtests are for research: tuning parameters, picking promising strategies, computing risk-adjusted returns.
Mode 2 — paper-trade (foreground)¶
Paper-trading runs the same strategy file against a live data feed, but the broker is in-process (no real venue, no real money).
A few things will happen:
- A live tick feed starts. By default, qkt connects to TradingView's free-tier ticks. You'll see ticks flowing.
- An observability HTTP server starts on a random high port. The terminal prints
QKT_PORT=47291(or similar). - The strategy listens to live ticks and emits trades into an in-memory paper broker.
- Trades print to stdout as they happen.
To exit, press Ctrl-C. The strategy stops cleanly.
While it's running, in another terminal:
You'll see the current positions, recent trades, equity, etc. This is the same observability surface your live deployment will expose.
What this proves: the strategy correctly responds to a live tick stream in real-time. If it never trades, see Debug a strategy that isn't firing.
Run paper-trade for at least a few days before moving to live. Watch for:
- Trades you didn't expect (rule misfiring)
- Trades you expected but didn't get (warmup, threshold issue)
- Disconnections / reconnections (resilience of the feed)
- Strategy behavior around news events, weekends, market opens
Mode 3 — live demo broker (MT5)¶
When paper-trading has been clean for a while, you're ready for a live demo broker. Demo, not real money — qkt is pre-1.0; we'll save real funds for after 1.0 ships.
Set up the Docker stack¶
The repo includes a docker-compose.yml for the full stack. Copy the templates:
Edit .env with your Exness demo credentials (you got them when you signed up at exness.com):
MT5_LOGIN=12345678
MT5_PASSWORD=your-demo-password
MT5_SERVER=Exness-MT5Trial
VNC_PASSWORD=changeme
EXNESS_GATEWAY_URL=http://mt5-gateway:5001
The qkt.config.yaml.example ships with an exness profile pre-configured.
Bring up the stack¶
Two containers start:
- mt5-gateway — a Wine container running the MT5 desktop terminal with a Flask HTTP API exposing its functionality
- qkt — the trading daemon, depends on the gateway being healthy
The first time, the gateway needs you to log in interactively. Open a VNC viewer at localhost:3000 (password from .env), then inside the MT5 GUI: File → Login to Trade Account, enter your demo credentials, click Login. The "connected" indicator at the bottom-right of MT5 turns green.
Verify from your host:
If you see {"ok": true, ...} with your account number, the gateway is alive.
Adapt the strategy for live¶
Tutorial 1's strategy uses BACKTEST:BTCUSDT. For live trading, swap the broker prefix and pick a symbol your demo account has:
STRATEGY momentum_live VERSION 1
SYMBOLS
eur = EXNESS:EURUSD EVERY 5m
RULES
WHEN ema(eur.close, 9) CROSSES ABOVE ema(eur.close, 21)
AND POSITION.eur = 0
THEN BUY eur SIZING 0.01
BRACKET {
STOP_LOSS BY 30 PCT,
TAKE_PROFIT BY 60 PCT
}
LOG "long entry" price=eur.close
WHEN ema(eur.close, 9) CROSSES BELOW ema(eur.close, 21)
AND POSITION.eur > 0
THEN CLOSE eur
LOG "exit"
Changes from Tutorial 1:
BACKTEST:BTCUSDT→EXNESS:EURUSD(real broker, real symbol — but a demo account)1m→5m(live trading at 1-minute resolution is noisy; 5m is more comfortable)SIZING 0.1→SIZING 0.01(0.01 lots is the minimum on most MT5 demo accounts, about $1,000 of notional)- Added a
BRACKETto cap loss per trade - Used
POSITION.eur(notPOSITION.btc) to match the new stream alias
Drop the file into ./strategies/ (mounted into the qkt container at /strategies).
Audit the feed first¶
Before deploying, verify the broker's ticks are clean:
If you see gaps, out-of-order ticks, or duplicates — investigate before deploying. A flaky feed wrecks even a good strategy.
Deploy¶
You should see:
Verify with qkt list:
Watch it work¶
Tail the logs:
You'll see live EURUSD ticks arriving, indicators warming up, and eventually the cross-up condition firing.
Check status from outside the container:
The MT5 GUI (via VNC) will show your demo account's positions in the Trade tab tagged with magic=4242 (the magic number from qkt.config.yaml).
Stop cleanly¶
--flatten closes any open positions at market before stopping. Without it, positions stay open and qkt reconciles them on next start.
To tear down the whole stack:
What you learned¶
- Three modes, one strategy file. Backtest, paper, live — the strategy file doesn't change; only the broker prefix does (e.g.
BACKTEST→EXNESS). - Each mode has a purpose. Backtest = research, paper = real-time validation, live demo = operational practice.
- The Docker stack runs MT5 in a Wine container with an HTTP gateway, abstracting the platform from qkt.
- Observability is uniform. Every mode exposes the same
/status,/logs,/eventsendpoints. You debug the same way regardless of mode.
What's still missing¶
You've gone backtest → paper → live demo. To go to live real money:
- qkt needs to reach 1.0 (it isn't yet — we're at 0.25)
- The strategy needs to paper-trade and demo-trade for weeks without surprises
- Your risk rules in
qkt.config.yamlneed to be tight - You need a monitoring story (alerts when the daemon crashes, when a stop hits, when equity drops)
Tutorial 3 covers the next step in strategy complexity — composing several strategies into a regime-gated portfolio.
If something went wrong¶
docker compose up -dfails onmt5-gateway:latest— the image isn't on Docker Hub. Build it locally from github.com/elitekaycy/mt5-gateway first.curl localhost:5001/healthreturns{"ok": false}— you haven't logged into MT5 yet. VNC atlocalhost:3000and log in via the GUI.- Strategy deploys but doesn't trade — see debug a strategy that isn't firing. Most often the EMAs haven't warmed up yet — the first 21 candles produce no signal.
- MT5 logs out periodically — some brokers force daily re-auth. VNC back in. Add a healthcheck alert in production.