Guidance for coding agents (and a short version of CONTRIBUTING.md for people). This code places real orders in real brokerage accounts. When a rule below conflicts with making a test pass, the rule wins.
| Path | What |
|---|---|
crates/rustyschwab-types |
Wire types for Schwab's JSON. Most are #[non_exhaustive], so other crates build them with constructors (Candle::new) or serde, and match on their enums needs a _ arm. |
crates/rustyschwab |
The SDK: auth, client, streaming, execution (order journal), risk, reconciliation, paper (the venue), history, performance, metrics. |
crates/rustyschwab-mcp |
The MCP server (src/server.rs holds every #[tool]), its policy layer, and the rustyschwab-admin, rustyschwab-authority, rustyschwab-remote, rustyschwab-paper, rustyschwab-eval binaries. |
crates/rustyschwab-console |
The operator console core: reads the servers' state into a serializable Snapshot and runs operator Commands (approve, delegate, halt, kill). No UI code. |
crates/rustyschwab-dash |
The ratatui terminal frontend over the console (rustyschwab-dash). Rendering and keys only; anything that reads state or acts belongs in the console. |
crates/schwab-py |
PyO3 bindings, published as rustyschwab. Not a workspace member: it has its own Cargo.lock, and workspace commands do not build it. Python sources are in python/rustyschwab/, tests in tests/. |
docs/ |
User and operator guides; docs/INDEX.md lists them. docs/history/ is archived. docs/audit/ is git-ignored local evidence. |
examples/, fuzz/, ops/, tools/ |
Runnable programs, fuzz targets, paper configs and Prometheus assets, soak and SBOM scripts. |
To find the module for a change, use the "Where to look" table in docs/ARCHITECTURE_OVERVIEW.md.
- Never read, print or copy
.env,schwab_tokens.*,*.keyor*.orders.store/. They hold live credentials and order state. Use.env.examplefor the variable list. - Tests never reach Schwab. Use
wiremock(Rust) or theMockSchwabfixture incrates/schwab-py/tests/conftest.py. The only live test istests/live_conformance.rs, opt-in throughSCHWAB_LIVE_CONFORMANCE, and it only issuesGETs. - An order submission is never retried. Everything that submits goes through the execution journal (
begin_oncethenfinish). An ambiguous outcome isError::SubmissionUnknownand is settled by reconciliation. Do not add retries, replays or "resend on timeout" anywhere on that path. - Money is decimal. Prices, quantities and cash are
rust_decimal::Decimalin Rust,decimal.Decimalor decimal strings in Python and over MCP. Never route them throughf64. - Metrics are declared once, in a
metrics.rscatalog with a row in its module-doc table.tests/metrics_catalog.rsrejects ad-hoc names. - MCP tools are listed twice on purpose:
EXPECTED_TOOLSincrates/rustyschwab-mcp/tests/tool_inventory.rsand the tool catalog indocs/MCP_LLM_INTEGRATION.md. The test fails if either drifts from the server. - Wire-shape changes need a fixture. Add a
wiremockor fixture test with the change. Schwab's published OpenAPI files are not authoritative; recorded responses are. - Errors go through
rustyschwab::Error. Add a variant with the rightErrorKindandis_retryableinstead of a string. - Secrets stay secret. Tokens are
SecretString;Debug, logs, errors and Python reprs must not contain them.
Fast loop while working:
cargo test -p rustyschwab --test paper_trading # or whichever crate/test you touched
cargo clippy -p rustyschwab-mcp --all-targets -- -D warningsBefore calling a change done, run the full list in CONTRIBUTING.md. Its two cargo test lines are both needed: --all-targets skips doctests.
The Python bindings, from crates/schwab-py:
cargo fmt --check && cargo clippy --all-targets --locked -- -D warnings && cargo test --locked
maturin develop # in a virtualenv with the test extra installed
python -m pytest && python -m mypy --strict python/rustyschwabSetting CARGO_PROFILE_DEV_DEBUG=0 CARGO_PROFILE_TEST_DEBUG=0 makes local builds much smaller and faster.
These examples run in CI, so keep them working rather than editing around them:
- The crate READMEs, which are the docs.rs front pages (
cargo test --doc). - The Python example in the root
README.md(tests/test_trader.py::test_the_readme_strategy_runs). - The backtest loop in
docs/PYTHON.md(tests/test_backtest.py). - The MCP tool catalog (
tool_inventory.rs) and the metric tables (metrics_catalog.rs).
- Add a line under
## [Unreleased]inCHANGELOG.md. Intentional public-API breaks need migration notes; CI runscargo-semver-checks. - Update
.env.examplewhen you add or rename aSCHWAB_*variable. - Conventional commits (
feat(mcp): …,fix(paper): …), one concern per commit. - What is core and what is experimental: docs/SUPPORT.md.