Thank you for your interest in contributing! This workspace hosts multiple crates that compose the borsa ecosystem: a connector trait and core types, a high-level router, and provider connectors. We welcome fixes, features, docs, tests, and new connectors.
Please review and follow our Code of Conduct
- Rust toolchain: stable (edition 2024)
- Tests runner:
cargo-nextest(used byjust test) — install withcargo install cargo-nextest --locked - Run tests:
just test - Lint:
just lint - Format:
just fmt
borsa-types: shared error, reports, config, attribution typesborsa-core: core traits and utilities; re-exports types fromborsa-typesborsa: the high-level router/orchestratorborsa-yfinance: Yahoo Finance connectorborsa-mock: mock connector with deterministic fixtures and a programmableDynamicMockConnectorfor tests/examplesborsa-middleware: reusable middleware (quota-aware, cache, blacklist) and small builderborsa-macros: procedural macros used by middleware/connectorsborsa/examples/: runnable example programs
- Good first issues: small doc fixes, examples, or tests
- Features: extending router capabilities or adding data domains
- Providers: implement a new connector crate for a data vendor
- Fork the repo and create a feature branch
- Make focused commits with clear messages
- Ensure
cargo test --workspacepasses and no clippy warnings remain - Update docs and examples as needed
- Open a PR describing the change, motivation, and testing
New connectors live as their own crate at the workspace root (e.g., borsa-someprovider/). A connector implements the BorsaConnector trait from borsa-core and exposes a small, ergonomic constructor.
Key steps:
- Create a crate:
cargo new borsa-someprovider --lib - Add dependencies (in crate
Cargo.toml):borsa-core = { workspace = true }- Other client libs for the vendor API
- Implement the connector in
src/lib.rsusing capability accessors:fn name(&self) -> &'static strreturns a stable identifier (e.g.,"borsa-someprovider")fn vendor(&self) -> &'static strreturns a human-friendly vendor namefn supports_kind(&self, kind: AssetKind) -> booldeclares supported asset kinds (e.g., equities, crypto)- Advertise capabilities via
as_*_provideraccessors onBorsaConnector(e.g.,as_quote_provider,as_history_provider,as_search_provider, ...) - Implement async methods on the corresponding role traits you support (e.g.,
QuoteProvider::quote,HistoryProvider::history,SearchProvider::search, ...)
- For history, implement
HistoryProviderand declare native intervals viafn supported_history_intervals(&self, kind: AssetKind) -> &'static [Interval]on the provider implementation (not on the connector trait). - Map vendor-specific models to
borsa-coretypes - Add tests (unit + integration) validating:
- Interval mapping and errors for unsupported intervals
- Data kind support (e.g., equities, crypto, forex)
- Error handling:
NotFoundvsUnsupportedvsOther
- Document crate usage in
README.mdand note any required environment variables (e.g., API keys)
Reference implementations:
borsa-yfinance/src/lib.rs- Trait:
borsa-core/src/connector.rs
- Prefer to return
BorsaError::NotFoundfor symbols or datasets that the provider does not have, so the router can fall back to lower-priority connectors. - Use
BorsaError::unsupported("feature")for capabilities the provider cannot serve. - History intervals: return only the exact native intervals in
supported_history_intervals; the router may resample intraday data to the requested interval. - For overlapping history from multiple connectors, the router can prioritize adjusted data (
prefer_adjusted_history(true)) and resample to daily/weekly.
- Keep tests deterministic; use recorded fixtures or mock connectors where possible
- Avoid hitting external APIs in CI; if unavoidable, guard behind opt-in flags
- Exercise corner cases: empty series, missing fields, timezone metadata, actions alignment
- Use present tense: "Add X", "Fix Y"
- Keep the first line under ~72 chars; include additional context in the body
- Reference issues when applicable
If you discover a vulnerability, please email the maintainers privately at security@borsa.rs rather than opening a public issue.
By contributing, you agree that your contributions are licensed under the MIT License (see LICENSE).