# AGENTS.md — Mobius Quant Engine API

> 面向消费此 API 的 AI agent / 程序客户端 / 集成方的操作手册。多数失败来自字
> 段名 / 大小写 / 端点猜错;本文档就是为了消除这种猜测。
>
> 想看能力全集 / 数据源 / 指标列表? 看 [`README.md`](./README.md)。

---

## 1. 这是什么

Mobius Quant Engine 是一个**多市场行情 + 技术分析 HTTP API**。

- **Crypto**(常驻 store 模式):Binance / Bybit / OKX spot+perp + Hyperliquid perp,
  共 7 venue,~2520 streams 实时同步,1m / 5m / 15m / 1h / 4h / 1d 全档
- **Stock + Forex**(转发模式):A 股 + 港股 + 美股 + 外汇,**任意标的代码都可查**
  (引擎实时从腾讯财经免费接口拉)
- **56 个技术指标**符合 TradingView 数学规范,按需计算
- 提供原始 OHLCV、指标值、声明式 chart 绘制协议三种粒度

它**不是**:下单平台、历史档案(缓存有界)、tick 流(最小 bar = 1m)、基本面 / 新闻
源、投资建议引擎。

详细数据源 + 指标罗列见 [`README.md`](./README.md)。

---

## 2. 接入参数

| | |
|---|---|
| Base URL | `https://api.mobiusquant.ai` |
| Auth | **可选** `Authorization: Bearer mq_xxx` —— 不带也能调,带 token 提速 |
| TLS | 必需(HTTP 自动跳 HTTPS) |
| 限速 | 匿名 **10 req/min per IP** · token **60 req/min**(详见 §11) |
| POST 请求 | `Content-Type: application/json` |

**冷启动期**:crypto venue init 约 6-7 分钟,期间数据端点返回 `404 No kline
data cached`。**始终先 probe `GET /api/health`**:`status: "starting"` 表示
等,不是重试。Stock / forex venue 转发模式无 init,启动即可用。

---

## 3. 端点决策树

| 目标 | 方法 | 路径 |
|---|---|---|
| **数据 / 行情** | | |
| 列出所有 venue + 健康状态 | GET | `/api/markets` |
| 列出 venue 上支持的 symbol(crypto 用) | GET | `/api/markets/{exchange}/{market}/symbols` |
| 列出支持的 interval + 缓存深度 | GET | `/api/intervals` |
| 拉原始 OHLCV | GET | `/api/klines` |
| 计算指标(用于交易逻辑) | **POST** | `/api/indicators` |
| 构建图表(klines + 指标渲染协议) | **POST** | `/api/chart` |
| 用自带数据算指标(BYO klines,不查 store) | **POST** | `/api/indicators/compute` |
| **发现 / 元数据** | | |
| 列出所有指标 + 参数 + 输出列 | GET | `/api/indicators/registry` |
| 列出可绘图的指标子集 + 样式元数据 | GET | `/api/indicators/catalog` |
| **运营层(playbook + builtin symbols)** | | |
| 列出 builtin symbol(natural-name → canonical) | GET | `/api/symbols/builtin` |
| 搜索标的 | GET | `/api/symbols/search?q=` |
| 列出所有 playbook | GET | `/api/playbooks` |
| 单 playbook 详情 | GET | `/api/playbooks/{name}` |
| 一键跑 playbook(多周期分析) | **POST** | `/api/analyze` |
| **服务自描述** | | |
| 服务健康 | GET | `/api/health` |
| 本文档 | GET | `/agents.md` |
| OpenAPI schema | GET | `/openapi.json` |
| Swagger UI | GET | `/docs` |

**常见误用**:`/api/indicators` 和 `/api/chart` 都是 **POST**,GET 会返回 `405
Method Not Allowed`。请求体 schema 一致(见 §6),返回格式不同。

---

## 4. 概念

### 4.1 Venue 是 (exchange, market) 对,不是单字符串

两个字段分开传:

```jsonc
// ✅ 正确
{"exchange": "binance", "market": "perp", ...}

// ❌ 错误
{"venue": "binance:perp", ...}
{"exchange": "binance:perp", ...}
{"exchange": "binance-perp", ...}
```

**完整 venue 表**(11 个):

