Operator documentation
From zero to live multi-market.
Everything you need to set up the three broker accounts, run the bot, monitor it across the dashboard, Telegram, and TUI, and flip each market live when you're ready. No magic numbers — every knob below maps to a field in your .env.
Intro
What Vectra is
Vectra is a gate-driven multi-market trading bot. Crypto on MEXC trades live today. Equities (Interactive Brokers) and FX (OANDA) ship in code as paper-ready broker adapters — they flow real orders the moment you set the matching env flag and connect an account.
Each market has its own champion strategy and risk profile, wrapped in an equity-curve bear-defense overlay that redirects capital into a complement basket (sp100 cross-sectional momentum + fx7 trend-following at 3x leverage) when its own rolling Sharpe drops — turning what would have been a 4% APR cash-park into real alpha. The combined backtest delivers live champion stats (loading…), with real alpha during the 2022 chop window (previously a cash-park period), zero multi-month flat periods, near-zero cross-market correlation. See methodology for the strategies themselves and performance for the acceptance-gate audit.
Step 0
Prerequisites
You'll need:
- A Linux host (the bot runs as a
systemd --userunit; macOS works for local dev, production assumes Linux). - Rust toolchain 1.83+ via
rustup;moldlinker recommended. - PostgreSQL 16 and Redis 7 reachable from the bot host.
- Node.js 20+ and
npmif you also want to serve the web dashboard locally. - At least one broker account (start with MEXC; equities + FX can stay paper while you get comfortable).
Hardware
Step 1
Per-market setup
One card per broker. Open the account, generate credentials, paste the env vars into .env, repeat per market you want enabled.
MEXC (crypto futures)
USDT-margined perpetuals · vol-mom champion · live by default
- Account min
- $0
- Strategy floor
- $30
- Recommended
- $100 +
Floor: MEXC min notional ~$5, vol-mom default base_notional_fraction=0.085 × leverage_cap=3.0 sizes a slot to ~$5 needed equity ≈ $19.6; +30% preflight safety buffer rounds up to $30.
$100+ gives breathing room for funding-rate hiccups and lets the bot keep multiple slots open concurrently without one losing position eating the buffer.
Env vars
MEXC_API_KEYMEXC_API_SECRETMEXC_LIVE_SYMBOL
Interactive Brokers (equities)
US sp100 · vol-mom shared-equity · paper-ready, opt-in
- Account min
- $0
- Strategy floor
- $200
- Recommended
- $1,000 +
Floor: vol-mom shared mode with default n_concurrent=5 slots: $200 / 5 = $40 per slot. IBKR fractional shares clear the $1 commission floor at ~2.5% round-trip cost — below ~$40 per slot the fee burden swamps the strategy edge.
$1k+ lets each of 5 concurrent slots hold full shares of cheaper sp100 names and keeps commission-as-a-fraction well under 0.5% per round-trip.
Env vars
VECTRA_EQUITIES_LIVEIBKR_ACCOUNT_IDIBKR_GATEWAY_URL
OANDA (FX majors)
fx7 universe · xsec trend, top-2 long @ 5× leverage · paper-ready, opt-in
- Account min
- $0
- Strategy floor
- $50
- Recommended
- $500 +
Floor: fx7 = 7 pairs, xsec trend at 5× leverage: top-2 ranked pairs go long, rest flat. $50 → ~$35 margin per slot → ~$175 notional across 2 concurrent positions on average.
$500+ supports concurrent positions across multiple pairs at 5× lev without preflight shrinking later entries.
Env vars
VECTRA_FX_LIVEOANDA_API_TOKENOANDA_ACCOUNT_IDOANDA_PRACTICE
MEXC (crypto) — full steps
- 1Open mexc.com → Profile → API Management.
- 2Click Create API. Toggle ON: Read account information + Trade futures. Leave Withdrawal and Spot trading OFF.
- 3Optionally bind the key to your server's IP (the bot prints its outbound IP on startup).
- 4Copy the
keyandsecret— the secret is shown once. - 5Paste into
.envasMEXC_API_KEY+MEXC_API_SECRET. Restart the bot.
IBKR (equities) — full steps
IBKR doesn't expose a remote API key. You run their Client Portal Gateway daemon locally and the bot talks to it on https://localhost:5000. Session lasts 24h; re-auth is a browser SSO flow.
- 1Open an IBKR Pro account (NL/EU residents → IB Ireland Ltd). Complete KYC. Aim for $1,000+ funded.
- 2Download the Client Portal Gateway from interactivebrokers.com/api, extract, and run
./bin/run.sh root/conf.yaml. - 3Open
https://localhost:5000in a browser and log in with your IBKR credentials. The gateway holds the session. - 4Set
IBKR_ACCOUNT_IDin.env(e.g.U1234567). - 5When ready to flip live, also set
VECTRA_EQUITIES_LIVE=trueand restart. Preflight + drift monitor activate automatically.
24-hour session limit
https://localhost:5000; the bot resumes on the next tick.OANDA (FX) — full steps
OANDA is the cleanest of the three — pure HTTPS API, no local daemon, generous practice account.
- 1Sign up for a free practice account at oanda.com/demo-account. Develop against this first; flip live once you trust the workflow.
- 2Go to My Services → Manage API Access and click Generate. Copy the personal access token.
- 3Paste into
.env:OANDA_API_TOKEN+OANDA_ACCOUNT_ID(format001-002-1234567-001). KeepOANDA_PRACTICE=true. - 4For live, open the funded account at oanda.com/eu-en, generate a SEPARATE live token (live + practice tokens are NOT interchangeable), then set
VECTRA_FX_LIVE=trueandOANDA_PRACTICE=false.
Practice ≠ live tokens
Reference
Capital minimums per market
Three numbers per market:
- Broker min — what each broker advertises to open an account. All three are zero today.
- Strategy floor — the smallest balance where preflight will actually fire orders. Below this, every signal is skipped as below-min-notional and the strategy is paper-only in practice.
- Recommended — comfortable operation amount. Lets the full concurrent universe run without one losing slot eating the buffer.
| Market | Strategy | Broker min | Strategy floor | Recommended | Why (strategy floor) |
|---|---|---|---|---|---|
| Crypto (MEXC) | vol-mom shared | $0 | $30 | $100+ | $5 min notional ÷ (0.085 × 3.0 effective leverage) ≈ $19.6, ÷ 0.7 preflight buffer → $30. |
| Equities (IBKR) | vol-mom sp100 | $0 | $200 | $1,000+ | 5 concurrent slots × ~$40 minimum per slot = $200, so the $1 commission floor stays ≤ 2.5% round-trip. |
| FX (OANDA) | xsec trend 5× | $0 | $50 | $500+ | $50 / 7 pairs × 5× lev, top-2 long: ~$35 margin/slot, comfortably above OANDA's 1-unit (~$1) floor. |
| Combined | all three | — | $280 | $2,000+ | Sum of strategy floors. Each broker funded separately (you can't pool the equity across brokers). |
Smaller amounts work — they just leave money on the table
Step 2
Bot configuration (.env)
The bot reads everything from .env at process start. Below is the operator-relevant subset; full list lives in rust/crates/vectra-config/src/lib.rs.
| Key | Required | Default | What it does |
|---|---|---|---|
POSTGRES_DSN | yes | postgresql://vectra:vectra@localhost:5432/vectra | Live Postgres connection string. Trade outcomes, equity snapshots, command audit. |
REDIS_URL | yes | redis://localhost:6379/0 | Redis instance for the state-bus (state:* keys consumed by every operator surface). |
DUCKDB_PATH | yes | ./data/vectra.duckdb | Local DuckDB file for backtest tick storage. |
MEXC_API_KEY | yes (crypto) | — | Trade-only API key from MEXC futures. Generated under Profile → API Management. |
MEXC_API_SECRET | yes (crypto) | — | Companion secret. Shown once at key creation — copy before closing the dialog. |
MEXC_LIVE_SYMBOL | yes | BTC/USDT:USDT | Symbol the live engine trades. Auto-switcher overrides when funding-rate flips elsewhere. |
VECTRA_MEXC_MARGIN_MODE | no | isolated | isolated (default) | cross. Isolated = per-position margin pool. Cross = shared, capital-efficient but riskier. |
VECTRA_EQUITIES_LIVE | no | false | Explicit opt-in to send real orders via IBKR. Token presence alone never auto-trades. |
IBKR_ACCOUNT_ID | when EQUITIES_LIVE=true | — | IBKR account selector (e.g. U1234567). Required when live trading is enabled. |
IBKR_GATEWAY_URL | no | https://localhost:5000/v1/api | Local Client Portal Gateway URL. Change only when the gateway runs on a non-default port. |
VECTRA_FX_LIVE | no | false | Explicit opt-in to send real orders via OANDA. Same gate logic as VECTRA_EQUITIES_LIVE. |
OANDA_API_TOKEN | when FX_LIVE=true | — | OANDA personal access token. Practice and live tokens are NOT interchangeable. |
OANDA_ACCOUNT_ID | when FX_LIVE=true | — | OANDA account selector (e.g. 001-002-1234567-001). |
OANDA_PRACTICE | no | true | true → api-fxpractice.oanda.com. false → live host. Flip explicitly to go live. |
TELEGRAM_BOT_TOKEN | no | — | BotFather token for alerts + slash commands. |
TELEGRAM_CHAT_ID | no | — | Numeric chat ID of the operator. Bot only responds in this chat. |
VECTRA_TELEGRAM_DAILY_SUMMARY_ENABLED | no | true | Daily 22:00 UTC summary push. Set false to silence. |
VECTRA_COMMAND_HMAC_SECRET | yes (dry_live/prod) | — | HMAC secret for Telegram command auth. Must be set for any non-paper environment. |
Never commit .env
.env stays on disk (mode 0600) and is gitignored. Every credential is process-local; logs redact secrets at the formatter level.Step 3
Running the bot
Build a release binary, drop the systemd unit in place, start it. The bot self-restarts on crash; you only restart manually after rebuilds.
# Build release binary (slow once; subsequent builds are incremental)
cd rust
cargo build --release -p vectra-bot
# Install + enable the user systemd unit (one-time)
mkdir -p ~/.config/systemd/user
cp -n configs/vectra-bot-drylive.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now vectra-bot-drylive
# Tail the live logs
journalctl --user -u vectra-bot-drylive -f --no-pager
# After code edits — rebuild + restart in one shot
worker-bot-rebuild-restartbashThe worker script worker-bot-rebuild-restart lives in ~/.local/bin/ on the deployment host — it runs cargo build --release and systemctl --user restart atomically.
Step 4
Operator surfaces
Three places to watch the bot. All three render the same underlying state — you can verify drift between them.
Web dashboard
The richest surface, mounted at /app. Market tabs switch between Combined / Crypto / Equities / FX:
- Combined — live-style aggregated view: broker status grid, per-market KPI cards, combined equity rollup, merged recent trades, cross-market correlation.
- Crypto — full live MEXC dashboard: equity pulse, vol-mom candidate funnel, breadth meter, live positions.
- Equities / FX — paper-ready snapshot with shadow runner state. Flips to live data the moment the env flag is set.
Settings → Brokers tab gives a one-glance status across all three brokers (equity, free margin, fills, drift count).
Telegram bot
Mobile-friendly. Set TELEGRAM_BOT_TOKEN + TELEGRAM_CHAT_ID then DM the bot.
/balance— equity + free margin per broker/trades— last 10 fills across markets/markets— per-market state + broker block/risk— DD vs gate-3 cap, daily-loss usage/halt— pause new entries (positions held)/resume— un-pause/alerts— manage push subscriptions
TUI
Terminal dashboard for SSH sessions. Run vectra-cli tui from the bot host. Hotkeys: q overview, d dashboard, p positions, r risk, j orders. Mobile-Mosh friendly at 80×20.
Step 5
Going live per market
Live trading is explicit opt-in per market. Token / account presence alone never auto-trades — the env flag is the authorization signal.
# Crypto goes live by default once MEXC_API_KEY + MEXC_API_SECRET are set.
# Flip equities live (after IBKR account + gateway are up):
echo 'VECTRA_EQUITIES_LIVE=true' >> .env
echo 'IBKR_ACCOUNT_ID=U1234567' >> .env
worker-bot-rebuild-restart
# Flip FX live (after OANDA account + token are real, not practice):
echo 'VECTRA_FX_LIVE=true' >> .env
echo 'OANDA_API_TOKEN=...' >> .env
echo 'OANDA_ACCOUNT_ID=001-002-1234567-001' >> .env
echo 'OANDA_PRACTICE=false' >> .env
worker-bot-rebuild-restartbashWhen the bot restarts with a live flag set, two background tasks activate automatically:
- Preflight — every order goes through
vectra-strategy::preflightwith broker-specific defaults. Approves / shrinks / skips before submission. - Drift monitor — every 30s polls broker positions and diffs against the last snapshot. Anything that moved without a matching
place_ordershows up instate:broker_drift_*and on /app/settings → Brokers.
Step 6
Backtests + acceptance gates
The bot ships pre-validated champions per market. Re-running a backtest refreshes the snapshot the dashboard reads — useful after a config tweak or fresh bar data.
# Full 3-market combined backtest. Writes
# data/backtest_cache/ma_*.json + ma_combined.json.
cargo run --release -p vectra-cli -- ma-combined-backtest
# Or trigger from the dashboard:
# /app/backtests → 'Re-run' button (operator-only).bashEach market is scored against a 9-gate acceptance matrix: OOS Sharpe, max DD, walk-forward stability, Bonferroni p-values, fold consistency, etc. A market only flips to LIVE-ready when all gates pass. See methodology for the gate definitions.
Safety
Safety rails
The bot enforces these unconditionally:
- Preflight shrinks or skips orders that would overshoot broker free margin.
- Kill switch halts new entries on daily-loss limit, broker API errors, or reconciliation drift on any market. Open positions held.
- Drift monitor emits an operator-visible warn the moment broker positions and bot state diverge.
- IBKR session lapse halts the equities reconcile batch with a typed
BrokerError::Authrather than silently retrying. - Idempotent client order IDs — re-running a reconcile pass is a broker-side no-op for already-submitted orders.
- Trade-only API keys — withdrawal permission is never enabled. The bot literally cannot move funds.
When things break
Troubleshooting
Bot says PAPER but I set the live flag
- Confirm the flag is in
.env, not just exported in your shell. - For IBKR: check
IBKR_ACCOUNT_IDis also set. Empty account id → silent fallback to paper. - For OANDA: check both
OANDA_API_TOKENANDOANDA_ACCOUNT_IDare set. - Restart with
worker-bot-rebuild-restart— a plainsystemctl restartreuses the old binary. - Check
journalctl --user -u vectra-bot-drylive | grep PAPER— the bot logs its mode at startup.
IBKR gateway won't connect
- Is the Java daemon running?
ps aux | grep clientportal. - Is the session authenticated?
curl -ksX POST https://localhost:5000/v1/api/iserver/auth/status— look forauthenticated:true. - If false, open
https://localhost:5000in a browser and re-auth (24h SSO). - The bot logs
broker auth lapse — halting reconcile batchwhen it detects the lapse.
OANDA 401 on every call
Almost always practice/live token mismatch. Check OANDA_PRACTICE matches the environment the token was generated in. Tokens are NOT interchangeable.
Equity in dashboard doesn't match broker
Check the Brokers tab in /app/settings for drift count. If non-zero, the drift monitor caught it — investigate manually. Bot equity values come from broker.account_summary() refreshed every 30s.
Kill switch fired, how to resume
1) Read the halt reason in state:bot_runtime.halt_reason or via Telegram /risk. 2) Address the root cause (margin top-up, broker outage cleared, etc.). 3) Send /resume via Telegram, or restart the bot.
Reference
Glossary
- Preflight
- Margin-aware order-size check that runs before every broker submission. Approves, shrinks, or skips.
- Drift
- A broker position changed without a matching
place_order. Operator-visible warning. - Reconcile
- The 5-min pass that diffs desired strategy state vs broker positions and emits the minimum orders to converge.
- Kill switch
- Global halt of new entries. Tripped on daily-loss limit, broker error, or reconcile drift. Open positions held.
- OOS Sharpe
- Sharpe measured on out-of-sample walk-forward folds (vs in-sample optimization fits).
- Gate-3 cap
- Acceptance gate that fails any strategy whose worst-fold max DD exceeds the per-market threshold (17.94% crypto).
- Vol-target sizing
- Position notional = vol_target_usd / realized_volatility. Halves position size when realized vol doubles.
- cOID / clientExtensions.id
- IBKR / OANDA idempotency keys. Bot passes a deterministic id so duplicate POSTs are broker-side no-ops.
Ready to wire it up?
Open the guided onboarding stepper — it walks you through picking a broker, generating the key, and pasting the env vars.