QuantLib · Errors and pitfalls

QuantLib 报错与结果异常排查:价格为 0、曲线超界、引擎不匹配

本页不写「跑通了」的示例,只写跑不通的东西:13 类真实报错的原文、根因与修复动作,以及 10 类不报错但结果可疑的陷阱——价格为 0、折现因子大于 1、隐含波动率反解失败、绑定函数根本调用不了。所有文本都在 QuantLib 1.43 / Python 3.11.9 上逐字跑出。

依据:本机 QuantLib 1.43 实测输出报错 13 类 / 静默陷阱 10 类每条都给出根因与下一步
先分清两类抛异常 / 静默错值
把约定打出来估值日 / 结算日 / 计息 / 日历
交叉验证换引擎或换方法复算
再定位到单变量一次只改一项

排查顺序:先判断「报错」还是「静默错值」,再把约定打印出来,然后用第二种方法复算,一次只改一个变量;示意图非实盘界面。

测试版本QuantLib 1.43(PyPI wheel `quantlib-1.43-cp39-abi3-win_amd64`)Python 版本3.11.9(Windows,platform 字符串 `Windows-10-10.0.26200-SP0`)最后核验2026-10-09(每条报错原文均在本机跑出)估值日2024-01-15(例外的边界实验单独写明)复现方式把本页脚本复制成 .py 直接运行即可复现同类报错覆盖范围13 类真实报错 + 10 类「不报错但结果可疑」证据等级本站实测(原文逐字抄录,不做改写与推测)适用范围报错文本随 QuantLib 版本与操作系统可能不同,以你本机输出与官方为准
证据来源本机运行输出(报错原文逐字抄录)
覆盖范围13 类报错 + 10 类静默陷阱
排查入口6 步流程 + 复现脚本
适用版本QuantLib 1.43 / Python 3.11.9
Error index

报错速查表:症状、原文、根因、修复

下表每一行的「报错原文」都是从本机运行结果里逐字抄下来的,未做改写。如果你的报错文本与之不同,先按类型判断,再到官方仓库检索。

