| name | schwab-trader |
| description | 此技能用于通过 Charles Schwab Trader API 进行美股交易操作,包括 OAuth 认证、行情数据获取、下单交易(股票/期权)、账户查询和实时流式数据订阅。支持 schwab-py 封装库或直接调用官方 REST API。适用于用户提到 Schwab API、schwab-py、Charles Schwab 自动化交易、美股 API 下单、期权链查询、实时行情流式订阅、get_price_history、place_order、thinkorswim API 的场景。 |
Schwab Trader API 调用
Schwab 提供标准 REST API(api.schwabapi.com),无官方 Python SDK。
| 调用方式 | 优点 | 适用场景 |
|---|
schwab-py(非官方库) | 自动处理 OAuth 与 token 刷新,代码简洁 | Python 项目首选 |
直接 requests | 无依赖,逻辑完全透明,可移植任意语言 | 学习原理、非 Python 环境、轻量脚本 |
两种方式调用完全相同的官方端点,区别仅在于 OAuth 处理。认证流程和直接调用示例见 references/auth-and-tokens.md。
不适用于:thinkorswim 专有功能(thinkScript、策略分析器、内置回测无 API)、期货/外汇下单(暂未开放)、历史期权定价、沙盒测试(Schwab 无沙盒环境)。
关于 ThinkOrSwim API:TOS 有未文档化的内部 WebSocket 协议(wsjson),被社区逆向工程,仅有 Node.js 实现。用 Schwab REST API 可操作与 TOS 完全相同的账户持仓,大多数自动化场景 REST API 已足够。
安装与注册
pip install schwab-py
注册:在 developer.schwab.com 申请 App,选 Accounts and Trading Production,callback URL 设为 https://127.0.0.1:8182,等待审批(1-5 工作日)得到 App Key 和 App Secret。详见 references/auth-and-tokens.md。
工作流
复制此 checklist 追踪进度:
Schwab API 进度:
- [ ] 步骤 1: 初始化认证客户端
- [ ] 步骤 2: 获取账户哈希
- [ ] 步骤 3: 选择任务类型(行情 / 交易 / 流式)
- [ ] 步骤 4: 调用 API,检查响应
- [ ] 步骤 5: 处理数据
- [ ] 步骤 6: 运行质量检查
步骤 1: 初始化认证客户端
from schwab import auth
c = auth.easy_client(
api_key="YOUR_APP_KEY",
app_secret="YOUR_APP_SECRET",
callback_url="https://127.0.0.1:8182",
token_path="/path/to/token.json",
)
步骤 2: 获取账户哈希
API 不接受裸账号,必须先获取哈希:
resp = c.get_account_numbers()
resp.raise_for_status()
account_hash = resp.json()[0]["hashValue"]
步骤 3: 选择任务类型
| 需求 | 方法 |
|---|
| 日线 / 分钟历史行情 | c.get_price_history_every_day() / c.get_price_history_every_minute() |
| 实时快照报价 | c.get_quotes(["AAPL", "MSFT"]) |
| 期权链(含 Greeks) | c.get_option_chain(symbol) |
| 账户余额 / 持仓 | c.get_account(account_hash, fields=[...]) |
| 下单 | c.place_order(account_hash, order_spec) |
| 实时流式行情 | StreamClient + WebSocket(步骤 4c) |
| 市场交易时间 | c.get_market_hours(markets) |
步骤 4a: 行情数据
resp = c.get_price_history_every_day("AAPL")
resp.raise_for_status()
candles = resp.json()["candles"]
resp = c.get_quotes(["AAPL", "MSFT", "SPY"])
resp.raise_for_status()
quotes = resp.json()
resp = c.get_option_chain("AAPL")
resp.raise_for_status()
chain = resp.json()
完整参数和期权链解析见 references/api-reference.md。
步骤 4b: 下单交易
from schwab.orders.equities import equity_buy_market, equity_buy_limit
from schwab.orders.options import option_buy_to_open_limit, bull_call_vertical_open, OptionSymbol
from schwab.orders.common import Duration, one_cancels_other
import datetime
c.place_order(account_hash, equity_buy_market("AAPL", 1).build()).raise_for_status()
c.place_order(
account_hash,
equity_buy_limit("GOOG", 10, 175.0).set_duration(Duration.GOOD_TILL_CANCEL).build()
).raise_for_status()
sym = OptionSymbol("AAPL", datetime.date(2025, 12, 19), "C", "200").build()
c.place_order(account_hash, option_buy_to_open_limit(sym, 1, 5.50).build()).raise_for_status()
long_c = OptionSymbol("SPY", datetime.date(2025, 12, 19), "C", "550").build()
short_c = OptionSymbol("SPY", datetime.date(2025, 12, 19), "C", "560").build()
c.place_order(account_hash, bull_call_vertical_open(long_c, short_c, 1, 5.0).build()).raise_for_status()
完整订单类型、OCO、触发订单见 references/common-recipes.md。
步骤 4c: 实时流式数据(WebSocket)
from schwab.streaming import StreamClient
import asyncio, json
stream_client = StreamClient(c, account_id=YOUR_ACCOUNT_ID)
async def stream_quotes():
await stream_client.login()
stream_client.add_level_one_equity_handler(
lambda msg: print(json.dumps(msg, indent=2))
)
await stream_client.level_one_equity_subs(["AAPL", "TSLA"])
while True:
await stream_client.handle_message()
asyncio.run(stream_quotes())
可用流式服务:Level1(股票/期权/期货/外汇)、Level2 委托簿(NYSE/NASDAQ)、分钟 OHLCV、Screener 涨跌榜、账户活动。完整列表和字段见 references/api-reference.md。
步骤 5: 账户数据处理
from schwab.client import Client
from schwab.utils import Utils
resp = c.get_account(account_hash, fields=[Client.Account.Fields.POSITIONS])
resp.raise_for_status()
positions = resp.json()["securitiesAccount"].get("positions", [])
order_id = Utils(c, account_hash).extract_order_id(resp)
c.cancel_order(order_id, account_hash).raise_for_status()
步骤 6: 质量检查
遇到错误时排查顺序:
- 401 → access token 过期(schwab-py 自动处理);若仍报 401 检查系统时钟
- 401 + 重新登录仍失败 → refresh token 超 7 天过期,需重新走完整 OAuth 登录
- 403 → App 未审批或期权权限未开通
- 429 → 超 120 次/分钟,加
time.sleep(0.5) 或指数退避
- 下单被拒 → 检查账户余额、期权交易权限级别、市场是否开盘
Token 生命周期
| Token | 有效期 | 处理 |
|---|
| Access Token | 30 分钟 | schwab-py 自动刷新 |
| Refresh Token | 7 天 | 必须手动刷新,建议每 6 天触发一次 |
常见陷阱
- 期权符号格式严格(OCC 标准),推荐从
get_option_chain() 直接取,不要手拼
- 流式客户端必须先
add_xxx_handler(),再调用 xxx_subs(),顺序颠倒会丢消息
- 每个 token 文件只能对应一个 Client 实例,多实例会导致 OAuth 状态冲突
- 历史 K 线不支持期权和期货(只有行情流)
- 期货和外汇目前只有流式行情,REST 下单暂不支持
质量检查
参考资料(按需加载)
references/auth-and-tokens.md - OAuth 完整流程(schwab-py 和直接 requests 两种)、token 刷新策略、故障排查
references/api-reference.md - 所有端点、行情参数、订单类型枚举、期权符号格式、流式服务列表
references/common-recipes.md - 完整代码:行情采集、期权策略下单、账户查询、流式监控、批量限速