Unofficial Python client for the Wealthsimple GraphQL API
Project description
wealthsimple-python
Unofficial Python client for the Wealthsimple Trade platform
Features • Installation • Quick Start • Documentation • Examples
⚠️ Important Disclaimer
This is an unofficial API client for Wealthsimple Trade. It is not affiliated with, officially maintained by, or endorsed by Wealthsimple. Use at your own risk.
- ⚠️ No warranty or guarantee is provided
- 🧪 Always test with small amounts first
- 🔒 Keep your credentials secure
- 📝 API may change without notice
📋 Table of Contents
- Features
- Installation
- Quick Start
- Authentication
- Documentation
- Interactive Trading Tool
- Examples
- Advanced Usage
- API Reference
- Contributing
- Legacy API
✨ Features
🔐 Authentication
- OAuth v2 authentication with automatic token refresh
- Support for 2FA/OTP
- Environment variable support for secure credential storage
- Token expiry management and auto-refresh
📊 Market Data
- Security search by ticker symbol or company name
- Real-time quotes with bid/ask spreads
- Detailed security information (fundamentals, market status)
- Nearest market open and market buffer lookups
- Intraday chart quotes from Wealthsimple's native chart API
- Support for stocks across multiple exchanges (NASDAQ, NYSE, TSX, etc.)
- WebSocket subscriptions for real-time streaming data
- Real-time security quote updates
- Activity feed notifications
- Account balance change alerts
- Identity and account core updates
💼 Account Management
- Retrieve all accounts (Personal, TFSA, RRSP, etc.)
- Account balances and buying power
- Current account financials and broker-native graph data
- Current positions with unrealized P/L
- Activity feed and order history
- Daily historical portfolio financials with automatic pagination
- Net liquidation value over any date range
- Net deposits vs. unrealised gain/loss
- Simple return % calculations
📈 Stock Trading
- Market orders (buy/sell)
- Limit orders (buy/sell)
- Stop-limit orders (buy/sell)
- Good-Till-Cancelled (GTC) and Day orders
- Custom order creation
📉 Options Trading
- Full option chain retrieval
- Available expiry dates
- Option greeks (delta, gamma, theta, vega, rho)
- Implied volatility
- Buy to open/close
- Sell to open/close (writing covered calls)
- Transaction fee calculation
🎮 Interactive Trading Tool
- Command-line interface for easy trading
- Interactive security search
- Real-time quote viewing
- Order confirmation before execution
- Support for both stocks and options
📦 Installation
Requirements
- Python 3.9 or higher
Install directly from GitHub
pip install git+https://github.com/henryhuangh/wealthsimple-python.git
With optional dependencies:
# Secure token storage (highly recommended)
pip install "wealthsimple-python[keyring] @ git+https://github.com/henryhuangh/wealthsimple-python.git"
# WebSocket subscriptions
pip install "wealthsimple-python[websockets] @ git+https://github.com/henryhuangh/wealthsimple-python.git"
# Everything
pip install "wealthsimple-python[all] @ git+https://github.com/henryhuangh/wealthsimple-python.git"
Or clone and install locally
git clone https://github.com/henryhuangh/wealthsimple-python.git
cd wealthsimple-python
pip install .
With optional dependencies:
# Secure token storage (highly recommended)
pip install ".[keyring]"
# WebSocket subscriptions
pip install ".[websockets]"
# Everything
pip install ".[all]"
The core package only requires requests. Optional extras:
| Extra | Package | Purpose |
|---|---|---|
keyring |
keyring |
OS-level secure token storage (macOS Keychain, Windows Credential Locker, Linux Secret Service) |
websockets |
websockets |
Real-time streaming via WebSocket subscriptions |
all |
both | Install everything |
🚀 Quick Start
Basic Usage
from wealthsimple_python import WealthsimpleV2
# Initialize and authenticate
ws = WealthsimpleV2(
username='your@email.com',
password='yourpassword',
otp='123456' # Optional, only if 2FA is enabled
)
# Search for a security
results = ws.search_securities('AAPL')
security_id = ws.get_ticker_id('AAPL', 'NASDAQ')
# Get a real-time quote
quote = ws.get_security_quote(security_id)
print(f"AAPL: ${quote['price']} (Bid: ${quote['bid']}, Ask: ${quote['ask']})")
# Get your accounts
accounts = ws.get_accounts()
for account in accounts:
print(f"{account['nickname']}: {account['id']}")
# Place a limit buy order
account_id = accounts[0]['id']
order = ws.limit_buy(
account_id=account_id,
security_id=security_id,
quantity=1,
limit_price=150.00
)
print(f"Order placed: {order['orderId']}")
🔐 Authentication
Basic Authentication
from wealthsimple_python import WealthsimpleV2
ws = WealthsimpleV2(username='your@email.com', password='yourpassword')
With 2FA/OTP
If you have two-factor authentication enabled:
ws = WealthsimpleV2(
username='your@email.com',
password='yourpassword',
otp='123456' # Your 6-digit 2FA code
)
Token Persistence
After successful authentication, tokens are automatically saved securely using the keyring library (if available), which stores credentials in your operating system's secure credential storage:
- macOS: Keychain
- Windows: Credential Locker
- Linux: Secret Service / KWallet
Benefits:
- 🔒 Tokens are encrypted by the operating system
- 🔄 Tokens persist across terminal sessions and reboots
- 🛡️ More secure than plain text environment variables
- 🚀 Automatic loading on subsequent runs
Fallback: If keyring is not installed, tokens are saved to environment variables (WS_ACCESS_TOKEN and WS_REFRESH_TOKEN) for the current session only.
# First time - authenticate with credentials
ws = WealthsimpleV2(username='your@email.com', password='yourpassword')
# Tokens are now securely saved to keyring (and environment variables as fallback)
# Later - even in a new terminal/session - tokens are auto-loaded
ws = WealthsimpleV2() # Automatically uses saved tokens from keyring
Token storage priority:
- Keyring (secure OS credential storage) - checked first
- Environment variables - fallback if keyring unavailable
- Manual tokens - if provided explicitly
Logout / Clear Tokens
To clear all stored tokens:
ws.logout() # Clears tokens from keyring, environment, and instance
Automatic Token Refresh
The client automatically refreshes expired access tokens:
# No need to manually refresh - it happens automatically
accounts = ws.get_accounts() # Token is auto-refreshed if expired
When tokens are refreshed, both keyring and environment variables are automatically updated.
Manual Token Refresh
If needed, you can manually refresh:
success = ws.refresh_access_token()
if success:
print("Token refreshed successfully")
# Keyring and environment variables are automatically updated
📚 Documentation
Security Search & Quotes
Search for Securities
# Search by ticker or company name
results = ws.search_securities('AAPL')
# Search with specific security groups
results = ws.search_securities('TSLA', security_group_ids=['stock', 'adr'])
# Display results
for security in results:
print(f"{security['stock']['symbol']} - {security['stock']['name']}")
Get Ticker ID
# Get security ID by ticker symbol
security_id = ws.get_ticker_id('AAPL', 'NASDAQ')
security_id = ws.get_ticker_id('SHOP', 'TSX')
# The ID is required for trading and quotes
print(f"Security ID: {security_id}")
Get Real-Time Quote
# Get quote for a security
quote = ws.get_security_quote(security_id)
print(f"Price: ${quote['price']}")
print(f"Bid: ${quote['bid']} x {quote['bidSize']}")
print(f"Ask: ${quote['ask']} x {quote['askSize']}")
print(f"High: ${quote['high']} | Low: ${quote['low']}")
print(f"Volume: {quote['volume']}")
print(f"Market Status: {quote['quoteStatus']}")
Get Detailed Security Information
# Get comprehensive security details
security = ws.get_security(security_id)
# Access various data points
stock = security['stock']
print(f"Symbol: {stock['symbol']}")
print(f"Name: {stock['name']}")
print(f"Exchange: {stock['primaryExchange']}")
print(f"Currency: {stock['currency']}")
print(f"52-Week High: ${stock['high52Week']}")
print(f"52-Week Low: ${stock['low52Week']}")
# Fundamentals
fundamentals = stock.get('fundamentals', {})
print(f"P/E Ratio: {fundamentals.get('peRatio')}")
print(f"Market Cap: ${fundamentals.get('marketCap')}")
print(f"Dividend Yield: {fundamentals.get('dividendYield')}%")
Get Nearest Market Open
# Get previous/current/next market session boundaries
market_open = ws.get_nearest_market_open()
print(f"Exchange: {market_open['exchangeName']} ({market_open['mic']})")
print(f"Current trade day: {market_open['currentTradeDay']['date']}")
Get Market Buffer
# Get the current market buffer for a country / asset class
buffer = ws.get_market_buffer(country='CA', is_option=False)
print(f"Market buffer: {buffer}")
Get Security Status
# Check trading status for a security
status = ws.get_security_status(security_id)
print(f"Security status: {status['status']}")
Get Intraday Chart Quotes
# Get Wealthsimple-native chart points
bars = ws.get_intraday_chart_quotes(
security_id=security_id,
period='ONE_DAY',
trading_session='REGULAR'
)
for bar in bars:
print(f"{bar['timestamp']}: {bar['price']}")
Resolve Ticker Market Symbols
# Convert ticker.market symbols into Wealthsimple security IDs
symbol, market = ws.parse_ticker_market('TSLA.US')
print(symbol, market)
security_id = ws.resolve_security_id('MFC.TO')
print(f"Security ID: {security_id}")
Account Management
Get All Accounts
# Retrieve all accounts
accounts = ws.get_accounts()
for account in accounts:
print(f"Nickname: {account['nickname']}")
print(f"ID: {account['id']}")
print(f"Type: {account['accountType']}")
print(f"Status: {account['status']}")
print("---")
Transfer Funds Between Accounts
# Move idle cash from one Wealthsimple account to another
transfer = ws.create_internal_transfer(
source_account_id='non-registered-xxxxx',
destination_account_id='ca-cash-xxxxx',
amount=25.75,
currency='CAD'
)
print(f"Transfer created: {transfer['id']}")
Get Account Financials
# Get detailed financial information
account_ids = [acc['id'] for acc in accounts]
financials = ws.get_account_financials(account_ids)
for financial in financials:
print(f"Account: {financial['accountId']}")
print(f"Net Worth: ${financial['netWorth']['amount']} {financial['netWorth']['currency']}")
print(f"Buying Power: ${financial['buyingPower']['amount']}")
print(f"Cash Balance: ${financial['currentCashBalance']['amount']}")
print("---")
Get Current Positions
# Get all positions across all accounts
positions = ws.get_positions()
for position in positions:
security = position['security']
symbol = security['stock']['symbol']
quantity = position['quantity']
# Market value
market_value = position['totalValue']['amount']
# P/L information
pnl = position.get('profitLossAmount', {}).get('amount', 0)
pnl_pct = position.get('profitLossPercentage', 0)
print(f"{symbol}: {quantity} shares")
print(f" Value: ${market_value}")
print(f" P/L: ${pnl} ({pnl_pct:.2f}%)")
Filter Positions by Account
# Get positions for specific accounts
account_ids = [accounts[0]['id']] # Only first account
positions = ws.get_positions(account_ids=account_ids)
Get Current Financials
# Get current metrics for a single account
current = ws.get_account_current_financials(account_id)
print(f"Net liquidation value: {current['netLiquidationValueV2']['amount']}")
print(f"Net deposits: {current['netDeposits']['amount']}")
print(f"Total withdrawals: {current['totalWithdrawals']['amount']}")
Get Account Graph Data
# Get broker-native graph data for charting
graph = ws.get_account_graph_data(
account_id=account_id,
currency='CAD',
time_range='ONE_DAY',
market_session='REGULAR'
)
for point in graph.get('data', []):
print(f"{point['dateTime']}: {point['netLiquidationValue']['amount']}")
Stock Trading
Market Orders
# Market buy
order = ws.market_buy(
account_id=account_id,
security_id=security_id,
quantity=10
)
# Market sell
order = ws.market_sell(
account_id=account_id,
security_id=security_id,
quantity=10
)
print(f"Order ID: {order['orderId']}")
print(f"Status: {order['status']}")
Limit Orders
# Limit buy - buy at or below limit price
order = ws.limit_buy(
account_id=account_id,
security_id=security_id,
quantity=5,
limit_price=150.00
)
# Limit sell - sell at or above limit price
order = ws.limit_sell(
account_id=account_id,
security_id=security_id,
quantity=5,
limit_price=160.00
)
Stop-Limit Orders
# Stop-limit buy
# When price rises to $155, place limit buy at $156
order = ws.stop_limit_buy(
account_id=account_id,
security_id=security_id,
quantity=5,
limit_price=156.00,
stop_price=155.00
)
# Stop-limit sell (stop loss)
# When price drops to $145, place limit sell at $144
order = ws.stop_limit_sell(
account_id=account_id,
security_id=security_id,
quantity=5,
limit_price=144.00,
stop_price=145.00
)
Custom Order Creation
# Create a custom order with all parameters
order = ws.create_order(
account_id=account_id,
security_id=security_id,
quantity=10,
limit_price=150.00,
order_type='BUY_QUANTITY',
order_sub_type='LIMIT',
time_in_force='GTC', # Good-Till-Cancelled
stop_price=None
)
Cancel Orders
# Place an order and get the external ID
order = ws.limit_buy(
account_id=account_id,
security_id=security_id,
quantity=5,
limit_price=150.00
)
# Get the external ID from the order response
external_id = order.get('externalCanonicalId') or order.get('externalId')
if external_id:
# Cancel the order
cancel_response = ws.cancel_order(external_id)
print(f"Order {external_id} cancelled successfully")
else:
print("Could not find external ID in order response")
Note: The external_id is the unique identifier for the order (typically in the format order-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx). It is returned in the order response when you create an order, or can be retrieved from pending orders or activity history.
Options Trading
Get Option Expiry Dates
# Get available expiry dates for an underlying security
expiry_dates = ws.get_option_expiry_dates(security_id)
print(f"Available expiry dates: {len(expiry_dates)}")
for date in expiry_dates[:5]: # Show first 5
print(f" {date}")
Get Option Chain
# Get call options for a specific expiry
options = ws.get_option_chain(
security_id=security_id,
expiry_date=expiry_dates[0],
option_type='CALL'
)
# Display option chain
for option in options:
symbol = option['optionSymbol']
strike = option['strikePrice']['amount']
# Greeks
greeks = option.get('greeks', {})
delta = greeks.get('delta')
gamma = greeks.get('gamma')
theta = greeks.get('theta')
vega = greeks.get('vega')
iv = greeks.get('impliedVolatility')
# Quote
quote = option.get('quote', {})
bid = quote.get('bid')
ask = quote.get('ask')
print(f"{symbol}")
print(f" Strike: ${strike}")
print(f" Bid: ${bid} | Ask: ${ask}")
print(f" Delta: {delta:.4f} | IV: {iv:.2%}")
print("---")
Get Put Options
# Get put options
puts = ws.get_option_chain(
security_id=security_id,
expiry_date=expiry_dates[0],
option_type='PUT'
)
Filter Options by Strike Price
# Get options near a specific strike price
options = ws.get_option_chain(
security_id=security_id,
expiry_date=expiry_dates[0],
option_type='CALL',
min_strike=145.00,
max_strike=155.00
)
Buy Options (Long Position)
# Buy to open a call option
order = ws.buy_option(
account_id=account_id,
option_id=options[0]['id'],
quantity=1, # 1 contract = 100 shares
limit_price=2.50 # Premium per share ($250 total)
)
# Buy to close a short position
order = ws.buy_option(
account_id=account_id,
option_id=option_id,
quantity=1,
limit_price=2.50,
order_sub_type='LIMIT'
)
Sell Options (Short Position or Close Long)
# Sell to open (write a covered call)
order = ws.sell_option(
account_id=account_id,
option_id=option_id,
quantity=1,
limit_price=2.50
)
# Sell to close a long position
order = ws.sell_option(
account_id=account_id,
option_id=option_id,
quantity=1,
limit_price=2.50,
order_sub_type='LIMIT'
)
Calculate Option Fees
# Calculate transaction fees for buying options
fees = ws.get_option_transaction_fees(
side='BUY',
premium=2.50, # Price per share
quantity=1, # Number of contracts
currency='USD'
)
print(f"Premium: ${fees['premium']['amount']}")
print(f"Commission: ${fees['commission']['amount']}")
print(f"Total Cost: ${fees['totalCost']['amount']}")
Activity & History
Get Activity Feed
# Get recent activities
activities = ws.get_activities(limit=50)
for activity in activities:
activity_type = activity['type']
symbol = activity.get('assetSymbol', 'N/A')
date = activity.get('occurredAt')
print(f"{date}: {activity_type} - {symbol}")
Filter Activities by Type
# Get only buy/sell activities
activities = ws.get_activities(
types=['buy', 'sell'],
limit=20
)
Filter Activities by Account
# Get activities for specific account
activities = ws.get_activities(
account_ids=[account_id],
limit=50
)
Real-Time WebSocket Subscriptions
The library supports real-time data streaming via WebSocket subscriptions. This enables you to receive live updates without polling the API.
Prerequisites
Subscriptions require the websockets library:
pip install websockets
Basic Usage
import asyncio
from wealthsimple_python import WealthsimpleV2
ws = WealthsimpleV2()
async def stream_quotes():
# Get security ID
security_id = ws.get_ticker_id('AAPL')
# Connect and subscribe
async with ws.subscribe() as sub:
async for msg in sub.stream_quotes([security_id]):
quote_data = msg['payload']['data']['securityQuoteUpdates']['quoteV2']
print(f"AAPL Price: ${quote_data['price']}")
print(f"Bid: ${quote_data['bid']}, Ask: ${quote_data['ask']}")
# Run the async function
asyncio.run(stream_quotes())
Stream Real-Time Quotes
Monitor live price updates for one or more securities:
async def watch_stocks():
ws = WealthsimpleV2()
# Get security IDs
aapl_id = ws.get_ticker_id('AAPL')
tsla_id = ws.get_ticker_id('TSLA')
async with ws.subscribe() as sub:
async for msg in sub.stream_quotes([aapl_id, tsla_id]):
quote_data = msg['payload']['data']['securityQuoteUpdates']
security_id = quote_data['id']
quote = quote_data['quoteV2']
print(f"{security_id}: ${quote['price']} "
f"(Bid: ${quote['bid']}, Ask: ${quote['ask']})")
asyncio.run(watch_stocks())
Stream Activity Feed Updates
Get notified when new activities occur (orders, trades, deposits, etc.):
async def monitor_activity():
ws = WealthsimpleV2()
async with ws.subscribe() as sub:
async for msg in sub.stream_activity_updates():
update = msg['payload']['data']['activityFeedUpdates']
print(f"New activity on account {update['accountId']}")
print(f"Activity ID: {update['activityId']}")
print(f"Updated at: {update['updatedAt']}")
asyncio.run(monitor_activity())
Stream Account Balance Changes
Monitor cash balance changes in custodian accounts:
async def watch_balances():
ws = WealthsimpleV2()
# Get custodian account IDs from your accounts
accounts = ws.get_accounts()
custodian_ids = []
for account in accounts:
for custodian in account.get('custodianAccounts', []):
custodian_ids.append(custodian['id'])
if custodian_ids:
async with ws.subscribe() as sub:
async for msg in sub.stream_balance_changes(custodian_ids):
print("Balance change detected!")
print(json.dumps(msg, indent=2))
asyncio.run(watch_balances())
Stream Identity and Account Updates
Get notified of account or identity changes:
async def watch_account_updates():
ws = WealthsimpleV2()
async with ws.subscribe() as sub:
async for msg in sub.stream_identity_updates():
update = msg['payload']['data']['identityAccountCoreUpdates']
print(f"Update type: {update['__typename']}")
print(f"Event: {update.get('eventName', 'N/A')}")
print(f"ID: {update.get('id', 'N/A')}")
asyncio.run(watch_account_updates())
Multiple Subscriptions Concurrently
You can run multiple subscriptions at the same time:
async def multi_stream():
ws = WealthsimpleV2()
security_id = ws.get_ticker_id('AAPL')
async with ws.subscribe() as sub:
# Create tasks for multiple subscriptions
tasks = []
# Stream quotes
async def quotes():
async for msg in sub.stream_quotes([security_id]):
quote = msg['payload']['data']['securityQuoteUpdates']['quoteV2']
print(f"Quote: ${quote['price']}")
tasks.append(asyncio.create_task(quotes()))
# Stream activity
async def activity():
async for msg in sub.stream_activity_updates():
print(f"Activity: {msg['payload']['data']['activityFeedUpdates']['activityId']}")
tasks.append(asyncio.create_task(activity()))
# Run both concurrently
await asyncio.gather(*tasks)
asyncio.run(multi_stream())
Keep Connection Alive
The subscription client automatically handles connection management. You can also send ping messages:
async with ws.subscribe() as sub:
# Send ping to keep connection alive
await sub.ping()
# Stream quotes
async for msg in sub.stream_quotes([security_id]):
# Process messages
pass
Error Handling
async def safe_stream():
ws = WealthsimpleV2()
try:
async with ws.subscribe() as sub:
async for msg in sub.stream_quotes([security_id]):
# Process message
pass
except Exception as e:
print(f"Subscription error: {e}")
# Connection will be automatically closed
asyncio.run(safe_stream())
Subscription Message Structure
All subscription messages follow this structure:
{
"type": "next", # or "error", "complete"
"id": "subscription-id",
"payload": {
"data": {
# Subscription-specific data
}
}
}
For quote updates, the payload structure is:
{
"payload": {
"data": {
"securityQuoteUpdates": {
"id": "sec-s-xxxxx",
"quoteV2": {
"price": 150.25,
"bid": 150.20,
"ask": 150.30,
"currency": "USD",
"marketStatus": "OPEN",
# ... more fields
}
}
}
}
}
Testing Subscriptions
A test script is included to demonstrate subscription functionality:
# Stream quotes for a ticker
python test_sub.py --ticker AAPL
# Stream activity updates
python test_sub.py --activity
# Multiple subscriptions
python test_sub.py --ticker AAPL --activity --seconds 60
For more options, see test_sub.py --help.
🎮 Interactive Trading Tool
An interactive command-line interface is included for easy testing and trading:
python interactive_trade.py
Features
- 🔐 Interactive Authentication: Enter credentials securely
- 🔍 Security Search: Search by ticker or browse popular stocks
- 📊 Real-Time Quotes: View detailed security information
- 📈 Stock Trading: Place market and limit orders
- 📉 Options Trading: Browse option chains and trade options
- 💼 Account Selection: Choose from your trading accounts
- ✅ Order Confirmation: Review before executing
Usage
The script will guide you through:
- Authentication - Login with your credentials
- Security Search - Find stocks or options
- Quote Display - View real-time data
- Account Selection - Choose your trading account
- Trade Type - Select stocks or options
- Order Placement - Execute with confirmation
📖 Examples
Example 1: Portfolio Summary
from wealthsimple_python import WealthsimpleV2
ws = WealthsimpleV2()
# Get all positions
positions = ws.get_positions()
total_value = 0
print("Your Portfolio:")
print("=" * 60)
for position in positions:
symbol = position['security']['stock']['symbol']
quantity = position['quantity']
value = position['totalValue']['amount']
pnl = position.get('profitLossAmount', {}).get('amount', 0)
total_value += value
print(f"{symbol:6s} | Qty: {quantity:>6} | Value: ${value:>10.2f} | P/L: ${pnl:>8.2f}")
print("=" * 60)
print(f"Total Portfolio Value: ${total_value:,.2f}")
Example 2: Buy Stock with Quote Check
from wealthsimple_python import WealthsimpleV2
ws = WealthsimpleV2()
# Define trade parameters
ticker = 'AAPL'
exchange = 'NASDAQ'
quantity = 10
# Get security ID and current quote
security_id = ws.get_ticker_id(ticker, exchange)
quote = ws.get_security_quote(security_id)
current_price = quote['price']
print(f"Current price of {ticker}: ${current_price}")
# Calculate limit price (1% below current)
limit_price = round(current_price * 0.99, 2)
print(f"Placing limit buy at ${limit_price}")
# Get account
accounts = ws.get_accounts()
account_id = accounts[0]['id']
# Place order
order = ws.limit_buy(account_id, security_id, quantity, limit_price)
print(f"Order placed: {order['orderId']}")
print(f"Status: {order['status']}")
Example 3: Sell Covered Call
from wealthsimple_python import WealthsimpleV2
ws = WealthsimpleV2()
# Get security (stock you own)
ticker = 'AAPL'
security_id = ws.get_ticker_id(ticker, 'NASDAQ')
# Get option expiry dates
expiry_dates = ws.get_option_expiry_dates(security_id)
nearest_expiry = expiry_dates[0]
print(f"Expiry date: {nearest_expiry}")
# Get call options
calls = ws.get_option_chain(security_id, nearest_expiry, 'CALL')
# Find out-of-the-money call (strike > current price)
quote = ws.get_security_quote(security_id)
current_price = quote['price']
otm_calls = [c for c in calls if c['strikePrice']['amount'] > current_price]
if otm_calls:
selected_call = otm_calls[0] # Nearest OTM strike
strike = selected_call['strikePrice']['amount']
option_id = selected_call['id']
# Get bid price
bid = selected_call['quote']['bid']
print(f"Selling covered call at ${strike} strike for ${bid} premium")
# Sell to open
account_id = ws.get_accounts()[0]['id']
order = ws.sell_option(account_id, option_id, quantity=1, limit_price=bid)
print(f"Order placed: {order['orderId']}")
Example 4: Monitor Multiple Positions
from wealthsimple_python import WealthsimpleV2
import time
ws = WealthsimpleV2()
def display_portfolio():
positions = ws.get_positions()
print("\n" + "=" * 80)
print(f"{'Symbol':<10} {'Qty':<8} {'Value':<12} {'P/L $':<12} {'P/L %':<10}")
print("=" * 80)
for pos in positions:
symbol = pos['security']['stock']['symbol']
qty = pos['quantity']
value = pos['totalValue']['amount']
pnl_amt = pos.get('profitLossAmount', {}).get('amount', 0)
pnl_pct = pos.get('profitLossPercentage', 0)
print(f"{symbol:<10} {qty:<8} ${value:<11.2f} ${pnl_amt:<11.2f} {pnl_pct:>7.2f}%")
# Display portfolio every 60 seconds
while True:
display_portfolio()
time.sleep(60)
Example 5: Real-Time Price Monitor
from wealthsimple_python import WealthsimpleV2
import asyncio
ws = WealthsimpleV2()
async def monitor_price():
# Get security ID
security_id = ws.get_ticker_id('AAPL')
print("Monitoring AAPL price... (Ctrl+C to stop)")
print("=" * 60)
async with ws.subscribe() as sub:
async for msg in sub.stream_quotes([security_id]):
quote = msg['payload']['data']['securityQuoteUpdates']['quoteV2']
price = quote['price']
bid = quote['bid']
ask = quote['ask']
spread = ask - bid if bid and ask else None
print(f"\rAAPL: ${price:.2f} | Bid: ${bid:.2f} | Ask: ${ask:.2f} | "
f"Spread: ${spread:.2f}" if spread else f"AAPL: ${price:.2f}",
end='', flush=True)
asyncio.run(monitor_price())
Example 6: Real-Time Order Monitor
from wealthsimple_python import WealthsimpleV2
import asyncio
ws = WealthsimpleV2()
async def watch_orders():
print("Monitoring for new activities...")
print("=" * 60)
async with ws.subscribe() as sub:
async for msg in sub.stream_activity_updates():
update = msg['payload']['data']['activityFeedUpdates']
print(f"\n🔔 New activity detected!")
print(f" Account: {update['accountId']}")
print(f" Activity ID: {update['activityId']}")
print(f" Updated: {update['updatedAt']}")
# You can fetch full activity details using get_activities()
# activities = ws.get_activities(limit=1)
# if activities:
# print(f" Type: {activities[0].get('type')}")
asyncio.run(watch_orders())
Example 7: Historical Portfolio Chart
from datetime import date, timedelta
from wealthsimple_python import WealthsimpleV2
ws = WealthsimpleV2()
# Fetch one year of daily data
start_date = (date.today() - timedelta(days=365)).isoformat()
result = ws.get_identity_historical_financials(
start_date=start_date,
currency="CAD",
include_simple_returns=True,
)
edges = result.get("edges", [])
print(f"Retrieved {len(edges)} daily records")
for edge in edges:
node = edge["node"]
date = node["date"]
value = float(node["netLiquidationValueV2"]["amount"])
deposits = float(node["netDepositsV2"]["amount"])
ret = node.get("simpleReturns", {})
print(rate)
rate = float(ret.get("rate", 0))
print(f"{date}: value=${value:,.2f} deposits=${deposits:,.2f} return={rate:.2f}%")
🔧 Advanced Usage
Custom GraphQL Queries
# Execute custom GraphQL queries
query = """
query CustomQuery($securityId: ID!) {
security(id: $securityId) {
id
stock {
symbol
name
currency
}
}
}
"""
variables = {"securityId": security_id}
result = ws.graphql_query("CustomQuery", query, variables)
Error Handling
from wealthsimple_python import WealthsimpleV2
try:
ws = WealthsimpleV2(username='user@email.com', password='wrong_password')
except Exception as e:
print(f"Authentication failed: {e}")
try:
security_id = ws.get_ticker_id('INVALID_TICKER', 'NASDAQ')
if not security_id:
print("Security not found")
except Exception as e:
print(f"Error: {e}")
Token Management
Tokens are automatically saved to and loaded from keyring (secure OS credential storage):
# Authenticate once - tokens are securely saved to keyring
ws = WealthsimpleV2(username='your@email.com', password='yourpassword')
# Tokens stored in macOS Keychain / Windows Credential Locker / Linux Secret Service
# Later sessions (even after reboot) - tokens are automatically loaded
ws = WealthsimpleV2() # Automatically uses saved tokens from keyring
# Logout and clear all tokens
ws.logout() # Removes tokens from keyring and environment
# Or manually provide tokens (overrides keyring and environment variables)
ws = WealthsimpleV2(access_token='your_token', refresh_token='your_refresh')
Security Note: Using keyring is highly recommended as it provides OS-level encryption and secure storage. Tokens are never stored in plain text files.
📋 API Reference
Class: WealthsimpleV2
Initialization
ws = WealthsimpleV2(
username=None,
password=None,
otp=None,
client_id=None,
access_token=None,
refresh_token=None
)
Authentication Methods
| Method | Description |
|---|---|
authenticate(username, password, otp=None) |
Authenticate with username/password |
refresh_access_token() |
Manually refresh the access token |
logout() |
Clear all tokens from keyring and environment |
Security Methods
| Method | Description |
|---|---|
search_securities(query, security_group_ids=None) |
Search for securities by ticker or name |
get_security(security_id, currency=None) |
Get detailed security information |
get_security_quote(security_id, currency=None) |
Get real-time quote |
get_nearest_market_open() |
Get nearest market open details |
get_market_buffer(country='CA', is_option=False) |
Get the market buffer multiplier |
get_security_status(security_id) |
Get security trading status |
get_intraday_chart_quotes(...) |
Get broker-native intraday chart quotes |
get_ticker_id(ticker, exchange=None) |
Get security ID from ticker symbol |
parse_ticker_market(ticker_market) |
Split TICKER.MARKET into parts |
_is_us_exchange(exchange) |
Check whether an exchange is US-listed |
_is_canadian_exchange(exchange) |
Check whether an exchange is CA-listed |
resolve_security_id(ticker_market) |
Resolve TICKER.MARKET to a security ID |
Account Methods
| Method | Description |
|---|---|
get_accounts(identity_id=None) |
Get all accounts |
create_internal_transfer(source_account_id, destination_account_id, amount, currency='CAD') |
Transfer funds between accounts |
get_account_financials(account_ids, currency='CAD') |
Get account balances |
get_account_current_financials(account_id, currency='CAD', start_date=None) |
Get current account financials |
get_account_graph_data(account_id, currency='CAD', time_range='ONE_DAY', market_session='REGULAR', include_simple_returns=False) |
Get account graph data |
get_positions(identity_id=None, account_ids=None, security_types=None) |
Get current positions |
get_activities(account_ids=None, types=None, limit=50) |
Get activity feed |
get_identity(identity_id=None) |
Get user identity information |
get_identity_historical_financials(identity_id=None, currency='CAD', start_date=None, end_date=None, account_ids=None, include_simple_returns=False, ...) |
Get daily historical portfolio financials |
Stock Trading Methods
| Method | Description |
|---|---|
market_buy(account_id, security_id, quantity) |
Place market buy order |
market_sell(account_id, security_id, quantity) |
Place market sell order |
limit_buy(account_id, security_id, quantity, limit_price) |
Place limit buy order |
limit_sell(account_id, security_id, quantity, limit_price) |
Place limit sell order |
stop_limit_buy(account_id, security_id, quantity, limit_price, stop_price) |
Place stop-limit buy |
stop_limit_sell(account_id, security_id, quantity, limit_price, stop_price) |
Place stop-limit sell |
create_order(account_id, security_id, quantity, ...) |
Create custom order |
cancel_order(external_id) |
Cancel an existing order |
Options Trading Methods
| Method | Description |
|---|---|
get_option_chain(security_id, expiry_date, option_type, ...) |
Get option chain |
get_option_expiry_dates(security_id, min_date=None, max_date=None) |
Get available expiry dates |
get_option_transaction_fees(side, premium, quantity, currency) |
Calculate option fees |
buy_option(account_id, option_id, quantity, limit_price, ...) |
Buy option contract |
sell_option(account_id, option_id, quantity, limit_price, ...) |
Sell option contract |
Subscription Methods
| Method | Description |
|---|---|
subscribe(device_id=None) |
Create a WebSocket subscription client |
WealthsimpleSubscriptions.stream_quotes(security_ids, ...) |
Stream real-time quote updates |
WealthsimpleSubscriptions.stream_activity_updates() |
Stream activity feed updates |
WealthsimpleSubscriptions.stream_identity_updates(...) |
Stream identity/account updates |
WealthsimpleSubscriptions.stream_balance_changes(...) |
Stream balance change notifications |
WealthsimpleSubscriptions.ping() |
Send keep-alive ping |
Utility Methods
| Method | Description |
|---|---|
graphql_query(operation_name, query, variables=None) |
Execute custom GraphQL query |
Class: WealthsimpleSubscriptions
WebSocket subscription client for real-time data streaming. Created via ws.subscribe().
Context Manager Usage
async with ws.subscribe() as sub:
# Use subscription methods
async for msg in sub.stream_quotes([security_id]):
# Process messages
pass
Methods
| Method | Description |
|---|---|
connect() |
Establish WebSocket connection |
close() |
Close WebSocket connection |
stream_quotes(security_ids, currency=None) |
Stream real-time quotes for securities |
stream_activity_updates() |
Stream activity feed notifications |
stream_identity_updates(identity_id=None) |
Stream identity/account core updates |
stream_balance_changes(custodian_account_ids) |
Stream custodian account balance changes |
ping() |
Send ping to keep connection alive |
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Areas for Improvement
- Add unit tests
- Implement rate limiting
- Support for multi-leg option strategies
- Enhanced error handling and retry logic
- CLI improvements
🔗 Resources
- API Documentation: See
README.mdfor detailed API information - Wealthsimple: https://www.wealthsimple.com/
📞 Support
For questions, issues, or feature requests, please open an issue on GitHub.
⚖️ Legal
This project is not affiliated with, officially maintained by, or endorsed by Wealthsimple. All trademarks are the property of their respective owners.
USE AT YOUR OWN RISK. This software is provided "as is" without warranty of any kind. The authors are not responsible for any financial losses or damages resulting from the use of this software.
Always verify orders before executing and start with small amounts when testing.
📚 Legacy API
The original REST API (archive/wealthsimple.py) is still available but uses deprecated Trade API endpoints. It is strongly recommended to migrate to the v2 GraphQL API for new projects.
Legacy API Quick Reference
The legacy API provided basic functionality:
# Legacy API (deprecated)
from archive.wealthsimple import wealthsimple
ws = wealthsimple('email', 'password', MFA='123456')
accounts = ws.accounts()
tick_id = ws.tick_id('AAPL', 'NASDAQ')
ws.limit_buy(tick_id, 10, 140)
For legacy API documentation, see archive/README.md.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file wealthsimple_python-2.0.0.tar.gz.
File metadata
- Download URL: wealthsimple_python-2.0.0.tar.gz
- Upload date:
- Size: 50.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
067695e558b0ec50e6d036a637359e3537477584ee58641f3a85a2471b13ccc2
|
|
| MD5 |
d6c5a759e04cfe3c47431b481efc0f21
|
|
| BLAKE2b-256 |
d229a964bd9e497e2665148425667f966e67ae8bbb47e244af42ff8c8db03717
|
Provenance
The following attestation bundles were made for wealthsimple_python-2.0.0.tar.gz:
Publisher:
publish.yml on henryhuangh/wealthsimple-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
wealthsimple_python-2.0.0.tar.gz -
Subject digest:
067695e558b0ec50e6d036a637359e3537477584ee58641f3a85a2471b13ccc2 - Sigstore transparency entry: 1676945193
- Sigstore integration time:
-
Permalink:
henryhuangh/wealthsimple-python@5b141177b9b4a6c938a66c023a18fc7d941a0648 -
Branch / Tag:
refs/tags/v2.0.0 - Owner: https://github.com/henryhuangh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5b141177b9b4a6c938a66c023a18fc7d941a0648 -
Trigger Event:
release
-
Statement type:
File details
Details for the file wealthsimple_python-2.0.0-py3-none-any.whl.
File metadata
- Download URL: wealthsimple_python-2.0.0-py3-none-any.whl
- Upload date:
- Size: 29.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
082531659c193e70766fa0f01ddb37c6fcd87928b7c6d144c4247281ef72adae
|
|
| MD5 |
1b2fe0e62d1011971dcd07a286be40f5
|
|
| BLAKE2b-256 |
1817ae09f9acb79ac289e351f7fc641dfd7ffee0fc9b113cbce5f959604e39ff
|
Provenance
The following attestation bundles were made for wealthsimple_python-2.0.0-py3-none-any.whl:
Publisher:
publish.yml on henryhuangh/wealthsimple-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
wealthsimple_python-2.0.0-py3-none-any.whl -
Subject digest:
082531659c193e70766fa0f01ddb37c6fcd87928b7c6d144c4247281ef72adae - Sigstore transparency entry: 1676945196
- Sigstore integration time:
-
Permalink:
henryhuangh/wealthsimple-python@5b141177b9b4a6c938a66c023a18fc7d941a0648 -
Branch / Tag:
refs/tags/v2.0.0 - Owner: https://github.com/henryhuangh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5b141177b9b4a6c938a66c023a18fc7d941a0648 -
Trigger Event:
release
-
Statement type: