Skip to main content

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

AdapterAsset classesLivePaperNotes
paperallReference impl; in-process fill simulator
ibkrequities, options, futures, fx, crypto (PAXOS)Client Portal API + IB Gateway
coinbasecryptoCoinbase has no paper sandbox; use paper adapter for paper
schwabequities, optionsOAuth2
moomooequities (US/HK)OpenAPI
tradierequities, optionsREST
krakencryptoKraken 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 (BTC not BTC-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 paper adapter 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:

CapabilityWhy
Implement BrokerProtocolThe interface contract
Pass isinstance(x, BrokerProtocol)runtime + mypy verify
Idempotent submit (dedup on idempotency_key)OMS retries are safe
Symbol normalizationCaller passes BTC-USD, adapter handles native format
Fee normalizationAdapter emits fees in account base currency
Retries via mts1b-platform/retryTransient HTTP errors
Rate-limit via mts1b-platform/ratelimitRespect venue limits
Log redaction via mts1b-platform/security/redactNever log raw API responses
Live tests gated behind pytest.mark.liveCI is hermetic
Capabilities matrix in docsWhat works, what doesn't

Roadmap

VersionItems
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