报错原文均来自 QuantLib 1.43 / Python 3.11.9 本机运行
症状报错原文(类型 + 消息)根因修复动作
读到空价格RuntimeError: null pricing engine未 setPricingEngine() 就直接调用 bond.cleanPrice() / NPV 一类需要引擎的方法先挂引擎:bond.setPricingEngine(ql.DiscountingBondEngine(yts));只想按收益率算价就用 ql.BondFunctions.cleanPrice(bond, y, dc, ql.Compounded, ql.Annual)
期权定价直接失败RuntimeError: not an European option把美式期权(AmericanExercise)交给了只支持欧式的解析引擎换匹配引擎(二叉树 / 有限差分),或把行权方式改成欧式;引擎与行权方式必须成对选择
取希腊字母失败RuntimeError: vega not provided树引擎(BinomialVanillaEngine)不提供 vega,MC 引擎不提供 delta用解析引擎或有限差分引擎取 vega;或自行对波动率做差分(本机实测差分 vega 与解析值差 3.1e-09)
曲线取早期日期报错RuntimeError: negative time (-4.03825) given向曲线请求参考日之前的折现因子,时间为负把估值日与曲线参考日对齐;历史数据另建一条曲线,不要向同一条曲线索要过去的时间点
问远端期限失败RuntimeError: time (5.075) is past max curve time (1.02222)曲线只喂了 1 年报价,却请求 5 年零息;QuantLib 默认不外推补齐报价节点,或显式 curve.enableExtrapolation()(外推是模型假设,要自己承担)
报价倒挂后取远端失败RuntimeError: time (2.03056) is past max curve time (1.02222)6M 5% → 1Y 1% 这类倒挂报价会让 bootstrap 只建到最近的那个节点就停下先检查报价单调性与量级;倒挂本身不报错,但会静默把曲线截断
构造日期报错TypeError: Wrong number or type of arguments for overloaded function 'new_Date'.ql.Date 传入了浮点(如 ql.Date(2024.5, 1, 1))日、月、年必须都是整数;字符串日期用 ql.Date('2025-01-15', '%Y-%m-%d')
期权构造报错TypeError: in method 'new_EuropeanExercise', argument 1 of type 'Date const &'把 '2025-01-15' 这类字符串直接传给 EuropeanExercise先转成 ql.Date:ql.EuropeanExercise(ql.Date(15, 1, 2025))
估值日设到上限日期RuntimeError: Date's serial number (109575) outside allowed range [367-109574], i.e. [January 1st, 1901-December 31st, 2199]evaluationDate 设为 2199-12-31 后,内部再叠加结算天数就越界把估值日留在合理区间;上限日期 ql.Date.maxDate() 只是日期类型的边界,不是可用的估值日
模型取 delta 失败RuntimeError: delta not providedAnalyticHestonEngine 不提供 delta自己用有限差分算 delta(对现货 ±h 重定价),或换用专门的数值引擎
类不存在AttributeError: module 'QuantLib' has no attribute 'BinomialHestonEngine'该类在 1.43 的 Python 绑定里不存在先用 dir(ql) 确认可用类名,不要假设 C++ 文档里的类在 Python 侧一定可用
函数不存在AttributeError: module 'QuantLib' has no attribute 'bondYield'顶层 ql.bondYield / ql.bondPrice 在 1.43 的 Python 绑定里没有改用 bond.bondYield(dc, ql.Compounded, ql.Annual) 或 ql.BondFunctions.bondYield(...)
绑定整体不可用TypeError: Wrong number or type of arguments for overloaded function 'BondFunctions_zSpread'.BondFunctions.zSpread 的 10 个 C++ 重载在 Python 侧都不可调用(参数类型 BondPrice 为 std::pair<Real,Real>,绑定未包装)别在 Python 里调它:自己实现 z-spread 求解(对利差做二分或牛顿迭代,直到价格匹配)
隐含波动率反解失败RuntimeError: negative Put price (-6.5637) implied by put-call parity. No solution exists for Call strike 80, forward 105.142, price 17.3484, deflator 0.951099给出的价格与远期/行权价不满足无套利边界,反解不存在解先校验价格是否落在 max(F−K,0) 与远期价格之间;再检查远期与折现因子是否与定价时一致
怎么用这张表:先按「症状」定位,再把你的报错原文与表中文本比对。完全一致 → 直接照修复动作做;只有前半段一致(例如同为 RuntimeError 但消息不同)→ 把消息整段复制到官方仓库检索,仍以官方答复与你的本机输出为准。
Zero price

价格为 0 的四种成因:程序不报错,结果却是空的

「价格为 0」是本页实测中最难发现的一类问题——它不抛异常,容易被当成「便宜」而不是「错了」。下表四种成因都在本机复现过,判据可以直接照用。

实测环境:QuantLib 1.43,估值日 2024-01-15
成因本机实测现象判据(怎么确认)修复动作
期权已到期到期日 2024-01-01(早于估值日 2024-01-15)时 NPV = 0.0、delta = 0.0,isExpired 为 True,不抛异常打印 option.isExpired();为 True 就是到期,不是定价问题把估值日移到到期日之前,或改用到期日的实现价值(payoff)而不是引擎 NPV
到期日等于估值日到期日与 valuationDate 同为 2024-01-15 时同样返回 0.0,isExpired 为 True检查 evaluationDate 与到期日是否相差 0 天至少留出 1 天剩余期限;对当日到期的头寸用实现价值口径
债券结算日被推到到期日之后1 年期债券设 settlementDays=300 → 结算日 2025-03-18 晚于到期日 2025-01-15,净价 / 全价 / 应计全部为 0.0,而 isExpired 仍为 False比较 bond.settlementDate() 与 bond.maturityDate();isExpired 在这个场景下不可靠,不能只用它判断把 settlementDays 改回市场实际结算天数(本机默认示例为 2)
同一成因换档位复现同只债券把 settlementDays 改成 400 → 结算日 2025-08-08,净价依然为 0.0说明不是某一档参数的偶发数值问题,而是结算日越界后的稳定行为多档一起试、并把结算日与到期日写进日志,比只看一个数字更容易定位
债券到期日早于估值日到期日 2023-01-15(估值日 2024-01-15)时净价与应计利息都是 0.0,也不报错先打印 bond.maturityDate() 与估值日,确认债券是否还有存续现金流换一只仍存续的债券;已到期债券的价格要用到期日口径另行计算
现金流为空(间接表现)周期设置为 1 年、区间只有 6 个月时,Schedule 只生成端点日期,不报错;若后续工具依赖中间现金流,价格会出现异常小值或 0打印现金流:[str(cf.date()) for cf in bond.cashflows()]把周期改到小于区间长度,并核对生成的付息日与发行文件一致
通用判据:价格等于 0 时先看三样东西——isExpired()、settlementDate()、maturityDate()。本机实测说明:isExpired 只覆盖「工具自身已到期」,覆盖不了「结算日在到期日之后」和「现金流为空」这两类,所以不能只依赖它。
Silent traps

