|
From: <mat...@gm...> - 2020-12-18 08:36:21
|
Hi David, Thank you for your advice. I retried my old (Python) exercise adding a guess for the parameter vector (an optional argument whose existence I missed) and indeed it does help. Kind regards, Matthias From: da...@el... <da...@el...> Sent: Wednesday, 16 December 2020 23:00 To: mat...@gm... Cc: qua...@li... Subject: RE: [Quantlib-users] FittedBondCurve in R Hi Matthias & “Random User”, “That said, I also tried Nelson-Siegel and indeed the exponential splines, but both methods would give me the linear curve you got as well. So I quickly discarded them without much thinking about as to why this kind of “deformation” may have happened.” This same question was asked on Stack Overflow, so I’ll give pretty much the same reply here, which relates specifically to the exponential spline, and why it doesn’t seem to work in the current R wrapper for QuantLib in the low-rate world. See: QuantLib in R: Bond Setup - Stack Overflow <https://stackoverflow.com/questions/65006282/quantlib-in-r-bond-setup> (I’ll do my best with showing the equations in plain text). For the default QuantLib exponential spline fit (which is what you seem to get in R), the discount function is fitted to this form: df(t) = Coeff1.exp(-kappa.t) + Coeff2.exp(-2.kappa.t) + … CoeffN.exp(-N.kappa.t), where N is fixed at 9. So you have 9 Coeffs and 1 kappa as your fit parameters. In addition, the default is to constrain df(0) = 1 (ie PV of 1$ today is 1$ which seems reasonable). This imposes a constraint that: Sum(Coeffs) = 1 So you actually only have 8 independent Coeffs. You can think of kappa as the far-forward overnight rate, as when t gets large df(t) ~ Coeff1.exp(-kappa.t). When I first started using exponential splines for European government curves back in the ‘90s (which dates me terribly), rates were much higher than today, and kappa came out around 3%. Actually we didn’t vary kappa, left it fixed and just varied the coefficients (you find that the fitted curve is pretty insensitive to the choice of kappa within reason). The issue today is rates are close to zero. I don’t know the details of the QL optimization process, but if the routine tries a solution with kappa close (or equal to) zero then it hits a special case. Why? Well if kappa = 0, then the discount function reduces to: df(t) = Coeff1 + Coeff2 + …. Coeff9 But the sum of the Coeffs is constrained to 1. So df(t) = 1 for any variation of the 8 independent coefficients. As a consequence I think the optimization routine considers it has found a solution, since no matter how much it varies the 8 independent Coeffs, the fit doesn’t improve. You can only see this when using the C++ or Python libraries, as they let you inspect the values of the fit parameters. Indeed I went to the trouble of trying to fit the Treasury Curve using the default exponential spline fitting method (see the SO link above), and I got the same results as Matthias: an almost linear yield curve. When you look at the final fit parameters you see that kappa = 0.000. However, the C++/Python libraries allow the user to specify an initial guess for the fit parameters (as well as choosing how to weight each bond in the fit). If you supply a non-zero initial guess for kappa (say 1% or 0.01) then the fitting algorithm doesn’t get stuck and you get a decent fit to the Treasury curve (including bonds with the same maturity), complete with inflection points. Unfortunately the R implementation doesn’t give you access to this approach. Since I was trying to use the exponential spline via QuantLibXL, I dug quite deep into this issue. I’ve submitted a pull request on the QuantLib github which allows a C++ user to fix kappa in the exponential fit (which speeds up the fit), as well as specify how many Coeffs to use. At some point I guess this will find its way to the Python and R interfaces too. So, when it comes to the exponential fit via R, it is not a matter of incorrect day count issues, or bonds with the same maturity. It is that R is not giving access to the finer details of the fitting method, which are needed in this case. I hope this all sounds reasonable, David Sansom From: mat...@gm... <mailto:mat...@gm...> <mat...@gm... <mailto:mat...@gm...> > Sent: Wednesday, 16 December 2020 20:51 To: 'Random User' <qld...@gm... <mailto:qld...@gm...> >; qua...@li... <mailto:qua...@li...> Subject: Re: [Quantlib-users] FittedBondCurve in R Hello “Random User”, Your example made me try out the QuantLib R extension which I never used before. Put your code into a file if someone else wants to try – I believe the attachment may survive distribution in this list. Anyway this will not be the answer you hoped for, but let me share an anecdotal (attempt of) advice. Some while ago I tried to essentially do the same: Fit a curve based on government bonds. My dataset also included several bonds at the same tenor, so the first approach to simply pass the bonds to a function like this (Python code)… ql.PiecewiseLogCubicDiscount(settle_days, calendar, bond_helpers, day_count) …required to filter the dataset to avoid such “collisions”. With that, I got a somewhat bumpy curve with quite some oscillations on the short tenors. So, in order to smoothen that curve, I used the same class you also used, but chose cubic splines instead of exponential, because some paper suggested so. Do not remember which, but not the ones mentioned in the C++ documentation of the regarding class. Well, I did not read them, but I am pretty sure the one I had was way more recent. ql.FittedBondDiscountCurve(settle_days, calendar, bond_helpers, day_count, ql.CubicBSplinesFitting(knots, True), tolerance, iterations) That said, I also tried Nelson-Siegel and indeed the exponential splines, but both methods would give me the linear curve you got as well. So I quickly discarded them without much thinking about as to why this kind of “deformation” may have happened. Speaking of deformation: I had to do some uneducated tweaking for the control knots vector to get suitable results where “suitable results” means: pricing of some benchmark corporate bonds yielded reasonable results. Not sure if there a good universal way to choose those knots, looking again at the C++ code docs: \warning "The results are extremely sensitive to the number and location of the knot points, and there is no optimal way of selecting them." James, J. and N. Webber, "Interest Rate Modelling" John Wiley, 2000, pp. 440. So, all in all not very scientific and more of a fun exercise. But all the same I am, too, interested in more details about this function. Suppose that more data cleansing could help, but I am pretty sure not to have included anything but plain bonds. Regards, Matthias From: Random User <qld...@gm... <mailto:qld...@gm...> > Sent: Wednesday, 2 December 2020 08:59 To: qua...@li... <mailto:qua...@li...> Subject: [Quantlib-users] FittedBondCurve in R Hello all - I'm trying to build a fitted US Treasury curve using FittedBondCurve(). However, my results are odd for two reasons. (1) the shape of the curve is a straight line and not concave as one might expect (2) the yields of the modeled term structure are within a very tight range for the entire curve and well below even the lowest yields in the yield dataset that is being fit (yields being fit are shown in the dataframe below). The only possible issues I can suspect are day count issues in my dateparams (unclear why) or the function being able to fit multiple data points for a single maturity. On this second point, for example, I'm trying to model all outstanding Treasuries, so there are several bonds that sit at the same maturity point (eg. at the 1Y point there could be an originally issued 2Y bond that has aged 1 year along with an originally issued 30Y bond that has aged 29 years etc.). I've included the relatively short code along with the dataset being fit to hopefully ease replication. This is very close to the QuantLib example for the function with the major difference that I'm trying to fit actual bond prices (the df that is referenced is shown in full below). Please let me know if you need more information. Thanks for any help! [[ REMOVED ]] |