Vectra

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 --user unit; macOS works for local dev, production assumes Linux).
  • Rust toolchain 1.83+ via rustup; mold linker recommended.
  • PostgreSQL 16 and Redis 7 reachable from the bot host.
  • Node.js 20+ and npm if 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

The bot is ARM64-friendly. A Hetzner CAX21 (4× ARM, 8 GB) runs a complete deployment: bot + Postgres + Redis + dashboard + IBKR gateway daemon.

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

LIVE
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_KEY
  • MEXC_API_SECRET
  • MEXC_LIVE_SYMBOL
Open MEXC account

Interactive Brokers (equities)

US sp100 · vol-mom shared-equity · paper-ready, opt-in

PAPER-READY
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_LIVE
  • IBKR_ACCOUNT_ID
  • IBKR_GATEWAY_URL
Open IBKR Pro account (NL/EU)

OANDA (FX majors)

fx7 universe · xsec trend, top-2 long @ 5× leverage · paper-ready, opt-in

PAPER-READY
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_LIVE
  • OANDA_API_TOKEN
  • OANDA_ACCOUNT_ID
  • OANDA_PRACTICE
Open OANDA EU account (Practice or Live)

MEXC (crypto) — full steps

  1. 1
    Open mexc.com → Profile → API Management.
  2. 2
    Click Create API. Toggle ON: Read account information + Trade futures. Leave Withdrawal and Spot trading OFF.
  3. 3
    Optionally bind the key to your server's IP (the bot prints its outbound IP on startup).
  4. 4
    Copy the key and secret — the secret is shown once.
  5. 5
    Paste into .env as MEXC_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.

  1. 1
    Open an IBKR Pro account (NL/EU residents → IB Ireland Ltd). Complete KYC. Aim for $1,000+ funded.
  2. 2
    Download the Client Portal Gateway from interactivebrokers.com/api, extract, and run ./bin/run.sh root/conf.yaml.
  3. 3
    Open https://localhost:5000 in a browser and log in with your IBKR credentials. The gateway holds the session.
  4. 4
    Set IBKR_ACCOUNT_ID in .env (e.g. U1234567).
  5. 5
    When ready to flip live, also set VECTRA_EQUITIES_LIVE=true and restart. Preflight + drift monitor activate automatically.

24-hour session limit

IBKR's gateway times the session out after ~24h. The bot emits a typed Auth error on the next order attempt and halts the reconcile batch with an operator-visible warn. Re-auth interactively at 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.

  1. 1
    Sign up for a free practice account at oanda.com/demo-account. Develop against this first; flip live once you trust the workflow.
  2. 2
    Go to My Services → Manage API Access and click Generate. Copy the personal access token.
  3. 3
    Paste into .env: OANDA_API_TOKEN + OANDA_ACCOUNT_ID (format 001-002-1234567-001). Keep OANDA_PRACTICE=true.
  4. 4
    For 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=true and OANDA_PRACTICE=false.

Practice ≠ live tokens

A practice token returns 401 against the live host (and vice-versa). The bot pins the host at startup so a leaked token from one environment can't be replayed against the other.

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.
MarketStrategyBroker minStrategy floorRecommendedWhy (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.
Combinedall 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

Vectra runs in PAPER mode by default on the live deployment (one operator) at ~$23 of live MEXC equity. Strategies still evaluate signals correctly; preflight just skips most as below-min-notional. Use the recommended starters when you want every signal to actually trade.

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.

KeyRequiredDefaultWhat it does
POSTGRES_DSNyespostgresql://vectra:vectra@localhost:5432/vectraLive Postgres connection string. Trade outcomes, equity snapshots, command audit.
REDIS_URLyesredis://localhost:6379/0Redis instance for the state-bus (state:* keys consumed by every operator surface).
DUCKDB_PATHyes./data/vectra.duckdbLocal DuckDB file for backtest tick storage.
MEXC_API_KEYyes (crypto)Trade-only API key from MEXC futures. Generated under Profile → API Management.
MEXC_API_SECRETyes (crypto)Companion secret. Shown once at key creation — copy before closing the dialog.
MEXC_LIVE_SYMBOLyesBTC/USDT:USDTSymbol the live engine trades. Auto-switcher overrides when funding-rate flips elsewhere.
VECTRA_MEXC_MARGIN_MODEnoisolatedisolated (default) | cross. Isolated = per-position margin pool. Cross = shared, capital-efficient but riskier.
VECTRA_EQUITIES_LIVEnofalseExplicit opt-in to send real orders via IBKR. Token presence alone never auto-trades.
IBKR_ACCOUNT_IDwhen EQUITIES_LIVE=trueIBKR account selector (e.g. U1234567). Required when live trading is enabled.
IBKR_GATEWAY_URLnohttps://localhost:5000/v1/apiLocal Client Portal Gateway URL. Change only when the gateway runs on a non-default port.
VECTRA_FX_LIVEnofalseExplicit opt-in to send real orders via OANDA. Same gate logic as VECTRA_EQUITIES_LIVE.
OANDA_API_TOKENwhen FX_LIVE=trueOANDA personal access token. Practice and live tokens are NOT interchangeable.
OANDA_ACCOUNT_IDwhen FX_LIVE=trueOANDA account selector (e.g. 001-002-1234567-001).
OANDA_PRACTICEnotruetrue → api-fxpractice.oanda.com. false → live host. Flip explicitly to go live.
TELEGRAM_BOT_TOKENnoBotFather token for alerts + slash commands.
TELEGRAM_CHAT_IDnoNumeric chat ID of the operator. Bot only responds in this chat.
VECTRA_TELEGRAM_DAILY_SUMMARY_ENABLEDnotrueDaily 22:00 UTC summary push. Set false to silence.
VECTRA_COMMAND_HMAC_SECRETyes (dry_live/prod)HMAC secret for Telegram command auth. Must be set for any non-paper environment.

Never commit .env

The deployment .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-restart
bash

The 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-restart
bash

When the bot restarts with a live flag set, two background tasks activate automatically:

  • Preflight — every order goes through vectra-strategy::preflight with 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_order shows up in state: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).
bash

Each 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::Auth rather 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

  1. Confirm the flag is in .env, not just exported in your shell.
  2. For IBKR: check IBKR_ACCOUNT_ID is also set. Empty account id → silent fallback to paper.
  3. For OANDA: check both OANDA_API_TOKEN AND OANDA_ACCOUNT_ID are set.
  4. Restart with worker-bot-rebuild-restart — a plain systemctl restart reuses the old binary.
  5. Check journalctl --user -u vectra-bot-drylive | grep PAPER — the bot logs its mode at startup.

IBKR gateway won't connect

  1. Is the Java daemon running? ps aux | grep clientportal.
  2. Is the session authenticated? curl -ksX POST https://localhost:5000/v1/api/iserver/auth/status — look for authenticated:true.
  3. If false, open https://localhost:5000 in a browser and re-auth (24h SSO).
  4. The bot logs broker auth lapse — halting reconcile batch when 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.