不报错但结果可疑:十种「安静的错误」

这一节比报错更值得警惕:程序照常返回数字,数字却没有意义。下表每一条都是本机实测的原值。

以上均为本机实测返回值,未做四舍五入改写
场景本机实测返回为什么可疑怎么处理
已到期期权NPV = 0.0返回 0 而不是报错,容易被当成正常结果先判 isExpired,再谈定价
结算日晚于到期日的债券净价 / 全价 / 应计 = 0.0isExpired 仍为 False,没有任何提示比较结算日与到期日,不依赖 isExpired
负利率报价折现因子 = 1.255230125523013折现因子大于 1 是负利率下的正常数学结果,但和直觉冲突,容易被当成 bug负利率环境里把 DF 的检验区间放宽到大于 1;核对报价符号
波动率设为 0NPV = 4.890087197528255、delta = 1.0没有任何提示,delta 恒为 1 说明模型退化成了远期现金流检查波动率输入链路(报价、曲面插值、日历)是否把波动率清零
Heston 违反 Feller 条件 + 深度虚值NPV = 1.5582964397103938e-05(2κv0−σ² = −2.248)参数不规范仍能出价,只是数值已失去金融含义先验证 2κv0 − σ² > 0;不满足时改用全隐式格式或重新校准
BondFunctions 的基点价值符号−0.05396566负号与市场口径相反;直接当 DV01 用会把风险方向写反市场口径用 (P(y−1bp) − P(y+1bp))/2,本机得 0.05396585
计息约定名字与直觉不符ActualActual(Bond) 的名字显示为 Actual/Actual (ISMA);Thirty360(German) 显示为 30E/360 (ISDA) day counter按名字反查实现会走错分支;对账双方可能各自以为「同名」以 dayCounter().name() 的实际输出与官方实现说明为准,不要只看构造时写的关键字
报价倒挂曲线仍能构造成功,只是后续取远端期限报 max curve time构造阶段不报错,错误延后到取值阶段才暴露构造后立刻做自洽性检查:用曲线反算输入报价的 par rate,偏差应在 1bp 量级
日历选错非假期边界上价格与对照组完全相同(本机实测 1/15、7/15 付息日)「没报错、价格也一样」不代表日历选对了,差异只在假期边界出现构造假期边界日期(如 10/1、2/11)做针对性验证
估值日未显式设置以运行当天为基准(本机 ql.Date.todaysDate() 返回运行日)同一份脚本每天结果不同,回测与对账无法复现在脚本开头固定 ql.Settings.instance().evaluationDate
一句话判据:结果「看起来合理」不等于「算对了」。遇到小于 1e-4 的价格、恒为 1 的 delta、或者是 0 的结果,都当作可疑值处理,先用第二种方法复算一次再下结论。
Curve failures

曲线与 bootstrap 类故障:从构造到取值都可能出错

曲线的问题有一半不在构造阶段,而在取值阶段。下表把本机实测的六类曲线故障按「什么时候暴露」排列。

