Access Questrade brokerage accounts and Canadian/US market data for balances, positions, orders, executions, Level 1 quotes, historical candles, and symbol s...
---
name: questrade
description: Query a Questrade brokerage account and Canadian/US market data via the Questrade REST API. Use for account info (balances, positions, orders, executions, activities), Level 1 market quotes, historical OHLCV candles, and symbol search. Use when the user asks about their Questrade portfolio, Canadian/US stock prices, wants to check orders or executions, or search for a ticker. Note: personal API tokens are read-only — order placement requires Questrade partner access.
---
# Questrade Skill
Interact with a Questrade brokerage account and Canadian/US market data via
the [Questrade REST API](https://www.questrade.com/api/documentation/getting-started).
## Setup
### 1. Activate API Access
Log in to Questrade → **App Hub** → **API Centre** → **Activate API**.
Generate a **manual authorization token** (this is your initial refresh token,
valid for 7 days).
### 2. Store Credentials
**Option A — environment variables (recommended):**
```bash
export QUESTRADE_REFRESH_TOKEN="paste-your-token-here"
export QUESTRADE_PRACTICE="false" # "true" for a practice account
export QUESTRADE_READ_ONLY="true" # "true" to block order placement/cancellation
```
**Option B — credentials file (`~/.openclaw/credentials/questrade.json`):**
```json
{
"refreshToken": "paste-your-token-here",
"practice": false,
"readOnly": true
}
```
> **Important:** Questrade refresh tokens *rotate* — every time the token is
> used to obtain a new access token, Questrade issues a new refresh token.
> This script automatically saves the new refresh token back to the credentials
> file, so you only need to paste the initial token once.
### 3. Install Dependencies
```bash
pip install -r requirements.txt
```
Or manually:
```bash
pip install requests
```
---
## Quick Reference
### Server Time
```bash
python3 scripts/questrade_cli.py time
```
### Accounts
```bash
python3 scripts/questrade_cli.py accounts
```
### Balances
```bash
python3 scripts/questrade_cli.py balances 12345678
```
### Positions
```bash
python3 scripts/questrade_cli.py positions 12345678
```
### Orders
```bash
# Open orders (default)
python3 scripts/questrade_cli.py orders 12345678
# All orders in a date range
python3 scripts/questrade_cli.py orders 12345678 --state All --start 2026-01-01 --end 2026-03-01
# Closed orders
python3 scripts/questrade_cli.py orders 12345678 --state Closed
```
### Order Detail
```bash
python3 scripts/questrade_cli.py order-detail 12345678 987654321
```
### Executions
```bash
python3 scripts/questrade_cli.py executions 12345678
python3 scripts/questrade_cli.py executions 12345678 --start 2026-01-01 --end 2026-02-28
```
### Account Activities
```bash
# Max 30-day window; --end defaults to today if omitted
python3 scripts/questrade_cli.py activities 12345678 --start 2026-02-01
python3 scripts/questrade_cli.py activities 12345678 --start 2026-02-01 --end 2026-02-28
```
### Symbol Search
```bash
# Search by ticker prefix
python3 scripts/questrade_cli.py symbol-search SHOP
# Search by company name
python3 scripts/questrade_cli.py symbol-search "Royal Bank"
```
> **Note:** Questrade market data endpoints use integer **symbol IDs**, not
> ticker strings. Always run `symbol-search` first to get the correct ID
> before calling `quote` or `candles`.
### Symbol Info
```bash
# Full metadata for a symbol (by Questrade ID)
python3 scripts/questrade_cli.py symbol-info 8049
```
### Level 1 Quotes
```bash
# Single symbol
python3 scripts/questrade_cli.py quote 8049
# Multiple symbols (comma-separated IDs)
python3 scripts/questrade_cli.py quote 8049,38738,45340
```
### Historical Candles (OHLCV)
```bash
# Daily candles for the past month
python3 scripts/questrade_cli.py candles 8049 --start 2026-02-01
# Hourly candles for a specific range
python3 scripts/questrade_cli.py candles 8049 --start 2026-01-01 --end 2026-02-01 --interval OneHour
# Available intervals:
# OneMinute, TwoMinutes, ThreeMinutes, FourMinutes, FiveMinutes,
# TenMinutes, FifteenMinutes, TwentyMinutes, HalfHour,
# OneHour, TwoHours, FourHours, OneDay, OneWeek, OneMonth, OneYear
```
### Markets
```bash
python3 scripts/questrade_cli.py markets
```
### Place Order *(partner API access required)*
```bash
# Market order
python3 scripts/questrade_cli.py order buy 12345678 8049 10 Market
# Limit order
python3 scripts/questrade_cli.py order buy 12345678 8049 10 Limit --limit-price 145.50
# Stop-limit order
python3 scripts/questrade_cli.py order sell 12345678 8049 5 StopLimit --stop-price 140.00 --limit-price 139.50
# Skip confirmation prompts
python3 scripts/questrade_cli.py order buy 12345678 8049 10 Market --force
```
**Order Guardrails:**
1. **Live quote fetch** — retrieves current bid/ask before submitting
2. **Price sanity check** — warns if buy limit > ask or sell limit < bid
3. **Order summary** — shows notional cost and requires `y/n` confirmation
4. Use `--force` to skip all prompts (automated use only)
> **Note:** Order placement and cancellation require Questrade *partner*
> API access. Personal API tokens are read-only (GET requests only).
### Cancel Order
```bash
python3 scripts/questrade_cli.py cancel-order 12345678 987654321
```
---
## Workflow: Finding a Symbol and Getting a Quote
Because Questrade quotes use integer IDs rather than tickers, the typical
two-step flow is:
```bash
# Step 1: Find the symbol ID
python3 scripts/questrade_cli.py symbol-search AAPL
# Step 2: Use the ID from the output
python3 scripts/questrade_cli.py quote 8049
```
---
## Script Location
All commands use: `scripts/questrade_cli.py` (relative to this skill directory)
## API Reference
See `references/api.md` for full endpoint documentation and response schemas.
## Known Limitations
- **Level 1 quotes and candles** (`quote`, `candles`) may return `403 out of allowed OAuth scopes` if your Questrade account does not have a market data subscription or the token was not generated with market data scope enabled. Account data commands (`accounts`, `balances`, `positions`, `orders`, `executions`, `activities`) work with all personal tokens.
- **Activities** endpoint has a maximum window of **30 days** — always provide both `--start` and `--end`. If `--end` is omitted it defaults to today.
## Safety Notes
- **Personal API tokens cannot place trades.** Questrade restricts order placement
and cancellation (`POST`/`DELETE /accounts/{id}/orders`) to partner-level API
access only. Attempting either with a personal token returns `403 Forbidden`.
- The `order` and `cancel-order` commands are included for completeness but will
not work unless you have Questrade partner API access.
- The refresh token rotates on every use; do **not** share the credentials file.
- Access tokens expire in ~30 minutes; the script caches and refreshes them automatically.
don't have the plugin yet? install it then click "run inline in claude" again.
restructured into six implexa components with explicit decision logic, edge case handling (token rotation, rate limits, timeouts, scope errors, 30-day activities window), detailed inputs with env var and file paths, complete procedure with step-by-step inputs/outputs, standardized output contract with json schemas and file locations, and outcome signals covering auth, data freshness, order flow, and error cases.
Query a Questrade brokerage account and Canadian/US market data via the Questrade REST API. Use this skill to check account info (balances, positions, orders, executions, activities), fetch Level 1 market quotes, retrieve historical OHLCV candles, and search for symbols. Activate this when the user asks about their Questrade portfolio, Canadian/US stock prices, wants to verify orders or executions, or search for a ticker. note: personal API tokens are read-only, order placement and cancellation require Questrade partner access.
External Connection: Questrade REST API
https://login.questrade.com and https://api01.questrade.com (or practice endpoint)quote and candles), account data access (always included)Credentials (choose one method):
Environment variables (recommended):
QUESTRADE_REFRESH_TOKEN: initial refresh token from API Centre (valid 7 days, auto-rotates)QUESTRADE_PRACTICE: set to "true" for practice account, "false" for liveQUESTRADE_READ_ONLY: set to "true" to block order placement/cancellation (default: "true")Credentials file (~/.openclaw/credentials/questrade.json):
{
"refreshToken": "paste-your-token-here",
"practice": false,
"readOnly": true
}
Setup Steps:
pip install requestsEdge Cases & Rate Limits:
Initialize connection & refresh access token
https://login.questrade.com/oauth2/token with grant_type=refresh_tokenFetch server time (optional, diagnostic)
/timeFetch account list
/accountsFetch account balances
/accounts/{id}/balancesFetch account positions
/accounts/{id}/positionsFetch orders (open or date-filtered)
/accounts/{id}/orders?statuses={state}&startDate={start}&endDate={end}Fetch order detail
/accounts/{id}/orders/{orderId}Fetch executions (fills, optional date range)
/accounts/{id}/executions?startDate={start}&endDate={end}Fetch account activities (deposits, withdrawals, fees, max 30-day window)
/accounts/{id}/activities?startDate={start}&endDate={end}Search symbols (by ticker prefix or company name)
/symbols?prefix={query}Fetch symbol metadata
/symbols/{id}Fetch Level 1 quote (single or batch)
/quotes?ids={id1},{id2},...Fetch historical candles (OHLCV)
/candles/{id}?startDate={start}&endDate={end}&interval={interval}Fetch markets (list active markets)
/marketsPlace order (requires partner API access; personal tokens will fail with 403)
/accounts/{id}/ordersCancel order (requires partner API access; personal tokens will fail with 403)
/accounts/{id}/orders/{orderId}if market data scope disabled (403 out of allowed OAuth scopes): quote and candles endpoints fail; account data commands (accounts, balances, positions, orders, executions, activities) still work. user must enable market data scope in Questrade API Centre and regenerate token.
if credentials not found: check env vars (QUESTRADE_REFRESH_TOKEN), fall back to credentials file (~/.openclaw/credentials/questrade.json). if neither exists, exit with clear error message and setup instructions.
if refresh token expired (>7 days without use): API returns 401 Unauthorized. user must manually regenerate token from Questrade API Centre and update credentials.
if access token expired (~30 min): script auto-refreshes using stored refresh token (step 1). if refresh fails, token is invalid.
if activities query exceeds 30-day window: reject with error; user must split into multiple 30-day queries.
if no end date provided for activities: default end date to today.
if symbol search returns empty: return empty array with note; user may try alternative search term or ticker.
if candles/quote returns empty array: symbol may not have trading data or market data subscription is inactive.
if order placement flagged read-only (QUESTRADE_READ_ONLY=true): block POST/DELETE calls and return error. user must set flag to false and confirm partner access.
if order placement without force flag: always fetch live quote, display order summary (notional cost, bid/ask comparison for limit orders), and require y/n prompt. only submit if user confirms.
if order placement returns 403 Forbidden: personal API token lacks order placement permission. user needs partner API access.
if network timeout (>30 sec): retry up to 2 times with exponential backoff (2 sec, 4 sec). if all retries fail, return timeout error with endpoint and last response code.
if rate limit hit (429 Too Many Requests): back off for 60 seconds, then retry. log rate limit hit.
success format:
all commands return structured JSON output with consistent schema:
{
"status": "success",
"data": <endpoint-specific array or object>,
"meta": {
"timestamp": "2026-02-15T14:32:00Z",
"endpoint": "/accounts/{id}/balances",
"responseTime": 234
}
}
file output (if writing to disk):
~/.openclaw/questrade_cache/ (created on first run){command}_{account_id}_{timestamp}.jsonlpositions_12345678_2026-02-15T143200Z.jsonledge case outputs:
{ "status": "success", "data": [], "meta": {...} }{ "status": "error", "code": "AUTH_FAILED", "message": "refresh token expired", "meta": {...} }{ "status": "error", "code": "RATE_LIMITED", "retryAfter": 60 }{ "status": "error", "code": "TIMEOUT", "message": "no response after 30s", "endpoint": "/..." }{ "status": "error", "code": "SCOPE_ERROR", "message": "403 out of allowed OAuth scopes", "hint": "enable market data in API Centre" }the skill worked if:
symbol-search first, gets symbol id, then calls quote with id (not ticker string)order and cancel-order return clear error if QUESTRADE_READ_ONLY=true or partner access missing