写代码搞懂债券:从定价到收益率曲线,一个函数就够了

当我开始投资的前五年,手里一根债券都没有买。理由很简单:股票平均年化收益9%,债券只有3%,凭什么把钱投到更差的资产上?当时跟我一样想法的开发者不少——2010年后入场的人基本都配了100%的股票。2022年债券崩盘,长期国债跌了30%,股票也跟着跌,好像“债券没用”的结论被证明了。

这个推理的问题不在于数学,而在于思维方式。债券不是股票的“低收益替代品”。它们是不同的工具,有不同的收益驱动因素、不同的风险特性、不同的组合角色。理解这些差异——以及背后的债券数学——才是区分“被动应对市场波动”和“提前预判市场变化”的关键。下面是我希望自己一开始就懂的债券逻辑。

债券就是带价格的借条

债券就是一份合同:你把钱借给某个主体(美国国债、公司、地方政府),对方在固定期限内付你利息,到期时归还本金。就这么简单。复杂的地方在于债券可以二级市场交易,所以它的价格会在发行日和到期日之间随利率变化而波动。

这个关系是机械的、确定的,不像股票收益那样随机。假如你买了一只10年期国债,票面利率4%,后来市场利率升到5%,你手里债券的市场价值就会下跌——因为新发的债券利息更高。你的债券价格必须跌到一定程度,使它的实际收益率和新市场利率持平。这不是市场“恐慌”,而是固定现金流在更高贴现率下的数学结果。

对于开发者来说,更容易理解的模型是:债券是一个函数,输入贴现率,输出现值。贴现率一变,现值就确定性地变化:

def bond_price(face_value, coupon_rate, years, yield_to_maturity, payments_per_year=2):
    # 计算债券价格:面值、票面利率、年限、到期收益率、每年付息次数
    periods = int(years * payments_per_year)  # 总付息期数
    coupon = face_value * coupon_rate / payments_per_year  # 每期付息金额
    ytm_period = yield_to_maturity / payments_per_year  # 每期收益率

    # 所有利息的现值之和
    pv_coupons = sum([coupon / (1 + ytm_period) ** t for t in range(1, periods + 1)])
    # 到期本金的现值
    pv_face = face_value / (1 + ytm_period) ** periods

    return pv_coupons + pv_face

# 面值1000美元,票面利率4%,10年期,到期收益率5%
price = bond_price(1000, 0.04, 10, 0.05)
print(f"债券价格: ${price:.2f}")  # 输出约922美元

这10行代码就是固定收益定价的核心。试着改改 yield_to_maturity 参数,看看价格怎么变——10年期债券收益率上升1%,价格大约跌7~8%。这个敏感度就是下面要讲的概念:久期(Duration)。

久期:一个数字解释一切

久期衡量债券价格对利率变化的敏感度。修正久期告诉你:收益率平行上移1%,债券价格大概会跌百分之多少。如果一个债券修正久期是7,那么利率升1%,价格跌约7%;利率降1%,价格涨约7%。

计算方式如下:

def modified_duration(face_value, coupon_rate, years, yield_to_maturity):
    epsilon = 0.0001  # 微小变动,即1个基点
    price_up = bond_price(face_value, coupon_rate, years, yield_to_maturity + epsilon)
    price_down = bond_price(face_value, coupon_rate, years, yield_to_maturity - epsilon)
    price_current = bond_price(face_value, coupon_rate, years, yield_to_maturity)
    return (price_down - price_up) / (2 * epsilon * price_current)

dur = modified_duration(1000, 0.04, 10, 0.05)
print(f"修正久期: {dur:.2f}")

组合配置时需要记住的核心直觉:期限越长 → 久期越高 → 对利率越敏感。一只30年期国债的久期风险大约是10年期的三倍。如果你预计利率会上升,就缩短久期;如果预计利率会下降,就拉长久期。这是固定收益投资中最重要的战术决策,和信用质量决策(国债 vs 公司债 vs 垃圾债)是完全独立的。

警告

久期是线性近似。对于大幅度利率变动(比如2%以上),久期会低估价格变化,因为存在凸性(Convexity)。正常债券具有正凸性,当利率上升时,实际跌幅小于久期预测;当利率下降时,实际涨幅大于久期预测。久期在小幅变动时很准;做大幅利率冲击的情景分析时,直接用完整 bond_price 函数,别用久期近似。

收益率曲线:一个状态机

收益率曲线——把不同期限(1个月到30年)的美国国债收益率画成图——是债券市场情绪最好的总结。正常情况下,期限越长收益率越高:10年期比2年期高,因为你承担了更多利率风险。当曲线倒挂(2年期收益率 > 10年期收益率),债券市场就在定价“衰退”。

你可以从美国财政部公开API拉取收益率曲线数据:

import requests
import xml.etree.ElementTree as ET

url = ("https://home.treasury.gov/resource-center/"
       "data-chart-center/interest-rates/pages/"
       "xml?data=daily_treasury_yield_curve&field_tdr_date_value=2026")
resp = requests.get(url)
root = ET.fromstring(resp.content)

# 遍历每个条目,提取期限和收益率
for entry in root.findall('.//{http://www.w3.org/2005/Atom}entry'):
    # 具体解析依赖响应结构,这里留作练习
    pass

更简单的办法是用FRED API(另有单独指南),它能拿到干净的时间序列:DGS2(2年期收益率)、DGS10(10年期收益率)、DGS30(30年期收益率)。计算利差就是 DGS10 - DGS2——负值表示倒挂。1970年以来美国每次衰退之前都出现过收益率曲线倒挂,通常提前12~18个月。这不是市场瞎预测,而是债券市场重新定价未来增长预期的机械结果。

TIPS:带细微差别的通胀保护

通胀保值国债(TIPS)的本金会根据CPI调整。如果通胀率3%,你的TIPS本金就涨3%,利息(按调整后的本金计算)也同比增加。听起来像白送的保护——有政府背书又防通胀。

但注意:TIPS的名义收益率比同期限的普通国债低。两者之差就是“盈亏平衡通胀率”——在这个通胀率下,两种债券收益相同。如果实际通胀高于盈亏平衡率,TIPS赚;如果低于,普通国债赚。

2026年中,10年期盈亏平衡率大约2.3%。这意味着市场预期未来十年平均通胀约2.3%。如果你认为通胀会高于这个数字,TIPS便宜;如果你认为美联储能把通胀压到2%以下,普通国债更好。这种判断把“该不该买债券”变成了一个有分析框架的问题。

债券ETF vs 单只债券

开发者投资者通常喜欢ETF,因为它像股票一样交易,不需要懂债券知识。像 AGGBND 这样的债券ETF持有几百只债券,并保持大致恒定的久期。这很方便,但有一个重要区别:单只债券有确定的到期日,到期时你拿回本金。债券ETF永远不会到期——它会不断卖出临近到期的债券、买入新的债券来维持目标久期。

后果:如果你买了一只5年期国债,票面利率4%,无论期间利率怎么变,5年后你都能拿回本金。但如果你买了一只平均久期5年的债券ETF,在第4年遇到利率飙升,它的价格会暴跌,而且没有确定的价格回升日期。ETF的久期一直固定,而你自己的单只债券久期会随着到期日临近逐渐下降。

实际建议:如果你有已知的未来支出(5年后的学费、3年后的首付),就买与日期匹配的单只债券。如果你管理的是退休组合,没有固定取款时间,用低成本债券ETF更简单、分散化也足够。

类似文章