GitHubBlog

Search Documentation

Search for a page in the docs

Accounts & Brokers

Status: active development. The UTA stack and trading workflow are evolving fast and known to be unstable across releases. Treat live trading as opt-in only after validating with paper / demo accounts. If you hit a bug or unexpected behaviour, please open an issue at github.com/TraderAlice/OpenAlice/issues so it can be reproduced and fixed.

Every trading account in OpenAlice is a Unified Trading Account (UTA) — a self-contained entity that bundles a broker connection, a git-like operation history, a guard pipeline, and a snapshot scheduler.

AI and the frontend interact with UTAs exclusively. Brokers are internal implementation details.

Preset-based configuration

UTA config is preset-driven: you pick one of the bundled presets (OKX, Bybit, Alpaca, IBKR, ...) and fill in the credentials it asks for. The preset itself decides which engine to use, which fields to render, which fields to mask as passwords, and how to translate the form values into the engine's internal config.

This replaced the older {type, brokerConfig} schema. Existing accounts.json files in the old shape are auto-migrated on first startup (see Migration below).

[
  {
    "id": "alpaca-paper",
    "label": "Alpaca Paper",
    "presetId": "alpaca",
    "enabled": true,
    "guards": [
      { "type": "max-position-size", "options": { "maxPercentOfEquity": 20 } }
    ],
    "presetConfig": {
      "mode": "paper",
      "apiKey": "your-key",
      "apiSecret": "your-secret"
    }
  }
]
FieldDescription
idUnique identifier, used in source parameters and as the aliceId prefix
labelDisplay name (optional, defaults to id)
presetIdOne of the catalog ids (okx, bybit, hyperliquid, bitget, ccxt-custom, alpaca, ibkr-tws)
enabledSet to false to skip loading this account
guardsArray of guard configs (type + options)
presetConfigUser-filled form values, validated against the preset's own Zod schema

Built-in presets

The preset catalog is the single source of truth. Each preset ships with a Zod schema, an optional Mode dropdown (Live / Demo / Testnet / Paper / etc.), per-field password masking, and a toEngineConfig() translator.

Preset idEngineModesNotes
okxCCXTLive, Demo TradingDemo mode requires demo-specific API keys generated from OKX's demo console — live keys are rejected
bybitCCXTLive, Testnet, Demo TradingThree environments: Testnet (separate domain, fake market data), Demo Trading (real market data, simulated fills), Live
hyperliquidCCXTMainnet, TestnetWallet-based auth — generate a dedicated API wallet at app.hyperliquid.xyz/API, never paste your main wallet's key
bitgetCCXTLive, Demo TradingRequires API key + secret + passphrase; demo routes to simulated matching on prod domain
ccxt-customCCXTPower-user escape hatch for any of CCXT's 100+ exchanges that doesn't have a dedicated preset; sandbox/demoTrading semantics vary per exchange
alpacaAlpacaPaper, LivePaper and Live use separate API keys generated from the matching dashboard
ibkr-twsIBKR(port-driven)Auth via local TWS / IB Gateway socket — no API keys here. Default ports: TWS 7496/7497, Gateway 4001/4002

CCXT-engine presets all map to the same CcxtBroker class — the preset just picks the right exchange id and translates its mode flag (sandbox / demoTrading) for that exchange's quirks. Adding a new exchange-specific preset is a one-file edit; see Custom Brokers.

The new-account wizard

The Web UI's Trading page has a multi-step wizard that walks you through creating a UTA without hand-editing JSON:

  1. Pick — choose a preset from the catalog (grouped by category: crypto / securities / custom).
  2. Config — fill in the fields rendered from that preset's Zod schema. Password fields are masked as you type. Mode selectors render as a dropdown when the preset declares them.
  3. Test — the wizard POSTs to /api/trading/config/test-connection, which actually tries to connect to the broker with the supplied credentials. Only after a successful test does the Save button enable; the validated config is then written to accounts.json and the new UTA reconnects.

This flow catches three common mistakes at creation time: bad keys, wrong region/environment (e.g., live keys against demo endpoint), and missing credentials a particular exchange requires.

You can also create or edit a UTA programmatically by writing to accounts.json directly — the wizard is a convenience layer, not a requirement.

Brokers

The presets cover three engine implementations. The engine is invisible to the AI and the UI — both only talk to UTAs.

CCXT (Crypto)

Connects to 100+ crypto exchanges via the CCXT library. The OKX, Bybit, Hyperliquid, and Bitget presets all use this engine; ccxt-custom exposes the raw credential field set for any other exchange.

Spot synthesis. CCXT brokers now synthesize spot holdings into Position records: balances are pulled via fetchBalance, paired with the preferred quote market (USDT > USDC > USD) for live pricing, and merged with derivatives positions in a single getPositions() response. Stablecoins (USDT/USDC) are normalized to USD. Before this, spot accounts looked empty in the AI's view of positions; now they're first-class.

Exchange-specific overrides. Most exchanges work out of the box. Two presets carry quirks worth flagging:

  • Bybit — three modes (Live / Testnet / Demo Trading). Testnet and Demo Trading each need their own API keys from the matching environment.
  • Hyperliquid — uses wallet signatures, not API keys. The "wallet address" can be either the main vault wallet or the API wallet; the "private key" must be the API wallet's. Generate one at app.hyperliquid.xyz/API.

