|
From: Marcin R. <mry...@gm...> - 2024-03-18 10:59:26
|
Hi Aleksis, Thank you for your thorough analysis. You do have a point here and there seems to be a bug in the source code which implies that the curve settlement date should be the same as the instrument settlement date, in order to price the instrument back to par. I created a PR that should fix this: https://github.com/lballabio/QuantLib/pull/1930 Kind regards, Marcin On Sat, 2 Mar 2024 at 19:00, Aleksis Ali Raza <ale...@go...> wrote: > Thanks for reply Marcin. > > Agreed, but unfortunately the only way I can get the xccy swaps to price > to par is by having a curve settlement set to 2 and PVing the cashflows to > the sett’t date. I’ve spent some on this but can’t find a work around. > Below is a toy example illustrating this problem (a copy/paste will run > it). The only way I get a zero npv for the xccy swap is by having a curve > sett of T+2 in all curves (euribor3M_curve, usdlibor3M_curve and > eur_usd_curve). > > import QuantLib as ql > > today = ql.Date(27, ql.October, 2021) > ql.Settings.instance().evaluationDate = today > > calendar = ql.UnitedStates(ql.UnitedStates.FederalReserve) > euribor3M_curve = ql.YieldTermStructureHandle(ql.FlatForward(0,calendar,0.06,ql.Actual360())) > euribor3M = ql.IborIndex('c', ql.Period('3m'), 2, ql.Currency(), ql.TARGET(), > ql.ModifiedFollowing, False, ql.Actual365Fixed(),euribor3M_curve) > usdlibor3M_curve = ql.YieldTermStructureHandle(ql.FlatForward(0,calendar,0.02,ql.Actual365Fixed())) > usdlibor3M = ql.IborIndex('c', ql.Period('3m'), 2, ql.Currency(), calendar, > ql.ModifiedFollowing, False, ql.Actual360(),usdlibor3M_curve) > notional = 1_000_000 > fx_0 = 0.85 > start_date = calendar.advance(today, ql.Period(2, ql.Days)) > end_date = calendar.advance(start_date, ql.Period(3, ql.Months)) > tenor = ql.Period(3, ql.Months) > rule = ql.DateGeneration.Forward > convention = ql.Following > end_of_month = False > schedule = ql.Schedule(start_date,end_date,tenor,calendar,convention,convention,rule,end_of_month) > usd_leg = ((ql.SimpleCashFlow(-notional, schedule[0]),)+ ql.IborLeg(nominals=[notional],schedule=schedule,index=usdlibor3M,) > + (ql.SimpleCashFlow(notional, schedule[-1]),)) > usd_npv = ql.CashFlows.npv(usd_leg, usdlibor3M_curve, True,start_date) > print(usd_npv) > quote = -0.03 > ccbs_quotes = {'3M': quote} > day_counter = euribor3M_curve.dayCounter() > ccbs_helpers = [ql.ConstNotionalCrossCurrencyBasisSwapRateHelper( > ql.QuoteHandle(ql.SimpleQuote(value)), > ql.Period(period), > 2, > calendar, > convention, > end_of_month, > usdlibor3M, > euribor3M, > usdlibor3M_curve, > True, > False) > for period, value in ccbs_quotes.items()] > > eur_usd_curve = ql.YieldTermStructureHandle(ql.PiecewiseLogLinearDiscount(0, calendar, ccbs_helpers, day_counter)) > eur_leg = ((ql.SimpleCashFlow(-notional * fx_0, schedule[0]),)+ ql.IborLeg(nominals=[notional * fx_0],schedule=schedule,index=euribor3M,spreads=[quote]) > + (ql.SimpleCashFlow(notional * fx_0, schedule[-1]),)) > eur_npv = ql.CashFlows.npv(eur_leg,eur_usd_curve, True,start_date) > print(eur_npv) > eur_leg_euribor = ((ql.SimpleCashFlow(-notional * fx_0, schedule[0]),)+ ql.IborLeg(nominals=[notional * fx_0],schedule=schedule,index=euribor3M,spreads=[0]) > + (ql.SimpleCashFlow(notional * fx_0, schedule[-1]),)) > eur_npv_euribor = ql.CashFlows.npv(eur_leg_euribor,euribor3M_curve, True,start_date) > print(eur_npv_euribor) > > print(ccbs_helpers[0].impliedQuote()) > > > On Mar 2, 2024, at 9:33 AM, Marcin Rybacki <mry...@gm...> wrote: > > Hi Aleksis, > > The curve origin (curve settlement date) does not necessarily need to > correspond to the settlement convention of the instruments used to > construct the curve. > So, you can safely set it to T+0 and both FX swaps and cross currency > swaps, that you use to build the term structure, should price back to par. > > I hope this answers your question. > > Kind regards, > Marcin > > On Wed, 28 Feb 2024 at 14:05, Aleksis Ali Raza via QuantLib-users < > qua...@li...> wrote: > >> Perhaps this is a naive question but when bootstrapping a cross currency >> curve using FXSwapHelper for short end and >> ConstNotionalCrossCurrencyBasisSwapHelper for the long end, I’m running >> into an issue where I need to specify the *curve settlement date* >> (curve_sett in the code below ) as T+2*. *This is to reflect correct >> valuation of the cross currency swaps when priced off the curve. However, >> for FXSwapHelpers curve_sett should be T+0 - this is so that ON and TN FX >> swaps can be used in the helpers and repriced correctly. The code used is >> below. >> >> How does one get around this issue (without having to work with two >> separate curves, or without leaving out ON and TN from the FXSwapHelpers)? >> >> Thanks, Aleksis >> >> # FX swaps >> fxSwapHelpers = [ql.FxSwapRateHelper(ql.QuoteHandle(q), >> ql.QuoteHandle(FX), >> m, >> int(d), >> calendar, >> business_convention_base, >> end_of_month, >> True, >> collateral_curve) >> for d, q, m in zip(dffxswapsFXspotlag, fxswap_quotes, dffxswapsmaturity)] >> >> # xccy basis curve >> swap_helpers_xccybasis = [ql.ConstNotionalCrossCurrencyBasisSwapRateHelper(ql.QuoteHandle(q), >> m, >> int(d), >> calendar, >> business_convention_quote, >> end_of_month, >> index_base, >> index_quote, >> collateral_curve, >> True, >> False) >> for q, m, d in zip(xccybasisquotes, xccybasismaturies, dfxccybasisFXspotlag)] >> >> rate_helpers_xccybasis = fxSwapHelpers + swap_helpers_xccybasis >> curve_basis = curve_interpolation(curve_sett, calendar, rate_helpers_xccybasis, curve_daycount) >> curve_basis.enableExtrapolation() >> final_curve_basis = ql.RelinkableYieldTermStructureHandle(curve_basis) >> >> >> _______________________________________________ >> QuantLib-users mailing list >> Qua...@li... >> https://lists.sourceforge.net/lists/listinfo/quantlib-users >> > > |