mts1b-brokers
Broker adapters: IBKR, Coinbase, Schwab, Moomoo, Tradier, Kraken, paper. All implement
BrokerProtocol.
Repo: github.com/MTS1B/mts1b-brokers
Layer: 2
Depends on: foundation, platform, httpx, ib-insync, ...
Audience: mts1b-oms
What it is
Async adapters that implement mts1b_foundation.protocols.BrokerProtocol for every broker MTS1B supports. The OMS sees one uniform interface — switching brokers is a config change, not a code change.
Supported brokers
| Adapter | Asset classes | Live | Paper | Notes |
|---|---|---|---|---|
paper | all | ✅ | ✅ | Reference impl; in-process fill simulator |
ibkr | equities, options, futures, fx, crypto (PAXOS) | ✅ | ✅ | Client Portal API + IB Gateway |
coinbase | crypto | ✅ | ❌ | Coinbase has no paper sandbox; use paper adapter for paper |
schwab | equities, options | ✅ | ❌ | OAuth2 |
moomoo | equities (US/HK) | ✅ | ✅ | OpenAPI |
tradier | equities, options | ✅ | ✅ | REST |
kraken | crypto | ✅ | ✅ | Kraken sandbox |
Module layout
mts1b_brokers/
├── __init__.py # BROKER_REGISTRY
├── paper/
│ ├── client.py # PaperClient — in-process fill simulator
│ └── fill_engine.py
├── ibkr/
│ ├── client.py # @dataclass IbkrClient(BrokerProtocol)
│ ├── connection.py # IB Gateway / Client Portal session mgmt
│ ├── symbology.py # IBKR-specific contract resolution
│ └── normalizer.py # IBKR fills → foundation.Fill
├── coinbase/
│ ├── client.py
│ ├── auth.py # ECDSA signing for Coinbase Advanced
│ └── websocket.py
├── schwab/...
├── moomoo/...
├── tradier/...
└── kraken/...
Top API
BrokerProtocol
@runtime_checkable
class BrokerProtocol(Protocol):
@property
def name(self) -> str: ...
async def submit(self, order: Order) -> Order: ...
async def cancel(self, order_id: str) -> bool: ...
async def get_open_orders(self) -> list[Order]: ...
async def get_positions(self) -> list[Position]: ...
async def stream_fills(self) -> AsyncIterator[Fill]: ...
Defined in mts1b-foundation. Every adapter in this repo implements it.
BROKER_REGISTRY
from mts1b_brokers import BROKER_REGISTRY
# Map name → adapter class
{
"paper": PaperClient,
"ibkr": IbkrClient,
"coinbase": CoinbaseClient,
"schwab": SchwabClient,
"moomoo": MoomooClient,
"tradier": TradierClient,
"kraken": KrakenClient,
}
cls = BROKER_REGISTRY["coinbase"]
async with cls(api_key="...", api_secret="...") as broker:
positions = await broker.get_positions()
mts1b-oms uses the registry to instantiate adapters from mts1b.config.
IbkrClient
@dataclass
class IbkrClient(BrokerProtocol):
"""IBKR via Client Portal API + IB Gateway."""
name: str = "ibkr"
gateway_host: str = "127.0.0.1"
gateway_port: int = 5000
account_id: str = ""
paper: bool = False
extended_hours_default: bool = False
API source: adapters/src/mts/adapters/brokers/ibkr/client.py:IbkrClient.
Quirks documented:
- IBKR PAXOS crypto uses bare 3-char symbols (
BTCnotBTC-USD) — adapter normalizes - Extended-hours orders need
extended_hours=True+order_type=limit(IBKR rejects MARKET outside RTH) - Crypto coverage on PAXOS: BTC/ETH/LTC/BCH/SOL/ADA/AVAX/MATIC/DOT/XRP/DOGE/SHIB
- IBKR overnight session (IBEOS) covers ~10k+ symbols 20:00 ET → 03:50 ET
CoinbaseClient
@dataclass
class CoinbaseClient(BrokerProtocol):
"""Coinbase Advanced Trade via REST + WebSocket."""
name: str = "coinbase"
api_key: str = ""
api_secret: str = "" # ECDSA private key (PEM)
passphrase: str = ""
sandbox: bool = False # Coinbase Advanced has no sandbox; flag kept for symmetry
API source: adapters/src/mts/adapters/brokers/coinbase/client.py:CoinbaseClient.
Quirks:
- No paper sandbox (deprecated 2023). Use the
paperadapter for paper. - Quote endpoint format is "rates[QUOTE] = QUOTE-per-base" — adapter handles this correctly (fixed bug 2026-05-05; previously inverted, producing absurd quantities).
- Fees vary by volume tier; adapter pulls live fee schedule on connect.
PaperClient
@dataclass
class PaperClient(BrokerProtocol):
"""In-process fill simulator. Useful for paper trading and tests."""
name: str = "paper"
fill_model: str = "midpoint" # midpoint | nbbo | slippage_model
fee_bps_per_side: float = 5.0
slippage_bps: float = 2.0
fill_delay_ms: int = 100
Backed by live quotes from mts1b-marketdata. Used when you want realistic execution simulation without a real venue.
Adapter contract
Every adapter MUST:
| Capability | Why |
|---|---|
Implement BrokerProtocol | The interface contract |
Pass isinstance(x, BrokerProtocol) | runtime + mypy verify |
Idempotent submit (dedup on idempotency_key) | OMS retries are safe |
| Symbol normalization | Caller passes BTC-USD, adapter handles native format |
| Fee normalization | Adapter emits fees in account base currency |
Retries via mts1b-platform/retry | Transient HTTP errors |
Rate-limit via mts1b-platform/ratelimit | Respect venue limits |
Log redaction via mts1b-platform/security/redact | Never log raw API responses |
Live tests gated behind pytest.mark.live | CI is hermetic |
| Capabilities matrix in docs | What works, what doesn't |
Roadmap
| Version | Items |
|---|---|
| 0.1 (Wave 1) | 7 adapters: paper, ibkr, coinbase, schwab, moomoo, tradier, kraken |
| 0.2 (Wave 2) | Binance Spot + Futures, Bybit, OKX |
| 0.3 (Wave 2) | Robinhood Crypto, Webull |
| 0.4 (Wave 3) | FIX gateway (community plugin via mts1b-pluginsdk preferred) |
| 1.0 (LTS) | Stable interface; new brokers as plugins |
See also
- Foundation BrokerProtocol — the contract
- Tutorial: Add a broker — write a new adapter
mts1b-oms— primary consumer