Treasury Bond Par Pricing Depends on Accrued Interest and Day-Count Conventions
Summary
This QuantLib discussion investigates why a newly constructed Treasury bond does not return exactly par when priced at a coupon date using a flat yield. The example is a long-dated, semiannual fixed-rate bond with a 2% coupon and a 1% annual yield. The resulting clean price is slightly above 100, despite the expectation that a bond discounted at its coupon rate should price at par.
The author reports obtaining par after setting the global evaluation date to a coupon payment date and using an Actual/Actual convention tied to the bond’s coupon schedule, both in the bond and discount-curve setup. The example highlights that bond valuation depends on consistent schedules, settlement timing, accrued-interest treatment, and day-count conventions. The discussion provides a diagnostic outcome rather than a general pricing derivation; exact results can vary with the library configuration and instrument conventions.
Key ideas
- A bond’s clean price can differ slightly from par when timing and day-count conventions are inconsistent.
- The example uses a semiannual Treasury bond and a flat yield in QuantLib.
- The reported par result follows setting the evaluation date to a coupon date and using schedule-aware Actual/Actual conventions.
- Settlement, accrued interest, and coupon schedule details matter when comparing clean price with present value.
Tags
Full text
# QuantLib - Help buiding a simple T-Bond (Mischievous Pricing Conventions)
# QuantLib - Help buiding a simple T-Bond (Mischievous Pricing Conventions)
Im having a problem getting the right price for a simple T-Bond, maybe a Mischievous Pricing Conventions like Luigi says in his book. Im tying to price "912810SZ Govt"
```
def t_bond(iss, mat, coup):
calendar = ql.UnitedStates()
busconv = ql.Unadjusted
sett = 1
face = 100.
freq = ql.Period(ql.Semiannual)
schedule = ql.Schedule(iss, \
mat, \
freq, calendar, busconv, busconv, \
ql.DateGeneration.Backward, True)
bond = ql.FixedRateBond(sett, face, schedule, [coup/2/100], \
ql.ActualActual(ql.ActualActual.Bond), ql.Unadjusted)
return bond
bond = t_bond(ql.Date(15, 8, 2021), ql.Date(15, 8, 2051), 2.)
rate = ql.InterestRate(0.01, ql.ActualActual(), ql.SimpleThenCompounded, ql.Annual)
ql.BondFunctions.cleanPrice(bond, rate)
>>> 100.00006747141342
```
Tell me if im worg, but it should return 100.0, right?
When I inspect the cashflows it seems correct
```
coupons = [ql.as_coupon(c) for c in bond.cashflows()[:-1]]
dados = [(c.date(), c.rate(), c.accrualPeriod(), c.accruedAmount(c.date())) for c in coupons]
pd.DataFrame(dados, columns=["Date", "Rate", "Acc Period", "AccAmount"], index=range(1, len(coupons)+1))
```
What am I doing wrong? Thanks in advance.
EDIT
I got the right par value of the bond by setting the global evaluation date to a coupom payment date and by passing a Schedule instance with act/act daycount convention in the bond object and on the discount curve object
```
ql.ActualActual(ql.ActualActual.ISMA, schedule)
```
This discussion helped me to arive at the right answer for my problem: Difference arising between Dirty Price and NPV using QuantLib Python Thanks!Shown 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.