ADR 039 — NautilusTrader optional adapter boundary
- Status: Accepted (2026-08-10); extraction-ready package seam + facade selector landed (follow-up to PR #353) — Confirmed in field ratification 2026-08-10 (APPROVED)
- Related: ADR 035, ADR 036, ADR 004, wave 3b domain sides (
legacy_sides)
Context
NautilusTrader is a high-fidelity event-driven trading engine used by AlphaSwarm for:
- LiveNode brokerage bridging (
alphaswarm/trading/brokerages/nautilus/) - Optional backtest parity (
alphaswarm/backtest/nautilus_engine.py) - Venue exec/data client packages under
trading/execution/nautilus_adapters/
Upstream licensing is LGPL-family. Tightly coupling Nautilus into always-on core import paths would expand the GPL/LGPL surface of the platform runtime and make the money-plane / API boot path depend on an optional heavy SDK.
AlphaSwarm already treats other venue SDKs (Alpaca, IBKR, Tradier) as optional extras with lazy factories. Nautilus must follow the same posture while remaining a first-class engine bridge — not a stub or afterthought.
Decision
- Adapter only. NautilusTrader is consumed exclusively through optional adapter modules. The platform does not vendor Nautilus source into core.
- Optional extra. Install via
pip install 'alphaswarm[nautilus]'(nautilus-traderinpyproject.tomlextras). Selection throughalphaswarm.integrations.brokerages(registry slugnautilus) or the thin facade entryalphaswarm.facade.brokerage.select_brokerageraisesBrokerProviderUnavailableError(code=provider_unavailable) when the extra is absent. The facade module must not importnautilus_traderat import time. - Lazy imports.
nautilus_trader(and submodules) may be imported only inside adapter methods / factory bodies — never at module top level of always-on packages (alphaswarm/core/**, API boot, config, facade, etc.). - Vendor-neutral wire types. Types crossing the brokerage boundary are
AlphaSwarm domain /
alphaswarm.core.typesonly (OrderRequest,OrderData,PositionData,AccountData, domainOrderSide/PositionSidevialegacy_sides/legacy_map). Nautilus enums never escape the adapter. - CI guard.
scripts/ci/check_nautilus_import_boundary.pyfails if non-allowlisted paths underalphaswarm/importnautilus_trader. The allowlisted adapter home is the package prefixalphaswarm/trading/brokerages/nautilus/. - Dynamic linking posture. Operators who enable the
nautilusextra dynamically link the LGPL library at runtime as a plugin. Core distributions that omit the extra do not ship or load Nautilus. - Extraction-ready layout. The adapter lives as a package
(
nautilus/__init__.pypublic re-exports +nautilus/adapter.pyimplementation +nautilus/EXTRACTION.md). A later sibling cut toalphaswarm_nautilusis a product timing decision; the in-tree layout makes that cut mechanical without changing this boundary contract.
Consequences
- Live submission via Nautilus still requires a wired venue exec client
(
configure_exec_clients/ N-1 gate) — configuration, not a stub. - Creating the sibling git repo / CI matrix for
alphaswarm_nautilusremains deferred; the package seam +EXTRACTION.mdchecklist are the readiness steps. - OrderEvent / OrderTicket event-bus rewrite remains out of scope for this ADR (separate program).
- Docs and design notes that previously left “Nautilus LGPL legal review” open are superseded for the engineering boundary; counsel review of distribution packaging remains a release checklist item.