| Exchange | Market | 模式 | 说明 |
|---|---|---|---|
| `binance` | `spot` / `perp` | store(WS) | USDT-M 期货叫 perp,不叫 futures |
| `bybit` | `spot` / `perp` | store(WS) | perp 含 USDT-M + USDC-M(都用 USDC 后缀作 canonical) |
| `okx` | `spot` / `perp` | store(WS) | perp 仅 USDT-M(无 USDC) |
| `hyperliquid` | `perp` | store(WS) | 仅 USDC perp,无 spot |
| `stock` | `cn` / `hk` / `us` | forwarded(HTTP) | A 股 / 港股 / 美股 |
| `forex` | `spot` | forwarded(HTTP) | 外汇即期 |

`market` 合法值:`spot` `perp` `cn` `hk` `us`。**不接受** `futures` / `future`
/ `swap` / `margin` / `usdt-m`。

**覆盖缺口**:不存在的 venue 返回 400。
- `okx:perp` 无 USDC 对(只有 USDT)
- `hyperliquid:perp` 无 USDT 对(只有 USDC)

### 4.2 Symbol — canonical 格式按 venue 不同

#### Crypto:`{BASE}{QUOTE}` 全大写连写

```jsonc
✅ "BTCUSDT" "ETHUSDC" "1000PEPEUSDT" "BNBUSDC"
❌ "btcusdt"           // 大小写
   "BTC/USDT"          // 分隔符
   "BTC-USDT"          // 连字符
   "BTC"               // Hyperliquid 原生形态 — 不接受
   "BTCPERP"           // Bybit USDC perp 原生 — 不接受
   "BTC-USDT-SWAP"     // OKX 原生 — 不接受
```

引擎内部翻译 canonical → exchange-native。**永远发 canonical**,native 形态
返回 404。

#### Stock A 股 (`stock:cn`):**6 位裸代码**(无 sh/sz/bj 前缀)

```jsonc
✅ "600519"  // 茅台,沪市主板
✅ "300750"  // 宁德时代,创业板
✅ "688981"  // 中芯国际,科创板
✅ "430047"  // 北交所(自动识别 4/8 开头)
✅ "510500"  // 沪市 ETF
✅ "159915"  // 深市 ETF
❌ "sh600519"   // 不要带前缀
❌ "600519.SH" // 不要带后缀
```

板块由代码前缀自动判别:`6/9/5/11/20` 沪市,`0/3/12/15/16` 深市,`43/83/87/88/92`
北交所。

#### Stock 港股 (`stock:hk`):**5 位带前导零**

```jsonc
✅ "00700"  // 腾讯
✅ "09988"  // 阿里
✅ "00388"  // 港交所
✅ "08083"  // GEM 板
❌ "700"     // 不要去前导零
❌ "hk00700" // 不要带 hk 前缀
```

#### Stock 美股 (`stock:us`):**裸 ticker**(无后缀)

```jsonc
✅ "AAPL"    // NASDAQ
✅ "JPM"     // NYSE(引擎内置 NYSE/NASDAQ 后缀映射表)
✅ "NVDA"    // 任意主流 ticker
✅ "TSM"     // ADR
❌ "AAPL.OQ"   // 不要带 .OQ
❌ "JPM.N"     // 不要带 .N
❌ "BRK.B"     // 含点 ticker 暂不支持
```

NASDAQ / NYSE 区分由引擎内置映射处理;不在表中的 ticker 默认按 NASDAQ 走,
未命中可能空数据。

#### Forex (`forex:spot`):**6 字母连写**

```jsonc
✅ "EURUSD" "USDJPY" "GBPUSD" "USDCHF" "USDCNY"
❌ "EUR/USD"   // 不要分隔符
❌ "EURUSD=X"  // 不要后缀
```

### 4.3 Symbol 前缀约定(crypto memecoin)

- 价格极小的 memecoin 带 `1000` 前缀:`1000PEPEUSDT` `1000LUNCUSDT` `1000BONKUSDT`
- Hyperliquid 原生 `kPEPE` canonical 化为 `1000PEPEUSDC`
- 在 `GET /api/markets/{ex}/{mk}/symbols` 确认精确名,**不要靠猜**

### 4.4 Interval — 严格小写

完整集合:`1m` `5m` `15m` `30m` `1h` `4h` `1d`

**每个 venue 实际支持的子集不同**:

| Venue | 支持的 intervals |
|---|---|
| crypto(7 个 venue 都一样) | `1m` `5m` `15m` `1h` `4h` `1d` |
| `stock:cn` | `1m` `5m` `15m` `30m` `1h` `1d` |
| `stock:hk` / `stock:us` / `forex:spot` | **仅 `1d`**(腾讯无分钟 K) |