曲线类故障实测:QuantLib 1.43,参考日 2024-01-15
现象暴露阶段本机实测表现处理顺序
时间超界(未外推)取值阶段time (5.075) is past max curve time (1.02222)① 补报价节点 ② 显式开启外推 ③ 确认你请求的期限确实在市场可交易范围内
向参考日之前取值取值阶段negative time (-4.03825) given① 检查估值日与曲线参考日是否一致 ② 历史时点另建曲线
报价倒挂导致曲线截断构造阶段不报错、取值阶段报错6M 5% → 1Y 1% 的报价下,请求 2 年零息时报 time (2.03056) is past max curve time (1.02222)① 先做报价质量检查(单调性、量级、单位是百分数还是小数) ② 再做自洽性反算
负利率报价构造阶段不报错−20% 存款报价下折现因子为 1.255230125523013① 确认负利率是否真实 ② 若真实,把 DF 检查区间放宽到大于 1 ③ 检查取对数/开方类后续步骤
插值方法带来的偏差使用阶段同一组报价下,4 年期折现因子的极差为 2.5723 bp(四种插值方法对照)① 在报价节点上结果一致是正常的,要检查节点之间 ② 定价用的期限尽量落在节点上
BondFunctions.zSpread 无法调用调用阶段TypeError: Wrong number or type of arguments for overloaded function 'BondFunctions_zSpread'.(10 个重载全部失败)① 先确认不是参数写错 ② 换成自己实现 z-spread 求解 ③ 记录版本与失败方式便于检索
曲线自洽性检查(本页建议固定动作):曲线建好后,立刻用曲线反算输入工具的 par rate,偏差应在 1bp 量级。本机实测的一组正常曲线,各期限反算偏差在 +0.018 bp 到 −1.364 bp 之间;如果你看到几十 bp 的偏差,说明报价、日历或计息约定至少有一项对不上。
Binding gaps

Python 绑定缺口与命名差异:C++ 文档里有的,Python 里可能没有

这一节是本站实测中最容易被教程误导的部分。很多中文示例来自更早的版本或 C++ 文档,直接复制到 1.43 会报 AttributeError。下表是逐项实测结果。

逐项实测(QuantLib 1.43 / Python 3.11.9)
对象本机实测结果影响替代写法
ql.bondYieldAttributeError: module 'QuantLib' has no attribute 'bondYield'沿用旧示例的脚本直接失败bond.bondYield(dc, ql.Compounded, ql.Annual) 或 ql.BondFunctions.bondYield(bond, price, dc, ql.Compounded, ql.Annual)
ql.bondPrice同样不存在(同为顶层函数)无法用函数式计算指定收益率下的价格ql.BondFunctions.cleanPrice(bond, y, dc, ql.Compounded, ql.Annual)
bond.cleanPrice()(无参)RuntimeError: null pricing engine不挂引擎就没有价格先 setPricingEngine;或走 BondFunctions 的按收益率算价路径
BondFunctions.zSpread10 个重载在 Python 侧全部不可调用(TypeError)z-spread 无法直接用绑定计算自己实现求解:对利差迭代,直到按曲线 + 利差折现得到的价格等于目标价格
BondFunctions.basisPointValue返回 −0.05396566(负号)符号与市场口径相反,直接用于风险统计会反向用双点差分:(P(y−1bp) − P(y+1bp))/2 = 0.05396585
ql.Average 枚举只有 Arithmetic 与 Geometric;没有 ql.AverageType按 AverageType 写的亚式期权脚本报 AttributeError用 ql.Average.Arithmetic / ql.Average.Geometric
BinomialHestonEngineAttributeError(1.43 无此类)想做 Heston 树定价时会先撞在类名上先用 dir(ql) 列出可用类;能用的 Heston 引擎是解析引擎
AnalyticHestonEngine.delta()RuntimeError: delta not provided模型定价可以,但拿不到解析希腊字母对现货做 ±h 重定价求差分 delta
ActualActual(Bond) 的显示名Actual/Actual (ISMA)按名字回查实现容易走错分支以实际输出名为准,并在对账文档里写清构造方式
Thirty360(German) 的显示名30E/360 (ISDA) day counterGerman 与 ISDA 在本机为同一实现,名字与直觉不符对账时写清构造关键字,不要只写显示名
通用做法:写新脚本前先用 dir(ql) 与 help(ql.BondFunctions) 确认对象是否存在、重载长什么样;函数文档字符串里会列出全部重载签名,比凭记忆写快得多。
Install vs logic

先判断「是不是安装问题」:四条判据与指向

很多「算不出价格」的问题其实出在环境上。安装步骤不在这里重复(见安装页),这一节只给判据:怎么在 1 分钟内判断故障属于环境层还是代码层。

