QuantLib · Instruments

QuantLib 债券与利率工具:从现金流到净价、久期与 DV01

债券定价不是一行 NPV():发行日、到期日、付息频率、日历、调整规则、计息约定、结算天数、折现曲线,每一项都会改变结果。本页把一只虚拟债券的 11 条真实现金流、净价/全价/应计、收益率、久期、凸性、DV01 全部跑出来,并给出把利率掉期定价跑起来时逐步暴露的三个真实报错。

实测环境:QuantLib 1.43 / Python 3.11.9本页实测:11 条现金流 + 8 项风险指标证据等级:本站实测
1 工具FixedRateBond / VanillaSwap
2 现金流Schedule + DayCounter
3 折现曲线或单一收益率
4 指标价格 / 久期 / DV01

工具定价链:先把工具拆成现金流,再决定用曲线还是用单一收益率折现,最后才产出价格与风险指标。本页所有数字都来自这条链的本机实测。

测试版本QuantLib 1.43(PyPI wheel,cp39-abi3)Python 版本3.11.9 / Windows最后核验2026-10-09(现金流、价格、久期均为本机实测输出)估值日与结算估值日 2024-01-15;结算日 2024-01-17(T+2)日历与计息TARGET + Actual/Actual (ISDA);年付折现曲线平曲线 3%(Actual/Actual ISDA)可直接复现是(现金流表、价格、久期、掉期链均可按本页代码复算)本站实测范围债券与利率掉期定价链;CDS 与可转债只核对类名,未实测价格
实验环境QuantLib 1.43 / Python 3.11.9 / Windows
本页实测11 条现金流 + 8 项指标 + 掉期报错链
核心结论净价 + 应计 = 全价;DV01 报负号
证据等级本站实测(脚本与输出均在本页)
Inventory

QuantLib 有哪些金融工具?能定价到什么程度

下表按官方类名整理,并如实标注本站的核验程度:只有债券与利率掉期链在本机跑出过价格,其余工具只核对了类名存在性。

工具清单与核验边界(类名依据官方文档与本机 dir(QuantLib) 核对)
工具类别QuantLib 类本站核验程度适用场景注意点
固定利率债券FixedRateBond已实测定价(本页全部指标)国债、政金债、企业债的现金流与风险计量必须自己给定日历、计息约定与结算天数
零息债券ZeroCouponBond已实测构造(未做风险指标)贴现债、短期融资券到期一次还本,应计逻辑与附息债不同
浮动利率债券FloatingRateBond只核对类名挂钩基准利率的浮息债需要基准指数与预测曲线,否则定价会抛缺失定盘价
利率掉期VanillaSwap已实测定价(含三个真实报错)互换报价与曲线自洽性检验需要指数定盘价 + 预测曲线,见本页实测报错链
互换期权Swaption + JamshidianSwaptionEngine已实测定价(利率模型页)利率波动率交易的定价与风险结果依赖模型(Hull-White 参数),不是无模型价格
信用违约互换CreditDefaultSwap只核对类名,本站未实测价格信用风险定价与保护成本计量需要违约概率曲线与回收率假设,缺一不可
可转债ConvertibleFixedCouponBond / ConvertibleZeroCouponBond只核对类名,本站未实测价格转债的股债混合定价需要股价过程与转股条款,模型选择影响大
上限/下限、回望等CapFloor 等只核对类名利率上下限类结构引擎选择与波动率曲面口径必须写清
边界说明:QuantLib 提供的是计算框架,不是中国市场的数据与估值规则。本页只对债券与掉期给出实测价格;CDS、可转债等工具需要外部假设(违约概率、回收率、股价过程),本站没有实测数据,因此不写价格、只给类名与官方入口,避免让你误以为「有类名就等于能算准」。
Bond chain

债券定价六步:每一步都有可核对的产物

