Strasmore Research
学习 Matt Connor作者: Matt Connor · data as of August 12, 2026 · refreshed weekly

QuantLib Rust 移植版 libitofin 介绍

libitofin 是 QuantLib 的 Rust 语言移植版本,并可通过 itofin 包在 Python 中调用。本文探讨了定价库相比电子表格的结构化优势,并说明了为何在项目发布一版之前应锁定特定版本标签以确保稳定性。

libitofin 的核心定义是:它是 QuantLib 的 Rust 语言移植版。QuantLib 是自 21 世纪初以来衍生品定价领域的开源参考标准 C++ 库。libitofin 同时提供了一个名为 itofin 的 Python 包。截至 2026 年 8 月,该项目仍处于 1.0 版本之前,这一标签决定了其实际应用中的所有注意事项。定价库的价值在于其公式外围的配套机制,而无论使用何种编程语言,这些机制的构建工作本质相同。

定价库相比电子表格公式的优势

在电子表格中,Black-Scholes 单元格只需输入五个参数即可返回价格。公式本身是最简单的部分,库的价值在于其外围的四个层级。

期限结构(Term structures)。 折现率是一条跨越不同期限的曲线,对于报价点之间的缺口,需要通过插值规则来确定。下图展示了原始数据:特定日期的国债期限报价。

查询最新国债收益率曲线
每个数字背后的完整 SQL
SELECT
    arrayElement(tenors, i)                  AS tenor,
    round(arrayElement(rates, i), 2)         AS yield_pct,
    formatDateTime(curve_date, '%b %e, %Y')  AS as_of
FROM
(
    SELECT
        date AS curve_date,
        ['1 month', '3 months', '6 months', '1 year', '2 years', '3 years',
         '5 years', '7 years', '10 years', '20 years', '30 years']         AS tenors,
        [toFloat64(yield_1_month), toFloat64(yield_3_month), toFloat64(yield_6_month),
         toFloat64(yield_1_year),  toFloat64(yield_2_year),  toFloat64(yield_3_year),
         toFloat64(yield_5_year),  toFloat64(yield_7_year),  toFloat64(yield_10_year),
         toFloat64(yield_20_year), toFloat64(yield_30_year)]                AS rates,
        arrayJoin(range(1, 12))                                            AS i
    FROM global_markets.treasury_yields
    WHERE date = (SELECT max(date) FROM global_markets.treasury_yields)
)
WHERE yield_pct > 0
ORDER BY i
Run this yourself

截至 Aug 10, 2026,该报价曲线包含 7 个期限,从 3.79% 的 1 month5.25% 的 30 years。电子表格处理此类问题通常采用查找功能和硬编码的 4% 利率。而定价库则使用曲线对象,所有工具均基于此定价,并遵循明确的插值策略(如零息利率线性插值、折现因子对数线性插值、单调样条插值)及针对末端点的外推策略。只需将该对象移动一个基点,账簿中的所有敏感度指标都会随之同步调整。

计息天数惯例(Day-count conventions)。 利息按年化分数累积,该分数的定义取决于工具所采用的惯例。Actual/360 将经过天数除以 360;Actual/365 除以 365;30/360 系列则假设每月均为 30 天。Business/252 根据 252 天的年度计算交易时段,这要求加载包含节假日的真实交易所日历。假设借款 100 万美元,利率 5%,期限 90 天:在同一笔交易中,Actual/360 计息 12,500 美元,而 Actual/365 计息 12,329 美元。下图说明了为何营业日基准需要日历而非简单的除数。

查询按月统计:交易日与日历日对比
每个数字背后的完整 SQL
SELECT
    formatDateTime(toStartOfMonth(date), '%Y-%m')      AS month,
    countDistinct(date)                                AS trading_days,
    toUInt8(toDayOfMonth(toLastDayOfMonth(max(date)))) AS calendar_days
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
  AND date >= toStartOfMonth(today() - 365)
  AND date <  toStartOfMonth(today())
GROUP BY month
ORDER BY month
Run this yourself

在所观察的 12 个月中,2026-07 包含 22 个交易时段,而日历天数为 31 天。没有任何算术规则能得出第一个数字,它源于节假日日历,且每个月的结果各不相同。定价库会针对不同场所和国家提供这些日历,而电子表格则需要用户自行维护。