```
✅ "1m" "5m" "15m" "30m" "1h" "4h" "1d"
❌ "1M" "1H" "1D"   // 大写
❌ "60" "60m"       // 数字 / 错误后缀
❌ "1hr" "240m"     // 拼写错误
❌ "1w" "1M"        // 周线月线暂未暴露
```

打错的 interval 返回 400 with `valid: [...]`。

### 4.5 时间格式

**所有时间一律毫秒** Unix epoch。

```jsonc
✅ "startTime": 1735689600000   // 2025-01-01T00:00:00Z
❌ "startTime": 1735689600      // 秒 — 解释为 1970-era
❌ "startTime": "2025-01-01"    // 字符串 — Pydantic 422
```

响应 `open_time` 也是 ms。Chart payload 的 `time` 是**秒**(lightweight-charts
惯例)。**两者故意不一致**,不要做归一化。

### 4.6 指标请求格式 — 命名参数字典

`calc` 字段是**对象列表**,不是字符串。

```jsonc
✅ 正确
"calc": [
  {"name": "ema",  "params": {"period": 20}},
  {"name": "rsi",  "params": {"period": 14}},
  {"name": "macd", "params": {"fast": 12, "slow": 26, "signal": 9}}
]

❌ 错误(旧 compact 格式已下线)
"calc": "ema:20,rsi:14,macd:12:26:9"
```

**参数名必须精确匹配指标签名**。EMA 用 `period`(不是 `length` / `n` /
`window`)。查准确参数名:`GET /api/indicators/registry`。

### 4.7 `id` 字段 — 何时设置

每个 `calc[]` 条目在响应里对应一个 key。默认 key = `"name:k1=v1,k2=v2"`
(params 按字母排序)。两条目产生同 key → 400;用 `id` 消歧。

```jsonc
// ✅ 同指标不同参数 → 自动不同 key,无需 id
"calc": [
  {"name": "ema", "params": {"period": 20}},   // key: "ema:period=20"
  {"name": "ema", "params": {"period": 50}}    // key: "ema:period=50"
]

// ✅ 同指标同参数 → 必须给 id
"calc": [
  {"name": "rsi", "params": {"period": 14}, "id": "rsi_close"},
  {"name": "rsi", "params": {"period": 14}, "id": "rsi_hl2"}
]

// ❌ 完全重复无 id → 400 Duplicate calc key
```

### 4.8 `limit` 字段 — 每个端点上限不同

| 端点 | 上限 |
|---|---|
| `GET /api/klines` | 每 (venue, interval),查 `/api/intervals` 的 `max_klines` |
| `POST /api/indicators` | `1000`(常量) |
| `POST /api/chart` | 同 `/api/klines`,**静默截断** |
| `POST /api/analyze` | 由 playbook 决定,典型 50-200 |

`/api/indicators` 超出返回 400;`/api/chart` 静默截断到上限。

---

## 5. 响应结构

所有响应都是 JSON。

### 5.1 `GET /api/klines`

```jsonc
{
  "exchange": "binance", "market": "perp", "symbol": "BTCUSDT", "interval": "1h",
  "data": [
    [1735689600000, 95000.0, 95500.0, 94800.0, 95200.0, 1234.5, 117500000.0],
    // [open_time_ms, open, high, low, close, volume, quote_volume]
    ...
  ]
}
```

每行 7 列:`[open_time_ms, open, high, low, close, volume, quote_volume]`。
注意是标准 OHLC 顺序(不是 OCHL)。

### 5.2 `POST /api/indicators`

```jsonc
{
  "exchange":"binance", "market":"perp", "symbol":"BTCUSDT", "interval":"1h",
  "current_price": 95200.0,
  "count": 100,
  "klines": [[1735689600000, 95000.0, ...], ...],  // 与指标行 1:1 对齐
  "indicators": {
    "ema:period=20": {
      "columns": ["open_time", "ema_20"],
      "data": [[1735689600000, 94890.5], ...]
    },
    "rsi:period=14": {
      "columns": ["open_time", "rsi"],
      "data": [[1735689600000, 67.4], ...]
    },
    "macd:fast=12,signal=9,slow=26": {
      "columns": ["open_time", "macd", "signal", "hist"],
      "data": [[1735689600000, 120.5, 110.2, 10.3], ...]
    }
  }
}
```