判据表:先分层,再定位
判据环境层故障的表现代码层故障的表现下一步
能不能导入import QuantLib 直接失败(ModuleNotFoundError 或 DLL 加载失败)导入正常,报错发生在构造或取值阶段导入失败 → 先修环境;导入正常 → 进报错速查表
版本是否可见打印 QuantLib.__version__ 与预期不符版本正确但行为与教程不同版本不同 → 先按本页绑定缺口表核对接口;版本正确 → 查参数
最小脚本能否跑通只 import + 建一个日期就失败最小脚本正常,加入自己的输入后才失败先把最小脚本跑通再逐段加回你的代码
平台与 wheelWindows 上安装的是 ABI 不匹配的 wheel,或源码编译缺编译器 / Boost平台与 wheel 正常,只是结果不对环境问题看安装页;结果问题看本页第 2、3 节
多版本共存同一脚本在不同终端表现不同(虚拟环境未激活)任何终端表现一致固定使用同一个虚拟环境,并在报告里写明版本与路径
依赖与编码中文路径、非 UTF-8 默认编码导致的文件名或读取异常纯计算逻辑报错文件读取统一指定 encoding='utf-8';路径尽量用英文目录
环境类问题的入口:安装路径、wheel 命名与 ABI 兼容、验收检查项都写在 安装页;本页只负责「算出来的数不对」这一类。另外提示一句:QuantLib 不提供行情数据,如果你的问题是「取不到数据」,那不是安装问题,而是数据源不在库的范围内。
Workflow

六步通用排查流程:从复现到定位

