rust-ibapi
AGENTS.md400 starsChanged 7 days agoMITView on GitHub
rust-ibapi is an unofficial Rust client for the IBKR TWS API, with async and blocking clients. The async client is on by default; features are additive: - cargo build (default features) exposes the async client as client::Client - cargo build --no-default-features --features sync enables only the blocking client - cargo build --no-default-features --features "sync async" enables both; the blocking API lives under client::blocking::Client - main — 6.0 development (6.0.0-dev), the only maintained branch. 5.6.0 was the last 5.x release from…
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 31,774 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository5 other files it gives its agents.
- add-tws-featureSkill
- coverageSkill
- live-probeSkill
- releaseSkill
- shipSkill
The file
We split this AGENTS.md into the rules it gives an agent
2 rules. About 0 are common, 2 are distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 44
All changes go through pull requests.
- Distinctive: line 237
When running examples or tests:
≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
AGENTS.md
This file provides guidance to coding agents when working with code in this repository.
Quick Start
rust-ibapi is an unofficial Rust client for the IBKR TWS API, with async and blocking clients.
The async client is on by default; features are additive:
cargo build(default features) exposes the async client asclient::Clientcargo build --no-default-features --features syncenables only the blocking clientcargo build --no-default-features --features "sync async"enables both; the blocking API lives underclient::blocking::Client
Documentation Index
Getting Started
- Quick Start Guide - Get up and running in minutes
- Examples Guide - Running and writing examples
- Troubleshooting - Common issues and solutions
Core Concepts
- Architecture Overview - System design, components, and module organization
- Feature Flags - Sync vs async modes and feature guards
- API Patterns - Builder patterns, protocol versions, and common patterns
Development
- Rule Nodes - Project conventions as linked nodes; indexed below under Rule index
- Code Style Guidelines - Coding standards and conventions
- Build and Test - Build commands, testing patterns, and CI
- Testing Patterns - Test fixture stratification:
MessageBusStub/MemoryStream/ handshake-replay listener - Integration Tests - Writing tests against a live gateway
- Extending the API - Adding new TWS API functionality
- Wire Facts - Live-verified TWS behavior: what the gateway sends, accepts and rejects
- Blocked Issues - Work waiting on a gateway capture or paper-account setup
Versioning and branches
main— 6.0 development (6.0.0-dev), the only maintained branch. 5.6.0 was the last 5.x release frommain. Breaking changes (consistent naming, idiomatic API, cleanup; see the 6.0 milestone) land here, each with its section indocs/migration-6.0.md.docs/migration-5.0.mdis frozen.v2-stable— frozen. Touch it only to backport a specific named bug on explicit request.
All changes go through pull requests.
Rule index
Detail lives in docs/rules/, one directive per file. Follow the
link when the situation matches — the node carries the mechanics, the bug class, and the
precedents.
Wire protocol — protobuf-only at server_versions::PROTOBUF_REST_MESSAGES_3 (213)
- Writing or modifying a domain decoder → proto-only decoding
- Adding a
ResponseMessageaccessor, or a public API on a proto inbound message type → proto-aware accessors - Typing a
Stringfield as an enum → wire enum typing - Parsing or formatting a date/time, or adding a request option that changes TWS's date
rendering → timezone-free timestamps: send
UTC in a fixed format and parse zone-free forms (epoch,
YYYYMMDD, explicit UTC). Pass zoned text through as aString, and never resolve a zone or assume one - Fixtures and C# don't settle what TWS sends or accepts (a rejection code, an absent-field
default, units) → the
/live-probeskill (.claude/skills/live-probe) - Accepting an order id, minting a request id, or routing by an inbound id → id partition
- Adding a one-shot client method, or passing a processor to a
one_shot_*helper → one-shot narrowing — the(message type, decoder)pair lives on the payload'sProtoPayloadimpl, so the call site names neither
Code structure and style
- Adding a client method, or creating a domain module →
domain module layout; porting a request or field
from the C# client end to end is the
/add-tws-featureskill (.claude/skills/add-tws-feature) - Writing a public function that takes four or more parameters →
param budget — clippy's
too_many_argumentsonly fires at eight, so nothing gates this - Exposing one or two items from an otherwise-private module → narrow re-exports
- About to write a
macro_rules!, or reviewing one → macros last resort - Adding a named builder method that sets an enum, or adding a variant to such an enum → builder enum coverage — exhaustiveness checking does not reach "one setter per variant"
Testing
- Adding a
pub/pub(crate)fn, or checking coverage before opening a PR → coverage floor; measuring or raising it is the/coverageskill (.claude/skills/coverage) - Reviewing a new test, or asserting on captured request bytes → exercise production code
- Building a response test fixture → fixture builders
- Writing a
#[cfg(test)] mod testsblock → sibling test files - Asserting against a version-gated API → derive from constants
- Writing a doc-test that must not compile → pin compile_fail codes
- A function that reads the clock and then branches → clock seams
Sync / async parity
- Touching feature-gated code, or adding a
lib.rsdoctest → feature matrix — note--features syncis not the sync-only build; CI'ssyncleg passes--no-default-features - Reaching for
block_on, or needing a lock inside anasync fn→ no block_on - Adding a public type with distinct sync/async impls → dual-feature types
- Wrapping a tokio channel so both sides look alike → no parity wrappers
- Consuming an async
Subscription<T>→ subscription consumer idiom
Workflow
- About to commit, open, or merge a PR → pre-PR checks —
the integration crate builds are the one local-only gate; CI covers the rest. The full flow
through merge (stacked and fork PRs, CI quirks, merge strategy) is the
/shipskill (.claude/skills/ship) - Reviewing a PR, or about to report a bug, motivation or precedent from reading code → review claims: trace it to a production constructor or check it live first. Fixtures reach states production can't
- Touching
Subscription, a proto encoder/decoder, or any public API shape → integration crate builds — those crates are outsidedefault-members, so every automated gate skips them - Editing a module that still carries old idioms, or a feature needs a shared helper's signature changed → modernize touched modules
- Adding
#[non_exhaustive]/#[must_use], removing apubfield, or narrowing topub(crate)→ restrict after callers - A clippy lint fires locally but not in CI, or you are upgrading Rust → pinned toolchain
- Planning a change, or deferring a follow-up past the PR →
plans and issues —
plans/is untracked scratch for the change at hand; open work goes in GitHub issues, cited as#N
Documentation
- Adding a
pub fn, constructor, or builder entry point → public API examples - Asked to make async docs match sync, or removing an
Option<T>argument → doc parity audit — the doc gap is the trigger, the signature drift underneath is the job - Opening a PR that changes public behavior →
changelog entry — same PR, under
## [Unreleased] - Removing or renaming anything public →
user docs sync —
README.mdand the next major's migration guide ship with the change; their fenced Rust snippets are compiled by nothing - A multi-PR arc concludes (floor reached, major version, sweep finished) →
end-of-arc sweep — one docs-only PR
over
AGENTS.md,docs/rules/,README.mdanddocs/ - Cutting a release or drafting release notes → the
/releaseskill (.claude/skills/release) - Establishing, changing, or retiring a project convention → maintaining the rule graph, below
Rule numbers are retired; resolve an old "rule N" citation via retired rule numbers.
Maintaining the rule graph
Nodes ship in the PR that makes their claim true, not afterwards. Mechanics — frontmatter,
trigger phrasing, clusters, status — are in the rules README. These
have to fire without opening it, because just rules-check validates structure only (the
checks are listed in the header of tools/check-rules-graph.sh) and cannot tell whether a node
is still true.
- Establishing, changing, or retiring a convention → it gets a node and one index line
above, or it is not a convention. Changed: rewrite the directive, extend
precedents. Retired:status: historical, drop the index line, never delete the node — a concluded arc has no trigger, but its reasoning is the expensive part. - Deleting or renaming anything a node might cite →
grep -rn <symbol> docs/rules/in the same PR. A node describing a function that no longer exists reads exactly like one that is correct. - Writing a count or completeness claim — "all N sites", "fully applied", "zero remaining",
"every
pub fn" → run the command that produces it as you write it, and leave the command in the node. Checking that a cited symbol exists does not check a count, and this is the claim class that has been wrong most often — twice in prose warning about invented counts. - Landing a PR that proves, extends, or contradicts a node → append to
precedents:with one line on how it ended. One that became a counter-example is worth more than one that confirmed the rule; record it as such rather than dropping it. - Reading a node that calls its own failure mode silent, unenforced, or ungated → that is a missing gate, not a documentation problem. Make the failure loud; then rewrite the node's claim rather than appending to it, and delete regression tests the gate made impossible.
- Touching
AGENTS.mdordocs/rules/→just rules-checkbefore the PR.
Quick Commands
The pre-PR gate, in full — see pre-PR checks for what each one catches and which of them CI does not run:
# Format code
cargo fmt
# Clippy, one run per feature configuration.
# `--features sync` alone keeps the default async client on; use
# --no-default-features for the genuine sync-only build.
# See docs/rules/parity/feature-matrix.md.
cargo clippy --all-targets -- -D warnings
cargo clippy --all-targets --no-default-features --features sync -- -D warnings
cargo clippy --all-targets --all-features -- -D warnings
# Rustdoc intra-doc links — `cargo test --doc` misses these (CI also runs them).
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --no-default-features --features sync
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features
# Run all tests (one leg per feature configuration)
just test
# Examples are a caller surface `just test` never compiles
cargo build --examples
cargo build --examples --no-default-features --features sync
cargo build --examples --all-featuresSituational — run when the change touches the matching surface:
# Wire surfaces: integration crates are outside default-members, so every
# other gate skips them. See docs/rules/workflow/integration-crate-builds.md.
cargo build -p ibapi-integration-sync --tests
cargo build -p ibapi-integration-async --tests
cargo clippy -p ibapi-integration-sync --tests -- -D warnings
cargo clippy -p ibapi-integration-async --tests -- -D warnings
# AGENTS.md or docs/rules/ — validates the rule graph and its index
just rules-check
# Coverage report, nightly-only. See docs/rules/testing/coverage-floor.md.
just coverConnection Settings
When running examples or tests:
- IB Gateway Paper Trading: 127.0.0.1:4002 (recommended)
- IB Gateway Live Trading: 127.0.0.1:4001
- TWS Paper Trading: 127.0.0.1:7497
- TWS Live Trading: 127.0.0.1:7496
Environment Variables
# Set log level
RUST_LOG=debug cargo run --example <example_name>
# Record TWS messages for debugging (parsed messages, re-framed)
IBAPI_RECORDING_DIR=/tmp/tws-messages cargo run --example <example_name>
# Capture the raw inbound stream, length prefixes intact. The only tool that
# can diagnose a framing desync — the recorder above discards the prefix.
# Walk a capture with `cargo run --example replay_raw_capture -- <file>.bin`.
IBAPI_RAW_CAPTURE_DIR=/tmp/tws-raw cargo run --example <example_name>Git Commit Guidelines
- No "Generated with Claude Code" or similar attribution in commits or PRs
- Messages describe the technical change and its purpose
- Merge strategy follows what the branch holds; the table and the stacked-PR order are in the
/shipskill (.claude/skills/ship)
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

