Skip to content

execution: implement IbkrAdapter venue adapter#14

Merged
marwinsteiner merged 7 commits intomainfrom
phase-5/ibkr-adapter
Feb 22, 2026
Merged

execution: implement IbkrAdapter venue adapter#14
marwinsteiner merged 7 commits intomainfrom
phase-5/ibkr-adapter

Conversation

@marwinsteiner
Copy link
Copy Markdown
Owner

Summary

Implements IbkrAdapter(VenueAdapter) -- a thin translation layer between sysls's normalized order types and Interactive Brokers TWS/Gateway API via the ib_async library (successor to ib_insync).

What's included

  • Connect/disconnect lifecycle: async connectAsync() with import guard, host/port/clientId/account params, SyslsConnectionError wrapping
  • Contract building (_to_ib_contract): maps sysls Instrument to ib_async Stock, Option (with space-separated symbol parsing), Future, Forex
  • Order building (_to_ib_order): maps OrderRequest to MarketOrder, LimitOrder, StopOrder, StopLimitOrder with price validation
  • submit_order: calls placeOrder via asyncio.to_thread, emits OrderAccepted on bus
  • cancel_order: looks up trade by orderId in openTrades(), calls cancelOrder, emits OrderCancelled
  • get_order_status: searches trades() by orderId, maps IB status strings via _IB_STATUS_MAP
  • get_positions: calls ib.positions(), builds Instrument from contract (STK/OPT/FUT/CASH), skips zeros
  • get_balances: calls ib.accountValues(), filters CashBalance tags, parses Decimal, excludes BASE
  • Error wrapping (_wrap_ib_error): ConnectionError/OSError/TimeoutError -> SyslsConnectionError, ValueError -> OrderError, others -> VenueError
  • pyproject.toml: added ib_async>=1.0 as optional dep (sysls[ibkr]) and dev dependency

Changes

  • src/sysls/execution/venues/ibkr.py -- 536 lines (new)
  • tests/execution/test_ibkr.py -- 1029 lines (new), 61 tests
  • pyproject.toml -- 2 lines changed

Commits (6 atomic)

  1. execution: add IBKR adapter wireframe with stubs
  2. execution: implement IbkrAdapter connect/disconnect lifecycle
  3. execution: implement IBKR contract and order building helpers
  4. execution: implement IBKR submit_order and cancel_order
  5. execution: implement IBKR get_positions and get_balances
  6. execution: add IBKR error wrapping and edge case tests

Test plan

  • All 61 IBKR adapter tests pass (uv run pytest tests/execution/test_ibkr.py -v)
  • Full test suite passes: 749 passed, 10 skipped (Windows-related, pre-existing)
  • ruff check src/ tests/ -- clean
  • ruff format --check src/ tests/ -- clean
  • All tests use mocked ib_async -- no real TWS/Gateway connection needed
  • Architect review of contract/order mapping logic
  • Architect review of error wrapping strategy

Add IbkrAdapter class with NotImplementedError stubs for all VenueAdapter
ABC methods. Includes status mapping (_IB_STATUS_MAP), _require_ib helper,
properties (name, is_connected, supported_order_types), and test wireframe
with 14 passing tests covering properties and status mapping. Add ib_async
as optional dependency (sysls[ibkr]) and dev dependency.
Implement connect() with ib_async import guard (raises
SyslsConnectionError if not installed), async connectAsync call with
host/port/clientId/account params, and error wrapping for connection
failures. Implement disconnect() that calls ib.disconnect() and clears
state, safe to call multiple times. 7 new tests covering connect
success, account param, import error, connection failure, disconnect,
disconnect-when-not-connected, and async context manager.
Implement _to_ib_contract() mapping sysls Instrument to ib_async
Contract subclasses: Stock (equity), Option (with space-separated
symbol parsing for underlying/expiry/strike/right), Future, and Forex
(CRYPTO_SPOT, uses IDEALPRO exchange). Unsupported asset classes raise
OrderError.

Implement _to_ib_order() mapping OrderRequest to ib_async Order
subclasses: MarketOrder, LimitOrder, StopOrder, StopLimitOrder. Validates
required price fields and raises OrderError for missing values.

15 new tests covering all contract types, order types, edge cases,
and error conditions.
Implement submit_order() that builds ib_async Contract + Order from sysls
types, calls placeOrder via asyncio.to_thread, emits OrderAccepted on the
bus, and returns the venue order ID.

Implement cancel_order() that looks up the trade by orderId in
openTrades(), calls cancelOrder via asyncio.to_thread, and emits
OrderCancelled. Raises OrderError if the order is not found.

Implement get_order_status() that searches trades() by orderId and maps
the IB status string.

Add _wrap_ib_error() helper mapping Python exception types to sysls
exceptions (ConnectionError/OSError -> SyslsConnectionError, ValueError
-> OrderError, others -> VenueError).

7 new tests covering submit, submit error, not-connected, cancel,
cancel-not-found, get_order_status found/not-found.
Implement get_positions() that calls ib.positions() via asyncio.to_thread,
builds Instrument from each contract using _build_instrument_from_contract,
and skips zero-quantity positions. Supports STK, OPT, FUT, CASH contract
types mapped to sysls AssetClass.

Implement get_balances() that calls ib.accountValues() via asyncio.to_thread,
filters for CashBalance tags (excluding BASE currency), parses Decimal
values, and skips zeros.

Add _build_instrument_from_contract() helper with _SEC_TYPE_MAP mapping
IB secType strings to AssetClass. Extracts multiplier from contract.

11 new tests covering instrument building (5 contract types), positions
(empty, long, short, skip-zero), and balances (filtered, empty).
Add comprehensive error wrapping tests covering all exception type
mappings: ConnectionError/OSError/TimeoutError -> SyslsConnectionError,
ValueError -> OrderError, RuntimeError -> VenueError. Add edge case
tests for invalid Decimal parsing in get_balances() and multiple
instruments in get_positions(). Total: 61 tests, all passing.
@marwinsteiner marwinsteiner merged commit c89324f into main Feb 22, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant