Skip to main content

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​

  1. Adapter only. NautilusTrader is consumed exclusively through optional adapter modules. The platform does not vendor Nautilus source into core.
  2. Optional extra. Install via pip install 'alphaswarm[nautilus]' (nautilus-trader in pyproject.toml extras). Selection through alphaswarm.integrations.brokerages (registry slug nautilus) or the thin facade entry alphaswarm.facade.brokerage.select_brokerage raises BrokerProviderUnavailableError (code=provider_unavailable) when the extra is absent. The facade module must not import nautilus_trader at import time.
  3. 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.).
  4. Vendor-neutral wire types. Types crossing the brokerage boundary are AlphaSwarm domain / alphaswarm.core.types only (OrderRequest, OrderData, PositionData, AccountData, domain OrderSide / PositionSide via legacy_sides / legacy_map). Nautilus enums never escape the adapter.
  5. CI guard. scripts/ci/check_nautilus_import_boundary.py fails if non-allowlisted paths under alphaswarm/ import nautilus_trader. The allowlisted adapter home is the package prefix alphaswarm/trading/brokerages/nautilus/.
  6. Dynamic linking posture. Operators who enable the nautilus extra dynamically link the LGPL library at runtime as a plugin. Core distributions that omit the extra do not ship or load Nautilus.
  7. Extraction-ready layout. The adapter lives as a package (nautilus/__init__.py public re-exports + nautilus/adapter.py implementation + nautilus/EXTRACTION.md). A later sibling cut to alphaswarm_nautilus is 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_nautilus remains deferred; the package seam + EXTRACTION.md checklist 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.