定期存单应计利息

定期存单应计利息 · ISDA(国际通用) 给定结算日、到期日等计算应计利息
根据存单息票率、结算日、到期日、签发日及日计数基准计算定期存单的应计利息。
格式 yyyy-mm-dd
格式 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_cdai(
    coupon_rate: float,
    settle_date: str,
    maturity_date: str,
    issue_date: str,
    basis: int = 2,          # 默认 2 = Actual/360
):
    try:
        # ---------- 参数校验 ----------
        if coupon_rate < 0:
            return {"ok": False, "msg": "息票率不能为负"}

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

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

        day_counter = _map_basis(basis)

        # ---------- 核心计算 ----------
        # 定期存单应计利息(CD Accrued Interest)
        # 公式:accrued = face * coupon_rate * yearFraction(issue, settle)
        # 注意:CD 通常按面值 100 计算应计利息,但这里按“每 100 面值”的利息返回。
        # 接口不传 face,因此以 100 为隐含面值,返回每 100 面值的应计利息。
        FACE = 100.0

        # 使用 QuantLib 的 yearFraction 计算从签发日到结算日的应计年化比例
        # 部分 basis(如 Actual/Actual ISMA)需要提供参考期,CD 通常用 Issue-Maturity 作为参考期
        if basis in (0, 8):
            # Actual/Actual (ISMA) 对 CD 使用签发日至到期日作为参考期
            yf = day_counter.yearFraction(issue_qd, settle_qd, issue_qd, maturity_qd)
        else:
            yf = day_counter.yearFraction(issue_qd, settle_qd)

        accrued = FACE * coupon_rate * yf

        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:实际/360(默认,最常用的 CD 口径)")
    print("=" * 60)
    r1 = qjquant_calu_cdai(
        coupon_rate=0.05,
        settle_date="2024-06-15",
        maturity_date="2024-12-15",
        issue_date="2024-01-15",
        basis=2,
    )
    print(r1)

    print()
    print("=" * 60)
    print("示例 2:实际/实际(ISDA)")
    print("=" * 60)
    r2 = qjquant_calu_cdai(
        coupon_rate=0.05,
        settle_date="2024-06-15",
        maturity_date="2024-12-15",
        issue_date="2024-01-15",
        basis=0,
    )
    print(r2)

    print()
    print("=" * 60)
    print("示例 3:30/360(SIA)")
    print("=" * 60)
    r3 = qjquant_calu_cdai(
        coupon_rate=0.05,
        settle_date="2024-06-15",
        maturity_date="2024-12-15",
        issue_date="2024-01-15",
        basis=1,
    )
    print(r3)

    print()
    print("=" * 60)
    print("示例 4:实际/365 Fixed")
    print("=" * 60)
    r4 = qjquant_calu_cdai(
        coupon_rate=0.05,
        settle_date="2024-06-15",
        maturity_date="2024-12-15",
        issue_date="2024-01-15",
        basis=3,
    )
    print(r4)

    print()
    print("=" * 60)
    print("示例 5:实际/实际(ICMA,带参考期)")
    print("=" * 60)
    r5 = qjquant_calu_cdai(
        coupon_rate=0.05,
        settle_date="2024-06-15",
        maturity_date="2024-12-15",
        issue_date="2024-01-15",
        basis=8,
    )
    print(r5)