Valuing Bonds
Overview
Methods
- dirtyPrice =
- cleanPrice =
- NPV =
- accruedAmount =
Sample Code
The value function in
import valuation as vl
value = vl.value(bond, curve)
dirty = value.dirtyPrice()
clean = value.cleanPrice()
npv = value.NPV()
accrued = value.accruedAmount()
Qunatlib Sample Code
The following shows a sample implementation using the native python quantlib (no davinci wrapper)
to value a bond.
def value_contract(bond, curve):
term_structure_handle = ql.YieldTermStructureHandle(curve)
bond_engine = ql.DiscountingBondEngine(term_structure_handle)
bond.setPricingEngine(bond_engine)
dirty_price = bond.NPV()
print(f"Dirty Price / NPV: {dirty_price:.4f}")
# 5. Calculate Clean Price (NPV minus accrued interest)
accrued_amount = bond.accruedAmount()
clean_price = dirty_price - accrued_amount
return (dirty_price, clean_price)
Notes
In Python QuantLib, bond.dirtyPrice() and bond.NPV() return different values primarily because they discount future cash flows to different target dates and use different scaling metrics.
Here is a breakdown of why these two metrics diverge:
-
Different Discounting Target Dates (The Main Driver)
The primary difference lies in the exact date to which the bond's future cash flows are discounted:
bond.NPV() discounts all future cash flows back to the evaluation date (or the reference date of the discount curve).
This represents the absolute present value of the asset as of today.
bond.dirtyPrice() discounts cash flows to the bond's settlement date. In financial markets, a bond trade takes a few days to settle (e.g., T+1 or T+2).
The dirty price represents the actual invoice amount paid on that specific settlement day.Because the settlement date is usually a few days after the evaluation date, dirtyPrice() discounts the cash flows over a slightly shorter time horizon, resulting in a different value.
-
Percentage Price vs. Currency Value
The two methods output data using entirely different units and scaling conventions:
bond.dirtyPrice() is always normalized and quoted as a percentage of the face value (typically clean/dirty prices are based on a nominal value of 100).
bond.NPV() returns the absolute currency value based on the actual total face value/notional specified when you constructed the bond object.
For example, if your bond has a face value of $100,000, the NPV will reflect the full dollar amount (e.g., $101,330), while the dirty price will display as 101.33.3.
Handling of Accrued Interest and Cash Flow ExclusionsDepending on where the settlement date falls relative to coupon payments, the cash flows included in the calculation can vary:
If a coupon payment falls strictly between the evaluation date and the settlement date, bond.NPV() will include that coupon in its valuation because it occurs in the future relative to today.
bond.dirtyPrice() will completely ignore that coupon because it belongs to the previous owner by the time the trade actually settles.
2.
How to Make Them Match
If you want to eliminate the timing discrepancy so that the percentage price aligns directly with the discounted cash flows, you must force both dates to be identical:
Set your bond's settlementDays parameter to 0 when defining the bond object.Alternatively, ensure the evaluation date and settlement date are exactly the same in your script settings.
Once the time gap is closed, bond.NPV() will equal bond.dirtyPrice() * bond.notional() / 100.