校准机制(Calibration machinery)。 模型参数并非直接报价。你需要通过将模型价格拟合至整个报价曲面上的市场价格来选取参数,并随曲面变动进行重新拟合。这是一个有界的最小二乘问题,定价库提供了相应的循环机制:Levenberg-Marquardt 优化器、无需硬约束即可保持方差为正的参数变换、针对报价集的成本函数,以及在无法收敛时会明确报错而非返回初始猜测值的收敛准则。

数值计算层(A numerics layer)。 这一切的基础是线性代数和积分。QR 和 SVD 分解用于求解拟合产生的系统,求积法和傅里叶积分用于对公式为积分形式的模型进行定价,求根算法则用于反推隐含波动率。这一层虽然枯燥,但恰恰是容易被错误实现的部分。典型错误是手写的牛顿求解器,它在流动性好的平值报价上能收敛,但在深度虚值报价上则会失效。其次是矩阵求逆,在近奇异拟合中会损失精度,返回看似合理实则错误的参数。如果你打算从零开始构建思维模型,开源量化交易书籍比任何库的 API 参考手册都是更好的起点。

什么是 libitofin,它是 Rust 版的 QuantLib 吗?

是的,在关键意义上确实如此。它是 QuantLib 设计的 Rust 移植版,且项目声明已通过 QuantLib 自身的测试套件验证。这是移植数值库最诚实的方式:结果是与参考实现进行比对,而非基于对答案的臆测。Python 接口名为 itofin,因此 Python 进程无需 C++ 工具链即可调用 Rust 引擎。

更重要的标签是“1.0 版本之前”。根据 Rust 的版本控制惯例,0.x 版本不保证次版本之间的兼容性:从 0.4 升级到 0.5 可能会重命名、移动或删除任何内容。请将 API 视为移动目标,并养成以下习惯:

  • 在锁文件(lockfile)中锁定具体的标签版本,并有目的地进行升级,以自身的测试套件作为准入关卡。
  • 在库类型外封装一层薄薄的包装器,这样重命名只需修改一个文件而非四十个。
  • 记录产生数据时所用的库版本,以便在重新运行结果不一致时,能定位到升级而非市场波动。
  • 在 1.0 版本发布前,生产环境的保证金、抵押品和监管风险指标应使用具备兼容性承诺的工具。

以上并非对该项目的批评。1.0 版本之前是一个准确的自我描述,对于一个庞大库的早期移植项目而言,这是恰当的定位。失败的模式在于读者将其视为 QuantLib 的直接替代品,结果在季度中途因签名变更而导致代码中断。安装说明也会随版本变动,且 cargo 和 pip 解析依赖时均需网络连接,因此请阅读你打算锁定的标签版本对应的 README,而非复制博客文章中的片段。

Python、编译引擎还是 Rust?

两个问题决定了选择,且与个人偏好无关。你每秒需要重新定价多少次?你的数字是否需要在不同进程间保持一致?链条规模决定了第一个问题的答案。

查询单日成交的独立期权合约数
每个数字背后的完整 SQL
WITH (SELECT max(date) FROM global_markets.options_greeks) AS last_session
SELECT
    underlying_symbol                      AS symbol,
    countDistinct(ticker)                  AS contracts_priced,
    formatDateTime(max(date), '%b %e, %Y') AS as_of
FROM global_markets.options_greeks
WHERE date = last_session
  AND underlying_symbol IN ('SPY', 'AAPL', 'NVDA', 'MSFT', 'KO')
  AND volume > 0
GROUP BY symbol
ORDER BY contracts_priced DESC
Run this yourself

Aug 11, 2026SPY 在单次会话中交易了 5336 个不同合约,而 KO360。对宽泛的链条进行一次定价并不困难。但如果要在每次报价更新时,对账簿中所有标的资产的每个合约进行五种敏感度计算,那就是一个具有不同约束的程序。

当循环速度以每分钟数千次估值为单位,且周边工作为研究、分析或日终标记时,请继续使用带有成熟库的 Python。QuantLib 的 Python 绑定是成熟的选择:相同的 C++ 引擎、最广泛的工具覆盖范围以及多年的生产验证。在研究代码中,速度很少是瓶颈,覆盖范围和正确性才是。

当循环处于高频状态,而周边代码并非如此时,可从 Python 调用编译引擎。需要关注的是边界成本。从 Python 进行逐合约调用会在每次跨越边界时产生开销,解决方法是向引擎传递数组并取回数组。这正是 itofin 与 QuantLib-Python 共同瞄准的领域。

