本文面向第一次运行和日常维护 CBLens 的使用者。更底层的数据字段说明见 data/README.md,维护约定见 AGENTS.md。
git clone https://github.com/Grefer/CBLens.git
cd CBLens
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
python -m pip install -U pip
pip install -e ".[dev]"| 依赖 | 用途 | 备注 |
|---|---|---|
| Python 3.10+ | 运行环境 | 使用 X | None 等 3.10 类型语法 |
numpy, scipy |
PDE 定价数值计算 | |
customtkinter, matplotlib |
GUI 界面 | |
pillow |
图像处理 | |
akshare |
免费动态行情源 | |
| WindPy | 全字段条款同步 + 实时行情 |
WindPy 不通过 pip 发布。如需使用:
- 打开 Wind 金融终端
- 进入 插件管理 → Python 接口
- 选择当前虚拟环境的 Python 路径进行安装
- 验证:
python -c "from WindPy import w; print(w)"
Tip
不连接 Wind 也能正常使用!离线 PDE 模型、已有 data/cb_data.json、akshare 动态行情都不依赖 Wind。
正式 Release 会附带可直接运行的桌面包:
- macOS:
CBLens-macOS.zip,解压后双击CBLens.app - Windows:
CBLens-Windows.zip,解压后双击CBLens.exe
桌面包暂未使用 Apple Developer ID 或 Windows Authenticode 证书签名,首次运行需要手动放行:
- macOS:Gatekeeper 可能提示"无法验证开发者"或"已损坏,无法打开"。在终端执行
xattr -dr com.apple.quarantine /path/to/CBLens.app去掉隔离属性,或在 Finder 中右键CBLens.app→ 打开,再在弹窗中点"打开"。- Windows:SmartScreen 会显示"Windows 已保护你的电脑"。点击"更多信息" → "仍要运行"。
如希望避免上述提示,可参考下方步骤在本机自行编译。
源码构建桌面包:
python -m pip install -e ".[desktop]"
python scripts/build_desktop.py构建产物位于 dist/。打包后的 APP 会把运行态数据写入用户目录,而不是写入应用安装目录:
- macOS:
~/Library/Application Support/CBLens/data - Windows:
%APPDATA%\CBLens\data - 可用
CBLENS_DATA_DIR环境变量覆盖数据目录
若打包后怀疑内置数据或数据源依赖没有被带上,可在终端运行:
dist/CBLens/CBLens --diagnose
# 或 macOS .app 形态:
dist/CBLens.app/Contents/MacOS/CBLens --diagnose诊断输出会列出 APP 内置种子数据、用户数据目录中的 cb_data.json 债券数量,以及 WindPy / akshare / certifi / requests 是否能被定位;WindPy 会实际 import 一次但不会启动连接。
GitHub Actions 自动构建环境不带 WindPy;在装有 Wind API 的本机打包时,APP 会像 DeltaLab 一样优先使用包内 WindPy。若发布包未内置 WindPy,运行时会自动探测本机 Wind 终端;非默认位置可把 CBLENS_WINDPY_PATH 指向 WindPy.py 或其所在目录。
发布桌面包时,Windows 与 macOS 分开处理,避免 CI 中无 WindPy 的 macOS 包覆盖本机 WindPy 版:
- Windows: 触发 GitHub Actions 的
build-desktop.yml,它只会覆盖 Release 里的CBLens-Windows.zip - macOS: 在装有 Wind API 的本机运行
python scripts/release_macos_desktop.py --tag v1.0.0,脚本会先诊断 WindPy 与缓存数据,再覆盖上传CBLens-macOS.zip
CBLens 把 静态条款 和 动态行情 分开处理:
| 数据类型 | 默认来源 | 落盘位置 | 说明 |
|---|---|---|---|
| 转债条款、转股价、票息、评级、余额 | Wind | data/cb_data.json |
半静态信息,建议定期同步 |
| 公告事件 | cninfo | data/cb_events.json |
默认不需要 Wind |
| 停牌、强赎、摘牌、ST、成交额 | Wind | data/cb_data.json |
每日增量刷新 |
| 正股/转债行情、历史波动率、股息率、利率 | Wind / akshare / CSV | 不固定落盘 | 按行情源选择实时获取 |
| 关注池 | 本地维护 | data/watchlist.json |
GUI 批量页管理,运行态文件默认不提交 |
Important
字段缺失时,主池准入筛选遵循保守原则:只有明确命中风险条件才剔除,None 不会直接剔除。
建议先建立完整本地条款库,再进入 GUI:
cb-sync-tradable # 同步全市场基础条款
cb-sync-admission-status # 刷新准入状态
cb-sync-events --apply # 同步公告事件
cb-screen-pool # 查看主池报告
cb-gui # 启动 GUI# 验证 PDE 引擎
python CB.py
# 手工输入条款做单只定价
cb-gui
# 若仓库已有 data/cb_data.json,用 akshare 行情
python CB.py 128009.SZ --source aksharecb-gui
# 或
python -m convertible_bond.gui.app
# 或
python gui.py| 元素 | 功能 |
|---|---|
| 深色/浅色模式 | 切换 Catppuccin Latte/Mocha 主题 |
| Tab 切换 | 📦 批量 · 🎯 策略 · ⚡ 定价 · 📈 回测 · 🔥 敏感性 |
| 行情源 | 选择 Wind 或 akshare |
| 🌐 同步池 | 全市场基础信息、准入状态、公告事件同步入口 |
| 代码输入 | 输入 128009.SZ 或六位代码,命中条款库时自动补全 |
| 📥 同步 | 读取本地条款库 + 拉取正股行情、历史波动率与股息率 |
| 🔄 刷新 | 强制用 Wind 刷新当前债条款(下修/评级变更后使用) |
| 💾 / 📂 | 保存/加载参数预设 (Ctrl+S / Ctrl+O) |
| 快捷键 | 功能 |
|---|---|
Ctrl + Enter |
运行定价 |
Ctrl + S |
保存预设 |
Ctrl + O |
加载预设 |
Ctrl + D |
收敛诊断(开发者调试) |
批量页是默认首页,适合从全市场池里找复核候选。
操作步骤:
- 选择行情源
- 选择视图:综合机会 / 低估候选 / 转股折价 / 需复核
- 设置最低机会分
- 点击 刷新重算
- 主表查看理论价、市价、偏离、转股溢价、机会分、风险标签
- 选中标的 → 加入关注池
- 关注池重算 快速更新已关注标的
- 导出 CSV 留档
关键指标解读:
| 字段 | 含义 |
|---|---|
deviation |
(市价 - 理论价) / 理论价,负值越大 → 模型认为越低估 |
undervaluation_rate |
-deviation,正值表示低估程度 |
opportunity_score |
综合低估、转股折价、HV、余额、评级、久期的排序辅助字段 |
risk_tags |
|
review_notes |
模型或数据异常的复核建议 |
Warning
opportunity_score 不是交易信号。它仅辅助筛选值得人工复核的标的,不能替代投资判断。
回放"按规则选债 + 定期调仓"的历史表现, 并与等权基准对比。这是研究诊断工具, 不是收益承诺。
三步上手
- 「策略方案」选一个预设 (稳健打底 / 低估轮动 / 折价套利) —— 模板会一键设好 全部参数 (含选券权重、现金收益), 不残留上次手动值; 日期默认近一年;
- 点「预检」确认池子与耗时 → 点「运行策略」。历史口径用默认「标准」(分钟级);
- 只看三个数: 总收益 vs 等权基准 (净值图虚线, 跑赢没有)、最大回撤 (最深亏多少)、结论卡 (一句话定性)。
参数分层 (该动哪些?)
| 区域 | 参数 | 普通用户建议 |
|---|---|---|
| 基础 | 策略方案 / 日期 / 频率 | 用模板 + 默认即可 |
| 基础 | 选债规则 · Top N · 选券权重 | 模板已设好。两种选券权重均无稳健选股 alpha (见 README"模型边界"), 是研究配置不是推荐 |
| 基础 | 成本 (bps) · 现金收益 (%/年) | 默认 20bps / 2.2% 即可。现金收益 = 闲置现金按年化计息 (留现金/缺成交价时生效); 设 0 复现旧口径 |
| 高级 | 回测范围 / 历史口径 / 选债条件区间 | 深入研究再动 |
历史口径怎么选
- 标准: 本地条款 + 补丁 + 事件回放, 分钟级; 历史发生过下修的债可能有转股价跳点偏差。
- Wind高保真: 逐债逐期从 Wind 拉历史条款, 最准但大池可能数小时;
等价 CLI (
cb-strategy-backtest) 配--cache-dir后复跑可大幅提速。
结果怎么读 (四条铁律)
- 一切对照基准: 总览同时给「等权基准」(全可投池) 与「中证转债指数」两条线, 跑不赢 = 这套规则没有超额, 哪怕绝对收益为正;
- 先看「稳健性」卡: 块自助给 Sharpe 置信区间与跑赢基准概率 —— CI 含 0 = 差异 可能只是运气, 别把点估计 (如 Sharpe 0.6 vs 0.4) 当真; 期数越少 CI 越宽;
- 机会分是复核标记、不是收益预测; 单一市场周期的回测不构成策略承诺;
- 「数据」子页先看条款补丁覆盖率与缺价跳过数 —— 数据质量差时, 先补数据再下结论。
进阶旋钮 (高级研究)
- 仓位择时: 「估值缩放」按全市场中位偏差调总仓位 (贵→降仓), 是模型唯一跨牛熊· 跨频率稳健的优势 (风险预算工具, 非收益增强); 默认「恒定满仓」。
- 现金收益: 留现金/缺价/降仓留出的现金按此年化计息 (默认 2.2%≈货基); 设 0 复现旧口径。 注意 Sharpe 课征无风险门槛, 现金 0 计息会系统性低估持现金策略。
CLI 等价命令: cb-strategy-backtest --start 2025-01-01 --end 2026-01-01 --freq M --holding-mode top_score --cash-yield 0.022 --cache-dir .cache/bt (全部参数见 --help)。
定价页适合单债深度分析。
操作步骤:
- 输入转债代码 → 点击 📥 同步
- 确认自动填充的条款参数(正股价、转股价、票息、到期日等)
- 调整模型参数:
sigma、r、q、base_spread、distress_k、p_down - 点击 开始计算(或
Ctrl + Enter) - 查看结果:
- 理论价与市价偏离
- 纯债价值 / 转股价值 / 期权溢价
- 希腊值:Δ, Γ, ν, Θ
- 输入市价 → 点击 解 IV 反解隐含波动率
- 点击 现金流 查看付息和到期兑付计划
参数来源标签:
每个输入字段旁标注数据来源(手工 / 条款库 / Wind / akshare / 模型 / 预设),方便追溯。
核心模型参数:
| 参数 | 含义 | 单位与默认 |
|---|---|---|
sigma |
正股年化历史波动率 | 百分数,默认按窗口历史价估算 |
r |
无风险利率 | 百分数,可用 Shibor 参考 |
q |
正股连续股息率 | 百分数,默认从数据源读取,缺失时为 0 |
base_spread |
信用利差 | 百分数,可按评级填入 |
p_down |
下修事件年化强度 | 百分数/年 |
distress_k |
正股下跌时信用利差扩张参数 | 百分数 |
PDE 中股价风险中性漂移使用 r - q,折现仍使用 r + credit_spread(S)。因此提高 q 通常会压低转股权相关价值。
回测页用于复盘模型表现。
操作步骤:
- 在定价页先同步或手工确认当前条款
- 切到回测页
- 选择开始/结束日期和频率(日/周/月)
- 可选:开启 价值分解 和 反解 IV
- 点击 运行回测
- 分析指标:
- 理论价曲线 vs 市价曲线
- IV / HV spread
- 统计偏差指标
回测会沿用定价页中的 r、q、信用利差、下修强度和强赎宽限天数。历史正股价与滚动 sigma 从数据源取数,q 当前作为固定输入参与整段回测。
Note
历史回测默认使用当前条款。发生过下修的债可能出现历史转股价跳点偏差。
敏感性页生成 σ–S 二维热力图。
操作步骤:
- 先在定价页准备好条款和基础参数
- 设置
S (%K)和sigma (%)的扫描范围 - 设置网格密度
- 点击 运行分析
- 查看热力图:颜色深浅对应理论价变化
- 点击 PNG 导出报告图
敏感性热力图只扫描 S 与 sigma,r、q、信用利差和下修参数保持定价页当前值不变。
python CB.py 128009.SZ # 自动选源
python CB.py 128009.SZ 2026-04-20 --source auto # 指定估值日
python CB.py 128009.SZ --source akshare # 指定 akshare
python CB.py # 离线示例--source 只选择动态行情源。静态条款优先读取 data/cb_data.json。
cb-sync-tradable # 全量同步
cb-sync-tradable --info # 仅查看状态
cb-sync-tradable --limit 50 # 限量同步
cb-sync-tradable --codes 113050.SH 128009.SZ # 指定代码Tip
典型节奏:月初全量同步;新债上市、下修、评级变更、退市集中发生后补同步。
cb-sync-admission-status # 全量刷新
cb-sync-admission-status --limit 50 # 限量刷新
cb-sync-admission-status --codes 113050.SH # 指定代码刷新字段:停牌、强赎状态、最后交易日、摘牌日、正股 ST、转债成交额、评级、剩余余额等。
cb-sync-events # 扫描新事件
cb-sync-events --limit 50 # 限量扫描
cb-sync-events --codes 118006.SH --apply # 指定代码 + 应用
cb-sync-events --source cninfo --no-pdf # 跳过 PDF 解析--apply 会把事件表应用回 cb_data.json(例如强赎公告写入 call_status,不下修写入 down_reset_block_until)。
cb-screen-pool # 默认参数
cb-screen-pool --min-rating AA- --min-balance 1 # 严格筛选
cb-screen-pool --min-turnover 10000000 --show-excluded 50from datetime import date
from convertible_bond.pricer import UniversalCBPricer
pricer = UniversalCBPricer(
S0=55.0,
K=52.77,
current_date=date(2026, 4, 20),
maturity_date=date(2026, 7, 30),
issue_date=date(2020, 7, 30),
conversion_start_date=date(2021, 2, 6),
coupon_rates=(0.003, 0.004, 0.008, 0.015, 0.018, 0.02),
redemption_price=107.0,
)
price = pricer.price(sigma=0.28, r=0.022, q=0.015, base_spread=0.03)q 为连续股息率,小数形式。例如 0.015 表示 1.5%/年。
from convertible_bond.pricing_api import price_from_auto
row = price_from_auto("128009.SZ", prefer="akshare")
print(row["theoretical_price"], row["market_price"], row["sigma"], row["q"])自动取数定价会调用行情源的 get_stock_dividend_yield()。该接口返回百分数,例如 2.5 表示 2.5%/年;price_from_auto() 返回结果里的 q 已经转换为模型小数,例如 0.025。如果行情源没有返回有效股息率,q 会回退为 0.0。
from convertible_bond.batch_pricing import build_batch_provider, list_batch_codes_from_cache
from convertible_bond.cache import TermsBundle, project_bundle_path
from convertible_bond.pricing_api import batch_price_from_provider_threaded
bundle = TermsBundle(project_bundle_path())
codes = list_batch_codes_from_cache(bundle)[:50]
provider = build_batch_provider("akshare", terms_cache=bundle)
rows = batch_price_from_provider_threaded(provider, codes, max_workers=4)
rows = [row for row in rows if row.get("status") == "ok"]| 文件 | 用途 | 手工编辑 |
|---|---|---|
data/cb_data.json |
全市场条款与准入状态 | ❌ 一般不要 |
data/cb_events.json |
结构化公告事件 | ❌ 由同步维护 |
data/down_reset_overrides.json |
人工下修覆盖 | ✅ 可以手工维护 |
data/watchlist.json |
关注池 | ✅ 可由 GUI 管理,默认忽略 |
data/batch_pricing_cache.json |
批量定价缓存 | ❌ 自动生成,默认忽略 |
Note
JSON 写入采用 .tmp 后 rename 的原子写模式,避免半截文件。
确认 Wind 终端已安装 Python 接口到当前 venv:
which python
python -c "from WindPy import w; print(w)"如果路径不匹配,需要在 Wind 终端中重新选择正确的 Python 路径。
这是预期行为。akshare 动态行情可用,但完整转债条款(强赎/回售触发比例、回售观察期、完整付息计划)仍以 Wind 写入的 cb_data.json 为准。
先确认条款库里有这只债:
cb-sync-tradable --info
cb-sync-tradable --codes 128009.SZ先缩小范围定位问题:
cb-screen-pool --show-excluded 50
cb-sync-admission-status --limit 50
cb-sync-events --limit 50 --apply如果是网络或数据源问题,换行情源或降低并发后再跑。
优先检查以下几项:
- 转股价是否刚下修但本地条款未刷新
- 是否已公告强赎、摘牌或停牌
- 正股价、转债市价和估值日是否同日
- HV 是否异常高,导致期权价值被放大
- 股息率
q是否缺失或口径异常,尤其是高股息正股 - 余额、评级、转股溢价是否触发风险标签
Wind 或 akshare 未返回有效股息率时,系统会保守回退到 0,避免数据缺口直接阻断定价。可以在定价页手工输入 q (%) 后重新计算;保存参数预设时,q 也会一并保存。
# 全量
pytest
# 快速失败
pytest -x -q
# 按模块
pytest tests/test_pricer.py -x -q
pytest tests/test_pricing_api.py -x -q
pytest tests/test_batch_pricing.py -x -q