`indicators` 的 key 由 name+params(或 `id`)确定。**按 key 读,不要按位置**。

### 5.3 `POST /api/chart`

```jsonc
{
  "exchange":"binance", "market":"perp", "symbol":"BTCUSDT", "interval":"1h",
  "klines": [{"time":1735689600, "open":95000.0, "high":95500.0,
              "low":94800.0, "close":95200.0, "volume":1234.5}, ...],
  "panels": [
    {"id":"main", "overlay":true, "height_ratio":3.0, "items":[
      {"type":"line", "label":"EMA(20)", "style":{"role":"primary","width":2},
       "data":[{"time":1735689600,"value":94890.5}, ...]}
    ]},
    {"id":"rsi:period=14", "overlay":false, "height_ratio":1.0,
     "value_range":[0,100], "items":[
      {"type":"line", "label":"RSI(14)", "style":{...}, "data":[...]},
      {"type":"hline", "value":30, "style":{"role":"oversold","dash":"dashed"}},
      {"type":"hline", "value":70, "style":{"role":"overbought","dash":"dashed"}}
    ]}
  ]
}
```

`time` 这里是**秒**(不是 ms)。`style.role` 是语义标签(frontend 按主题映射到
颜色)。每个 panel 渲染为独立子图垂直堆叠,`height_ratio` 控制相对高度。

### 5.4 `POST /api/analyze`(playbook)

```jsonc
{
  "playbook": "market-pulse",
  "asset_class": "crypto",
  "intervals": ["5m", "15m"],
  "data": {
    "5m": {
      "current_price": 95200.0,
      "klines": [[...], ...],
      "indicators": {
        "mobius_trend:...": {"data":[...], "explain": { /* knowledge */ }},
        "cvd:...":          {"data":[...], "explain": { /* knowledge */ }}
      }
    },
    "15m": { /* 同结构 */ }
  }
}
```

每个 indicator 内嵌 `explain`(knowledge 的 `summary_focus`),LLM 可直接据此
按 playbook 的 `synthesis` / `output_template` / `constraints` 综合分析。

### 5.5 `POST /api/indicators/compute`(BYO klines)

跟 `/api/indicators` 几乎同形态,区别:**用户自带 OHLCV**,引擎不查 store。
适用场景:回测、自定义数据源、引擎没订阅的市场、离线分析。

```jsonc
// Request
{
  "klines": [
    [1735689600000, 95000.0, 95500.0, 94800.0, 95200.0, 1234.5],
    [1735693200000, 95200.0, 95800.0, 95100.0, 95650.0, 1500.2],
    // ... ≤ 2000 根
  ],
  // 或对象形式(自动识别):
  // [{"open_time":1735689600000, "open":95000, "high":95500, "low":94800,
  //   "close":95200, "volume":1234.5, "quote_volume":117526200}, ...]
  "interval": "1h",         // 可选,纯回显;某些 MTF 指标用不到
  "calc": [{"name":"smc", "params":{"show_premium_discount_zones":true}, "id":"smc"}],
  "echo_klines": true       // 默认 true;false 时响应不回 klines 数组
}

// Response
{
  "count": 500,
  "interval": "1h",         // 仅当 request 传了
  "klines": [[...], ...],   // echo,除非 echo_klines=false
  "indicators": {
    "smc": {
      "columns": [...],
      "data":    [...],
      "objects": {...},     // SMC 等非数值指标自动透传 sidecar
      "explain": {...}      // explain=true 时附带 knowledge 解读
    }
  }
}
```

**约束**:
- `klines` 上限 **2000 根**(`413` 拒绝)
- `open_time` 必须**严格单调递增的毫秒**;OHLCV 必须 finite
- **无 warmup buffer** —— 用户自带数据全权负责:ATR(200)/EMA(200) 至少 200 根、SMC swing ≥ 50、FVG ≥ 3
- `calc` schema 跟 `/api/indicators` 完全一致;auth + rate limit 同套

---

## 6. Recipes

### 6.1 拉 BTC perp 最近 100 根 1h K 线

```bash
curl 'https://api.mobiusquant.ai/api/klines?exchange=binance&market=perp&symbol=BTCUSDT&interval=1h&limit=100'
```

### 6.2 看 BTC 是不是超买(RSI > 70)

