Building a CLP Swap Curve for QuantLib Valuation
Summary
The discussion addresses why a Chilean peso swap valuation built from flat discount and projection curves may differ substantially from market valuations. The response recommends constructing a curve from market quotes for the relevant CLP overnight indexed swaps, using rate helpers and a piecewise discount curve, then linking that curve to the overnight index and discounting engine. The example also highlights conventions such as spot-date settlement, day count, calendar, payment frequency, and the change from zero coupon payments at shorter maturities to semiannual payments at longer maturities.
To illustrate calibration, the response prices swaps corresponding to the input curve instruments and compares their fair rates with the quotes, which are close in the example. This is a calibration check, not evidence that the setup will match every market valuation. The quoted curve is a snapshot and the answer notes limited familiarity with CLP conventions; the sample also concerns OIS instruments rather than a complete specification of every CLP-TNA swap. Accurate results require appropriate market data and instrument conventions.
Key ideas
- Flat curves are generally inadequate for market-comparable swap valuation across maturities.
- Build the discount curve from relevant market quotes using rate helpers and a piecewise term structure.
- Match settlement, index, day-count, calendar, and payment-frequency conventions to the traded instruments.
- Reprice curve instruments and compare fair rates with their input quotes as a calibration check.
- The example is a starting point and does not fully specify every convention for CLP-TNA swaps.
Tags
Full text
# How to compute NPV of Latin American swap CLP-TNA (chilean) using quantlib?
# How to compute NPV of Latin American swap CLP-TNA (chilean) using quantlib?
I am trying to value the Latin Americans swaps. But CLP-TNA valuation is far off from the actual valuation. Please suggest, what I am missing in below methodology to compute NPV.
```
# construct discount curve and libor curve
risk_free_rate = 0.01
libor_rate = 0.02
day_count = ql.Actual360()
discount_curve = ql.YieldTermStructureHandle(
ql.FlatForward(calculation_date, risk_free_rate, day_count)
)
libor_curve = ql.YieldTermStructureHandle(
ql.FlatForward(calculation_date, libor_rate, day_count)
)
# CLP index
CLP_index = ql.OvernightIndex('CLP', 0, ql.CLPCurrency(), ql.WeekendsOnly(), ql.Actual360())
calendar = ql.WeekendsOnly()
settle_date = calendar.advance(calculation_date, 5, ql.Days)
maturity_date = calendar.advance(settle_date, 10, ql.Years)
fixed_schedule = ql.Schedule(settle_date, maturity_date,
fixed_leg_tenor, calendar,
ql.ModifiedFollowing, ql.ModifiedFollowing,
ql.DateGeneration.Forward, False)
float_schedule = ql.Schedule (settle_date, maturity_date,
float_leg_tenor, calendar,
ql.ModifiedFollowing, ql.ModifiedFollowing,
ql.DateGeneration.Forward, False)
notional = 10000000
fixed_rate = 0.025
fixed_leg_daycount = ql.Actual360()
float_spread = 0.004
float_leg_daycount = ql.Actual360()
ir_swap = ql.VanillaSwap(ql.VanillaSwap.Payer, notional, fixed_schedule,
fixed_rate, fixed_leg_daycount, float_schedule,
libor3M_index, float_spread, float_leg_daycount )
swap_engine = ql.DiscountingSwapEngine(discount_curve)
ir_swap.setPricingEngine(swap_engine)
```
## Answer by David Duarte (score 2, accepted)
https://quant.stackexchange.com/a/65709
Here is an example that might point you in the right direction. As Luigi said the comments, you can't really expect to arrive at comparable values if you are just using flat curves.
So the first step would be to build a curve comparable to Bloomberg. I don't really have any experience with CLP buy looking at the info on BBG it looks like these swap are zero coupon until 18M and pay semi after that.
```
import QuantLib as ql
today = ql.Date(25,6,2021)
calendar = ql.WeekendsOnly()
ql.Settings.instance().evaluationDate = today
spot = calendar.advance(today, 2, ql.Days)
dayCount = ql.Actual360()
cop_ois_quotes = [
('3M', 0.780),
('6M', 1.140),
('9M', 1.435),
('12M', 1.770),
('18M', 2.145),
('2Y', 2.430),
('3Y', 2.745),
('4Y', 3.01),
('5Y', 3.28),
('6Y', 3.53),
('7Y', 3.715),
('8Y', 3.835),
('9Y', 3.93),
('10Y', 4.055),
('15Y', 4.365),
('20Y', 4.465),
]
helpers = []
clp_ois_yts = ql.RelinkableYieldTermStructureHandle()
index = ql.OvernightIndex('CLICP', 0, ql.CLPCurrency(), ql.WeekendsOnly(), dayCount, clp_ois_yts)
for tenor, value in cop_ois_quotes:
value /= 100
quote = ql.QuoteHandle(ql.SimpleQuote(value))
period = ql.Period(tenor)
paymentFrequency = ql.Semiannual if period.units() > 2 else ql.Once
helper = ql.OISRateHelper(2, period, quote, index, paymentFrequency=paymentFrequency)
helpers.append(helper)
clp_ois_crv = ql.PiecewiseLogLinearDiscount(spot, helpers, ql.ActualActual())
clp_ois_crv.enableExtrapolation()
clp_ois_yts.linkTo(clp_ois_crv)
```
You can then test your curve by pricing some swaps. Here I'm pricing the input instruments so it is expected to yield the same results but you can play around with other dates.
```
swapType = ql.OvernightIndexedSwap.Payer
nominal = 100
engine = ql.DiscountingSwapEngine(clp_ois_yts)
for tenor, value in cop_ois_quotes:
value /= 100
maturity = calendar.advance(spot, ql.Period(tenor))
freq = '18M' if ql.Period(tenor).units() == 2 else '6M'
schedule = ql.MakeSchedule(spot, maturity, ql.Period(freq), calendar=calendar)
ois_swap = ql.OvernightIndexedSwap(swapType, nominal, schedule, 0.0, dayCount, index)
ois_swap.setPricingEngine(engine)
print(f"{tenor}: {ois_swap.fairRate()*100:.4f}, {value*100:.4f}")
```
3M: 0.7800, 0.7800 6M: 1.1400, 1.1400 9M: 1.4350, 1.4350 12M: 1.7700, 1.7700 18M: 2.1450, 2.1450 2Y: 2.4300, 2.4300 3Y: 2.7479, 2.7450 4Y: 3.0100, 3.0100 5Y: 3.2800, 3.2800 6Y: 3.5300, 3.5300 7Y: 3.7150, 3.7150 8Y: 3.8350, 3.8350 9Y: 3.9312, 3.9300 10Y: 4.0550, 4.0550 15Y: 4.3650, 4.3650 20Y: 4.4651, 4.4650Shown 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.