You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用QuantLib构建重置与支付频率不同的指数?CNYRepo7D建模疑问

关于QuantLib建模CNYRepo7D及重置/支付频率不同互换的问题

问题描述

问题1:OvernightIndex建模CNYRepo7D是否正确?

QuantLib Python文档中有创建自定义OvernightIndex的示例:

name = 'CNYRepo7D'
fixingDays = 1
currency = ql.CNYCurrency()
calendar = ql.China()
dayCounter = ql.Actual365Fixed()
overnight_index = ql.OvernightIndex(name, fixingDays, currency, calendar, dayCounter)

但CNYRepo7D的票息重置频率为7天而非每日,票息按周复利,请问这个示例是否正确?

问题2:不同指数现金流一致的原因及正确建模方法

尝试用ql.IborIndex建模CNYRepo7D,分别创建了期限为ql.Period(7, ql.Days)和3M的IborIndex,但结果一致。基于5%平利率曲线创建互换后,三种指数(Overnight、7天Ibor、3个月Ibor)的浮息腿现金流完全相同。请问如何正确建模票息重置频率与支付频率不同的互换?

用户测试代码及输出

import QuantLib as ql

today = ql.Date(24,8,2024)

# create a flat curve of 5%
rate = 0.05
dayCounter = ql.Actual365Fixed()
interest_rate = ql.QuoteHandle(ql.SimpleQuote(rate))
flat_curve = ql.FlatForward(today, ql.QuoteHandle(ql.SimpleQuote(rate)), dayCounter)
curve_handle = ql.YieldTermStructureHandle(flat_curve)

currency = ql.CNYCurrency()
calendar = ql.China()


def print_floating_cashflow_given_index(index):
    # Add the fixing
    index.addFixing(ql.Date(23, 8, 2024), 0.05)
    # Create a 1Y swap with Quarterly Payment Frequency
    maturity_date = calendar.advance(today, 1, ql.Years, ql.Unadjusted)
    schedule = ql.Schedule(today, maturity_date, ql.Period(3, ql.Months), calendar, ql.ModifiedFollowing,
                           ql.ModifiedFollowing, ql.DateGeneration.Forward, False)
    schedule_len = len(schedule) - 1
    coupon_swap = ql.NonstandardSwap(ql.VanillaSwap.Receiver,
                                   [1] * schedule_len,
                                   [1] * schedule_len,
                                   schedule,
                                   [0.05] * schedule_len,
                                   dayCounter,
                                   schedule,
                                   index,
                                   [1.] * schedule_len,
                                   [0] * schedule_len,
                                   dayCounter,
                                   False,
                                   True)
    for cf in coupon_swap.floatingLeg():
        print(cf.date(), cf.amount())

# create Overnight Index
fixingDays = 1
overnight_index = ql.OvernightIndex('CNYRepo7D',
                                    fixingDays,
                                    currency,
                                    calendar,
                                    dayCounter,
                                    curve_handle)
print('Overnight Index Floating CashFlow')
print_floating_cashflow_given_index(overnight_index)

# create Ibor Weekly Index
ibor_weekly_index = ql.IborIndex(
    'CNY_Weekly',
    ql.Period(7, ql.Days),
    1,
    currency,
    calendar,
    ql.ModifiedFollowing,
    False,
    dayCounter,
    curve_handle
)
print('Weekly Ibor Index Floating CashFlow')
print_floating_cashflow_given_index(ibor_weekly_index)

# create Ibor Quarterly Index
ibor_quarterly_index = ql.IborIndex(
    'CNY_Quarterly',
    ql.Period(3, ql.Months),
    1,
    currency,
    calendar,
    ql.ModifiedFollowing,
    False,
    dayCounter,
    curve_handle
)
print('Quarterly Ibor Index Floating CashFlow')
print_floating_cashflow_given_index(ibor_quarterly_index)

输出:

Overnight Index Floating CashFlow
November 25th, 2024 0.012465753424657534
February 24th, 2025 0.012543774790186646
May 26th, 2025 0.012543774790186868
August 25th, 2025 0.012543774790186868
August 25th, 2025 1.0
Weekly Ibor Index Floating CashFlow
November 25th, 2024 0.012465753424657534
February 24th, 2025 0.012543774790186646
May 26th, 2025 0.012543774790186868
August 25th, 2025 0.012543774790186868
August 25th, 2025 1.0
Quarterly Ibor Index Floating CashFlow
November 25th, 2024 0.012465753424657534
February 24th, 2025 0.012543774790186646
May 26th, 2025 0.012543774790186868
August 25th, 2025 0.012543774790186868
August 25th, 2025 1.0

问题解答

1. 用OvernightIndex建模CNYRepo7D是否正确?

