rateslib
rateslib copied to clipboard
FloatRateNote is failing to calculate cashflows
Hi! I've been trying to follow the example in the FloatingRateNote Guide, but there seems to be a bug in the definition of the fixing
I have not been able to calculate a working cashflow trying many combinations of the parameters
cc @attack68
Hi the example in the user guide can probably be improved, actually. The specific example you seem to have taken was designed to show the calculation of accrued when some fixings in a period are provided and some are not.
E.g.
from pandas import Series, date_range
fixings = Series(2.0, index=date_range(dt(1999, 12, 1), dt(2000, 6, 2)))
frn = FloatRateNote(
effective=dt(1998, 12, 7),
termination=dt(2015, 12, 7),
frequency="S",
currency="gbp",
convention="Act365F",
ex_div=3,
fixings=fixings,
fixing_method="rfr_observation_shift",
method_param=5,
)
frn.accrued(dt(2000, 6, 4))
# 0.9911540636168727
If you try to calculate cashflows
without a curve to forecast NPVs it will display the structure of the bond and display the values it can but it cant forecast floating rates and DF, and NPVs...
If instead you build a Curve
which extends to the end of the bond and starts where the fixings
have ended and try to get cashflows
:
curve = Curve(
{dt(2000, 6, 3): 1.0,
dt(2020, 6, 3): 0.5,},
calendar=NoInput(0),
convention="act365f",
)
frn.cashflows(curve)
# ValueError: RFRs could not be calculated, have you missed providing `fixings`
# or does the `Curve` begin after the start of a `FloatPeriod` includingthe `method_param` adjustment?
This error is not the clearest but essentially what it is indicating is that rateslib is trying to calculate the rate for the periods before the fixings
start, i.e. between Dec 1998 and Dec 1999, using a Curve
that begins in Jun 2000.
The solution is to provide all the fixing data for all of those prior Periods:
fixings = Series(2.0, index=date_range(dt(1997, 12, 1), dt(2000, 6, 2)))
frn = FloatRateNote(
effective=dt(1998, 12, 7),
termination=dt(2015, 12, 7),
frequency="S",
currency="gbp",
convention="Act365F",
ex_div=3,
fixings=fixings,
fixing_method="rfr_observation_shift",
method_param=5,
)
frn.cashflows(curve)
This may sound like a silly question but, why do I need a curve if I have a full list of fixing values, including those in the future?
On Wed, Apr 24, 2024, 02:34 JHM Darbyshire @.***> wrote:
Hi the example in the user guide can probably be improved, actually. The specific example you seem to have taken was designed to show the calculation of accrued when some fixings in a period are provided and some are not.
E.g.
from pandas import Series, date_rangefixings = Series(2.0, index=date_range(dt(1999, 12, 1), dt(2000, 6, 2))) frn = FloatRateNote( effective=dt(1998, 12, 7), termination=dt(2015, 12, 7), frequency="S", currency="gbp", convention="Act365F", ex_div=3, fixings=fixings, fixing_method="rfr_observation_shift", method_param=5, )frn.accrued(dt(2000, 6, 4))# 0.9911540636168727
If you try to calculate cashflows without a curve to forecast NPVs it will display the structure of the bond and display the values it can but it cant forecast floating rates and DF, and NPVs...
Screenshot.2024-04-24.at.07.21.18.png (view on web) https://github.com/attack68/rateslib/assets/24256554/107f9ef8-2b4f-4fa4-abcd-d69565c3d1d1
If instead you build a Curve which extends to the end of the bond and starts where the fixings have ended and try to get cashflows:
curve = Curve( {dt(2000, 6, 3): 1.0, dt(2020, 6, 3): 0.5,}, calendar=NoInput(0), convention="act365f", )frn.cashflows(curve)# ValueError: RFRs could not be calculated, have you missed providing
fixings
# or does theCurve
begin after the start of aFloatPeriod
includingthemethod_param
adjustment?This error is not the clearest but essentially what it is indicating is that rateslib is trying to calculate the rate for the periods before the fixings start, i.e. between Dec 1998 and Dec 1999, using a Curve that begins in Jun 2000. The solution is to provide all the fixing data for all of those prior Periods:
fixings = Series(2.0, index=date_range(dt(1997, 12, 1), dt(2000, 6, 2))) frn = FloatRateNote( effective=dt(1998, 12, 7), termination=dt(2015, 12, 7), frequency="S", currency="gbp", convention="Act365F", ex_div=3, fixings=fixings, fixing_method="rfr_observation_shift", method_param=5, ) frn.cashflows(curve)
Screenshot.2024-04-24.at.07.33.20.png (view on web) https://github.com/attack68/rateslib/assets/24256554/b37fe677-fd78-4059-896c-f594cc2a5a2e
— Reply to this email directly, view it on GitHub https://github.com/attack68/rateslib/issues/149#issuecomment-2074066365, or unsubscribe https://github.com/notifications/unsubscribe-auth/AF7AZXD2OQ2XXZFU3QLDD5LY64745AVCNFSM6AAAAABGVUP66WVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDANZUGA3DMMZWGU . You are receiving this because you authored the thread.Message ID: @.***>
If you provide all the fixings for all the periods. It should work. Provide a reproducible example otherwise it is impossible to bug fix.
The discounting Curve
is used as reference for discounting cashflows to the present day. The immediate date is measured as the first node on the curve. This displays all cashflows and values them at zero becuase they are historical relative to the Curve
.
frn = FloatRateNote(
effective=dt(2000, 12, 7),
termination=dt(2002, 12, 7),
frequency="S",
currency="gbp",
convention="Act365F",
ex_div=3,
fixings=[2.5, 3.5, 4.5, 6.5],
fixing_method="ibor",
method_param=5,
)
curve = Curve({dt(2022, 1, 1): 1.0})
frn.cashflows(curve)
just to give you an example
In the code above, if you expand fixings to 2020 you would have the full range covered, and no need for a curve
fixings = Series(2.0, index=date_range(dt(1997, 12, 1), dt(2020, 6, 2)))
but I am still getting Nones
This is ultimately due to the evaluation of cashflows
of a single FloatPeriod
. The code contains:
if curve is not NoInput.blank:
cashflow = float(self.cashflow(curve))
rate = float(100 * cashflow / (-self.notional * self.dcf))
npv = float(self.npv(curve, disc_curve_))
npv_fx = npv * float(fx)
else:
cashflow, rate, npv, npv_fx = None, None, None, None
Even if the rate can be known if there are enough fixings, it is still not generated. Maybe a design flaw - maybe not. In any case, if you want to trigger the evaluation of the rates and their display use a Curve
with a reference date.
fixings = Series(2.0, index=date_range(dt(1997, 12, 1), dt(2020, 6, 2)))
frn = FloatRateNote(
effective=dt(1998, 12, 7),
termination=dt(2015, 12, 7),
frequency="S",
currency="gbp",
convention="Act365F",
ex_div=3,
fixings=fixings,
fixing_method="rfr_observation_shift",
method_param=5,
)
frn.cashflows(Curve({dt(2020, 6, 3): 1.0}))
Maybe an idea to add a psuedo curve to generate results..