Skip to content

Full-stack walkthrough

Step-by-step deployment of the full-stack starter bundle. About 15 minutes from clone to first paper trade.

Demo accounts only

Use a Bybit testnet API key and an Exness MT5 demo account for this walkthrough. qkt is pre-1.0 — do not connect funded accounts.

1. Create the directory structure

mkdir -p my-trading-stack/strategies my-trading-stack/reports
cd my-trading-stack

2. Copy the bundle files

Copy the seven files from the bundle page into this directory. Final layout:

my-trading-stack/
├── .env.example
├── docker-compose.yml
├── qkt.config.yaml
└── strategies/
    ├── btc-trend.qkt
    └── eur-meanrev.qkt

3. Create your .env from the template

cp .env.example .env

Edit .env:

.env
BYBIT_API_KEY=your-bybit-testnet-key
BYBIT_API_SECRET=your-bybit-testnet-secret
BYBIT_ENVIRONMENT=testnet              # important — testnet first
BYBIT_TRADE_MODE=demo
BYBIT_ACCOUNT_LOGIN=                   # from the gateway's /v1/health once it runs
BYBIT_TRADER_TOKEN=a-long-random-value
BYBIT_GUARDIAN_TOKEN=another-long-random-value

MT5_LOGIN=12345678
MT5_PASSWORD=your-exness-demo-password
MT5_SERVER=Exness-MT5Trial
MT5_ENABLE_ALGO_TRADING=1
MT5_API_KEY=replace-with-a-long-random-value
VNC_PASSWORD=changeme

QKT_BROKER_EXNESS_GATEWAY_URL=http://mt5-gateway:5001

For Bybit testnet API keys: testnet.bybit.com → API Management. Real and testnet keys are different — don't mix them. The keys go to the gateway-bybit-spot service (the qkt-venue-gateway running its Bybit adapter); qkt itself only holds the gateway's trader token.

For Exness demo: any phone signup gets you Exness-MT5Trial server credentials within a minute.

4. Add .env to .gitignore

echo '.env' >> .gitignore
echo 'reports/' >> .gitignore

You should never commit credentials. Run this even if you don't plan to push the repo anywhere — it's a habit worth building early.

5. Bring up the stack

docker compose up -d

This starts two containers:

  • mt5-gateway — Wine + MT5 terminal + Flask API on port 5001
  • qkt — the trading daemon, waits for the gateway to pass its healthcheck

Verify both are running:

docker compose ps
# Output:
#   NAME           STATUS              PORTS
#   mt5-gateway    Up (healthy)        0.0.0.0:3000->3000/tcp, 0.0.0.0:5001->5001/tcp
#   qkt            Up

If mt5-gateway is Up (unhealthy), it's still booting Wine + MT5. Wait 60 seconds and re-check.

6. Log into MT5 once

The MT5 terminal needs an interactive login the first time. Connect via VNC at localhost:3000 using VNC_PASSWORD from your .env.

Inside the MT5 GUI:

  1. File → Login to Trade Account
  2. Enter login + password + server name from .env
  3. Click Login — the indicator bottom-right turns green

Verify from the host:

curl -H "Authorization: Bearer $MT5_API_KEY" http://localhost:5001/health/ready
# {"ok": true, "account": {"login": 12345678, "balance": 10000.00, ...}}

If ok: false, the login didn't take — back into the VNC GUI to retry.

7. Verify broker profiles loaded

docker compose exec qkt qkt brokers list

Expected output:

NAME              KIND  GATEWAY                  SUFFIX  SERVER TIME       MAGIC
exness            mt5   http://mt5-gateway:5001 m       new_york_close    4242

brokers list shows MT5 profiles; the bybit_spot gateway account is checked when the daemon starts (it refuses to start if the gateway reports another adapter, account or trade mode). If exness is missing, the qkt.config.yaml didn't load — check the bind-mount path in docker-compose.yml.

8. Audit the live feeds before deploying

