4
$\begingroup$

I'm working with a fixed-rate bond in QuantLib, and I have set the day count convention to ISMA, but I would like to understand how this specific day count convention is used in the calculation of the bond's first cashflow I have gone through the link https://www.isda.org/a/pIJEE/The-Actual-Actual-Day-Count-Fraction-1999.pdf still not able to understand how quantlib is calculating the first short period or long period cashflow. Here are the relevant code and cashflows:

issue_date = ql.Date(1, 1, 2021)
maturity_date = ql.Date(15, 1, 2022)
stubdate = ql.Date(28, 2, 2021)
coupon_rate = 8.40 / 100
face_value = 1000000
calendar = ql.NullCalendar()
day_count = ql.ActualActual(ql.ActualActual.ISMA)
compounding = ql.Simple
payfreq = ql.Monthly
payment_schedule = ql.Schedule(issue_date, maturity_date, ql.Period(payfreq), calendar, ql.Unadjusted, ql.Unadjusted, ql.DateGeneration.Forward, True, stubdate)
lst_pysch = list(payment_schedule)
lst_pysch.pop(0)
lst_pysch.insert(0, issue_date)
new_paysch = ql.Schedule(lst_pysch)

fixedrate_leg = ql.FixedRateLeg(
            schedule=new_paysch, dayCount=day_count, nominals=[face_value], couponRates=[coupon_rate])

bond = ql.Bond(0, calendar, 100.0, maturity_date, issue_date, fixedrate_leg)

[(a.date(), a.amount()) for a in fixedrate_leg]

Output:

[(Date(28, 2, 2021), 14000.000000000013),
 (Date(31, 3, 2021), 6999.999999999895),
 ...
 (Date(15, 1, 2022), 3452.054794520487)]

My specific question is, how is the first cashflow value of 14,000 calculated? I would like to understand the logic and calculations behind this particular cashflow.

Thank you for your help!

$\endgroup$
2
  • $\begingroup$ You did not specify the issue_date. $\endgroup$
    – Attack68
    Commented Sep 24, 2023 at 8:03
  • $\begingroup$ Have added issue_date $\endgroup$ Commented Sep 24, 2023 at 8:58

2 Answers 2

4
$\begingroup$

Calculate the schedule first, and then create the day counter as:

day_count = ql.ActualActual(ql.ActualActual.ISMA, payment_schedule)

this allows the act/act instance to have all the information it needs to perform the calculations so well detailed by @Attack68. After this change, the output of your code is:

[(Date(28,2,2021), 13774.193548387047),
 (Date(31,3,2021), 6999.999999999895),
 (Date(30,4,2021), 6999.999999999895),
 (Date(31,5,2021), 6999.999999999895),
 (Date(30,6,2021), 6999.999999999895),
 (Date(31,7,2021), 6999.999999999895),
 (Date(31,8,2021), 6999.999999999895),
 (Date(30,9,2021), 6999.999999999895),
 (Date(31,10,2021), 6999.999999999895),
 (Date(30,11,2021), 6999.999999999895),
 (Date(31,12,2021), 6999.999999999895),
 (Date(15,1,2022), 3387.0967741935765)]
$\endgroup$
5
$\begingroup$

I cannot speak for Quantlib, I do not believe that calculation is correct actually.

But I can tell you how this calculation is performed in Rateslib.

from rateslib import *

bond = FixedRateBond(
    effective=dt(2021, 1, 1),
    termination=dt(2022, 1, 15),
    front_stub=dt(2021, 2, 28),
    stub="LongFrontShortBack",
    roll=31,
    convention="ActActICMA",
    modifier="none",
    frequency="M",
    fixed_rate=8.4
)
curve = Curve({dt(2021, 1, 1): 1.0, dt(2022, 12, 31): 0.96})
bond.cashflows(curve)

enter image description here

In this case the first period is identified as a Stub under ActActICMA mode and the calculation proceeds as follows:

i) Subtract a regular period from 28-feb-21 to get to 31-Jan-21.

ii) The start is less than 31-Jan-21 so it must be a LongStub. Set fraction=1.0.

iii) Subtract another regular period from 31-Jan-21 to get to 31-Dec-20.

iv) 1-Jan-21 is after this date so we add the following amount to fraction:

$$ \frac{31/Jan/21 - 1/Jan/21}{31/Jan/21 - 31/Dec/21} = 0.967741935483871 $$

v) fraction = 1.967741935483871 and coupon=8.4% / 12 * fraction * 1mm = 13,774.19

Other Factors

Roll day

I observed from your bond schedule that the roll was end-of-month (31) so this has been added to generate the schedule. If the roll was 28 this would change the above calculations. Nothing else about the bond needs to change except the roll and this will produce different cashflows in the front and back stubs.

other_bond = FixedRateBond(
    effective=dt(2021, 1, 1),
    termination=dt(2022, 1, 15),
    front_stub=dt(2021, 2, 28),
    stub="LONGFRONTSHORTBACK",
    roll=28,
    fixed_rate=8.4,
    frequency="M",
    convention="ActActICMA",
)
other_bond.cashflows(curve)

enter image description here

Calculation Mode Convention

Some conventions, such as Canadian Government Bonds work differently with ActActICMA. Under this calculation any identified Stub period adopts an Act365F calculation in the fractional part. Rateslib has a specifically named day count convention for these bonds: 'ActActICMA_stub365f':

another_bond = FixedRateBond(
    effective=dt(2021, 1, 1),
    termination=dt(2022, 1, 15),
    front_stub=dt(2021, 2, 28),
    stub="LONGFRONTSHORTBACK",
    roll=31,
    fixed_rate=8.4,
    frequency="M",
    convention="ActActICMA_stub365f",
)
another_bond.cashflows(curve)

enter image description here

In this latter case the fraction is assessed as: $$ fraction = 1.0 + \frac{31/Jan/21 - 1/Jan/21}{365} * 12 = 1.986301369.. $$ And the cashflow is calculated as: $$ 1mm * 8.4\% / 12 * fraction = 13,904.11 $$

QuantLib Speculation

If you assert that quantlib is using something similar to the "ActActICMA_stub365f" calculation but is using an 'Act360' convention instead of 'ACT365f' convention then the first stub cashflow is actually very close to \$14,000.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.