Skip to content
All library documents

Troubleshooting Chilean CLP Swap Curve Bootstrapping

Article Quant Q&A · Author: Jose Pedro Melo

Summary

The document describes a QuantLib setup for constructing a Chilean peso overnight swap curve from short rate quotes and longer dated swap quotes. It defines a Chilean holiday calendar, assigns deposit and overnight indexed swap helpers, builds a piecewise cubic zero curve, and compares the resulting discount factors and zero rates with Bloomberg output. The displayed comparison shows a growing difference at longer maturities, reaching roughly ten basis points near the long end.

The author asks which conventions could explain the discrepancy after experimenting with helper types and year fractions. The document supplies the inputs and outputs but contains no answer or diagnosis, so it does not establish which market convention is responsible. It is useful as a curve construction and validation case: calendar holidays, settlement dates, instrument conventions, compounding, and quote mapping all warrant checking when implementations disagree. The comparison is specific to the stated CLP setup and dates; it does not demonstrate that either curve is correct.

Key ideas

  • The curve combines short deposit quotes with longer overnight indexed swap quotes.
  • A custom local holiday calendar affects instrument dates and curve pillars.
  • QuantLib and Bloomberg outputs can diverge at longer maturities even with similar quoted inputs.
  • Curve validation should check market conventions and quote-to-helper mappings, but the document leaves the discrepancy unresolved.

Tags

Full text
# Bootstraping CLP Swap with Quantlib


# Bootstraping CLP Swap with Quantlib












After reviewing and fixing my last code (Swap Bootstrapping with quantlib), i managed to get the zero rates for the ICP swap curve (CLP). Now the thing is that there is a considerable difference between the rates shown in the Bloomberg swap bootstrapping an QL's results. Here is the updated code:

```
import QuantLib as ql
import pandas as pd

def create_calendar_chile(start_year,n_years):
    Chile = ql.WeekendsOnly()
    days = [1,14,15,1,21,26,2,16,15,18,19,9,27,1,19,8,17,25,31]
    months = [1,4,4,5,5,6,8,9,9,10,10,11,12,12,12,12]
    name = ['Año Nuevo','Viernes Santo','Sabado Santo','Dia del Trabajo','Dia de las Glorias Navales','San Pedro y San Pablo','Elecciones Primarias','Dia de la Virgen del Carmen','Asuncion de la Virgen','Independencia Nacional','Glorias del Ejercito','Encuentro de dos mundos','Día de las Iglesias Evangélicas y Protestantes','Día de todos los Santos','Elecciones Presidenciales y Parlamentarias','Inmaculada Concepción','Segunda vuelta Presidenciales','Navidad','Feriado Bancario']
    for i in range(n_years+1):
        for x,y in zip(days,months):
            date = ql.Date(x,y,start_year+i)
            Chile.addHoliday(date)
    return Chile
def get_curve(date,swap,currency = 'CLP'):
    calendar = create_calendar_chile(2001,50)
    dayCounter_Act360 = ql.Actual360()
    settlement_days_icp = 2
    # OIS quotes up to 20 years
    ICP = ql.OvernightIndex("ICP", settlement_days_icp, ql.CLPCurrency(),
    calendar, dayCounter_Act360)
    fixingDays = 0

    if currency == 'CLP':
        # setup DepositRateHelper for 0-1 days
        TPM = swap[0]
        months = [3,6,9,12,18]
        swap_month = swap[1:6]
        years =[2,3,4,5,6,7,8,9,10,15,20]
        swap_years = swap[6:]
        helpers = [ql.DepositRateHelper(ql.QuoteHandle(ql.SimpleQuote(TPM/100)),
                                         ql.Period(1,ql.Days), fixingDays,
                                         calendar, ql.Following, False, ql.Actual360())]
    else:
        months = [3,6,9,12]
        swap_month = swap[0:3]
        years =[2,3,4,5,6,7,8,9,10,15,20]
        swap_years = swap[4:]
        helpers = []

    # setup OISRateHelper from 3 months to 20 years
    helpers += [ql.DepositRateHelper(ql.QuoteHandle(ql.SimpleQuote(rate/100)),
                ql.Period(months,ql.Months),
                settlement_days_icp,
                calendar,
                ql.Following,
                False,
                ql.Actual360())
                for rate, months in zip(swap_month,months)]

    #helpers += [ql.OISRateHelper(settlement_days_icp, ql.Period(months,ql.Months),
    #                                 ql.QuoteHandle(ql.SimpleQuote(rate/100)),ICP)
    #                                 for rate, months in zip(swap_month,months)]

    helpers += [ql.OISRateHelper(settlement_days_icp, ql.Period(years,ql.Years),
                                     ql.QuoteHandle(ql.SimpleQuote(rate/100)),ICP)
                                     for rate, years in zip(swap_years,years)]

    icp_curve = ql.PiecewiseCubicZero(date, helpers, ql.Actual360())
    icp_curve.enableExtrapolation()
    return icp_curve
def print_zero(date,yieldcurve):
    day_count = ql.Actual360()
    spots = []
    dates = []
    tenors = []
    df = []
    for d in yieldcurve.dates():
        yrs = day_count.yearFraction(date, d)
        df.append(yieldcurve.discount(d))
        dates.append(d)
        compounding = ql.Simple
        freq = ql.Annual
        zero_rate = yieldcurve.zeroRate(yrs, compounding, freq)
        tenors.append(yrs)
        eq_rate = zero_rate.equivalentRate(day_count,compounding,freq,date,d).rate()
        zero_rate.equivalentRate(day_count,compounding,freq,date,d).rate()
        spots.append(100*eq_rate)

    datatable = {'Dates':dates,'Years': tenors,'DF':df,'Zero': spots}
    datatable = pd.DataFrame.from_dict(datatable)
    print(datatable)

#Eval. Date
date_ql = ql.Date(10,1,2018)
ql.Settings.instance().evaluationDate = date_ql
swap_clp = [2.5, 2.48, 2.47, 2.48, 2.53, 2.64, 2.82, 3.17, 3.43, 3.62, 3.81,
3.96, 4.09, 4.19, 4.29, 4.45, 4.62]
yieldcurve_clp = get_curve(date_ql,swap_clp)
print_zero(date_ql,yieldcurve_clp)
```

