关于 Deskpricer
Deskpricer 是面向个人交易台使用的本地期权定价服务,可通过 HTTP 或 MCP 提供分析能力。它支持欧式和美式股票期权,并可与 Excel 公式配合使用。
主要功能
- 01计算期权价格与 Greeks
- 02反推隐含波动率
- 03分解期权损益归因
- 04聚合多腿投资组合 Greeks
- 05默认提供 XML,支持 JSON
适用场景
- 01在 Excel 工作表中获取期权价格与 Greeks
- 02根据市场价格求解隐含波动率
- 03分析持仓期间的损益构成
- 04汇总多腿期权组合风险
MCP 相关
MCP 服务提供 `price_option`、`implied_volatility`、`pnl_attribution` 和 `portfolio_greeks` 工具,使用与 HTTP API 相同的定价引擎。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
DeskPricer v3.5.0
用于欧式和美式普通股期权的本地 HTTP 定价微服务。专为与 Excel WEBSERVICE + FILTERXML 集成而设计——无需 VBA,服务内部也不会调用 Bloomberg 终端。
设计意图: DeskPricer 是一款仅限本地使用的工具,用于个人交易台定价和期权分析。不适合以公共服务或服务器服务的形式运行或提供服务。所有设计选择——绑定 localhost、无身份验证、无 TLS、无速率限制、默认使用 XML——都体现了这一点。
与 AI Agent 配合使用(MCP)
DeskPricer 可作为 MCP 服务使用。将其添加到 Cursor、Claude Desktop 或任何兼容 MCP 的 Agent 中:
pip install deskpricer
已发布到 PyPI:https://pypi.org/project/deskpricer/
Cursor — 添加到 ~/.cursor/mcp.json:
{
"mcpServers": {
"deskpricer": {
"command": "deskpricer-mcp",
"args": []
}
}
}
Claude Desktop — 添加到 claude_desktop_config.json:
{
"mcpServers": {
"deskpricer": {
"command": "deskpricer-mcp"
}
}
}
如果 deskpricer-mcp 不在 PATH 中,请使用虚拟环境中该可执行文件的完整路径。
工具:price_option、implied_volatility、pnl_attribution、portfolio_greeks ——使用与 HTTP API 相同的定价引擎。
完整的设置说明、约定和提示词示例,请参阅 docs/mcp_quickstart.md。
快速入门
从干净克隆到成功调用定价接口,不到 5 分钟:
# 1. Install
python -m venv .venv
.venv\Scripts\activate
pip install -e ".[dev]"
# 2. Run
python -m deskpricer.main
# 3. Test with curl
curl "http://127.0.0.1:8765/v1/greeks?s=100&k=105&t=0.25&r=0.05&q=0.02&b=0.0&v=0.20&type=call&style=european"
# 4. Test with Excel (copy into a cell)
# =FILTERXML(WEBSERVICE("http://127.0.0.1:8765/v1/greeks?s=100&k=105&t=0.25&r=0.05&q=0.02&b=0.0&v=0.20&type=call&style=european"),"//outputs/price")
curl 调用的预期输出(XML):
<?xml version="1.0" encoding="UTF-8"?>
<greeks>
<meta>
<service_version>3.5.0</service_version>
<quantlib_version>1.42.1</quantlib_version>
<engine>analytic</engine>
<valuation_date>2026-06-10</valuation_date>
</meta>
<inputs>
<s>100.0</s>
<k>105.0</k>
<t>0.25</t>
<r>0.05</r>
<q>0.02</q>
<b>0.0</b>
<v>0.2</v>
<type>call</type>
<style>european</style>
</inputs>
<outputs>
<price>2.288743</price>
<delta>0.356244</delta>
<gamma>0.037206</gamma>
<vega>0.185519</vega>
<theta>-0.033315</theta>
<rho>0.083111</rho>
<charm>-0.001241</charm>
</outputs>
</greeks>
如需 JSON,请发送 Accept: application/json,或附加 ?format=json。
试用演示工作簿
打开 sample/DeskPricer_Bitcoin_Demo.xlsx,查看一个开箱即用的示例。该工作簿包含 3 个工作表:
| 工作表 | 内容 |
|---|---|
| Greeks | 比特币欧式看涨期权——现货价 $75K、行权价 $100K、到期时间 3M、波动率 50% |
| ImpliedVol | 根据 $3,398.71 的市场价格反推出约 68.3% 的隐含波动率 |
| PnL Attribution | 分解现货价从 $75K 涨至 $80K、波动率从 50% 扩大至 55% 时的 PnL |
每个工作表都已预先填入实际的 WEBSERVICE 和 FILTERXML 公式。只需启动 DeskPricer,单元格就会自动填充。
功能
- 定价 + Greeks:支持单个期权或多腿投资组合
- 隐含波动率求解器(通过 QuantLib 使用 Brent 方法)
- PnL 归因——将期权 PnL 分解为 delta、gamma、vega、theta、rho、vanna、volga 和残差
- 默认使用 XML——Excel
WEBSERVICE+FILTERXML开箱即用;也可通过Accept: application/json使用 JSON - 仅限 localhost——绑定到
127.0.0.1;不暴露到网络
约定
| Greek | 单位 / 约定 |
|---|---|
delta | 绝对值(∂V/∂S) |
gamma | 绝对值(∂²V/∂S²) |
vega | 每 1% 波动率点 |
theta | 每 日历日(ACT/365)。典型的期权多头为负值(时间衰减)。其符号与 Bloomberg DM 相反,后者将 theta 报告为正的衰减值。 |
rho | 每 1% 利率点(仅无风险利率;不包含股息收益率或借券成本的 rho) |
charm | 每 日历日(∂delta/∂t) |
借券成本(b):可选的年化股票借券成本(十进制)。有效持有成本为 r − q − b。省略该参数或设为 0.0,均与 3.4.0 之前的行为一致。
距到期时间(t):以 ACT/365 下的年数提供。内部会将其转换为日历日(round(t * 365)),并设置最小值为 1 天,然后根据所选日历(默认 hong_kong)顺延至下一个工作日。Theta 和 charm 按日历日而非工作日计算。
PnL 归因:calendar_days 表示实际经过的持仓日历天数。theta_pnl = theta × calendar_days_elapsed。如果未提供估值日期,calendar_days 默认为 1。多日持仓时,请提供明确日期以确保准确性。
安装选项
独立可执行文件(推荐)
从 Releases 页面下载 DeskPricer_v3.exe 并运行:
.\DeskPricer_v3.exe
服务将在端口 8765 上启动。如需使用其他端口:
.\DeskPricer_v3.exe --port 9000
从源代码安装
python -m venv .venv
.venv\Scripts\activate
pip install -e ".[dev]"
python -m deskpricer.main
Excel 用户指南
服务状态
拉取价格前,请检查服务是否正在运行:
| 单元格 | 公式 |
|---|---|
| 状态 | =IFERROR(FILTERXML(WEBSERVICE("http://127.0.0.1:8765/v1/health"),"//status"),"DOWN") |
预期输出: UP
示例 1:为单个期权定价并计算 Greeks
假设工作表中有:
| 列 | 标签 | 示例值 |
|---|---|---|
| C | 现货价格 | 100 |
| K | 执行价 | 105 |
| T | 到期时间(年) | 0.25 |
| R | 无风险利率 | 0.05 |
| Q | 股息率 | 0.02 |
| B | 借贷成本(可选,默认值为 0.0) | 0.0 |
| V | 波动率 | 0.20 |
| TYPE | 期权类型 | call |
| STYLE | 风格 | european |
步骤 1 — 在辅助单元格中构建 URL(例如 H2):
="http://127.0.0.1:8765/v1/greeks?s="&C2&"&k="&K2&"&t="&T2&"&r="&R2&"&q="&Q2&"&b="&B2&"&v="&V2&"&type="&TYPE2&"&style="&STYLE2
步骤 2 — 获取原始 XML(例如 I2):
=WEBSERVICE(H2)
步骤 3 — 将值提取到各个单元格中:
| 输出 | 公式 |
|---|---|
| 价格 | =VALUE(FILTERXML(I2,"//outputs/price")) |
| Delta | =VALUE(FILTERXML(I2,"//outputs/delta")) |
| Gamma | =VALUE(FILTERXML(I2,"//outputs/gamma")) |
| Vega | =VALUE(FILTERXML(I2,"//outputs/vega")) |
| Theta | =VALUE(FILTERXML(I2,"//outputs/theta")) |
| Rho | =VALUE(FILTERXML(I2,"//outputs/rho")) |
| Charm | =VALUE(FILTERXML(I2,"//outputs/charm")) |
上述示例的预期输出:
| Greek | 值 |
|---|---|
| 价格 | 2.288743 |
| Delta | 0.356244 |
| Gamma | 0.037206 |
| Vega | 0.185519 |
| Theta | -0.033315 |
| Rho | 0.083111 |
| Charm | -0.001241 |
提示: 将每个
FILTERXML包装在IFERROR(...,"ERR")中,避免某一行出错导致整个工作表无法使用。
示例 2:根据市场价格反推隐含波动率
你观察到同一期权的市场中间价为 6.50,并希望求出隐含波动率。
步骤 1 — 构建 URL(例如 H2):
="http://127.0.0.1:8765/v1/impliedvol?s="&C2&"&k="&K2&"&t="&T2&"&r="&R2&"&q="&Q2&"&b="&B2&"&price=6.50&type="&TYPE2&"&style="&STYLE2
步骤 2 — 提取隐含波动率:
=VALUE(FILTERXML(WEBSERVICE(H2),"//outputs/implied_vol"))
预期输出: 0.417484(≈ 41.7 % 波动率)
示例 3:PnL 归因
你昨天(t-1)持有一个头寸,并希望解释今天的 PnL。
假设:
| 字段 | t-1 值 | t 值 |
|---|---|---|
| 现货价格 | 100 | 102 |
| 时间 | 0.25 | 0.2466 |
| 波动率 | 0.20 | 0.22 |
| 利率 | 0.05 | 0.05 |
| 股息 | 0.02 | 0.02 |
| 借贷成本 | 0.0 | 0.0 |
| 数量 | 10 | — |
步骤 1 — 构建 URL:
="http://127.0.0.1:8765/v1/pnl_attribution?s_t_minus_1=100&s_t=102&k=105&t_t_minus_1=0.25&t_t=0.2466&r_t_minus_1=0.05&r_t=0.05&q_t_minus_1=0.02&q_t=0.02&b_t_minus_1=0.0&b_t=0.0&v_t_minus_1=0.2&v_t=0.22&type=call&style=european&qty=10&cross_greeks=true"
步骤 2 — 提取归因分类:
| 分类 | 公式 |
|---|---|
| 实际 PnL | =VALUE(FILTERXML(WEBSERVICE(H2),"//outputs/actual_pnl")) |
| Delta PnL | =VALUE(FILTERXML(WEBSERVICE(H2),"//outputs/delta_pnl")) |
| Gamma PnL | =VALUE(FILTERXML(WEBSERVICE(H2),"//outputs/gamma_pnl")) |
| Vega PnL | =VALUE(FILTERXML(WEBSERVICE(H2),"//outputs/vega_pnl")) |
| Theta PnL | =VALUE(FILTERXML(WEBSERVICE(H2),"//outputs/theta_pnl")) |
| Rho PnL | =VALUE(FILTERXML(WEBSERVICE(H2),"//outputs/rho_pnl")) |
| Vanna PnL | =VALUE(FILTERXML(WEBSERVICE(H2),"//outputs/vanna_pnl")) |
| Volga PnL | =VALUE(FILTERXML(WEBSERVICE(H2),"//outputs/volga_pnl")) |
| 残差 | =VALUE(FILTERXML(WEBSERVICE(H2),"//outputs/residual_pnl")) |
预期输出:
| 分类 | 值 |
|---|---|
| price_t_minus_1 | 2.288743 |
| price_t | 3.448862 |
| 实际 PnL | 11.601 |
| Delta PnL | 7.125 |
| Gamma PnL | 0.744 |
| Vega PnL | 3.710 |
| Theta PnL | -0.333 |
| Rho PnL | 0.0 |
| Vanna PnL | 0.343 |
| Volga PnL | 0.031 |
| 已解释 PnL | 11.621 |
| 残差 | -0.020 |
residual_pnl用于捕捉 t-1 与 t 之间的高阶效应和模型差异。启用cross_greeks=true可纳入 vanna 和 volga 的贡献。
示例 4:投资组合 / 批量 Greeks
对于组合层面的汇总,可通过 Power Query 或小型 VBA 辅助工具使用 POST /v1/portfolio/greeks 端点。该端点接受包含多条腿的 JSON 请求体,并返回各腿及汇总 Greeks。
完整的请求/响应架构请参阅 docs/api.md。
希腊字母约定
| 希腊字母 | 单位 | 说明 |
|---|---|---|
| Delta | 绝对值 | 每 1 美元现货价格变动 |
| Gamma | 绝对值 | 每 1 美元现货价格变动 |
| Vega | 每 1 个波动率点 | 即小数形式的波动率 × 100 |
| Theta | 每 日历日 | 经过一个日历日后的远期盈亏(1 个日历日后的重估值 − 今日价格)。对于衰减中的多头期权,该值为负。 |
| Rho | 每 1 个利率百分点 | 即小数形式的利率 × 100 |
| Charm | 每 日历日 | ∂delta/∂t(t − 1/365 时的 delta − 今日 delta)。对于看涨期权多头,该值为负 — delta 会随着到期日临近而衰减 |
日志位置与结构化日志记录
- 日志路径:
DESKPRICER_LOG_DIR环境变量会覆盖默认路径(Windows 上为C:\ProgramData\DeskPricer\logs,其他系统上为~/.local/share/deskpricer/logs)。 - 格式:使用 Python 标准库的
logging模块,搭配自定义 JSON 格式化器和RotatingFileHandler(每 10 MB 轮换,保留 5 个备份)。这取代了早期手动编写的open()方案。 - 更改路径:
$env:DESKPRICER_LOG_DIR = "C:\MyLogs" python -m deskpricer.main
故障排除
QuantLib 安装失败
如果 pip install 在安装 QuantLib 时失败,请确保已安装 C++ 编译器和 CMake,或使用预构建的 wheel。详细步骤请参阅 docs/operator_guide.md。
零日期到期带来的意外情况
t=0 会被设为至少 1 个日历日,以防止 QuantLib 崩溃。你会得到少量时间价值溢价,而不是纯粹的内在价值。这是有意为之。
引擎与行权风格不匹配
style=european→ 仅支持engine=analyticstyle=american→ 仅支持engine=binomial_crr或binomial_jr
XML 与 JSON
Excel 默认接收 XML。如需 JSON,请发送 Accept: application/json 或使用 ?format=json。
错误代码
| 代码 | 含义 |
|---|---|
INVALID_INPUT | 业务规则或架构验证失败 |
UNSUPPORTED_COMBINATION | 引擎与行权风格不匹配 |
PRICING_FAILURE | 意外的内部错误(不会泄露回溯信息) |
限制
- 没有 FD 引擎 — 欧式期权和经济上等价的美式期权使用 BSM 闭式解;其他美式期权使用二叉树 CRR/JR。
- 通过进程池并发运行 QuantLib — 默认使用
min(4, cpu_count())个工作进程(DESKPRICER_WORKERS)。每个请求中的投资组合腿会在单次工作进程调用中批量处理。 - 有限的扰动范围 —
bump_spot_rel≤ 0.1、bump_vol_abs≤ 0.01、bump_rate_abs≤ 0.01。 - 没有数据库或持久化 — 所有状态均为每个请求独立的内存状态。
- 没有身份验证、TLS 或速率限制 — 按设计仅供本地使用。
运行测试
pytest tests -v
设计决策
按设计仅供本地使用
DeskPricer 是一款个人桌面工具,而非公共 API。这解释了所有有意省略的功能:
- 没有身份验证 / 授权 — 只有
127.0.0.1能访问该服务。 - 没有 TLS / HTTPS — 按设计,本地环回流量未加密。
- 没有速率限制 — 不进行节流;并发请求由进程池处理,而不是在单一 QuantLib 全局状态上串行处理。
- 没有 Swagger / Redoc — 生产版本会隐藏 OpenAPI 文档,以缩小攻击面。
- 默认使用 XML — Excel 的
WEBSERVICE函数不会发送Accept: application/json。
如果你需要这些功能,DeskPricer 并不适合你。请使用正规的 API 网关或功能齐全的定价平台。
QuantLib 为什么在进程池中运行
QuantLib 的 Python 绑定依赖单个进程全局的 Settings.instance() 对象。DeskPricer 不会使用 asyncio.Lock 将所有请求串行化,而是通过 ProcessPoolExecutor 分派 QuantLib 工作。每个工作进程都有独立隔离的设置,因此并发的 Agent 或 Excel 调用可以并行定价,而不会破坏评估日期。进程池大小默认为 min(4, cpu_count()),可通过 DESKPRICER_WORKERS 配置。
欧式期权和经济上等价的美式期权完全绕过 QuantLib,改用纯 Python 的 BSM 实现(bsm_fast),并通过 QuantLib 验证,精度达到小数点后六位。
为什么 PnL 归因使用带有许多查询参数的 GET
Excel 的 WEBSERVICE 函数仅支持 HTTP GET。由于此服务的主要用户是 Excel,因此 GET /v1/pnl_attribution 端点专门针对与 WEBSERVICE 的兼容性进行设计。需要更简洁 JSON 请求正文的编程客户端目前可以使用 POST /v1/portfolio/greeks;未来版本可能会添加用于 PnL 归因的 POST 替代方案。
项目结构
DeskPricer/
├── pyproject.toml
├── README.md
├── src/deskpricer/ # FastAPI app + pricing core
│ ├── app.py # Thin composition root
│ ├── routers/ # APIRouter modules
│ ├── services/ # Pricing orchestration + QL lock
│ ├── pricing/ # QuantLib pricing engines
│ ├── schemas.py # Pydantic models
│ ├── responses.py # XML/JSON serializers
│ ├── errors.py # Custom exceptions
│ ├── logging_config.py # Structured JSON logging
│ └── main.py # Uvicorn entrypoint
├── tests/ # pytest + hypothesis
├── tests/fixtures/ # Regression baseline JSONs
├── scripts/ # Build + fixture generation
├── sample/ # Demo Excel workbook
└── docs/ # API ref + operator guide
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。