GitHubBlog

Search Documentation

Search for a page in the docs

Unified Trading Account

The Unified Trading Account (UTA) is OpenAlice's core business entity for trading. Every broker connection is wrapped in a UTA before it's exposed to the AI or the frontend. Understanding why this abstraction exists is key to understanding how OpenAlice trades.

The Problem

Without UTA, the AI would need to:

  • Know which broker API to call and how each one differs
  • Track order history manually across different exchange formats
  • Implement safety checks separately for each broker
  • Manage connection health, reconnection, and error recovery
  • Handle the gap between order submission and fill differently per exchange

Each broker has different APIs, different order models, different error semantics. Alpaca returns order IDs as strings, IBKR uses numeric IDs with callback-based state machines, CCXT has yet another model. Exposing this complexity to the AI would make every trading interaction fragile and broker-specific.

The Solution

A UTA wraps all of this into a single, uniform entity:

┌─────────────────────────────────────────────┐
│  Unified Trading Account                    │
│                                             │
│  ┌──────────┐  ┌───────────┐  ┌──────────┐ │
│  │  IBroker  │  │ TradingGit│  │  Guards  │ │
│  │ (Alpaca,  │  │ stage →   │  │ pre-exec │ │
│  │  CCXT,    │  │ commit →  │  │ safety   │ │
│  │  IBKR)    │  │ push      │  │ checks   │ │
│  └──────────┘  └───────────┘  └──────────┘ │
│  ┌──────────┐  ┌───────────┐               │
│  │ Snapshots│  │  Health   │               │
│  │ periodic │  │ tracking  │               │
│  │ capture  │  │ + recovery│               │
│  └──────────┘  └───────────┘               │
└─────────────────────────────────────────────┘

The AI sees one interface. It doesn't know or care whether the broker underneath is Alpaca, Bybit, or Interactive Brokers. It calls the same tools, follows the same workflow, gets the same response format.

What a UTA Owns

Broker Connection (IBroker)

An abstract interface implemented by every broker. The UTA delegates all broker-specific operations through this interface — account queries, order placement, contract search, market data. The broker is an internal implementation detail that never leaks to the AI.

All types (Contract, Order, Execution, OrderState) come from IBKR's type system via @traderalice/ibkr. Even Alpaca and CCXT brokers map their native types into this unified format. One type system across all brokers.

Operation History (TradingGit)

Every trade follows a git-like workflow: stage operations, commit with a message, push to execute. The UTA owns its own TradingGit instance — each account has independent commit history, staging area, and head pointer.

Think of multiple UTAs as a monorepo with independent histories. Account A's trading decisions don't pollute account B's log.

Guard Pipeline

Guards are pre-execution safety checks configured per account. When the UTA pushes a commit, the guard pipeline evaluates each operation against current positions and account state before anything reaches the broker.

Different accounts can have different risk rules. Your paper account might have no guards; your live account might enforce position size limits and cooldowns.

Snapshot Scheduler

Snapshots capture account state periodically and after key events (post-push, post-reject). The UTA hooks into the snapshot service to trigger captures at the right moments.

Health Tracking

The UTA monitors broker connection health with three states:

StateConsecutive FailuresBehavior
Healthy0-2Normal operation
Degraded3-5Still accepting requests, warnings logged
Offline6+Auto-recovery with exponential backoff (5s → 60s)

Transient errors (network, timeout) trigger automatic reconnection. Permanent errors (bad credentials, invalid config) disable the account — manual fix required.

The aliceId Convention

Every contract gets an aliceId — a composite key in the format accountId|nativeKey:

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

The UTA stamps aliceId on every contract returned by the broker. This lets the AI unambiguously reference "AAPL on my Alpaca paper account" vs "AAPL on my IBKR live account" — same symbol, different accounts, different aliceIds.

AccountManager

The AccountManager sits above UTAs and manages the full lifecycle:

  • Init — Creates a UTA from config, kicks off async broker connection
  • Resolve — Routes source parameters to the right UTA(s). Accepts account ID ("alpaca-paper"), broker type ("ccxt"), or omit for all accounts.
  • Enable/Disable — Toggle accounts at runtime
  • Close — Graceful shutdown

The AI tools use manager.resolve(source) to route operations. This one method handles "query all accounts", "query one specific account", and "query all accounts of a type" — the AI doesn't need to know how routing works.

Design Principles

Brokers are internal. The AI never imports broker code, calls broker methods, or handles broker-specific types. Everything goes through UTA's uniform interface.

Each UTA is self-contained. Adding a new account means adding one JSON entry. No wiring, no code changes. The UTA bootstraps everything from config.

Self-registration. Brokers register themselves in the broker registry with their config schema, UI field descriptors, and factory function. Adding a new broker type requires zero changes to the framework — see Custom Brokers.

Fail-safe by default. Health tracking, auto-recovery, guard pipeline, and push approval all exist to prevent bad things from happening. The UTA assumes the worst and requires explicit confirmation for destructive actions.