```bash
curl -X POST https://api.mobiusquant.ai/api/indicators \
  -H 'content-type: application/json' \
  -d '{
    "exchange":"binance","market":"perp","symbol":"BTCUSDT","interval":"1h",
    "limit": 1,
    "calc":[{"name":"rsi","params":{"period":14}}]
  }'
```

读 `indicators["rsi:period=14"].data[-1][1]`,大于 70 即超买。

### 6.3 EMA 金叉死叉(短 vs 长)

```bash
curl -X POST https://api.mobiusquant.ai/api/indicators \
  -H 'content-type: application/json' \
  -d '{
    "exchange":"binance","market":"perp","symbol":"ETHUSDT","interval":"4h",
    "limit": 2,
    "calc":[
      {"name":"ema","params":{"period":20},"id":"ema_short"},
      {"name":"ema","params":{"period":50},"id":"ema_long"}
    ]
  }'
```

对比 `indicators.ema_short.data[-1][1]` 和 `indicators.ema_long.data[-1][1]`
最近两根的相对位置,判断金叉 / 死叉方向。

### 6.4 拉 A 股茅台日 K + RSI

```bash
curl -X POST https://api.mobiusquant.ai/api/indicators \
  -H 'content-type: application/json' \
  -d '{
    "exchange":"stock","market":"cn","symbol":"600519","interval":"1d",
    "limit": 30,
    "calc":[{"name":"rsi","params":{"period":14}}]
  }'
```

### 6.5 拉 A 股 5 分钟 K 线 + EMA

```bash
curl -X POST https://api.mobiusquant.ai/api/indicators \
  -H 'content-type: application/json' \
  -d '{
    "exchange":"stock","market":"cn","symbol":"300750","interval":"5m",
    "limit": 200,
    "calc":[{"name":"ema","params":{"period":20}}]
  }'
```

注意:港股 / 美股 / 外汇 **没有分钟 K**,interval 必须传 `1d`。

### 6.6 拉港股腾讯日 K

```bash
curl 'https://api.mobiusquant.ai/api/klines?exchange=stock&market=hk&symbol=00700&interval=1d&limit=60'
```

### 6.7 拉美股 NVDA 日 K(任意 ticker 都可,无需预注册)

```bash
curl 'https://api.mobiusquant.ai/api/klines?exchange=stock&market=us&symbol=NVDA&interval=1d&limit=60'
```

### 6.8 拉外汇 EURUSD 日 K

```bash
curl 'https://api.mobiusquant.ai/api/klines?exchange=forex&market=spot&symbol=EURUSD&interval=1d&limit=30'
```

### 6.9 渲染图表 — K 线 + EMA + RSI + MACD

```bash
curl -X POST https://api.mobiusquant.ai/api/chart \
  -H 'content-type: application/json' \
  -d '{
    "exchange":"binance","market":"perp","symbol":"BTCUSDT","interval":"1h",
    "limit": 200,
    "calc":[
      {"name":"ema","params":{"period":50}},
      {"name":"rsi","params":{"period":14}},
      {"name":"macd","params":{"fast":12,"slow":26,"signal":9}}
    ]
  }'
```

遍历 `panels[]`,每个 panel 渲染为独立子图。

### 6.10 用自带数据算指标(BYO klines)

回测、自定义数据源、引擎没订阅的市场场景:

```bash
curl -X POST 'https://api.mobiusquant.ai/api/indicators/compute?explain=true' \
  -H 'authorization: Bearer mq_xxx' \
  -H 'content-type: application/json' \
  -d '{
    "klines": [
      [1735689600000, 95000.0, 95500.0, 94800.0, 95200.0, 1234.5],
      [1735693200000, 95200.0, 95800.0, 95100.0, 95650.0, 1500.2]
    ],
    "interval": "1h",
    "calc": [{"name": "rsi", "params": {"period": 14}}]
  }'
```

注意:
- klines 自带,**无 warmup buffer** —— 需要够 history。ATR/EMA(200) ≥ 200 根、SMC swing ≥ 50、FVG ≥ 3。
- 上限 **2000 根**(超返 413)
- `open_time` 必须严格单调递增的毫秒,OHLCV 必须 finite
- auth / rate limit 跟 `/api/indicators` 同套

适合场景:
- 给历史数据跑指标(数据库导出的 OHLCV、CSV 上传)
- 自定义合约 / 跨交易所聚合的虚拟 K 线
- 模拟数据回测策略

