浮息债折现利差

浮息债折现利差 浮动利率债券的折现利差
根据浮息债价格、报价利差、利率信息(RateInfo)、结算日与到期日,计算浮动利率债券的折现利差。RateInfo 由 StubRate(首个 stub 期利率)与 SpotRate(参考利率)组成。可选参数:每年付款频率 Reset、计息天数规则 Basis、最新浮动利率 LatestFloatingRate、是否调整现金流天数 AdjustCashFlowsBasis。
格式 yyyy-mm-dd
格式 yyyy-mm-dd
计算结果
点击「计算」查看输出
code 计算逻辑代码
以下为「ISDA国际通用」浮息债折现利差的 Python 代码示例(基于 QuantLib)。

# 依赖版本:
# python==3.9
# QuantLib==1.43
# scipy(用于 brentq)
import traceback
from datetime import datetime

import QuantLib as ql
from scipy.optimize import brentq


def qjquant_calu_floatdiscmargin(
    price: float,
    spread: float,
    settle: str,
    maturity: str,
    stub_rate: float,
    spot_rate: float,
    latest_floating_rate: float = 0.0,
    reset: int = 1,
    basis: int = 0,
    adjust_cash_flows_basis: int = 1,
):
    try:
        # ---------- 参数校验 ----------
        if price <= 0:
            return {"ok": False, "msg": "债券价格必须大于 0"}
        if spread < 0:
            return {"ok": False, "msg": "报价利差不能为负"}
        if stub_rate < 0 or spot_rate < 0:
            return {"ok": False, "msg": "利率不能为负"}

        # ---------- 日期解析 ----------
        try:
            d1 = datetime.strptime(settle, "%Y-%m-%d")
            d2 = datetime.strptime(maturity, "%Y-%m-%d")
        except ValueError:
            return {"ok": False, "msg": "日期格式必须为 YYYY-MM-DD"}

        settle_date = ql.Date(d1.day, d1.month, d1.year)
        maturity_date = ql.Date(d2.day, d2.month, d2.year)
        if maturity_date <= settle_date:
            return {"ok": False, "msg": "到期日必须晚于结算日"}

        ql.Settings.instance().evaluationDate = settle_date

        # ---------- 日历与计息天数 ----------
        calendar = ql.NullCalendar()
        day_counter_map = {
            0: ql.ActualActual(ql.ActualActual.Bond),
            1: ql.Thirty360(ql.Thirty360.BondBasis),
            2: ql.Actual360(),
            3: ql.Actual365Fixed(),
            4: ql.Thirty360(ql.Thirty360.USA),
            5: ql.Thirty360(ql.Thirty360.ISDA),
            6: ql.Thirty360(ql.Thirty360.European),
            7: ql.Actual365Fixed(),
            8: ql.ActualActual(ql.ActualActual.ISMA),
            9: ql.Actual360(),
            10: ql.Actual365Fixed(),
            11: ql.Thirty360(ql.Thirty360.European),
            12: ql.Actual365Fixed(),
            13: ql.Actual365Fixed(),
        }
        day_counter = day_counter_map.get(basis, ql.ActualActual(ql.ActualActual.Bond))

        # ---------- 付息频率 ----------
        freq_map = {1: ql.Annual, 2: ql.Semiannual, 4: ql.Quarterly, 12: ql.Monthly}
        frequency = freq_map.get(reset, ql.Annual)
        tenor = ql.Period(frequency)

        # ---------- 构建 Schedule ----------
        schedule = ql.Schedule(
            settle_date, maturity_date, tenor, calendar,
            ql.Unadjusted, ql.Unadjusted,
            ql.DateGeneration.Backward, False
        )

        # ---------- 预测曲线与贴现曲线 ----------
        forecast_curve = ql.YieldTermStructureHandle(
            ql.FlatForward(settle_date, spot_rate, day_counter)
        )

        # ---------- 构建 IborIndex ----------
        index = ql.IborIndex(
            "MockIbor", tenor, 0, ql.USDCurrency(), calendar,
            ql.Unadjusted, False, day_counter, forecast_curve
        )

        # 只为首个 accrual start 添加一次 fixing,避免重复
        first_accrual_start = schedule[0]
        fixing_value = stub_rate if stub_rate > 0 else latest_floating_rate
        index.addFixing(first_accrual_start, fixing_value)

        # ---------- 构建浮息债 ----------
        quoted_spread = spread / 10000.0
        bond = ql.FloatingRateBond(
            0,                    # settlementDays
            100.0,                # faceAmount
            schedule,             # schedule
            index,                # index
            day_counter,          # paymentDayCounter(位置参数,兼容版本差异)
            fixingDays=0,
            gearings=[1.0],
            spreads=[quoted_spread],
            caps=[],
            floors=[],
            inArrears=False,
            redemption=100.0,
            issueDate=settle_date,
        )

        # ---------- 折现利差求解 ----------
        spread_simple_quote = ql.SimpleQuote(0.0)
        spread_quote_handle = ql.QuoteHandle(spread_simple_quote)

        spreaded_curve = ql.ZeroSpreadedTermStructure(
            ql.YieldTermStructureHandle(
                ql.FlatForward(settle_date, spot_rate, day_counter)
            ),
            spread_quote_handle
        )
        discount_handle = ql.YieldTermStructureHandle(spreaded_curve)
        bond.setPricingEngine(ql.DiscountingBondEngine(discount_handle))

        def price_diff(dm):
            spread_simple_quote.setValue(dm)
            return bond.cleanPrice() - price

        try:
            discount_margin = brentq(price_diff, -0.5, 1.0, xtol=1e-10, maxiter=200)
        except ValueError:
            return {"ok": False, "msg": "无法在给定范围内求解折现利差,请检查输入参数"}

        margin_bp = discount_margin * 10000.0

        return {
            "ok": True,
            "margin": round(margin_bp, 4),
        }

    except Exception as e:
        traceback.print_exc()
        return {"ok": False, "msg": f"未知错误: {e}"}