Alpaca (US Equities)

Commission-free US equities and ETFs with fractional share support via Alpaca. The alpaca preset offers a Paper / Live mode toggle. Paper accounts are free and provide realistic simulation with delayed market data; Paper and Live use separate API keys.

IBKR (Interactive Brokers)

Professional-grade trading via TWS (Trader Workstation) or IB Gateway. Supports stocks, options, futures, and bonds. The ibkr-tws preset connects to a local TWS/Gateway socket — there are no API keys, just host/port/clientId and an optional accountId.

Default ports:

ModeTWSIB Gateway
Paper74974002
Live74964001

IBKR requires TWS or IB Gateway running locally with "Enable ActiveX and Socket Clients" turned on (File → Global Configuration → API → Settings).

Contract Identity — aliceId

Every contract in OpenAlice has an aliceId in the format accountId|nativeKey:

alpaca-paper|AAPL          → AAPL on Alpaca paper account
bybit-demo|BTC/USDT:USDT   → BTC perpetual on Bybit demo
ibkr-live|265598            → AAPL by conId on IBKR

The nativeKey part is broker-specific:

  • Alpaca: ticker symbol (AAPL)
  • CCXT: unified symbol (BTC/USDT:USDT)
  • IBKR: conId (numeric contract identifier)

All trading tools accept aliceId to unambiguously identify which contract on which account. Use searchContracts (or the cross-UTA search described below) to discover available contracts.

Contract Search

Contract discovery is a first-class capability that runs across every configured UTA in one query. The broker side has two strategies:

StrategyBrokersHow it works
EnumeratingCatalogAlpaca, CCXT, MockLoads the full market list at startup into memory; refreshed every 6h
SearchingCatalogIBKRDelegates to reqMatchingSymbols server-side — no local cache

A central fuzzy ranker scores hits (exact 100 → prefix 80 → name boundary 70 → full word 50 → substring 30 → quote-currency 20). Same score keeps the broker's original order, so CCXT's liquidity-sorted results stay liquidity-sorted.

A normalization rule set translates data-vendor symbols (e.g., yfinance BTCUSDT) into broker-side patterns (BTC) before searching, so a single market panel symbol can resolve to tradeable contracts on every exchange that lists it.

HTTP route (used by the frontend's market workbench):

GET /api/trading/contracts/search?pattern=AAPL&assetClass=equity

assetClass is a hint: equity, crypto, currency, commodity, or unknown (default). For crypto, the rule set strips the quote suffix so a search for BTCUSDT matches BTC/USDT on every exchange.

The Web UI's market analysis panels show a Tradeable Contracts card with the top results — clicking one jumps to the corresponding UTA's order entry surface.

Catalog refresh cron. Every 6 hours the engine calls refreshCatalog() on every UTA. For EnumeratingCatalog brokers this re-pulls the market list; for IBKR (SearchingCatalog) it's a no-op. Newly listed assets surface, delisted ones drop. This is an internal timer, separate from user-defined cron jobs.

Multiple Accounts

You can run multiple accounts simultaneously — even across different brokers and presets:

[
  { "id": "alpaca-paper", "presetId": "alpaca", "presetConfig": { "mode": "paper", ... } },
  { "id": "bybit-demo", "presetId": "bybit", "presetConfig": { "mode": "demo", ... } },
  { "id": "okx-live", "presetId": "okx", "presetConfig": { "mode": "live", ... } }
]

Each account gets its own UTA with independent git history, guards, and snapshots. Tools that accept a source parameter let you target specific accounts or query all at once.

Account Lifecycle

The AccountManager manages UTA lifecycle:

  1. Init — Creates a UTA from config, starts async broker connection
  2. Connect — Broker initializes and verifies credentials
  3. Healthy — Broker responding normally
  4. Degraded — 3+ consecutive failures, still accepting requests
  5. Offline — 6+ failures, auto-recovery starts (exponential backoff)
  6. Disabled — Permanent error (bad credentials), requires manual fix

Health transitions emit events and are visible in the Web UI. The Web UI also exposes a per-UTA detail page at /uta/:id with positions, orders, market clock, and a manual order entry form that bypasses the AI for direct trade entry.

Migration

If your accounts.json is in the old {type, brokerConfig} shape, the engine auto-migrates it on the first startup that sees the new code:

  1. The original file is backed up to accounts.json.backup-pre-preset (so a bad migration is never destructive).
  2. Each legacy record is mapped to the closest preset:
    • type: "ccxt" with exchange: "okx"presetId: "okx", presetConfig.mode derived from sandbox/demoTrading
    • type: "alpaca"presetId: "alpaca", presetConfig.mode from paper
    • type: "ibkr"presetId: "ibkr-tws", host/port/clientId carried over
    • Unknown CCXT exchanges fall through to ccxt-custom
  3. The translated records are validated against the new schema and written back to accounts.json.
  4. Records that can't be mapped (unknown engine, missing exchange) are skipped and logged — recreate them via the wizard.

This migration block is scheduled to remove before v1.0.

Runtime Management

Accounts can be added, enabled, or disabled at runtime:

  • Use the Web UI's account management panel (add via wizard, edit, enable/disable, reconnect — all hot)
  • Or edit accounts.json directly and call /api/trading/uta/:id/reconnect to apply

The Web UI is the recommended path because the wizard's connection test catches misconfigurations before they hit disk.