到期付息贴现债券应计利息

到期付息贴现债券应计利息 · ISDA(国际通用) 给定结算日、到期日等计算应计利息
根据债券结算日、到期日、面值、贴现率等计算到期时支付的贴现证券的应计利息。
格式 yyyy-mm-dd
格式 yyyy-mm-dd
计算结果
点击「计算」查看输出
code 计算逻辑代码
以下为「ISDA国际通用」QuantLib 代码示例。

# 依赖版本:
# python==3.9
# QuantLib==1.43
from datetime import datetime
import traceback

import QuantLib as ql


# ============================================================
# 工具函数
# ============================================================
def _parse_date(s: str) -> ql.Date:
    """解析 'yyyy-mm-dd' 字符串为 ql.Date"""
    d = datetime.strptime(s.strip(), "%Y-%m-%d")
    return ql.Date(d.day, d.month, d.year)


def _map_basis(basis: int):
    """日计数基准编号 -> ql.DayCounter"""
    mapping = {
        0:  ql.ActualActual(ql.ActualActual.ISDA),
        1:  ql.Thirty360(ql.Thirty360.USA),
        2:  ql.Actual360(),
        3:  ql.Actual365Fixed(),
        4:  ql.Thirty360(ql.Thirty360.BondBasis),
        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.EurobondBasis),
        12: ql.Actual365Fixed(),
        13: ql.Business252(),
    }
    if basis not in mapping:
        raise ValueError(f"暂不支持的 Basis: {basis}")
    return mapping[basis]


# ============================================================
# 核心计算函数
# ============================================================
def qjquant_calu_acrudisc(
    settle_date: str,
    maturity_date: str,
    face: float,
    discount: float,
    period: int = 2,
    basis: int = 0,
):
    try:
        def _period_to_months(p: int):
            """acrudisc 专用:避开 ql.Period(Frequency),只用 (Integer, TimeUnit)。"""
            m = {1: 12, 2: 6, 3: 4, 4: 3, 6: 2, 12: 1}
            if p not in m:
                raise ValueError(f"不支持的 Period: {p}")
            return m[p]

        if face <= 0:
            return {"ok": False, "msg": "面值必须大于 0"}
        if discount < 0:
            return {"ok": False, "msg": "贴现率不能为负"}

        settle_qd   = _parse_date(settle_date)
        maturity_qd = _parse_date(maturity_date)

        if settle_qd >= maturity_qd:
            return {"ok": False, "msg": "结算日必须早于到期日"}

        day_counter = _map_basis(basis)

        # ---- 核心计算 ----
        if period == 0:
            # 零息:无周期,按年化比例线性分摊
            if basis in (0, 8):
                yf = ql.ActualActual(ql.ActualActual.ISDA).yearFraction(settle_qd, maturity_qd)
            else:
                yf = day_counter.yearFraction(settle_qd, maturity_qd)
            accrued = face * discount * yf
        else:
            months = _period_to_months(period)
            coupon_period = ql.Period(months, ql.Months)   # ★ (Integer, TimeUnit)
            prev_coupon_qd = maturity_qd - coupon_period
            period_days = day_counter.dayCount(prev_coupon_qd, maturity_qd)
            settle_days = day_counter.dayCount(settle_qd, maturity_qd)
            if period_days <= 0:
                return {"ok": False, "msg": "付息周期天数异常"}
            year_fraction = settle_days / period_days
            accrued = face * discount * year_fraction / period

        if accrued < 0:
            accrued = 0.0

        return {
            "ok": True,
            "accru_interest": round(accrued, 4),
        }

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


# ============================================================
# 示例:复制本文件后直接运行
# ============================================================
if __name__ == "__main__":
    print("=" * 60)
    print("示例 1:半年期贴现债(period=2,实际/实际)")
    print("=" * 60)
    r1 = qjquant_calu_acrudisc(
        settle_date="2024-05-15",
        maturity_date="2024-11-15",
        face=100.0,
        discount=0.05,
        period=2,
        basis=0,
    )
    print(r1)

    print()
    print("=" * 60)
    print("示例 2:一年期贴现债(period=1,30/360 USA)")
    print("=" * 60)
    r2 = qjquant_calu_acrudisc(
        settle_date="2024-06-15",
        maturity_date="2025-06-15",
        face=100.0,
        discount=0.04,
        period=1,
        basis=1,
    )
    print(r2)

    print()
    print("=" * 60)
    print("示例 3:季付口径贴现债(period=4,实际/365 Fixed)")
    print("=" * 60)
    r3 = qjquant_calu_acrudisc(
        settle_date="2024-02-15",
        maturity_date="2024-05-15",
        face=100.0,
        discount=0.03,
        period=4,
        basis=3,
    )
    print(r3)

    print()
    print("=" * 60)
    print("示例 4:零息口径(period=0,按年化比例线性分摊)")
    print("=" * 60)
    r4 = qjquant_calu_acrudisc(
        settle_date="2024-03-15",
        maturity_date="2025-03-15",
        face=100.0,
        discount=0.04,
        period=0,
        basis=0,
    )
    print(r4)