questrade-client
Async Rust client for the Questrade REST API.
Handles OAuth token refresh, typed market-data access (quotes, option chains, candles), and account-data access (positions, balances, activities, orders, executions).
Features
- Automatic OAuth token management with single-use refresh token rotation
- Token caching to skip OAuth round-trips on subsequent runs
- Transparent 401 retry — forces a token refresh and retries once on Unauthorized
- Rate-limit retry with exponential backoff and jitter on 429 responses
- Fully typed request/response types with serde deserialization
tracinginstrumented for structured logging at debug/trace levels- Raw response logging mode for API debugging
Quick start
Add to your Cargo.toml:
[]
= "0.1"
= { = "1", = ["full"] }
= "1"
use ;
async
Auth flow
Questrade uses OAuth 2.0 with single-use refresh tokens. Every time you exchange a refresh token for an access token, the old refresh token is invalidated and a new one is returned. If you lose the rotated token, you must generate a new one from the Questrade API Hub.
Token persistence
Pass an OnTokenRefresh callback to TokenManager::new to persist the rotated token after every automatic refresh:
use Arc;
use ;
let on_refresh: OnTokenRefresh = new;
let manager = new.await?;
To skip the OAuth round-trip on subsequent runs, pass a CachedToken:
let cached = CachedToken ;
let manager = new.await?;
See the token_manager example for a complete working implementation.
API coverage
| Category | Method | Questrade endpoint |
|---|---|---|
| Auth | TokenManager::new |
GET /oauth2/token |
| Server | get_server_time |
GET /v1/time |
| Markets | get_markets |
GET /v1/markets |
| Symbols | resolve_symbol |
GET /v1/symbols/search |
| Symbols | get_symbol |
GET /v1/symbols/:id |
| Quotes | get_raw_quote |
GET /v1/markets/quotes/:id |
| Options | get_option_chain_structure |
GET /v1/symbols/:id/options |
| Options | get_option_quotes_by_ids |
POST /v1/markets/quotes/options |
| Options | get_option_quotes_raw |
POST /v1/markets/quotes/options |
| Candles | get_candles |
GET /v1/markets/candles/:id |
| Accounts | get_accounts |
GET /v1/accounts |
| Positions | get_positions |
GET /v1/accounts/:id/positions |
| Balances | get_account_balances |
GET /v1/accounts/:id/balances |
| Activities | get_activities |
GET /v1/accounts/:id/activities |
| Orders | get_orders |
GET /v1/accounts/:id/orders |
| Executions | get_executions |
GET /v1/accounts/:id/executions |
| Raw | get_text |
Any GET /v1/* endpoint |
Not yet implemented
| Endpoint | Description | Notes |
|---|---|---|
POST /v1/markets/quotes/strategies |
Multi-leg strategy quotes (up to 4 legs) | Read-only (read_md scope). Useful for evaluating spreads, iron condors, etc. |
POST /v1/accounts/:id/orders |
Place a new order | Requires trade scope (partner-only) |
POST /v1/accounts/:id/orders/:orderId |
Replace/modify an existing order | Requires trade scope (partner-only) |
DELETE /v1/accounts/:id/orders/:orderId |
Cancel an order | Requires trade scope (partner-only) |
POST /v1/accounts/:id/orders/:orderId/impact |
Preview order impact (buying power, commission) | Requires trade scope (partner-only) |
The order management endpoints require the trade OAuth scope, which Questrade restricts to approved partner developers.
Partial coverage notes
POST /v1/markets/quotes/optionssupports two modes: by explicit IDs (implemented) and by filters (underlyingId+expiryDate+ optional strike range + option type). Only the ID-based mode is currently exposed.GET /v1/symbols/searchis wrapped byresolve_symbol()which returns only the first exact-match symbol ID. The raw search response (multiple partial matches) is not directly exposed as a public method.
Automatic windowing
The get_activities and get_executions methods automatically split date ranges longer than 30 days into compliant sub-windows (Questrade limits queries to 31-day windows). Results are combined and sorted chronologically.
Examples
dump_responses— dump raw API JSON to stdout for debuggingtoken_manager— persist OAuth tokens across application runs
License
MIT License. See LICENSE for details.