# Schwab Marketdata Ops

> |

- **Type:** Skill
- **Install:** `agentstack add skill-kevinkda-schwab-marketdata-skill-schwab-marketdata-ops`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [kevinkda](https://agentstack.voostack.com/s/kevinkda)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [kevinkda](https://github.com/kevinkda)
- **Source:** https://github.com/kevinkda/schwab-marketdata-skill/tree/main/schwab-marketdata-ops

## Install

```sh
agentstack add skill-kevinkda-schwab-marketdata-skill-schwab-marketdata-ops
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# schwab-marketdata-ops

> **Responsibility disclaimer**: 本工具调用 Schwab Market Data
> Production API。用户须自行阅读  与
> ，并对其使用方式的合规性负全责。
> `schwab-marketdata-mcp` 与 `schwab-marketdata-skill` 的作者不对违反
> Schwab ToS 的使用承担责任。

## Activation handshake (run first)

激活本 skill 时**第一步**必须调用 `get_server_info`，确认返回的
`server_version` 落在 frontmatter `compatible_mcp_version` 范围内：

```text
get_server_info()
→ { "server_version": "0.1.x", "supported_tools": [...13 names...] }
```

若版本不匹配或调用失败：**立即停下**，告知用户升级
`schwab-marketdata-mcp` 或本 skill，**不要**继续执行业务调用。

完整握手流程（含 token 体检）：

```text
1. get_server_info()  → 校验 server_version ∈ compatible_mcp_version
2. health_check()     → 校验 token_state == "valid"
                       且 token_expires_in_days >= 0.5
3. 若任一步骤失败 → 立即停下，按 reference 修复
```

## Quick Start — 7 步从零到 hello-world

第一次接入或换台机器，按顺序读以下 7 个 step 文件：

| Step | 文件 | 期望产出 |
| ---- | ---- | -------- |
| 1 | [`references/quick-start/step-1-developer-portal-app.md`](references/quick-start/step-1-developer-portal-app.md) | Schwab Developer Portal app 已创建，App Key / Secret 已拿到 |
| 2 | [`references/quick-start/step-2-credentials-env.md`](references/quick-start/step-2-credentials-env.md) | `.env` 已填好（chmod 600），pre-commit 钩子已装 |
| 3 | [`references/quick-start/step-3-first-oauth.md`](references/quick-start/step-3-first-oauth.md) | `token.json` 已落地（chmod 600） |
| 4 | [`references/quick-start/step-4-token-health-check.md`](references/quick-start/step-4-token-health-check.md) | `health` 模块退出 0，`token_state == "valid"` |
| 5 | [`references/quick-start/step-5-first-mcp-tool-call.md`](references/quick-start/step-5-first-mcp-tool-call.md) | 最小 Python MCP client 跑通 `get_quote("VOO")` |
| 6 | [`references/quick-start/step-6-cursor-integration.md`](references/quick-start/step-6-cursor-integration.md) | `~/.cursor/mcp.json` 注册成功，agent 能调 13 个 tool |
| 7 | [`references/quick-start/step-7-cron-launchd-setup.md`](references/quick-start/step-7-cron-launchd-setup.md) | cron / launchd 健康巡检已启用，桌面通知通道已自检 |

## Common usage scenarios

仿照 helis "I want to..." 路由表，按用户意图直达对应工具与参考文档。

### "我要查实时报价"

| 场景 | 用什么 | 备注 |
| ---- | ------ | ---- |
| 单只股票 / ETF / 指数 | `get_quote("AAPL")` / `get_quote("$SPX")` | symbol 必须 UPPERCASE；指数加 `$` 前缀 |
| 批量 (≤ 50) | `get_quotes(symbols=[…])` | 超 50 自行分批，否则 `SchwabValidationError(field="symbols")` |
| 期权合约 | `get_quote("AAPL  240119C00170000")` | **OSI 21 字符**：root 6 字符（含空格右补齐）+ YYMMDD + C/P + 8 位 strike(×1000) |

→ Schema 完整定义见 [`references/tools/tool-reference-quotes.md`](references/tools/tool-reference-quotes.md)。

### "我要研究期权链"

1. `get_option_expiration_chain(symbol)` — 先取可用到期日列表
2. `get_option_chain(symbol, contract_type=…, strike_count=…, strike_range=…)` — 按 ATM/OTM/ITM 拉具体合约
3. 多步骤研究型工作流 → 转用 `schwab-marketdata-workflows` skill 的 `option-chain-research.md` playbook

→ Schema 见 [`references/tools/tool-reference-options.md`](references/tools/tool-reference-options.md)；
Greeks 时效性见 playbook Cautions 段与
[`references/concepts/osi-option-symbol.md`](references/concepts/osi-option-symbol.md)。

### "Token 过期了怎么办"

```text
1. health_check()
   → 看 token_state ∈ {"valid","missing","malformed","insecure_perms"}
   → 看 token_expires_in_days（< 0.5 强烈建议立即 reauthorize）
2. 若 SchwabAuthError(reason="refresh_token_expired" | "token_not_initialized")：
   uv run python -m schwab_marketdata_mcp.auth login_flow   # 默认
   # 或 manual_flow（headless / SSH-only / WSL2）
3. 重新 health_check() 确认 token_state == "valid"
```

→ 完整对照表与逐步处置见
[`references/troubleshooting/auth-overview.md`](references/troubleshooting/auth-overview.md)；
OAuth 流程详解见
[`references/oauth/oauth-overview.md`](references/oauth/oauth-overview.md)；
token 生命周期见
[`references/oauth/oauth-token-lifecycle.md`](references/oauth/oauth-token-lifecycle.md)。

### "限流被拒了怎么办"

| 症状 | 处置 |
| ---- | ---- |
| `SchwabRateLimitError(retry_after_seconds=N)` | 等待 N 秒后重试；连续两次告诉用户 |
| stderr 出现 `{"event":"rate_limit_warning","remaining":<20}` | 将批量请求改用 `get_quotes`（一次 50）；或降低 `SCHWAB_RATE_LIMIT_PER_MIN` |
| 0 slots 立即 raise | agent 端实施 retry-with-backoff（schwab-py 内部已重试 `SCHWAB_MAX_RETRIES` 次） |

→ token-bucket 行为与排查见
[`references/operations/rate-limit-token-bucket.md`](references/operations/rate-limit-token-bucket.md)；
4 种限流症状分流见
[`references/troubleshooting/rate-limit-overview.md`](references/troubleshooting/rate-limit-overview.md)。

### "我想看 server 健康"

```text
get_server_info()  → server_version / mcp_sdk_version / schwab_py_version / supported_tools
health_check()     → token_state / token_expires_in_days / rate_limit_remaining_per_min / recent_error_count_24h
```

两者组合即可在 30 秒内回答 "MCP server 是否健康、token 是否有效、限流是否已被吃掉"。

→ Schema 见 [`references/tools/tool-reference-meta.md`](references/tools/tool-reference-meta.md)。

### "我要把数据画 K 线 / 拉历史"

```text
get_price_history(symbol, period_type, period?, frequency_type, frequency?, ...)
```

`(period_type, period, frequency_type, frequency)` 四元组只接受
**受限组合**，非法组合服务端静默 400。MCP server 在 Pydantic 层提前
拦截。

→ Schema + 合法组合表见
[`references/tools/tool-reference-price-history.md`](references/tools/tool-reference-price-history.md)；
笛卡尔积错误处置见
[`references/troubleshooting/validation-pricehistory-cartesian.md`](references/troubleshooting/validation-pricehistory-cartesian.md)。

### "我要查 instrument 元数据 / 公司基本面"

| 场景 | 用什么 |
| ---- | ------ |
| 已知 ticker → 元数据 | `search_instruments(symbols=["AAPL"], projection="SYMBOL_SEARCH")` |
| 已知 ticker → 基本面 | `search_instruments(symbols=["AAPL"], projection="FUNDAMENTAL")` |
| 已知 9 位 CUSIP | `get_instrument_by_cusip(cusip="037833100")` |
| 公司名模糊查找 | `search_instruments(symbols=["TESLA"], projection="DESCRIPTION_SEARCH")` |

→ Schema 见 [`references/tools/tool-reference-instruments.md`](references/tools/tool-reference-instruments.md)。

### "我要看 top movers / 当日异动"

```text
get_movers(index="DJI"|"COMPX"|"SPX"|..., sort_order=..., frequency=...)
```

`index` 等枚举值必须传 enum 名（`"DJI"`），不是 wire 值（`"$DJI"`）。

→ Schema + 枚举详解见 [`references/tools/tool-reference-movers.md`](references/tools/tool-reference-movers.md)。

### "我要从自己的 Python / TypeScript / Rust 应用调"

| 客户端 | 文件 |
| ------ | ---- |
| Python（mcp SDK） | [`references/integration/python-mcp-client.md`](references/integration/python-mcp-client.md) |
| TypeScript / Node | [`references/integration/typescript-mcp-client.md`](references/integration/typescript-mcp-client.md) |
| Rust（rmcp 或自实现） | [`references/integration/rust-mcp-client.md`](references/integration/rust-mcp-client.md) |
| Shell + jq pipe | [`references/integration/cli-jq-pipe.md`](references/integration/cli-jq-pipe.md) |

## Data coverage clarifications

### `get_price_history` 就是 K 线 / candlestick 接口

如果你在找 OHLCV bar（candle / kline / candlestick），`get_price_history`
就是对应的 tool。返回结构包含 `candles[]`，每根 candle 含 `open` / `high`
/ `low` / `close` / `volume` / `datetime`（毫秒级 epoch）。

支持的粒度由 `(period_type, frequency_type, frequency)` 三元组决定：

- `period_type=DAY`：`MINUTE` × {1, 5, 10, 15, 30}（约 48 天 / 9 个月历史）
- `period_type=MONTH`：`DAILY` / `WEEKLY`（最多 6 个月）
- `period_type=YEAR`：`DAILY` / `WEEKLY` / `MONTHLY`（**最多 20 年**）
- `period_type=YEAR_TO_DATE`：`DAILY` / `WEEKLY`（年初至今）

亚分钟级 K 线（秒级、tick 级）不在 Schwab Market Data API 范围内。

→ 完整合法组合表 + 笛卡尔积错误处置见
[`references/tools/tool-reference-price-history.md`](references/tools/tool-reference-price-history.md)
与 MCP 仓库的
[README → "Data coverage clarifications"](https://github.com/kevinkda/schwab-marketdata-mcp/blob/main/README.md#data-coverage-clarifications)。

### Schwab Market Data API **不提供**的数据

以下数据在 Schwab Market Data Production API 上**架构性不可用**，需要
第三方数据源补齐：

- **Time & sales / tape（逐笔成交）** —— Schwab 2024 API 迁移移除
  `TIMESALE_*` services 后，REST 与 Streaming 都没有。
- **Tick-by-tick history（逐笔历史）** —— Schwab API 不提供。
- **Level 2 历史快照** —— 仅 Streaming，REST 无历史回放。
- **基本面 / 财报时间序列**（EPS 历史、营收历史等）—— `quotes` 带
  FUNDAMENTAL 字段但**没有**对应历史接口。
- **新闻 / SEC 文件** —— Market Data API 不提供。

各数据类的推荐第三方（Polygon.io、Tiingo、Alpaca、Databento、FMP、SEC
EDGAR）见 MCP 仓库
[README → "Data coverage clarifications"](https://github.com/kevinkda/schwab-marketdata-mcp/blob/main/README.md#data-coverage-clarifications)。

### Trader API 不在范围内

**Trader API 端点**（账户、订单、成交、持仓）显式排除在范围之外 ——
本 skill 只涵盖**只读 Market Data**。如果用户请求下单 / 修改持仓，立刻
拒绝并指向 MCP README 的 "Responsible use" 段。

## Decision tree — pick the right tool

| 用户意图                                  | 用什么 tool                                              |
| ----------------------------------------- | -------------------------------------------------------- |
| 单只股票/ETF/指数现价                     | `get_quote(symbol="AAPL")`                               |
| 一次性查多只行情（≤50）                   | `get_quotes(symbols=[...])`                              |
| 历史 K 线 / OHLC                          | `get_price_history(symbol, period_type, …)`              |
| 期权链快照                                | `get_option_chain(symbol, contract_type=…)`              |
| 期权到期日列表                            | `get_option_expiration_chain(symbol)`                    |
| 多市场开闭市状态                          | `get_market_hours(markets_list=[…])`                     |
| 单一市场开闭市状态                        | `get_market_hour_single(market_id)`                      |
| 当日 top movers                           | `get_movers(index, sort_order)`                          |
| 用 ticker 模糊查找 instrument             | `search_instruments(symbols, projection)`                |
| 用 9 位 CUSIP 精确查找                    | `get_instrument_by_cusip(cusip)`                         |
| 调试 token 状态 / 错误计数                | `health_check()`                                         |
| 取 server 元数据（版本、13 tool 列表）    | `get_server_info()`                                      |
| 实时 bid/ask/last 或 1 分钟 K 线（实验性）| `get_streaming_snapshot(symbols, service, duration_ms?)` |

**枚举值**全部用 `models.py` 中定义的 **enum 名**（如 `"VOLUME"`、
`"NASDAQ"`、`"DAY"`），MCP server 内部会翻译成 schwab-py 的 wire 值。

## Key Concepts

| 术语 | 定义 |
| ---- | ---- |
| **access_token** | Schwab OAuth bearer token，**90 分钟**有效；schwab-py 在每次 API 调用前自动续签，agent 无需关心。 |
| **refresh_token** | **rotate-on-use**：每次刷新都签发新 refresh_token，**7 天**绝对寿命到期后必须重新 `login_flow`，不可绕过（Schwab OAuth 设计如此）。 |
| **TokenState** | `health_check()` 返回的 4 个枚举：`valid`（可用） / `missing`（无 token.json） / `insecure_perms`（权限非 600/700） / `malformed`（JSON 损坏或字段缺失）。前三种由 `auth login_flow` 修复，第四种通常需要先备份再删除 token.json。 |
| **SchwabAuthError reason** | 业务调用失败时附带的细分原因枚举（6 种）：`refresh_token_expired_soon` / `refresh_token_expired` / `token_not_initialized` / `token_corrupted` / `insecure_token_perms` / `callback_url_mismatch`。每种 reason 在 [`references/troubleshooting/`](references/troubleshooting/) 有独立 5 段处置。 |
| **rate_limit_bucket** | MCP server 用 **token-bucket + 滑动窗口**（容量 = `SCHWAB_RATE_LIMIT_PER_MIN`，默认 120）而非 `asyncio.Semaphore`。区别：Semaphore 在 retry sleep 期间持有 slot，会阻塞其他并发 tool；token-bucket 在 sleep 期间释放 slot，让别的 tool 继续跑。 |
| **OSI option symbol** | 21 字符固定格式：`{ROOT:6}{YYMMDD:6}{C\|P:1}{STRIKE×1000:8}`，root 不足 6 字符用空格右补齐。例：`"AAPL  240119C00170000"` = AAPL 2024-01-19 Call $170.00；`"BRK/B 240419P00350000"`（含 `/`）。可直接传给 `get_quote`。 |
| **pricehistory cartesian product** | Schwab 对 `(period_type, period, frequency_type, frequency)` 四元组只接受**受限组合**，非法组合服务端静默返回 400。MCP server 在 Pydantic 层提前拦截，避免对计费配额的浪费。完整合法组合表见 [`references/tools/tool-reference-price-history.md`](references/tools/tool-reference-price-history.md)。 |
| **Pydantic Literal enum 名 vs wire 值** | 公开 API 一律传 **enum 名**（如 `MoversIndex.DJI` = `"DJI"`），MCP server 在内部翻译成 schwab-py 的 wire 值（如 `"$DJI"`）。这层翻译让 agent 不用记住前缀字符（`$` / `lowercase` / 大小写）。**永远不要**直接传 wire 值，否则触发 `SchwabValidationError`。 |
| **Activation handshake** | skill 激活后必经的两步：`get_server_info` 校验版本兼容、`health_check` 校验 token 状态。任一失败立即停下，不得静默 fallback。 |
| **error normalization** | 所有 schwab-py 异常被 server 包装成 `{"error": "Schwab*Error", ...}` dict 返回，**不抛异常**穿透 MCP 协议；agent 必须永远先查 `error` 字段再读 payload。 |
| **non-redistributable** | Schwab Market Data 数据不可再分发；任何 markdown / 报告写入必须留在私有仓库（workflows skill 用 `gh repo view --json isPrivate` 强制校验）。 |

## Architecture overview

```text
┌────────────────────┐
│  AI agent (this    │  Cursor / Claude / Kiro / 自定义 SDK 客户端
│  skill activates)  │
└────────┬───────────┘
         │ MCP JSON-RPC over stdio
         ▼
┌────────────────────────────────────────────┐
│  schwab-marketdata-mcp（独立仓库）          │
│  Pydantic → token-bucket → schwab-py       │
└──────────┬─────────────────────────────────┘
           │ HTTPS Bearer access_token
           ▼
┌────────────────────────┐
│ Schwab Market Data API │ api.schwabapi.com
└────────────────────────┘
```

完整架构与 5 个核心组件、数据流、信任边界见
[`references/concepts/architecture-overview.md`](references/concepts/architecture-overview.md)。

## Glossary

跨 OAuth / MCP / Schwab API / 限流 / 错误体系的术语速查见
[`references/concepts/glossary.md`](references/concepts/glossary.md)。

11 个最关键概念的 "为什么这样设计" 解释见
[`references/concepts/key-concepts.md`](references/concepts/key-concepts.md)。

## Schema cheat-sheet（详尽版按 family 拆分到 `references/tools/`）

```text
get_quote(symbol: str, fields?: list[QuoteFields])  # symbol must be UPPERCASE; "$SPX"/"AAPL  240119C00170000" also OK
get_quotes(symbols: list[str], fields?, indicative?)  # ≤50 symbols / call

get_price_history(
  symbol, period_type, period?, frequency_type, frequency?,
  start_datetime?, end_datetime?, need_extended_hours_data?, need_previous_close?
)
# Cartesian-product rules in references/tools/tool-reference-price-history.md.  TZ-aware datetimes only.

get_option_chain(symbol, contract_type?, strike_count?, ...)  # see references
get_option_expiration_chain(symbol)
get_market_hours(markets_list: list[MarketEnum], date?)
get_market_hour_single(market_id, date?)
get_movers(index, sort_order?, frequency?)
search_instruments(symbols, projection)
get_instrument_by_cusip(cusip: 9 alphanumerics)
health_check()
get_server_info()

# Experimental — bounded WebSocket snapshot (≤10 s):
get_streaming_snapshot(
  symbols: list[str],                                # ≤ 20
  service: "LEVELONE_EQUITIES" | "CHART_EQUITY",
  duration_ms: int = 2000                            # [500, 10000]
)
```

## Error normalization

所有业务 tool 在出错时**返回 dict 而非抛出**（让 agent 看到结构化数据）：

| `error` 字段                | 处置                                                                                    |
| --------------------------- | --------------------------------------------------------------------------------------- |
| `SchwabAuthError`           | 看 `reason` → [`references/troubleshooting/auth-overview.md`](references/troubleshooting/auth-overview.md) 路由表（6 种 reason 各有独立子文件）。 |
| `SchwabRateLimitError`      | `retry_after_seconds` 后重试或降低 `SCHWAB_RATE_LIMIT_PER_MIN`。详见 [`references/troubleshooting/rate-limit-overview.md`](references/troubleshooting/rate-limit-overview.md)。 |
| `SchwabTransientError`      | Schwab 5xx/网络抖动；可短期重试，频繁出现告知用户。详见 [`references/troubleshooting/transient-errors.md`](references/troubleshooting/transient-errors.md)。 |
| `SchwabValidationError`     | 输入参数错误；详见 [`references/troubleshooting/validation-overview.md`](references/troubleshooting/validation-overview.md)。 |

完整分流索引见 [`references/error-recovery.md`](references/error-recovery.md)。

## Compatibility matrix

不同 agent 客户端对本 skill frontmatter 字段的支持情况。详细 5 客户端
× 8 字段表格见仓库根 `README.md` 的 "Compatibility matrix" 段。关键
约定：所有非标准 frontmatter 字段（`compatible_mcp_version` /
`required_workspace` / `language_directive` / `auto_activate` /
`activation_handshake` / `compatibility` / `dependencies` /
`governance`）**只是文档化约束**——真正的执行靠本 SKILL.md body 的
运行时检查（`get_server_info` 版本握手 + `cwd` 校验 + 中文回复指令）。
即便客户端不识别 frontmatter 字段，skill 行为仍正确。

## FAQ

25+ 个常见问题速查见
[`references/faq.md`](references/faq.md)（按"入门 / 安装注册 /
OAuth / 限流 / 调用 / 安全 / skill / 故障"7 类分组）。

## Reference index

按主题分类的完整 reference 文件索引，仿 helis SKILL.md 的多级表格结构。

### Quick Start (7 files)

| File | Description |
| ---- | ----------- |
| [quick-start/step-1-developer-portal-app.md](references/quick-start/step-1-developer-portal-app.md) | 注册 Schwab Developer Portal app，拿 App Key / Secret |
| [quick-start/step-2-cred

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [kevinkda](https://github.com/kevinkda)
- **Source:** [kevinkda/schwab-marketdata-skill](https://github.com/kevinkda/schwab-marketdata-skill)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-kevinkda-schwab-marketdata-skill-schwab-marketdata-ops
- Seller: https://agentstack.voostack.com/s/kevinkda
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