下面的顺序是按「先便宜后昂贵」排的:先记录最小复现,再分层,最后才动模型与参数。每一步都给出可执行动作与预期输出。

  • 1. 先把最小复现脚本固定下来

    把估值日、标的、报价全部写死在脚本里。预期输出:拿到一份能稳定复现同一报错的脚本——本页的复制脚本就是按这个要求写的,去掉所有随机与时间依赖。
  • 2. 分层:是环境问题还是计算问题

    先跑 import QuantLib 与 print(QuantLib.__version__)。预期输出:版本号(本机 1.43)。若这一步失败,直接去安装页,不要在定价代码里找原因。
  • 3. 把约定全部打印出来再下结论

    打印 evaluationDate、bond.settlementDate()、bond.dayCounter().name()、bond.calendar().name()、引擎类型。预期输出:五项约定一目了然——本机实测中,价格对不上多数源于这五项里的某一项。
  • 4. 判断是「报错」还是「静默错值」

    看返回值:是抛异常,还是返回 0 / 小于 1e-4 / 恒为 1 的数。预期输出:分类结论。静默错值走本页第 2、3 节,报错走第 1 节。
  • 5. 用第二种方法复算一次

    解析引擎对二叉树/有限差分/蒙特卡洛,或按收益率算价对按曲线折现。预期输出:两个结果的差;本机实测同参数下 CRR 801 步与解析解差 201.75 ppm、有限差分差 37.91 ppm——量级决定你该接受还是该继续查。
  • 6. 一次只改一个变量,并记录版本

    只改日历、或只改计息约定、或只改结算天数,观察结果变化。预期输出:把「改了什么、从多少变到多少、在哪台环境」写成一行记录。本机实测:仅把应计约定换成 Actual/360,净价就从 99.75408467 变到 99.99419065。
  • Reproduce

    错误复现脚本:把这些报错在自己机器上跑一遍

    下面这段脚本可以一次触发本页的多数报错,并打印类型与消息。建议在你的环境里跑一次,把输出与「报错速查表」对照——不同版本或平台的消息可能不同,以你的输出为准。

    repro_errors.py(QuantLib 1.43 / Python 3.11.9 实测)import QuantLib as ql def cap(label, fn): try: print("[无异常]", label, "->", fn()) except Exception as e: print("[报错]", label, "|", type(e).__name__, "|", str(e).splitlines()[0]) today = ql.Date(15, 1, 2024) ql.Settings.instance().evaluationDate = today dc = ql.ActualActual(ql.ActualActual.ISDA) sched = ql.Schedule(ql.Date(15, 1, 2020), ql.Date(15, 1, 2030), ql.Period(ql.Annual), ql.TARGET(), ql.ModifiedFollowing, ql.ModifiedFollowing, ql.DateGeneration.Forward, False) bond = ql.FixedRateBond(2, 100.0, sched, [0.03], dc) cap("未设引擎读价格", lambda: bond.cleanPrice()) # null pricing engine cap("顶层 bondYield", lambda: ql.bondYield(0.03, bond, dc, ql.Compounded, ql.Annual)) # AttributeError cap("zSpread 绑定", lambda: ql.BondFunctions.zSpread( bond, bond.dirtyPrice(), ql.YieldTermStructureHandle(ql.FlatForward(today, 0.03, dc)), ql.Compounded, ql.Annual)) # TypeError cap("Date 传浮点", lambda: ql.Date(2024.5, 1, 1)) # TypeError cap("到期日传字符串", lambda: ql.EuropeanExercise("2025-01-15")) # TypeError # 曲线时间超界 short = ql.FlatForward(today, 0.03, dc) cap("向曲线索要过去", lambda: short.discount(ql.Date(1, 1, 2020))) # negative time one_year = ql.PiecewiseFlatForward(0, ql.TARGET(), [ ql.DepositRateHelper(ql.QuoteHandle(ql.SimpleQuote(0.05)), ql.Period(1, ql.Years), 2, ql.TARGET(), ql.ModifiedFollowing, True, ql.Actual360())], ql.Actual360()) cap("索取超出末端的期限", lambda: one_year.zeroRate( ql.TARGET().advance(today, ql.Period(5, ql.Years)), ql.Actual360(), ql.Compounded, ql.Annual).rate()) # past max curve time # 静默错值:不报错,但结果是 0 expired = ql.VanillaOption(ql.PlainVanillaPayoff(ql.Option.Call, 100.0), ql.EuropeanExercise(ql.Date(1, 1, 2024))) proc = ql.BlackScholesMertonProcess( ql.QuoteHandle(ql.SimpleQuote(100.0)), ql.YieldTermStructureHandle(ql.FlatForward(today, 0.0, ql.Actual365Fixed())), ql.YieldTermStructureHandle(ql.FlatForward(today, 0.05, ql.Actual365Fixed())), ql.BlackVolTermStructureHandle(ql.BlackConstantVol(today, ql.NullCalendar(), 0.20, ql.Actual365Fixed()))) expired.setPricingEngine(ql.AnalyticEuropeanEngine(proc)) print("[静默] 已到期期权 NPV =", expired.NPV(), "isExpired =", expired.isExpired()) # 0.0 / True
    运行脚本后应当看到的输出对照(以你本机输出为准)
    复现项预期看到的类型本机实测消息(片段)
    未设引擎读价格RuntimeErrornull pricing engine
    顶层 bondYieldAttributeErrormodule 'QuantLib' has no attribute 'bondYield'
    zSpread 绑定TypeErrorWrong number or type of arguments for overloaded function 'BondFunctions_zSpread'.
    ql.Date 传浮点TypeErrorWrong number or type of arguments for overloaded function 'new_Date'.
    到期日传字符串TypeErrorin method 'new_EuropeanExercise', argument 1 of type 'Date const &'
    向曲线索要过去RuntimeErrornegative time (-4.03825) given
    索取超出末端的期限RuntimeErrortime (5.075) is past max curve time (1.02222)
    已到期期权(静默)不报错NPV = 0.0,isExpired = True
    FAQ

    常见问题

    为什么程序不报错,但价格是 0?

    本机实测有四种成因:期权已到期(isExpired 为 True)、到期日等于估值日、债券结算日在到期日之后(此时 isExpired 仍是 False,不要只依赖它)、以及债券已到期(到期日早于估值日)。判据是先打印 isExpired()、settlementDate()、maturityDate() 三项。报错文本与行为随版本可能变化,以你本机输出与官方文档为准。

    BondFunctions.zSpread 为什么调用不了?

    本机逐项试过 handle、裸曲线对象、可重链接 handle、带结算日、带计息约定、以及传 (净价, 全价) 元组六种写法,10 个重载全部返回同一个 TypeError——参数类型 BondPrice 对应 C++ 的 std::pair<Real,Real>,Python 绑定里没有可用构造。替代做法是自己写求解:对利差迭代,直到按「曲线 + 利差」折现的价格等于目标价格。该结论只对 1.43 + Python 3.11 有效,换版本请重新验证。

    报错里说 vega not provided,是我的参数写错了吗?

    不是参数问题,是引擎能力边界。本机实测:树引擎(BinomialVanillaEngine)与蒙特卡洛引擎不提供 vega,AnalyticHestonEngine 不提供 delta。替代方案有两条:换成提供解析希腊字母的引擎,或者自己用有限差分算——本机实测差值 delta 8.6e-09、gamma 2.9e-10、vega 3.1e-09,对多数用途已经足够。具体引擎能力以官方文档中的引擎说明为准。

    曲线报 past max curve time,只能加报价吗?

    两条路:补齐报价节点,或者显式调用 enableExtrapolation()。本机实测的另一种情形是报价倒挂(6M 5% → 1Y 1%),曲线在构造阶段不报错,但实际只建到最近的那个节点,于是取值时才失败——所以构造完成后应立刻做自洽性检查(用曲线反算输入报价),而不是等到定价时才看结果。外推属于模型假设,用之前要写明依据。

    把估值日设成 2199-12-31 为什么报 serial number 越界?

    本机实测:ql.Date.maxDate() 是 December 31st, 2199,但把它设为 evaluationDate 后,内部还要叠加结算天数,于是越界报 Date's serial number (109575) outside allowed range [367-109574], i.e. [January 1st, 1901-December 31st, 2199]。日期类型的边界不等于可用的估值日,实际使用请留在合理区间。

    能换一个版本绕过这些报错吗?

    能换版本,但要自己验证而不是假设。本页所有结论都限定在 QuantLib 1.43 / Python 3.11.9 / Windows:绑定缺口(如 zSpread 不可调用)、类是否存在(如无 BinomialHestonEngine)、符号约定(basisPointValue 返回负值)都可能随版本变化。换版本后建议重跑本页的复现脚本,把新的输出作为你自己环境的事实记录;具体版本行为以官方发布说明与仓库 Issues 为准。

    排查这些问题需要额外装什么工具吗?

    不需要。本页的复现脚本只依赖 QuantLib 本身(本机由 pip install QuantLib 装到 1.43),不涉及行情数据、不涉及实盘接口。如果你更想用自然语言提问的方式做量化研究(例如查一只股票的指标),那属于另一条路线:EasyClaw 的本机技能可以免部署发起这类提问式研究,但 QuantLib 与 EasyClaw 无已证实集成,两者是独立的工具,本页只讨论 QuantLib 侧的报错与判据。

    本页的报错能不能直接当成「官方 bug 列表」?

    不能。本页记录的是本站实测行为,其中一部分是使用方式问题(如未设引擎、引擎与行权方式不匹配),一部分是版本相关的绑定限制(如 zSpread 不可调用),还有一部分是设计选择(如 basisPointValue 的符号)。要判断是否为缺陷,请到官方仓库检索同类报告并以其答复为准。

    本页结论QuantLib 的问题分两类:会抛异常的(13 类原文已在速查表)和不会抛异常的(10 类静默陷阱)。后者更危险——价格为 0、折现因子大于 1、delta 恒为 1 都不报错。遇到可疑数值时,先打印约定流程,再用第二种方法复算。
    适用边界全部报错文本与返回值来自 QuantLib 1.43 / Python 3.11.9 / Windows(platform `Windows-10-10.0.26200-SP0`)本机运行;不同版本、平台与安装方式下消息与行为可能不同,以你本机输出与官方文档为准。绑定缺口结论只对该版本成立。
    风险提示本页只讨论计算与工程问题,不涉及任何标的估值结论,不构成投资建议。按报错速查表修复后仍应重新校验数值合理性,不要因为「不报错」就默认结果可用。

    排查完报错,回到约定与安装

    如果报错解决了但价格仍与对手方对不上,去金融约定审计逐项核对口径;如果是环境层问题(导入失败、版本不符),回到安装页按验收清单确认。