当定价循环本身就是产品时,请使用 Rust:例如报价服务中的定价器、不可错过的定时风险计算,或部署在没有 Python 环境的机器上的二进制文件。第二个问题也指向这里。浮点运算结果取决于操作顺序,因此同一模型的两次实现可能在最后几位数字上存在差异,而研究笔记本与生产服务之间的不一致会导致数周的排查工作。使用同一个引擎可以消除此类差异,这也是为何无论使用何种语言,编译核心配合绑定都是持久的论点。同样的逻辑也适用于策略研究,正如 可复现的回测 所展示的那样。

校准目标的实际位置

校准需要拟合对象,即市场隐含波动率曲面。求根计算部分详见 隐含波动率的计算方法。下图展示了模型必须匹配的形态。

查询AAPL 平值期权隐含波动率(按到期时间)
每个数字背后的完整 SQL
SELECT
    multiIf(days_to_expiry <=   7, '0 to 7 days',
            days_to_expiry <=  30, '8 to 30 days',
            days_to_expiry <=  60, '31 to 60 days',
            days_to_expiry <= 120, '61 to 120 days',
            days_to_expiry <= 240, '121 to 240 days',
                                   '241 days or more') AS dte_bucket,
    round(avg(implied_volatility) * 100, 1)            AS iv_pct,
    countDistinct(ticker)                              AS contracts
FROM global_markets.options_greeks
WHERE underlying_symbol = 'AAPL'
  AND date >= today() - 10
  AND days_to_expiry >= 0
  AND iv_converged = 1
  AND volume > 0
  AND abs(toFloat64(strike_price) / toFloat64(underlying_close) - 1) < 0.05
GROUP BY dte_bucket
ORDER BY min(days_to_expiry)
Run this yourself

在平值附近,0 to 7 days 桶中的 AAPL 合约在所观察的会话中平均隐含波动率为 29.6%,而 241 days or more29.3%。携带单一波动率参数的模型无法同时拟合这两个点,这正是存在波动率期限结构模型的原因。将模型拟合至此类曲面即为校准步骤,而从拟合模型中得出的敏感度即为希腊字母(Greeks),详见 期权希腊字母详解

这些图表是如何构建的

曲线面板读取国债系列中最近的一个报价日期,并将 11 个期限列展开为行,剔除当天无报价的期限。会话面板统计每月 SPY 磁带上的不同日期,这是衡量完整交易所会话数的有效代理。链条面板统计最新可用会话中成交量非零的不同合约代码,并按标的资产分组。波动率面板仅保留收敛且成交量非零、行权价在标的收盘价 5% 以内的合约,这是标准的平值区间;前端桶包含当日到期的合约。

常见问题解答

libitofin 是 QuantLib 的直接替代品吗?

不是。截至 2026 年 8 月,它是一个 1.0 版本之前的移植项目,仅覆盖了 QuantLib 的部分功能,并根据 QuantLib 自身的测试套件验证结果。对于生产环境,通过 Python 绑定调用的 QuantLib 仍然是更广泛且更稳定的选择。

对于定价库而言,1.0 版本之前意味着什么?

根据 Rust 的版本控制惯例,0.x 版本不提供兼容性承诺:下一个次版本可以自由重命名或删除任何内容。在实践中,这意味着需要锁定具体的标签版本,并在每次升级时重新运行自己的测试套件。

我需要会写 Rust 才能使用 libitofin 吗?

不需要。该项目发布了一个名为 itofin 的 Python 包,因此可以在普通的 Python 进程中调用该引擎。当你需要交付定价循环本身时,编写 Rust 才变得有意义。

定价库相比电子表格公式能提供什么?

它提供曲线而非单一利率、带有真实交易所日历的计息天数惯例、将模型参数拟合至报价的校准循环,以及支撑上述三者的经过测试的数值计算层。闭式解公式只是工作的一小部分。

编程语言会改变期权价格吗?

数学上不会。但它会影响可复现性:浮点运算结果取决于操作顺序,因此同一模型的两种实现可能在最后几位数字上存在差异。使用同一个引擎进行研究和生产可以消除这种差距。


此处每个面板下方均附有其对应的 SQL 查询,展开即可查看统计方式。关于曲线、日历和链条的相同问题,均可在 Strasmore 终端上用简单英语进行查询。