下面六步是本站实际执行的顺序。每步都给出真实代码与预期产物,最后一步的数值应当与本页表格一致。

  • 1. 固定估值日

    先设 ql.Settings.instance().evaluationDate。本页用 2024-01-15;不设的话,价格会随运行当天变化,无法对账。
  • 2. 建日历与 Schedule

    ql.Schedule(发行日, 到期日, ql.Period(ql.Annual), cal, ql.ModifiedFollowing, ql.ModifiedFollowing, ql.DateGeneration.Forward, False);实测显示本页债券的付息日会出现 1/15、1/16、1/17 三种落点,就是调整规则在起作用。
  • 3. 建债券并指定应计计息约定

    ql.FixedRateBond(2, 100.0, sched, [0.03], ql.ActualActual(ql.ActualActual.ISDA))。第一个参数是结算天数(T+2),它会同时影响应计利息与折现起点。
  • 4. 指定价引擎

    两条路:收益率的 ql.BondFunctions.*,或曲线的 ql.DiscountingBondEngine(...)。本页实测中,不设引擎直接调用 bond.cleanPrice() 会抛 RuntimeError: null pricing engine。
  • 5. 取价格与应计

    实测输出:净价 99.75408467、全价 99.77047812、应计 0.01639344,且净价 + 应计 = 全价(差为 0)。
  • 6. 算风险指标

    由净价反解收益率 3.0454534%,再用 ql.BondFunctions.duration / convexity / basisPointValue 得修正久期、凸性与 BPV;注意 basisPointValue 返回负值,见下页说明。
  • bond_chain.py(本页数值的生成脚本,QuantLib 1.43 实测通过)import QuantLib as ql today = ql.Date(15, 1, 2024) ql.Settings.instance().evaluationDate = today cal = ql.TARGET() dc = ql.ActualActual(ql.ActualActual.ISDA) sched = ql.Schedule(ql.Date(15, 1, 2020), ql.Date(15, 1, 2030), ql.Period(ql.Annual), cal, ql.ModifiedFollowing, ql.ModifiedFollowing, ql.DateGeneration.Forward, False) bond = ql.FixedRateBond(2, 100.0, sched, [0.03], dc) # 结算天数 = 2 bond.setPricingEngine(ql.DiscountingBondEngine( ql.YieldTermStructureHandle(ql.FlatForward(today, 0.03, dc)))) for cf in bond.cashflows(): print(cf.date(), cf.amount()) # 11 条现金流(见下表) print(bond.cleanPrice(), bond.accruedAmount(), bond.dirtyPrice()) y = bond.bondYield(dc, ql.Compounded, ql.Annual) print(y, ql.BondFunctions.duration(bond, y, dc, ql.Compounded, ql.Annual, ql.Duration.Modified), ql.BondFunctions.convexity(bond, y, dc, ql.Compounded, ql.Annual))
    Cashflows

    11 条真实现金流:为什么每期票息金额不一样

    这是本页最容易忽略、也最能说明问题的一张表:同一只 3% 票息的债券,各期票息金额并不相同。原因不是数据错,而是 Actual/Actual (ISDA) 在每个付息期间给出的年化分数不同;同时可以看到营业日调整把 1/15 推到了 1/16、1/17。

    实测:全 11 条现金流(债券:票息 3%、2020-01-15 发行、2030-01-15 到期、年付、TARGET、A/A ISDA)
    付息日(实测)金额(每 100 面值)与名义 3.0 的差该期发生了什么
    2021-01-153.00031439+0.00031439首期,2020 为闰年,年化分数略大于 1
    2022-01-173.01643836+0.016438361/15 是周六,顺延到 1/17;区间含闰日
    2023-01-162.99178082−0.008219181/15 是周日,顺延到 1/16
    2024-01-152.99146643−0.00853357区间跨越 2023/2024 两个年度
    2025-01-153.00031439+0.00031439回到正常落点
    2026-01-153.000000000.00000000整年,分数恰好为 1
    2027-01-153.000000000.00000000整年
    2028-01-173.01607905+0.016079051/15 顺延;区间含 2028 闰日
    2029-01-152.98392095−0.01607905与上一期互补
    2030-01-153.000000000.00000000末期票息
    2030-01-15100.00000000—本金偿还(同一日两笔现金流)
    读法:票息金额的波动区间是 2.9839 ~ 3.0164,相对名义值上下浮动约 0.55%。如果你的模型假设「每期票息都是面值 × 票息率」,在长周期债券上会累积出可见误差;本页把这一条单独列表,是因为它同时解释了「现金流日期看起来对不上」和「票息金额为什么没有规律」两种常见困惑。
    Metrics

    八项指标一次看全:价格、收益率、久期、凸性、DV01

    同一只债券在 2024-01-17 结算日的实测指标。把它们放在一起看,才能发现「同一天、同一只债」在不同折现口径下会给出不同的久期。

    实测:结算日 2024-01-17、结算天数 T+2、平曲线 3%
    指标实测值计算口径注意点
    净价 clean price99.75408467现金流折现后扣掉应计报价习惯用净价,但不是结算金额
    全价 dirty price99.77047812净价 + 应计,等于结算金额口径与折现的现值口径一致
    应计利息0.01639344上一付息日至结算日按 A/A ISDA 计息T+0 时为 0;随结算日线性增长
    由净价反解收益率3.0454534%年化复利、年付(ql.Compounded, ql.Annual)同一价格换成半年复利口径会得到不同数字
    修正久期5.40899919在债券自身收益率(3.0455%)处计算久期是收益率与计息约定的函数,不是常数
    麦考利久期5.57372774同一收益率处计算与修正久期差一个 (1+y/f) 因子
    凸性35.81120973在债券自身收益率处计算换成 3.00% 收益率计算得 35.84789852,不是同一个数
    BPV(库函数返回值)−0.05396566ql.BondFunctions.basisPointValue 直接返回本机实测带负号,市场口径应取绝对值
    DV01(市场口径,每 100 面值)0.05396585(P(y−1bp) − P(y+1bp)) / 2收益率 +1bp → 净价 99.70013669;−1bp → 99.80806839
    久期与凸性为什么「换个收益率就变了」(实测对照)y_own = 0.03045453395351691 # 由净价反解出的收益率 y_flat = 0.03 # 曲线水平(3.00%) ql.BondFunctions.duration(bond, y_own, dc, ql.Compounded, ql.Annual, ql.Duration.Modified) # -> 5.40899919 ql.BondFunctions.convexity(bond, y_own, dc, ql.Compounded, ql.Annual) # -> 35.81120973 ql.BondFunctions.duration(bond, y_flat, dc, ql.Compounded, ql.Annual, ql.Duration.Modified) # -> 5.41197919 (与上面不同!) ql.BondFunctions.convexity(bond, y_flat, dc, ql.Compounded, ql.Annual) # -> 35.84789852 (与上面不同!)
    要点:价格是「算出来」的,久期和凸性是「在一个指定收益率处算出来」的。本机实测同一只债券在 3.0455% 与 3.0000% 两个收益率处的修正久期分别是 5.40899919 与 5.41197919。如果你的久期和对方差在第三位小数,先问「你是在哪个收益率上算的」,而不是怀疑公式。
    Rate source

    折现用曲线还是用单一收益率?两者给出不同的价格体系

    债券有两条定价路径:一条用单一收益率(BondFunctions 系列),一条用曲线折现(DiscountingBondEngine)。下表是同一只债券在不同曲线下的实测结果——最后一行说明了「为什么单一收益率复现不了曲线价」。

    实测:同一只债券,只替换折现口径(估值日 2024-01-15、结算 T+2)
    折现方式净价全价由净价反解的单一收益率DV01(每 100 面值)说明
    平曲线 2%105.48014237105.496535812.020134%0.05777747曲线水平低于票息,债券溢价
    平曲线 3%99.7540846799.770478123.0454534%0.05396585基准情形
    平曲线 4%94.3518961194.368289554.08107742%0.05040818折价;收益率高于票息
    递增零息曲线(2.65% → 7.15%,31 个节点)97.6912447597.707638193.43261594%0.05260284曲线有斜率:反解出的单一收益率无法复现同一价格
    结论:曲线定价与收益率定价是两个不同的价格体系,不能混用结论。本例中递增曲线给出的净价是 97.69124475,而由这条曲线「反解」出的收益率只有 3.43261594%——把这个收益率代回平曲线公式,得不到 97.69124475。所以做对账时,先确认双方用的是同一类折现口径,再比较数字。曲线构造与 bootstrap 细节见期限结构页。
    Swap

    利率掉期定价:三个真实报错,一条可通行的路径

    把 5 年期 Payer 掉期(固定 4.5% 年付 30/360 对 USD-6M 浮动)在本机跑通的过程里,QuantLib 依次抛出三个报错。它们不是 bug,而是掉期定价必须补齐的三件输入。

    实测:掉期定价的三个输入门槛(报错原文逐字来自本机运行)
    步骤缺少什么实测报错原文修复动作
    A指数定盘价与预测曲线都没有RuntimeError: 2nd leg: Missing USD-6M6M Actual/360 fixing for January 11th, 2024为指数补上历史定盘价(浮动腿的起点要能确定)
    B有定盘价,但指数没有预测曲线RuntimeError: 2nd leg: null term structure set to this instance of USD-6M6M Actual/360给指数传入预测曲线句柄,浮动腿才能外推未来定盘价
    C两者齐备不再报错,可得到价格设置折现引擎后取 NPV / fairRate / BPS 等量
    实测:5Y Payer 掉期、名义 100、固定腿年付 30/360、浮动腿 USD-6M Act/360、平曲线 3%
    输出量Payer 固定 4.5%Payer 固定 3.0%说明
    NPV−5.36573658(100 名义)+1.49489970固定利率高于均衡利率时为负
    fairRate3.3268428%3.3268428%与固定利率无关,由曲线和合约结构决定
    fixed leg NPV−20.58190885−13.72127256固定腿现值随固定利率变化
    floating leg NPV+15.21617226+15.21617226浮动腿不受固定利率影响
    fixed leg BPS−0.04573758−0.04573758固定腿 1bp 价值
    floating leg BPS+0.04642366+0.04642366两腿 BPS 不相等,差额来自计息约定差异
    为什么 fairRate 是 3.3268% 而不是曲线水平 3.00%?因为两腿的计息约定不同(固定腿年付 30/360、浮动腿 6M Act/360),均衡固定利率不会等于曲线的某个单一数字。本站实测的可核对部分是「fairRate 37 个基点高于曲线、两腿 BPS 反向不相等」这一现象本身;把它拆成逐项贡献需要额外的实验,本站没有做,因此不写推测的分解结论。
    China market

    中国债券口径:能用什么、必须自己补什么

    中国市场相关能力必须分开看:日历可以直接用,计息口径要自己选,估值规则与数据源要自己补。下表是本机实测的边界。

    实测:中国市场相关能力的边界(价格用平曲线 3%、T+1 结算)
    环节QuantLib 1.43 提供什么本站实测结果你必须自己补什么
    交易日历ql.China(ql.China.SSE) / ql.China(ql.China.IB)名字分别为 Shanghai stock exchange / China inter bank market;2024 年非营业日 SSE 124 天、IB 115 天与当年实际放假安排核对(调休、临时休市)
    计息约定ActualActual 枚举:ISDA / Bond / Historical / AFB / Euro / Actual365 / ISMA枚举里没有 China 口径按发行文件选定(国债常用 A/A 或 A/365,须逐只确认)
    债券现金流Schedule + Calendar + DayCounter 可完整搭建CGB 口径债券(2.75% 半年付、SSE、A/A ISDA、T+1)实测净价 98.51481374、应计 0.00751366、收益率 3.02261292%、修正久期 5.48254592付息频率、闰年规则、净价/全价口径按发行文件核对
    银行间 vs 交易所两套日历(IB / SSE)2024 年非营业日相差 9 天(115 vs 124)按你的结算场所选日历
    行情与中债估值不提供行情、不提供中债估值规则本站未测试中债数据导入(无数据授权)数据下载口径、字段映射、日期与计息字段需自行解析并留痕
    日历是否影响价格会(通过现金流日期)本组 1/15、7/15 付息日在 SSE 与 TARGET 下都是营业日,实测价格完全相同(差为 0)结论只对本组日期成立:假期边界(如 10/1 起算)必须单独验证
    诚实标注:本页中国案例使用虚构的 2.75% 票息与固定 3% 平曲线,用于演示口径与计算链路,不代表任何真实债券的估值。中债数据导入、银行间结算细节、可转债转股条款等本站没有数据授权与实测,因此只写「要补什么」,不写具体操作步骤,以发行文件与市场规则为准。
    Troubleshoot

    工具建模出错的排查顺序(含真实报错原文)

    从本页实测的三个报错出发,总结出债券与掉期建模的排查顺序:先确认输入齐全,再确认口径一致,最后才查数值。

    排查顺序:输入齐全 → 口径一致 → 数值比较
    现象 / 报错根因下一步该查什么
    RuntimeError: null pricing engine调用了需要引擎的 cleanPrice() 却没设引擎改走 BondFunctions 收益率路径,或给债券 setPricingEngine
    AttributeError: module 'QuantLib' has no attribute 'bondYield'本机 1.43 没有顶层 ql.bondYield 函数改用 bond.bondYield(...) 或 ql.BondFunctions.bondYield(...)
    Missing USD-6M6M Actual/360 fixing ...浮动腿缺历史定盘价先补定盘价,再谈价格
    null term structure set to this instance of ...指数没有预测曲线句柄把曲线句柄传给指数
    净价与全价差值和应计对不上结算天数或应计计息约定不同打印 settlementDate 与 dayCounter().name() 比对
    久期/凸性与对方差在第三位计算所用的收益率不同确认双方用的是债券自身收益率还是曲线水平
    BPV 符号为负库函数按自身约定返回市场口径取 ×(−1) 或改用双侧 bump 定义
    掉期 fairRate 不等于曲线水平两腿计息约定不同核对固定腿频率/计息约定与浮动腿指数约定
    FAQ

    常见问题

    为什么我的 DV01 是负的?

    本机实测 ql.BondFunctions.basisPointValue 返回 −0.05396566,而市场口径的 DV01 是 (P(y−1bp) − P(y+1bp))/2 = 0.05396585。这不是债券方向,而是库函数的符号约定。建议统一用双侧 bump 自己定义 DV01,或在报告里注明所用口径。以官方实现为准。

    YTM 和曲线折现价为什么不同?

    两者是不同的价格体系:YTM 把整条现金流压成一个收益率,曲线折现按每个期限单独取折现因子。本站实测:递增零息曲线下净价 97.69124475,反解出的单一收益率为 3.43261594%,把它代回平曲线公式得不到同一价格。做对账时先统一口径,再比数字。以官方文档为准。

    中国国债能不能直接用 QuantLib 定价?

    框架可以,规则要自己补。实测:ql.China(ql.China.SSE) 与 ql.China(ql.China.IB) 可用(2024 年非营业日分别是 124 与 115 天),但 ActualActual 枚举里没有 China 计息口径,也没有中债估值规则与行情数据。本站的中国案例用的是虚构票息与示意曲线,真实债券必须按发行文件与市场规则逐只核对。以官方文档与你所在市场的规则为准。

    为什么每期票息金额不一样?

    因为计息约定按「实际天数 / 实际天数」计算,每个付息期间的年化分数不同。本站实测 11 条现金流中,票息金额在 2.98392095 到 3.01643836 之间波动,还有两期因为营业日调整把 1/15 推到了 1/16、1/17。这不是数据错误,而是 Schedule 与 DayCounter 的正常输出。以发行文件的计息条款为准。

    掉期为什么跑不起来?

    本机实测的报错链很明确:先是 Missing USD-6M6M Actual/360 fixing for January 11th, 2024(缺定盘价),补上后变成 null term structure set to this instance of USD-6M6M Actual/360(指数缺预测曲线),两者齐备才能定价。掉期不是「有类和曲线就能算」,浮动腿的两项输入都是必需的。详细报错目录见错误与排查页;QuantLib 与 EasyClaw 无已证实集成,这类定位需要你自己完成。

    为什么同一只债券在不同工具里久期不同?

    因为久期要在某个指定收益率处计算。本机实测:同一只债券在自身反解收益率(3.0454534%)处修正久期为 5.40899919,在曲线水平 3.00% 处为 5.41197919,凸性则是 35.81120973 与 35.84789852。对账时必须先对齐「用哪个收益率算的」。以官方文档为准。

    CDS 和可转债本页为什么没有数字?

    因为本站没有实测它们:CDS 需要违约概率曲线与回收率假设,可转债需要股价过程与转股条款,这些输入不在本页的实验范围内。写不出实测数字时,本页只给类名与核验状态,不给「看起来像结果」的示例。需要时请以官方示例与你自己市场的假设为准(QuantLib 与 EasyClaw 无已证实集成,本页不做集成暗示)。

    看完这页应该先去哪?

    如果要构造或校准曲线,去期限结构页;如果价格或指标对不上,先去金融约定审计页把日期与计息口径对齐;如果程序报错或结果为 0,直接去错误与排查页按报错原文定位。

    本页结论债券定价链的每一步都有可核对产物:11 条真实现金流(票息金额 2.9839 ~ 3.0164 波动)、净价 99.75408467 + 应计 0.01639344 = 全价 99.77047812、修正久期 5.40899919、凸性 35.81120973、市场口径 DV01 0.05396585;掉期定价必须补齐定盘价与预测曲线两件输入。
    适用边界全部数值来自虚构债券与 5Y 掉期、固定 3% 平曲线、估值日 2024-01-15、QuantLib 1.43 / Python 3.11.9;CDS 与可转债仅核对类名,未实测价格;跨 QuantLib 版本可能有差异。
    风险提示本页只演示计算链路与口径,不涉及任何真实标的的估值结论,不构成投资建议;把本页数字套到真实债券上会产生偏差。

    价格算出来了,接下来核什么?

    先用金融约定审计把日期与计息口径对齐,再回期限结构检查折现曲线的自洽性;如果结果是 0 或报错,直接进错误与排查按原文定位。