Guides · bonds
DV01 — dollar value of one basis point, also called BPV — is the change in a bond's price when its yield rises by one basis point (0.01%). POST /v1/bonds/price returns it as the bpv field, computed by full repricing rather than a duration approximation, per 100 of face value. Multiply by notional/100 to get your position's DV01 in currency.
A 10-year 3% semiannual bond (30/360), issued 2026-01-15, maturing 2036-01-15, settling 2026-07-15, at a flat 3.5% yield:
curl -s https://quantbox.dev/v1/bonds/price \
-H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d '{"coupon_rate": 0.03,
"issue_date": "2026-01-15",
"maturity_date": "2036-01-15",
"settlement_date": "2026-07-15",
"frequency": "semiannual",
"day_count": "30/360",
"yield_rate": 0.035}'
Runs on the free tier — 500 calls/month, no credit card. Get a key.
{
"clean_price": 95.9871,
"dirty_price": 95.9871,
"accrued": 0.0,
"ytm": 0.035,
"modified_duration": 8.1641,
"convexity": 76.5788,
"bpv": -0.0784,
"conventions": {
"prices": "per 100 of face value (market quote convention)",
"settlement": "as provided (settlementDays=0, no implicit T+2)",
"ytm": "compounded semiannual, 30/360",
"zero_curve": "zero rates continuously compounded, act/365",
"calendar": "TARGET, unadjusted schedule"
}
}
Settlement 2026-07-15 falls exactly on the first coupon date of the semiannual schedule, so accrued is 0 and clean = dirty here.
import requests
KEY = "YOUR_API_KEY" # free key from https://quantbox.dev
r = requests.post("https://quantbox.dev/v1/bonds/price",
headers={"X-API-Key": KEY},
json={"coupon_rate": 0.03,
"issue_date": "2026-01-15",
"maturity_date": "2036-01-15",
"settlement_date": "2026-07-15",
"frequency": "semiannual",
"day_count": "30/360",
"yield_rate": 0.035})
bond = r.json()
notional = 10_000_000 # €10M nominal
dv01 = bond["bpv"] * notional / 100 # bpv is per 100 face
print(f"bpv per 100 face: {bond['bpv']:.4f}")
print(f"position DV01: {dv01:,.2f} EUR/bp")
# bpv per 100 face: -0.0784
# position DV01: -7,836.42 EUR/bp
The API bumps the yield by +1 bp, reprices the whole bond, and returns the exact dirty-price difference — no first-order approximation. Three things to hold on to:
| Property | Here | Meaning |
|---|---|---|
| Sign | -0.0784 | Negative: yields up, price down. A long position loses money when rates rise. |
| Unit | per 100 face | Price points per 100 of face value — the same unit the prices are quoted in. |
| Method | repriced | Exact +1 bp reprice, so convexity is already inside the number. |
Sanity check against duration: modified duration × dirty price / 10,000 = 8.1641 × 95.9871 / 10,000 ≈ 0.0784 — matching the repriced BPV to four decimals, as it should for a 1 bp move.
The bpv is quoted per 100 of face value, so a position of nominal N contains N/100 of those units:
position DV01 = bpv × notional / 100
€10,000,000 nominal:
= -0.078364 × 10,000,000 / 100
= -0.078364 × 100,000
≈ -7,836.42 EUR per +1 bp
| Nominal | Position DV01 (EUR/bp) |
|---|---|
| €1,000,000 | -783.64 |
| €10,000,000 | -7,836.42 |
| €100,000,000 | -78,364.24 |
Scale before you round. The response carries full precision (-0.07836423...); the 4-decimal display value -0.0784 × 100,000 would give -7,840 — a €4/bp error from rounding alone. Use r.json()["bpv"] directly, as the Python snippet above does.
Same risk, different units. Modified duration (8.1641 here) is a relative measure — percent price change per unit of yield. DV01 is absolute — currency per basis point — which is why desks hedge with it: to neutralize a position, match DV01s, not durations. The conversion is DV01 ≈ duration × dirty price / 10,000, but the API's bpv is the exact repriced figure, so use it directly. For duration and convexity in depth, see bond duration, convexity and BPV over HTTP; for the full response walkthrough, price a fixed-rate bond with a REST API.
| Convention | Value |
|---|---|
| prices | per 100 of face value (market quote convention) |
| settlement | as provided (settlementDays=0, no implicit T+2) |
| ytm | compounded semiannual, 30/360 |
| zero_curve | zero rates continuously compounded, act/365 |
| calendar | TARGET, unadjusted schedule |
The YTM convention follows your request — compounded at the frequency you set (semiannual here), measured with your day_count (30/360). Had settlement fallen mid-period, the accrued would be non-zero and clean and dirty would split — see clean vs dirty price, explained with code.
Get the DV01 of your own book. Key in seconds, 500 free calls a month, full request schema in the docs.
Get a free API keyNo credit card · schemas and error codes in /docs
Related: Price a fixed-rate bond with a REST API · Clean vs dirty bond price and accrued interest · Bond duration, convexity and BPV over HTTP