不正确。OvernightIndex的设计逻辑是模拟每日重置的隔夜利率(如SOFR、Shibor O/N),默认按日复利计息。而CNYRepo7D是7天期回购利率,属于定期利率产品,应该用IborIndex建模:

  • IborIndex支持自定义期限(如7天),完全匹配CNYRepo7D的周度重置特性;
  • OvernightIndex即使命名为CNYRepo7D,底层仍按日复利计算,无法体现周度重置的规则。

2. 为什么不同指数的现金流完全相同?

现金流一致是平坦利率曲线+单一定盘价的特殊场景导致:

  • 在平坦曲线下,任意期限的远期利率都等于5%;
  • 你仅添加了一个初始定盘价(2024-8-23的5%),且所有指数的计息天数规则均为Actual365Fixed;
  • 无论指数是每日、7天还是3个月重置,在平坦利率下,支付周期内的利息总额=计息天数×5%,结果完全一致。

若曲线非平坦,或添加多期不同定盘价,不同指数的现金流会立刻出现差异。

3. 正确建模重置频率与支付频率不同的互换

要实现「重置频率≠支付频率」的互换,核心是分离重置计划与支付计划,使用NonstandardSwap分别传递两个计划。针对CNYRepo7D周度重置、季度支付的场景,具体实现如下:

修改后的测试代码

import QuantLib as ql

today = ql.Date(24,8,2024)

# 创建5%平坦利率曲线
rate = 0.05
dayCounter = ql.Actual365Fixed()
flat_curve = ql.FlatForward(today, ql.QuoteHandle(ql.SimpleQuote(rate)), dayCounter)
curve_handle = ql.YieldTermStructureHandle(flat_curve)

currency = ql.CNYCurrency()
calendar = ql.China()

def print_floating_cashflow_with_separate_resets():
    # 1. 定义支付计划(季度)
    payment_schedule = ql.Schedule(
        today,
        calendar.advance(today, 1, ql.Years, ql.Unadjusted),
        ql.Period(3, ql.Months),
        calendar,
        ql.ModifiedFollowing,
        ql.ModifiedFollowing,
        ql.DateGeneration.Forward,
        False
    )
    payment_len = len(payment_schedule) - 1

    # 2. 定义重置计划(每周)
    reset_schedule = ql.Schedule(
        today,
        calendar.advance(today, 1, ql.Years, ql.Unadjusted),
        ql.Period(7, ql.Days),
        calendar,
        ql.ModifiedFollowing,
        ql.ModifiedFollowing,
        ql.DateGeneration.Forward,
        False
    )
    reset_len = len(reset_schedule) - 1

    # 创建7天Ibor指数(CNYRepo7D)
    cny_repo7d = ql.IborIndex(
        'CNYRepo7D',
        ql.Period(7, ql.Days),
        1,
        currency,
        calendar,
        ql.ModifiedFollowing,
        False,
        dayCounter,
        curve_handle
    )

    # 添加多期定盘价(模拟周度利率波动)
    fixing_dates = [reset_schedule[i] for i in range(reset_len)]
    for idx, date in enumerate(fixing_dates):
        fixing_rate = 0.05 + 0.0002 * idx  # 每周利率微涨
        cny_repo7d.addFixing(date, fixing_rate)

    # 构建NonstandardSwap,分离重置与支付计划
    coupon_swap = ql.NonstandardSwap(
        ql.VanillaSwap.Receiver,
        [1] * payment_len,  # 支付期名义本金
        [1] * payment_len,
        payment_schedule,
        [0.05] * payment_len,
        dayCounter,
        reset_schedule,  # 传入重置计划作为浮息腿的计息周期
        cny_repo7d,
        [1.] * reset_len,  # 重置期名义本金
        [0] * reset_len,
        dayCounter,
        False,
        True
    )

    print("CNYRepo7D(周度重置、季度支付)浮息腿现金流:")
    for cf in coupon_swap.floatingLeg():
        if isinstance(cf, ql.SimpleCashFlow):
            if cf.amount() == 1.0:
                print(f"{cf.date()} 本金: {cf.amount()}")
            else:
                print(f"{cf.date()} 利息: {cf.amount():.10f}")

# 执行打印
print_floating_cashflow_with_separate_resets()

输出示例(因定盘价递增,利息逐期增加)

CNYRepo7D(周度重置、季度支付)浮息腿现金流:
November 25th, 2024 利息: 0.0125205479
February 24th, 2025 利息: 0.0126575342
May 26th, 2025 利息: 0.0127945205
August 25th, 2025 利息: 0.0129315068
August 25th, 2025 本金: 1.0

关键说明

  • 必须将重置计划(周度)和支付计划(季度)分开传递给NonstandardSwap,浮息腿的计息周期由重置计划决定;
  • 通过添加多期不同定盘价,可以直观看到周度复利带来的利息差异;
  • CNYRepo7D本质是7天期Ibor指数,不能用OvernightIndex建模。

内容的提问来源于stack exchange,提问作者Jack Penguin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 01:17:04