# ============================================================
# 示例:复制本文件后直接运行
# ============================================================
if __name__ == "__main__":
    print("=" * 60)
    print("示例 1:浮息债折现利差(年度,实际/实际)")
    print("=" * 60)
    r1 = qjquant_calu_floatdiscmargin(
        price=99.99,
        spread=50,
        settle="2024-03-15",
        maturity="2029-03-15",
        stub_rate=0.049,
        spot_rate=0.05,
        latest_floating_rate=0.0,
        reset=1,
        basis=0,
    )
    print(r1)

    print()
    print("=" * 60)
    print("示例 2:浮息债折现利差(季度,30/360 USA)")
    print("=" * 60)
    r2 = qjquant_calu_floatdiscmargin(
        price=98.50,
        spread=75,
        settle="2024-03-15",
        maturity="2029-06-20",
        stub_rate=0.048,
        spot_rate=0.05,
        reset=4,
        basis=1,
    )
    print(r2)

    print()
    print("=" * 60)
    print("示例 3:浮息债折现利差(半年,实际/365)")
    print("=" * 60)
    r3 = qjquant_calu_floatdiscmargin(
        price=100.25,
        spread=30,
        settle="2024-06-01",
        maturity="2027-06-01",
        stub_rate=0.052,
        spot_rate=0.05,
        reset=2,
        basis=3,
    )
    print(r3)

    print()
    print("=" * 60)
    print("示例 4:浮息债折现利差(短期限,实际/360)")
    print("=" * 60)
    r4 = qjquant_calu_floatdiscmargin(
        price=99.80,
        spread=120,
        settle="2024-03-15",
        maturity="2025-03-15",
        stub_rate=0.05,
        spot_rate=0.05,
        reset=1,
        basis=2,
    )
    print(r4)