Guides · bonds

DV01 / BPV of a bond via API (with worked example)

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.

01The call

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:

request
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.

response
{
  "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.

02Same call in Python, straight to position DV01

python
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

03Reading the bpv field

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:

PropertyHereMeaning
Sign-0.0784Negative: yields up, price down. A long position loses money when rates rise.
Unitper 100 facePrice points per 100 of face value — the same unit the prices are quoted in.
MethodrepricedExact +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.

04Scaling to a real position

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
NominalPosition 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.

05DV01 vs modified duration

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.

06Conventions

ConventionValue
pricesper 100 of face value (market quote convention)
settlementas provided (settlementDays=0, no implicit T+2)
ytmcompounded semiannual, 30/360
zero_curvezero rates continuously compounded, act/365
calendarTARGET, 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 key

No 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