不适合:
- 实时行情(走 `/api/indicators` 让引擎从 store 取,延迟更低)
- MTF 跨周期需求(`fvg_timeframe` 等参数走 store 路径才支持)

### 6.11 跑 playbook(自动多周期分析)

```bash
curl -X POST https://api.mobiusquant.ai/api/analyze \
  -H 'content-type: application/json' \
  -d '{
    "playbook":"market-pulse",
    "exchange":"binance","market":"perp","symbol":"BTCUSDT",
    "limit": 100
  }'
```

引擎按 playbook 定义的 intervals + indicators 跑完一套,返回每个周期的 K 线 +
指标 + explain。LLM 按 playbook 的 `synthesis` 模板生成最终回答。

### 6.12 发现哪些 symbol / 指标 / playbook 可用

```bash
# Crypto symbol 列表(per venue)
curl 'https://api.mobiusquant.ai/api/markets/binance/perp/symbols'

# Stock / forex 转发模式 — 任意标的都可查,无需先列表;若想看预热示例:
curl 'https://api.mobiusquant.ai/api/symbols/builtin'

# 所有指标
curl 'https://api.mobiusquant.ai/api/indicators/registry'

# 所有 playbook
curl 'https://api.mobiusquant.ai/api/playbooks'
```

### 6.13 探测服务健康

```bash
curl 'https://api.mobiusquant.ai/api/health'
```

| `status` | 含义 | 行动 |
|---|---|---|
| `ok` | 所有 venue 健康 | 继续 |
| `degraded` | 部分流过期(数据仍服务) | 继续 |
| `dead` | 所有 WS batch 失败 | 任务失败 |
| `starting` | crypto init 中 | 等 60s 重试 |

---

## 7. 错误 — 状态码语义

| 码 | 原因 | 行动 |
|---|---|---|
| **400** | 未知 exchange/market/interval、未知指标、calc 参数与签名不符、calc key 重复(无 id)、limit 超 cap、不支持的 venue+interval 组合(如 `stock:hk`+`5m`) | 读 `detail` 修正后重发 |
| **404** | symbol 在该 venue 上不存在,或缓存空(crypto init 中);**stock/forex 假代码也是 404 + 空 data** | 先看 `/api/health`,`starting` 等;否则查 `/api/markets/.../symbols` |
| **405** | GET 调了 POST 端点 | 换 POST + JSON body |
| **422** | Pydantic 校验:必填缺失、类型错、未知字段、JSON 错乱 | 读 validation 数组修请求结构 |
| **401** | 缺 token / token 错误 / 失效 / 撤销 | 不要重试,先修 token(§11) |
| **429** | 速率限制(匿名 10/min per IP 或 token 60/min) | 看 `Retry-After` 头退避;匿名可申 token 升档 |
| **502** | 上游容器重启 | 30 秒后重试 |
| **504** | 上游(腾讯)超时,仅 stock/forex | 短时重试,可能命中 stale cache |

**错误体一律 `{"detail": "..."}`,重试前必须读 `detail`** — 400 盲目重试会
死循环。

---

## 8. 常见坑 — 集成方踩过的

1. **`GET /api/indicators` → 405**。是 POST。`/api/chart` `/api/analyze` 同
2. **旧 compact-list `"calc":"ema:20"`** 已下线,用 object 列表
3. **`interval:"1H"` → 400**,小写
4. **`symbol:"BTC/USDT"` → 404**,canonical 无分隔符
5. **`symbol:"BTC"` 给 Hyperliquid → 404**,用 `BTCUSDC`(canonical 已翻译)
6. **`startTime` 用秒**,默默拉到 1970 — 务必毫秒
7. **`exchange:"binance:perp"` → 400**,分两字段
8. **`market:"futures"` → 400**,用 `perp`
9. **OKX 求 USDC perp → 404**,OKX 只有 USDT perp
10. **Hyperliquid 求 USDT pair → 404**,HL 只有 USDC
11. **A 股代码带前缀 `"sh600519"` → 404**,canonical 是裸代码 `600519`
12. **港股代码 `"700"` → 404**,canonical 是 5 位带前导零 `00700`
13. **美股代码 `"AAPL.OQ"` → 404 或 0 行**,canonical 是裸 `AAPL`
14. **`stock:hk` + `interval:5m` → 400** "Interval '5m' not supported on
    stock:hk; valid: ['1d']";港股 / 美股 / 外汇腾讯只有日级