Results:

```
          DF               Dates      Years      Zero
0   1.000000  January 10th, 2018   0.000000  0.000000
1   0.999931  January 11th, 2018   0.002778  2.500000
2   0.993700    April 12th, 2018   0.255556  2.480773
3   0.987597     July 12th, 2018   0.508333  2.470668
4   0.981404  October 12th, 2018   0.763889  2.480488
5   0.974721  January 14th, 2019   1.025000  2.530187
6   0.961368     July 12th, 2019   1.522222  2.639855
7   0.944897  January 13th, 2020   2.036111  2.864088
8   0.908864  January 12th, 2021   3.050000  3.287675
9   0.871107  January 12th, 2022   4.063889  3.640960
10  0.833387  January 12th, 2023   5.077778  3.937202
11  0.793979  January 12th, 2024   6.091667  4.259577
12  0.755379  January 13th, 2025   7.111111  4.553990
13  0.717674  January 12th, 2026   8.122222  4.843389
14  0.681810  January 12th, 2027   9.136111  5.108135
15  0.646116  January 12th, 2028  10.150000  5.396163
16  0.506455  January 12th, 2033  15.225000  6.400718
17  0.386192  January 12th, 2038  20.297222  7.830561
```

BBG:

```
Start   End Days    Start Disc.Factor   Frequency   Zero
10-01-2018  11-01-2018  1   1,000069    ZERO    2,500
12-01-2018  12-04-2018  90  0,993838    ZERO    2,480
12-01-2018  12-07-2018  181 0,987734    ZERO    2,470
12-01-2018  12-10-2018  273 0,981540    ZERO    2,480
12-01-2018  14-01-2019  367 0,974857    ZERO    2,530
12-01-2018  12-07-2019  546 0,961501    ZERO    2,640
12-01-2018  13-01-2020  731 0,944621    SEMIANNUAL  2,887
12-01-2018  12-01-2021  1096    0,908255    SEMIANNUAL  3,318
12-01-2018  12-01-2022  1461    0,870133    SEMIANNUAL  3,678
12-01-2018  12-01-2023  1826    0,832049    SEMIANNUAL  3,980
12-01-2018  12-01-2024  2191    0,792244    SEMIANNUAL  4,309
12-01-2018  13-01-2025  2558    0,753263    SEMIANNUAL  4,610
12-01-2018  12-01-2026  2922    0,715195    SEMIANNUAL  4,906
12-01-2018  12-01-2027  3287    0,679005    SEMIANNUAL  5,178
12-01-2018  12-01-2028  3652    0,642988    SEMIANNUAL  5,473
12-01-2018  12-01-2033  5479    0,502562    SEMIANNUAL  6,504
12-01-2018  12-01-2038  7305    0,382045    SEMIANNUAL  7,971
```

There is a +10 bp difference in the long term part. I tried using the swap, deposit and ois rate helpers, but minimal changes occurred. Also hoped that changing the year fractions would have an impact but it didn't change significaly. Is there anything i could be missing?

Thanks,

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.