This is the step new operators skip and regret. Capture a minute of ticks from each venue and check for anomalies:

docker compose exec qkt qkt audit-ticks \
  --symbol EURUSD --duration 60 --mt5-profile exness \
  --reference mt5-history

Expected output:

poll samples:             240
unique in-window ticks:   96
history ticks:            121
exact timestamp matches:  96
exact bid/ask matches:    96
timestamp price mismatch: 0
missing from history:     0
quote age p95 ms:         380
result:                   PASS

The default reference compares TradingView with MT5. --reference mt5-history is the appropriate mode when the deployed strategy and execution path both use MT5. Auditing the Bybit feed is on the roadmap as part of broader broker-tick audit support — see Planned features.

If you see gaps, out-of-order ticks, or duplicates — investigate before deploying. A flaky feed wrecks even a good strategy.

9. Deploy both strategies

docker compose exec qkt qkt deploy /strategies/btc-trend.qkt    --as btc-trend
docker compose exec qkt qkt deploy /strategies/eur-meanrev.qkt  --as eur-meanrev

Each prints a port:

[INFO] deployed btc-trend
QKT_PORT=47291

[INFO] deployed eur-meanrev
QKT_PORT=47292

10. Verify deployment

docker compose exec qkt qkt list
NAME              KIND       UPTIME    PORT     TRADES   STATE
btc-trend         strategy   00:00:18  47291    0        running
eur-meanrev       strategy   00:00:14  47292    0        running

11. Watch them work

Tail one strategy's logs:

docker compose exec qkt qkt logs btc-trend -f

You'll see ticks arrive + rule evaluations. When a condition transitions to true, you'll see the BUY action and the broker submission. Fills come back as TradeEvent a few hundred ms later.

Check status from outside the container:

docker compose exec qkt qkt status btc-trend

Returns the full StatusSnapshot — positions, recent trades, equity, pending stack layers, etc.

12. Stop cleanly

When you're done (or want to bring everything down):

# Stop strategies one at a time with --flatten to close open positions
docker compose exec qkt qkt stop btc-trend     --flatten
docker compose exec qkt qkt stop eur-meanrev   --flatten

# Then tear down the stack
docker compose down                 # keeps qkt-state volume + reports
docker compose down -v              # full wipe — state + logs gone

--flatten closes any open positions at market before stopping. Without it, positions stay open on the venue and qkt reconciles them on next start.

What to do next

  • Adapt the strategies. Change parameters, swap symbols, try different timeframes. Validate with qkt resync --dry-run, then apply with qkt resync --as <name>.
  • Add more strategies. Drop more .qkt files into strategies/ and deploy them. The daemon assigns each one an ephemeral loopback port.
  • Backtest first. Don't deploy a strategy you haven't backtested. Run qkt backtest strategies/btc-trend.qkt --from 2024-01-01 --to 2024-06-01 to validate.
  • Set up alerts. The observability HTTP endpoints (/status, /events, /health) are scrape-able by Prometheus. See operations/logging.

Common gotchas

  • Compose can't pull the gateway image. Verify Docker Hub access to elitekaycy/mt5-gateway-api:0.3.2, or override MT5_GATEWAY_IMAGE with a digest-pinned/private-registry image.
  • MT5 logs out periodically. Some brokers force daily re-auth. VNC back in, or restart mt5-gateway to trigger a re-login attempt.
  • Bybit testnet vs mainnet. BYBIT_ENVIRONMENT=testnet keeps the gateway on testnet; positions/keys/symbols all live there. Don't mix testnet and mainnet keys in the same .env, and set BYBIT_TRADE_MODE=real only together with BYBIT_ENVIRONMENT=mainnet.
  • Bind-mount path issues on Mac/Windows. Docker Desktop sometimes needs explicit file-sharing permissions for paths outside your home directory. Keep the project under ~/.
  • Port collisions. If something else is already on 47291, the daemon picks the next available. Check qkt list for the actual ports.

See also