15. **`limit:9999` 给 `/api/indicators` → 400**,常量 1000 上限
16. **同名同参数 calc 无 id → 400** Duplicate calc key
17. **按下标读 `indicators` → 错** — 是 dict,按 key 取,顺序保留但不要靠它
18. **服务刚重启 → 404 在 crypto venue** — 等 6-7 分钟 crypto init,probe `/api/health`,
    forwarded venue 不受影响

---

## 9. 运营层 — playbook 和 symbol search

引擎不仅给"原料"(K 线 / 指标),也给"成品配方"(playbook)。

```
basics layer    /api/indicators       单指标
                /api/chart            可视化
                /api/klines           原始 K 线

operations layer /api/symbols/...     natural-name → canonical
                 /api/playbooks       预定义指标组合 + 周期
                 /api/analyze         按 playbook 自动跑完整分析
```

### 当前 playbook 目录

| Playbook | 适用 | 指标组合 | 周期 |
|---|---|---|---|
| `market-pulse` | crypto / stock / forex 通用 | `mobius_trend` + `cvd` | crypto 5m/15m · stock 30m/1h · forex 15m/1h |

调 `/api/playbooks` 拿最新列表(admin 后台改 → engine 60s 自动拉)。

### Symbol 搜索

`GET /api/symbols/search?q=` 接受中英 ticker / 名称 / 别名,返回 canonical。
适合"用户说 AAPL 想看苹果"这种自然语言场景。

---

## 10. 参考

| 端点 | 用途 |
|---|---|
| `GET /openapi.json` | 完整机器可读 schema(所有端点 / 字段类型 / 示例) |
| `GET /docs` | Swagger UI(人 / 抽查) |
| `GET /api/indicators/registry` | 每个指标的参数 + 输出列 |
| `GET /api/indicators/catalog` | 有 chart spec 的指标子集 |
| `GET /api/markets` | 每 venue symbol 数 + WS 状态 |
| `GET /api/intervals` | interval 列表 + 缓存深度 + staleness budget |

**字段名或值有疑问时,先调发现端点,再发 POST**。一次额外 GET 远比一次失败
POST 的代价小。

---

## 11. 鉴权与速率限制

分档限速:

| 档 | 触发条件 | 限速 | key |
|---|---|---|---|
| 匿名 | 不带 `Authorization` 头 | **10 req/min per IP** | `ip:<CF-Connecting-IP>` |
| Token | 带合法 `Authorization: Bearer mq_xxx` | **60 req/min** | `tok:<user_id>` 或 `atok:<token_id>` |

带了 Authorization 但 token **不合法**(拼错 / 过期 / 已撤销)→ `401`,不降级匿名。

burst = 配额本身 —— 可一秒打完,然后等满整一分钟从空桶补满。

**豁免路径**(既不要求 token 也不限速):`/api/health` `/agents.md` `/docs`
`/redoc` `/openapi.json`。

### 申请 token(可选,匿名也能直接用)

- **匿名试用**(7 天):https://www.mobiusquant.ai/zh/apply-token,过人机验证即可
- **登录永久**:https://www.mobiusquant.ai/zh/account → API Tokens → 新建
- 明文 `mq_<43 chars>` **只显示一次**,立刻复制保存
- 调用时 `Authorization: Bearer mq_xxx`
- 登录用户最多 10 个 active token;撤销/到期 ≤60 秒传播到引擎,新建 token cache miss
  会触发单查回源,几乎 0 延迟生效

### 响应头

每个非豁免调用响应都会带:

```
X-RateLimit-Limit: 10    (匿名档) 或 60 (token 档)
```

429 时:

```
HTTP/1.1 429 Too Many Requests
Retry-After: 7
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0

# 匿名超限:
{"detail": "rate limit exceeded (10/min); apply for a free token at https://www.mobiusquant.ai/zh/apply-token to upgrade to 60/min."}

# token 超限:
{"detail": "rate limit exceeded (60/min); consider batching requests."}
```

**尊重 `Retry-After`**。死循环重试只会让桶永远空。

### 401 vs 429

- **401** = 带了 token 但 token 错(拼错 / 过期 / 撤销)。停止重试,先修 token
- **429** = 桶被打空,按 `Retry-After` 退避;匿名档可申 token 升 6x

---

## 附:语言

知识库(指标 `desc` / `summary_focus` / playbook `synthesis`)**只有中文**,
LLM 翻译给其他语言用户。这是设计决策(见 VISION §8 决策 7),不是 bug。
