内容
# 无尽
**基于 Rust 的 Interactive Brokers 客户端门户 Web API 的类型化异步访问,提供 HTTP、CLI、MCP 和 TypeScript 接口,均从同一份 OpenAPI 规范自动生成。**
[](https://github.com/isaacrowntree/bezant/actions/workflows/ci.yml)
[](https://isaacrowntree.github.io/bezant/)
[](https://github.com/isaacrowntree/bezant/actions/workflows/audit.yml)
[](#license)
[](Cargo.toml)
> 📖 **文档:** <https://isaacrowntree.github.io/bezant/> · 📦 **问题:** <https://github.com/isaacrowntree/bezant/issues>
---
Bezant 将 IBKR 的 154 端点 CPAPI 转换为 **五个易用的接口**,均从同一份 OpenAPI 3.1 规范生成:
| 仓库 / 包 | 目的 |
|---|---|
| [`bezant`](crates/bezant-core) | 易用的异步 Rust 接口 — `Client`,保持连接,健康检查,WebSocket 流式传输,分页,符号缓存,类型化错误 |
| [`bezant-api`](crates/bezant-api) | 自动生成的 Rust 客户端,覆盖 **154 路径 / 167 方法 / 1030 类型** |
| [`bezant-server`](crates/bezant-server) | HTTP 侧车 — 将 CPAPI 公开为纯 REST+JSON,以便任何语言都可以消费它 |
| [`bezant-cli`](crates/bezant-cli) | `bezant` 命令行界面 — `bezant health`,`bezant positions DU123456`,`bezant conid AAPL` 等 |
| [`bezant-mcp`](crates/bezant-mcp) | 模型上下文协议服务器 — 将 IBKR 公开为 MCP 工具,用于 Claude / Cursor / Continue |
| [`bezant-client`](clients/typescript) | TypeScript 客户端 — 从同一份规范为 Node / Deno / 浏览器生成 |
所有五个接口均由同一份规范驱动。当 IBKR 发布新修订版时,重新运行 `./scripts/codegen.sh`,所有接口都会更新。
## 快速入门(Docker)
```sh
git clone https://github.com/isaacrowntree/bezant
cd bezant
docker compose up
```
- <https://localhost:5000> — 在浏览器中登录 IBKR 网关一次
- <http://localhost:8080/health> — 验证侧车是否看到一个已认证的会话
```sh
curl http://localhost:8080/accounts
curl "http://localhost:8080/accounts/DU123456/positions?page=0"
```
每个接口的完整快速入门:<https://isaacrowntree.github.io/bezant/quickstart.html>
## Rust
```toml
[dependencies]
bezant = { git = "https://github.com/isaacrowntree/bezant", package = "bezant-core" }
tokio = { version = "1", features = ["full"] }
```
```rust,no_run
use std::time::Duration;
#[tokio::main]
async fn main() -> bezant::Result<()> {
let client = bezant::Client::new("https://localhost:5000/v1/api")?;
let _keepalive = client.spawn_keepalive(Duration::from_secs(60));
client.health().await?;
// 在生成的客户端上提供便利的帮助函数
let positions = client.all_positions("DU123456").await?;
let aapl = bezant::SymbolCache::new(client).conid_for("AAPL").await?;
println!("{} 仓位; AAPL = conid {aapl}", positions.len());
Ok(())
}
```
## 亮点
- **类型化、异步、基于 reqwest 的客户端**,涵盖每个 CPAPI 端点。
- **内置会话保持连接** — 不再有 5 分钟会话过期。
- **WebSocket 流式传输**,使用来自 REST 会话的 Cookie 身份验证,并为市场数据 / 订单 / PnL 提供类型化的订阅帮助函数。
- **`bezant-server` 上的事件捕获**(可选)— 内部 CPAPI WebSocket 消费者 + 基于光标分页的 `/events/*` REST 接口,以便任何语言的轮询消费者都不会错过订单 / 填充 / PnL 帧之间的策略ticks。可选的 SQLite 历史记录,带有每个主题的保留期。
- **企业级规范标准化**(13 个步骤)适用于真正的 IBKR 怪癖 — 缺失 / 重复的 `operationId`,格式错误的 `security[]`,具有浮点示例值的整数字段等。在 [规范标准化](https://isaacrowntree.github.io/bezant/internals/normalisation.html) 中有详细记录。
- **快照测试** 由真实的 IBKR 示例有效载荷驱动 — 在用户之前捕获上游规范漂移。
- **约 100 个测试** 跨工作区(上次计数 97),全部在 CI 中为绿色:单元测试,集成测试,快照测试和针对 wiremock 模拟网关的端到端测试。
- **双重 MIT / Apache-2.0 许可**,遵循 Rust 生态系统约定。
## 使用 bezant 构建
- **[sovereign-ibkr-fund](https://github.com/isaacrowntree/sovereign-ibkr-fund)** — 一个用于 Interactive Brokers 的自主、多代理投资组合基金。确定性 TypeScript 代理处理分配(HRP / Black-Litterman),风险,税收和执行,通过 bezant 的 HTTP 接口和 TypeScript 客户端进行交易。 *bezant 创造访问权限;sovereign 花费它。*
<!-- ANCHOR: pi-cloudflare -->
## 生产部署:Cloudflare Zero Trust + Pi
现实世界的 IBKR API 部署会遇到一个问题: **`api.ibkr.com`(由 Akamai 提供)拒绝来自云数据中心 IP 的连接**。因此,您的 bezant-server 无法在 Railway / Fly / Render / Heroku 上运行并成功连接到上游 — IBKR 以 401 状态码响应 SSO→CPAPI 桥接调用,并且每个类型化 API 调用都会级联为 401 状态码。
2026 年的模式:
```
您的机器人(Railway/云)
│ HTTPS + 服务令牌
▼
Cloudflare Zero Trust(隧道 + 访问)
│ Cloudflare 隧道
▼
家中的树莓派
│ ┌───────────────────────────────┐
│ │ bezant-server(端口 8080) │
│ │ CPGateway(端口 5000) │
│ └───────────────────────────────┘
│ Cloudflare WARP 出口
▼
api.ibkr.com(Akamai)
```
**为什么选择每个部分:**
- **家中的树莓派** — 住宅 ISP IP 也会被 Akamai 标记 *如果没有 WARP*。(不要跳过 WARP。)
- **Cloudflare WARP** 在树莓派上 — 通过 Cloudflare 的边缘 IP 将 outbound 路由到 api.ibkr.com,这些 IP 在声誉上是干净的。
- **Cloudflare 隧道** — 在不打开路由器端口或拥有公共 IP 的情况下,将树莓派暴露给您的机器人。
- **Cloudflare Zero Trust 访问** — 控制公共主机名。浏览器(您)会遇到 SSO 挑战;服务到服务的调用(您的机器人)会携带两个标头中的服务令牌。
### 一次性设置
```bash
# 1. 在树莓派上(Raspberry Pi 4/5,4GB+ RAM,Pi OS Lite arm64):
sudo apt update && curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
# 2. 安装 Cloudflare WARP(住宅 → 清洁 IP 出口):
curl -fsSL https://pkg.cloudflareclient.com/pubkey.gpg | \
sudo gpg --dearmor -o /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] \
https://pkg.cloudflareclient.com/ bookworm main" | \
sudo tee /etc/apt/sources.list.d/cloudflare-client.list
sudo apt update && sudo apt install -y cloudflare-warp
warp-cli --accept-tos registration new
warp-cli --accept-tos connect
# 3. 安装 Cloudflare 隧道(cloudflared)— 从 Zero Trust 仪表板获取令牌 → 网络 → 隧道 → 创建:
sudo cloudflared service install <YOUR_TUNNEL_TOKEN>
# 4. 运行 bezant-combined(CPGateway + bezant-server 在一个容器中):
docker run -d --name bezant --restart unless-stopped \
-p 127.0.0.1:8080:8080 \
-e BEZANT_DEBUG_TOKEN="$(openssl rand -hex 32)" \
ghcr.io/isaacrowntree/bezant-combined:latest
```
### Cloudflare 仪表板配置
1. **隧道** → 添加 **公共主机名**:`bezant.yourdomain.com` → 服务 `HTTP localhost:8080`。
2. **访问** → **应用程序** → 添加 **自托管** 以获取相同的主机名。添加两个策略:
- **浏览器**(允许):"Emails = you@yourdomain.com" — 供您
交互式登录使用。
- **服务**(服务身份验证):生成的服务令牌 — 供您的
机器人使用。保存 **客户端 ID + 密钥**。
3. 您的机器人以两个标头调用 `https://bezant.yourdomain.com/...`:
```
CF-Access-Client-Id: <client-id>.access
CF-Access-Client-Secret: <secret>
```
### 登录流程
您需要定期进行交互式 IBKR 登录 — 在浏览器中打开 `https://bezant.yourdomain.com/sso/Login`,获取 Cloudflare Access SSO 的挑战,然后获取 IBKR 自己的登录表单,并批准手机上的 2FA 推送。一旦完成,bezant-server 的保持连接功能就会保持会话温暖,您的机器人的 API 调用就会成功。
您需要重新登录的频率取决于 IBKR — 社区报告的范围从 ~12 小时到几天不等,IBKR 会运行夜间维护,通常会强制每天进行一次新的登录。不要假设硬 SLA;设计您的机器人通过显示“需要登录”的警报而不是崩溃来处理 401 状态码。
### 安全模型
- **Cloudflare Zero Trust 是主要的安全边界。** 通过正确配置的访问策略,只有您的电子邮件身份验证的浏览器和您的令牌身份验证的机器人才能访问树莓派。
- **`bezant-server` 的 `BEZANT_BIND` 默认值为 `0.0.0.0:8080`** — 在 **Cloudflare 隧道 + `127.0.0.1` Docker 端口绑定** (如上面的代码片段)后面,这是可以的。不要直接将 8080 端口暴露给互联网,而不使用 Zero Trust。
- **调试端点(`/debug/jar`,`/debug/probe`)** 默认情况下关闭。设置 `BEZANT_DEBUG_TOKEN` 启用它们,由 `X-Bezant-Debug-Token` 标头(或 `?token=…` 查询)保护。有了 Zero
Trust 在前面,这是防御深度 — 在验证您的访问策略是否严格之前关闭它。
- **共享的 Cookie 仓库持有实时 IBKR 会话 Cookie。** 任何可以读取它的人都可以恢复 IBKR 会话并交易您的账户。
bezant-server **按设计为单租户** — 不要在多个租户中部署此代理。
<!-- ANCHOR_END: pi-cloudflare -->
## 仓库布局
```
crates/
bezant-spec/ — 供应商 IBKR OpenAPI 规范 + 刷新工具
bezant-api/ — 自动生成的 Rust 客户端(不要手动编辑)
bezant-core/ — 易用的接口
bezant-server/ — axum HTTP 侧车
bezant-cli/ — clap 支持的命令行界面
bezant-mcp/ — MCP 服务器
clients/
typescript/ — 自动生成的 TypeScript 客户端
scripts/ — 规范管道(标准化 → 升级 → 代码生成)
docs/ — mdbook 源代码,用于文档站点
xtask/ — 仅限开发的工具(规范探测 / 二分法)
.github/ — CI,发布,文档,审计工作流
```
## 贡献
我们欢迎贡献 — 特别是规范改进和新的客户端语言。参见 [CONTRIBUTING.md](CONTRIBUTING.md) 获取开发工作流程,或在 GitHub 上打开讨论以讨论架构。
## 状态
**Alpha — v0.1.** 可以与 IBKR 模拟账户进行端到端测试。 API 接口将演变,直到 v1.0。参见 [ROADMAP](ROADMAP.md) 获取下一步计划。
## 安全
参见 [SECURITY.md](SECURITY.md) 获取披露政策。 **不要公开漏洞** — 而是给维护者发送电子邮件。
## 许可证
双重许可,根据您的选择:
- [MIT 许可证](LICENSE-MIT)
- [Apache 许可证 2.0](LICENSE-APACHE)
除非另有约定,否则贡献被视为按照相同条款提供。
**供应商 IBKR OpenAPI 规范** 本身是 Interactive Brokers 的知识产权;在本例中包含是根据互操作性惯例的合理使用。
## 不隶属于 Interactive Brokers
Bezant 是一个独立的开源项目。交易涉及重大风险;该软件 **不提供任何保证**。参见许可证文本。